yi-0.10.0: src/library/Yi/Keymap/Vim/Ex/Commands/Common.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK show-extensions #-}
-- |
-- Module : Yi.Keymap.Vim.Ex.Commands.Common
-- License : GPL-2
-- Maintainer : yi-devel@googlegroups.com
-- Stability : experimental
-- Portability : portable
--
-- Implements common 'ExCommand's for the Vim keymap.
module Yi.Keymap.Vim.Ex.Commands.Common
( parse
, parseWithBang
, parseWithBangAndCount
, parseRange
, OptionAction(..)
, parseOption
, filenameComplete
, forAllBuffers
, pureExCommand
, impureExCommand
, errorNoWrite
) where
import Control.Applicative
import Control.Monad
import Data.List.NonEmpty (NonEmpty(..))
import Data.Monoid
import qualified Data.Text as T
import System.Directory
import qualified Text.ParserCombinators.Parsec as P
import Text.Read (readMaybe)
import Yi.Buffer
import Yi.Editor
import Yi.Keymap
import Yi.Keymap.Vim.Common
import Yi.Keymap.Vim.Ex.Types
import Yi.Misc
import Yi.Monad
import Yi.Style (errorStyle)
import Yi.Utils
parse :: P.GenParser Char () ExCommand -> EventString -> Maybe ExCommand
parse parser (Ev s) =
either (const Nothing) Just (P.parse parser "" $ T.unpack s)
parseWithBangAndCount :: P.GenParser Char () a
-- ^ The command name parser.
-> (a -> Bool
-> (Maybe Int)
-> P.GenParser Char () ExCommand)
-- ^ A parser for the remaining command arguments.
-> EventString
-- ^ The string to parse.
-> Maybe ExCommand
parseWithBangAndCount nameParser argumentParser (Ev s) = do
either (const Nothing) Just (P.parse parser "" $ T.unpack s)
where
parser = do
mcount <- parseCount
a <- nameParser
bang <- parseBang
argumentParser a bang mcount
parseWithBang :: P.GenParser Char () a
-- ^ The command name parser.
-> (a -> Bool -> P.GenParser Char () ExCommand)
-- ^ A parser for the remaining command arguments.
-> EventString
-- ^ The string to parse.
-> Maybe ExCommand
parseWithBang nameParser argumentParser (Ev s) = do
either (const Nothing) Just (P.parse parser "" $ T.unpack s)
where
parser = do
a <- nameParser
bang <- parseBang
argumentParser a bang
parseBang :: P.GenParser Char () Bool
parseBang = P.string "!" *> return True <|> return False
parseRange :: P.GenParser Char () LineRange
parseRange = return CurrentLineRange
parseCount :: P.GenParser Char () (Maybe Int)
parseCount = do
readMaybe <$> P.many P.digit
data OptionAction = Set !Bool | Invert | Ask
parseOption :: String -> (OptionAction -> Action) -> EventString -> Maybe ExCommand
parseOption name action = parse $ do
void $ P.string "set "
nos <- P.many (P.string "no")
invs <- P.many (P.string "inv")
void $ P.string name
bangs <- P.many (P.string "!")
qs <- P.many (P.string "?")
return $ pureExCommand {
cmdShow = T.concat [ "set "
, T.pack $ concat nos
, T.pack name
, T.pack $ concat bangs
, T.pack $ concat qs ]
, cmdAction = action $
case fmap (not . null) [qs, bangs, invs, nos] of
[True, _, _, _] -> Ask
[_, True, _, _] -> Invert
[_, _, True, _] -> Invert
[_, _, _, True] -> Set False
_ -> Set True
}
removePwd :: T.Text -> YiM T.Text
removePwd path = do
pwd' <- T.pack <$> io getCurrentDirectory
return $! if pwd' `T.snoc` '/' `T.isPrefixOf` path
then T.drop (1 + T.length pwd') path
else path
filenameComplete :: T.Text -> YiM [T.Text]
filenameComplete f = case f == "%" of
True -> do
-- current buffer is minibuffer
-- actual file is in the second buffer in bufferStack
gets bufferStack >>= \case
_ :| [] -> do
withEditor $ printMsg "filenameComplete: Expected to see minibuffer!"
return []
_ :| bufferRef : _ -> do
currentFileName <- fmap T.pack . withGivenBuffer bufferRef $
fmap bufInfoFileName bufInfoB
let sanitizedFileName = if "//" `T.isPrefixOf` currentFileName
then '/' `T.cons` currentFileName
else currentFileName
return <$> removePwd sanitizedFileName
False -> do
files <- matchingFileNames Nothing f
case files of
[] -> return []
[x] -> return <$> removePwd x
xs -> sequence $ fmap removePwd xs
forAllBuffers :: MonadEditor m => (BufferRef -> m ()) -> m ()
forAllBuffers f = readEditor bufferStack >>= \(b :| bs) -> f b >> mapM_ f bs
pureExCommand :: ExCommand
pureExCommand = ExCommand {
cmdIsPure = True
, cmdComplete = return []
, cmdAcceptsRange = False
, cmdAction = undefined
, cmdShow = undefined
}
impureExCommand :: ExCommand
impureExCommand = pureExCommand { cmdIsPure = False }
-- | Show an error on the status line.
errorEditor :: T.Text -> EditorM ()
errorEditor s = printStatus (["error: " <> s], errorStyle)
-- | Show the common error message about an unsaved file on the status line.
errorNoWrite :: EditorM ()
errorNoWrite = errorEditor "No write since last change (add ! to override)"