From 169f96f459a612a04f192dbb2b9e35846777c57f Mon Sep 17 00:00:00 2001 From: Humza Shahid Date: Tue, 16 Sep 2025 04:35:49 +0100 Subject: [PATCH] fix exception when the buffer is empty --- .../text-builder/normal-mode-text-builder.sml | 107 ++++++++++-------- shell/shell.sml | 25 +++- 2 files changed, 83 insertions(+), 49 deletions(-) diff --git a/fcore/text-builder/normal-mode-text-builder.sml b/fcore/text-builder/normal-mode-text-builder.sml index 8d7bc3c..66b4f08 100644 --- a/fcore/text-builder/normal-mode-text-builder.sml +++ b/fcore/text-builder/normal-mode-text-builder.sml @@ -19,53 +19,70 @@ struct , acc ) = let - val {rightStrings, rightLines, line = curLine, idx = curIdx, ...} = buffer + val + { rightStrings + , rightLines + , line = curLine + , idx = curIdx + , textLength + , ... + } = buffer + + val env = Utils.initEnv + ( 5 + , 5 + , windowWidth + , windowHeight + , floatWindowWidth + , floatWindowHeight + , searchList + , String.size searchString + , visualScrollColumn + , startLine + ) + val {startX, startY, ...} = env in - case (rightStrings, rightLines) of - (shd :: stl, lhd :: ltl) => - let - (* get relative index of line to start building from *) - val strPos = - Utils.getRelativeLineStartFromRightHead (startLine, curLine, lhd) - (* get absolute idx of line *) - val absIdx = curIdx + strPos + if textLength = 1 then + (* empty string, so there is nothing we can draw + * except a cursor at the line start. + * An empty string is usually thought of to have a length of 0 + * and this is true, but we always have a \n at the end of the buffer + * to respect Unix-style file endings, which we always uphold. + * So, for us, an empty string has a length of 1. *) + [Utils.makeCursor (startX, startY, env)] + else + case (rightStrings, rightLines) of + (shd :: stl, lhd :: ltl) => + let + (* get relative index of line to start building from *) + val strPos = + Utils.getRelativeLineStartFromRightHead + (startLine, curLine, lhd) + (* get absolute idx of line *) + val absIdx = curIdx + strPos - val searchPos = BinSearch.equalOrMore (absIdx, searchList) - val searchPos = - if searchPos = ~1 then Vector.length searchList else searchPos - - val env = Utils.initEnv - ( 5 - , 5 - , windowWidth - , windowHeight - , floatWindowWidth - , floatWindowHeight - , searchList - , String.size searchString - , visualScrollColumn - , startLine - ) - val {startX, startY, ...} = env - in - TextBuilderWithHighlight.build - ( strPos - , shd - , stl - , lhd - , ltl - , startX - , startY - , 0 - , startLine - , absIdx - , cursorPos - , env - , acc - , searchPos - ) - end - | (_, _) => acc + val searchPos = BinSearch.equalOrMore (absIdx, searchList) + val searchPos = + if searchPos = ~1 then Vector.length searchList else searchPos + in + TextBuilderWithHighlight.build + ( strPos + , shd + , stl + , lhd + , ltl + , startX + , startY + , 0 + , startLine + , absIdx + , cursorPos + , env + , acc + , searchPos + ) + end + | (_, _) => acc end fun buildWithExisting diff --git a/shell/shell.sml b/shell/shell.sml index d996614..68413ed 100644 --- a/shell/shell.sml +++ b/shell/shell.sml @@ -49,10 +49,27 @@ struct () end - fun ioToLineGap (io, acc) = - case TextIO.inputLine io of - SOME str => ioToLineGap (io, LineGap.append (str, acc)) - | NONE => LineGap.goToStart acc + local + fun loop (io, acc, lastCharWasNewline) = + case TextIO.inputLine io of + SOME str => + let + val endsWithNewline = + String.size str > 0 + andalso String.sub (str, String.size str - 1) = #"\n" + in + loop (io, LineGap.append (str, acc), endsWithNewline) + end + | NONE => + if lastCharWasNewline then + LineGap.goToStart acc + else + let val acc = LineGap.append ("\n", acc) + in LineGap.goToStart acc + end + in + fun ioToLineGap (io, acc) = loop (io, acc, false) + end fun main () = let