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` ["", ""] 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", ""] = WholeMatch () prereq _ _ = NoMatch action evs = do currentMode <- fmap vsMode getDynamic let newStyle = case evs of "v" -> Inclusive "V" -> LineWise "" -> Block _ -> error "Can't happen because of prereq, this just prevents warning" newMode = Visual newStyle if newMode == currentMode then do vbeAction escBinding "" 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