about summary refs log tree commit diff stats
path: root/src/buffer/cell.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer/cell.nim')
-rw-r--r--src/buffer/cell.nim284
1 files changed, 131 insertions, 153 deletions
diff --git a/src/buffer/cell.nim b/src/buffer/cell.nim
index a147a420..feaaf668 100644
--- a/src/buffer/cell.nim
+++ b/src/buffer/cell.nim
@@ -1,7 +1,5 @@
-import sequtils
-import streams
-import strutils
-import sugar
+import options
+import tables
 
 import css/stylednode
 import types/color
@@ -64,16 +62,23 @@ iterator items*(grid: FixedGrid): FixedCell {.inline.} =
 proc len*(grid: FixedGrid): int = grid.cells.len
 proc high*(grid: FixedGrid): int = grid.cells.high
 
-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),
-  FLAG_BLINK: (5, 25),
+const FormatCodes*: array[FormatFlags, tuple[s, e: uint8]] = [
+  FLAG_BOLD: (1u8, 22u8),
+  FLAG_ITALIC: (3u8, 23u8),
+  FLAG_UNDERLINE: (4u8, 24u8),
+  FLAG_REVERSE: (7u8, 27u8),
+  FLAG_STRIKE: (9u8, 29u8),
+  FLAG_OVERLINE: (53u8, 55u8),
+  FLAG_BLINK: (5u8, 25u8),
 ]
 
+const FormatCodeMap = block:
+  var res: Table[uint8, tuple[flag: FormatFlags, reverse: bool]]
+  for x in FormatFlags:
+    res[FormatCodes[x][0]] = (x, false)
+    res[FormatCodes[x][1]] = (x, true)
+  res
+
 template flag_template(format: Format, val: bool, flag: FormatFlags) =
   if val: format.flags.incl(flag)
   else: format.flags.excl(flag)
@@ -148,116 +153,7 @@ proc insertFormat*(line: var FlexibleLine, pos, i: int, format: Format, node: St
 proc addFormat*(line: var FlexibleLine, pos: int, format: Format, node: StyledNode = nil) =
   line.formats.add(FormatCell(format: format, node: node, pos: pos))
 
-template inc_check(i: int) =
-  inc i
-  if i >= buf.len:
-    return i
-
-proc handleAnsiCode(format: var Format, final: char, params: string) =
-  case final
-  of 'm':
-    if params.len == 0:
-      format = newFormat()
-    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:
-            format = newFormat()
-          of 1: format.bold = true
-          of 3: format.italic = true
-          of 4: format.underline = true
-          of 5: format.blink = true
-          of 7: format.reverse = true
-          of 9: format.strike = true
-          of 22: format.bold = false
-          of 23: format.italic = false
-          of 25: format.blink = false
-          of 27: format.reverse = false
-          of 29: format.strike = false
-          of 30..37: format.fgcolor = uint8(ip[pi]).cellColor()
-          of 38:
-            inc pi
-            if pi < ip.len:
-              if ip[pi] == 2:
-                inc pi
-                if pi + 2 < ip.len:
-                  let r = ip[pi]
-                  inc pi
-                  let g = ip[pi]
-                  inc pi
-                  let b = ip[pi]
-                  format.fgcolor = rgb(r, g, b).cellColor()
-              else:
-                #TODO
-                inc pi
-                continue
-            else:
-              break
-          of 39:
-            format.fgcolor = defaultColor
-          of 40..47:
-            format.bgcolor = uint8(ip[0]).cellColor()
-          of 48:
-            inc pi
-            if pi < ip.len:
-              if ip[pi] == 2:
-                inc pi
-                if pi + 2 < ip.len:
-                  let r = ip[pi]
-                  inc pi
-                  let g = ip[pi]
-                  inc pi
-                  let b = ip[pi]
-                  format.bgcolor = rgb(r, g, b).cellColor()
-              else:
-                #TODO
-                inc pi
-                continue
-            else:
-              break
-          of 49: format.bgcolor = defaultColor
-          of 53: format.overline = true
-          of 55: format.overline = false
-          else: discard
-          inc pi
-      except ValueError: discard
-  else: discard
-
-proc parseAnsiCode*(format: var Format, 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:
-    format.handleAnsiCode(final, params)
-
-  return i
-
+# https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf
 type
   AnsiCodeParseState* = enum
     PARSE_START, PARSE_PARAMS, PARSE_INTERM, PARSE_FINAL, PARSE_DONE
@@ -266,11 +162,122 @@ type
     state*: AnsiCodeParseState
     params: string
 
+proc getParam(parser: AnsiCodeParser, i: var int, colon = false): string =
+  while i < parser.params.len and
+      not (parser.params[i] == ';' or colon and parser.params[i] == ':'):
+    result &= parser.params[i]
+    inc i
+  if i < parser.params.len:
+    inc i
+
+template getParamU8(parser: AnsiCodeParser, i: var int,
+    colon = false): uint8 =
+  if i < parser.params.len:
+    return false
+  let u = parseUInt8(parser.getParam(i))
+  if u.isNone:
+    return false
+  u.get
+
+proc parseSGRDefColor(parser: AnsiCodeParser, format: var Format,
+    i: var int, isfg: bool): bool =
+  let u = parser.getParamU8(i, colon = true)
+  if u == 2:
+    let param0 = parser.getParamU8(i, colon = true)
+    if i < parser.params.len:
+      let r = param0
+      let g = parser.getParamU8(i, colon = true)
+      let b = parser.getParamU8(i, colon = true)
+      if isfg:
+        format.fgcolor = cellColor(rgb(r, g, b))
+      else:
+        format.bgcolor = cellColor(rgb(r, g, b))
+    else:
+      let n = param0
+      if isfg:
+        format.fgcolor = cellColor(rgb(n, n, n))
+      else:
+        format.bgcolor = cellColor(rgb(n, n, n))
+  else:
+    if u in 0u8..7u8:
+      if isfg:
+        format.fgcolor = cellColor(u + 30)
+      else:
+        format.bgcolor = cellColor(u + 40)
+    elif u in 8u8..15u8:
+      format.bold = true
+      if isfg:
+        format.fgcolor = cellColor(u + 22)
+      else:
+        format.bgcolor = cellColor(u + 22)
+    elif u in 16u8..231u8:
+      #16 + 36 × r + 6 × g + b
+      discard
+    else:
+      discard
+
+proc parseSGRColor(parser: AnsiCodeParser, format: var Format,
+    i: var int, u: uint8): bool =
+  if u in 30u8..37u8:
+    format.fgcolor = u.cellColor()
+  elif u == 38:
+    return parser.parseSGRDefColor(format, i, isfg = true)
+  elif u == 39:
+    format.fgcolor = defaultColor
+  elif u in 40u8..47u8:
+    format.bgcolor = u.cellColor()
+  elif u == 48:
+    return parser.parseSGRDefColor(format, i, isfg = false)
+  elif u == 49:
+    format.bgcolor = defaultColor
+  elif u in 90u8..97u8:
+    format.fgcolor = cellColor(u - 60u8)
+    format.bold = true
+  elif u in 100u8..107u8:
+    format.bgcolor = cellColor(u - 60u8)
+    format.bold = true
+  else:
+    return false
+  return true
+
+proc parseSGRAspect(parser: AnsiCodeParser, format: var Format,
+    i: var int): bool =
+  let u = parser.getParamU8(i)
+  if u in FormatCodeMap:
+    let entry = FormatCodeMap[u]
+    if entry.reverse:
+      format.flags.excl(entry.flag)
+    else:
+      format.flags.incl(entry.flag)
+    return true
+  elif u == 0:
+    format = newFormat()
+  else:
+    return parser.parseSGRColor(format, i, u)
+
+proc parseSGR(parser: AnsiCodeParser, format: var Format) =
+  if parser.params.len == 0:
+    format = newFormat()
+  else:
+    var i = 0
+    while i < parser.params.len:
+      if not parser.parseSGRAspect(format, i):
+        break
+      inc i
+
+proc parseControlFunction(parser: var AnsiCodeParser, format: var Format,
+    f: char) =
+  case f
+  of 'm':
+    parser.parseSGR(format)
+  else: discard # unknown
+
 proc reset*(parser: var AnsiCodeParser) =
   parser.state = PARSE_START
   parser.params = ""
 
-proc parseAnsiCode*(parser: var AnsiCodeParser, format: var Format, c: char): bool =
+proc parseAnsiCode*(parser: var AnsiCodeParser, format: var Format,
+    c: char): bool =
   case parser.state
   of PARSE_START:
     if 0x40 <= int(c) and int(c) <= 0x5F:
@@ -297,36 +304,7 @@ proc parseAnsiCode*(parser: var AnsiCodeParser, format: var Format, c: char): bo
   of PARSE_FINAL:
     parser.state = PARSE_DONE
     if 0x40 <= int(c) and int(c) <= 0x7E:
-      format.handleAnsiCode(c, parser.params)
+      parser.parseControlFunction(format, c)
     else:
       return true
   of PARSE_DONE: discard
-
-proc parseAnsiCode*(format: var Format, stream: Stream) =
-  if stream.atEnd(): return
-  var c = stream.readChar()
-  if 0x40 <= int(c) and int(c) <= 0x5F:
-    if c != '[':
-      #C1, TODO?
-      return
-    if stream.atEnd(): return
-    c = stream.readChar()
-
-  var params = $c
-  #parameter bytes
-  while 0x30 <= int(c) and int(c) <= 0x3F:
-    params &= c
-    if stream.atEnd(): return
-    c = stream.readChar()
-
-  #intermediate bytes
-  #var interm = $c
-  while 0x20 <= int(c) and int(c) <= 0x2F:
-    #interm &= c
-    if stream.atEnd(): return
-    c = stream.readChar()
-
-  #final byte
-  if 0x40 <= int(c) and int(c) <= 0x7E:
-    let final = c
-    format.handleAnsiCode(final, params)
ref='#n584'>584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705