From 859860b19fe6c369cecdd28e0cf0fc886e2b6fb6 Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Fri, 19 Sep 2025 22:33:37 +0100 Subject: [PATCH] reimplement vi's 'j' motion, and remove the original function for this motion which was in cursor.sml --- fcore/cursor.sml | 141 ------------------------------ fcore/move.sml | 2 - fcore/normal-mode/normal-mode.sml | 2 +- fcore/normal-mode/normal-move.sml | 90 ++++++++++++++++++- 4 files changed, 90 insertions(+), 145 deletions(-) diff --git a/fcore/cursor.sml b/fcore/cursor.sml index 8610f7f..dd2fafc 100644 --- a/fcore/cursor.sml +++ b/fcore/cursor.sml @@ -132,147 +132,6 @@ struct if lineIdx = 0 then cursorIdx else cursorIdx - lineIdx - 1 end - fun helpViJ - ( strPos - , str - , absIdx - , lineColumn - , preferredColumn - , hasPassedLine - , strTl - , lineTl - , prevIsLn - ) = - if strPos = String.size str then - case (strTl, lineTl) of - (shd :: stl, lhd :: ltl) => - (* todo: possibly check if we have passed line, - * and if so, if there are any line breaks in the lineHd - * which we could use to skip searching part of the string. - * However, this will likely have worse cache locality - * as we switch to searching in string to searcing in line vector - * so perhaps not. *) - helpViJ - ( 0 - , shd - , absIdx - , lineColumn - , preferredColumn - , hasPassedLine - , stl - , ltl - , prevIsLn - ) - | (_, _) => (* empty, so return end of previous string *) absIdx - 1 - else - case String.sub (str, strPos) of - #"\n" => - if hasPassedLine then - (* reached end of line twice, - * but line has fewer chars than preferredColumn *) - if prevIsLn then - (* line break is preceded by linebreak *) - absIdx - else - (* line break is preceded by graphical chr - * so go to graphical chr *) - absIdx - 1 - else - (* reached end of line once; - * continue iterationg *) - helpViJ - ( strPos + 1 - , str - , absIdx + 1 - , 0 - , preferredColumn - , true - , strTl - , lineTl - , true - ) - | _ => - if lineColumn = preferredColumn andalso hasPassedLine then - (* we're at the preferredColumn so return absIdx *) - absIdx - else - (* we're not in the preferred column, so keep iterating *) - helpViJ - ( strPos + 1 - , str - , absIdx + 1 - , lineColumn + 1 - , preferredColumn - , hasPassedLine - , strTl - , lineTl - , false - ) - - fun viJ (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 - (* strIdx is in this string *) - let - val lineColumn = getCursorColumn - (strIdx, strHd, lnHd, leftStrings, leftLines, cursorIdx) - in - helpViJ - ( strIdx - , strHd - , cursorIdx - , lineColumn - , lineColumn - , false - , strTl - , lnTl - , false - ) - end - else - (* strIdx must be in the strTl *) - (case (strTl, lnTl) of - (nestStrHd :: nestStrTl, nestLnHd :: nestLnTl) => - let - val strIdx = strIdx - String.size strHd - val leftStrings = strHd :: leftStrings - val leftLines = lnHd :: leftLines - val lineColumn = getCursorColumn - ( strIdx - , nestStrHd - , nestLnHd - , leftStrings - , leftLines - , cursorIdx - ) - in - helpViJ - ( strIdx - , nestStrHd - , cursorIdx - , lineColumn - , lineColumn - , false - , nestStrTl - , nestLnTl - , false - ) - end - | (_, _) => cursorIdx) - end - | (_, _) => (* nowhere to go rightward, so return cursorIdx *) cursorIdx - end - (* equivalent of vi's 'w' command *) val nextWord = ViWordDfa.startOfNextWord diff --git a/fcore/move.sml b/fcore/move.sml index 7a42471..d18751a 100644 --- a/fcore/move.sml +++ b/fcore/move.sml @@ -42,8 +42,6 @@ struct end end -structure MoveViJ = MakeMove (struct val fMove = Cursor.viJ end) - structure MoveToStartOfLine = MakeMove (struct val fMove = Cursor.vi0 end) signature DFA_MOVE = diff --git a/fcore/normal-mode/normal-mode.sml b/fcore/normal-mode/normal-mode.sml index 169bb26..5ef4a25 100644 --- a/fcore/normal-mode/normal-mode.sml +++ b/fcore/normal-mode/normal-mode.sml @@ -147,7 +147,7 @@ struct fun parseChr (app: app_type, count, chr, str, time) = case chr of #"h" => MoveViH.move (app, count) - | #"j" => MoveViJ.move (app, count) + | #"j" => NormalMove.moveCursorDown (app, count) | #"k" => NormalMove.moveCursorUp (app, count) | #"l" => MoveViL.move (app, count) | #"w" => MoveToNextWord.move (app, count) diff --git a/fcore/normal-mode/normal-move.sml b/fcore/normal-mode/normal-move.sml index b5fdb25..6d5cf92 100644 --- a/fcore/normal-mode/normal-move.sml +++ b/fcore/normal-mode/normal-move.sml @@ -132,6 +132,7 @@ struct else LineGap.idxToLineNumber (cursorIdx, buffer) val newCursorLineNumber = Int.max (cursorLineNumber - count, 0) + val column = if newCursorLineNumber = 0 then column - 1 else column val buffer = LineGap.goToLine (newCursorLineNumber, buffer) val lineIdx = LineGap.lineNumberToIdx (newCursorLineNumber, buffer) @@ -143,7 +144,6 @@ struct val buffer = LineGap.goToIdx (lineIdx, buffer) val endOfLineIdx = Cursor.viDlr (buffer, lineIdx, 1) - val column = if newCursorLineNumber = 0 then column - 1 else column val cursorIdx = Int.min (endOfLineIdx, lineIdx + column) val buffer = LineGap.goToIdx (cursorIdx, buffer) @@ -188,6 +188,94 @@ struct ) end + fun moveCursorDown (app: app_type, count) = + let + val + { windowWidth + , windowHeight + , cursorIdx + , buffer + , startLine = prevLineNumber + , searchList + , searchString + , bufferModifyTime + , visualScrollColumn = prevScrollColumn + , ... + } = app + + (* calculate new idx to move to *) + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val startOfLine = Cursor.vi0 (buffer, cursorIdx) + val column = cursorIdx - startOfLine + + val cursorLineNumber = + if Cursor.isNextChrEndOfLine (buffer, cursorIdx) then + LineGap.idxToLineNumber (cursorIdx + 1, buffer) + else + LineGap.idxToLineNumber (cursorIdx, buffer) + + val newCursorLineNumber = cursorLineNumber + count + val newCursorLineNumber = + if cursorLineNumber >= #lineLength buffer - 2 then + Int.max (0, #lineLength buffer - 2) + else + newCursorLineNumber + + val buffer = LineGap.goToLine (newCursorLineNumber, buffer) + val lineIdx = LineGap.lineNumberToIdx (newCursorLineNumber, buffer) + val buffer = LineGap.goToIdx (lineIdx, buffer) + val lineIdx = + if Cursor.isNextChrEndOfLine (buffer, lineIdx) then lineIdx + else lineIdx + 1 + + val buffer = LineGap.goToIdx (lineIdx, buffer) + val endOfLineIdx = Cursor.viDlr (buffer, lineIdx, 1) + + val cursorIdx = Int.min (endOfLineIdx, lineIdx + column) + + val buffer = LineGap.goToIdx (cursorIdx, buffer) + + (* create draw message *) + val visualScrollColumn = + TextScroll.getScrollColumn + (buffer, cursorIdx, windowWidth, prevScrollColumn) + + val startLine = + TextScroll.getStartLine + (prevLineNumber, newCursorLineNumber, windowHeight) + + val buffer = LineGap.goToLine (startLine, buffer) + + val drawMsg = NormalModeTextBuilder.build + ( startLine + , cursorIdx + , buffer + , windowWidth + , windowHeight + , searchList + , searchString + , visualScrollColumn + ) + val drawMsg = Vector.concat drawMsg + val drawMsg = DrawMsg.DRAW_TEXT drawMsg + val drawMsg = [MailboxType.DRAW drawMsg] + + val mode = NORMAL_MODE "" + in + NormalModeWith.bufferAndCursorIdx + ( app + , buffer + , cursorIdx + , mode + , startLine + , searchList + , drawMsg + , bufferModifyTime + , visualScrollColumn + ) + end + + fun moveToLine (app: app_type, reqLine) = let val reqLine = reqLine - 1