2026-01-15 12:32:13 +00:00
|
|
|
structure NormalMoveTests =
|
2025-03-22 05:18:25 +00:00
|
|
|
struct
|
|
|
|
|
open Railroad
|
|
|
|
|
open Railroad.Test
|
|
|
|
|
open InputMsg
|
|
|
|
|
|
|
|
|
|
fun getChr (app: AppType.app_type) =
|
|
|
|
|
let
|
|
|
|
|
val {cursorIdx, buffer, ...} = app
|
|
|
|
|
val c = LineGap.substring (cursorIdx, 1, buffer)
|
|
|
|
|
in
|
|
|
|
|
String.sub (c, 0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
val hMove = describe "move motion 'h'"
|
2025-09-20 04:18:15 +01:00
|
|
|
[ test "moves cursor left by one when cursorIdx > 0 and is not on a newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-09-20 04:18:15 +01:00
|
|
|
val app = AppWith.idx (app, 1)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test "does not move cursor when cursor is already at index 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello\n\nworld\n"
|
2025-09-20 04:18:15 +01:00
|
|
|
val app = AppWith.idx (app, 0)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to char before a newline\
|
|
|
|
|
\ when there is just one newline to the left"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello\nworld\n"
|
2025-09-20 04:18:15 +01:00
|
|
|
val app = AppWith.idx (app, 6)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 4)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
2025-09-21 21:45:30 +01:00
|
|
|
"moves cursor past first newline immediately following \
|
|
|
|
|
\a non-newline character"
|
2025-09-20 04:18:15 +01:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello\n\nworld\n"
|
2025-09-20 04:18:15 +01:00
|
|
|
val app = AppWith.idx (app, 7)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
2025-09-21 21:45:30 +01:00
|
|
|
Expect.isTrue (cursorIdx = 6)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor past newline when we see, to the left, \
|
|
|
|
|
\ a newline with a chr prior to it"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello\nworld\n"
|
2025-09-21 21:45:30 +01:00
|
|
|
val app = AppWith.idx (app, 6)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 4)
|
2025-09-20 04:18:15 +01:00
|
|
|
end)
|
2025-09-22 04:10:39 +01:00
|
|
|
, test "moves cursor to a newline when newline is not preceded by char"
|
2025-09-20 04:18:15 +01:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-22 04:10:39 +01:00
|
|
|
val app = TestUtils.init "\n\n\nhello\n"
|
|
|
|
|
val app = AppWith.idx (app, 3)
|
2025-09-20 04:18:15 +01:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-09-22 04:10:39 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"h")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"h")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"h")
|
|
|
|
|
val app4 = TestUtils.update (app3, CHAR_EVENT #"h")
|
|
|
|
|
|
2025-09-20 04:18:15 +01:00
|
|
|
(* assert *)
|
2025-09-22 04:10:39 +01:00
|
|
|
val c1 = #cursorIdx app1 = 2
|
|
|
|
|
val c2 = #cursorIdx app2 = 1
|
|
|
|
|
val c3 = #cursorIdx app3 = 0
|
|
|
|
|
val c4 = #cursorIdx app4 = 0
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3 andalso c4)
|
2025-09-20 04:18:15 +01:00
|
|
|
end)
|
|
|
|
|
]
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
val lMove = describe "move motion 'l'"
|
2025-09-20 06:31:14 +01:00
|
|
|
[ test "moves cursor right by one when cursorIdx < length" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-09-20 06:31:14 +01:00
|
|
|
val {cursorIdx = oldCursorIdx, ...} = app
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 06:31:14 +01:00
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (oldCursorIdx = 0 andalso cursorIdx = 1)
|
|
|
|
|
end)
|
2025-09-23 12:15:14 +01:00
|
|
|
, test
|
|
|
|
|
"does not move cursor when cursorIdx is at end of buffer \
|
|
|
|
|
\and last char is a newline preceded by a newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello world\n\n"
|
|
|
|
|
val initialCursorIdx = String.size str - 1
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-23 12:15:14 +01:00
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = initialCursorIdx)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor when cursorIdx is at end of buffer \
|
|
|
|
|
\and last char is a non-newline preceded by a non-newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello world"
|
|
|
|
|
val initialCursorIdx = String.size str - 1
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = initialCursorIdx)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor when cursorIdx is at end of buffer \
|
|
|
|
|
\and last char is a newline preceded by a non-newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello world\n"
|
|
|
|
|
val initialCursorIdx = String.size str - 2
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = initialCursorIdx)
|
|
|
|
|
end)
|
2025-09-22 04:10:39 +01:00
|
|
|
, test "moves cursor to char past newline when newline is preceded by char"
|
2025-03-22 05:18:25 +00:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello\nworld\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 6)
|
|
|
|
|
end)
|
2025-09-22 04:10:39 +01:00
|
|
|
, test "moves cursor to second newline when newline is preceded by char"
|
2025-09-20 06:31:14 +01:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello\n\nworld\n"
|
2025-09-20 06:31:14 +01:00
|
|
|
val app = AppWith.idx (app, 4)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
2025-09-22 04:10:39 +01:00
|
|
|
Expect.isTrue (cursorIdx = 6)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to each newline without skipping when no newline \
|
|
|
|
|
\is preceded by char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "\n\n\nhello\n"
|
|
|
|
|
val app = AppWith.idx (app, 0)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"l")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"l")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"l")
|
|
|
|
|
val app4 = TestUtils.update (app3, CHAR_EVENT #"l")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val c1 = #cursorIdx app1 = 1
|
|
|
|
|
val c2 = #cursorIdx app2 = 2
|
|
|
|
|
val c3 = #cursorIdx app3 = 3
|
|
|
|
|
val c4 = #cursorIdx app4 = 4
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3 andalso c4)
|
2025-09-20 06:31:14 +01:00
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val jMove = describe "move motion 'j'"
|
2025-09-20 08:09:35 +01:00
|
|
|
[ test "moves cursur down one column when column = 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
(* "world" at end of string is intentionally misspelled as "qorld"
|
|
|
|
|
* since "world" appears twice and it is useful to differentiate them
|
|
|
|
|
* *)
|
|
|
|
|
val app = TestUtils.init "hello \nworld \ngoodbye \nqorld \n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 08:09:35 +01:00
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"j")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"j")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 08:09:35 +01:00
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"w"
|
|
|
|
|
val c2 = getChr app2 = #"g"
|
|
|
|
|
val c3 = getChr app3 = #"q"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursur down one column when column = 1" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello \nworld \nbye \nfriends \n"
|
|
|
|
|
val app = AppWith.idx (app, 1)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 08:09:35 +01:00
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"j")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"j")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 08:09:35 +01:00
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"o"
|
|
|
|
|
val c2 = getChr app2 = #"y"
|
|
|
|
|
val c3 = getChr app3 = #"r"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursur down one column when column = 2" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello \nworld \nbye \nfriends \n"
|
|
|
|
|
val app = AppWith.idx (app, 2)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"j")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"j")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"r"
|
|
|
|
|
val c2 = getChr app2 = #"e"
|
|
|
|
|
val c3 = getChr app3 = #"i"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves to last char on below column \
|
|
|
|
|
\when cursor is on a column that is greater than \
|
|
|
|
|
\the number of columns on the next line"
|
2025-03-22 05:18:25 +00:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 08:09:35 +01:00
|
|
|
val str =
|
|
|
|
|
"hello world!\n\
|
|
|
|
|
\bye!\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-09-20 08:09:35 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"j")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
2025-09-20 08:09:35 +01:00
|
|
|
val c1 = getChr app = #"!"
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
2025-09-20 08:09:35 +01:00
|
|
|
Expect.isTrue c1
|
2025-03-22 05:18:25 +00:00
|
|
|
end)
|
2025-09-22 05:37:54 +01:00
|
|
|
, test "when next newline is preceded by char, goes to idx after newline"
|
2025-03-22 05:18:25 +00:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 08:09:35 +01:00
|
|
|
val app = TestUtils.init "hello\n\nworld\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
2025-09-22 05:37:54 +01:00
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
end)
|
2025-09-20 08:09:35 +01:00
|
|
|
, test "moves to same column on last line after a count" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str =
|
|
|
|
|
"let\n\
|
|
|
|
|
\hello\n\
|
|
|
|
|
\in\n\
|
|
|
|
|
\0\n\
|
|
|
|
|
\end\n"
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app1 = AppWith.idx (app, 0)
|
|
|
|
|
val app2 = AppWith.idx (app, 1)
|
|
|
|
|
val app3 = AppWith.idx (app, 2)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
2025-09-23 11:04:47 +01:00
|
|
|
val newApp1 = TestUtils.updateMany (app1, "4j")
|
|
|
|
|
val newApp2 = TestUtils.updateMany (app2, "4j")
|
|
|
|
|
val newApp3 = TestUtils.updateMany (app3, "4j")
|
2025-09-20 08:09:35 +01:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr newApp1 = #"e"
|
|
|
|
|
val c2 = getChr newApp2 = #"n"
|
|
|
|
|
val c3 = getChr newApp3 = #"d"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
2025-09-23 11:11:33 +01:00
|
|
|
, test "leaves cursor at same idx when on the last line" (fn _ =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val str = "hello \nworld \ntime to go\n\n"
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init str
|
2025-09-20 10:04:38 +01:00
|
|
|
|
2025-09-23 11:04:47 +01:00
|
|
|
val initialCursorIdx = String.size str - 1
|
2025-09-20 10:04:38 +01:00
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
2025-09-23 11:11:33 +01:00
|
|
|
Expect.isTrue (cursorIdx = initialCursorIdx)
|
2025-03-22 05:18:25 +00:00
|
|
|
end)
|
2025-09-22 05:37:54 +01:00
|
|
|
, test "goes to next idx when cursor is on a newline" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\n\nworld\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-22 05:37:54 +01:00
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 7)
|
|
|
|
|
end)
|
2025-09-23 13:18:36 +01:00
|
|
|
, test
|
2025-09-23 13:30:54 +01:00
|
|
|
"goes to second-last newline in file \
|
2025-09-23 13:18:36 +01:00
|
|
|
\when newline is preceded by a non-newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:30:54 +01:00
|
|
|
val str = "hello\nworld\n"
|
|
|
|
|
val initialCursorIdx = 4
|
2025-09-23 13:18:36 +01:00
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
2025-09-23 15:22:49 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
2025-09-23 13:30:54 +01:00
|
|
|
|
2025-09-23 13:18:36 +01:00
|
|
|
(* assert *)
|
2025-09-23 13:30:54 +01:00
|
|
|
val expectedIdx = 10
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
2025-09-23 13:18:36 +01:00
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"goes to last newline in file \
|
|
|
|
|
\when newline is preceded by another newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\n\nworld\n\n"
|
|
|
|
|
val initialCursorIdx = String.size str - 5
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val expectedIdx = String.size str - 1
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
|
|
|
|
end)
|
2025-09-23 15:22:49 +01:00
|
|
|
, test "goes to last line in file when last char is not a newline" (fn _ =>
|
2025-09-23 13:30:54 +01:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\nworld"
|
2025-09-23 15:22:49 +01:00
|
|
|
val initialCursorIdx = 0
|
2025-09-23 13:30:54 +01:00
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
2025-09-23 15:22:49 +01:00
|
|
|
val expectedIdx = 6
|
2025-09-23 13:30:54 +01:00
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"leaves cursor at same idx when on last line \
|
|
|
|
|
\and file ends with a non-newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\nworld"
|
|
|
|
|
val initialCursorIdx = 6
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"j")
|
|
|
|
|
|
2025-09-23 15:22:49 +01:00
|
|
|
(* assert *)
|
|
|
|
|
val expectedIdx = initialCursorIdx
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not go to last chr in file \
|
|
|
|
|
\when last chr is a newline preceded by a non-newline \
|
|
|
|
|
\and a count is provided"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\nworld\n"
|
|
|
|
|
val initialCursorIdx = 0
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.updateMany (app, "2j")
|
|
|
|
|
|
2025-09-23 13:30:54 +01:00
|
|
|
(* assert *)
|
2025-09-23 15:30:28 +01:00
|
|
|
val expectedIdx = 6
|
2025-09-23 13:30:54 +01:00
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
|
|
|
|
end)
|
2025-09-20 10:04:38 +01:00
|
|
|
]
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 10:04:38 +01:00
|
|
|
val kMove = describe "move motion 'k'"
|
|
|
|
|
[ test "moves cursur up one column when column = 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "0__\n4___\n9___\n14_\n"
|
2025-09-20 10:04:38 +01:00
|
|
|
val app = AppWith.idx (app, 14)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 10:04:38 +01:00
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"k")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"k")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 10:04:38 +01:00
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"9"
|
|
|
|
|
val c2 = getChr app2 = #"4"
|
|
|
|
|
val c3 = getChr app3 = #"0"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursur up one column when column = 1" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "_w_\n_5__\n_10_\n_15\n"
|
2025-09-20 10:04:38 +01:00
|
|
|
val app = AppWith.idx (app, 15)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-09-20 10:04:38 +01:00
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"k")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"k")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"1"
|
|
|
|
|
val c2 = getChr app2 = #"5"
|
|
|
|
|
val c3 = getChr app3 = #"w"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursur up one column when column = 2" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "__2\n__6\n__10\n__15\n"
|
|
|
|
|
val app = AppWith.idx (app, 15)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
val app2 = TestUtils.update (app1, CHAR_EVENT #"k")
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"k")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val c1 = getChr app1 = #"1"
|
|
|
|
|
val c2 = getChr app2 = #"6"
|
|
|
|
|
val c3 = getChr app3 = #"2"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (c1 andalso c2 andalso c3)
|
|
|
|
|
end)
|
2025-09-22 07:13:04 +01:00
|
|
|
, test "goes to last newline when there are two newlines preceding cursor"
|
2025-03-22 05:18:25 +00:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 10:04:38 +01:00
|
|
|
val str = "hello\n\n world\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"k")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
2025-09-20 10:04:38 +01:00
|
|
|
(* assert *)
|
2025-09-22 07:13:04 +01:00
|
|
|
Expect.isTrue (cursorIdx = 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
end)
|
2025-09-20 10:04:38 +01:00
|
|
|
, test "leaves cursor at same idx when already on first line" (fn _ =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello \nworld \ntime to go\n"
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init str
|
2025-03-22 05:18:25 +00:00
|
|
|
(* line below does nothing; just for explicitness *)
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 0)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"k")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val isAtStart = cursorIdx = 0
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue isAtStart
|
|
|
|
|
end)
|
2025-09-20 10:04:38 +01:00
|
|
|
, test
|
|
|
|
|
"goes to last column of previous line when cursor is \
|
|
|
|
|
\on a column greater than the number of columns in the previous line"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str =
|
|
|
|
|
"hello world\n\
|
|
|
|
|
\now a quite long line is next\n"
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, String.size str - 2)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 10)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
2025-09-22 07:13:04 +01:00
|
|
|
"when the previous newline is preceded by a non-newline, \
|
|
|
|
|
\jumps past newline"
|
2025-09-20 10:04:38 +01:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-22 07:13:04 +01:00
|
|
|
val str = "hello\n\nworld\n"
|
2025-09-20 10:04:38 +01:00
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
2025-09-22 07:13:04 +01:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-09-20 10:04:38 +01:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
2025-09-22 07:13:04 +01:00
|
|
|
Expect.isTrue (cursorIdx = 0)
|
2025-09-20 10:04:38 +01:00
|
|
|
end)
|
2025-09-23 09:00:50 +01:00
|
|
|
, test
|
|
|
|
|
"when file ends with two newlines, \
|
|
|
|
|
\and cursor is on second-last newline, \
|
|
|
|
|
\we should be able to move up by one line"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "hello\nworld\n\n"
|
|
|
|
|
|
|
|
|
|
val app = TestUtils.init str
|
2025-09-23 12:15:14 +01:00
|
|
|
val app = AppWith.idx (app, 12)
|
2025-09-23 09:00:50 +01:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"k")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 6)
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val wMove = describe "move motion 'w'"
|
|
|
|
|
[ test "moves cursor to start of next word in contiguous string" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chr = String.sub ("hello world", cursorIdx)
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (chr = #"w")
|
|
|
|
|
end)
|
2025-12-27 06:05:36 +00:00
|
|
|
, test
|
|
|
|
|
"moves cursor to first char-after-newline \
|
|
|
|
|
\when cursor is on last word of line \
|
|
|
|
|
\and there is another line after this one"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello \n\n\n world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-12-27 06:05:36 +00:00
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"w")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 7)
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
, test "does not break on undescore when cursor is on alphanumeric char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello_world goodbye_world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"g")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on punctuation when cursor is on alphanumeric char" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello, world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #",")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on alphanumeric char when cursor is on punctuation" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!#%^()hello\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on non-blank char when on blank char" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "0123 \t \n \t 789\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"7")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to first alphanumeric char when on punctuation"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!!! hello\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val startsAtExc = getChr app = #"!"
|
|
|
|
|
val movedToH = getChr app1 = #"h"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (startsAtExc andalso movedToH)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to last char when cursor is on last word" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chrIsEnd = getChr app = #"d"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue chrIsEnd
|
|
|
|
|
end)
|
2025-09-23 08:20:40 +01:00
|
|
|
, test
|
2025-09-23 12:15:14 +01:00
|
|
|
"moves cursor to second newline when cursor is on the last word \
|
2025-09-23 08:20:40 +01:00
|
|
|
\and the file ends with two newlines"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello\n\n"
|
|
|
|
|
val app = AppWith.idx (app, 0)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
2025-09-23 12:15:14 +01:00
|
|
|
Expect.isTrue (#cursorIdx app = 6)
|
2025-09-23 08:20:40 +01:00
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move to or past newline when cursor is on last word \
|
|
|
|
|
\and text ends with newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello\n"
|
|
|
|
|
val app = AppWith.idx (app, 0)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = 4)
|
|
|
|
|
end)
|
2025-12-27 07:32:22 +00:00
|
|
|
, 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)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val WMove = describe "move motion 'W'"
|
|
|
|
|
[ test "moves cursor to start of next WORD in contiguous string" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"W")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chr = String.sub ("hello world", cursorIdx)
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (chr = #"w")
|
|
|
|
|
end)
|
2025-12-27 09:35:32 +00:00
|
|
|
, test
|
|
|
|
|
"moves cursor to first char-after-newline \
|
|
|
|
|
\when cursor is on last word of line \
|
|
|
|
|
\and there is another line after this one"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello \n\n\n world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-12-27 09:35:32 +00:00
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"W")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 7)
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
, test "does not break on punctuation when cursor is on alphanumeric char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
(* vi's definition of 'WORD' instead of 'word' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello, world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"W")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"w")
|
|
|
|
|
end)
|
|
|
|
|
, test "does not break on alphanumeric char when cursor is on punctuation"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
(* vi's definition of 'WORD' instead of 'word' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "#!hello!!! world!!!\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"W")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"w")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to first non-blank when cursor is on blank" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "0123 \t \n \t 789\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"7")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to last char when cursor is on last word" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"w")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chrIsEnd = getChr app = #"d"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue chrIsEnd
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val eMove = describe "move motion 'e'"
|
|
|
|
|
[ test
|
|
|
|
|
"moves cursor to last alphanumeric char in contiguous string\
|
|
|
|
|
\when in alphanumeric word and there is at least one\
|
|
|
|
|
\alphanumeric char after cursor"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"o")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last punctuation char in contiguous string\
|
|
|
|
|
\when in punctuation word and there is at least one\
|
|
|
|
|
\punctuation char after cursor"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "#$%!^ world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"^")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last char of next word,\
|
|
|
|
|
\when cursor is on last char of current word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"d")
|
|
|
|
|
end)
|
|
|
|
|
, test "does not break on undescore when cursor is on alphanumeric char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello_world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"d")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on undescore when cursor is on punctuation char" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "#!^*(_#!@*(\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"(")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on punctuation when cursor is on alphanumeric char" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello, world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"o")
|
|
|
|
|
end)
|
|
|
|
|
, test "breaks on alphanumeric char when cursor is on punctuation" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!#%^()hello\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #")")
|
|
|
|
|
end)
|
2025-12-27 06:05:36 +00:00
|
|
|
, test "skips past tab" (fn _ =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-12-27 06:05:36 +00:00
|
|
|
val app = TestUtils.init "0123 \t \t \t 789\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"9")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last char in punctuation string \
|
|
|
|
|
\when cursor is on punctuation"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!!! hello\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 2)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to last char when cursor is on last word" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chrIsEnd = getChr app = #"d"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue chrIsEnd
|
|
|
|
|
end)
|
2025-12-27 06:05:36 +00:00
|
|
|
, test
|
|
|
|
|
"moves cursor to end of current word \
|
|
|
|
|
\when current word is last word in line"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "hello\n\n\nworld\n"
|
|
|
|
|
val app = AppWith.idx (app, 0)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"e")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val isAtEndOfHello = getChr app = #"o"
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue isAtEndOfHello
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val EMove = describe "move motion 'E'"
|
|
|
|
|
[ test "moves cursor to last char in WORD when in contiguous string"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hel!!!lo world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"o")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last char of next WORD,\
|
|
|
|
|
\when cursor is on last char of current WORD"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "#ELL) !@*(ORL$\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"$")
|
|
|
|
|
end)
|
|
|
|
|
, test "does not break on punctuation when in alphanumeric char" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello, world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #",")
|
|
|
|
|
end)
|
|
|
|
|
, test "does not break on alphanumeric char when in punctuation" (fn _ =>
|
|
|
|
|
(* vi's definition of 'word' instead of 'WORD' *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!#%^()hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"o")
|
|
|
|
|
end)
|
2025-12-27 06:05:36 +00:00
|
|
|
, test "skips past tab: '\\t'" (fn _ =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-12-27 06:05:36 +00:00
|
|
|
val app = TestUtils.init "0123 \t \t \t 789\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 4)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val cursorChr = getChr app
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (cursorChr = #"9")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to last char when cursor is on last word" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world!\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"E")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"!")
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val bMove = describe "move motion 'b'"
|
|
|
|
|
[ test "leaves cursor at 0 when cursor is already at 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
2025-07-21 00:28:25 +01:00
|
|
|
, test "moves cursor previous word when on first character of next word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-07-21 00:28:25 +01:00
|
|
|
val app = AppWith.idx (app, 6)
|
|
|
|
|
val chr1 = getChr app
|
|
|
|
|
|
2025-08-20 13:50:57 +01:00
|
|
|
val app2 = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-07-21 00:28:25 +01:00
|
|
|
val chr2 = getChr app2
|
|
|
|
|
in
|
|
|
|
|
Expect.isTrue (chr1 = #"w" andalso chr2 = #"h")
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
, test "moves cursor to 0 when cursor > 0 and cursor is on first word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 3)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first alphanumeric char after whitespace \
|
|
|
|
|
\when in alphanumeric word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first alphanumeric char after punctuation \
|
|
|
|
|
\when in alphanumeric word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!*#hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first punctuation char after whitespace \
|
|
|
|
|
\when in punctuation word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init " !@#$%^&*()\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"!")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first punctuation char after \
|
|
|
|
|
\alphanumeric char when in punctuation word"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "abc!@#$%^&*()\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"b")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"!")
|
|
|
|
|
end)
|
2025-12-27 06:05:36 +00:00
|
|
|
, test "stops when char preceding word is newline" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val originalString = "hello\n\n\nworld\n"
|
|
|
|
|
val app = TestUtils.init originalString
|
|
|
|
|
val app = AppWith.idx (app, String.size originalString - 2)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"b")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"w")
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val BMove = describe "move motion 'B'"
|
|
|
|
|
[ test "leaves cursor at 0 when cursor is already at 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to 0 when cursor > 0 and cursor is on first WORD"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 3)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first non-space char after whitespace \
|
|
|
|
|
\when in WORD"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to 0 when cursor is on first letter of first WORD \
|
|
|
|
|
\and there are leadinng spaces before first letter"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 3)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first char in WORD \
|
|
|
|
|
\when in alphanumeric word preceded by punctuation"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "!*#hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"!")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to first char after whitespace when in WORD" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " !qwerty@#$%^&*()\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 17)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"!")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first char in WORD \
|
|
|
|
|
\when in punctuation word preceded by alphanumeric"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "abc!@#$%^&*()\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 11)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"B")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"a")
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val zeroMove = describe "move motion '0'"
|
2025-09-23 16:05:23 +01:00
|
|
|
[ test "moves cursor to 0 when on first line" (fn _ =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello w7rld\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"0")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test "leaves cursor on 0 when cursor is already on 0" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"0")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test "leaves cursor at same idx when cursor is on '\\n'" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n hello again\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 11)
|
2025-03-22 05:18:25 +00:00
|
|
|
val {cursorIdx = oldIdx, ...} = app
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx = newIdx, ...} =
|
2025-08-20 13:50:57 +01:00
|
|
|
TestUtils.update (app, CHAR_EVENT #"0")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (oldIdx = newIdx)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
2025-09-23 16:05:23 +01:00
|
|
|
"moves cursor to first char after newline when cursor is after first line"
|
2025-03-22 05:18:25 +00:00
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello world\n#ello again\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 21)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"0")
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val chr = getChr app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (chr = #"#")
|
|
|
|
|
end)
|
2025-09-23 16:05:23 +01:00
|
|
|
, test "leaves cursor at same idx when on last newline" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str =
|
|
|
|
|
"hello\n\
|
|
|
|
|
\\n\
|
|
|
|
|
\\n\
|
|
|
|
|
\world\n\
|
|
|
|
|
\\n\
|
|
|
|
|
\\n"
|
|
|
|
|
|
|
|
|
|
val initialCursorIdx = String.size str - 1
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
val app = AppWith.idx (app, initialCursorIdx)
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val {cursorIdx, ...} = TestUtils.update (app, CHAR_EVENT #"0")
|
|
|
|
|
|
|
|
|
|
(* assert *)
|
|
|
|
|
val expectedIdx = initialCursorIdx
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (cursorIdx = expectedIdx)
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val dlrMove = describe "move motion '$'"
|
|
|
|
|
[ test "moves cursor to char before '\\n' in contiguous string" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello wor9\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"$")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"9")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"leaves cursor at same idx in contiguous string\
|
|
|
|
|
\when char after cursor is '\\n'"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello\n world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 11)
|
2025-03-22 05:18:25 +00:00
|
|
|
val oldIdx = #cursorIdx app
|
|
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"$")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
|
|
|
|
|
val nchr = getChr app
|
|
|
|
|
val nchr = Char.toString nchr ^ "\n"
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (oldIdx = newIdx)
|
|
|
|
|
end)
|
2025-09-07 21:09:20 +01:00
|
|
|
, test "does not move cursor when cursor is on a newline" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello\n\nworld\n"
|
2025-09-07 21:09:20 +01:00
|
|
|
val app = AppWith.idx (app, 6)
|
|
|
|
|
val oldIdx = #cursorIdx app
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"$")
|
|
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
|
|
|
|
|
val nchr = getChr app
|
|
|
|
|
val nchr = Char.toString nchr ^ "\n"
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (oldIdx = newIdx)
|
|
|
|
|
end)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
2025-03-22 06:13:01 +00:00
|
|
|
val hatMove = describe "move motion '^'"
|
2025-03-22 05:18:25 +00:00
|
|
|
[ test
|
|
|
|
|
"moves cursor to first non-space char in first line\
|
|
|
|
|
\when first line starts with spaces\
|
|
|
|
|
\and cursor is on first space"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " 3ello world\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"^")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"3")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first non-space char in first line\
|
|
|
|
|
\when first line starts with space\
|
|
|
|
|
\and cursor is after first non-space char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init " 3ell7 world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"^")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"3")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first non-space char\
|
|
|
|
|
\when cursor is after first line\
|
|
|
|
|
\and before first non-space char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello\n world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 7)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"^")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"w")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to first non-space char\
|
|
|
|
|
\when cursor is after first line\
|
|
|
|
|
\and after first non-space char"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hello\n world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 11)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"^")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"w")
|
|
|
|
|
end)
|
|
|
|
|
, test "leaves cursor in same position when on '\\n'" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "hel\nlo\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 3)
|
2025-03-22 05:18:25 +00:00
|
|
|
val oldIdx = #cursorIdx app
|
|
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"^")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = oldIdx)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
val GMove = describe "move motion 'G'"
|
2025-09-23 11:57:19 +01:00
|
|
|
[ test
|
|
|
|
|
"moves cursor to second last char in buffer, \
|
|
|
|
|
\if last char is a newline preced by a non-newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
(* Note: We assume unix-style line endings:
|
|
|
|
|
* End of file always has \n at the end. *)
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "01234\n56789\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"G")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = String.size str - 2)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last char in buffer, \
|
|
|
|
|
\if last char is a newline and second-last char is also a newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "01234\n5678\n\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"G")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = String.size str - 1)
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"moves cursor to last char in buffer, \
|
|
|
|
|
\if last char is not a newline and second-last char \
|
|
|
|
|
\is also not a newline"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val str = "01234\n5678\n\n"
|
|
|
|
|
val app = TestUtils.init str
|
|
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"G")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = String.size str - 1)
|
|
|
|
|
end)
|
|
|
|
|
]
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
val percentMove = describe "move motion '%'"
|
|
|
|
|
[ test "moves to next ) when cursor is on (" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(hello)\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #")")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to preceding ( when cursur is on )" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(hello)\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"(")
|
|
|
|
|
end)
|
|
|
|
|
(* testing that cursor goes to correct level of nesting *)
|
|
|
|
|
, test "moves to outermost ) when cursor is on outermost (" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 10)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to outermost ( when cursor is on outermost )" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 0)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to middle ) when cursor is on middle (" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 1)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 9)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to middle ( when cursor is on middle )" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 9)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 1)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to innermost ) when cursor is on innermost (" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 2)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 8)
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to innermost ( when cursor is on innermost )" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "(((hello)))\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 8)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = 2)
|
|
|
|
|
end)
|
|
|
|
|
(* testing different pair combinations *)
|
|
|
|
|
, test "moves to next ] when cursor is on [" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "[hello]\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"]")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to preceding [ when cursur is on ]" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "[hello]\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"[")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to next } when cursor is on {" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "{hello}\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"}")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to preceding { when cursur is on }" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "{hello}\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"{")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to next > when cursor is on <" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "<hello>\n"
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #">")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves to preceding < when cursur is on >" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-20 03:44:48 +01:00
|
|
|
val app = TestUtils.init "<hello>\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 6)
|
2025-03-22 05:18:25 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"<")
|
|
|
|
|
end)
|
2026-01-01 06:58:30 +00:00
|
|
|
, test
|
|
|
|
|
"does not move when cursor is on a non-pair-character, \
|
|
|
|
|
\and there is no pair-character where the cursor is at or after the cursor"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
|
|
|
|
val app = TestUtils.init "he()o, world\n"
|
|
|
|
|
val app = AppWith.idx (app, 5)
|
|
|
|
|
val oldIdx = #cursorIdx app
|
2025-03-22 05:18:25 +00:00
|
|
|
|
2026-01-01 06:58:30 +00:00
|
|
|
(* act *)
|
|
|
|
|
val app = TestUtils.update (app, CHAR_EVENT #"%")
|
|
|
|
|
val newIdx = #cursorIdx app
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (newIdx = oldIdx)
|
|
|
|
|
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)
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
|
2025-03-22 06:13:01 +00:00
|
|
|
(* movements which use multiple chars *)
|
|
|
|
|
fun updateMany (app, str) =
|
|
|
|
|
let
|
|
|
|
|
fun loop (pos, app) =
|
|
|
|
|
if pos = String.size str then
|
|
|
|
|
app
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, pos)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app = TestUtils.update (app, CHAR_EVENT chr)
|
2025-03-22 06:13:01 +00:00
|
|
|
in
|
|
|
|
|
loop (pos + 1, app)
|
|
|
|
|
end
|
|
|
|
|
in
|
|
|
|
|
loop (0, app)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
val tMove = describe "move motion 't'"
|
|
|
|
|
[ test
|
|
|
|
|
"motion 'td' moves cursor to char before 'd' in string \"hello world\""
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 06:13:01 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "td")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test "repeating 't' motion with same char does not move cursor" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 06:13:01 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "td")
|
|
|
|
|
val app2 = updateMany (app1, "td")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2 andalso getChr app1 = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor at all when char following 't' is not in string"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 06:13:01 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "t;")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app1 = #cursorIdx app)
|
|
|
|
|
end)
|
|
|
|
|
, test "is cancellable by pressing escape" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 06:13:01 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"t")
|
|
|
|
|
val app2 = TestUtils.update (app1, KEY_ESC)
|
2025-03-22 13:09:50 +00:00
|
|
|
(* should not move cursor like other 't' tests do *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"d")
|
2025-03-22 06:13:01 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2
|
|
|
|
|
andalso #cursorIdx app2 = #cursorIdx app3)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
2025-03-22 13:09:50 +00:00
|
|
|
val TMove = describe "move motion 'T'"
|
|
|
|
|
[ test
|
|
|
|
|
"motion 'Th' moves cursor to char after 'h' in string \"hello world\""
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:09:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "Th")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"e")
|
|
|
|
|
end)
|
|
|
|
|
, test "repeating 'T' motion with same char does not move cursor" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:09:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "Te")
|
|
|
|
|
val app2 = updateMany (app1, "Te")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2 andalso getChr app1 = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor at all when char following 'T' is not in string"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:09:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "T;")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app1 = #cursorIdx app)
|
|
|
|
|
end)
|
|
|
|
|
, test "is cancellable by pressing escape" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:09:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"T")
|
|
|
|
|
val app2 = TestUtils.update (app1, KEY_ESC)
|
2025-03-22 13:09:50 +00:00
|
|
|
(* should ordinarily move cursor to 'w' but in this case should not
|
|
|
|
|
* as escape key should cancel motion which was in progress *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #" ")
|
2025-03-22 13:09:50 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2
|
|
|
|
|
andalso #cursorIdx app2 = #cursorIdx app3)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
2025-03-22 13:41:50 +00:00
|
|
|
val fMove = describe "move motion 'f'"
|
|
|
|
|
[ test "motion 'fw' moves cursor to first 'w' in string \"hello world\""
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 13:41:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "fw")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"w")
|
|
|
|
|
end)
|
2025-03-22 13:50:24 +00:00
|
|
|
, test "count followed by f<char> moves forwards to count'th match" (fn _ =>
|
2025-03-22 13:41:50 +00:00
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 13:41:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "3fl")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = 9 andalso getChr app = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"'count f<char>' goes to last match when count is greater than number of chars"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 13:41:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "9fl")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = 9 andalso getChr app = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor at all when char following 'f' is not in string"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 13:41:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "f;")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app1 = #cursorIdx app)
|
|
|
|
|
end)
|
|
|
|
|
, test "is cancellable by pressing escape" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 13:41:50 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"f")
|
|
|
|
|
val app2 = TestUtils.update (app1, KEY_ESC)
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"d")
|
2025-03-22 13:41:50 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2
|
|
|
|
|
andalso #cursorIdx app2 = #cursorIdx app3)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
2025-03-22 13:50:24 +00:00
|
|
|
val FMove = describe "move motion 'F'"
|
|
|
|
|
[ test "motion 'Fe' moves cursor to first 'e' before cursor" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:50:24 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "Fe")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"e")
|
|
|
|
|
end)
|
|
|
|
|
, test "count followed by F<char> moves backwards to count'th match"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:50:24 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "3Fl")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = 2 andalso getChr app = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"'count F<char>' goes to first match when count is greater than number of chars"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:50:24 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "9Fl")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app = 2 andalso getChr app = #"l")
|
|
|
|
|
end)
|
|
|
|
|
, test
|
|
|
|
|
"does not move cursor at all when char following 'F' is not in string"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:50:24 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app1 = updateMany (app, "F;")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app1 = #cursorIdx app)
|
|
|
|
|
end)
|
|
|
|
|
, test "is cancellable by pressing escape" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 13:50:24 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"F")
|
|
|
|
|
val app2 = TestUtils.update (app1, KEY_ESC)
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"r")
|
2025-03-22 13:50:24 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue
|
|
|
|
|
(#cursorIdx app1 = #cursorIdx app2
|
|
|
|
|
andalso #cursorIdx app2 = #cursorIdx app3)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
2025-03-22 20:58:39 +00:00
|
|
|
val ggMove = describe "move motion 'gg'"
|
|
|
|
|
[ test "moves cursor to start when cursor is at end" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 10)
|
2025-03-22 20:58:39 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "gg")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test "moves cursor to start when cursor is in middle" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 5)
|
2025-03-22 20:58:39 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "gg")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test "leaves cursor in same place when cursor is already at start"
|
|
|
|
|
(fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-22 20:58:39 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
|
|
|
|
val app = updateMany (app, "gg")
|
|
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (getChr app = #"h")
|
|
|
|
|
end)
|
|
|
|
|
, test "is cancellable by pressing escape" (fn _ =>
|
|
|
|
|
let
|
|
|
|
|
(* arrange *)
|
2025-09-23 13:18:36 +01:00
|
|
|
val app = TestUtils.init "hello world\n"
|
2025-03-23 07:47:38 +00:00
|
|
|
val app = AppWith.idx (app, 5)
|
2025-03-22 20:58:39 +00:00
|
|
|
|
|
|
|
|
(* act *)
|
2025-08-20 13:50:57 +01:00
|
|
|
val app1 = TestUtils.update (app, CHAR_EVENT #"g")
|
|
|
|
|
val app2 = TestUtils.update (app1, KEY_ESC)
|
|
|
|
|
val app3 = TestUtils.update (app2, CHAR_EVENT #"g")
|
2025-03-22 20:58:39 +00:00
|
|
|
in
|
|
|
|
|
(* assert *)
|
|
|
|
|
Expect.isTrue (#cursorIdx app3 = 5)
|
|
|
|
|
end)
|
|
|
|
|
]
|
|
|
|
|
|
2025-03-23 07:47:38 +00:00
|
|
|
val tests =
|
2025-03-22 05:18:25 +00:00
|
|
|
[ hMove
|
|
|
|
|
, jMove
|
|
|
|
|
, kMove
|
2025-03-22 06:13:01 +00:00
|
|
|
, lMove
|
2025-03-22 05:18:25 +00:00
|
|
|
, wMove
|
|
|
|
|
, WMove
|
|
|
|
|
, bMove
|
|
|
|
|
, BMove
|
2025-03-22 06:13:01 +00:00
|
|
|
, eMove
|
|
|
|
|
, EMove
|
2025-03-22 05:18:25 +00:00
|
|
|
, zeroMove
|
|
|
|
|
, dlrMove
|
2025-03-22 06:13:01 +00:00
|
|
|
, hatMove
|
2025-03-22 05:18:25 +00:00
|
|
|
, GMove
|
|
|
|
|
, percentMove
|
2025-03-22 06:13:01 +00:00
|
|
|
(* multi-char motions *)
|
|
|
|
|
, tMove
|
2025-03-22 13:09:50 +00:00
|
|
|
, TMove
|
2025-03-22 13:41:50 +00:00
|
|
|
, fMove
|
2025-03-22 13:50:24 +00:00
|
|
|
, FMove
|
2025-03-22 20:58:39 +00:00
|
|
|
, ggMove
|
2025-03-22 05:18:25 +00:00
|
|
|
]
|
|
|
|
|
end
|