{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      :  Yi.Keymap.Vim.StateUtils
-- License     :  GPL-2
-- Maintainer  :  yi-devel@googlegroups.com
-- Stability   :  experimental
-- Portability :  portable

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
    , saveSubstitutionE
    , loadSubstitutionE
    ) where

import           Control.Monad            (when)
import qualified Data.HashMap.Strict      as HM (insert, lookup)
import           Data.Maybe               (fromMaybe, isJust)
import           Data.Monoid              ((<>))
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 :: VimMode -> VimState -> VimState
switchMode VimMode
mode VimState
state = VimState
state { vsMode :: VimMode
vsMode = VimMode
mode }

switchModeE :: VimMode -> EditorM ()
switchModeE :: VimMode -> EditorM ()
switchModeE VimMode
mode = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ VimMode -> VimState -> VimState
switchMode VimMode
mode

modifyStateE :: (VimState -> VimState) -> EditorM ()
modifyStateE :: (VimState -> VimState) -> EditorM ()
modifyStateE VimState -> VimState
f = do
    VimState
currentState <- EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    VimState -> EditorM ()
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Functor m) =>
a -> m ()
putEditorDyn (VimState -> EditorM ()) -> VimState -> EditorM ()
forall a b. (a -> b) -> a -> b
$ VimState -> VimState
f VimState
currentState

resetCount :: VimState -> VimState
resetCount :: VimState -> VimState
resetCount VimState
s = VimState
s { vsCount :: Maybe Int
vsCount = Maybe Int
forall a. Maybe a
Nothing }

resetCountE :: EditorM ()
resetCountE :: EditorM ()
resetCountE = (VimState -> VimState) -> EditorM ()
modifyStateE VimState -> VimState
resetCount

getMaybeCountE :: EditorM (Maybe Int)
getMaybeCountE :: EditorM (Maybe Int)
getMaybeCountE = (VimState -> Maybe Int) -> EditorM VimState -> EditorM (Maybe Int)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap VimState -> Maybe Int
vsCount EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn

getCountE :: EditorM Int
getCountE :: EditorM Int
getCountE = do
    VimState
currentState <- EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    Int -> EditorM Int
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> EditorM Int) -> Int -> EditorM Int
forall a b. (a -> b) -> a -> b
$! Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
1 (VimState -> Maybe Int
vsCount VimState
currentState)

setCountE :: Int -> EditorM ()
setCountE :: Int -> EditorM ()
setCountE Int
n = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s { vsCount :: Maybe Int
vsCount = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
n }

accumulateBindingEventE :: Event -> EditorM ()
accumulateBindingEventE :: Event -> EditorM ()
accumulateBindingEventE Event
e = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$
    \VimState
s -> VimState
s { vsBindingAccumulator :: EventString
vsBindingAccumulator = VimState -> EventString
vsBindingAccumulator VimState
s EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> Event -> EventString
eventToEventString Event
e }

accumulateEventE :: Event -> EditorM ()
accumulateEventE :: Event -> EditorM ()
accumulateEventE Event
e = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$
    \VimState
s -> VimState
s { vsAccumulator :: EventString
vsAccumulator = VimState -> EventString
vsAccumulator VimState
s EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> Event -> EventString
eventToEventString Event
e }

accumulateTextObjectEventE :: EventString -> EditorM ()
accumulateTextObjectEventE :: EventString -> EditorM ()
accumulateTextObjectEventE EventString
evs = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$
    \VimState
s -> VimState
s { vsTextObjectAccumulator :: EventString
vsTextObjectAccumulator = VimState -> EventString
vsTextObjectAccumulator VimState
s EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> EventString
evs }

flushAccumulatorE :: EditorM ()
flushAccumulatorE :: EditorM ()
flushAccumulatorE = do
    EventString
accum <- VimState -> EventString
vsAccumulator (VimState -> EventString)
-> EditorM VimState -> EditorM EventString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    let repeatableAction :: RepeatableAction
repeatableAction = EventString -> RepeatableAction
stringToRepeatableAction EventString
accum
    EventString
accum EventString -> EditorM () -> EditorM ()
`seq` (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s ->
        VimState
s { vsRepeatableAction :: Maybe RepeatableAction
vsRepeatableAction = RepeatableAction -> Maybe RepeatableAction
forall a. a -> Maybe a
Just RepeatableAction
repeatableAction
          , vsAccumulator :: EventString
vsAccumulator = EventString
forall a. Monoid a => a
mempty
          , vsCurrentMacroRecording :: Maybe (MacroName, EventString)
vsCurrentMacroRecording = ((MacroName, EventString) -> (MacroName, EventString))
-> Maybe (MacroName, EventString) -> Maybe (MacroName, EventString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((EventString -> EventString)
-> (MacroName, EventString) -> (MacroName, EventString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> EventString
accum))
                                           (VimState -> Maybe (MacroName, EventString)
vsCurrentMacroRecording VimState
s)
          }

dropAccumulatorE :: EditorM ()
dropAccumulatorE :: EditorM ()
dropAccumulatorE =
    (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s ->
        let accum :: EventString
accum = VimState -> EventString
vsAccumulator VimState
s
        in VimState
s { vsAccumulator :: EventString
vsAccumulator = EventString
forall a. Monoid a => a
mempty
             , vsCurrentMacroRecording :: Maybe (MacroName, EventString)
vsCurrentMacroRecording = ((MacroName, EventString) -> (MacroName, EventString))
-> Maybe (MacroName, EventString) -> Maybe (MacroName, EventString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((EventString -> EventString)
-> (MacroName, EventString) -> (MacroName, EventString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> EventString
accum))
                                              (VimState -> Maybe (MacroName, EventString)
vsCurrentMacroRecording VimState
s)
             }

dropBindingAccumulatorE :: EditorM ()
dropBindingAccumulatorE :: EditorM ()
dropBindingAccumulatorE =
  (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s { vsBindingAccumulator :: EventString
vsBindingAccumulator = EventString
forall a. Monoid a => a
mempty }

dropTextObjectAccumulatorE :: EditorM ()
dropTextObjectAccumulatorE :: EditorM ()
dropTextObjectAccumulatorE =
  (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s { vsTextObjectAccumulator :: EventString
vsTextObjectAccumulator = EventString
forall a. Monoid a => a
mempty }

getRegisterE :: RegisterName -> EditorM (Maybe Register)
getRegisterE :: MacroName -> EditorM (Maybe Register)
getRegisterE MacroName
name = (VimState -> Maybe Register)
-> EditorM VimState -> EditorM (Maybe Register)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (MacroName -> HashMap MacroName Register -> Maybe Register
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup MacroName
name (HashMap MacroName Register -> Maybe Register)
-> (VimState -> HashMap MacroName Register)
-> VimState
-> Maybe Register
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VimState -> HashMap MacroName Register
vsRegisterMap) EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn

setRegisterE :: RegisterName -> RegionStyle -> YiString -> EditorM ()
setRegisterE :: MacroName -> RegionStyle -> YiString -> EditorM ()
setRegisterE MacroName
name RegionStyle
style YiString
rope = do
    HashMap MacroName Register
rmap <- (VimState -> HashMap MacroName Register)
-> EditorM VimState -> EditorM (HashMap MacroName Register)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap VimState -> HashMap MacroName Register
vsRegisterMap EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
    let rmap' :: HashMap MacroName Register
rmap' = MacroName
-> Register
-> HashMap MacroName Register
-> HashMap MacroName Register
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HM.insert MacroName
name (RegionStyle -> YiString -> Register
Register RegionStyle
style YiString
rope) HashMap MacroName Register
rmap
    (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
state -> VimState
state { vsRegisterMap :: HashMap MacroName Register
vsRegisterMap = HashMap MacroName Register
rmap' }

normalizeCountE :: Maybe Int -> EditorM ()
normalizeCountE :: Maybe Int -> EditorM ()
normalizeCountE Maybe Int
n = do
    Maybe Int
mcount <- EditorM (Maybe Int)
getMaybeCountE
    (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s {
      vsCount :: Maybe Int
vsCount = Maybe Int -> Maybe Int -> Maybe Int
forall a. Num a => Maybe a -> Maybe a -> Maybe a
maybeMult Maybe Int
mcount Maybe Int
n
      , vsAccumulator :: EventString
vsAccumulator = Text -> EventString
Ev (Int -> Text
forall a. Show a => a -> Text
showT (Int -> Text) -> (Maybe Int -> Int) -> Maybe Int -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
1 (Maybe Int -> Text) -> Maybe Int -> Text
forall a b. (a -> b) -> a -> b
$ Maybe Int -> Maybe Int -> Maybe Int
forall a. Num a => Maybe a -> Maybe a -> Maybe a
maybeMult Maybe Int
mcount Maybe Int
n)
          EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> (Int, EventString) -> EventString
forall a b. (a, b) -> b
snd (EventString -> (Int, EventString)
splitCountedCommand (EventString -> (Int, EventString))
-> (EventString -> EventString)
-> EventString
-> (Int, EventString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventString -> EventString
normalizeCount (EventString -> (Int, EventString))
-> EventString -> (Int, EventString)
forall a b. (a -> b) -> a -> b
$ VimState -> EventString
vsAccumulator VimState
s)
      }

maybeMult :: Num a => Maybe a -> Maybe a -> Maybe a
maybeMult :: Maybe a -> Maybe a -> Maybe a
maybeMult (Just a
a) (Just a
b) = a -> Maybe a
forall a. a -> Maybe a
Just (a
a a -> a -> a
forall a. Num a => a -> a -> a
* a
b)
maybeMult Maybe a
Nothing  Maybe a
Nothing = Maybe a
forall a. Maybe a
Nothing
maybeMult Maybe a
a        Maybe a
Nothing = Maybe a
a
maybeMult Maybe a
Nothing  Maybe a
b       = Maybe a
b

updateModeIndicatorE :: VimState -> EditorM ()
updateModeIndicatorE :: VimState -> EditorM ()
updateModeIndicatorE VimState
prevState = do
  VimState
currentState <- EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
  let mode :: VimMode
mode     = VimState -> VimMode
vsMode VimState
currentState
      prevMode :: VimMode
prevMode = VimState -> VimMode
vsMode VimState
prevState
      paste :: Bool
paste = VimState -> Bool
vsPaste VimState
currentState
      isRecording :: Bool
isRecording   = Maybe (MacroName, EventString) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (MacroName, EventString) -> Bool)
-> (VimState -> Maybe (MacroName, EventString)) -> VimState -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VimState -> Maybe (MacroName, EventString)
vsCurrentMacroRecording (VimState -> Bool) -> VimState -> Bool
forall a b. (a -> b) -> a -> b
$ VimState
currentState
      prevRecording :: Bool
prevRecording = Maybe (MacroName, EventString) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (MacroName, EventString) -> Bool)
-> (VimState -> Maybe (MacroName, EventString)) -> VimState -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VimState -> Maybe (MacroName, EventString)
vsCurrentMacroRecording (VimState -> Bool) -> VimState -> Bool
forall a b. (a -> b) -> a -> b
$ VimState
prevState

  Bool -> EditorM () -> EditorM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (VimMode
mode VimMode -> VimMode -> Bool
forall a. Eq a => a -> a -> Bool
/= VimMode
prevMode Bool -> Bool -> Bool
|| Bool
isRecording Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
/= Bool
prevRecording) (EditorM () -> EditorM ()) -> EditorM () -> EditorM ()
forall a b. (a -> b) -> a -> b
$ do
      let modeName :: Text
modeName = case VimMode
mode of
            Insert MacroName
_ -> Text
"INSERT" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> if Bool
paste then Text
" (paste) " else Text
""
            VimMode
InsertNormal -> Text
"(insert)"
            VimMode
InsertVisual -> Text
"(insert) VISUAL"
            VimMode
Replace -> Text
"REPLACE"
            Visual RegionStyle
Block -> Text
"VISUAL BLOCK"
            Visual RegionStyle
LineWise -> Text
"VISUAL LINE"
            Visual RegionStyle
_ -> Text
"VISUAL"
            VimMode
_ -> Text
""

          decoratedModeName' :: Text
decoratedModeName' = if Text -> Bool
T.null Text
modeName
                              then Text
forall a. Monoid a => a
mempty
                              else Text
"-- " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
modeName Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" --"
          decoratedModeName :: Text
decoratedModeName = if Bool
isRecording
                              then Text
decoratedModeName' Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"recording"
                              else Text
decoratedModeName'

      Status -> EditorM ()
forall (m :: * -> *). MonadEditor m => Status -> m ()
setStatus ([Text
decoratedModeName], StyleName
defaultStyle)

saveInsertEventStringE :: EventString -> EditorM ()
saveInsertEventStringE :: EventString -> EditorM ()
saveInsertEventStringE EventString
evs = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s ->
  VimState
s { vsOngoingInsertEvents :: EventString
vsOngoingInsertEvents = VimState -> EventString
vsOngoingInsertEvents VimState
s EventString -> EventString -> EventString
forall a. Semigroup a => a -> a -> a
<> EventString
evs }

resetActiveRegisterE :: EditorM ()
resetActiveRegisterE :: EditorM ()
resetActiveRegisterE = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s { vsActiveRegister :: MacroName
vsActiveRegister = MacroName
'\0' }

saveSubstitutionE :: Substitution -> EditorM ()
saveSubstitutionE :: Substitution -> EditorM ()
saveSubstitutionE Substitution
sub = (VimState -> VimState) -> EditorM ()
modifyStateE ((VimState -> VimState) -> EditorM ())
-> (VimState -> VimState) -> EditorM ()
forall a b. (a -> b) -> a -> b
$ \VimState
s -> VimState
s { vsLastSubstitution :: Maybe Substitution
vsLastSubstitution = Substitution -> Maybe Substitution
forall a. a -> Maybe a
Just Substitution
sub }

loadSubstitutionE :: EditorM (Maybe Substitution)
loadSubstitutionE :: EditorM (Maybe Substitution)
loadSubstitutionE = VimState -> Maybe Substitution
vsLastSubstitution (VimState -> Maybe Substitution)
-> EditorM VimState -> EditorM (Maybe Substitution)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn