{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Yi.Types where
import Control.Concurrent (MVar, modifyMVar, modifyMVar_, readMVar)
import Control.Monad.Base (MonadBase, liftBase)
import Control.Monad.Reader
import Control.Monad.State.Strict
import Control.Monad (ap, liftM3, void, forever)
import qualified Data.Set as Set
import Data.Binary (Binary)
import qualified Data.Binary as B (get, put)
import Data.Default (Default, def)
import qualified Data.DelayList as DelayList (DelayList)
import qualified Data.DynamicState as ConfigState (DynamicState)
import qualified Data.DynamicState.Serializable as DynamicState (DynamicState)
import Data.Function (on)
import Data.List.NonEmpty (NonEmpty)
import Data.List.PointedList (PointedList)
import qualified Data.Map.Strict as M (Map)
import qualified Data.Text as T (Text)
import qualified Data.Text.Encoding as E (decodeUtf8, encodeUtf8)
import Data.Time (UTCTime (..))
import Data.Typeable (Typeable)
import qualified Data.Sequence as S
import Data.Word (Word8)
import Yi.Buffer.Basic (BufferRef, WindowRef)
import Yi.Buffer.Implementation
import Yi.Buffer.Undo (URList)
import Yi.Config.Misc (ScrollStyle)
import Yi.Event (Event)
import qualified Yi.Interact as I (I, P (End))
import Yi.KillRing (Killring)
import Yi.Layout (AnyLayoutManager)
import Yi.Monad (getsAndModify)
import Yi.Process (SubprocessId, SubprocessInfo)
import qualified Yi.Rope as R (YiString)
import Yi.Style (StyleName)
import Yi.Style.Library (Theme)
import Yi.Syntax (ExtHL, Stroke)
import Yi.Tab (Tab)
import Yi.UI.Common (UI)
import Yi.Window (Window)
data Action = forall a. Show a => YiA (YiM a)
| forall a. Show a => EditorA (EditorM a)
| forall a. Show a => BufferA (BufferM a)
deriving Typeable
emptyAction :: Action
emptyAction = BufferA (return ())
class (Default a, Binary a, Typeable a) => YiVariable a
class (Default a, Typeable a) => YiConfigVariable a
instance Eq Action where
_ == _ = False
instance Show Action where
show (YiA _) = "@Y"
show (EditorA _) = "@E"
show (BufferA _) = "@B"
type Interact ev a = I.I ev Action a
type KeymapM a = Interact Event a
type Keymap = KeymapM ()
type KeymapEndo = Keymap -> Keymap
type KeymapProcess = I.P Event Action
data IsRefreshNeeded = MustRefresh | NoNeedToRefresh
deriving (Show, Eq)
data Yi = Yi { yiUi :: UI Editor
, yiInput :: [Event] -> IO ()
, yiOutput :: IsRefreshNeeded -> [Action] -> IO ()
, yiConfig :: Config
, yiVar :: MVar YiVar
}
deriving Typeable
data YiVar = YiVar { yiEditor :: !Editor
, yiSubprocessIdSupply :: !SubprocessId
, yiSubprocesses :: !(M.Map SubprocessId SubprocessInfo)
}
newtype YiM a = YiM {runYiM :: ReaderT Yi IO a}
deriving (Monad, Applicative, MonadReader Yi, MonadBase IO, Typeable, Functor)
instance MonadState Editor YiM where
get = yiEditor <$> (liftBase . readMVar =<< yiVar <$> ask)
put v = liftBase . flip modifyMVar_ (\x -> return $ x {yiEditor = v}) =<< yiVar <$> ask
instance MonadEditor YiM where
askCfg = yiConfig <$> ask
withEditor f = do
r <- asks yiVar
cfg <- asks yiConfig
liftBase $ unsafeWithEditor cfg r f
unsafeWithEditor :: Config -> MVar YiVar -> EditorM a -> IO a
unsafeWithEditor cfg r f = modifyMVar r $ \var -> do
let e = yiEditor var
let (e',a) = runEditor cfg f e
e' `seq` a `seq` return (var {yiEditor = e'}, a)
data KeymapSet = KeymapSet
{ topKeymap :: Keymap
, insertKeymap :: Keymap
}
extractTopKeymap :: KeymapSet -> Keymap
extractTopKeymap kms = forever (topKeymap kms)
newtype BufferM a = BufferM { fromBufferM :: ReaderT Window (State FBuffer) a }
deriving ( Monad, Functor, Typeable
, MonadState FBuffer
, MonadReader Window )
data IndentSettings = IndentSettings
{ expandTabs :: !Bool
, tabSize :: !Int
, shiftWidth :: !Int
} deriving (Eq, Show, Typeable)
instance Applicative BufferM where
pure = return
(<*>) = ap
data FBuffer = forall syntax.
FBuffer { bmode :: !(Mode syntax)
, rawbuf :: !(BufferImpl syntax)
, attributes :: !Yi.Types.Attributes
}
deriving Typeable
instance Eq FBuffer where
(==) = (==) `on` bkey__ . attributes
type WinMarks = MarkSet Mark
data MarkSet a = MarkSet { fromMark, insMark, selMark :: !a }
deriving (Traversable, Foldable, Functor, Show)
instance Binary a => Binary (MarkSet a) where
put (MarkSet f i s) = B.put f >> B.put i >> B.put s
get = liftM3 MarkSet B.get B.get B.get
data Attributes
= Attributes
{ ident :: !BufferId
, bkey__ :: !BufferRef
, undos :: !URList
, bufferDynamic :: !DynamicState.DynamicState
, preferCol :: !(Maybe Int)
, preferVisCol :: !(Maybe Int)
, stickyEol :: !Bool
, pendingUpdates :: !(S.Seq UIUpdate)
, selectionStyle :: !SelectionStyle
, keymapProcess :: !KeymapProcess
, winMarks :: !(M.Map WindowRef WinMarks)
, lastActiveWindow :: !Window
, lastSyncTime :: !UTCTime
, readOnly :: !Bool
, inserting :: !Bool
, directoryContent :: !Bool
, pointFollowsWindow :: !(Set.Set WindowRef)
, updateTransactionInFlight :: !Bool
, updateTransactionAccum :: !(S.Seq Update)
, fontsizeVariation :: !Int
, updateStream :: !(S.Seq Update)
} deriving Typeable
instance Binary Yi.Types.Attributes where
put (Yi.Types.Attributes n b u bd pc pv se pu selectionStyle_
_proc wm law lst ro ins _dc _pfw isTransacPresent transacAccum fv lg') = do
let putTime (UTCTime x y) = B.put (fromEnum x) >> B.put (fromEnum y)
B.put n >> B.put b >> B.put u >> B.put bd
B.put pc >> B.put pv >> B.put se >> B.put pu >> B.put selectionStyle_ >> B.put wm
B.put law >> putTime lst >> B.put ro >> B.put ins >> B.put _dc
B.put isTransacPresent >> B.put transacAccum >> B.put fv >> B.put lg'
get = Yi.Types.Attributes <$> B.get <*> B.get <*> B.get <*> B.get <*>
B.get <*> B.get <*> B.get <*> B.get <*> B.get <*> pure I.End <*> B.get <*> B.get
<*> getTime <*> B.get <*> B.get <*> B.get
<*> pure (mempty) <*> B.get <*> B.get <*> B.get <*> B.get
where
getTime = UTCTime <$> (toEnum <$> B.get) <*> (toEnum <$> B.get)
data BufferId = MemBuffer !T.Text
| FileBuffer !FilePath
deriving (Show, Eq, Ord)
instance Binary BufferId where
get = B.get >>= \case
(0 :: Word8) -> MemBuffer . E.decodeUtf8 <$> B.get
1 -> FileBuffer <$> B.get
x -> fail $ "Binary failed on BufferId, tag: " ++ show x
put (MemBuffer t) = B.put (0 :: Word8) >> B.put (E.encodeUtf8 t)
put (FileBuffer t) = B.put (1 :: Word8) >> B.put t
data SelectionStyle = SelectionStyle
{ highlightSelection :: !Bool
, rectangleSelection :: !Bool
} deriving (Typeable, Show)
instance Binary SelectionStyle where
put (SelectionStyle h r) = B.put h >> B.put r
get = SelectionStyle <$> B.get <*> B.get
data AnyMode = forall syntax. AnyMode (Mode syntax)
deriving Typeable
data Mode syntax = Mode
{ modeName :: T.Text
, modeApplies :: FilePath -> R.YiString -> Bool
, modeHL :: ExtHL syntax
, modePrettify :: syntax -> BufferM ()
, modeKeymap :: KeymapSet -> KeymapSet
, modeIndent :: syntax -> IndentBehaviour -> BufferM ()
, modeFollow :: syntax -> Action
, modeIndentSettings :: IndentSettings
, modeToggleCommentSelection :: Maybe (BufferM ())
, modeGetStrokes :: syntax -> Point -> Point -> Point -> [Stroke]
, modeOnLoad :: BufferM ()
, modeModeLine :: [T.Text] -> BufferM T.Text
, modeGotoDeclaration :: BufferM ()
}
data IndentBehaviour =
IncreaseCycle
| DecreaseCycle
| IncreaseOnly
| DecreaseOnly
deriving (Eq, Show)
type Status = ([T.Text], StyleName)
type Statuses = DelayList.DelayList Status
data Editor = Editor
{ bufferStack :: !(NonEmpty BufferRef)
, buffers :: !(M.Map BufferRef FBuffer)
, refSupply :: !Int
, tabs_ :: !(PointedList Tab)
, dynamic :: !DynamicState.DynamicState
, statusLines :: !Statuses
, maxStatusHeight :: !Int
, killring :: !Killring
, currentRegex :: !(Maybe SearchExp)
, searchDirection :: !Direction
, pendingEvents :: ![Event]
, onCloseActions :: !(M.Map BufferRef (EditorM ()))
} deriving Typeable
newtype EditorM a = EditorM {fromEditorM :: ReaderT Config (State Editor) a}
deriving (Monad, Applicative, MonadState Editor,
MonadReader Config, Functor, Typeable)
instance MonadEditor EditorM where
askCfg = ask
withEditor = id
class (Monad m, MonadState Editor m) => MonadEditor m where
askCfg :: m Config
withEditor :: EditorM a -> m a
withEditor f = do
cfg <- askCfg
getsAndModify (runEditor cfg f)
withEditor_ :: EditorM a -> m ()
withEditor_ = withEditor . void
runEditor :: Config -> EditorM a -> Editor -> (Editor, a)
runEditor cfg f e = let (a, e') = runState (runReaderT (fromEditorM f) cfg) e
in (e',a)
data UIConfig = UIConfig {
configFontName :: Maybe String,
configFontSize :: Maybe Int,
configScrollStyle :: Maybe ScrollStyle,
configScrollWheelAmount :: Int,
configLeftSideScrollBar :: Bool,
configAutoHideScrollBar :: Bool,
configAutoHideTabBar :: Bool,
configLineWrap :: Bool,
configCursorStyle :: CursorStyle,
configWindowFill :: Char,
configTheme :: Theme,
configLineNumbers :: Bool
}
type UIBoot = Config -> ([Event] -> IO ())
-> ([Action] -> IO ()) -> Editor -> IO (UI Editor)
data CursorStyle = AlwaysFat
| NeverFat
| FatWhenFocused
| FatWhenFocusedAndInserting
data Config = Config {startFrontEnd :: UIBoot,
configUI :: !UIConfig,
startActions :: ![Action],
initialActions :: ![Action],
defaultKm :: !KeymapSet,
configInputPreprocess :: !(I.P Event Event),
modeTable :: ![AnyMode],
debugMode :: !Bool,
configRegionStyle :: !RegionStyle,
configKillringAccumulate :: !Bool,
configCheckExternalChangesObsessively :: !Bool,
bufferUpdateHandler :: !(S.Seq (S.Seq Update -> BufferM ())),
layoutManagers :: ![AnyLayoutManager],
configVars :: !ConfigState.DynamicState
}
data RegionStyle = LineWise
| Inclusive
| Exclusive
| Block
deriving (Eq, Typeable, Show)
instance Binary RegionStyle where
put LineWise = B.put (0 :: Word8)
put Inclusive = B.put (1 :: Word8)
put Exclusive = B.put (2 :: Word8)
put Block = B.put (3 :: Word8)
get = B.get >>= \case
(0 :: Word8) -> return LineWise
1 -> return Inclusive
2 -> return Exclusive
3 -> return Block
n -> fail $ "Binary RegionStyle fail with " ++ show n
instance Default RegionStyle where
def = Inclusive
instance YiVariable RegionStyle