From 742b571b4eb2c9dad678d6d1240854ae38c32fb5 Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Sat, 27 Dec 2025 07:32:22 +0000 Subject: [PATCH] amend 'dw' motion in line with previous commit --- fcore/cursor-dfa/vi-word-dfa.sml | 16 ++++++-- fcore/normal-mode/make-normal-delete.sml | 49 ++++++++++++++++++++++++ fcore/normal-mode/normal-mode.sml | 4 +- temp.txt | 3 +- test/normal-delete.sml | 37 ++++++++---------- test/normal-move.sml | 12 ++++++ 6 files changed, 93 insertions(+), 28 deletions(-) diff --git a/fcore/cursor-dfa/vi-word-dfa.sml b/fcore/cursor-dfa/vi-word-dfa.sml index abde534..24d44b2 100644 --- a/fcore/cursor-dfa/vi-word-dfa.sml +++ b/fcore/cursor-dfa/vi-word-dfa.sml @@ -16,8 +16,11 @@ struct val newlineToNewline: Word8.word = 0w9 val chrToNewline: Word8.word = 0w10 - val alphaToPunct: Word8.word = 0w11 - val punctToAlpha: Word8.word = 0w12 + val newlineToAlpha: Word8.word = 0w11 + val newlineToPunct: Word8.word = 0w12 + + val alphaToPunct: Word8.word = 0w13 + val punctToAlpha: Word8.word = 0w14 fun makeStart i = let @@ -63,10 +66,10 @@ struct let val chr = Char.chr i in - if Char.isAlphaNum chr orelse chr = #"_" then startAlpha + if Char.isAlphaNum chr orelse chr = #"_" then newlineToAlpha else if chr = #"\n" then newlineToNewline else if Char.isSpace chr then startSpace - else startPunct + else newlineToPunct end val startTable = Vector.tabulate (255, makeStart) @@ -99,6 +102,9 @@ struct , newlineTable , newlineTable , newlineTable + + , startAlphaTable + , startPunctTable ] structure StartOfNextWord = @@ -117,6 +123,8 @@ struct orelse currentState = spaceToAlpha orelse currentState = spaceToPunct orelse currentState = newlineToNewline + orelse currentState = newlineToAlpha + orelse currentState = newlineToPunct fun finish x = x end) diff --git a/fcore/normal-mode/make-normal-delete.sml b/fcore/normal-mode/make-normal-delete.sml index 3b7f805..36057ac 100644 --- a/fcore/normal-mode/make-normal-delete.sml +++ b/fcore/normal-mode/make-normal-delete.sml @@ -33,6 +33,9 @@ struct val low = if Cursor.isOnNewlineAfterChr (buffer, low) then low - 1 else low + + val buffer = + if #textLength buffer = 0 then LineGap.fromString "\n" else buffer in finishAfterDeletingBuffer (app, low, buffer, time, initialMsg) end @@ -324,6 +327,52 @@ struct end end + fun deleteWord (app as {buffer, ...}: app_type, count, time) = + if #textLength buffer = 1 then + NormalFinish.clearMode app + else + let + val {buffer, cursorIdx, ...} = app + + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val high = Cursor.nextWord (buffer, cursorIdx, count) + val buffer = LineGap.goToIdx (high, buffer) + 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 + end + fun deleteByDfa (app as {buffer, ...}: app_type, count, fMove, time) = if #textLength buffer = 1 then NormalFinish.clearMode app diff --git a/fcore/normal-mode/normal-mode.sml b/fcore/normal-mode/normal-mode.sml index 7106b9f..9efd867 100644 --- a/fcore/normal-mode/normal-mode.sml +++ b/fcore/normal-mode/normal-mode.sml @@ -257,7 +257,7 @@ struct * other cursor motions *) | #"j" => NormalDelete.deleteLineDown (app, count, time) | #"k" => NormalDelete.deleteLineBack (app, count, time) - | #"w" => NormalDelete.deleteByDfa (app, count, Cursor.nextWord, time) + | #"w" => NormalDelete.deleteWord (app, count, time) | #"W" => NormalDelete.deleteByDfa (app, count, Cursor.nextWORD, time) | #"b" => NormalDelete.deleteByDfa (app, count, Cursor.prevWord, time) | #"B" => NormalDelete.deleteByDfa (app, count, Cursor.prevWORD, time) @@ -346,7 +346,7 @@ struct * other cursor motions *) | #"j" => NormalYankDelete.deleteLineDown (app, count, time) | #"k" => NormalYankDelete.deleteLineBack (app, count, time) - | #"w" => NormalYankDelete.deleteByDfa (app, count, Cursor.nextWord, time) + | #"w" => NormalYankDelete.deleteWord (app, count, time) | #"W" => NormalYankDelete.deleteByDfa (app, count, Cursor.nextWORD, time) | #"b" => NormalYankDelete.deleteByDfa (app, count, Cursor.prevWord, time) | #"B" => NormalYankDelete.deleteByDfa (app, count, Cursor.prevWORD, time) diff --git a/temp.txt b/temp.txt index 917cd57..0056b4a 100644 --- a/temp.txt +++ b/temp.txt @@ -1,4 +1,3 @@ hello - - world +again diff --git a/test/normal-delete.sml b/test/normal-delete.sml index 5e44d6d..3cc4c4a 100644 --- a/test/normal-delete.sml +++ b/test/normal-delete.sml @@ -944,30 +944,27 @@ struct in Expect.isTrue (stringsAreExpected andalso cursorsAreExpected) end) - , test - "deletes newline when there is a newline after current word \ - \and there is another word following that newline" - (fn _ => - let - (* arrange *) - val originalString = "hello\nworld\nagain\n" - val app = TestUtils.init originalString - val app = AppWith.idx (app, 1) + , test "does not delete newline following word" (fn _ => + let + (* arrange *) + val originalString = "hello\nworld\nagain\n" + val app = TestUtils.init originalString + val app = AppWith.idx (app, 0) - (* act *) - val {buffer, cursorIdx, ...} = TestUtils.updateMany (app, "dw") + (* act *) + val {buffer, cursorIdx, ...} = TestUtils.updateMany (app, "dw") - (* assert *) - val expectedString = "hworld\nagain\n" - val expectedCursor = 1 + (* assert *) + val expectedString = "\nworld\nagain\n" + val expectedCursor = 0 - val actualString = LineGap.toString buffer + val actualString = LineGap.toString buffer - val stringIsExpected = expectedString = actualString - val cursorIsExpected = expectedCursor = cursorIdx - in - Expect.isTrue (stringIsExpected andalso cursorIsExpected) - end) + val stringIsExpected = expectedString = actualString + val cursorIsExpected = expectedCursor = cursorIdx + in + Expect.isTrue (stringIsExpected andalso cursorIsExpected) + end) , test "deletes until first punctuation char when on an alpha char \ \and there is no space between alpha and punctuation" diff --git a/test/normal-move.sml b/test/normal-move.sml index 03a35eb..13e00df 100644 --- a/test/normal-move.sml +++ b/test/normal-move.sml @@ -754,6 +754,18 @@ struct (* assert *) Expect.isTrue (#cursorIdx app = 4) end) + , test "moves cursor to character after newline" (fn _ => + let + (* arrange *) + val app = TestUtils.init "hello\nworld\nagain\n" + val app = AppWith.idx (app, 0) + + (* act *) + val app = TestUtils.update (app, CHAR_EVENT #"w") + in + (* assert *) + Expect.isTrue (#cursorIdx app = 6) + end) ] val WMove = describe "move motion 'W'"