Files
sml-projects/test/test.sml
2024-11-19 06:27:38 +00:00

432 lines
13 KiB
Standard ML

open Railroad
open Railroad.Test
open InputMsg
local
fun helpCountLineBreaks (pos, acc, str) =
if pos < 0 then
Vector.fromList acc
else
let
val chr = String.sub (str, pos)
in
if chr = #"\n" then
(* Is this a \r\n pair? Then the position of \r should be consed. *)
if pos = 0 then
Vector.fromList (0 :: acc)
else
let
val prevChar = String.sub (str, pos - 1)
in
if prevChar = #"\r" then
helpCountLineBreaks (pos - 2, (pos - 1) :: acc, str)
else
helpCountLineBreaks (pos - 1, pos :: acc, str)
end
else if chr = #"\r" then
helpCountLineBreaks (pos - 1, pos :: acc, str)
else
helpCountLineBreaks (pos - 1, acc, str)
end
fun countLineBreaks str =
helpCountLineBreaks (String.size str - 1, [], str)
in
(* creates a LineGap.t with valid metadata from a list of strings *)
fun fromList lst =
{ idx = 0
, line = 0
, leftStrings = []
, leftLines = []
, rightStrings = lst
, rightLines = List.map countLineBreaks lst
}
end
fun withIdx (app: AppType.app_type, idx) =
let
val
{ startLine
, buffer
, searchList
, searchString
, mode
, windowWidth
, windowHeight
, cursorIdx = _
} = app
in
{ startLine = startLine
, buffer = buffer
, searchList = searchList
, searchString = searchString
, mode = mode
, windowWidth = windowWidth
, windowHeight = windowHeight
, cursorIdx = idx
}
end
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 movementTests = describe "movement operations"
[ test "'h' moves cursor left by one in contiguous string when cursorIdx > 0"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello world"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 1)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"h")
in
(* assert *)
Expect.isTrue (cursorIdx = 0)
end)
, test "'h' moves cursor left by one in split string when cursorIdx > 0"
(fn _ =>
let
(* arrange *)
val buffer = fromList ["hello", " world"]
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 5)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"h")
in
(* assert *)
Expect.isTrue (cursorIdx = 4)
end)
, test "'h' does not move cursor when cursorIdx = 0" (fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello world"
val app = AppType.init (buffer, 0, 0)
val {cursorIdx = oldCursorIdx, ...} = app
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"h")
in
(* assert *)
Expect.isTrue (oldCursorIdx = 0 andalso cursorIdx = 0)
end)
, test
"'h' moves cursor left by two in contiguous string when prev chr is \\n"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello\nworld"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 6)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"h")
in
(* assert *)
Expect.isTrue (cursorIdx = 4)
end)
, test "'h' moves cursor left by two in split string when prev chr is \\n"
(fn _ =>
let
(* arrange *)
val buffer = fromList ["hello\n", " world"]
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 6)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"h")
in
(* assert *)
Expect.isTrue (cursorIdx = 4)
end)
, test
"'l' moves cursor right by one in contiguous string when cursorIdx < length"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello world"
val app = AppType.init (buffer, 0, 0)
val {cursorIdx = oldCursorIdx, ...} = app
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"l")
in
(* assert *)
Expect.isTrue (oldCursorIdx = 0 andalso cursorIdx = 1)
end)
, test "'l' moves cursor right by one in split string when cursorIdx < length"
(fn _ =>
let
(* arrange *)
val buffer = fromList ["hello ", "world"]
val app = AppType.init (buffer, 0, 0)
val {cursorIdx = oldCursorIdx, ...} = app
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"l")
in
(* assert *)
Expect.isTrue (oldCursorIdx = 0 andalso cursorIdx = 1)
end)
, test "'l' does not move cursor right by one when cursorIdx = length"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello world\n"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 10)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"l")
in
(* assert *)
Expect.isTrue (cursorIdx = 10)
end)
, test
"'l' moves right by two in contiguous string when char is followed by \\n"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello\nworld\n"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 4)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"l")
in
(* assert *)
Expect.isTrue (cursorIdx = 6)
end)
, test "'l' moves right by two in split string when char is followed by \\n"
(fn _ =>
let
(* arrange *)
val buffer = fromList ["hello\n", "world"]
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 4)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"l")
in
(* assert *)
Expect.isTrue (cursorIdx = 6)
end)
, test "'j' moves cursur down one column in contiguous string 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 buffer = LineGap.fromString "hello \nworld \ngoodbye \nqorld \n"
val app = AppType.init (buffer, 0, 0)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.update (app2, CHAR_EVENT #"j")
(* 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 "'j' moves cursur down one column in split string when column = 0"
(fn _ =>
let
(* arrange *)
val buffer = fromList ["hello \n", "world \n", "goodbye \n", "qorld"]
val app = AppType.init (buffer, 0, 0)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.update (app2, CHAR_EVENT #"j")
(* 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 "'j' moves cursur down one column in contiguous string when column = 1"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello \nworld \nbye \nfriends \n"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 1)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.update (app2, CHAR_EVENT #"j")
(* 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 "'j' moves cursur down one column in split string when column = 1"
(fn _ =>
let
(* arrange *)
val buffer =
fromList ["hello \n", "world ", "\nb", "ye \nfriends \n"]
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 1)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.update (app2, CHAR_EVENT #"j")
(* 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 "'j' moves cursur down one column in contiguous string when column = 2"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello \nworld \nbye \nfriends \n"
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 2)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.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 "'j' moves cursur down one column in split string when column = 2"
(fn _ =>
let
(* arrange *)
val buffer =
fromList ["hello \n", "world ", "\nb", "ye \nfriends \n"]
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 2)
(* act *)
val (app1, _) = AppUpdate.update (app, CHAR_EVENT #"j")
val (app2, _) = AppUpdate.update (app1, CHAR_EVENT #"j")
val (app3, _) = AppUpdate.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
"'j' skips '\\n' when cursor is on non-\\n and is followed by two '\\n's"
(fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello\n\n nworld\n"
val app = AppType.init (buffer, 0, 0)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"j")
(* assert *)
val isSkipped = cursorIdx = 6
in
Expect.isTrue isSkipped
end)
, test "'j' moves to end of buffer when on last line" (fn _ =>
let
(* arrange *)
val str = "hello \nworld \ntime to go\n"
val buffer = LineGap.fromString str
val app = AppType.init (buffer, 0, 0)
val app = withIdx (app, 15)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"j")
(* assert *)
(* String.size str - 1 is a valid char position
* but we are counting String.size str - 2 as the end
* because, in Vim, saved files always end with \n
* but the last char, \n, is not visible *)
val isAtEnd = cursorIdx = String.size str - 2
in
Expect.isTrue isAtEnd
end)
, test "'w' moves cursor to start of next word in contiguous string" (fn _ =>
let
(* arrange *)
val buffer = LineGap.fromString "hello world"
val app = AppType.init (buffer, 0, 0)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"w")
(* assert *)
val chr = String.sub ("hello world", cursorIdx)
in
Expect.isTrue (chr = #"w")
end)
, test "'w' moves cursor to start of next word in split string" (fn _ =>
let
(* arrange *)
val buffer = fromList ["hello ", "world"]
val app = AppType.init (buffer, 0, 0)
(* act *)
val ({cursorIdx, ...}, _) = AppUpdate.update (app, CHAR_EVENT #"w")
(* assert *)
val chr = String.sub ("hello world", cursorIdx)
in
Expect.isTrue (chr = #"w")
end)
]
val tests = concat [movementTests]
val _ = run tests