{-# language OverloadedStrings #-}
module Yi.Keymap.Vim.Substitution
( substituteE
, substituteConfirmE
, repeatSubstitutionE
, repeatSubstitutionFlaglessE
) where
import Control.Monad (void)
import Data.Monoid
import Yi.MiniBuffer
import Yi.Keymap (Keymap)
import qualified Yi.Rope as R
import Yi.Regex
import Yi.Buffer
import Yi.Editor
import Yi.Search
import Yi.Keymap.Keys (char, choice, (?>>!))
import Yi.Keymap.Vim.Common
import Yi.Keymap.Vim.StateUtils
substituteE :: Substitution -> BufferM Region -> EditorM ()
substituteE s@(Substitution from to global caseInsensitive confirm) regionB = do
let opts = if caseInsensitive then [IgnoreCase] else []
lines' <- withCurrentBuffer $ regionB >>= linesOfRegionB
regex <- if R.null from
then getRegexE
else return . (either (const Nothing) Just)
. makeSearchOptsM opts . R.toString $ from
case regex of
Nothing -> printMsg "No previous search pattern"
Just regex' -> do
saveSubstitutionE s
if confirm
then substituteConfirmE regex' to global lines'
else do
withCurrentBuffer $ do
mapM_ (void . searchAndRepRegion0 regex' to global) (reverse lines')
moveToSol
substituteConfirmE :: SearchExp -> R.YiString -> Bool -> [Region] -> EditorM ()
substituteConfirmE regex to global lines' = do
setRegexE regex
regions <- withCurrentBuffer $ findMatches regex global lines'
substituteMatch to 0 False regions
findMatches :: SearchExp -> Bool -> [Region] -> BufferM [Region]
findMatches regex global lines' = do
let f = if global then id else take 1
concat <$> mapM (fmap f . regexRegionB regex) lines'
substituteMatch :: R.YiString -> Int -> Bool -> [Region] -> EditorM ()
substituteMatch _ _ _ [] = resetRegexE
substituteMatch to co autoAll (m:ms) = do
let m' = offsetRegion co m
withCurrentBuffer . moveTo $ regionStart m'
len <- withCurrentBuffer $ R.length <$> readRegionB m'
let diff = R.length to - len
tex = "replace with " <> R.toText to <> " (y/n/a/q)?"
if autoAll
then do withCurrentBuffer $ replaceRegionB m' to
substituteMatch to (co + diff) True ms
else void . spawnMinibufferE tex . const $ askKeymap to co (co + diff) m ms
offsetRegion :: Int -> Region -> Region
offsetRegion k reg = mkRegion (regionStart reg + k') (regionEnd reg + k')
where k' = fromIntegral k
askKeymap :: R.YiString -> Int -> Int -> Region -> [Region] -> Keymap
askKeymap to co co' m ms = choice [
char 'n' ?>>! cleanUp >> substituteMatch to co False ms
, char 'a' ?>>! do cleanUp
replace
substituteMatch to co' True ms
, char 'y' ?>>! do cleanUp
replace
substituteMatch to co' False ms
, char 'q' ?>>! cleanUp >> resetRegexE
]
where cleanUp = closeBufferAndWindowE
replace = withCurrentBuffer $ replaceRegionB (offsetRegion co m) to
repeatSubstitutionFlaglessE :: Substitution -> EditorM ()
repeatSubstitutionFlaglessE (Substitution from to _ _ _) =
substituteE (Substitution from to False False False) (regionOfB Line)
repeatSubstitutionE :: Substitution -> EditorM ()
repeatSubstitutionE s = substituteE s (regionOfB Line)