structure TextBuilderWithCursor = struct structure TC = TextConstants structure Utils = TextBuilderUtils fun goToFirstLineAfter (stl, ltl, posY, lineNumber, absIdx, cursorIdx, env, acc) = case (stl, ltl) of (shd :: stl, lhd :: ltl) => if Vector.length lhd > 0 then let val lineOffset = Vector.sub (lhd, 0) val strPos = lineOffset + 1 val absIdx = absIdx + strPos val posY = posY + TC.ySpace val lineNumber = lineNumber + 1 in build ( strPos , shd , stl , lhd , ltl , #startX env , posY , 0 , lineNumber , absIdx , cursorIdx , env , acc ) end else (* keep looping until we find a linebreak *) goToFirstLineAfter ( stl , ltl , posY , lineNumber , absIdx + String.size shd , cursorIdx , env , acc ) | (_, _) => acc and skipToColumnStart (pos, str, stl, line, ltl, posY, lineNumber, absIdx, cursorIdx, env, acc) = if Vector.length line = 0 then let (* get index of buffer after this string *) val absIdx = absIdx - pos val absIdx = absIdx + String.size str in goToFirstLineAfter (stl, ltl, posY, lineNumber, absIdx, cursorIdx, env, acc) end else (* bin search lines *) let val searchPos = BinSearch.equalOrMore (pos + 1, #searchList env) in if searchPos = Vector.length line then (* next line is not in this node *) let val absIdx = absIdx - pos val absIdx = absIdx + String.size str in goToFirstLineAfter (stl, ltl, posY, lineNumber, absIdx, cursorIdx, env, acc) end else let val lineOffset = Vector.sub (line, searchPos) val newStrPos = lineOffset + 1 val absIdx = absIdx - pos + newStrPos val posY = posY + TC.ySpace val lineNumber = lineNumber + 1 in build ( newStrPos , str , stl , line , ltl , #startX env , posY , 0 , lineNumber , absIdx , cursorIdx , env , acc ) end end and build ( pos , str , stl , line , ltl , posX , posY , column , lineNumber , absIdx , cursorIdx , env: Utils.env_data , acc ) = if pos = String.size str then case (stl, ltl) of (str :: stl, line :: ltl) => build ( 0 , str , stl , line , ltl , posX , posY , column , lineNumber , absIdx , cursorIdx , env , acc ) | (_, _) => acc else if column < #scrollColumnStart env then skipToColumnStart ( pos , str , stl , line , ltl , posY , lineNumber , absIdx , cursorIdx , env , acc ) else case String.sub (str, pos) of #" " => let val acc = if absIdx = cursorIdx then Utils.makeCursor (posX, posY, env) :: acc else acc in build ( pos + 1 , str , stl , line , ltl , posX + TC.xSpace , posY , column + 1 , lineNumber , absIdx + 1 , cursorIdx , env , acc ) end | #"\n" => let val acc = if absIdx = cursorIdx then Utils.makeCursor (posX, posY, env) :: acc else acc val nextLineNumber = lineNumber + 1 in if nextLineNumber > #lastLineNumber env then acc else build ( pos + 1 , str , stl , line , ltl , #startX env , posY + TC.ySpace , 0 , lineNumber + 1 , absIdx + 1 , cursorIdx , env , acc ) end | chr => let val acc = if absIdx = cursorIdx then let val acc = Utils.makeCursor (posX, posY, env) :: acc in Utils.makeCursorOnChr (chr, posX, posY, env) :: acc end else Utils.makeCursor (posX, posY, env) :: acc in build ( pos + 1 , str , stl , line , ltl , posX + TC.xSpace , posY , column + 1 , lineNumber , absIdx + 1 , cursorIdx , env , acc ) end end