From 6efce2dd67c8f75f081d09b7c4f703eb1724882b Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Sat, 27 Dec 2025 09:45:11 +0000 Subject: [PATCH] make 'dW' motion use transition function that is specific for deleting, and add a test to ensure that 'dW' does not delete a newline when it is not on a newline character --- fcore/cursor.sml | 1 + fcore/normal-mode/make-normal-delete.sml | 14 ++++++++++++ fcore/normal-mode/normal-mode.sml | 4 ++-- test/normal-delete.sml | 27 +++++++++++++++++++++--- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/fcore/cursor.sml b/fcore/cursor.sml index c162d3a..84fdafe 100644 --- a/fcore/cursor.sml +++ b/fcore/cursor.sml @@ -64,6 +64,7 @@ struct (* equivalent of vi's 'W' command *) val nextWORD = ViCapsWordDfa.startOfNextWORD + val nextWORDForDelete = ViCapsWordDfa.startOfNextWORDForDelete (* equivalent of vi's 'b' command *) val prevWord = ViWordDfa.startOfCurrentWord diff --git a/fcore/normal-mode/make-normal-delete.sml b/fcore/normal-mode/make-normal-delete.sml index 8436ab9..2cf035a 100644 --- a/fcore/normal-mode/make-normal-delete.sml +++ b/fcore/normal-mode/make-normal-delete.sml @@ -341,6 +341,20 @@ struct deleteAndFinish (app, cursorIdx, length, buffer, time) 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.nextWORDForDelete (buffer, cursorIdx, count) + val length = high - cursorIdx + in + deleteAndFinish (app, cursorIdx, length, buffer, time) + 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 9efd867..45095b9 100644 --- a/fcore/normal-mode/normal-mode.sml +++ b/fcore/normal-mode/normal-mode.sml @@ -258,7 +258,7 @@ struct | #"j" => NormalDelete.deleteLineDown (app, count, time) | #"k" => NormalDelete.deleteLineBack (app, count, time) | #"w" => NormalDelete.deleteWord (app, count, time) - | #"W" => NormalDelete.deleteByDfa (app, count, Cursor.nextWORD, time) + | #"W" => NormalDelete.deleteWORD (app, count, time) | #"b" => NormalDelete.deleteByDfa (app, count, Cursor.prevWord, time) | #"B" => NormalDelete.deleteByDfa (app, count, Cursor.prevWORD, time) | #"e" => @@ -347,7 +347,7 @@ struct | #"j" => NormalYankDelete.deleteLineDown (app, count, time) | #"k" => NormalYankDelete.deleteLineBack (app, count, time) | #"w" => NormalYankDelete.deleteWord (app, count, time) - | #"W" => NormalYankDelete.deleteByDfa (app, count, Cursor.nextWORD, time) + | #"W" => NormalYankDelete.deleteWORD (app, count, time) | #"b" => NormalYankDelete.deleteByDfa (app, count, Cursor.prevWord, time) | #"B" => NormalYankDelete.deleteByDfa (app, count, Cursor.prevWORD, time) | #"e" => diff --git a/test/normal-delete.sml b/test/normal-delete.sml index 3cc4c4a..ab6f382 100644 --- a/test/normal-delete.sml +++ b/test/normal-delete.sml @@ -1197,8 +1197,29 @@ struct in Expect.isTrue (stringsAreExpected andalso cursorsAreExpected) end) + , 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") + + (* assert *) + val expectedString = "\nworld\nagain\n" + val expectedCursor = 0 + + val actualString = LineGap.toString buffer + + val stringIsExpected = expectedString = actualString + val cursorIsExpected = expectedCursor = cursorIdx + in + Expect.isTrue (stringIsExpected andalso cursorIsExpected) + end) , test - "deletes newline when there is a newline after current word \ + "does not delete newline when there is a newline after current word \ \and there is another word following that newline" (fn _ => let @@ -1211,8 +1232,8 @@ struct val {buffer, cursorIdx, ...} = TestUtils.updateMany (app, "dW") (* assert *) - val expectedString = "hworld\nagain\n" - val expectedCursor = 1 + val expectedString = "h\nworld\nagain\n" + val expectedCursor = 0 val actualString = LineGap.toString buffer