module Yi.Keymap.Vim2.VisualMap
( defVisualMap
) where
import Yi.Prelude
import Prelude ()
import Data.Char (ord)
import Data.List (drop, group, length, reverse)
import Data.Maybe (fromJust)
import Yi.Buffer hiding (Insert)
import Yi.Editor
import Yi.Keymap.Vim2.Common
import Yi.Keymap.Vim2.Operator
import Yi.Keymap.Vim2.StateUtils
import Yi.Keymap.Vim2.StyledRegion
import Yi.Keymap.Vim2.Utils
import Yi.MiniBuffer
defVisualMap :: [VimOperator] -> [VimBinding]
defVisualMap operators =
[escBinding, motionBinding, changeVisualStyleBinding, setMarkBinding]
++ [chooseRegisterBinding]
++ operatorBindings operators ++ digitBindings ++ [replaceBinding, switchEdgeBinding]
++ [insertBinding, exBinding, shiftDBinding]
escBinding :: VimBinding
escBinding = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) =
matchFromBool $ evs `elem` ["<Esc>", "<C-c>"]
prereq _ _ = NoMatch
action _ = do
resetCountE
clrStatus
withBuffer0 $ do
setVisibleSelection False
putA regionStyleA Inclusive
switchModeE Normal
return Drop
exBinding :: VimBinding
exBinding = VimBindingE prereq action
where prereq ":" (VimState { vsMode = (Visual _) }) = WholeMatch ()
prereq _ _ = NoMatch
action _ = do
discard $ spawnMinibufferE ":'<,'>" id
switchModeE Ex
return Finish
digitBindings :: [VimBinding]
digitBindings = zeroBinding : fmap mkDigitBinding ['1' .. '9']
zeroBinding :: VimBinding
zeroBinding = VimBindingE prereq action
where prereq "0" (VimState { vsMode = (Visual _) }) = WholeMatch ()
prereq _ _ = NoMatch
action _ = do
currentState <- getDynamic
case vsCount currentState of
Just c -> do
setDynamic $ currentState { vsCount = Just (10 * c) }
return Continue
Nothing -> do
withBuffer0 moveToSol
setDynamic $ resetCount currentState
return Continue
setMarkBinding :: VimBinding
setMarkBinding = VimBindingE prereq action
where prereq "m" (VimState { vsMode = (Visual _) }) = PartialMatch
prereq ('m':_:[]) (VimState { vsMode = (Visual _) }) = WholeMatch ()
prereq _ _ = NoMatch
action ('m':c:[]) = do
withBuffer0 $ setNamedMarkHereB [c]
return Continue
action _ = error "Can't happen"
changeVisualStyleBinding :: VimBinding
changeVisualStyleBinding = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) | evs `elem` ["v", "V", "<C-v>"] = WholeMatch ()
prereq _ _ = NoMatch
action evs = do
currentMode <- fmap vsMode getDynamic
let newStyle = case evs of
"v" -> Inclusive
"V" -> LineWise
"<C-v>" -> Block
_ -> error "Can't happen because of prereq, this just prevents warning"
newMode = Visual newStyle
if newMode == currentMode
then do
vbeAction escBinding "<Esc>"
else do
modifyStateE $ \s -> s { vsMode = newMode }
withBuffer0 $ do
putA regionStyleA newStyle
putA rectangleSelectionA $ Block == newStyle
setVisibleSelection True
return Finish
mkDigitBinding :: Char -> VimBinding
mkDigitBinding c = VimBindingE prereq action
where prereq (c':[]) (VimState { vsMode = (Visual _) }) = matchFromBool $ c == c'
prereq _ _ = NoMatch
action _ = do
modifyStateE mutate
return Continue
mutate vs@(VimState {vsCount = Nothing}) = vs { vsCount = Just d }
mutate vs@(VimState {vsCount = Just count}) = vs { vsCount = Just $ count * 10 + d }
d = ord c ord '0'
motionBinding :: VimBinding
motionBinding = mkMotionBinding Continue $
\m -> case m of
Visual _ -> True
_ -> False
regionOfSelectionB :: BufferM Region
regionOfSelectionB = savingPointB $ do
start <- getSelectionMarkPointB
stop <- pointB
return $! mkRegion start stop
operatorBindings :: [VimOperator] -> [VimBinding]
operatorBindings operators = fmap mkOperatorBinding $ operators ++ visualOperators
where visualOperators = fmap synonymOp
[ ("x", "d")
, ("~", "g~")
, ("Y", "y")
, ("u", "gu")
, ("U", "gU")
]
synonymOp (newName, existingName) =
VimOperator newName . operatorApplyToRegionE . fromJust
. stringToOperator operators $ existingName
chooseRegisterBinding :: VimBinding
chooseRegisterBinding = mkChooseRegisterBinding $
\s -> case s of
(VimState { vsMode = (Visual _) }) -> True
_ -> False
shiftDBinding :: VimBinding
shiftDBinding = VimBindingE prereq action
where prereq "D" (VimState { vsMode = (Visual _) }) = WholeMatch ()
prereq _ _ = NoMatch
action _ = do
(Visual style) <- vsMode <$> getDynamic
reg <- withBuffer0 regionOfSelectionB
case style of
Block -> withBuffer0 $ do
(start, lengths) <- shapeOfBlockRegionB reg
moveTo start
startCol <- curCol
forM_ (reverse [0 .. length lengths 1]) $ \l -> do
moveTo start
discard $ lineMoveRel l
whenM (fmap (== startCol) curCol) deleteToEol
leftOnEol
_ -> do
reg' <- withBuffer0 $ convertRegionToStyleB reg LineWise
reg'' <- withBuffer0 $ mkRegionOfStyleB (regionStart reg')
(regionEnd reg' -~ Size 1)
Exclusive
discard $ operatorApplyToRegionE opDelete 1 $ StyledRegion LineWise reg''
resetCountE
switchModeE Normal
return Finish
mkOperatorBinding :: VimOperator -> VimBinding
mkOperatorBinding op = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) =
evs `matchesString` (operatorName op)
prereq _ _ = NoMatch
action _ = do
(Visual style) <- vsMode <$> getDynamic
region <- withBuffer0 regionOfSelectionB
count <- getCountE
token <- operatorApplyToRegionE op count $ StyledRegion style region
resetCountE
clrStatus
withBuffer0 $ do
setVisibleSelection False
putA regionStyleA Inclusive
return token
replaceBinding :: VimBinding
replaceBinding = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) =
case evs of
"r" -> PartialMatch
('r':_:[]) -> WholeMatch ()
_ -> NoMatch
prereq _ _ = NoMatch
action (_:c:[]) = do
(Visual style) <- vsMode <$> getDynamic
region <- withBuffer0 regionOfSelectionB
withBuffer0 $ transformCharactersInRegionB (StyledRegion style region)
(\x -> if x == '\n' then x else c)
switchModeE Normal
return Finish
action _ = error "can't happen"
switchEdgeBinding :: VimBinding
switchEdgeBinding = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) =
matchFromBool $ evs `elem` group "oO"
prereq _ _ = NoMatch
action (c:[]) = do
(Visual style) <- vsMode <$> getDynamic
withBuffer0 $ do
here <- pointB
there <- getSelectionMarkPointB
(here', there') <- case (c, style) of
('O', Block) -> flipRectangleB here there
(_, _) -> return (there, here)
moveTo here'
setSelectionMarkPointB there'
return Continue
action _ = error "can't happen"
insertBinding :: VimBinding
insertBinding = VimBindingE prereq action
where prereq evs (VimState { vsMode = (Visual _) }) =
matchFromBool $ evs `elem` group "IA"
prereq _ _ = NoMatch
action evs = do
(Visual style) <- vsMode <$> getDynamic
region <- withBuffer0 regionOfSelectionB
cursors <- withBuffer0 $ case evs of
"I" -> leftEdgesOfRegionB style region
"A" -> rightEdgesOfRegionB style region
_ -> error "can't happen"
withBuffer0 $ moveTo $ head cursors
modifyStateE $ \s -> s { vsSecondaryCursors = drop 1 cursors }
switchModeE $ Insert (head evs)
return Continue