reimplement vi's '$' motion as a DFA, also eliminating a bug that involves double deleteion in the process

This commit is contained in:
2025-08-04 05:37:08 +01:00
parent fe9dd0f034
commit 528aea59a1
6 changed files with 78 additions and 94 deletions

View File

@@ -509,7 +509,7 @@ struct
* We also rely on helpRemoveChr to handle backwards-movement logic: * We also rely on helpRemoveChr to handle backwards-movement logic:
* If cursorIdx is at \n after deletion, then stop. * If cursorIdx is at \n after deletion, then stop.
* Else, move back one chr. *) * Else, move back one chr. *)
val lastChr = Cursor.viDlr (buffer, cursorIdx) val lastChr = Cursor.viDlr (buffer, cursorIdx, 1)
val length = lastChr - cursorIdx val length = lastChr - cursorIdx
val buffer = LineGap.delete (cursorIdx, length, buffer) val buffer = LineGap.delete (cursorIdx, length, buffer)
@@ -522,39 +522,23 @@ struct
end end
end end
fun helpDeleteLine (app: app_type, buffer, cursorIdx, otherIdx, count) =
if count = 0 then
let
val otherIdx = Cursor.clipIdx (buffer, otherIdx)
val length = otherIdx - cursorIdx
val buffer = LineGap.delete (cursorIdx, length, buffer)
val {searchList, searchString, ...} = app
val (buffer, searchList) = deleteSearchList
(cursorIdx, length, searchString, searchList, buffer)
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Cursor.clipIdx (buffer, cursorIdx)
in
Finish.buildTextAndClear (app, buffer, cursorIdx, searchList)
end
else
let
(* get otherIdx, where cursor will want to go after motion. *)
val buffer = LineGap.goToIdx (otherIdx, buffer)
val newOtherIdx = Cursor.viDlr (buffer, otherIdx)
val newOtherIdx = Cursor.viL (buffer, newOtherIdx)
val newCount = if newOtherIdx = otherIdx then 0 else count - 1
in
helpDeleteLine (app, buffer, cursorIdx, newOtherIdx, newCount)
end
fun deleteLine (app: app_type, count) = fun deleteLine (app: app_type, count) =
let let
val {buffer, cursorIdx, ...} = app val {buffer, cursorIdx, searchList, searchString, ...} = app
val cursorIdx = Cursor.vi0 (buffer, cursorIdx) val buffer = LineGap.goToIdx (cursorIdx, buffer)
val startIdx = Cursor.vi0 (buffer, cursorIdx)
val finishIdx = Cursor.viDlrForDelete (buffer, cursorIdx, count)
val length = finishIdx - startIdx
val buffer = LineGap.delete (startIdx, length, buffer)
val (buffer, searchList) = deleteSearchList
(startIdx, length, searchString, searchList, buffer)
val buffer = LineGap.goToIdx (startIdx, buffer)
in in
helpDeleteLine (app, buffer, cursorIdx, cursorIdx, count) Finish.buildTextAndClear (app, buffer, cursorIdx, searchList)
end end
fun helpDeleteLineBack (app, buffer, low, high, count) = fun helpDeleteLineBack (app, buffer, low, high, count) =
@@ -587,7 +571,7 @@ struct
let let
val {buffer, cursorIdx, ...} = app val {buffer, cursorIdx, ...} = app
val low = Cursor.vi0 (buffer, cursorIdx) val low = Cursor.vi0 (buffer, cursorIdx)
val high = Cursor.viDlr (buffer, cursorIdx) + 1 val high = Cursor.viDlr (buffer, cursorIdx, 1) + 1
in in
helpDeleteLineBack (app, buffer, low, high, count) helpDeleteLineBack (app, buffer, low, high, count)
end end

View File

@@ -0,0 +1,56 @@
structure ViDlrDfa =
struct
val startState: Word8.word = 0w0
val newlineState: Word8.word = 0w1
val notNewlineState = 0w2
fun makeStart i =
if Char.chr i = #"\n" then newlineState else notNewlineState
val startTable = Vector.tabulate (255, makeStart)
val newlineTable = startTable
val notNewlineTable = startTable
val tables = #[startTable, newlineTable, notNewlineTable]
fun isFinal currentState = currentState = newlineState
structure ViDlr =
MakeNextDfaLoop
(struct
val startState = startState
structure Folder =
MakeCharFolderNext
(struct
val startState = startState
val tables = tables
fun finish x = x - 1
val isFinal = isFinal
end)
val fStart = Folder.foldNext
end)
structure ViDlrForDelete =
MakeNextDfaLoop
(struct
val startState = startState
structure Folder =
MakeCharFolderNext
(struct
val startState = startState
val tables = tables
fun finish x = x + 1
val isFinal = isFinal
end)
val fStart = Folder.foldNext
end)
val next = ViDlr.next
val nextForDelete = ViDlrForDelete.next
end

View File

@@ -47,67 +47,8 @@ struct
fun vi0 (lineGap, cursorIdx) = Vi0.foldPrev (lineGap, cursorIdx, ()) fun vi0 (lineGap, cursorIdx) = Vi0.foldPrev (lineGap, cursorIdx, ())
fun helpViDlr (strPos, str, absIdx, strTl, lineTl) = val viDlr = ViDlrDfa.next
if strPos = String.size str then val viDlrForDelete = ViDlrDfa.nextForDelete
case (strTl, lineTl) of
(shd :: stl, lhd :: ltl) => helpViDlr (0, shd, absIdx, stl, ltl)
| (_, _) => absIdx - 1
else
case String.sub (str, strPos) of
#"\n" => absIdx - 1
| _ => helpViDlr (strPos + 1, str, absIdx + 1, strTl, lineTl)
fun viDlr (lineGap: LineGap.t, cursorIdx) =
let
val
{rightStrings, idx = bufferIdx, rightLines, leftStrings, leftLines, ...} =
lineGap
in
case (rightStrings, rightLines) of
(strHd :: strTl, lnHd :: lnTl) =>
let
(* convert absolute cursorIdx to idx relative to hd string *)
val strIdx = cursorIdx - bufferIdx
in
if strIdx < String.size strHd then
if String.sub (strHd, strIdx) <> #"\n" then
(* not in double linebreak *)
helpViDlr (strIdx + 1, strHd, cursorIdx + 1, strTl, lnTl)
else (* check if we are in double linebreak *) if strIdx - 1 >= 0 then
if String.sub (strHd, strIdx - 1) = #"\n" then
(* we are in double linebreak, so do nothing *)
cursorIdx
else
(* not in double linebreak, so iterate *)
helpViDlr (strIdx + 1, strHd, cursorIdx + 1, strTl, lnTl)
else
(* check if double linebreak in strTl *)
(case strTl of
nestStrHd :: _ =>
if String.sub (nestStrHd, 0) = #"\n" then
cursorIdx
else
helpViDlr (strIdx + 1, strHd, cursorIdx + 1, strTl, lnTl)
| [] => cursorIdx)
else
(* strIdx must be in the strTl *)
(case (strTl, lnTl) of
(nestStrHd :: nestStrTl, nestLnHd :: nestLnTl) =>
let
val strIdx = strIdx - String.size strHd
in
helpViDlr
( strIdx + 1
, nestStrHd
, cursorIdx + 1
, nestStrTl
, nestLnTl
)
end
| (_, _) => cursorIdx)
end
| (_, _) => (* nowhere to go, so return cursorIdx *) cursorIdx
end
fun helpViL (strIdx, hd, cursorIdx, tl) = fun helpViL (strIdx, hd, cursorIdx, tl) =
if String.sub (hd, strIdx) = #"\n" then if String.sub (hd, strIdx) = #"\n" then

View File

@@ -43,7 +43,6 @@ structure MoveViK = MakeMove (struct val fMove = Cursor.viK end)
structure MoveViL = MakeMove (struct val fMove = Cursor.viL end) structure MoveViL = MakeMove (struct val fMove = Cursor.viL end)
structure MoveToStartOfLine = MakeMove (struct val fMove = Cursor.vi0 end) structure MoveToStartOfLine = MakeMove (struct val fMove = Cursor.vi0 end)
structure MoveToEndOfLine = MakeMove (struct val fMove = Cursor.viDlr end)
signature DFA_MOVE = signature DFA_MOVE =
sig sig
@@ -82,3 +81,5 @@ structure MoveToEndOfPrevWord =
MakeDfaMove (struct val fMove = Cursor.endOfPrevWord end) MakeDfaMove (struct val fMove = Cursor.endOfPrevWord end)
structure MoveToEndOfPrevWORD = structure MoveToEndOfPrevWORD =
MakeDfaMove (struct val fMove = Cursor.endOfPrevWORD end) MakeDfaMove (struct val fMove = Cursor.endOfPrevWORD end)
structure MoveToEndOfLine = MakeDfaMove (struct val fMove = Cursor.viDlr end)

View File

@@ -25,6 +25,7 @@ in
fcore/cursor-dfa/make-dfa-loop.sml fcore/cursor-dfa/make-dfa-loop.sml
fcore/cursor-dfa/vi-WORD-dfa.sml fcore/cursor-dfa/vi-WORD-dfa.sml
fcore/cursor-dfa/vi-word-dfa.sml fcore/cursor-dfa/vi-word-dfa.sml
fcore/cursor-dfa/vi-dlr-dfa.sml
end end
fcore/cursor.sml fcore/cursor.sml
fcore/text-window.sml fcore/text-window.sml

View File

@@ -25,6 +25,7 @@ in
fcore/cursor-dfa/make-dfa-loop.sml fcore/cursor-dfa/make-dfa-loop.sml
fcore/cursor-dfa/vi-WORD-dfa.sml fcore/cursor-dfa/vi-WORD-dfa.sml
fcore/cursor-dfa/vi-word-dfa.sml fcore/cursor-dfa/vi-word-dfa.sml
fcore/cursor-dfa/vi-dlr-dfa.sml
end end
fcore/cursor.sml fcore/cursor.sml
fcore/text-window.sml fcore/text-window.sml