{-# language Rank2Types, OverloadedStrings #-} module Rasa.Internal.Actions ( -- * Performing Actions on Buffers bufDo , bufDo_ , buffersDo , buffersDo_ -- * Editor Actions , exit , newBuffer , getBufRefs , getBuffers , getBuffer , nextBufRef , prevBufRef -- * Buffer Actions , overRange , replaceRange , deleteRange , insertAt , sizeOf ) where import Rasa.Internal.Editor import Rasa.Internal.Action import Rasa.Internal.BufAction import Rasa.Internal.Range import Rasa.Internal.Listeners import Rasa.Internal.Events import Rasa.Internal.Buffer as B import Control.Lens import Control.Monad import Control.Arrow (first) import Data.Maybe import Data.IntMap as M import qualified Yi.Rope as Y -- | This lifts a 'Rasa.Action.BufAction' to an 'Rasa.Action.Action' which -- performs the 'Rasa.Action.BufAction' on every buffer and collects the return -- values as a list. buffersDo :: BufAction a -> Action [a] buffersDo bufAct = do bufRefs <- getBufRefs catMaybes . foldMap (:[]) <$> mapM (runBufAction bufAct) bufRefs buffersDo_ :: BufAction a -> Action () buffersDo_ = void . buffersDo -- | This lifts a 'Rasa.Internal.Action.BufAction' to an 'Rasa.Internal.Action.Action' which -- performs the 'Rasa.Internal.Action.BufAction' on the buffer referred to by the 'BufRef' -- If the buffer referred to no longer exists this returns: @Nothing@. bufDo :: BufRef -> BufAction a -> Action (Maybe a) bufDo bufRef bufAct = runBufAction bufAct bufRef bufDo_ :: BufRef -> BufAction a -> Action () bufDo_ bufRef bufAct = void $ bufDo bufRef bufAct -- | This adds a new buffer with the given text, returning a reference to that buffer. newBuffer :: Y.YiString -> Action BufRef newBuffer txt = do n <- nextBufId <<+= 1 buffers %= insert n (mkBuffer txt) let bufRef = BufRef n dispatchEvent (BufAdded bufRef) return bufRef -- | Returns an up-to-date list of all 'BufRef's getBufRefs :: Action [BufRef] getBufRefs = fmap BufRef <$> use (buffers.to keys) -- | Returns the 'Buffer' for a BufRef if it still exists. -- This is read-only; altering the buffer has no effect on the stored buffer. -- This function is useful for renderers. getBuffer :: BufRef -> Action (Maybe Buffer) getBuffer (BufRef bufInt) = use (buffers.at bufInt) -- | Returns an up-to-date list of all 'Buffer's, returned values -- are read-only; altering them has no effect on the actual stored buffers. -- This function is useful for renderers. getBuffers :: Action [(BufRef, Buffer)] getBuffers = fmap (first BufRef) <$> use (buffers.to assocs) -- | Gets 'BufRef' that comes after the one provided nextBufRef :: BufRef -> Action BufRef nextBufRef br@(BufRef bufInt) = do bufMap <- use buffers if M.null bufMap then return br else do let mGreaterInd = lookupGT bufInt bufMap case mGreaterInd of Just (greaterInd, _) -> return $ BufRef greaterInd Nothing -> return . BufRef . fst . findMin $ bufMap -- | Gets 'BufRef' that comes before the one provided prevBufRef :: BufRef -> Action BufRef prevBufRef br@(BufRef bufInt) = do bufMap <- use buffers if M.null bufMap then return br else do let mLesserInd = lookupLT bufInt bufMap case mLesserInd of Just (lesserInd, _) -> return $ BufRef lesserInd Nothing -> return . BufRef . fst . findMax $ bufMap -- | This signals to the editor that you'd like to shutdown. The current events -- will finish processing, then the 'Rasa.Internal.Listeners.onExit' event will be dispatched, -- then the editor will exit. exit :: Action () exit = exiting .= True -- | Runs function over given range of text overRange :: CrdRange -> (Y.YiString -> Y.YiString) -> BufAction () overRange r f = getRange r >>= setRange r . f -- | Deletes the text in the given range from the buffer. deleteRange :: CrdRange -> BufAction () deleteRange r = replaceRange r "" -- | Replaces the text in the given range with the given text. replaceRange :: CrdRange -> Y.YiString -> BufAction () replaceRange r txt = overRange r (const txt) -- | Inserts text into the buffer at the given 'Coord'. insertAt :: Coord -> Y.YiString -> BufAction () insertAt c = replaceRange r where r = Range c c