module Yi.Keymap.Vim.StateUtils
( switchMode
, switchModeE
, resetCount
, resetCountE
, setCountE
, modifyStateE
, getMaybeCountE
, getCountE
, accumulateEventE
, accumulateBindingEventE
, accumulateTextObjectEventE
, flushAccumulatorE
, dropAccumulatorE
, dropBindingAccumulatorE
, dropTextObjectAccumulatorE
, setRegisterE
, getRegisterE
, normalizeCountE
, maybeMult
, updateModeIndicatorE
, saveInsertEventStringE
, resetActiveRegisterE
) where
import Control.Applicative ((<$>))
import Control.Monad (when)
import qualified Data.HashMap.Strict as HM (insert, lookup)
import Data.Maybe (fromMaybe, isJust)
import Data.Monoid (Monoid (mempty), (<>))
import qualified Data.Text as T (null)
import Yi.Buffer.Normal (RegionStyle (Block, LineWise))
import Yi.Editor (EditorM, getEditorDyn, putEditorDyn, setStatus)
import Yi.Event (Event)
import Yi.Keymap.Vim.Common
import Yi.Keymap.Vim.EventUtils
import Yi.Rope (YiString)
import Yi.String (showT)
import Yi.Style (defaultStyle)
switchMode :: VimMode -> VimState -> VimState
switchMode mode state = state { vsMode = mode }
switchModeE :: VimMode -> EditorM ()
switchModeE mode = modifyStateE $ switchMode mode
modifyStateE :: (VimState -> VimState) -> EditorM ()
modifyStateE f = do
currentState <- getEditorDyn
putEditorDyn $ f currentState
resetCount :: VimState -> VimState
resetCount s = s { vsCount = Nothing }
resetCountE :: EditorM ()
resetCountE = modifyStateE resetCount
getMaybeCountE :: EditorM (Maybe Int)
getMaybeCountE = fmap vsCount getEditorDyn
getCountE :: EditorM Int
getCountE = do
currentState <- getEditorDyn
return $! fromMaybe 1 (vsCount currentState)
setCountE :: Int -> EditorM ()
setCountE n = modifyStateE $ \s -> s { vsCount = Just n }
accumulateBindingEventE :: Event -> EditorM ()
accumulateBindingEventE e = modifyStateE $
\s -> s { vsBindingAccumulator = vsBindingAccumulator s <> eventToEventString e }
accumulateEventE :: Event -> EditorM ()
accumulateEventE e = modifyStateE $
\s -> s { vsAccumulator = vsAccumulator s <> eventToEventString e }
accumulateTextObjectEventE :: EventString -> EditorM ()
accumulateTextObjectEventE evs = modifyStateE $
\s -> s { vsTextObjectAccumulator = vsTextObjectAccumulator s <> evs }
flushAccumulatorE :: EditorM ()
flushAccumulatorE = do
accum <- vsAccumulator <$> getEditorDyn
let repeatableAction = stringToRepeatableAction accum
modifyStateE $ \s ->
s { vsRepeatableAction = Just repeatableAction
, vsAccumulator = mempty
, vsCurrentMacroRecording = fmap (fmap (<> accum))
(vsCurrentMacroRecording s)
}
dropAccumulatorE :: EditorM ()
dropAccumulatorE =
modifyStateE $ \s ->
let accum = vsAccumulator s
in s { vsAccumulator = mempty
, vsCurrentMacroRecording = fmap (fmap (<> accum))
(vsCurrentMacroRecording s)
}
dropBindingAccumulatorE :: EditorM ()
dropBindingAccumulatorE =
modifyStateE $ \s -> s { vsBindingAccumulator = mempty }
dropTextObjectAccumulatorE :: EditorM ()
dropTextObjectAccumulatorE =
modifyStateE $ \s -> s { vsTextObjectAccumulator = mempty }
getRegisterE :: RegisterName -> EditorM (Maybe Register)
getRegisterE name = fmap (HM.lookup name . vsRegisterMap) getEditorDyn
setRegisterE :: RegisterName -> RegionStyle -> YiString -> EditorM ()
setRegisterE name style rope = do
rmap <- fmap vsRegisterMap getEditorDyn
let rmap' = HM.insert name (Register style rope) rmap
modifyStateE $ \state -> state { vsRegisterMap = rmap' }
normalizeCountE :: Maybe Int -> EditorM ()
normalizeCountE n = do
mcount <- getMaybeCountE
modifyStateE $ \s -> s {
vsCount = maybeMult mcount n
, vsAccumulator = Ev (showT . fromMaybe 1 $ maybeMult mcount n)
<> snd (splitCountedCommand . normalizeCount $ vsAccumulator s)
}
maybeMult :: Num a => Maybe a -> Maybe a -> Maybe a
maybeMult (Just a) (Just b) = Just (a * b)
maybeMult Nothing Nothing = Nothing
maybeMult a Nothing = a
maybeMult Nothing b = b
updateModeIndicatorE :: VimState -> EditorM ()
updateModeIndicatorE prevState = do
currentState <- getEditorDyn
let mode = vsMode currentState
prevMode = vsMode prevState
paste = vsPaste currentState
isRecording = isJust . vsCurrentMacroRecording $ currentState
prevRecording = isJust . vsCurrentMacroRecording $ prevState
when (mode /= prevMode || isRecording /= prevRecording) $ do
let modeName = case mode of
Insert _ -> "INSERT" <> if paste then " (paste) " else ""
InsertNormal -> "(insert)"
InsertVisual -> "(insert) VISUAL"
Replace -> "REPLACE"
Visual Block -> "VISUAL BLOCK"
Visual LineWise -> "VISUAL LINE"
Visual _ -> "VISUAL"
_ -> ""
decoratedModeName' = if T.null modeName
then mempty
else "-- " <> modeName <> " --"
decoratedModeName = if isRecording
then decoratedModeName' <> "recording"
else decoratedModeName'
setStatus ([decoratedModeName], defaultStyle)
saveInsertEventStringE :: EventString -> EditorM ()
saveInsertEventStringE evs = modifyStateE $ \s ->
s { vsOngoingInsertEvents = vsOngoingInsertEvents s <> evs }
resetActiveRegisterE :: EditorM ()
resetActiveRegisterE = modifyStateE $ \s -> s { vsActiveRegister = '\0' }