reimplement '/home/humza/Downloads/sml/shf/todo.md' motion so that it has the same behaviour as Vim. (If the cursor is not at a pair character, then check after the cursor to see if we find a pair-character there. If some location after the cursor has a pair-character, then move the cursor to it and find the pair of the character, if any.)

This commit is contained in:
2026-01-01 06:58:30 +00:00
parent a65b950b77
commit 44dbe1ffb0
5 changed files with 121 additions and 72 deletions

View File

@@ -209,14 +209,47 @@ struct
val toPrevChr = ToPrevChr.foldPrev val toPrevChr = ToPrevChr.foldPrev
structure NextPairChr =
MakeIfCharFolderNext
(struct
type env = unit
fun isPairChr chr =
chr = #"(" orelse chr = #")" orelse
chr = #"[" orelse chr = #"]" orelse
chr = #"{" orelse chr = #"}" orelse
chr = #"<" orelse chr = #">"
fun loop (strPos, str, absIdx, stl) =
if strPos = String.size str then
case stl of
str :: stl => loop (0, str, absIdx, stl)
| [] => ~1
else
let
val chr = String.sub (str, strPos)
in
if isPairChr chr then
absIdx
else
loop (strPos + 1, str, absIdx + 1, stl)
end
fun fStart (strPos, str, _, absIdx, stl, _, _) =
loop (strPos, str, absIdx, stl)
end)
fun nextPairChr (lineGap, cursorIdx) =
NextPairChr.foldNext (lineGap, cursorIdx, ())
fun helpMatchPairNext fun helpMatchPairNext
(strPos, str, absIdx, stl, origIdx, openChr, openNum, closeChr, closeNum) = (strPos, str, absIdx, stl, openChr, openNum, closeChr, closeNum) =
if strPos = String.size str then if strPos = String.size str then
case stl of case stl of
hd :: tl => hd :: tl =>
helpMatchPairNext helpMatchPairNext
(0, hd, absIdx, tl, origIdx, openChr, openNum, closeChr, closeNum) (0, hd, absIdx, tl, openChr, openNum, closeChr, closeNum)
| [] => origIdx | [] => ~1
else else
let let
val chr = String.sub (str, strPos) val chr = String.sub (str, strPos)
@@ -231,7 +264,6 @@ struct
, str , str
, absIdx + 1 , absIdx + 1
, stl , stl
, origIdx
, openChr , openChr
, openNum , openNum
, closeChr , closeChr
@@ -240,7 +272,7 @@ struct
end end
fun helpMatchPairPrev fun helpMatchPairPrev
(strPos, str, absIdx, stl, origIdx, openChr, openNum, closeChr, closeNum) = (strPos, str, absIdx, stl, openChr, openNum, closeChr, closeNum) =
if strPos < 0 then if strPos < 0 then
case stl of case stl of
hd :: tl => hd :: tl =>
@@ -249,13 +281,12 @@ struct
, hd , hd
, absIdx , absIdx
, tl , tl
, origIdx
, openChr , openChr
, openNum , openNum
, closeChr , closeChr
, closeNum , closeNum
) )
| [] => origIdx | [] => ~1
else else
let let
val chr = String.sub (str, strPos) val chr = String.sub (str, strPos)
@@ -270,7 +301,6 @@ struct
, str , str
, absIdx - 1 , absIdx - 1
, stl , stl
, origIdx
, openChr , openChr
, openNum , openNum
, closeChr , closeChr
@@ -286,7 +316,6 @@ struct
, shd , shd
, cursorIdx + 1 , cursorIdx + 1
, rightStrings , rightStrings
, cursorIdx
, #"(" , #"("
, 1 , 1
, #")" , #")"
@@ -298,7 +327,6 @@ struct
, shd , shd
, cursorIdx - 1 , cursorIdx - 1
, leftStrings , leftStrings
, cursorIdx
, #"(" , #"("
, 0 , 0
, #")" , #")"
@@ -310,7 +338,6 @@ struct
, shd , shd
, cursorIdx + 1 , cursorIdx + 1
, rightStrings , rightStrings
, cursorIdx
, #"[" , #"["
, 1 , 1
, #"]" , #"]"
@@ -322,7 +349,6 @@ struct
, shd , shd
, cursorIdx - 1 , cursorIdx - 1
, leftStrings , leftStrings
, cursorIdx
, #"[" , #"["
, 0 , 0
, #"]" , #"]"
@@ -334,7 +360,6 @@ struct
, shd , shd
, cursorIdx + 1 , cursorIdx + 1
, rightStrings , rightStrings
, cursorIdx
, #"{" , #"{"
, 1 , 1
, #"}" , #"}"
@@ -346,7 +371,6 @@ struct
, shd , shd
, cursorIdx - 1 , cursorIdx - 1
, leftStrings , leftStrings
, cursorIdx
, #"{" , #"{"
, 0 , 0
, #"}" , #"}"
@@ -358,7 +382,6 @@ struct
, shd , shd
, cursorIdx + 1 , cursorIdx + 1
, rightStrings , rightStrings
, cursorIdx
, #"<" , #"<"
, 1 , 1
, #">" , #">"
@@ -370,13 +393,12 @@ struct
, shd , shd
, cursorIdx - 1 , cursorIdx - 1
, leftStrings , leftStrings
, cursorIdx
, #"<" , #"<"
, 0 , 0
, #">" , #">"
, 1 , 1
) )
| _ => cursorIdx | _ => ~1
fun matchPair (lineGap: LineGap.t, cursorIdx) = fun matchPair (lineGap: LineGap.t, cursorIdx) =
let let

View File

@@ -398,46 +398,57 @@ struct
(* move LineGap and buffer to start of line *) (* move LineGap and buffer to start of line *)
val buffer = LineGap.goToIdx (cursorIdx, buffer) val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Cursor.matchPair (buffer, cursorIdx) val cursorIdx = Cursor.nextPairChr (buffer, cursorIdx)
in in
let if cursorIdx = ~1 then
val buffer = LineGap.goToIdx (cursorIdx, buffer) NormalFinish.clearMode app
val visualScrollColumn = else
TextScroll.getScrollColumn let
(buffer, cursorIdx, windowWidth, prevScrollColumn) val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Cursor.matchPair (buffer, cursorIdx)
in
if cursorIdx = ~1 then
NormalFinish.clearMode app
else
let
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn
(buffer, cursorIdx, windowWidth, prevScrollColumn)
val cursorLine = LineGap.idxToLineNumber (cursorIdx, buffer) val cursorLine = LineGap.idxToLineNumber (cursorIdx, buffer)
val startLine = val startLine =
TextScroll.getStartLine TextScroll.getStartLine
(prevLineNumber, cursorLine, windowHeight, #lineLength buffer) (prevLineNumber, cursorLine, windowHeight, #lineLength buffer)
val buffer = LineGap.goToLine (startLine, buffer) val buffer = LineGap.goToLine (startLine, buffer)
val drawMsg = NormalModeTextBuilder.build val drawMsg = NormalModeTextBuilder.build
( startLine ( startLine
, cursorIdx , cursorIdx
, buffer , buffer
, windowWidth , windowWidth
, windowHeight , windowHeight
, searchList , searchList
, visualScrollColumn , visualScrollColumn
) )
val drawMsg = Vector.concat drawMsg val drawMsg = Vector.concat drawMsg
val drawMsg = DrawMsg.DRAW_TEXT drawMsg val drawMsg = DrawMsg.DRAW_TEXT drawMsg
val drawMsg = [MailboxType.DRAW drawMsg] val drawMsg = [MailboxType.DRAW drawMsg]
in in
NormalModeWith.bufferAndCursorIdx NormalModeWith.bufferAndCursorIdx
( app ( app
, buffer , buffer
, cursorIdx , cursorIdx
, NORMAL_MODE "" , NORMAL_MODE ""
, startLine , startLine
, searchList , searchList
, drawMsg , drawMsg
, bufferModifyTime , bufferModifyTime
, visualScrollColumn , visualScrollColumn
) )
end end
end
end end
fun firstNonSpaceChr (app: app_type) = fun firstNonSpaceChr (app: app_type) =

View File

@@ -1,3 +1 @@
hello he()o world
world
again

View File

@@ -1752,21 +1752,39 @@ struct
(* assert *) (* assert *)
Expect.isTrue (getChr app = #"<") Expect.isTrue (getChr app = #"<")
end) end)
(* testing that % on a non-pair character is a no-op *) , test
, test "does not move when cursor is on a non-pair-character" (fn _ => "does not move when cursor is on a non-pair-character, \
let \and there is no pair-character where the cursor is at or after the cursor"
(* arrange *) (fn _ =>
val app = TestUtils.init "hello, world\n" let
val app = AppWith.idx (app, 5) (* arrange *)
val oldIdx = #cursorIdx app val app = TestUtils.init "he()o, world\n"
val app = AppWith.idx (app, 5)
val oldIdx = #cursorIdx app
(* act *) (* act *)
val app = TestUtils.update (app, CHAR_EVENT #"%") val app = TestUtils.update (app, CHAR_EVENT #"%")
val newIdx = #cursorIdx app val newIdx = #cursorIdx app
in in
(* assert *) (* assert *)
Expect.isTrue (newIdx = oldIdx) Expect.isTrue (newIdx = oldIdx)
end) end)
, test
"moves cursor when the cursor is not on a \
\pair-character, but there is a pair-character \
\after the cursor"
(fn _ =>
let
(* arrange *)
val app = TestUtils.init "he()o world\n"
val app = AppWith.idx (app, 0)
(* act *)
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"%")
in
(* assert *)
Expect.isTrue (cursorIdx = 3)
end)
] ]
(* movements which use multiple chars *) (* movements which use multiple chars *)

View File

@@ -1,8 +1,8 @@
# To-do list # To-do list
- Add tests for: - Add tests for:
- Reimplement `%` motion and `d%` motion. - Reimplement `d%` motion.
- They should both search for the next character in any pair, the same way in Vim - It should both search for the next character in any pair, the same way in Vim
- Add tests for reimplemented movements and motions - Add tests for reimplemented movements and motions
- Reimplement `di<symbol>` and `da<symbol>` - Reimplement `di<symbol>` and `da<symbol>`
- They should search for the next char in the specific pair, the same way in Vim - They should search for the next char in the specific pair, the same way in Vim