pull in new version of brolib-sml so that we can get bug fixes for LineGap.getLineNumberOfIdx, delete the TextWindow structure (which was previously used to get the start line to draw from), and replace previous usages of TextWindow with TextScroll.getStartLine. This enables us to scroll in a way tailored to the reimplemented TextBuilder (which do not wrap lines or characters)

This commit is contained in:
2025-09-13 05:12:17 +01:00
parent a3b00e09d8
commit 668dd07699
8 changed files with 76 additions and 580 deletions

View File

@@ -12,20 +12,24 @@ struct
fun buildTextAndClear
(app: app_type, buffer, cursorIdx, searchList, msgs, bufferModifyTime) =
let
val {windowWidth, windowHeight, startLine, searchString,
visualScrollColumn = prevScrollColumn, ...} = app
val
{ windowWidth
, windowHeight
, searchString
, visualScrollColumn = prevScrollColumn
, startLine = prevLineNumber
, ...
} = app
(* calculate scroll column *)
(* calculate new scroll column and start line, if there are any changes *)
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, cursorIdx, windowWidth, prevScrollColumn)
TextScroll.getScrollColumn
(buffer, cursorIdx, windowWidth, prevScrollColumn)
(* move LineGap to first line displayed on screen *)
val buffer = LineGap.goToLine (startLine, buffer)
(* get new startLine which may move screen depending on cursor movements *)
val startLine = TextWindow.getStartLine
(buffer, startLine, cursorIdx, windowWidth, windowHeight)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, windowHeight)
(* move buffer to new startLine as required by TextBuilder.build *)
val buffer = LineGap.goToLine (startLine, buffer)
@@ -93,7 +97,8 @@ struct
val newBuffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, cursorIdx, newWidth, prevScrollColumn)
TextScroll.getScrollColumn
(buffer, cursorIdx, newWidth, prevScrollColumn)
val newBuffer = LineGap.goToLine (startLine, newBuffer)
val lineIdx = TextBuilderUtils.getLineAbsIdxFromBuffer (startLine, buffer)
@@ -123,69 +128,14 @@ struct
)
end
(* Difference between this and buildTextAndClear is that
* this is meant to be called after a chr movement,
* where the cursor may possibly jump off window by a wide marigin.
* Since the cursor may move away a lot, it is best to recenter.
* *)
fun buildTextAndClearAfterChr
(app: app_type, buffer, cursorIdx, searchList, initialMsg, bufferModifyTime) :
AppType.app_type =
let
val {windowWidth, windowHeight, startLine, searchString, visualScrollColumn
= prevScrollColumn, ...} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, cursorIdx, windowWidth, prevScrollColumn)
(* move LineGap to first line displayed on screen *)
val buffer = LineGap.goToLine (startLine, buffer)
(* get new startLine which may move screen depending on cursor movements *)
val startLine = TextWindow.getStartLine
(buffer, startLine, cursorIdx, windowWidth, windowHeight)
(* move buffer to new startLine as required by TextBuilder.build
* and move searchList to idx where line starts as well *)
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 = DRAW_TEXT drawMsg
val msgs = DRAW drawMsg :: initialMsg
val mode = NORMAL_MODE ""
in
NormalModeWith.bufferAndCursorIdx
( app
, buffer
, cursorIdx
, mode
, startLine
, searchList
, msgs
, bufferModifyTime
, visualScrollColumn
)
end
(* todo: check; the way we get the startLine is almost certainly wrong *)
fun centreToCursor (app: app_type) =
let
val
{ buffer
, windowWidth
, windowHeight
, startLine = origLine
, startLine = prevLineNumber
, cursorIdx
, searchList
, searchString
@@ -193,10 +143,11 @@ struct
, visualScrollColumn
, ...
} = app
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val startLine = TextWindow.getStartLineWithCursorCentered
(buffer, cursorIdx, origLine, windowWidth, windowHeight div 2)
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, windowHeight div 2)
val buffer = LineGap.goToLine (startLine, buffer)
val drawMsg = NormalModeTextBuilder.build
@@ -213,20 +164,16 @@ struct
val drawMsg = DRAW_TEXT drawMsg
val drawMsg = [DRAW drawMsg]
in
let
val _ = raise Fail "centering to line is unimplemented\n"
in
NormalModeWith.bufferAndCursorIdx
( app
, buffer
, cursorIdx
, NORMAL_MODE ""
, startLine
, searchList
, drawMsg
, bufferModifyTime
, #visualScrollColumn app
)
end
NormalModeWith.bufferAndCursorIdx
( app
, buffer
, cursorIdx
, NORMAL_MODE ""
, startLine
, searchList
, drawMsg
, bufferModifyTime
, #visualScrollColumn app
)
end
end

View File

@@ -70,18 +70,11 @@ struct
val buffer = LineGap.goToIdx (bufferIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, bufferIdx, windowWidth, prevScrollColumn)
val bufferLine =
let
val maxHeight = windowHeight - TextConstants.ySpace
in
TextWindow.getStartLineWithCursorCentered
(buffer, bufferIdx, bufferLine, windowWidth, maxHeight)
end
TextScroll.getScrollColumn
(buffer, bufferIdx, windowWidth, prevScrollColumn)
val bufferLine = let in raise Fail "todo: fix" end
val buffer = LineGap.goToLine (bufferLine, buffer)
val lineIdx =
TextBuilderUtils.getLineAbsIdxFromBuffer (bufferLine, buffer)
val drawMsg = NormalModeTextBuilder.build
( bufferLine
@@ -121,7 +114,7 @@ struct
{ windowWidth
, windowHeight
, buffer
, startLine = origLine
, startLine = prevLineNumber
, searchList
, searchString
, bufferModifyTime
@@ -135,9 +128,12 @@ struct
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, cursorIdx, windowWidth, prevScrollColumn)
val startLine = TextWindow.getStartLineWithCursorCentered
(buffer, cursorIdx, origLine, windowWidth, windowHeight div 2)
TextScroll.getScrollColumn
(buffer, cursorIdx, windowWidth, prevScrollColumn)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, windowHeight)
val buffer = LineGap.goToLine (startLine, buffer)
@@ -177,7 +173,7 @@ struct
, cursorIdx
, windowWidth
, windowHeight
, startLine
, startLine = prevLineNumber
, searchList
, searchString
, bufferModifyTime
@@ -188,16 +184,16 @@ struct
(* move LineGap and buffer to start of line *)
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Cursor.matchPair (buffer, cursorIdx)
val buffer = LineGap.goToLine (startLine, buffer)
val lineIdx = TextBuilderUtils.getLineAbsIdxFromBuffer (startLine, buffer)
in
let
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val visualScrollColumn =
TextScroll.getScrollColumn (buffer, cursorIdx, windowWidth, prevScrollColumn)
val startLine = TextWindow.getStartLineWithCursorCentered
(buffer, cursorIdx, startLine, windowWidth, windowHeight div 2)
TextScroll.getScrollColumn
(buffer, cursorIdx, windowWidth, prevScrollColumn)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, windowHeight)
val buffer = LineGap.goToLine (startLine, buffer)
@@ -256,7 +252,7 @@ struct
fun helpMoveToChr (app: app_type, buffer, cursorIdx, count, fMove, chr) =
if count = 0 then
NormalFinish.buildTextAndClearAfterChr
NormalFinish.buildTextAndClear
(app, buffer, cursorIdx, #searchList app, [], #bufferModifyTime app)
else
let
@@ -288,7 +284,7 @@ struct
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
NormalFinish.buildTextAndClearAfterChr
NormalFinish.buildTextAndClear
(app, buffer, newCursorIdx, searchList, [], bufferModifyTime)
end
@@ -300,7 +296,7 @@ struct
if newCursorIdx = ~1 then
NormalFinish.clearMode app
else
NormalFinish.buildTextAndClearAfterChr
NormalFinish.buildTextAndClear
(app, buffer, newCursorIdx, searchList, [], bufferModifyTime)
end
end

View File

@@ -9,12 +9,13 @@ struct
val
{ buffer
, cursorIdx
, startLine
, startLine = prevLineNumber
, windowWidth
, windowHeight
, visualScrollColumn
, ...
} = app
val mode = NORMAL_SEARCH_MODE
{ searchString = searchString
, tempSearchList = tempSearchList
@@ -57,9 +58,11 @@ struct
)
end
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, windowHeight)
val buffer = LineGap.goToLine (startLine, buffer)
val startLine = TextWindow.getStartLine
(buffer, startLine, cursorIdx, windowWidth, windowHeight)
val remainingWindowHeight = windowHeight - (TextConstants.ySpace * 2)
@@ -92,8 +95,14 @@ struct
, tempSearchList
) =
let
val {buffer, cursorIdx, startLine, searchString, visualScrollColumn, ...} =
app
val
{ buffer
, cursorIdx
, startLine = prevLineNumber
, searchString
, visualScrollColumn
, ...
} = app
val floatWindowWidth = Real32.fromInt newWindowWidth
val floatWindowHeight = Real32.fromInt newWindowHeight
@@ -131,9 +140,11 @@ struct
)
end
val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorLine = LineGap.getLineNumberOfIdx (cursorIdx, buffer)
val startLine =
TextScroll.getStartLine (prevLineNumber, cursorLine, newWindowHeight)
val buffer = LineGap.goToLine (startLine, buffer)
val startLine = TextWindow.getStartLine
(buffer, startLine, cursorIdx, newWindowWidth, newWindowHeight)
val remainingWindowHeight = newWindowHeight - (TextConstants.ySpace * 2)

View File

@@ -64,10 +64,6 @@ struct
(* move LineGap to first line displayed on screen *)
val buffer = LineGap.goToLine (startLine, buffer)
(* get new startLine which may move screen depending on cursor movements *)
val startLine = TextWindow.getStartLine
(buffer, startLine, cursorIdx, windowWidth, windowHeight)
(* move buffer to new startLine as required by TextBuilder.build *)
val buffer = LineGap.goToLine (startLine, buffer)

View File

@@ -34,12 +34,11 @@ struct
(* cursorLine > prevLineNumber *)
let
val howManyLinesWeCanFit = windowHeight div TC.ySpace
val howManyLinesWeCanFit = howManyLinesWeCanFit - 2
in
if prevLineNumber + howManyLinesWeCanFit >= cursorLine then
prevLineNumber
if cursorLine > prevLineNumber + howManyLinesWeCanFit then
cursorLine - howManyLinesWeCanFit
else
let val lineDifference = cursorLine - prevLineNumber
in prevLineNumber + lineDifference
end
prevLineNumber
end
end

View File

@@ -1,452 +0,0 @@
structure TextWindow =
struct
open TextConstants
(* todo: at some point, make sure the functions in the TextWindow structure
* all act as if the screen is centered
* if the window width is more than 80 chars long *)
fun getStartLineBefore (sIdx, shd, lineNum, absIdx, cursorIdx, stl) =
if sIdx < 0 then
case stl of
hd :: tl =>
getStartLineBefore
(String.size hd - 1, hd, lineNum, absIdx, cursorIdx, tl)
| [] => 0
else if absIdx = cursorIdx then
Int.max (lineNum - 1, 0)
else
let
val chr = String.sub (shd, sIdx)
in
if chr = #"\n" then
getStartLineBefore
(sIdx - 1, shd, lineNum - 1, absIdx - 1, cursorIdx, stl)
else
getStartLineBefore
(sIdx - 1, shd, lineNum, absIdx - 1, cursorIdx, stl)
end
fun getStartLineAfter
( sIdx
, shd
, lineNum
, absIdx
, cursorIdx
, stl
, maxWidth
, maxHeight
, curWidth
, curHeight
, origLine
) =
if sIdx = String.size shd then
case stl of
hd :: tl =>
getStartLineAfter
( 0
, hd
, lineNum
, absIdx
, cursorIdx
, tl
, maxWidth
, maxHeight
, curWidth
, curHeight
, origLine
)
| [] => origLine
else if absIdx = cursorIdx then
origLine
else
let
val chr = String.sub (shd, sIdx)
in
if
chr = #"\n"
then
if curHeight + (ySpace * 3) >= maxHeight then
getStartLineAfter
( sIdx + 1
, shd
, lineNum + 1
, absIdx + 1
, cursorIdx
, stl
, maxWidth
, maxHeight
, 0
, curHeight + ySpace
, origLine + 1
)
else
getStartLineAfter
( sIdx + 1
, shd
, lineNum + 1
, absIdx + 1
, cursorIdx
, stl
, maxWidth
, maxHeight
, 0
, curHeight + ySpace
, origLine
)
else if
curWidth + xSpace <= maxWidth
then
let
val curWidth = curWidth + xSpace
in
getStartLineAfter
( sIdx + 1
, shd
, lineNum
, absIdx + 1
, cursorIdx
, stl
, maxWidth
, maxHeight
, curWidth
, curHeight
, origLine
)
end
else (* have to create visual line break *) if
curHeight + (ySpace * 3) >= maxHeight
then
getStartLineAfter
( sIdx + 1
, shd
, lineNum + 1
, absIdx + 1
, cursorIdx
, stl
, maxWidth
, maxHeight
, 0
, curHeight + ySpace
, origLine + 1
)
else
getStartLineAfter
( sIdx + 1
, shd
, lineNum + 1
, absIdx + 1
, cursorIdx
, stl
, maxWidth
, maxHeight
, 0
, curHeight + ySpace
, origLine
)
end
(* Prerequisite: LineGap is moved to oldLine first. *)
fun getStartLine (lineGap: LineGap.t, oldLine, cursorIdx, maxWidth, maxHeight) =
let
val
{ rightStrings
, rightLines
, line = curLine
, idx = curIdx
, leftStrings
, ...
} = lineGap
in
case (rightStrings, rightLines) of
(rStrHd :: rStrTl, rLnHd :: _) =>
let
(* get index of line to start building from *)
val startIdx =
if oldLine > curLine then
let
val lnPos = oldLine - curLine - 1
val startIdx = Vector.sub (rLnHd, lnPos)
in
startIdx - 1
end
else
0
val absIdx = curIdx + startIdx
in
if cursorIdx < absIdx then
(* move upwards *)
getStartLineBefore
(startIdx, rStrHd, oldLine, absIdx, cursorIdx, leftStrings)
else if cursorIdx = absIdx + 1 then
(* double linebreak *)
getStartLineBefore
( startIdx + 1
, rStrHd
, oldLine
, absIdx + 1
, cursorIdx
, leftStrings
)
else if cursorIdx > absIdx then
(* possibly move downwards *)
getStartLineAfter
( Int.max (startIdx, 0)
, rStrHd
, oldLine
, absIdx
, cursorIdx
, rStrTl
, maxWidth
, maxHeight
, 0
, 0
, oldLine
)
else
(* keep current line *)
Int.max (oldLine - 1, 0)
end
| (_, _) => oldLine
end
fun helpCentreCursor (strPos, str, lineNum, stl, maxW, halfH, curW, curH) =
if strPos < 0 then
case stl of
hd :: tl =>
helpCentreCursor
(String.size hd - 1, hd, lineNum, tl, maxW, halfH, curW, curH)
| [] => (* return 0 for start of buffer *) 0
else
let
val chr = String.sub (str, strPos)
in
if
chr = #"\n"
then
if curH + (ySpace * 3) >= halfH then
(* if we exceeded half the screen *)
lineNum
else
helpCentreCursor
(strPos - 1, str, lineNum - 1, stl, maxW, halfH, 0, curH + ySpace)
else if
curW + xSpace <= maxW
then
let
val curW = curW + xSpace
in
helpCentreCursor
(strPos - 1, str, lineNum, stl, maxW, halfH, curW + xSpace, curH)
end
else (* have to create visual line break *) if
curH + (ySpace * 3) >= halfH
then
(* if at limit, return current line lineNum *)
lineNum
else
helpCentreCursor
(strPos - 1, str, lineNum - 1, stl, maxW, halfH, 0, curH + ySpace)
end
(* search for prev \n, and once found,
* call function to return startLine where cursor is centered *)
fun getCursorStartLine (strPos, str, lineNum, stl, maxW, halfH) =
if strPos < 0 then
case stl of
hd :: tl =>
getCursorStartLine (String.size hd - 1, hd, lineNum, tl, maxW, halfH)
| [] => 0
else
let
val chr = String.sub (str, strPos)
in
if chr = #"\n" then
(* \n found *)
helpCentreCursor
(strPos - 1, str, lineNum - 1, stl, maxW, halfH, xSpace, ySpace)
else
getCursorStartLine (strPos - 1, str, lineNum, stl, maxW, halfH)
end
fun getLineNum (strIdx, lhd, bufferLine) =
if Vector.length lhd = 0 then
bufferLine
else if Vector.length lhd = 1 then
let val lineIdx = Vector.sub (lhd, 0)
in if lineIdx < strIdx then bufferLine + 1 else bufferLine
end
else
let
val firstLineIdx = Vector.sub (lhd, 0)
in
if firstLineIdx > strIdx then
bufferLine
else if firstLineIdx < strIdx then
BinSearch.equalOrLess (strIdx - 1, lhd) + bufferLine
else
bufferLine + 1
end
(* Prerequisite: LineGap is moved to cursor *)
fun getStartLineWithCursorCentered
(lineGap: LineGap.t, cursorIdx, origLine, maxWidth, maxHeight) =
let
val
{ rightStrings
, rightLines
, idx = bufferIdx
, line = bufferLine
, leftStrings
, ...
} = lineGap
in
case (rightStrings, rightLines) of
(shd :: stl, lhd :: ltl) =>
let
(* convert absolute cursorIdx to idx relative to hd string *)
val strIdx = cursorIdx - bufferIdx
in
if strIdx < String.size shd then
(* strIdx is in hd *)
let
val lineNum = getLineNum (strIdx, lhd, bufferLine)
in
getCursorStartLine
(strIdx, shd, lineNum, leftStrings, maxWidth, maxHeight)
end
else
(* strIdx is in tl *)
case (stl, ltl) of
(stlhd :: stltl, ltlhd :: _) =>
let
val strIdx = strIdx - String.size shd
val bufferLine = bufferLine + Vector.length lhd
val lineNum = getLineNum (strIdx, ltlhd, bufferLine)
val leftStrings = shd :: leftStrings
in
getCursorStartLine
(strIdx, stlhd, lineNum, leftStrings, maxWidth, maxHeight)
end
| (_, _) => origLine
end
| (_, _) => origLine
end
fun helpIsCursorVisible
(strPos, str, stl, absIdx, maxW, maxH, curW, curH, newCursorIdx) =
if strPos = String.size str then
case stl of
hd :: tl =>
helpIsCursorVisible
(0, hd, tl, absIdx, maxW, maxH, curW, curH, newCursorIdx)
| [] => true
else if absIdx = newCursorIdx then
true
else
let
val chr = String.sub (str, strPos)
in
if
chr = #"\n"
then
if curH + (ySpace * 3) >= maxH then
false
else
helpIsCursorVisible
( strPos + 1
, str
, stl
, absIdx + 1
, maxW
, maxH
, 0
, curH + ySpace
, newCursorIdx
)
else if
curW + xSpace <= maxW
then
helpIsCursorVisible
( strPos + 1
, str
, stl
, absIdx + 1
, maxW
, maxH
, curW + xSpace
, curH
, newCursorIdx
)
else (* have to create visual line break *) if
curH + (ySpace * 3) >= maxH
then
false
else
helpIsCursorVisible
( strPos + 1
, str
, stl
, absIdx + 1
, maxW
, maxH
, 0
, curH + ySpace
, newCursorIdx
)
end
fun startIsCursorVisible
(curIdx, shd, stl, lhd, startLine, curLine, maxW, maxH, newCursorIdx) =
if startLine = curLine then
helpIsCursorVisible (0, shd, stl, curIdx, maxW, maxH, 0, 0, newCursorIdx)
else
let
val relativeLine = (curLine + Vector.length lhd) - startLine
val lineIdx = Vector.sub (lhd, relativeLine)
val absIdx = curIdx + lineIdx
in
helpIsCursorVisible
(lineIdx, shd, stl, absIdx, maxW, maxH, 0, 0, newCursorIdx)
end
(* Prerequisite: move LineGap.t to startLine *)
fun isCursorVisible (lineGap: LineGap.t, newCursorIdx, startLine, maxW, maxH) =
let
val {rightStrings, rightLines, line = curLine, idx = curIdx, ...} =
lineGap
in
case (rightStrings, rightLines) of
(shd :: stl, lhd :: ltl) =>
if startLine < curLine + Vector.length lhd then
(* startLine in this node *)
startIsCursorVisible
( curIdx
, shd
, stl
, lhd
, startLine
, curLine
, maxW
, maxH
, newCursorIdx
)
else
(* startLine is in stl *)
(case (stl, ltl) of
(stlhd :: stltl, ltlhd :: ltltl) =>
startIsCursorVisible
( curIdx
, stlhd
, stltl
, ltlhd
, startLine
, curLine
, maxW
, maxH
, newCursorIdx
)
| (_, _) => true)
| (_, _) => true
end
end