make implementation of 'di<symbol>' more robust (we check if the cursor is inside a symbol-pair before checking if there is a symbol pair after the cursor), and add tests for 'di<symbol>' motion

This commit is contained in:
2026-01-03 08:09:08 +00:00
parent bce2a5a22d
commit ba6798f476
5 changed files with 153 additions and 42 deletions

View File

@@ -209,61 +209,69 @@ struct
val toPrevChr = ToPrevChr.foldPrev
structure ToCloseChrNext =
MakeIfCharFolderNext
structure ToOpenChrPrev =
MakeIfCharFolderPrev
(struct
type env = {openChr: char, closeChr: char}
fun loop (strPos, str, absIdx, stl, openChr, closeChr, openCount) =
if strPos = String.size str then
fun loop (strPos, str, absIdx, stl, openChr, closeChr, closeCount) =
if strPos < 0 then
case stl of
str :: stl =>
loop (0, str, absIdx, stl, openChr, closeChr, openCount)
loop
( String.size str - 1
, str
, absIdx
, stl
, openChr
, closeChr
, closeCount
)
| [] => ~1
else
let
val chr = String.sub (str, strPos)
in
if chr = openChr then
loop
( strPos + 1
, str
, absIdx + 1
, stl
, openChr
, closeChr
, openCount + 1
)
else if chr = closeChr then
if openCount = 0 then
if closeCount = 0 then
absIdx
else
loop
( strPos + 1
( strPos - 1
, str
, absIdx + 1
, absIdx - 1
, stl
, openChr
, closeChr
, openCount - 1
, closeCount - 1
)
else
else if chr = closeChr then
loop
( strPos + 1
( strPos - 1
, str
, absIdx + 1
, absIdx - 1
, stl
, openChr
, closeChr
, openCount
, closeCount + 1
)
else
loop
( strPos - 1
, str
, absIdx - 1
, stl
, openChr
, closeChr
, closeCount
)
end
fun fStart (strPos, str, _, absIdx, stl, _, {openChr, closeChr}) =
loop (strPos + 1, str, absIdx + 1, stl, openChr, closeChr, 0)
loop (strPos, str, absIdx, stl, openChr, closeChr, 0)
end)
val toCloseChrNext = ToCloseChrNext.foldNext
val toOpenChrPrev = ToOpenChrPrev.foldPrev
structure NextPairChr =
MakeIfCharFolderNext

View File

@@ -1068,29 +1068,56 @@ struct
val cursorChr = LineGap.sub (cursorIdx, buffer)
in
if cursorChr = openChr orelse cursorChr = closeChr then
(* cursor is at pair, so match and delete *)
let
val otherIdx = Cursor.matchPair (buffer, cursorIdx)
in
finishDeletingInsidePair (app, buffer, cursorIdx, otherIdx, dfa, time)
end
else
let
val nextIdx =
Cursor.toCloseChrNext
(buffer, cursorIdx, {openChr = openChr, closeChr = closeChr})
in
if nextIdx = ~1 then
if otherIdx = ~1 then
NormalFinish.clearMode app
else
finishDeletingInsidePair
(app, buffer, cursorIdx, otherIdx, dfa, time)
end
else
(* check to see if we are inside pair *)
let
val prevIdx =
Cursor.toOpenChrPrev
(buffer, cursorIdx, {openChr = openChr, closeChr = closeChr})
in
if prevIdx = ~1 then
(* no openChr before cursor, so check after cursor *)
let
val buffer = LineGap.goToIdx (nextIdx, buffer)
val matchIdx = Cursor.matchPair (buffer, nextIdx)
val nextIdx =
Cursor.toNextChr
(buffer, cursorIdx, {findChr = openChr, count = 1})
in
if matchIdx = ~1 then
if nextIdx = ~1 then
NormalFinish.clearMode app
else
let
val buffer = LineGap.goToIdx (nextIdx, buffer)
val matchIdx = Cursor.matchPair (buffer, nextIdx)
in
if matchIdx = ~1 then
NormalFinish.clearMode app
else
finishDeletingInsidePair
(app, buffer, nextIdx, matchIdx, dfa, time)
end
end
else
(* there is an openChr before cursor, so match it,
* and if there is a match, then delete *)
let
val buffer = LineGap.goToIdx (prevIdx, buffer)
val otherIdx = Cursor.matchPair (buffer, prevIdx)
in
if otherIdx = ~1 then
NormalFinish.clearMode app
else
finishDeletingInsidePair
(app, buffer, nextIdx, matchIdx, dfa, time)
(app, buffer, prevIdx, otherIdx, dfa, time)
end
end
end
@@ -1155,8 +1182,7 @@ struct
else
let
val nextIdx =
Cursor.toCloseChrNext
(buffer, cursorIdx, {openChr = openChr, closeChr = closeChr})
Cursor.toNextChr (buffer, cursorIdx, {findChr = openChr, count = 1})
in
if nextIdx = ~1 then
NormalFinish.clearMode app

View File

@@ -1 +1 @@
( ( ( hello ) ) )
( ( hello ) )

View File

@@ -5075,6 +5075,82 @@ struct
end)
]
val diParenDelete = describe "delete motion 'di('"
[ test "does not delete when there is no ( after the cursor" (fn _ =>
let
(* arrange *)
val originalString = " ( ) hello\n"
val app = TestUtils.init originalString
val app = AppWith.idx (app, 7)
(* act *)
val {buffer, ...} = TestUtils.updateMany (app, "di(")
(* assert *)
val expectedString = originalString
val actualString = LineGap.toString buffer
in
Expect.isTrue (expectedString = actualString)
end)
, test
"deletes pair after cursor when there is a pair \
\before the cursor and after the cursor"
(fn _ =>
let
(* arrange *)
val originalString = " ( abc ) xyz ( def )\n"
val app = TestUtils.init originalString
val app = AppWith.idx (app, 9)
(* act *)
val {buffer, ...} = TestUtils.updateMany (app, "di(")
(* assert *)
val expectedString = " ( abc ) xyz ()\n"
val actualString = LineGap.toString buffer
in
Expect.isTrue (expectedString = actualString)
end)
, test
"deletes inside outer parens when cursor is in \
\outer paren, and there is an inner paren after cursor"
(fn _ =>
let
(* arrange *)
val originalString = "( ( hello ) )\n"
val app = TestUtils.init originalString
val app = AppWith.idx (app, 1)
(* act *)
val {buffer, ...} = TestUtils.updateMany (app, "di(")
(* assert *)
val expectedString = "()\n"
val actualString = LineGap.toString buffer
in
Expect.isTrue (expectedString = actualString)
end)
, test
"deletes inside inner parren when cursor is in inner paren-pair, \
\and there is an outer paren above this inner paren"
(fn _ =>
let
(* arrange *)
val originalString = "( ( hello ) )\n"
val app = TestUtils.init originalString
val app = AppWith.idx (app, 5)
(* act *)
val {buffer, ...} = TestUtils.updateMany (app, "di(")
(* assert *)
val expectedString = "( () )\n"
val actualString = LineGap.toString buffer
in
Expect.isTrue (expectedString = actualString)
end)
]
val tests =
[ dhDelete
, dlDelete
@@ -5105,5 +5181,6 @@ struct
, dawDelete
, daWDelete
, pairDelete
, diParenDelete
]
end

View File

@@ -1,7 +1,7 @@
# To-do list
- Add tests for:
- `di<symbol>` and `da<symbol>`
- `da<symbol>`
Afterwards, add tests for yanking.