implement the difference between the 'w' motion and 'dw' motion using the transition table in vi-word-dfa, rather than convoluted if-branching

This commit is contained in:
2025-12-27 08:42:54 +00:00
parent 742b571b4e
commit 1df0952f08
3 changed files with 39 additions and 35 deletions

View File

@@ -132,6 +132,40 @@ struct
val fStart = Folder.foldNext
end)
(* This is the same as StartOfNextWord, except for the `isFinal` function.
* The difference is that the `isFinal` function here considers
* the state where any character goes to a newline,
* to be a final state.
* This is because, in Vim, the 'w' motion will move past a newline
* when that newline is preceded by a non-newline character.
* However, the 'dw' motion deletes until that newline
* (not including the newline itself).
* It is easier, less fragile, and perhaps clearer,
* to implement the difference using a transition table like this
* than convoluted if-statements. *)
structure StartOfNextWordForDelete =
MakeNextDfaLoop
(struct
val startState = startState
structure Folder =
MakeCharFolderNext
(struct
val startState = startState
val tables = tables
fun isFinal currentState =
currentState = alphaToPunct orelse currentState = punctToAlpha
orelse currentState = spaceToAlpha
orelse currentState = spaceToPunct
orelse currentState = chrToNewline
fun finish x = x
end)
val fStart = Folder.foldNext
end)
structure EndOfPrevWord =
MakePrevDfaLoop
(struct
@@ -232,6 +266,7 @@ struct
(* w *)
val startOfNextWord = StartOfNextWord.next
val startOfNextWordForDelete = StartOfNextWordForDelete.next
(* ge *)
val endOfPrevWord = EndOfPrevWord.prev
(* b *)

View File

@@ -60,6 +60,7 @@ struct
(* equivalent of vi's 'w' command *)
val nextWord = ViWordDfa.startOfNextWord
val nextWordForDelete = ViWordDfa.startOfNextWordForDelete
(* equivalent of vi's 'W' command *)
val nextWORD = ViCapsWordDfa.startOfNextWORD

View File

@@ -335,42 +335,10 @@ struct
val {buffer, cursorIdx, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val high = Cursor.nextWord (buffer, cursorIdx, count)
val buffer = LineGap.goToIdx (high, buffer)
val high = Cursor.nextWordForDelete (buffer, cursorIdx, count)
val length = high - cursorIdx
in
(* The Cursor.nextWord skips newlines in some cases,
* which makes sense for the 'w' move motion.
* However, we sometimes don't want to skip newlines when deleting.
* For example, when 'dw' is used on the last word in a line,
* the 'w' motion would go to the first character of the next line
* but the 'dw' motion should delete until the newline. *)
if high >= #textLength buffer then
let
val finish = #textLength buffer - 1
val buffer = LineGap.goToIdx (finish, buffer)
val length =
if Cursor.isCursorAtStartOfLine (buffer, finish) then
finish - cursorIdx
else
high - cursorIdx
in
deleteAndFinish (app, cursorIdx, length, buffer, time)
end
else if Cursor.isCursorAtStartOfLine (buffer, high) then
deleteAndFinish (app, cursorIdx, high - cursorIdx, buffer, time)
else
let
val beforeHigh = high - 1
val buffer = LineGap.goToIdx (beforeHigh, buffer)
val high =
if
Cursor.isCursorAtStartOfLine (buffer, beforeHigh)
andalso beforeHigh <> cursorIdx
then beforeHigh
else high
in
deleteAndFinish (app, cursorIdx, high - cursorIdx, buffer, time)
end
deleteAndFinish (app, cursorIdx, length, buffer, time)
end
fun deleteByDfa (app as {buffer, ...}: app_type, count, fMove, time) =