diff options
author | bptato <nincsnevem662@gmail.com> | 2021-11-13 11:17:40 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-11-13 11:17:40 +0100 |
commit | a6099512e42cc95cf6276ab784afd70febdd43b7 (patch) | |
tree | def2061199d9f119a3176858e331ba376c89e2c5 /src/io | |
parent | c141a3cea17f32f526853cd4085ae4cc80c4fed6 (diff) | |
download | chawan-a6099512e42cc95cf6276ab784afd70febdd43b7.tar.gz |
Parse ansi escape codes when displaying plain text
Diffstat (limited to 'src/io')
-rw-r--r-- | src/io/buffer.nim | 27 | ||||
-rw-r--r-- | src/io/cell.nim | 182 |
2 files changed, 146 insertions, 63 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim index a0d7839f..d1a55222 100644 --- a/src/io/buffer.nim +++ b/src/io/buffer.nim @@ -581,22 +581,31 @@ proc refreshDisplay*(buffer: Buffer) = proc renderPlainText*(buffer: Buffer, text: string) = buffer.clearText() var i = 0 + var x = 0 var y = 0 - var line = "" + var r: Rune + var format = newFormatting() while i < text.len: if text[i] == '\n': - buffer.setText(0, y, line.toRunes()) + buffer.addLine() inc y - line = "" + x = 0 + inc i elif text[i] == '\r': - discard + inc i elif text[i] == '\t': - line &= ' '.repeat(8) + for i in 0..8: + buffer.lines[^1].add(FlexibleCell(rune: Rune(' '), formatting: format)) + inc i + elif text[i] == '\e': + i = format.parseAnsiCode(text, i) + elif text[i].isControlChar(): + buffer.lines[^1].add(FlexibleCell(rune: Rune('^'), formatting: format)) + buffer.lines[^1].add(FlexibleCell(rune: Rune(text[i].getControlLetter()), formatting: format)) + inc i else: - line &= text[i] - inc i - if line.len > 0: - buffer.setText(0, y, line.toRunes()) + fastRuneAt(text, i, r) + buffer.lines[^1].add(FlexibleCell(rune: r, formatting: format)) buffer.updateCursor() proc renderDocument*(buffer: Buffer) = diff --git a/src/io/cell.nim b/src/io/cell.nim index 133b88d6..67b7361c 100644 --- a/src/io/cell.nim +++ b/src/io/cell.nim @@ -1,4 +1,7 @@ import unicode +import strutils +import sequtils +import sugar import types/color import utils/twtstr @@ -28,6 +31,16 @@ type FixedGrid* = seq[FixedCell] +func newFixedGrid*(w: int, h: int = 1): FixedGrid = + return newSeq[FixedCell](w * h) + +func width*(line: FlexibleLine): int = + for c in line: + result += c.rune.width() + +func newFormatting*(): Formatting = + return Formatting(fgcolor: defaultColor, bgcolor: defaultColor) + proc setText*(grid: var FlexibleGrid, x: int, y: int, text: seq[Rune]) = while grid.len <= y: grid.add(newSeq[FlexibleCell]()) @@ -40,15 +53,121 @@ proc setText*(grid: var FlexibleGrid, x: int, y: int, text: seq[Rune]) = grid[y][i].rune = text[i] inc i -func newFixedGrid*(w: int, h: int = 1): FixedGrid = - return newSeq[FixedCell](w * h) - -func width*(line: FlexibleLine): int = - for c in line: - result += c.rune.width() - -func newFormatting*(): Formatting = - return Formatting(fgcolor: defaultColor, bgcolor: defaultColor) +template inc_check(i: int) = + inc i + if i >= buf.len: + return i + +proc parseAnsiCode*(formatting: var Formatting, buf: string, fi: int): int = + var i = fi + if buf[i] != '\e': + return i + + inc_check i + if 0x40 <= int(buf[i]) and int(buf[i]) <= 0x5F: + if buf[i] != '[': + #C1, TODO? + return + inc_check i + + let sp = i + #parameter bytes + while 0x30 <= int(buf[i]) and int(buf[i]) <= 0x3F: + inc_check i + let params = buf.substr(sp, i - 1) + + let si = i + #intermediate bytes + while 0x20 <= int(buf[i]) and int(buf[i]) <= 0x2F: + inc_check i + let interm = buf.substr(si, i) + + let final = buf[i] + #final byte + if 0x40 <= int(buf[i]) and int(buf[i]) <= 0x7E: + inc_check i + + case final + of 'm': + if params.len == 0: + formatting = newFormatting() + else: + let sparams = params.split(';') + try: + let ip = sparams.map((x) => parseInt(x)) + var pi = 0 + while pi < ip.len: + case ip[pi] + of 0: + formatting = newFormatting() + of 1: + formatting.bold = true + of 3: + formatting.italic = true + of 4: + formatting.underline = true + of 9: + formatting.strike = true + of 22: + formatting.bold = false + of 23: + formatting.italic = 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: + if ip[pi] == 2: + inc pi + if pi + 2 < ip.len: + let r = uint8(ip[pi]) + inc pi + let g = uint8(ip[pi]) + inc pi + let b = uint8(ip[pi]) + formatting.fgcolor = CellColor(rgb: true, rgbcolor: (r: r, g: g, b: b)) + else: + #TODO + inc pi + continue + else: + break + of 39: + formatting.fgcolor = defaultColor + of 40..47: + formatting.bgcolor = CellColor(rgb: false, color: uint8(ip[0])) + of 48: + inc pi + if pi < ip.len: + if ip[pi] == 2: + inc pi + if pi + 2 < ip.len: + let r = uint8(ip[pi]) + inc pi + let g = uint8(ip[pi]) + inc pi + let b = uint8(ip[pi]) + formatting.bgcolor = CellColor(rgb: true, rgbcolor: (r: r, g: g, b: b)) + else: + #TODO + inc pi + continue + else: + break + of 49: + formatting.bgcolor = defaultColor + of 53: + formatting.overline = true + of 55: + formatting.overline = false + else: discard + inc pi + except ValueError: discard + else: discard + + return i proc processFormatting*(formatting: var Formatting, cellf: Formatting): string = if formatting.bold and not cellf.bold: @@ -96,48 +215,3 @@ proc processFormatting*(formatting: var Formatting, cellf: Formatting): string = result &= SGR(53) formatting = cellf - 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.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.strike and cellf.strike: - result &= SGR(9) - if not formatting.overline and cellf.overline: - result &= SGR(53) - - formatting = cellf |