1+ {-# LANGUAGE LambdaCase #-}
12#if __GLASGOW_HASKELL__ < 802
23{-# OPTIONS_GHC -Wno-redundant-constraints #-}
34#endif
@@ -21,15 +22,20 @@ type EitherMode = Either CommandMode InsertMode
2122
2223type SavedCommand m = Command (ViT m ) (ArgMode CommandMode ) EitherMode
2324
25+ data InlineSearch = F -- f | F
26+ | T -- t | T
27+
2428data ViState m = ViState {
2529 lastCommand :: SavedCommand m ,
26- lastSearch :: [Grapheme ]
30+ lastSearch :: [Grapheme ],
31+ lastInlineSearch :: Maybe (Grapheme , InlineSearch , Direction )
2732 }
2833
2934emptyViState :: Monad m => ViState m
3035emptyViState = ViState {
3136 lastCommand = return . Left . argState,
32- lastSearch = []
37+ lastSearch = [] ,
38+ lastInlineSearch = Nothing
3339 }
3440
3541type ViT m = StateT (ViState m ) (InputCmdT m )
@@ -134,6 +140,43 @@ simpleCmdActions = choiceCmd [
134140 , simpleKey KillLine `useKey` (noArg >=> killAndStoreC (SimpleMove moveToStart))
135141 ]
136142
143+ inlineSearchActions :: InputKeyCmd (ArgMode CommandMode ) CommandMode
144+ inlineSearchActions = choiceCmd
145+ [ simpleChar ' f' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch F Forward c >>= ) . viInlineSearch id )
146+ , simpleChar ' F' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch F Reverse c >>= ) . viInlineSearch id )
147+ , simpleChar ' t' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch T Forward c >>= ) . viInlineSearch id )
148+ , simpleChar ' T' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch T Reverse c >>= ) . viInlineSearch id )
149+ , simpleChar ' ;' `useKey` ((getLastInlineSearch >>= ) . viInlineSearch id )
150+ , simpleChar ' ,' `useKey` ((getLastInlineSearch >>= ) . viInlineSearch flipDir)
151+ ]
152+
153+ viInlineSearch :: forall m . Monad m => (Direction -> Direction )
154+ -> ArgMode CommandMode
155+ -> (Maybe (Grapheme , InlineSearch , Direction ))
156+ -> CmdM (ViT m ) CommandMode
157+ viInlineSearch flipdir = \ s -> \ case
158+ Nothing -> return $ argState s
159+ Just (g, fOrT, dir) -> setState $ search fOrT (flipdir dir) (== baseChar g) s
160+ where
161+ search :: InlineSearch -> Direction -> (Char -> Bool ) -> (ArgMode CommandMode ) -> CommandMode
162+ search F Forward = applyArgToInsMode . goRightUntil . overChar
163+ search F Reverse = applyArgToInsMode . goLeftUntil . overChar
164+ search T Forward = applyArgToInsMode . goRightUntil . beforeChar
165+ search T Reverse = applyArgToInsMode . goLeftUntil . afterChar
166+ applyArgToInsMode = applyArg . withCommandMode
167+
168+ getLastInlineSearch :: forall m . Monad m => CmdM (ViT m ) (Maybe (Grapheme , InlineSearch , Direction ))
169+ getLastInlineSearch = do
170+ (vs :: ViState m ) <- get -- TODO: ideally this is a usage of `gets`
171+ return $ lastInlineSearch vs
172+
173+ saveInlineSearch :: forall m . Monad m => InlineSearch -> Direction -> Char
174+ -> CmdM (ViT m ) (Maybe (Grapheme , InlineSearch , Direction ))
175+ saveInlineSearch fOrT dir char
176+ = do let ret = Just (baseGrapheme char, fOrT, dir)
177+ modify $ \ (vs :: ViState m ) -> vs {lastInlineSearch = ret}
178+ return ret
179+
137180replaceOnce :: InputCmd CommandMode CommandMode
138181replaceOnce = try $ changeFromChar replaceChar
139182
@@ -150,37 +193,36 @@ repeatedCommands = choiceCmd [argumented, doBefore noArg repeatableCommands]
150193 ]
151194
152195pureMovements :: InputKeyCmd (ArgMode CommandMode ) CommandMode
153- pureMovements = choiceCmd $ charMovements ++ map mkSimpleCommand movements
196+ pureMovements = choiceCmd $ map mkSimpleCommand movements
154197 where
155- charMovements = [ charMovement ' f' $ \ c -> goRightUntil $ overChar (== c)
156- , charMovement ' F' $ \ c -> goLeftUntil $ overChar (== c)
157- , charMovement ' t' $ \ c -> goRightUntil $ beforeChar (== c)
158- , charMovement ' T' $ \ c -> goLeftUntil $ afterChar (== c)
159- ]
160- mkSimpleCommand (k,move) = k +> change (applyCmdArg move)
161- charMovement c move = simpleChar c +> keyChoiceCmd [
162- useChar (change . applyCmdArg . move)
163- , withoutConsuming (change argState)
164- ]
165-
166- useMovementsForKill :: Command m s t -> (KillHelper -> Command m s t ) -> KeyCommand m s t
167- useMovementsForKill alternate useHelper = choiceCmd $
198+ mkSimpleCommand (k, move) = k `useKey` change (applyCmdArg move)
199+
200+ useMovementsForKill :: (KillHelper -> Command m s t ) -> KeyCommand m s t
201+ useMovementsForKill useHelper = choiceCmd $
168202 specialCases
169203 ++ map (\ (k,move) -> k `useKey` useHelper (SimpleMove move)) movements
170204 where
171205 specialCases = [ simpleChar ' e' `useKey` useHelper (SimpleMove goToWordDelEnd)
172206 , simpleChar ' E' `useKey` useHelper (SimpleMove goToBigWordDelEnd)
173207 , simpleChar ' %' `useKey` useHelper (GenericKill deleteMatchingBrace)
174- -- Note 't' and 'f' behave differently than in pureMovements.
175- , charMovement ' f' $ \ c -> goRightUntil $ afterChar (== c)
176- , charMovement ' F' $ \ c -> goLeftUntil $ overChar (== c)
177- , charMovement ' t' $ \ c -> goRightUntil $ overChar (== c)
178- , charMovement ' T' $ \ c -> goLeftUntil $ afterChar (== c)
179208 ]
180- charMovement c move = simpleChar c +> keyChoiceCmd [
181- useChar (useHelper . SimpleMove . move)
182- , withoutConsuming alternate]
183209
210+ useInlineSearchForKill :: Monad m => Command (ViT m ) s t -> (KillHelper -> Command (ViT m ) s t ) -> KeyMap (Command (ViT m ) s t )
211+ useInlineSearchForKill alternate killCmd = choiceCmd
212+ [ simpleChar ' f' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch F Forward c >>= ) . getSearchAndKill)
213+ , simpleChar ' F' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch F Reverse c >>= ) . getSearchAndKill)
214+ , simpleChar ' t' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch T Forward c >>= ) . getSearchAndKill)
215+ , simpleChar ' T' `useKey` keyCommand (useChar $ \ c -> (saveInlineSearch T Reverse c >>= ) . getSearchAndKill)
216+ , simpleChar ' ;' `useKey` ((getLastInlineSearch >>= ) . getSearchAndKill)
217+ , simpleChar ' ,' `useKey` ((reverseDir <$> getLastInlineSearch >>= ) . getSearchAndKill)
218+ ]
219+ where
220+ getSearchAndKill = \ s
221+ -> \ case (Just (g, F , Forward )) -> killCmd (SimpleMove $ goRightUntil $ afterChar (== baseChar g)) s
222+ (Just (g, F , Reverse )) -> killCmd (SimpleMove $ goLeftUntil $ overChar (== baseChar g)) s
223+ (Just (g, T , Forward )) -> killCmd (SimpleMove $ goRightUntil $ overChar (== baseChar g)) s
224+ (Just (g, T , Reverse )) -> killCmd (SimpleMove $ goLeftUntil $ afterChar (== baseChar g)) s
225+ Nothing -> alternate s
184226
185227repeatableCommands :: InputKeyCmd (ArgMode CommandMode ) EitherMode
186228repeatableCommands = choiceCmd
@@ -201,6 +243,7 @@ repeatableCmdMode = choiceCmd
201243 , simpleChar ' d' `useKey` deletionCmd
202244 , simpleChar ' y' `useKey` yankCommand
203245 , ctrlChar ' w' `useKey` killAndStoreC wordErase
246+ , inlineSearchActions
204247 , pureMovements
205248 ]
206249 where
@@ -220,7 +263,8 @@ deletionCmd :: InputCmd (ArgMode CommandMode) CommandMode
220263deletionCmd = keyChoiceCmd
221264 [ reinputArg >+> deletionCmd
222265 , simpleChar ' d' `useKey` killAndStoreC killAll
223- , useMovementsForKill (change argState) killAndStoreC
266+ , useMovementsForKill killAndStoreC
267+ , useInlineSearchForKill (change argState) killAndStoreC
224268 , withoutConsuming (change argState)
225269 ]
226270
@@ -232,7 +276,8 @@ deletionToInsertCmd = keyChoiceCmd
232276 -- readline does this too, so we should also.
233277 , simpleChar ' w' `useKey` killAndStoreE (SimpleMove goToWordDelEnd)
234278 , simpleChar ' W' `useKey` killAndStoreE (SimpleMove goToBigWordDelEnd)
235- , useMovementsForKill (fmap Left . change argState) killAndStoreE
279+ , useMovementsForKill killAndStoreE
280+ , useInlineSearchForKill (fmap Left . change argState) killAndStoreE
236281 , withoutConsuming (return . Left . argState)
237282 ]
238283
@@ -241,7 +286,8 @@ yankCommand :: InputCmd (ArgMode CommandMode) CommandMode
241286yankCommand = keyChoiceCmd
242287 [ reinputArg >+> yankCommand
243288 , simpleChar ' y' `useKey` copyAndStore killAll
244- , useMovementsForKill (change argState) copyAndStore
289+ , useMovementsForKill copyAndStore
290+ , useInlineSearchForKill (change argState) copyAndStore
245291 , withoutConsuming (change argState)
246292 ]
247293
@@ -451,3 +497,6 @@ viSearchHist dir toSearch cm = do
451497 Right sm -> do
452498 put vstate {lastSearch = toSearch'}
453499 setState (restore (foundHistory sm))
500+
501+ reverseDir :: Maybe (a , b , Direction ) -> Maybe (a , b , Direction )
502+ reverseDir = (fmap . fmap ) flipDir
0 commit comments