2024-10-17 02:57:26 +01:00
|
|
|
structure Cursor =
|
|
|
|
|
struct
|
2024-11-26 21:57:02 +00:00
|
|
|
(* returns absolute index of previous line break in string *)
|
|
|
|
|
fun helpVi0 (absIdx, stl, ltl) =
|
|
|
|
|
case (stl, ltl) of
|
|
|
|
|
(shd :: stl, lhd :: ltl) =>
|
|
|
|
|
if Vector.length lhd > 0 then
|
|
|
|
|
let
|
|
|
|
|
val startAbsIdx = absIdx - String.size shd
|
|
|
|
|
val lineIdx = Vector.sub (lhd, Vector.length lhd - 1)
|
|
|
|
|
in
|
|
|
|
|
(* found lineIdx.
|
|
|
|
|
* Need to make sure we follow cursor-on-linebreak rule:
|
|
|
|
|
* If line break is preceded by non-line break char,
|
|
|
|
|
* then increment by 1.
|
|
|
|
|
* *)
|
2025-03-22 05:18:25 +00:00
|
|
|
startAbsIdx + lineIdx + 1
|
2024-11-26 21:57:02 +00:00
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
helpVi0 (absIdx - String.size shd, stl, ltl)
|
|
|
|
|
| (_, _) => 0
|
|
|
|
|
|
|
|
|
|
fun startVi0 (strPos, shd, lhd, absIdx, stl, ltl) =
|
|
|
|
|
if String.sub (shd, strPos) = #"\n" then
|
|
|
|
|
absIdx
|
2025-03-22 05:18:25 +00:00
|
|
|
else if Vector.length lhd > 0 then
|
|
|
|
|
if Vector.sub (lhd, 0) < strPos then
|
|
|
|
|
let
|
|
|
|
|
val linePos = BinSearch.equalOrLess (strPos - 1, lhd)
|
|
|
|
|
val lineIdx = Vector.sub (lhd, linePos)
|
|
|
|
|
in
|
2025-09-13 02:18:14 +01:00
|
|
|
if linePos = ~1 then
|
|
|
|
|
(* no previous line in lhd *)
|
|
|
|
|
helpVi0 (absIdx - strPos, stl, ltl)
|
|
|
|
|
else
|
|
|
|
|
let val lineIdx = Vector.sub (lhd, linePos)
|
|
|
|
|
in absIdx - strPos + lineIdx + 1
|
|
|
|
|
end
|
2025-03-22 05:18:25 +00:00
|
|
|
end
|
2024-11-26 21:57:02 +00:00
|
|
|
else
|
|
|
|
|
helpVi0 (absIdx - strPos, stl, ltl)
|
2025-03-22 05:18:25 +00:00
|
|
|
else
|
|
|
|
|
helpVi0 (absIdx - strPos, stl, ltl)
|
2024-10-22 07:06:16 +01:00
|
|
|
|
2025-08-04 04:05:39 +01:00
|
|
|
structure Vi0 =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
fun fStart (strPos, shd, lhd, absIdx, stl, ltl, _) =
|
|
|
|
|
startVi0 (strPos, shd, lhd, absIdx, stl, ltl)
|
|
|
|
|
end)
|
2025-08-04 03:23:27 +01:00
|
|
|
|
2025-08-04 04:05:39 +01:00
|
|
|
fun vi0 (lineGap, cursorIdx) = Vi0.foldPrev (lineGap, cursorIdx, ())
|
2024-10-22 07:06:16 +01:00
|
|
|
|
2025-08-04 05:37:08 +01:00
|
|
|
val viDlr = ViDlrDfa.next
|
|
|
|
|
val viDlrForDelete = ViDlrDfa.nextForDelete
|
2024-10-22 07:06:16 +01:00
|
|
|
|
2025-09-21 17:49:30 +01:00
|
|
|
val viL = ViLDfa.next
|
2025-09-21 21:36:59 +01:00
|
|
|
val viH = ViHDfa.prev
|
2024-10-19 07:03:32 +01:00
|
|
|
|
2024-11-05 12:38:01 +00:00
|
|
|
(* equivalent of vi's 'w' command *)
|
2025-07-20 16:04:48 +01:00
|
|
|
val nextWord = ViWordDfa.startOfNextWord
|
2025-12-27 08:42:54 +00:00
|
|
|
val nextWordForDelete = ViWordDfa.startOfNextWordForDelete
|
2024-10-25 21:27:58 +01:00
|
|
|
|
2024-11-05 12:38:01 +00:00
|
|
|
(* equivalent of vi's 'W' command *)
|
2025-09-02 02:14:12 +01:00
|
|
|
val nextWORD = ViCapsWordDfa.startOfNextWORD
|
2025-12-27 09:45:11 +00:00
|
|
|
val nextWORDForDelete = ViCapsWordDfa.startOfNextWORDForDelete
|
2024-10-25 21:27:58 +01:00
|
|
|
|
|
|
|
|
(* equivalent of vi's 'b' command *)
|
2025-07-20 17:36:33 +01:00
|
|
|
val prevWord = ViWordDfa.startOfCurrentWord
|
2025-08-07 18:33:03 +01:00
|
|
|
val prevWordStrict = ViWordDfa.startOfCurrentWordStrict
|
2024-10-25 21:27:58 +01:00
|
|
|
|
|
|
|
|
(* equivalent of vi's 'B' command *)
|
2025-09-02 02:14:12 +01:00
|
|
|
val prevWORD = ViCapsWordDfa.startOfCurrentWORD
|
|
|
|
|
val prevWORDStrict = ViCapsWordDfa.startOfCurrentWORDStrict
|
2024-10-25 21:27:58 +01:00
|
|
|
|
2024-10-28 06:52:58 +00:00
|
|
|
(* equivalent of vi's 'ge' command *)
|
2025-07-20 16:55:42 +01:00
|
|
|
val endOfPrevWord = ViWordDfa.endOfPrevWord
|
2024-10-28 06:52:58 +00:00
|
|
|
|
|
|
|
|
(* equivalent of vi's 'gE' command *)
|
2025-09-02 02:14:12 +01:00
|
|
|
val endOfPrevWORD = ViCapsWordDfa.endOfPrevWORD
|
2024-10-28 06:52:58 +00:00
|
|
|
|
2024-10-25 21:27:58 +01:00
|
|
|
(* equivalent of vi's `e` command *)
|
2025-07-20 17:48:45 +01:00
|
|
|
val endOfWord = ViWordDfa.endOfCurrentWord
|
2025-08-03 17:35:33 +01:00
|
|
|
val endOfWordForDelete = ViWordDfa.endOfCurrentWordForDelete
|
2025-09-06 01:12:59 +01:00
|
|
|
val endOfWordStrict = ViWordDfa.endOfCurrentWordStrict
|
2024-10-25 21:27:58 +01:00
|
|
|
|
|
|
|
|
(* equivalent of vi's `E` command *)
|
2025-09-02 02:14:12 +01:00
|
|
|
val endOfWORD = ViCapsWordDfa.endOfCurrentWORD
|
|
|
|
|
val endOfWORDForDelete = ViCapsWordDfa.endOfCurrentWORDForDelete
|
2025-09-06 01:15:45 +01:00
|
|
|
val endOfWORDStrict = ViCapsWordDfa.endOfCurrentWORDStrict
|
2024-10-25 21:44:46 +01:00
|
|
|
|
|
|
|
|
(* Prerequisite:
|
|
|
|
|
* LineGap has been moved to start of line (provided with vi0). *)
|
2025-08-04 03:44:45 +01:00
|
|
|
structure FirstNonSpaceChr =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
2025-08-04 04:05:39 +01:00
|
|
|
type env = unit
|
|
|
|
|
|
2025-08-04 03:44:45 +01:00
|
|
|
fun helpFirstNonSpaceChr (strPos, str, absIdx, stl) =
|
|
|
|
|
if strPos = String.size str then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => helpFirstNonSpaceChr (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx - 1
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #" " then
|
|
|
|
|
helpFirstNonSpaceChr (strPos + 1, str, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx
|
|
|
|
|
end
|
|
|
|
|
|
2025-08-04 04:05:39 +01:00
|
|
|
fun fStart (strIdx, shd, _, absIdx, stl, _, _) =
|
2025-08-04 03:44:45 +01:00
|
|
|
if strIdx < String.size shd then
|
|
|
|
|
helpFirstNonSpaceChr (strIdx, shd, absIdx, stl)
|
|
|
|
|
else
|
|
|
|
|
case stl of
|
|
|
|
|
stlhd :: stltl => helpFirstNonSpaceChr (0, stlhd, absIdx, stltl)
|
|
|
|
|
| [] => (* tl is empty; just return absIdx *) absIdx
|
|
|
|
|
end)
|
|
|
|
|
|
2025-08-04 04:05:39 +01:00
|
|
|
fun firstNonSpaceChr (lineGap, cursorIdx) =
|
|
|
|
|
FirstNonSpaceChr.foldPrev (lineGap, cursorIdx, ())
|
2024-10-26 10:47:45 +01:00
|
|
|
|
2025-10-18 14:01:06 +01:00
|
|
|
structure ToNextChr =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = {findChr: char, count: int}
|
|
|
|
|
|
|
|
|
|
fun helpToNextChr
|
|
|
|
|
(strPos, str, absIdx, stl, lastFoundIdx, findChr, count) =
|
|
|
|
|
if strPos = String.size str then
|
|
|
|
|
case stl of
|
|
|
|
|
str :: stl =>
|
|
|
|
|
helpToNextChr
|
|
|
|
|
(0, str, absIdx, stl, lastFoundIdx, findChr, count)
|
|
|
|
|
| [] => lastFoundIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = findChr then
|
|
|
|
|
if count - 1 = 0 then
|
|
|
|
|
absIdx
|
|
|
|
|
else
|
|
|
|
|
helpToNextChr
|
|
|
|
|
( strPos + 1
|
|
|
|
|
, str
|
|
|
|
|
, absIdx + 1
|
|
|
|
|
, stl
|
|
|
|
|
, absIdx
|
|
|
|
|
, findChr
|
|
|
|
|
, count - 1
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
helpToNextChr
|
|
|
|
|
( strPos + 1
|
|
|
|
|
, str
|
|
|
|
|
, absIdx + 1
|
|
|
|
|
, stl
|
|
|
|
|
, lastFoundIdx
|
|
|
|
|
, findChr
|
|
|
|
|
, count
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, str, _, absIdx, stl, _, {findChr, count}) =
|
|
|
|
|
(* we want to start iterating from char after cursor *)
|
|
|
|
|
helpToNextChr (strPos + 1, str, absIdx + 1, stl, ~1, findChr, count)
|
|
|
|
|
end)
|
|
|
|
|
|
2025-10-18 15:17:26 +01:00
|
|
|
val toNextChr = ToNextChr.foldNext
|
2025-10-18 14:01:06 +01:00
|
|
|
|
2025-08-04 04:14:17 +01:00
|
|
|
structure ToPrevChr =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
2025-10-19 14:30:26 +01:00
|
|
|
type env = {findChr: char, count: int}
|
2024-10-26 21:21:33 +01:00
|
|
|
|
2025-10-19 14:30:26 +01:00
|
|
|
fun helpToPrevChr
|
|
|
|
|
(strPos, str, absIdx, stl, lastFoundIdx, findChr, count) =
|
2025-08-04 04:14:17 +01:00
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl =>
|
|
|
|
|
helpToPrevChr
|
2025-08-04 04:24:24 +01:00
|
|
|
( String.size shd - 1
|
|
|
|
|
, shd
|
|
|
|
|
, absIdx
|
|
|
|
|
, stl
|
2025-10-19 14:30:26 +01:00
|
|
|
, lastFoundIdx
|
2025-08-04 04:24:24 +01:00
|
|
|
, findChr
|
2025-10-19 14:30:26 +01:00
|
|
|
, count
|
2025-08-04 04:24:24 +01:00
|
|
|
)
|
2025-10-19 14:30:26 +01:00
|
|
|
| [] => lastFoundIdx
|
|
|
|
|
else if String.sub (str, strPos) = findChr then
|
|
|
|
|
if count - 1 = 0 then
|
|
|
|
|
absIdx
|
|
|
|
|
else
|
|
|
|
|
helpToPrevChr
|
|
|
|
|
(strPos - 1, str, absIdx - 1, stl, absIdx, findChr, count - 1)
|
2025-08-04 04:24:24 +01:00
|
|
|
else
|
2025-10-19 14:30:26 +01:00
|
|
|
helpToPrevChr
|
|
|
|
|
(strPos - 1, str, absIdx - 1, stl, lastFoundIdx, findChr, count)
|
2025-08-04 04:24:24 +01:00
|
|
|
|
2025-10-19 14:30:26 +01:00
|
|
|
fun fStart (strIdx, shd, _, absIdx, stl, _, {findChr, count}) =
|
|
|
|
|
helpToPrevChr (strIdx - 1, shd, absIdx - 1, stl, ~1, findChr, count)
|
2025-08-04 04:24:24 +01:00
|
|
|
end)
|
2024-10-26 22:11:52 +01:00
|
|
|
|
2025-10-19 14:30:26 +01:00
|
|
|
val toPrevChr = ToPrevChr.foldPrev
|
2024-10-31 11:07:52 +00:00
|
|
|
|
2026-01-03 08:09:08 +00:00
|
|
|
structure ToOpenChrPrev =
|
|
|
|
|
MakeIfCharFolderPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
(struct
|
2026-01-02 18:50:04 +00:00
|
|
|
type env = {openChr: char, closeChr: char}
|
2026-01-02 18:12:59 +00:00
|
|
|
|
2026-01-03 08:09:08 +00:00
|
|
|
fun loop (strPos, str, absIdx, stl, openChr, closeChr, closeCount) =
|
|
|
|
|
if strPos < 0 then
|
2026-01-02 18:12:59 +00:00
|
|
|
case stl of
|
2026-01-02 18:50:04 +00:00
|
|
|
str :: stl =>
|
|
|
|
|
loop
|
2026-01-03 08:09:08 +00:00
|
|
|
( String.size str - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
, str
|
2026-01-03 08:09:08 +00:00
|
|
|
, absIdx
|
2026-01-02 18:50:04 +00:00
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, closeChr
|
2026-01-03 08:09:08 +00:00
|
|
|
, closeCount
|
2026-01-02 18:50:04 +00:00
|
|
|
)
|
2026-01-03 08:09:08 +00:00
|
|
|
| [] => ~1
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = openChr then
|
|
|
|
|
if closeCount = 0 then
|
2026-01-02 18:50:04 +00:00
|
|
|
absIdx
|
|
|
|
|
else
|
|
|
|
|
loop
|
2026-01-03 08:09:08 +00:00
|
|
|
( strPos - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
, str
|
2026-01-03 08:09:08 +00:00
|
|
|
, absIdx - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, closeChr
|
2026-01-03 08:09:08 +00:00
|
|
|
, closeCount - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
)
|
2026-01-03 08:09:08 +00:00
|
|
|
else if chr = closeChr then
|
|
|
|
|
loop
|
|
|
|
|
( strPos - 1
|
|
|
|
|
, str
|
|
|
|
|
, absIdx - 1
|
|
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, closeChr
|
|
|
|
|
, closeCount + 1
|
|
|
|
|
)
|
2026-01-02 18:50:04 +00:00
|
|
|
else
|
|
|
|
|
loop
|
2026-01-03 08:09:08 +00:00
|
|
|
( strPos - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
, str
|
2026-01-03 08:09:08 +00:00
|
|
|
, absIdx - 1
|
2026-01-02 18:50:04 +00:00
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, closeChr
|
2026-01-03 08:09:08 +00:00
|
|
|
, closeCount
|
2026-01-02 18:50:04 +00:00
|
|
|
)
|
2026-01-02 18:12:59 +00:00
|
|
|
end
|
|
|
|
|
|
2026-01-02 18:50:04 +00:00
|
|
|
fun fStart (strPos, str, _, absIdx, stl, _, {openChr, closeChr}) =
|
2026-01-03 08:09:08 +00:00
|
|
|
loop (strPos, str, absIdx, stl, openChr, closeChr, 0)
|
2026-01-02 18:12:59 +00:00
|
|
|
end)
|
|
|
|
|
|
2026-01-03 08:09:08 +00:00
|
|
|
val toOpenChrPrev = ToOpenChrPrev.foldPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
|
2026-01-01 06:58:30 +00:00
|
|
|
structure NextPairChr =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
2026-01-02 18:12:59 +00:00
|
|
|
fun isPairChr chr =
|
|
|
|
|
chr = #"(" orelse chr = #")" orelse chr = #"[" orelse chr = #"]"
|
|
|
|
|
orelse chr = #"{" orelse chr = #"}" orelse chr = #"<"
|
|
|
|
|
orelse chr = #">"
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, str, absIdx, stl) =
|
|
|
|
|
if strPos = String.size str then
|
|
|
|
|
case stl of
|
|
|
|
|
str :: stl => loop (0, str, absIdx, stl)
|
|
|
|
|
| [] => ~1
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
in
|
|
|
|
|
if isPairChr chr then absIdx
|
|
|
|
|
else loop (strPos + 1, str, absIdx + 1, stl)
|
|
|
|
|
end
|
2026-01-01 06:58:30 +00:00
|
|
|
|
|
|
|
|
fun fStart (strPos, str, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, str, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun nextPairChr (lineGap, cursorIdx) =
|
|
|
|
|
NextPairChr.foldNext (lineGap, cursorIdx, ())
|
|
|
|
|
|
2025-03-22 05:18:25 +00:00
|
|
|
fun helpMatchPairNext
|
2026-01-01 06:58:30 +00:00
|
|
|
(strPos, str, absIdx, stl, openChr, openNum, closeChr, closeNum) =
|
2025-03-22 05:18:25 +00:00
|
|
|
if strPos = String.size str then
|
|
|
|
|
case stl of
|
|
|
|
|
hd :: tl =>
|
|
|
|
|
helpMatchPairNext
|
2026-01-01 06:58:30 +00:00
|
|
|
(0, hd, absIdx, tl, openChr, openNum, closeChr, closeNum)
|
|
|
|
|
| [] => ~1
|
2025-03-22 05:18:25 +00:00
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
val openNum = if chr = openChr then openNum + 1 else openNum
|
|
|
|
|
val closeNum = if chr = closeChr then closeNum + 1 else closeNum
|
|
|
|
|
in
|
|
|
|
|
if openNum = closeNum then
|
|
|
|
|
absIdx
|
|
|
|
|
else
|
|
|
|
|
helpMatchPairNext
|
|
|
|
|
( strPos + 1
|
|
|
|
|
, str
|
|
|
|
|
, absIdx + 1
|
|
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, openNum
|
|
|
|
|
, closeChr
|
|
|
|
|
, closeNum
|
|
|
|
|
)
|
|
|
|
|
end
|
2024-10-31 11:07:52 +00:00
|
|
|
|
2025-03-22 05:18:25 +00:00
|
|
|
fun helpMatchPairPrev
|
2026-01-01 06:58:30 +00:00
|
|
|
(strPos, str, absIdx, stl, openChr, openNum, closeChr, closeNum) =
|
2025-03-22 05:18:25 +00:00
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
hd :: tl =>
|
|
|
|
|
helpMatchPairPrev
|
|
|
|
|
( String.size hd - 1
|
|
|
|
|
, hd
|
|
|
|
|
, absIdx
|
|
|
|
|
, tl
|
|
|
|
|
, openChr
|
|
|
|
|
, openNum
|
|
|
|
|
, closeChr
|
|
|
|
|
, closeNum
|
|
|
|
|
)
|
2026-01-01 06:58:30 +00:00
|
|
|
| [] => ~1
|
2025-03-22 05:18:25 +00:00
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (str, strPos)
|
|
|
|
|
val openNum = if chr = openChr then openNum + 1 else openNum
|
|
|
|
|
val closeNum = if chr = closeChr then closeNum + 1 else closeNum
|
|
|
|
|
in
|
|
|
|
|
if openNum = closeNum then
|
|
|
|
|
absIdx
|
2024-10-31 11:07:52 +00:00
|
|
|
else
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairPrev
|
|
|
|
|
( strPos - 1
|
|
|
|
|
, str
|
|
|
|
|
, absIdx - 1
|
|
|
|
|
, stl
|
|
|
|
|
, openChr
|
|
|
|
|
, openNum
|
|
|
|
|
, closeChr
|
|
|
|
|
, closeNum
|
|
|
|
|
)
|
|
|
|
|
end
|
2024-10-31 11:07:52 +00:00
|
|
|
|
2024-10-31 11:50:18 +00:00
|
|
|
fun startMatchPair (strIdx, shd, leftStrings, rightStrings, cursorIdx) =
|
|
|
|
|
case String.sub (shd, strIdx) of
|
|
|
|
|
#"(" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairNext
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx + 1, shd, cursorIdx + 1, rightStrings, #"(", 1, #")", 0)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #")" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx - 1, shd, cursorIdx - 1, leftStrings, #"(", 0, #")", 1)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #"[" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairNext
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx + 1, shd, cursorIdx + 1, rightStrings, #"[", 1, #"]", 0)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #"]" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx - 1, shd, cursorIdx - 1, leftStrings, #"[", 0, #"]", 1)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #"{" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairNext
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx + 1, shd, cursorIdx + 1, rightStrings, #"{", 1, #"}", 0)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #"}" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx - 1, shd, cursorIdx - 1, leftStrings, #"{", 0, #"}", 1)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #"<" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairNext
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx + 1, shd, cursorIdx + 1, rightStrings, #"<", 1, #">", 0)
|
2024-10-31 11:50:18 +00:00
|
|
|
| #">" =>
|
2025-03-22 05:18:25 +00:00
|
|
|
helpMatchPairPrev
|
2026-01-02 18:12:59 +00:00
|
|
|
(strIdx - 1, shd, cursorIdx - 1, leftStrings, #"<", 0, #">", 1)
|
2026-01-01 06:58:30 +00:00
|
|
|
| _ => ~1
|
2024-10-31 11:19:10 +00:00
|
|
|
|
2024-10-31 11:07:52 +00:00
|
|
|
fun matchPair (lineGap: LineGap.t, cursorIdx) =
|
|
|
|
|
let
|
|
|
|
|
val {rightStrings, idx = bufferIdx, leftStrings, ...} = lineGap
|
|
|
|
|
in
|
|
|
|
|
case rightStrings of
|
|
|
|
|
shd :: stl =>
|
|
|
|
|
let
|
|
|
|
|
(* convert absolute cursorIdx to idx relative to hd string *)
|
|
|
|
|
val strIdx = cursorIdx - bufferIdx
|
|
|
|
|
in
|
|
|
|
|
if strIdx < String.size shd then
|
|
|
|
|
(* strIdx is in this string *)
|
2024-10-31 11:50:18 +00:00
|
|
|
startMatchPair (strIdx, shd, leftStrings, stl, cursorIdx)
|
2024-10-31 11:07:52 +00:00
|
|
|
else
|
|
|
|
|
(* strIdx is in tl *)
|
|
|
|
|
(case stl of
|
|
|
|
|
stlhd :: stltl =>
|
2025-03-22 05:18:25 +00:00
|
|
|
let
|
2024-10-31 11:07:52 +00:00
|
|
|
val strIdx = strIdx - String.size shd
|
|
|
|
|
val leftStrings = shd :: leftStrings
|
2025-03-22 05:18:25 +00:00
|
|
|
in
|
|
|
|
|
startMatchPair
|
|
|
|
|
(strIdx, stlhd, leftStrings, stltl, cursorIdx)
|
2024-10-31 11:07:52 +00:00
|
|
|
end
|
|
|
|
|
| [] => cursorIdx)
|
|
|
|
|
end
|
|
|
|
|
| [] => cursorIdx
|
|
|
|
|
end
|
2024-11-02 13:59:55 +00:00
|
|
|
|
2025-12-12 11:32:03 +00:00
|
|
|
structure FirstContiguousAlpha =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loop (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun firstContiguousAlpha (lineGap, cursorIdx) =
|
|
|
|
|
FirstContiguousAlpha.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure LastContiguousAlpha =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (0, shd, absIdx, stl)
|
|
|
|
|
| [] => Int.max (0, absIdx - 1)
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loop (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun lastContiguousAlpha (lineGap, cursorIdx) =
|
|
|
|
|
LastContiguousAlpha.foldNext (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure FirstContiguousSpace =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isSpace chr then
|
|
|
|
|
if chr = #"\n" then absIdx + 1
|
|
|
|
|
else loop (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun firstContiguousSpace (lineGap, cursorIdx) =
|
|
|
|
|
FirstContiguousSpace.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure LastContiguousSpace =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (0, shd, absIdx, stl)
|
|
|
|
|
| [] => Int.max (0, absIdx - 1)
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isSpace chr then
|
|
|
|
|
if chr = #"\n" then absIdx - 1
|
|
|
|
|
else loop (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun lastContiguousSpace (lineGap, cursorIdx) =
|
|
|
|
|
LastContiguousSpace.foldNext (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure FirstContiguousPunct =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isAlphaNum chr orelse chr = #"_" orelse Char.isSpace chr then
|
|
|
|
|
absIdx + 1
|
|
|
|
|
else
|
|
|
|
|
loop (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun firstContiguousPunct (lineGap, cursorIdx) =
|
|
|
|
|
FirstContiguousPunct.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure LastContiguousPunct =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (0, shd, absIdx, stl)
|
|
|
|
|
| [] => Int.max (0, absIdx - 1)
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isAlphaNum chr orelse chr = #"_" orelse Char.isSpace chr then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else
|
|
|
|
|
loop (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun lastContiguousPunct (lineGap, cursorIdx) =
|
|
|
|
|
LastContiguousPunct.foldNext (lineGap, cursorIdx, ())
|
|
|
|
|
|
2025-12-12 20:08:53 +00:00
|
|
|
structure FirstContiguousNonSpace =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isSpace chr then absIdx + 1
|
|
|
|
|
else loop (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun firstContiguousNonSpace (lineGap, cursorIdx) =
|
|
|
|
|
FirstContiguousNonSpace.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure LastContiguousNonSpace =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loop (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loop (0, shd, absIdx, stl)
|
|
|
|
|
| [] => Int.max (0, absIdx - 1)
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isSpace chr then absIdx - 1
|
|
|
|
|
else loop (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
loop (strPos, shd, absIdx, stl)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun lastContiguousNonSpace (lineGap, cursorIdx) =
|
|
|
|
|
LastContiguousNonSpace.foldNext (lineGap, cursorIdx, ())
|
|
|
|
|
|
2025-12-13 03:40:32 +00:00
|
|
|
structure AroundWordPrev =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loopAlphaNum (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl =>
|
|
|
|
|
loopAlphaNum (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loopAlphaNum (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopPunct (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopPunct (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if
|
|
|
|
|
Char.isAlphaNum chr orelse chr = #"_" orelse chr = #"\n"
|
|
|
|
|
orelse Char.isSpace chr
|
|
|
|
|
then absIdx + 1
|
|
|
|
|
else (* is punct *) loopPunct (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopSpace (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx + 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx
|
|
|
|
|
else if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loopAlphaNum (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopPunct (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun aroundWordPrev (lineGap, cursorIdx) =
|
|
|
|
|
AroundWordPrev.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure AroundWordNext =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun stopAtFirstNonSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => stopAtFirstNonSpace (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
stopAtFirstNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopAlphaNum (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopAlphaNum (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loopAlphaNum (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
stopAtFirstNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
(* is punct *)
|
|
|
|
|
absIdx - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopPunct (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopPunct (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
stopAtFirstNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
(* is punct *)
|
|
|
|
|
loopPunct (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopSpace (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loopAlphaNum (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopPunct (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx
|
|
|
|
|
else if Char.isAlphaNum chr orelse chr = #"_" then
|
|
|
|
|
loopAlphaNum (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopPunct (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun aroundWordNext (lineGap, cursorIdx) =
|
|
|
|
|
AroundWordNext.foldNext (lineGap, cursorIdx, ())
|
2025-12-13 03:57:21 +00:00
|
|
|
|
|
|
|
|
structure AroundWORDPrev =
|
|
|
|
|
MakeIfCharFolderPrev
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun loopNonSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl =>
|
|
|
|
|
loopNonSpace (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if Char.isSpace chr then absIdx + 1
|
|
|
|
|
else loopNonSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos < 0 then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopSpace (String.size shd - 1, shd, absIdx, stl)
|
|
|
|
|
| [] => 0
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx + 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx + 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopNonSpace (strPos - 1, shd, absIdx - 1, stl)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun aroundWORDPrev (lineGap, cursorIdx) =
|
|
|
|
|
AroundWORDPrev.foldPrev (lineGap, cursorIdx, ())
|
|
|
|
|
|
|
|
|
|
structure AroundWORDNext =
|
|
|
|
|
MakeIfCharFolderNext
|
|
|
|
|
(struct
|
|
|
|
|
type env = unit
|
|
|
|
|
|
|
|
|
|
fun stopAtFirstNonSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => stopAtFirstNonSpace (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
stopAtFirstNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
absIdx - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopNonSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopNonSpace (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
stopAtFirstNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun loopSpace (strPos, shd, absIdx, stl) =
|
|
|
|
|
if strPos = String.size shd then
|
|
|
|
|
case stl of
|
|
|
|
|
shd :: stl => loopSpace (0, shd, absIdx, stl)
|
|
|
|
|
| [] => absIdx
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx - 1
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun fStart (strPos, shd, _, absIdx, stl, _, _) =
|
|
|
|
|
let
|
|
|
|
|
val chr = String.sub (shd, strPos)
|
|
|
|
|
in
|
|
|
|
|
if chr = #"\n" then
|
|
|
|
|
absIdx
|
|
|
|
|
else if Char.isSpace chr then
|
|
|
|
|
loopSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
else
|
|
|
|
|
loopNonSpace (strPos + 1, shd, absIdx + 1, stl)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
fun aroundWORDNext (lineGap, cursorIdx) =
|
|
|
|
|
AroundWORDNext.foldNext (lineGap, cursorIdx, ())
|
2025-12-13 03:40:32 +00:00
|
|
|
|
2024-11-05 12:38:01 +00:00
|
|
|
fun isCursorAtStartOfLine (lineGap: LineGap.t, cursorIdx) =
|
|
|
|
|
let
|
|
|
|
|
val {rightStrings, idx = bufferIdx, ...} = lineGap
|
|
|
|
|
in
|
|
|
|
|
case rightStrings of
|
|
|
|
|
hd :: tl =>
|
|
|
|
|
let
|
|
|
|
|
(* convert absolute cursorIdx to idx relative to hd string *)
|
|
|
|
|
val strIdx = cursorIdx - bufferIdx
|
|
|
|
|
in
|
|
|
|
|
if strIdx < String.size hd then
|
|
|
|
|
(* chr is in hd *)
|
|
|
|
|
String.sub (hd, strIdx) = #"\n"
|
|
|
|
|
else
|
|
|
|
|
(* chr is in tl *)
|
|
|
|
|
(case tl of
|
2025-03-22 05:18:25 +00:00
|
|
|
tlhd :: _ =>
|
|
|
|
|
let val strIdx = strIdx - String.size hd
|
|
|
|
|
in String.sub (tlhd, strIdx) = #"\n"
|
|
|
|
|
end
|
|
|
|
|
| [] => true)
|
2024-11-05 12:38:01 +00:00
|
|
|
end
|
|
|
|
|
| [] => true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
(* Prerequisite: lineGap is moved to cursorIdx *)
|
|
|
|
|
fun isPrevChrStartOfLine (lineGap: LineGap.t, cursorIdx) =
|
|
|
|
|
let
|
|
|
|
|
val {rightStrings, idx = bufferIdx, leftStrings, ...} = lineGap
|
|
|
|
|
in
|
|
|
|
|
case rightStrings of
|
|
|
|
|
hd :: tl =>
|
|
|
|
|
let
|
|
|
|
|
(* convert absolute cursorIdx to idx relative to hd string *)
|
|
|
|
|
val strIdx = cursorIdx - bufferIdx
|
|
|
|
|
in
|
|
|
|
|
if strIdx > 0 then
|
|
|
|
|
(* prev chr is in hd *)
|
|
|
|
|
String.sub (hd, strIdx - 1) = #"\n"
|
|
|
|
|
else
|
|
|
|
|
(* prev chr if in leftStrings *)
|
|
|
|
|
(case leftStrings of
|
2025-03-22 05:18:25 +00:00
|
|
|
lhd :: _ => String.sub (lhd, String.size lhd - 1) = #"\n"
|
|
|
|
|
| [] =>
|
|
|
|
|
(* cursorIdx = 0 which means we are at start of file/line *)
|
|
|
|
|
true)
|
2024-11-05 12:38:01 +00:00
|
|
|
end
|
2025-03-22 05:18:25 +00:00
|
|
|
| [] => true
|
2024-11-05 12:38:01 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun helpIsNextChrEndOfLine (strIdx, hd, tl) =
|
|
|
|
|
if strIdx + 1 < String.size hd then
|
|
|
|
|
(* next chr is in this string *)
|
|
|
|
|
String.sub (hd, strIdx + 1) = #"\n"
|
|
|
|
|
else
|
|
|
|
|
(* next chr, if it exists, is in tl *)
|
|
|
|
|
(case tl of
|
2025-03-22 05:18:25 +00:00
|
|
|
tlhd :: _ => String.sub (tlhd, 0) = #"\n"
|
|
|
|
|
| [] => true)
|
2024-11-05 12:38:01 +00:00
|
|
|
|
|
|
|
|
(* Prerequisite: lineGap is moved to cursorIdx *)
|
|
|
|
|
fun isNextChrEndOfLine (lineGap: LineGap.t, cursorIdx) =
|
|
|
|
|
let
|
|
|
|
|
val {rightStrings, idx = bufferIdx, ...} = lineGap
|
|
|
|
|
in
|
|
|
|
|
case rightStrings of
|
|
|
|
|
hd :: tl =>
|
|
|
|
|
let
|
|
|
|
|
(* convert absolute cursorIdx to idx relative to hd string *)
|
|
|
|
|
val strIdx = cursorIdx - bufferIdx
|
|
|
|
|
in
|
|
|
|
|
if strIdx < String.size hd then
|
|
|
|
|
helpIsNextChrEndOfLine (strIdx, hd, tl)
|
|
|
|
|
else
|
|
|
|
|
(* strIdx is in tl *)
|
|
|
|
|
(case tl of
|
2025-03-22 05:18:25 +00:00
|
|
|
tlhd :: tltl =>
|
|
|
|
|
helpIsNextChrEndOfLine (strIdx - String.size hd, tlhd, tltl)
|
|
|
|
|
| [] =>
|
|
|
|
|
(* strIdx is at end of lineGap
|
|
|
|
|
* which also means at end of line *)
|
2024-11-05 12:38:01 +00:00
|
|
|
true)
|
|
|
|
|
end
|
|
|
|
|
| [] => true
|
|
|
|
|
end
|
2025-09-23 11:47:25 +01:00
|
|
|
|
|
|
|
|
(* Prerequisite: lineGap is moved to cursorIdx *)
|
|
|
|
|
fun isOnNewlineAfterChr (buffer, cursorIdx) =
|
|
|
|
|
cursorIdx > 0 andalso not (isPrevChrStartOfLine (buffer, cursorIdx))
|
|
|
|
|
andalso isCursorAtStartOfLine (buffer, cursorIdx)
|
2024-10-17 02:57:26 +01:00
|
|
|
end
|