{-# LANGUAGE TemplateHaskell, Rank2Types, ExistentialQuantification, ScopedTypeVariables, OverloadedStrings #-} module Rasa.Internal.Editor ( -- * Accessing/Storing state Editor , focused -- | 'focused' is the index of the currently selected buffer (this will likely change) , buffers -- |'buffers' is a lens into all buffers. , exiting -- | 'exiting' Whether the editor is in the process of exiting. Can be set inside an 'Rasa.Internal.Action.Action': -- -- > exiting .= True , ext , allBufExt , bufExt , focusedBuf , range ) where import Rasa.Internal.Buffer import Rasa.Internal.Extensions import Rasa.Internal.Range import Unsafe.Coerce import Data.Dynamic import Data.Default import Data.Maybe import Control.Lens import qualified Yi.Rope as Y -- | This is the primary state of the editor. data Editor = Editor { _buffers :: [Buffer] , _focused :: Int , _exiting :: Bool , _extState :: ExtMap } makeLenses ''Editor instance Show Editor where show editor = "Buffers==============\n" ++ show (editor^.buffers) ++ "\n\n" ++ "Editor Extensions==============\n" ++ show (editor^.extState) ++ "\n\n" ++ "---\n\n" instance Default Editor where def = Editor { _extState = def , _buffers=fmap newBuffer [ "Buffer 0\nHey! How's it going over there?\nI'm having just a splended time!\nAnother line for you sir?" , "Buffer 1\nHey! How's it going over there?\nI'm having just a splended time!\nAnother line for you sir?" ] , _focused=0 , _exiting=False } -- | A lens over the extensions of all buffers. -- This is useful for setting defaults or altering extension state across all buffers. allBufExt :: forall a. (Show a, Typeable a) => Traversal' Editor (Maybe a) allBufExt = buffers . traverse . bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce where coerce = iso (\(Ext x) -> unsafeCoerce x) Ext -- | 'bufExt' is a lens which will focus a given extension's state within a -- buffer (within a 'Data.Action.BufAction'). The lens will automagically focus -- the required extension by using type inference. It's a little bit of magic, -- if you treat the focus as a member of your extension state it should just -- work out. -- -- This lens falls back on the extension's 'Data.Default.Default' instance (when getting) if -- nothing has yet been stored. bufExt :: forall a. (Show a, Typeable a, Default a) => Lens' Buffer a bufExt = lens getter setter where getter buf = fromMaybe def $ buf ^. bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce setter buf new = set (bufExts . at (typeRep (Proxy :: Proxy a)) . mapping coerce) (Just new) buf coerce = iso (\(Ext x) -> unsafeCoerce x) Ext -- | 'ext' is a lens which will focus the extension state that matches the type -- inferred as the focal point. It's a little bit of magic, if you treat the -- focus as a member of your extension state it should just work out. -- -- This lens falls back on the extension's 'Data.Default.Default' instance (when getting) if -- nothing has yet been stored. ext :: forall a. (Show a, Typeable a, Default a) => Lens' Editor a ext = lens getter setter where getter editor = fromMaybe def $ editor ^. extState . at (typeRep (Proxy :: Proxy a)) . mapping coerce setter editor new = set (extState . at (typeRep (Proxy :: Proxy a)) . mapping coerce) (Just new) editor coerce = iso (\(Ext x) -> unsafeCoerce x) Ext -- | 'focusedBuf' is a lens which focuses the currently selected buffer. focusedBuf :: Lens' Editor Buffer focusedBuf = lens getter setter where getter editor = let foc = editor ^. focused in editor ^?! buffers . ix foc setter editor new = let foc = editor ^. focused in editor & buffers . ix foc .~ new -- | A lens over text which is encompassed by a 'Range' range :: Range -> Lens' Buffer Y.YiString range (Range start end) = lens getter setter where getter = view (rope . beforeC end . afterC start) setter old new = old & rope . beforeC end . afterC start .~ new