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

    -- |
    -- Module      :  Yi.Search
    -- License     :  GPL-2
    -- Maintainer  :  yi-devel@googlegroups.com
    -- Stability   :  experimental
    -- Portability :  portable
    --
    -- Search/Replace functions

    module Yi.Search (
            setRegexE,      -- :: SearchExp -> EditorM ()
            resetRegexE,    -- :: EditorM ()
            getRegexE,      -- :: EditorM (Maybe SearchExp)
            SearchMatch,
            SearchResult(..),
            SearchOption(..),
            doSearch,            -- :: (Maybe String) -> [SearchOption]
                                -- -> Direction -> YiM ()
            searchInit,        -- :: String
                                -- -> [SearchOption]
                                -- -> IO SearchExp
            continueSearch,          -- :: SearchExp
                                -- -> IO SearchResult
            makeSimpleSearch,

            -- * Batch search-replace
            searchReplaceRegionB,
            searchReplaceSelectionB,
            replaceString,
            searchAndRepRegion,
            searchAndRepRegion0,
            searchAndRepUnit, -- :: String -> String -> Bool -> TextUnit -> EditorM Bool

            -- * Incremental Search
            isearchInitE,
            isearchIsEmpty,
            isearchAddE,
            isearchPrevE,
            isearchNextE,
            isearchWordE,
            isearchHistory,
            isearchDelE,
            isearchCancelE,
            isearchFinishE,
            isearchCancelWithE,
            isearchFinishWithE,

            -- * Replace
            qrNext,
            qrReplaceAll,
            qrReplaceOne,
            qrFinish
                     ) where

    import           Lens.Micro.Platform          ((.=))
    import           Control.Monad       (void, when)
    import           Data.Binary         (Binary, get, put)
    import           Data.Char           (isAlpha, isUpper)
    import           Data.Default        (Default, def)
    import           Data.Maybe          (listToMaybe)
    import           Data.Monoid         ((<>))
    import qualified Data.Text           as T (Text, any, break, empty, length, null, takeWhile, unpack)
    import qualified Data.Text.Encoding  as E (decodeUtf8, encodeUtf8)
    import           Data.Typeable       (Typeable)
    import           Yi.Buffer
    import           Yi.Editor
    import           Yi.History          (historyFinishGen, historyMoveGen, historyStartGen)
    import           Yi.Regex            (SearchOption(..), makeSearchOptsM, emptyRegex, SearchExp(..))
    import qualified Yi.Rope             as R (YiString, null, toString, toText)
    import           Yi.Search.Internal  (getRegexE, resetRegexE, setRegexE)
    import           Yi.String           (showT)
    import           Yi.Types            (YiVariable)
    import           Yi.Utils            (fst3)
    import           Yi.Window           (Window)

    -- ---------------------------------------------------------------------
    --
    -- | Global searching. Search for regex and move point to that position.
    -- @Nothing@ means reuse the last regular expression. @Just s@ means use
    -- @s@ as the new regular expression. Direction of search can be
    -- specified as either @Backward@ or @Forward@ (forwards in the buffer).
    -- Arguments to modify the compiled regular expression can be supplied
    -- as well.
    --

    type SearchMatch = Region
    data SearchResult = PatternFound
                      | PatternNotFound
                      | SearchWrapped
      deriving SearchResult -> SearchResult -> Bool
    (SearchResult -> SearchResult -> Bool)
    -> (SearchResult -> SearchResult -> Bool) -> Eq SearchResult
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: SearchResult -> SearchResult -> Bool
    == :: SearchResult -> SearchResult -> Bool
    $c/= :: SearchResult -> SearchResult -> Bool
    /= :: SearchResult -> SearchResult -> Bool
    Eq

    doSearch :: Maybe String        -- ^ @Nothing@ means used previous
                                    -- pattern, if any. Complain otherwise.
                                    -- Use getRegexE to check for previous patterns
            -> [SearchOption]            -- ^ Flags to modify the compiled regex
            -> Direction            -- ^ @Backward@ or @Forward@
            -> EditorM SearchResult

    doSearch :: Maybe String -> [SearchOption] -> Direction -> EditorM SearchResult
    doSearch (Just String
    re) [SearchOption]
    fs Direction
    d = String
    -> Direction -> [SearchOption] -> EditorM (SearchExp, Direction)
    searchInit String
    re Direction
    d [SearchOption]
    fs EditorM (SearchExp, Direction)
    -> ((SearchExp, Direction) -> EditorM SearchResult)
    -> EditorM SearchResult
    forall a b. EditorM a -> (a -> EditorM b) -> EditorM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= BufferM SearchResult -> EditorM SearchResult
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM SearchResult -> EditorM SearchResult)
    -> ((SearchExp, Direction) -> BufferM SearchResult)
    -> (SearchExp, Direction)
    -> EditorM SearchResult
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (SearchExp, Direction) -> BufferM SearchResult
    continueSearch
    doSearch Maybe String
    Nothing   [SearchOption]
    _  Direction
    d = do
      mre <- EditorM (Maybe SearchExp)
    getRegexE
      case mre of
        Maybe SearchExp
    Nothing -> String -> EditorM SearchResult
    forall a. String -> EditorM a
    forall (m :: * -> *) a. MonadFail m => String -> m a
    fail String
    "No previous search pattern" -- NB
        Just SearchExp
    r  -> BufferM SearchResult -> EditorM SearchResult
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer ((SearchExp, Direction) -> BufferM SearchResult
    continueSearch (SearchExp
    r,Direction
    d))

    -- | Set up a search.
    searchInit :: String -> Direction -> [SearchOption] -> EditorM (SearchExp, Direction)
    searchInit :: String
    -> Direction -> [SearchOption] -> EditorM (SearchExp, Direction)
    searchInit String
    re Direction
    d [SearchOption]
    fs = do
        let Right SearchExp
    c_re = [SearchOption] -> String -> Either String SearchExp
    makeSearchOptsM [SearchOption]
    fs String
    re
        SearchExp -> EditorM ()
    setRegexE SearchExp
    c_re
        (Direction -> Identity Direction) -> Editor -> Identity Editor
    Lens' Editor Direction
    searchDirectionA ((Direction -> Identity Direction) -> Editor -> Identity Editor)
    -> Direction -> EditorM ()
    forall s (m :: * -> *) a b.
    MonadState s m =>
    ASetter s s a b -> b -> m ()
    .= Direction
    d
        (SearchExp, Direction) -> EditorM (SearchExp, Direction)
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (SearchExp
    c_re,Direction
    d)

    -- | Do a search, placing cursor at first char of pattern, if found.
    -- Keymaps may implement their own regex language. How do we provide for this?
    -- Also, what's happening with ^ not matching sol?
    continueSearch :: (SearchExp, Direction) -> BufferM SearchResult
    continueSearch :: (SearchExp, Direction) -> BufferM SearchResult
    continueSearch (SearchExp
    c_re, Direction
    dir) = do
      mp <- BufferM (Maybe (Either Region Region))
    -> BufferM (Maybe (Either Region Region))
    forall a. BufferM a -> BufferM a
    savingPointB (BufferM (Maybe (Either Region Region))
     -> BufferM (Maybe (Either Region Region)))
    -> BufferM (Maybe (Either Region Region))
    -> BufferM (Maybe (Either Region Region))
    forall a b. (a -> b) -> a -> b
    $ do
        TextUnit -> Direction -> BufferM ()
    moveB TextUnit
    Character Direction
    dir  -- start immed. after cursor
        rs <- Direction -> SearchExp -> BufferM [Region]
    regexB Direction
    dir SearchExp
    c_re
        moveB Document (reverseDir dir) -- wrap around
        ls <- regexB dir c_re
        return $ listToMaybe $ fmap Right rs ++ fmap Left ls
      maybe (return ()) (moveTo . regionStart . either id id) mp
      return $ f mp
        where
            f :: Maybe (Either a b) -> SearchResult
    f (Just (Right b
    _)) = SearchResult
    PatternFound
            f (Just (Left  a
    _)) = SearchResult
    SearchWrapped
            f Maybe (Either a b)
    Nothing          = SearchResult
    PatternNotFound

    ------------------------------------------------------------------------
    -- Batch search and replace
    --

    -- | Search and Replace all within the current region.
    -- Note the region is the final argument since we might perform
    -- the same search and replace over multiple regions however we are
    -- unlikely to perform several search and replaces over the same region
    -- since the first such may change the bounds of the region.
    searchReplaceRegionB :: R.YiString -- ^ The string to search for
                         -> R.YiString -- ^ The string to replace it with
                         -> Region -- ^ The region to perform this over
                         -> BufferM Int
    searchReplaceRegionB :: YiString -> YiString -> Region -> BufferM Int
    searchReplaceRegionB YiString
    from YiString
    to =
      SearchExp -> YiString -> Bool -> Region -> BufferM Int
    searchAndRepRegion0 (YiString -> SearchExp
    makeSimpleSearch YiString
    from) YiString
    to Bool
    True

    -- | Peform a search and replace on the selection
    searchReplaceSelectionB :: R.YiString  -- ^ text to search for
                            -> R.YiString  -- ^ text to replace it with
                            -> BufferM Int
    searchReplaceSelectionB :: YiString -> YiString -> BufferM Int
    searchReplaceSelectionB YiString
    from YiString
    to =
      BufferM Region
    getSelectRegionB BufferM Region -> (Region -> BufferM Int) -> BufferM Int
    forall a b. BufferM a -> (a -> BufferM b) -> BufferM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= YiString -> YiString -> Region -> BufferM Int
    searchReplaceRegionB YiString
    from YiString
    to

    -- | Replace a string by another everywhere in the document
    replaceString :: R.YiString -> R.YiString -> BufferM Int
    replaceString :: YiString -> YiString -> BufferM Int
    replaceString YiString
    a YiString
    b = TextUnit -> BufferM Region
    regionOfB TextUnit
    Document BufferM Region -> (Region -> BufferM Int) -> BufferM Int
    forall a b. BufferM a -> (a -> BufferM b) -> BufferM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= YiString -> YiString -> Region -> BufferM Int
    searchReplaceRegionB YiString
    a YiString
    b

    ------------------------------------------------------------------------
    -- | Search and replace in the given region.
    --
    -- If the input boolean is True, then the replace is done globally,
    -- otherwise only the first match is replaced. Returns the number of
    -- replacements done.
    searchAndRepRegion0 :: SearchExp -> R.YiString -> Bool -> Region -> BufferM Int
    searchAndRepRegion0 :: SearchExp -> YiString -> Bool -> Region -> BufferM Int
    searchAndRepRegion0 SearchExp
    c_re YiString
    str Bool
    globally Region
    region = do
        mp <- (if Bool
    globally then [Region] -> [Region]
    forall a. a -> a
    id else Int -> [Region] -> [Region]
    forall a. Int -> [a] -> [a]
    take Int
    1) ([Region] -> [Region]) -> BufferM [Region] -> BufferM [Region]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> SearchExp -> Region -> BufferM [Region]
    regexRegionB SearchExp
    c_re Region
    region  -- find the regex
        -- mp' is a maybe not reversed version of mp, the goal
        -- is to avoid replaceRegionB to mess up the next regions.
        -- So we start from the end.
        let mp' = Direction -> [Region] -> [Region]
    forall a. Direction -> [a] -> [a]
    mayReverse (Direction -> Direction
    reverseDir (Direction -> Direction) -> Direction -> Direction
    forall a b. (a -> b) -> a -> b
    $ Region -> Direction
    regionDirection Region
    region) [Region]
    mp
        mapM_ (`replaceRegionB` str) mp'
        return (length mp)

    searchAndRepRegion :: R.YiString -> R.YiString -> Bool -> Region -> EditorM Bool
    searchAndRepRegion :: YiString -> YiString -> Bool -> Region -> EditorM Bool
    searchAndRepRegion YiString
    s YiString
    str Bool
    globally Region
    region = case YiString -> Bool
    R.null YiString
    s of
      Bool
    False -> Bool -> EditorM Bool
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False
      Bool
    True -> do
        let c_re :: SearchExp
    c_re = YiString -> SearchExp
    makeSimpleSearch YiString
    s
        SearchExp -> EditorM ()
    setRegexE SearchExp
    c_re     -- store away for later use
        (Direction -> Identity Direction) -> Editor -> Identity Editor
    Lens' Editor Direction
    searchDirectionA ((Direction -> Identity Direction) -> Editor -> Identity Editor)
    -> Direction -> EditorM ()
    forall s (m :: * -> *) a b.
    MonadState s m =>
    ASetter s s a b -> b -> m ()
    .= Direction
    Forward
        BufferM Bool -> EditorM Bool
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM Bool -> EditorM Bool) -> BufferM Bool -> EditorM Bool
    forall a b. (a -> b) -> a -> b
    $ (Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    0) (Int -> Bool) -> BufferM Int -> BufferM Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> SearchExp -> YiString -> Bool -> Region -> BufferM Int
    searchAndRepRegion0 SearchExp
    c_re YiString
    str Bool
    globally Region
    region

    ------------------------------------------------------------------------
    -- | Search and replace in the region defined by the given unit.
    -- The rest is as in 'searchAndRepRegion'.
    searchAndRepUnit :: R.YiString -> R.YiString -> Bool -> TextUnit -> EditorM Bool
    searchAndRepUnit :: YiString -> YiString -> Bool -> TextUnit -> EditorM Bool
    searchAndRepUnit YiString
    re YiString
    str Bool
    g TextUnit
    unit =
      BufferM Region -> EditorM Region
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (TextUnit -> BufferM Region
    regionOfB TextUnit
    unit) EditorM Region -> (Region -> EditorM Bool) -> EditorM Bool
    forall a b. EditorM a -> (a -> EditorM b) -> EditorM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= YiString -> YiString -> Bool -> Region -> EditorM Bool
    searchAndRepRegion YiString
    re YiString
    str Bool
    g

    --------------------------
    -- Incremental search

    newtype Isearch = Isearch [(T.Text, Region, Direction)]
      deriving (Typeable, Int -> Isearch -> ShowS
    [Isearch] -> ShowS
    Isearch -> String
    (Int -> Isearch -> ShowS)
    -> (Isearch -> String) -> ([Isearch] -> ShowS) -> Show Isearch
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Isearch -> ShowS
    showsPrec :: Int -> Isearch -> ShowS
    $cshow :: Isearch -> String
    show :: Isearch -> String
    $cshowList :: [Isearch] -> ShowS
    showList :: [Isearch] -> ShowS
    Show)

    instance Binary Isearch where
      put :: Isearch -> Put
    put (Isearch [(Text, Region, Direction)]
    ts) = [(ByteString, Region, Direction)] -> Put
    forall t. Binary t => t -> Put
    put ((Text -> ByteString)
    -> [(Text, Region, Direction)] -> [(ByteString, Region, Direction)]
    forall a d b c. (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    map3 Text -> ByteString
    E.encodeUtf8 [(Text, Region, Direction)]
    ts)
      get :: Get Isearch
    get = [(Text, Region, Direction)] -> Isearch
    Isearch ([(Text, Region, Direction)] -> Isearch)
    -> ([(ByteString, Region, Direction)]
        -> [(Text, Region, Direction)])
    -> [(ByteString, Region, Direction)]
    -> Isearch
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ByteString -> Text)
    -> [(ByteString, Region, Direction)] -> [(Text, Region, Direction)]
    forall a d b c. (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    map3 ByteString -> Text
    E.decodeUtf8 ([(ByteString, Region, Direction)] -> Isearch)
    -> Get [(ByteString, Region, Direction)] -> Get Isearch
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Get [(ByteString, Region, Direction)]
    forall t. Binary t => Get t
    get

    map3 :: (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    map3 :: forall a d b c. (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    map3 a -> d
    _ [] = []
    map3 a -> d
    f ((a
    a, b
    b, c
    c):[(a, b, c)]
    xs) = (a -> d
    f a
    a, b
    b, c
    c) (d, b, c) -> [(d, b, c)] -> [(d, b, c)]
    forall a. a -> [a] -> [a]
    : (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    forall a d b c. (a -> d) -> [(a, b, c)] -> [(d, b, c)]
    map3 a -> d
    f [(a, b, c)]
    xs

    -- This contains: (string currently searched, position where we
    -- searched it, direction, overlay for highlighting searched text)

    -- Note that this info cannot be embedded in the Keymap state: the state
    -- modification can depend on the state of the editor.

    instance Default Isearch where
        def :: Isearch
    def = [(Text, Region, Direction)] -> Isearch
    Isearch []

    instance YiVariable Isearch

    isearchInitE :: Direction -> EditorM ()
    isearchInitE :: Direction -> EditorM ()
    isearchInitE Direction
    dir = do
      Text -> EditorM ()
    historyStartGen Text
    iSearch
      p <- BufferM Point -> EditorM Point
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer BufferM Point
    pointB
      putEditorDyn (Isearch [(T.empty ,mkRegion p p, dir)])
      printMsg "I-search: "

    isearchIsEmpty :: EditorM Bool
    isearchIsEmpty :: EditorM Bool
    isearchIsEmpty = do
      Isearch s <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      return . not . T.null . fst3 $ head s

    isearchAddE :: T.Text -> EditorM ()
    isearchAddE :: Text -> EditorM ()
    isearchAddE Text
    inc = (Text -> Text) -> EditorM ()
    isearchFunE (Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    inc)

    -- | Create a SearchExp that matches exactly its argument
    makeSimpleSearch :: R.YiString -> SearchExp
    makeSimpleSearch :: YiString -> SearchExp
    makeSimpleSearch YiString
    s = SearchExp
    se
        where Right SearchExp
    se = [SearchOption] -> String -> Either String SearchExp
    makeSearchOptsM [SearchOption
    QuoteRegex] (YiString -> String
    R.toString YiString
    s)

    makeISearch :: T.Text -> SearchExp
    makeISearch :: Text -> SearchExp
    makeISearch Text
    s = case [SearchOption] -> String -> Either String SearchExp
    makeSearchOptsM [SearchOption]
    opts (Text -> String
    T.unpack Text
    s) of
                      Left String
    _ -> String -> Regex -> Regex -> [SearchOption] -> SearchExp
    SearchExp (Text -> String
    T.unpack Text
    s) Regex
    emptyRegex Regex
    emptyRegex []
                      Right SearchExp
    search -> SearchExp
    search
       where opts :: [SearchOption]
    opts = SearchOption
    QuoteRegex SearchOption -> [SearchOption] -> [SearchOption]
    forall a. a -> [a] -> [a]
    : if (Char -> Bool) -> Text -> Bool
    T.any Char -> Bool
    isUpper Text
    s then [] else [SearchOption
    IgnoreCase]

    isearchFunE :: (T.Text -> T.Text) -> EditorM ()
    isearchFunE :: (Text -> Text) -> EditorM ()
    isearchFunE Text -> Text
    fun = do
      Isearch s <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      case s of
        [(Text, Region, Direction)
    _] -> EditorM ()
    resetRegexE
        [(Text, Region, Direction)]
    _ -> () -> EditorM ()
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
      let (previous,p0,direction) = head s
          current = Text -> Text
    fun Text
    previous
          srch = Text -> SearchExp
    makeISearch Text
    current
      printMsg $ "I-search: " <> current
      setRegexE srch
      prevPoint <- withCurrentBuffer pointB
      matches <- withCurrentBuffer $ do
          moveTo $ regionStart p0
          when (direction == Backward) $
             moveN $ T.length current
          regexB direction srch

      let onSuccess Region
    p = do BufferM () -> m ()
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM () -> m ()) -> BufferM () -> m ()
    forall a b. (a -> b) -> a -> b
    $ Point -> BufferM ()
    moveTo (Region -> Point
    regionEnd Region
    p)
                           Isearch -> m ()
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Functor m) =>
    a -> m ()
    putEditorDyn (Isearch -> m ()) -> Isearch -> m ()
    forall a b. (a -> b) -> a -> b
    $ [(Text, Region, Direction)] -> Isearch
    Isearch ((Text
    current, Region
    p, Direction
    direction) (Text, Region, Direction)
    -> [(Text, Region, Direction)] -> [(Text, Region, Direction)]
    forall a. a -> [a] -> [a]
    : [(Text, Region, Direction)]
    s)

      case matches of
        (Region
    p:[Region]
    _) -> Region -> EditorM ()
    forall {m :: * -> *}. MonadEditor m => Region -> m ()
    onSuccess Region
    p
        [] -> do matchesAfterWrap <- BufferM [Region] -> EditorM [Region]
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM [Region] -> EditorM [Region])
    -> BufferM [Region] -> EditorM [Region]
    forall a b. (a -> b) -> a -> b
    $ do
                   case Direction
    direction of
                     Direction
    Forward -> Point -> BufferM ()
    moveTo Point
    0
                     Direction
    Backward -> do
                       bufferLength <- BufferM Point
    sizeB
                       moveTo bufferLength
                   Direction -> SearchExp -> BufferM [Region]
    regexB Direction
    direction SearchExp
    srch

                 case matchesAfterWrap of
                   (Region
    p:[Region]
    _) -> Region -> EditorM ()
    forall {m :: * -> *}. MonadEditor m => Region -> m ()
    onSuccess Region
    p
                   [] -> do BufferM () -> EditorM ()
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Point -> BufferM ()
    moveTo Point
    prevPoint -- go back to where we were
                            Isearch -> EditorM ()
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Functor m) =>
    a -> m ()
    putEditorDyn (Isearch -> EditorM ()) -> Isearch -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ [(Text, Region, Direction)] -> Isearch
    Isearch ((Text
    current, Region
    p0, Direction
    direction) (Text, Region, Direction)
    -> [(Text, Region, Direction)] -> [(Text, Region, Direction)]
    forall a. a -> [a] -> [a]
    : [(Text, Region, Direction)]
    s)
                            Text -> EditorM ()
    forall (m :: * -> *). MonadEditor m => Text -> m ()
    printMsg (Text -> EditorM ()) -> Text -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Text
    "Failing I-search: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    current

    isearchDelE :: EditorM ()
    isearchDelE :: EditorM ()
    isearchDelE = do
      Isearch s <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      case s of
        ((Text, Region, Direction)
    _:(Text
    text,Region
    p,Direction
    dir):[(Text, Region, Direction)]
    rest) -> do
          BufferM () -> EditorM ()
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $
            Point -> BufferM ()
    moveTo (Point -> BufferM ()) -> Point -> BufferM ()
    forall a b. (a -> b) -> a -> b
    $ Region -> Point
    regionEnd Region
    p
          Isearch -> EditorM ()
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Functor m) =>
    a -> m ()
    putEditorDyn (Isearch -> EditorM ()) -> Isearch -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ [(Text, Region, Direction)] -> Isearch
    Isearch ((Text
    text,Region
    p,Direction
    dir)(Text, Region, Direction)
    -> [(Text, Region, Direction)] -> [(Text, Region, Direction)]
    forall a. a -> [a] -> [a]
    :[(Text, Region, Direction)]
    rest)
          SearchExp -> EditorM ()
    setRegexE (SearchExp -> EditorM ()) -> SearchExp -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Text -> SearchExp
    makeISearch Text
    text
          Text -> EditorM ()
    forall (m :: * -> *). MonadEditor m => Text -> m ()
    printMsg (Text -> EditorM ()) -> Text -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Text
    "I-search: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    text
        [(Text, Region, Direction)]
    _ -> () -> EditorM ()
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return () -- if the searched string is empty, don't try to remove chars from it.

    isearchHistory :: Int -> EditorM ()
    isearchHistory :: Int -> EditorM ()
    isearchHistory Int
    delta = do
      Isearch ((current,_p0,_dir):_) <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      h <- historyMoveGen iSearch delta (return current)
      isearchFunE (const h)

    isearchPrevE :: EditorM ()
    isearchPrevE :: EditorM ()
    isearchPrevE = Direction -> EditorM ()
    isearchNext0 Direction
    Backward

    isearchNextE :: EditorM ()
    isearchNextE :: EditorM ()
    isearchNextE = Direction -> EditorM ()
    isearchNext0 Direction
    Forward

    isearchNext0 :: Direction -> EditorM ()
    isearchNext0 :: Direction -> EditorM ()
    isearchNext0 Direction
    newDir = do
      Isearch ((current,_p0,_dir):_rest) <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      if T.null current
        then isearchHistory 1
        else isearchNext newDir

    isearchNext :: Direction -> EditorM ()
    isearchNext :: Direction -> EditorM ()
    isearchNext Direction
    direction = do
      Isearch ((current, p0, _dir) : rest) <- EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn
      withCurrentBuffer $ moveTo (regionStart p0 + startOfs)
      mp <- withCurrentBuffer $
        regexB direction (makeISearch current)
      case mp of
        [] -> do
          endPoint <- BufferM Point -> EditorM Point
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM Point -> EditorM Point) -> BufferM Point -> EditorM Point
    forall a b. (a -> b) -> a -> b
    $ do
                  Point -> BufferM ()
    moveTo (Region -> Point
    regionEnd Region
    p0) -- revert to offset we were before.
                  BufferM Point
    sizeB
          printMsg "isearch: end of document reached"
          let wrappedOfs = case Direction
    direction of
                             Direction
    Forward -> Point -> Point -> Region
    mkRegion Point
    0 Point
    0
                             Direction
    Backward -> Point -> Point -> Region
    mkRegion Point
    endPoint Point
    endPoint
          putEditorDyn $ Isearch ((current,wrappedOfs,direction):rest) -- prepare to wrap around.
        (Region
    p:[Region]
    _) -> do
          BufferM () -> EditorM ()
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $
            Point -> BufferM ()
    moveTo (Region -> Point
    regionEnd Region
    p)
          Text -> EditorM ()
    forall (m :: * -> *). MonadEditor m => Text -> m ()
    printMsg (Text -> EditorM ()) -> Text -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Text
    "I-search: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    current
          Isearch -> EditorM ()
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Functor m) =>
    a -> m ()
    putEditorDyn (Isearch -> EditorM ()) -> Isearch -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ [(Text, Region, Direction)] -> Isearch
    Isearch ((Text
    current,Region
    p,Direction
    direction)(Text, Region, Direction)
    -> [(Text, Region, Direction)] -> [(Text, Region, Direction)]
    forall a. a -> [a] -> [a]
    :[(Text, Region, Direction)]
    rest)
     where startOfs :: Point
    startOfs = case Direction
    direction of
                          Direction
    Forward  ->  Point
    1
                          Direction
    Backward -> -Point
    1

    isearchWordE :: EditorM ()
    isearchWordE :: EditorM ()
    isearchWordE = do
       -- add maximum 32 chars at a time.
      text <- YiString -> Text
    R.toText (YiString -> Text) -> EditorM YiString -> EditorM Text
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> BufferM YiString -> EditorM YiString
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM Point
    pointB BufferM Point -> (Point -> BufferM YiString) -> BufferM YiString
    forall a b. BufferM a -> (a -> BufferM b) -> BufferM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= Int -> Point -> BufferM YiString
    nelemsB Int
    32)

      let (prefix, rest) = T.break isAlpha text
          word = (Char -> Bool) -> Text -> Text
    T.takeWhile Char -> Bool
    isAlpha Text
    rest
      isearchAddE $ prefix <> word

    -- | Successfully finish a search. Also see 'isearchFinishWithE'.
    isearchFinishE :: EditorM ()
    isearchFinishE :: EditorM ()
    isearchFinishE = Bool -> EditorM ()
    isearchEnd Bool
    True

    -- | Cancel a search. Also see 'isearchCancelWithE'.
    isearchCancelE :: EditorM ()
    isearchCancelE :: EditorM ()
    isearchCancelE = Bool -> EditorM ()
    isearchEnd Bool
    False

    -- | Wrapper over 'isearchEndWith' that passes through the action and
    -- accepts the search as successful (i.e. when the user wants to stay
    -- at the result).
    isearchFinishWithE :: EditorM a -> EditorM ()
    isearchFinishWithE :: forall a. EditorM a -> EditorM ()
    isearchFinishWithE EditorM a
    act = EditorM a -> Bool -> EditorM ()
    forall a. EditorM a -> Bool -> EditorM ()
    isearchEndWith EditorM a
    act Bool
    True

    -- | Wrapper over 'isearchEndWith' that passes through the action and
    -- marks the search as unsuccessful (i.e. when the user wants to
    -- jump back to where the search started).
    isearchCancelWithE :: EditorM a -> EditorM ()
    isearchCancelWithE :: forall a. EditorM a -> EditorM ()
    isearchCancelWithE EditorM a
    act = EditorM a -> Bool -> EditorM ()
    forall a. EditorM a -> Bool -> EditorM ()
    isearchEndWith EditorM a
    act Bool
    False

    iSearch :: T.Text
    iSearch :: Text
    iSearch = Text
    "isearch"

    -- | Editor action describing how to end finish incremental search.
    -- The @act@ parameter allows us to specify an extra action to run
    -- before finishing up the search. For Vim, we don't want to do
    -- anything so we use 'isearchEnd' which just does nothing. For emacs,
    -- we want to cancel highlighting and stay where we are.
    isearchEndWith :: EditorM a -> Bool -> EditorM ()
    isearchEndWith :: forall a. EditorM a -> Bool -> EditorM ()
    isearchEndWith EditorM a
    act Bool
    accept = EditorM Isearch
    forall (m :: * -> *) a.
    (MonadEditor m, YiVariable a, Default a, Functor m) =>
    m a
    getEditorDyn EditorM Isearch -> (Isearch -> EditorM ()) -> EditorM ()
    forall a b. EditorM a -> (a -> EditorM b) -> EditorM b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
      Isearch [] -> () -> EditorM ()
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
      Isearch s :: [(Text, Region, Direction)]
    s@((Text
    lastSearched, Region
    _, Direction
    dir):[(Text, Region, Direction)]
    _) -> do
        let (Text
    _,Region
    p0,Direction
    _) = [(Text, Region, Direction)] -> (Text, Region, Direction)
    forall a. HasCallStack => [a] -> a
    last [(Text, Region, Direction)]
    s
        Text -> EditorM Text -> EditorM ()
    historyFinishGen Text
    iSearch (Text -> EditorM Text
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Text
    lastSearched)
        (Direction -> Identity Direction) -> Editor -> Identity Editor
    Lens' Editor Direction
    searchDirectionA ((Direction -> Identity Direction) -> Editor -> Identity Editor)
    -> Direction -> EditorM ()
    forall s (m :: * -> *) a b.
    MonadState s m =>
    ASetter s s a b -> b -> m ()
    .= Direction
    dir
        if Bool
    accept
           then do EditorM a -> EditorM ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void EditorM a
    act
                   Text -> EditorM ()
    forall (m :: * -> *). MonadEditor m => Text -> m ()
    printMsg Text
    "Quit"
           else do EditorM ()
    resetRegexE
                   BufferM () -> EditorM ()
    forall (m :: * -> *) a. MonadEditor m => BufferM a -> m a
    withCurrentBuffer (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Point -> BufferM ()
    moveTo (Point -> BufferM ()) -> Point -> BufferM ()
    forall a b. (a -> b) -> a -> b
    $ Region -> Point
    regionStart Region
    p0

    -- | Specialised 'isearchEndWith' to do nothing as the action.
    isearchEnd :: Bool -> EditorM ()
    isearchEnd :: Bool -> EditorM ()
    isearchEnd = EditorM () -> Bool -> EditorM ()
    forall a. EditorM a -> Bool -> EditorM ()
    isearchEndWith (() -> EditorM ()
    forall a. a -> EditorM a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ())

    -----------------
    -- Query-Replace

    -- | Find the next match and select it.
    -- Point is end, mark is beginning.
    qrNext :: Window -> BufferRef -> SearchExp -> EditorM ()
    qrNext :: Window -> BufferRef -> SearchExp -> EditorM ()
    qrNext Window
    win BufferRef
    b SearchExp
    what = do
      mp <- Window -> BufferRef -> BufferM [Region] -> EditorM [Region]
    forall (m :: * -> *) a.
    MonadEditor m =>
    Window -> BufferRef -> BufferM a -> m a
    withGivenBufferAndWindow Window
    win BufferRef
    b (BufferM [Region] -> EditorM [Region])
    -> BufferM [Region] -> EditorM [Region]
    forall a b. (a -> b) -> a -> b
    $ Direction -> SearchExp -> BufferM [Region]
    regexB Direction
    Forward SearchExp
    what
      case mp of
        [] -> do
          Text -> EditorM ()
    forall (m :: * -> *). MonadEditor m => Text -> m ()
    printMsg Text
    "String to search not found"
          EditorM ()
    qrFinish
        (Region
    r:[Region]
    _) -> Window -> BufferRef -> BufferM () -> EditorM ()
    forall (m :: * -> *) a.
    MonadEditor m =>
    Window -> BufferRef -> BufferM a -> m a
    withGivenBufferAndWindow Window
    win BufferRef
    b (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $ Region -> BufferM ()
    setSelectRegionB Region
    r

    -- | Replace all the remaining occurrences.
    qrReplaceAll :: Window -> BufferRef -> SearchExp -> R.YiString -> EditorM ()
    qrReplaceAll :: Window -> BufferRef -> SearchExp -> YiString -> EditorM ()
    qrReplaceAll Window
    win BufferRef
    b SearchExp
    what YiString
    replacement = do
      n <- Window -> BufferRef -> BufferM Int -> EditorM Int
    forall (m :: * -> *) a.
    MonadEditor m =>
    Window -> BufferRef -> BufferM a -> m a
    withGivenBufferAndWindow Window
    win BufferRef
    b (BufferM Int -> EditorM Int) -> BufferM Int -> EditorM Int
    forall a b. (a -> b) -> a -> b
    $ do
        BufferM ()
    exchangePointAndMarkB -- so we replace the current occurrence too
        SearchExp -> YiString -> Bool -> Region -> BufferM Int
    searchAndRepRegion0 SearchExp
    what YiString
    replacement Bool
    True (Region -> BufferM Int) -> BufferM Region -> BufferM Int
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< TextUnit -> Direction -> BufferM Region
    regionOfPartB TextUnit
    Document Direction
    Forward
      printMsg $ "Replaced " <> showT n <> " occurrences"
      qrFinish

    -- | Exit from query/replace.
    qrFinish :: EditorM ()
    qrFinish :: EditorM ()
    qrFinish = do
      (Maybe SearchExp -> Identity (Maybe SearchExp))
    -> Editor -> Identity Editor
    Lens' Editor (Maybe SearchExp)
    currentRegexA ((Maybe SearchExp -> Identity (Maybe SearchExp))
     -> Editor -> Identity Editor)
    -> Maybe SearchExp -> EditorM ()
    forall s (m :: * -> *) a b.
    MonadState s m =>
    ASetter s s a b -> b -> m ()
    .= Maybe SearchExp
    forall a. Maybe a
    Nothing
      EditorM ()
    closeBufferAndWindowE  -- the minibuffer.

    -- | We replace the currently selected match and then move to the next
    -- match.
    qrReplaceOne :: Window -> BufferRef -> SearchExp -> R.YiString -> EditorM ()
    qrReplaceOne :: Window -> BufferRef -> SearchExp -> YiString -> EditorM ()
    qrReplaceOne Window
    win BufferRef
    b SearchExp
    reg YiString
    replacement = do
      Window -> BufferRef -> YiString -> EditorM ()
    qrReplaceCurrent Window
    win BufferRef
    b YiString
    replacement
      Window -> BufferRef -> SearchExp -> EditorM ()
    qrNext Window
    win BufferRef
    b SearchExp
    reg

    -- | This may actually be a bit more general it replaces the current
    -- selection with the given replacement string in the given window and
    -- buffer.
    qrReplaceCurrent :: Window -> BufferRef -> R.YiString -> EditorM ()
    qrReplaceCurrent :: Window -> BufferRef -> YiString -> EditorM ()
    qrReplaceCurrent Window
    win BufferRef
    b YiString
    replacement =
      Window -> BufferRef -> BufferM () -> EditorM ()
    forall (m :: * -> *) a.
    MonadEditor m =>
    Window -> BufferRef -> BufferM a -> m a
    withGivenBufferAndWindow Window
    win BufferRef
    b (BufferM () -> EditorM ()) -> BufferM () -> EditorM ()
    forall a b. (a -> b) -> a -> b
    $
       (Region -> YiString -> BufferM ())
    -> YiString -> Region -> BufferM ()
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Region -> YiString -> BufferM ()
    replaceRegionB YiString
    replacement (Region -> BufferM ()) -> BufferM Region -> BufferM ()
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< BufferM Region
    getRawestSelectRegionB