    {-# 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 = 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
        currentState <- EditorM VimState
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
        putEditorDyn $ f currentState

    resetCount :: VimState -> VimState
    resetCount :: VimState -> VimState
    resetCount VimState
    s = VimState
    s { vsCount = 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 a b. (a -> b) -> EditorM a -> EditorM b
    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
        currentState <- EditorM VimState
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
        return $! fromMaybe 1 (vsCount 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 = Just 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 = vsBindingAccumulator s <> eventToEventString 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 = vsAccumulator s <> eventToEventString 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 = vsTextObjectAccumulator s <> evs }

    flushAccumulatorE :: EditorM ()
    flushAccumulatorE :: EditorM ()
    flushAccumulatorE = do
        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 = EventString -> RepeatableAction
    stringToRepeatableAction EventString
    accum
        accum `seq` modifyStateE $ \VimState
    s ->
            VimState
    s { vsRepeatableAction = Just repeatableAction
              , vsAccumulator = mempty
              , vsCurrentMacroRecording = fmap (fmap (<> accum))
                                               (vsCurrentMacroRecording 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 = mempty
                 , vsCurrentMacroRecording = fmap (fmap (<> accum))
                                                  (vsCurrentMacroRecording 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 = 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 = mempty }

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

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

    normalizeCountE :: Maybe Int -> EditorM ()
    normalizeCountE :: Maybe Int -> EditorM ()
    normalizeCountE Maybe Int
    n = do
        mcount <- EditorM (Maybe Int)
    getMaybeCountE
        modifyStateE $ \VimState
    s -> VimState
    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 :: forall a. Num a => 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
      currentState <- EditorM VimState
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      let mode     = VimState -> VimMode
    vsMode VimState
    currentState
          prevMode = VimState -> VimMode
    vsMode VimState
    prevState
          paste = VimState -> Bool
    vsPaste VimState
    currentState
          isRecording   = Maybe (RegisterName, EventString) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe (RegisterName, EventString) -> Bool)
    -> (VimState -> Maybe (RegisterName, EventString))
    -> VimState
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . VimState -> Maybe (RegisterName, EventString)
    vsCurrentMacroRecording (VimState -> Bool) -> VimState -> Bool
    forall a b. (a -> b) -> a -> b
    $ VimState
    currentState
          prevRecording = Maybe (RegisterName, EventString) -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe (RegisterName, EventString) -> Bool)
    -> (VimState -> Maybe (RegisterName, EventString))
    -> VimState
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . VimState -> Maybe (RegisterName, EventString)
    vsCurrentMacroRecording (VimState -> Bool) -> VimState -> Bool
    forall a b. (a -> b) -> a -> b
    $ VimState
    prevState

      when (mode /= prevMode || isRecording /= prevRecording) $ do
          let modeName = case VimMode
    mode of
                Insert RegisterName
    _ -> 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' = 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 = if Bool
    isRecording
                                  then Text
    decoratedModeName' Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "recording"
                                  else Text
    decoratedModeName'

          setStatus ([decoratedModeName], 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 = vsOngoingInsertEvents s <> 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 = '\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 = Just 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