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

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

module Yi.Keymap.Vim.SearchMotionMap (defSearchMotionMap) where

import           Control.Monad            (replicateM_)
import           Data.Maybe               (fromMaybe)
import qualified Data.Text                as T (pack, unpack)
import           Yi.Buffer                (Direction (Backward, Forward), elemsB)
import           Yi.Editor                (getEditorDyn, withCurrentBuffer)
import           Yi.History               (historyFinish, historyPrefixSet)
import           Yi.Keymap.Vim.Common
import           Yi.Keymap.Vim.Search     (continueVimSearch)
import           Yi.Keymap.Vim.StateUtils (getCountE, switchModeE)
import           Yi.Keymap.Vim.Utils      (matchFromBool)
import qualified Yi.Rope                  as R (toText)
import           Yi.Search

defSearchMotionMap :: [VimBinding]
defSearchMotionMap :: [VimBinding]
defSearchMotionMap = [VimBinding
enterBinding, VimBinding
editBinding, VimBinding
exitBinding]

enterBinding :: VimBinding
enterBinding :: VimBinding
enterBinding = (EventString -> VimState -> MatchResult (EditorM RepeatToken))
-> VimBinding
VimBindingE EventString -> VimState -> MatchResult (EditorM RepeatToken)
forall a.
(Eq a, IsString a) =>
a -> VimState -> MatchResult (EditorM RepeatToken)
f
    where f :: a -> VimState -> MatchResult (EditorM RepeatToken)
f a
"<CR>" (VimState { vsMode :: VimState -> VimMode
vsMode = Search {}} ) = EditorM RepeatToken -> MatchResult (EditorM RepeatToken)
forall a. a -> MatchResult a
WholeMatch (EditorM RepeatToken -> MatchResult (EditorM RepeatToken))
-> EditorM RepeatToken -> MatchResult (EditorM RepeatToken)
forall a b. (a -> b) -> a -> b
$ do
              Search VimMode
prevMode Direction
dir <- (VimState -> VimMode) -> EditorM VimState -> EditorM VimMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap VimState -> VimMode
vsMode EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
              -- TODO: parse cmd into regex and flags
              EditorM ()
isearchFinishE
              EditorM ()
historyFinish
              VimMode -> EditorM ()
switchModeE VimMode
prevMode

              Int
count <- EditorM Int
getCountE
              EditorM (Maybe SearchExp)
getRegexE EditorM (Maybe SearchExp)
-> (Maybe SearchExp -> EditorM ()) -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                Maybe SearchExp
Nothing -> () -> EditorM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
                Just SearchExp
regex -> BufferM () -> EditorM ()
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
forall a b. (a -> b) -> a -> b
$
                  if Int
count Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 Bool -> Bool -> Bool
&& Direction
dir Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
Forward
                  then do
                    -- Workaround for isearchFinishE leaving cursor after match
                    (SearchExp, Direction) -> BufferM ()
continueVimSearch (SearchExp
regex, Direction
Backward)
                    (SearchExp, Direction) -> BufferM ()
continueVimSearch (SearchExp
regex, Direction
Forward)
                  else Int -> BufferM () -> BufferM ()
forall (m :: * -> *) a. Applicative m => Int -> m a -> m ()
replicateM_ (Int
count Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (BufferM () -> BufferM ()) -> BufferM () -> BufferM ()
forall a b. (a -> b) -> a -> b
$ (SearchExp, Direction) -> BufferM ()
continueVimSearch (SearchExp
regex, Direction
dir)
              case VimMode
prevMode of
                  Visual RegionStyle
_ -> RepeatToken -> EditorM RepeatToken
forall (m :: * -> *) a. Monad m => a -> m a
return RepeatToken
Continue
                  VimMode
_ -> RepeatToken -> EditorM RepeatToken
forall (m :: * -> *) a. Monad m => a -> m a
return RepeatToken
Finish
          f a
_ VimState
_ = MatchResult (EditorM RepeatToken)
forall a. MatchResult a
NoMatch

editBinding :: VimBinding
editBinding :: VimBinding
editBinding = (EventString -> VimState -> MatchResult (EditorM RepeatToken))
-> VimBinding
VimBindingE ([Char] -> VimState -> MatchResult (EditorM RepeatToken)
f ([Char] -> VimState -> MatchResult (EditorM RepeatToken))
-> (EventString -> [Char])
-> EventString
-> VimState
-> MatchResult (EditorM RepeatToken)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Char]
T.unpack (Text -> [Char]) -> (EventString -> Text) -> EventString -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EventString -> Text
_unEv)
  where
    f :: [Char] -> VimState -> MatchResult (EditorM RepeatToken)
f [Char]
evs (VimState { vsMode :: VimState -> VimMode
vsMode = Search {}} )
      = [Char] -> EditorM RepeatToken
action [Char]
evs EditorM RepeatToken
-> MatchResult () -> MatchResult (EditorM RepeatToken)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$
        Bool -> MatchResult ()
matchFromBool ([Char]
evs [Char] -> [[Char]] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ((Text, EditorM ()) -> [Char]) -> [(Text, EditorM ())] -> [[Char]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> [Char]
T.unpack (Text -> [Char])
-> ((Text, EditorM ()) -> Text) -> (Text, EditorM ()) -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text, EditorM ()) -> Text
forall a b. (a, b) -> a
fst) [(Text, EditorM ())]
binds
                       Bool -> Bool -> Bool
|| [Char] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
drop Int
1 [Char]
evs))
    f [Char]
_ VimState
_ = MatchResult (EditorM RepeatToken)
forall a. MatchResult a
NoMatch
    action :: [Char] -> EditorM RepeatToken
action [Char]
evs = do
      let evs' :: Text
evs' = [Char] -> Text
T.pack [Char]
evs
      EditorM () -> Maybe (EditorM ()) -> EditorM ()
forall a. a -> Maybe a -> a
fromMaybe (Text -> EditorM ()
isearchAddE Text
evs') (Text -> [(Text, EditorM ())] -> Maybe (EditorM ())
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
evs' [(Text, EditorM ())]
binds)
      BufferM YiString -> EditorM YiString
forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
withCurrentBuffer BufferM YiString
elemsB EditorM YiString -> (YiString -> EditorM ()) -> EditorM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> EditorM ()
historyPrefixSet (Text -> EditorM ())
-> (YiString -> Text) -> YiString -> EditorM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. YiString -> Text
R.toText
      RepeatToken -> EditorM RepeatToken
forall (m :: * -> *) a. Monad m => a -> m a
return RepeatToken
Continue

    binds :: [(Text, EditorM ())]
binds = [ (Text
"<BS>", EditorM ()
isearchDelE)
            , (Text
"<C-h>", EditorM ()
isearchDelE)
            , (Text
"<C-p>", Int -> EditorM ()
isearchHistory Int
1)
            , (Text
"<Up>", Int -> EditorM ()
isearchHistory Int
1)
            , (Text
"<C-n>", Int -> EditorM ()
isearchHistory (-Int
1))
            , (Text
"<Down>", Int -> EditorM ()
isearchHistory (-Int
1))
            , (Text
"<lt>", Text -> EditorM ()
isearchAddE Text
"<")
            ]

exitBinding :: VimBinding
exitBinding :: VimBinding
exitBinding = (EventString -> VimState -> MatchResult (EditorM RepeatToken))
-> VimBinding
VimBindingE EventString -> VimState -> MatchResult (EditorM RepeatToken)
forall p. p -> VimState -> MatchResult (EditorM RepeatToken)
f
    where f :: p -> VimState -> MatchResult (EditorM RepeatToken)
f p
_ (VimState { vsMode :: VimState -> VimMode
vsMode = Search {}} ) = EditorM RepeatToken -> MatchResult (EditorM RepeatToken)
forall a. a -> MatchResult a
WholeMatch (EditorM RepeatToken -> MatchResult (EditorM RepeatToken))
-> EditorM RepeatToken -> MatchResult (EditorM RepeatToken)
forall a b. (a -> b) -> a -> b
$ do
              Search VimMode
prevMode Direction
_dir <- (VimState -> VimMode) -> EditorM VimState -> EditorM VimMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap VimState -> VimMode
vsMode EditorM VimState
forall (m :: * -> *) a.
(MonadEditor m, YiVariable a, Default a, Functor m) =>
m a
getEditorDyn
              EditorM ()
isearchCancelE
              VimMode -> EditorM ()
switchModeE VimMode
prevMode
              RepeatToken -> EditorM RepeatToken
forall (m :: * -> *) a. Monad m => a -> m a
return RepeatToken
Drop
          f p
_ VimState
_ = MatchResult (EditorM RepeatToken)
forall a. MatchResult a
NoMatch