diff --git a/fcore/app-update.sml b/fcore/app-update.sml index 6ad1240..a258231 100644 --- a/fcore/app-update.sml +++ b/fcore/app-update.sml @@ -85,12 +85,46 @@ struct (newApp, drawMsg) end + fun moveToLineStart (app: app_type) = + let + val {buffer, windowWidth, windowHeight, startLine, cursorIdx, ...} = app + + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val cursorIdx = Cursor.vi0 (buffer, cursorIdx) + + val buffer = LineGap.goToLine (startLine, buffer) + val drawMsg = TextBuilder.build + (startLine, cursorIdx, buffer, windowWidth, windowHeight) + + val newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx) + in + (newApp, drawMsg) + end + + fun moveToLineEnd (app: app_type) = + let + val {buffer, windowWidth, windowHeight, startLine, cursorIdx, ...} = app + + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val cursorIdx = Cursor.viDlr (buffer, cursorIdx) + + val buffer = LineGap.goToLine (startLine, buffer) + val drawMsg = TextBuilder.build + (startLine, cursorIdx, buffer, windowWidth, windowHeight) + + val newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx) + in + (newApp, drawMsg) + end + fun handleChr (app: app_type, chr) = case chr of #"h" => moveLeft app | #"j" => moveDown app | #"k" => moveUp app | #"l" => moveRight app + | #"0" => moveToLineStart app + | #"$" => moveToLineEnd app | _ => (app, []) fun update (app, msg) = diff --git a/fcore/cursor.sml b/fcore/cursor.sml index 2fdb725..1b7ce00 100644 --- a/fcore/cursor.sml +++ b/fcore/cursor.sml @@ -646,4 +646,150 @@ struct (* nowhere to go rightward, so return cursorIdx *) cursorIdx end + + fun helpVi0String (strPos, str, absIdx, strTl, lineTl) = + if strPos < 0 then + helpVi0List (strTl, lineTl, absIdx) + else + case String.sub (str, strPos) of + #"\n" => + absIdx + 1 + | _ => + helpVi0String (strPos - 1, str, absIdx - 1, strTl, lineTl) + + and helpVi0List (strings, lines, absIdx) = + case (strings, lines) of + (strHd::strTl, lineHd::lineTl) => + helpVi0String + ( String.size strHd - 1, strHd, absIdx + , strTl, lineTl + ) + | (_, _) => + (* this case means strings and lines are empty + * and empty means we are at first line + * so we can return 0 *) + 0 + + fun vi0 (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 *) + if String.sub (strHd, strIdx) = #"\n" then + (* don't need to do anything if we are already at newline; + * just return current cursorIdx *) + cursorIdx + else + (* not at newline so start iterating *) + helpVi0String + ( strIdx - 1, strHd, cursorIdx - 1 + , strTl, lnTl + ) + else + (* strIdx must be in the strTl *) + (case (strTl, lnTl) of + (nestStrHd :: _, nestLnHd :: _) => + let + val strIdx = strIdx - String.size strHd + in + if String.sub (nestStrHd, strIdx) = #"\n" then + (* already at linebreak so return same cursorIdx *) + cursorIdx + else + (* not in linebreak *) + helpVi0String + ( strIdx - 1, nestStrHd, cursorIdx - 1 + , strHd :: leftStrings, lnHd :: leftLines + ) + end + | (_, _) => cursorIdx) + end + | (_, _) => + (* nowhere to go, so return cursorIdx *) + cursorIdx + end + + fun helpViDlrString (strPos, str, absIdx, strTl, lineTl) = + if strPos = String.size str then + helpViDlrList (strTl, lineTl, absIdx) + else + case String.sub (str, strPos) of + #"\n" => + absIdx - 1 + | _ => + helpViDlrString (strPos + 1, str, absIdx + 1, strTl, lineTl) + + and helpViDlrList (strings, lines, absIdx) = + case (strings, lines) of + (strHd::strTl, lineHd::lineTl) => + helpViDlrString + (0, strHd, absIdx , strTl, lineTl) + | (_, _) => + (* this case means strings and lines are empty + * and empty means we have reached end of lineGap + * so we can return last chr *) + absIdx - 1 + + 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 *) + helpViDlrString + (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 *) + helpViDlrString + (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 + helpViDlrString + (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 + helpViDlrString + (strIdx + 1, nestStrHd, cursorIdx + 1, nestStrTl, nestLnTl) + end + | (_, _) => cursorIdx) + end + | (_, _) => + (* nowhere to go, so return cursorIdx *) + cursorIdx + end end diff --git a/shf b/shf index 11ff9d6..2bacd28 100755 Binary files a/shf and b/shf differ