{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_HADDOCK show-extensions #-} -- | -- Module : Yi.Keymap.Vim.Ex.Commands.Substitute -- License : GPL-2 -- Maintainer : yi-devel@googlegroups.com -- Stability : experimental -- Portability : portable module Yi.Keymap.Vim.Ex.Commands.Substitute (parse) where import Control.Applicative (Alternative) import Control.Monad (void) import qualified Data.Attoparsec.Text as P (char, inClass, many', match, satisfy, string, option, (), Parser) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import qualified Data.Text as T (Text, cons, snoc) import Lens.Micro.Platform (over, _2) import Yi.Buffer import Yi.Keymap (Action (EditorA)) import Yi.Keymap.Vim.Common (EventString, Substitution(..)) import qualified Yi.Keymap.Vim.Ex.Commands.Common as Common (parse, pureExCommand, parseRange) import Yi.Keymap.Vim.Ex.Types (ExCommand (cmdAction, cmdShow)) import qualified Yi.Rope as R (fromString, toText) import Yi.Keymap.Vim.Substitution -- | Skip one or no occurrences of a given parser. skipOptional :: Alternative f => f a -> f () skipOptional p = P.option () (() <$ p) {-# SPECIALIZE skipOptional :: P.Parser a -> P.Parser () #-} parse :: EventString -> Maybe ExCommand parse = Common.parse $ do (rangeText, rangeB) <- over _2 (fromMaybe $ regionOfB Line) <$> P.match Common.parseRange P.char 's' *> skipOptional (P.string "ub" *> skipOptional (P.string "stitute")) P. "substitute" delimiter <- P.satisfy (`elem` ("!@#$%^&*()[]{}<>/.,~';:?-=" :: String)) from <- R.fromString <$> P.many' (P.satisfy (/= delimiter)) void $ P.char delimiter to <- R.fromString <$> P.many' (P.satisfy (/= delimiter)) flagChars <- P.option "" $ P.char delimiter *> P.many' (P.satisfy $ P.inClass "gic") return $! substitute (Substitution from to ('g' `elem` flagChars) ('i' `elem` flagChars) ('c' `elem` flagChars)) delimiter rangeText rangeB substitute :: Substitution -> Char -> T.Text -> BufferM Region -> ExCommand substitute s@(Substitution from to global caseInsensitive confirm) delimiter regionText regionB = Common.pureExCommand { cmdShow = regionText <> "s" <> (delimiter `T.cons` R.toText from) <> (delimiter `T.cons` R.toText to) `T.snoc` delimiter <> (if confirm then "c" else "") <> (if caseInsensitive then "i" else "") <> (if global then "g" else "") , cmdAction = EditorA $ substituteE s regionB }