structure TextWindow = struct open TextConstants fun isPrevChrLn (str, strPos, strTl) = if strPos > 0 then String.sub (str, strPos - 1) = #"\n" else case strTl of hd :: _ => String.sub (hd, String.size hd - 1) = #"\n" | [] => false 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 let val chr = String.sub (shd, sIdx) in if chr = #"\n" then if absIdx <> cursorIdx then getStartLineBefore (sIdx - 1, shd, lineNum - 1, absIdx - 1, cursorIdx, stl) else (* we have found cursor, and it is at \n *) lineNum - 1 else if absIdx <> cursorIdx then getStartLineBefore (sIdx - 1, shd, lineNum, absIdx - 1, cursorIdx, stl) else (* we have found cursor; return line *) lineNum - 1 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 *) oldLine else (* keep current line *) Int.max (oldLine - 1, 0) end | (_, _) => oldLine end end