{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      :  Yi.Keymap.Vim.Ex.Commands.Buffer
-- License     :  GPL-2
-- Maintainer  :  yi-devel@googlegroups.com
-- Stability   :  experimental
-- Portability :  portable
--
-- :buffer ex command to switch to named or numbered buffer.

module Yi.Keymap.Vim.Ex.Commands.Buffer (parse,bufferIdentifier) where

import           Control.Applicative              (Alternative ((<|>)))
import           Control.Monad                    (void)
import           Control.Monad.State              (gets)
import qualified Data.Attoparsec.Text             as P (Parser, anyChar, choice,
                                                        digit, endOfInput, many', many1,
                                                        parseOnly, space, string)
import           Yi.Buffer.Basic                  (BufferRef (..))
import qualified Data.Text                        as T (Text, pack, unpack)
import           Yi.Buffer.Misc                   (bkey, isUnchangedBuffer)
import           Yi.Editor
import           Yi.Keymap                        (Action (EditorA))
import           Yi.Keymap.Vim.Common             (EventString)
import qualified Yi.Keymap.Vim.Ex.Commands.Common as Common (errorNoWrite, parseWithBangAndCount, pureExCommand)
import           Yi.Keymap.Vim.Ex.Types           (ExCommand (cmdAction, cmdShow))

parse :: EventString -> Maybe ExCommand
parse :: EventString -> Maybe ExCommand
parse = Parser ()
-> (() -> Bool -> Maybe Int -> Parser ExCommand)
-> EventString
-> Maybe ExCommand
forall a.
Parser a
-> (a -> Bool -> Maybe Int -> Parser ExCommand)
-> EventString
-> Maybe ExCommand
Common.parseWithBangAndCount Parser ()
nameParser ((() -> Bool -> Maybe Int -> Parser ExCommand)
 -> EventString -> Maybe ExCommand)
-> (() -> Bool -> Maybe Int -> Parser ExCommand)
-> EventString
-> Maybe ExCommand
forall a b. (a -> b) -> a -> b
$ \ ()
_ Bool
bang Maybe Int
mcount -> do
  Text
bufIdent <- Parser Text
bufferIdentifier
  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
$ ExCommand
Common.pureExCommand {
      cmdShow :: Text
cmdShow = Text
"buffer"
    , cmdAction :: Action
cmdAction = EditorM () -> Action
forall a. Show a => EditorM a -> Action
EditorA (EditorM () -> Action) -> EditorM () -> Action
forall a b. (a -> b) -> a -> b
$ do
        Bool
unchanged <- 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
$ (FBuffer -> Bool) -> BufferM Bool
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets FBuffer -> Bool
isUnchangedBuffer
        if Bool
bang Bool -> Bool -> Bool
|| Bool
unchanged
          then case Maybe Int
mcount of
            Maybe Int
Nothing -> Text -> EditorM ()
switchToBuffer Text
bufIdent
            Just Int
i  -> BufferRef -> EditorM ()
switchByRef (BufferRef -> EditorM ()) -> BufferRef -> EditorM ()
forall a b. (a -> b) -> a -> b
$ Int -> BufferRef
BufferRef Int
i
          else EditorM ()
Common.errorNoWrite
    }

bufferSymbol :: P.Parser T.Text
bufferSymbol :: Parser Text
bufferSymbol = Text -> Parser Text
P.string Text
"%" Parser Text -> Parser Text -> Parser Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Text -> Parser Text
P.string Text
"#"

bufferIdentifier :: P.Parser T.Text
bufferIdentifier :: Parser Text
bufferIdentifier = (String -> Text
T.pack (String -> Text) -> Parser Text String -> Parser Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char -> Parser Text String
forall (f :: * -> *) a. Alternative f => f a -> f [a]
P.many1 Parser Text Char
P.digit) Parser Text -> Parser Text -> Parser Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text
bufferSymbol Parser Text -> Parser Text -> Parser Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
    (String -> Text
T.pack (String -> Text) -> Parser Text String -> Parser Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char -> Parser Text String
forall (f :: * -> *) a. Alternative f => f a -> f [a]
P.many1 Parser Text Char
P.space) Parser Text -> Parser Text -> Parser Text
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (String -> Text
T.pack (String -> Text) -> Parser Text String -> Parser Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char -> Parser Text String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
P.many' Parser Text Char
P.anyChar) Parser Text -> Parser Text -> Parser Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
    Parser ()
forall t. Chunk t => Parser t ()
P.endOfInput Parser () -> Parser Text -> Parser Text
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Text -> Parser Text
forall (m :: * -> *) a. Monad m => a -> m a
return Text
""

nameParser :: P.Parser ()
nameParser :: Parser ()
nameParser = Parser Text -> Parser ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Parser Text -> Parser ())
-> ([Text] -> Parser Text) -> [Text] -> Parser ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Parser Text] -> Parser Text
forall (f :: * -> *) a. Alternative f => [f a] -> f a
P.choice ([Parser Text] -> Parser Text)
-> ([Text] -> [Parser Text]) -> [Text] -> Parser Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Parser Text) -> [Text] -> [Parser Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Parser Text
P.string ([Text] -> Parser ()) -> [Text] -> Parser ()
forall a b. (a -> b) -> a -> b
$ [Text
"buffer", Text
"buf", Text
"bu", Text
"b"]


switchToBuffer :: T.Text -> EditorM ()
switchToBuffer :: Text -> EditorM ()
switchToBuffer Text
s =
    case Parser BufferRef -> Text -> Either String BufferRef
forall a. Parser a -> Text -> Either String a
P.parseOnly Parser BufferRef
bufferRef Text
s of
        Right BufferRef
ref -> BufferRef -> EditorM ()
switchByRef BufferRef
ref
        Left String
_e   -> String -> EditorM ()
switchByName (String -> EditorM ()) -> String -> EditorM ()
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack Text
s
  where
    bufferRef :: Parser BufferRef
bufferRef = Int -> BufferRef
BufferRef (Int -> BufferRef) -> (String -> Int) -> String -> BufferRef
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Int
forall a. Read a => String -> a
read (String -> BufferRef) -> Parser Text String -> Parser BufferRef
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char -> Parser Text String
forall (f :: * -> *) a. Alternative f => f a -> f [a]
P.many1 Parser Text Char
P.digit


switchByName :: String -> EditorM ()
switchByName :: String -> EditorM ()
switchByName String
""      = () -> EditorM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
switchByName String
"%"     = () -> EditorM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
switchByName String
"#"     = Text -> EditorM ()
switchToBufferWithNameE Text
""
switchByName String
bufName = Text -> EditorM ()
switchToBufferWithNameE (String -> Text
T.pack String
bufName)


switchByRef :: BufferRef -> EditorM ()
switchByRef :: BufferRef -> EditorM ()
switchByRef BufferRef
ref = do
    Maybe FBuffer
mBuf <- BufferRef -> EditorM (Maybe FBuffer)
forall (m :: * -> *).
MonadEditor m =>
BufferRef -> m (Maybe FBuffer)
findBuffer BufferRef
ref
    EditorM ()
-> (FBuffer -> EditorM ()) -> Maybe FBuffer -> EditorM ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> EditorM ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) (BufferRef -> EditorM ()
switchToBufferE (BufferRef -> EditorM ())
-> (FBuffer -> BufferRef) -> FBuffer -> EditorM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FBuffer -> BufferRef
bkey) Maybe FBuffer
mBuf