{-# 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 :: f a -> f ()
skipOptional f a
p = () -> f () -> f ()
forall (f :: * -> *) a. Alternative f => a -> f a -> f a
P.option () (() () -> f a -> f ()
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ f a
p)
{-# SPECIALIZE skipOptional :: P.Parser a -> P.Parser () #-}

parse :: EventString -> Maybe ExCommand
parse :: EventString -> Maybe ExCommand
parse = Parser ExCommand -> EventString -> Maybe ExCommand
Common.parse (Parser ExCommand -> EventString -> Maybe ExCommand)
-> Parser ExCommand -> EventString -> Maybe ExCommand
forall a b. (a -> b) -> a -> b
$ do
    (Text
rangeText, BufferM Region
rangeB) <- ASetter
  (Text, Maybe (BufferM Region))
  (Text, BufferM Region)
  (Maybe (BufferM Region))
  (BufferM Region)
-> (Maybe (BufferM Region) -> BufferM Region)
-> (Text, Maybe (BufferM Region))
-> (Text, BufferM Region)
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over ASetter
  (Text, Maybe (BufferM Region))
  (Text, BufferM Region)
  (Maybe (BufferM Region))
  (BufferM Region)
forall s t a b. Field2 s t a b => Lens s t a b
_2 (BufferM Region -> Maybe (BufferM Region) -> BufferM Region
forall a. a -> Maybe a -> a
fromMaybe (BufferM Region -> Maybe (BufferM Region) -> BufferM Region)
-> BufferM Region -> Maybe (BufferM Region) -> BufferM Region
forall a b. (a -> b) -> a -> b
$ TextUnit -> BufferM Region
regionOfB TextUnit
Line) ((Text, Maybe (BufferM Region)) -> (Text, BufferM Region))
-> Parser Text (Text, Maybe (BufferM Region))
-> Parser Text (Text, BufferM Region)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser (Maybe (BufferM Region))
-> Parser Text (Text, Maybe (BufferM Region))
forall a. Parser a -> Parser (Text, a)
P.match Parser (Maybe (BufferM Region))
Common.parseRange
    Char -> Parser Char
P.char Char
's' Parser Char -> Parser Text () -> Parser Text ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> 
      Parser Text () -> Parser Text ()
forall (f :: * -> *) a. Alternative f => f a -> f ()
skipOptional (Text -> Parser Text
P.string Text
"ub" Parser Text -> Parser Text () -> Parser Text ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Text -> Parser Text ()
forall (f :: * -> *) a. Alternative f => f a -> f ()
skipOptional (Text -> Parser Text
P.string Text
"stitute"))
      Parser Text () -> String -> Parser Text ()
forall i a. Parser i a -> String -> Parser i a
P.<?> String
"substitute"
    Char
delimiter <- (Char -> Bool) -> Parser Char
P.satisfy (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (String
"!@#$%^&*()[]{}<>/.,~';:?-=" :: String))
    YiString
from <- String -> YiString
R.fromString (String -> YiString) -> Parser Text String -> Parser Text YiString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char -> Parser Text String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
P.many' ((Char -> Bool) -> Parser Char
P.satisfy (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
delimiter))
    Parser Char -> Parser Text ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Parser Char -> Parser Text ()) -> Parser Char -> Parser Text ()
forall a b. (a -> b) -> a -> b
$ Char -> Parser Char
P.char Char
delimiter
    YiString
to <- String -> YiString
R.fromString (String -> YiString) -> Parser Text String -> Parser Text YiString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Char -> Parser Text String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
P.many' ((Char -> Bool) -> Parser Char
P.satisfy (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
delimiter))
    String
flagChars <- String -> Parser Text String -> Parser Text String
forall (f :: * -> *) a. Alternative f => a -> f a -> f a
P.option String
"" (Parser Text String -> Parser Text String)
-> Parser Text String -> Parser Text String
forall a b. (a -> b) -> a -> b
$
      Char -> Parser Char
P.char Char
delimiter Parser Char -> Parser Text String -> Parser Text String
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Char -> Parser Text String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
P.many' ((Char -> Bool) -> Parser Char
P.satisfy ((Char -> Bool) -> Parser Char) -> (Char -> Bool) -> Parser Char
forall a b. (a -> b) -> a -> b
$ String -> Char -> Bool
P.inClass String
"gic")
    ExCommand -> Parser ExCommand
forall (m :: * -> *) a. Monad m => a -> m a
return (ExCommand -> Parser ExCommand) -> ExCommand -> Parser ExCommand
forall a b. (a -> b) -> a -> b
$! Substitution -> Char -> Text -> BufferM Region -> ExCommand
substitute
        (YiString -> YiString -> Bool -> Bool -> Bool -> Substitution
Substitution
            YiString
from
            YiString
to
            (Char
'g' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
flagChars)
            (Char
'i' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
flagChars)
            (Char
'c' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
flagChars))
        Char
delimiter
        Text
rangeText
        BufferM Region
rangeB

substitute :: Substitution -> Char -> T.Text -> BufferM Region -> ExCommand
substitute :: Substitution -> Char -> Text -> BufferM Region -> ExCommand
substitute s :: Substitution
s@(Substitution YiString
from YiString
to Bool
global Bool
caseInsensitive Bool
confirm) Char
delimiter Text
regionText BufferM Region
regionB = ExCommand
Common.pureExCommand
  { cmdShow :: Text
cmdShow = Text
regionText
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       Text
"s"
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       (Char
delimiter Char -> Text -> Text
`T.cons` YiString -> Text
R.toText YiString
from)
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       (Char
delimiter Char -> Text -> Text
`T.cons` YiString -> Text
R.toText YiString
to)
              Text -> Char -> Text
`T.snoc` Char
delimiter
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       (if Bool
confirm then Text
"c" else Text
"")
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       (if Bool
caseInsensitive then Text
"i" else Text
"")
              Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>       (if Bool
global then Text
"g" else Text
"")
  , cmdAction :: Action
cmdAction = EditorM () -> Action
forall a. Show a => EditorM a -> Action
EditorA (EditorM () -> Action) -> EditorM () -> Action
forall a b. (a -> b) -> a -> b
$ Substitution -> BufferM Region -> EditorM ()
substituteE Substitution
s BufferM Region
regionB
  }