From 9c688a75adcd647723a993f04cb964d62e7f05a4 Mon Sep 17 00:00:00 2001 From: bptato Date: Sat, 25 Dec 2021 17:29:39 +0100 Subject: Refactor formatting, improve buffer cursor movement --- src/io/buffer.nim | 91 +++++++----------------- src/io/cell.nim | 162 +++++++++++++++++------------------------- src/render/renderdocument.nim | 10 +-- 3 files changed, 98 insertions(+), 165 deletions(-) (limited to 'src') diff --git a/src/io/buffer.nim b/src/io/buffer.nim index 82b5b46e..56de9938 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -191,15 +191,10 @@ func cellOrigin(buffer: Buffer, x, y: int): int = func currentCellOrigin(buffer: Buffer): int = return buffer.cellOrigin(buffer.acursorx, buffer.acursory) -#TODO counter-intuitive naming? func currentDisplayCell(buffer: Buffer): FixedCell = let row = (buffer.cursory - buffer.fromy) * buffer.width return buffer.display[row + buffer.currentCellOrigin()] -func cell(buffer: Buffer): FixedCell = - let row = (buffer.cursory - buffer.fromy) * buffer.width - return buffer.display[row + buffer.acursorx] - func getLink(nodes: seq[Node]): Element = for node in nodes: if node.nodeType == ELEMENT_NODE: @@ -240,23 +235,20 @@ func currentWidth(buffer: Buffer): int = func prevWidth(buffer: Buffer): int = let line = buffer.currentLine + if line.len == 0: return 0 var w = 0 var i = 0 let cc = buffer.fromx + buffer.cursorx - if i >= line.len: - assert i == 0 - return 0 - var r: Rune var pr: Rune + var r: Rune + fastRuneAt(line, i, r) while i < line.len and w < cc: pr = r fastRuneAt(line, i, r) w += r.width() - if pr == Rune(0): - return 0 return pr.width() -func maxScreenWidth*(buffer: Buffer): int = +func maxScreenWidth(buffer: Buffer): int = for line in buffer.lines[buffer.fromy..buffer.lastVisibleLine - 1]: result = max(line.width(), result) @@ -267,9 +259,6 @@ func atPercentOf(buffer: Buffer): int = func hasAnchor*(buffer: Buffer, anchor: string): bool = return buffer.document.getElementById(anchor) != nil -proc addLine(buffer: Buffer) = - buffer.lines.addLine() - proc clearDisplay(buffer: Buffer) = buffer.prevdisplay = buffer.display buffer.display = newFixedGrid(buffer.width, buffer.height) @@ -294,7 +283,6 @@ proc refreshDisplay(buffer: Buffer) = if w > buffer.fromx: while k < w - buffer.fromx: buffer.display[dls + k].runes.add(Rune(' ')) - buffer.display[dls + k].ow = r.width() inc k while i < line.str.len: @@ -302,7 +290,6 @@ proc refreshDisplay(buffer: Buffer) = fastRuneAt(line.str, i, r) w += r.width() if w > buffer.fromx + buffer.width: - buffer.display[dls + k].ow += r.width() break if nf.pos != -1 and nf.pos <= j: cf = nf @@ -313,19 +300,12 @@ proc refreshDisplay(buffer: Buffer) = buffer.display[dls + k].nodes = cf.nodes let tk = k + r.width() while k < tk and k < buffer.width - 1: - buffer.display[dls + k].ow += r.width() inc k inc y -proc restoreCursorX(buffer: Buffer) = - buffer.cursorx = max(min(buffer.currentLineWidth() - 1, buffer.xend), 0) - proc setCursorXB(buffer: Buffer, byte: int) = var b = buffer.currentCursorBytes() - if byte == b: - return - var w = buffer.fromx + buffer.cursorx if b < byte: while b < byte: @@ -350,31 +330,25 @@ proc setCursorXB(buffer: Buffer, byte: int) = buffer.redraw = true buffer.xend = buffer.cursorx -proc setCursorX(buffer: Buffer, x: int) = - if buffer.cursorx == x: - return - var b = buffer.currentCursorBytes() - var w = buffer.fromx + buffer.cursorx - while b < buffer.currentLine.len and w < x: - var r: Rune - fastRuneAt(buffer.currentLine, b, r) - w += r.width() - - while b > 0 and w > x: - let (r, o) = lastRune(buffer.currentLine, b - 1) - w -= r.width() - b -= o - - if x - buffer.fromx >= 0 and x - buffer.width < buffer.fromx: +proc setCursorX(buffer: Buffer, x: int, refresh = true, save = true) = + if (not refresh) or (buffer.fromx <= x and x < buffer.fromx + buffer.width): buffer.cursorx = x else: - if x > buffer.cursorx: + if refresh and buffer.fromx > buffer.cursorx: + buffer.fromx = max(buffer.currentLineWidth() - 1, 0) + buffer.cursorx = buffer.fromx + elif x > buffer.cursorx: buffer.fromx = max(x - buffer.width + 1, 0) + buffer.cursorx = x elif x < buffer.cursorx: - buffer.fromx = min(x, buffer.maxfromx) - buffer.cursorx = x + buffer.fromx = x + buffer.cursorx = x buffer.redraw = true - buffer.xend = buffer.cursorx + if save: + buffer.xend = buffer.cursorx + +proc restoreCursorX(buffer: Buffer) = + buffer.setCursorX(max(min(buffer.currentLineWidth() - 1, buffer.xend), 0), false, false) proc setCursorY(buffer: Buffer, y: int) = if buffer.cursory == y: @@ -416,9 +390,7 @@ proc cursorRight*(buffer: Buffer) = buffer.setCursorX(buffer.cursorx + cellwidth) proc cursorLeft*(buffer: Buffer) = - let cellwidth = buffer.currentWidth() - if buffer.cursorx >= cellwidth: - buffer.setCursorX(buffer.cursorx - cellwidth) + buffer.setCursorX(max(buffer.cursorx - buffer.prevWidth(), 0)) proc cursorLineBegin*(buffer: Buffer) = buffer.setCursorX(0) @@ -568,16 +540,13 @@ proc cursorBottom*(buffer: Buffer) = buffer.setCursorY(min(buffer.fromy + buffer.height - 1, buffer.numLines - 1)) proc cursorLeftEdge*(buffer: Buffer) = - buffer.cursorx = buffer.fromx - buffer.xend = buffer.cursorx + buffer.setCursorX(buffer.fromx) proc cursorVertMiddle*(buffer: Buffer) = - buffer.cursorx = min(buffer.fromx + (buffer.width - 2) div 2, buffer.currentLineWidth) - buffer.xend = buffer.cursorx + buffer.setCursorX(min(buffer.fromx + (buffer.width - 2) div 2, buffer.currentLineWidth)) proc cursorRightEdge*(buffer: Buffer) = - buffer.cursorx = min(buffer.fromx + buffer.width - 1, buffer.currentLineWidth) - buffer.xend = buffer.cursorx + buffer.setCursorX(min(buffer.fromx + buffer.width - 1, buffer.currentLineWidth)) proc centerLine*(buffer: Buffer) = let ny = max(min(buffer.cursory - buffer.height div 2, buffer.numLines - buffer.height), 0) @@ -652,15 +621,13 @@ proc scrollUp*(buffer: Buffer) = proc scrollRight*(buffer: Buffer) = if buffer.fromx + buffer.width < buffer.maxScreenWidth(): inc buffer.fromx - if buffer.fromx >= buffer.cursorx: - buffer.cursorRight() buffer.redraw = true proc scrollLeft*(buffer: Buffer) = if buffer.fromx > 0: dec buffer.fromx - if buffer.fromx + buffer.height <= buffer.cursorx: - buffer.cursorLeft() + if buffer.cursorx < buffer.fromx: + buffer.setCursorX(max(buffer.currentLineWidth() - 1, 0)) buffer.redraw = true proc gotoAnchor*(buffer: Buffer) = @@ -698,10 +665,6 @@ proc updateCursor(buffer: Buffer) = buffer.fromy = 0 buffer.cursory = buffer.lastVisibleLine - 1 - if buffer.cursorx >= buffer.currentLineWidth() - 1: - buffer.cursorx = max(buffer.currentLineWidth() - 1, 0) - buffer.fromx = max(buffer.cursorx - buffer.width + 1, 0) - if buffer.lines.len == 0: buffer.cursory = 0 @@ -762,8 +725,8 @@ proc render*(buffer: Buffer) = buffer.updateCursor() proc cursorBufferPos(buffer: Buffer) = - let x = max(buffer.cursorx - buffer.fromx, 0) - let y = buffer.cursory - buffer.fromy + let x = buffer.acursorx + let y = buffer.acursory print(HVP(y + 1, x + 1)) proc clearStatusMessage(buffer: Buffer) = @@ -787,7 +750,7 @@ proc statusMsgForBuffer(buffer: Buffer) = if buffer.hovertext.len > 0: msg &= " " & buffer.hovertext var formatting: Formatting - formatting.reverse_on + formatting.reverse = true buffer.writeStatusMessage(msg, formatting) proc setStatusMessage*(buffer: Buffer, str: string) = diff --git a/src/io/cell.nim b/src/io/cell.nim index a4f56d61..f477bafd 100644 --- a/src/io/cell.nim +++ b/src/io/cell.nim @@ -9,12 +9,12 @@ import html/dom type FormatFlags* = enum - FLAG_ITALIC FLAG_BOLD + FLAG_ITALIC FLAG_UNDERLINE + FLAG_REVERSE FLAG_STRIKE FLAG_OVERLINE - FLAG_REVERSE Formatting* = object fgcolor*: CellColor @@ -36,34 +36,35 @@ type FixedCell* = object of Cell runes*: seq[Rune] - ow*: int FixedGrid* = seq[FixedCell] -func italic(formatting: Formatting): bool = FLAG_ITALIC in formatting.flags +const FormatCodes: array[FormatFlags, tuple[s: int, e: int]] = [ + FLAG_BOLD: (1, 22), + FLAG_ITALIC: (3, 23), + FLAG_UNDERLINE: (4, 24), + FLAG_REVERSE: (7, 27), + FLAG_STRIKE: (9, 29), + FLAG_OVERLINE: (53, 55), +] + func bold(formatting: Formatting): bool = FLAG_BOLD in formatting.flags +func italic(formatting: Formatting): bool = FLAG_ITALIC in formatting.flags func underline(formatting: Formatting): bool = FLAG_UNDERLINE in formatting.flags func reverse(formatting: Formatting): bool = FLAG_REVERSE in formatting.flags func strike(formatting: Formatting): bool = FLAG_STRIKE in formatting.flags func overline(formatting: Formatting): bool = FLAG_OVERLINE in formatting.flags -proc italic_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_ITALIC) -proc italic_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_ITALIC) - -proc bold_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_BOLD) -proc bold_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_BOLD) - -proc underline_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_UNDERLINE) -proc underline_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_UNDERLINE) +template flag_template(formatting: Formatting, val: bool, flag: FormatFlags) = + if val: formatting.flags.incl(flag) + else: formatting.flags.excl(flag) -proc reverse_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_REVERSE) -proc reverse_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_REVERSE) - -proc strike_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_STRIKE) -proc strike_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_STRIKE) - -proc overline_on*(formatting: var Formatting) = formatting.flags.incl(FLAG_OVERLINE) -proc overline_off*(formatting: var Formatting) = formatting.flags.excl(FLAG_OVERLINE) +template `italic=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_ITALIC +template `bold=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_BOLD +template `underline=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_UNDERLINE +template `reverse=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_REVERSE +template `strike=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_STRIKE +template `overline=`*(f: var Formatting, b: bool) = flag_template f, b, FLAG_OVERLINE #TODO ????? func `==`*(a: FixedCell, b: FixedCell): bool = @@ -178,7 +179,7 @@ proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int = #intermediate bytes while 0x20 <= int(buf[i]) and int(buf[i]) <= 0x2F: inc_check i - let interm = buf.substr(si, i) + #let interm = buf.substr(si, i) let final = buf[i] #final byte @@ -198,26 +199,16 @@ proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int = case ip[pi] of 0: formatting = newFormatting() - of 1: - formatting.bold_on - of 3: - formatting.italic_on - of 4: - formatting.underline_on - of 7: - formatting.reverse_on - of 9: - formatting.strike_on - of 22: - formatting.bold_off - of 23: - formatting.italic_off - of 27: - formatting.reverse_off - of 29: - formatting.strike_off - of 30..37: - formatting.fgcolor = CellColor(rgb: false, color: uint8(ip[pi])) + of 1: formatting.bold = true + of 3: formatting.italic = true + of 4: formatting.underline = true + of 7: formatting.reverse = true + of 9: formatting.strike = true + of 22: formatting.bold = false + of 23: formatting.italic = false + of 27: formatting.reverse = false + of 29: formatting.strike = false + of 30..37: formatting.fgcolor = CellColor(rgb: false, color: uint8(ip[pi])) of 38: inc pi if pi < ip.len: @@ -258,12 +249,9 @@ proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int = continue else: break - of 49: - formatting.bgcolor = defaultColor - of 53: - formatting.overline_on - of 55: - formatting.overline_off + of 49: formatting.bgcolor = defaultColor + of 53: formatting.overline = true + of 55: formatting.overline = false else: discard inc pi except ValueError: discard @@ -272,52 +260,34 @@ proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int = return i proc processFormatting*(formatting: var Formatting, cellf: Formatting): string = - if formatting.bold and not cellf.bold: - result &= SGR(22) - if formatting.italic and not cellf.italic: - result &= SGR(23) - if formatting.underline and not cellf.underline: - result &= SGR(24) - if formatting.reverse and not cellf.reverse: - result &= SGR(27) - if formatting.strike and not cellf.strike: - result &= SGR(29) - if formatting.overline and not cellf.overline: - result &= SGR(55) - - if cellf.fgcolor != formatting.fgcolor: - var color = cellf.fgcolor - if color.rgb: - let rgb = color.rgbcolor - result &= SGR(38, 2, rgb.r, rgb.g, rgb.b) - elif color == defaultColor: - result &= SGR() - formatting = newFormatting() - else: - result &= SGR(color.color) - - if cellf.bgcolor != formatting.bgcolor: - var color = cellf.bgcolor - if color.rgb: - let rgb = color.rgbcolor - result &= SGR(48, 2, rgb.r, rgb.g, rgb.b) - elif color == defaultColor: - result &= SGR() - formatting = newFormatting() - else: - result &= SGR(color.color) - - if not formatting.bold and cellf.bold: - result &= SGR(1) - if not formatting.italic and cellf.italic: - result &= SGR(3) - if not formatting.underline and cellf.underline: - result &= SGR(4) - if not formatting.reverse and cellf.reverse: - result &= SGR(7) - if not formatting.strike and cellf.strike: - result &= SGR(9) - if not formatting.overline and cellf.overline: - result &= SGR(53) - - formatting = cellf + for flag in FormatFlags: + if flag in formatting.flags and flag notin cellf.flags: + result &= SGR(FormatCodes[flag].e) + + if cellf.fgcolor != formatting.fgcolor: + var color = cellf.fgcolor + if color.rgb: + let rgb = color.rgbcolor + result &= SGR(38, 2, rgb.r, rgb.g, rgb.b) + elif color == defaultColor: + result &= SGR() + formatting = newFormatting() + else: + result &= SGR(color.color) + + if cellf.bgcolor != formatting.bgcolor: + var color = cellf.bgcolor + if color.rgb: + let rgb = color.rgbcolor + result &= SGR(48, 2, rgb.r, rgb.g, rgb.b) + elif color == defaultColor: + result &= SGR() + formatting = newFormatting() + else: + result &= SGR(color.color) + + for flag in FormatFlags: + if flag notin formatting.flags and flag in cellf.flags: + result &= SGR(FormatCodes[flag].s) + + formatting = cellf diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim index c3ad6cfe..21ae0945 100644 --- a/src/render/renderdocument.nim +++ b/src/render/renderdocument.nim @@ -14,15 +14,15 @@ import utils/twtstr func formatFromLine(line: CSSRowBox): Formatting = result.fgcolor = line.color.cellColor() if line.fontstyle in { FONT_STYLE_ITALIC, FONT_STYLE_OBLIQUE }: - result.italic_on + result.italic = true if line.fontweight > 500: - result.bold_on + result.bold = true if line.textdecoration == TEXT_DECORATION_UNDERLINE: - result.underline_on + result.underline = true if line.textdecoration == TEXT_DECORATION_OVERLINE: - result.overline_on + result.overline = true if line.textdecoration == TEXT_DECORATION_LINE_THROUGH: - result.strike_on + result.strike = true proc setRowBox(lines: var FlexibleGrid, line: CSSRowBox) = var r: Rune -- cgit 1.4.1-2-gfad0