about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-08-02 21:36:57 +0200
committerbptato <nincsnevem662@gmail.com>2022-08-02 22:14:14 +0200
commit7de898bd05fd54f216009ebc3775a007fd92c417 (patch)
tree8ea375792a27977a79b5932bf6bc786b19b3ec5f
parent8f7eb1f7d0ec0cac610c5dcc73188fdbe1577def (diff)
downloadchawan-7de898bd05fd54f216009ebc3775a007fd92c417.tar.gz
Fix renderdocument setText etc.
-rw-r--r--src/io/buffer.nim7
-rw-r--r--src/io/cell.nim47
-rw-r--r--src/render/renderdocument.nim198
-rw-r--r--src/types/color.nim6
4 files changed, 138 insertions, 120 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 4f434e5d..15853da7 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -1215,15 +1215,14 @@ proc drawBuffer*(buffer: Buffer) =
   var format = newFormat()
   for line in buffer.lines:
     if line.formats.len == 0:
-      print(line.str & "\r\n")
+      print(line.str & "\n")
     else:
       var x = 0
       var i = 0
       for f in line.formats:
         var outstr = ""
-        #TODO TODO TODO renderhtml has broken format outputting
         #assert f.pos < line.str.width(), "fpos " & $f.pos & "\nstr" & line.str & "\n"
-        while x < f.pos and i < line.str.len: #TODO x < f.pos should be enough
+        while x < f.pos:
           var r: Rune
           fastRuneAt(line.str, i, r)
           outstr &= r
@@ -1232,7 +1231,7 @@ proc drawBuffer*(buffer: Buffer) =
         print(format.processFormat(f.format))
       print(line.str.substr(i))
       print(format.processFormat(newFormat()))
-      print("\r\n")
+      print("\n")
 
 proc refreshBuffer*(buffer: Buffer, peek = false) =
   buffer.title = buffer.getTitle()
diff --git a/src/io/cell.nim b/src/io/cell.nim
index d8f3d23b..2faced78 100644
--- a/src/io/cell.nim
+++ b/src/io/cell.nim
@@ -28,6 +28,8 @@ type
     format*: Format
     node*: StyledNode
 
+  # A FormatCell *starts* a new terminal formatting context.
+  # If no FormatCell exists before a given cell, the default formatting is used.
   FormatCell* = object of Cell
     pos*: int
     computed*: ComputedFormat
@@ -71,7 +73,6 @@ template `strike=`*(f: var Format, b: bool) = flag_template f, b, FLAG_STRIKE
 template `overline=`*(f: var Format, b: bool) = flag_template f, b, FLAG_OVERLINE
 template `blink=`*(f: var Format, b: bool) = flag_template f, b, FLAG_BLINK
 
-#TODO ?????
 func `==`*(a: FixedCell, b: FixedCell): bool =
   return a.format == b.format and
     a.runes == b.runes and
@@ -89,6 +90,7 @@ func width*(cell: FixedCell): int =
 func newFormat*(): Format =
   return Format(fgcolor: defaultColor, bgcolor: defaultColor)
 
+# Get the first format cell after pos, if any.
 func findFormatN*(line: FlexibleLine, pos: int): int =
   var i = 0
   while i < line.formats.len:
@@ -111,46 +113,21 @@ func findNextFormat*(line: FlexibleLine, pos: int): FormatCell =
   else:
     result.pos = -1
 
-func subformats*(formats: seq[FormatCell], pos: int): seq[FormatCell] =
-  var i = 0
-  while i < formats.len:
-    if formats[i].pos >= pos:
-      if result.len == 0 and i > 0:
-        var f = formats[i - 1]
-        f.pos = 0
-        result.add(f)
-      var f = formats[i]
-      f.pos -= pos
-      result.add(f)
-    inc i
-
-  if result.len == 0 and i > 0:
-    var f = formats[i - 1]
-    f.pos = 0
-    result.add(f)
-
-proc setLen*(line: var FlexibleLine, len: int) =
-  for i in 0 ..< line.formats.len:
-    if line.formats[i].pos >= len:
-      line.formats.setLen(i)
-      break
-  line.str.setLen(len)
-  #line.formats = line.formats.filter((x) => x.pos < len)
-
-proc add*(a: var FlexibleLine, b: FlexibleLine) =
-  let l = a.str.len
-  a.formats.add(b.formats.map((x) => FormatCell(format: x.format, node: x.node, pos: l + x.pos)))
-  a.str &= b.str
-
 proc addLine*(grid: var FlexibleGrid) =
   grid.add(FlexibleLine())
 
+proc insertFormat*(line: var FlexibleLine, pos, i: int, format: Format, computed: ComputedFormat = nil) =
+  if computed == nil:
+    line.formats.insert(FormatCell(format: format, pos: pos), i)
+  else:
+    line.formats.insert(FormatCell(format: format, computed: computed, node: computed.node, pos: pos), i)
+
 proc addFormat*(line: var FlexibleLine, pos: int, format: Format) =
-  line.formats.add(FormatCell(format: format, pos: line.str.len))
+  line.formats.add(FormatCell(format: format, pos: pos))
 
 proc addFormat*(line: var FlexibleLine, pos: int, format: Format, computed: ComputedFormat) =
-  if computed != nil and line.formats.len > 0 and line.formats[^1].computed == computed and line.formats[^1].format.bgcolor != format.bgcolor:
-    return
+  #if computed != nil and line.formats.len > 0 and line.formats[^1].computed == computed and line.formats[^1].format.bgcolor != format.bgcolor:
+  #  return
   if computed == nil:
     line.formats.add(FormatCell(format: format, pos: pos))
   else:
diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim
index 54628946..484f8fb5 100644
--- a/src/render/renderdocument.nim
+++ b/src/render/renderdocument.nim
@@ -30,97 +30,128 @@ func formatFromWord(computed: ComputedFormat): Format =
     result.blink = true
   else: discard
 
-proc setFormats(line: var FlexibleLine, ox, newwidth, oldwidth: int,
-                newformat: Format, oformats: seq[FormatCell], computed: ComputedFormat = nil) {.inline.} =
-  let obg = newformat.bgcolor
-  var newformat = newformat
-  for format in oformats:
-    #assert format.pos < ox + oldwidth #TODO TODO TODO 
-    if format.format.bgcolor != newformat.bgcolor:
-      newformat.bgcolor = format.format.bgcolor
-
-      if format.pos < ox:
-        line.addFormat(ox, newformat, computed)
-      else:
-        line.addFormat(format.pos, newformat, computed)
-
-  if ox + oldwidth < ox + newwidth:
-    newformat.bgcolor = obg
-
-    #TODO this is probably a workaround for a bug...
-    if line.formats.len > 0 and line.formats[^1].pos == ox + oldwidth:
-      line.formats[^1].format.bgcolor = obg
-    else:
-      line.addFormat(ox + oldwidth, newformat, computed)
-
-proc setText(lines: var FlexibleGrid, linestr: string, format: ComputedFormat, x, y: int) {.inline.} =
-  var r: Rune
-  var x = x
-  var y = y
+proc setText(lines: var FlexibleGrid, linestr: string, cformat: ComputedFormat, x, y: int) {.inline.} =
   var i = 0
-
+  var r: Rune
+  # make sure we have line y
   while lines.len <= y:
     lines.addLine()
 
-  var cx = 0
+  var cx = 0 # first x of new string (before padding)
   while cx < x and i < lines[y].str.len:
     fastRuneAt(lines[y].str, i, r)
     cx += r.width()
 
   let ostr = lines[y].str.substr(i)
-  let oformats = lines[y].formats.subformats(i)
-  lines[y].setLen(i)
-
-  var nx = cx
-  let oldstrwidth = ostr.width()
-  if nx < x:
-    let spacelength = x - nx
-    var spaceformat = newFormat()
-    let str = ' '.repeat(spacelength)
-    lines[y].setFormats(nx, spacelength, oldstrwidth, spaceformat, oformats)
-
-    lines[y].str &= str
-    i += spacelength
-    nx = x
-
-  var wordformat = format.formatFromWord()
-  let newstrwidth = linestr.width()
-  lines[y].setFormats(nx, newstrwidth, oldstrwidth, wordformat, oformats, format)
-  nx += newstrwidth
+  lines[y].str.setLen(i)
+  var linestrwidth = 0
+  let padwidth = x - cx
+  if padwidth > 0:
+    lines[y].str &= ' '.repeat(padwidth)
+    linestrwidth += padwidth
 
   lines[y].str &= linestr
+  linestrwidth += linestr.width()
 
   i = 0
-  while cx < nx and i < ostr.len:
+  var nx = x # last x of new string
+  while nx < x + linestrwidth and i < ostr.len:
     fastRuneAt(ostr, i, r)
-    cx += r.width()
+    nx += r.width()
 
   if i < ostr.len:
-    let oline = FlexibleLine(str: ostr.substr(i), formats: oformats.subformats(i))
-    lines[y].add(oline)
+    lines[y].str &= ostr.substr(i)
+
+  # Skip unchanged formats before the new string
+  var fi = lines[y].findFormatN(cx) - 1
+
+  if padwidth > 0:
+    # Replace formats for padding
+    var padformat = newFormat()
+    if fi == -1:
+      # No formats
+      inc fi # insert after first format (meaning fi = 0)
+      lines[y].insertFormat(cx, fi, padformat)
+    else:
+      # First format's pos may be == cx here.
+      if lines[y].formats[fi].pos == cx:
+        padformat.bgcolor = lines[y].formats[fi].format.bgcolor
+        lines[y].formats.delete(fi)
+        lines[y].insertFormat(cx, fi, padformat)
+      else:
+        # First format < cx => split it up
+        assert lines[y].formats[fi].pos < cx
+        padformat.bgcolor = lines[y].formats[fi].format.bgcolor
+        inc fi # insert after first format
+        lines[y].insertFormat(cx, fi, padformat)
+    inc fi # skip last format
+    while fi < lines[y].formats.len and lines[y].formats[fi].pos < x:
+      # Other formats must be > cx => replace them
+      padformat.bgcolor = lines[y].formats[fi].format.bgcolor
+      let px = lines[y].formats[fi].pos
+      lines[y].formats.delete(fi)
+      lines[y].insertFormat(px, fi, padformat)
+      inc fi
+    dec fi # go back to previous format, so that pos <= x
+    assert lines[y].formats[fi].pos <= x
+
+  # Now for the text's formats:
+  var format = cformat.formatFromWord()
+  if fi == -1:
+    # No formats => just insert a new format at 0
+    inc fi
+    lines[y].insertFormat(x, fi, format, cformat)
+  else:
+    # First format's pos may be == x here.
+    if lines[y].formats[fi].pos == x:
+      # Replace.
+      format.bgcolor = lines[y].formats[fi].format.bgcolor
+      lines[y].formats.delete(fi)
+      lines[y].insertFormat(x, fi, format, cformat)
+    else:
+      # First format's pos < x => split it up.
+      assert lines[y].formats[fi].pos < x
+      format.bgcolor = lines[y].formats[fi].format.bgcolor
+      inc fi # insert after first format
+      lines[y].insertFormat(x, fi, format, cformat)
+  inc fi # skip last format
+
+  while fi < lines[y].formats.len and lines[y].formats[fi].pos < nx:
+    # Other formats must be > x => replace them
+    format.bgcolor = lines[y].formats[fi].format.bgcolor
+    let px = lines[y].formats[fi].pos
+    lines[y].formats.delete(fi)
+    lines[y].insertFormat(px, fi, format, cformat)
+    inc fi
+
+  dec fi # go back to previous format, so that pos <= nx
+  assert lines[y].formats[fi].pos <= nx
+  # That's it!
 
 proc setRowWord(lines: var FlexibleGrid, word: InlineWord, x, y: int, term: TermAttributes) =
   var r: Rune
 
-  var y = (y + word.offset.y) div term.ppl
-  if y < 0: y = 0
+  var y = (y + word.offset.y) div term.ppl # y cell
+  if y < 0: return # y is outside the canvas, no need to draw
 
-  var x = (x + word.offset.x) div term.ppc
+  var x = (x + word.offset.x) div term.ppc # x cell
   var i = 0
   while x < 0 and i < word.str.len:
     fastRuneAt(word.str, i, r)
     x += r.width()
+  if x < 0: return # highest x is outside the canvas, no need to draw
   let linestr = word.str.substr(i)
 
   lines.setText(linestr, word.format, x, y)
 
 proc setSpacing(lines: var FlexibleGrid, spacing: InlineSpacing, x, y: int, term: TermAttributes) =
-  var y = (y + spacing.offset.y) div term.ppl
-  if y < 0: y = 0
+  var y = (y + spacing.offset.y) div term.ppl # y cell
+  if y < 0: return # y is outside the canvas, no need to draw
 
-  var x = (x + spacing.offset.x) div term.ppc
-  let width = spacing.width div term.ppc
+  var x = (x + spacing.offset.x) div term.ppc # x cell
+  let width = spacing.width div term.ppc # cell width
 
+  if x + width < 0: return # highest x is outside the canvas, no need to draw
   var i = 0
   if x < 0:
     i -= x
@@ -133,32 +164,32 @@ proc paintBackground(lines: var FlexibleGrid, color: CSSColor, startx, starty, e
   let color = color.cellColor()
 
   var starty = starty div term.ppl
-  if starty < 0: starty = 0
-
   var endy = endy div term.ppl
-  if endy < 0: endy = 0
 
   if starty > endy:
-    let swap = endy
-    endy = starty
-    starty = swap
+    swap(starty, endy)
 
-  var startx = startx div term.ppc
+  if endy <= 0: return # highest y is outside canvas, no need to paint
   if starty < 0: starty = 0
+  if starty == endy: return # height is 0, no need to paint
+
+  var startx = startx div term.ppc
 
   var endx = endx div term.ppc
   if endy < 0: endy = 0
 
   if startx > endx:
-    let swap = endx
-    endx = startx
-    startx = swap
+    swap(startx, endx)
 
+  if endx <= 0: return # highest x is outside the canvas, no need to paint
+  if startx < 0: startx = 0
+  if startx == endx: return # width is 0, no need to paint
+
+  # make sure we have line y
   while lines.len <= endy:
     lines.addLine()
 
-  var y = starty
-  while y < endy:
+  for y in starty..<endy:
     # Make sure line.width() >= endx
     let linewidth = lines[y].width()
     if linewidth < endx:
@@ -171,7 +202,10 @@ proc paintBackground(lines: var FlexibleGrid, color: CSSColor, startx, starty, e
       lines[y].addFormat(startx, newFormat())
     else:
       let fi = lines[y].findFormatN(startx) - 1
-      if lines[y].formats[fi].pos == startx:
+      if fi == -1:
+        # No format <= startx
+        lines[y].insertFormat(startx, 0, newFormat())
+      elif lines[y].formats[fi].pos == startx:
         # Last format equals startx => next comes after, nothing to be done
         discard
       else:
@@ -183,24 +217,26 @@ proc paintBackground(lines: var FlexibleGrid, color: CSSColor, startx, starty, e
     # Process formatting around endx
     assert lines[y].formats.len > 0
     let fi = lines[y].findFormatN(endx) - 1
-    if fi == lines[y].formats.len - 1:
-      # Last format => nothing to be done
+    if fi == -1:
+      # Last format > endx -> nothing to be done
       discard
     else:
-      # Format ends before endx => separate format from endx
-      let copy = lines[y].formats[fi]
-      lines[y].formats[fi].pos = endx
-      lines[y].formats.insert(copy, fi + 1)
+      if lines[y].formats[fi].pos != endx:
+        let copy = lines[y].formats[fi]
+        if linewidth != endx:
+          lines[y].formats[fi].pos = endx
+          lines[y].formats.insert(copy, fi)
+        else:
+          lines[y].formats.delete(fi)
+          lines[y].formats.insert(copy, fi)
 
     # Paint format backgrounds between startx and endx
     for fi in 0..lines[y].formats.high:
-      if lines[y].formats[fi].pos > endx:
+      if lines[y].formats[fi].pos >= endx:
         break
       if lines[y].formats[fi].pos >= startx:
         lines[y].formats[fi].format.bgcolor = color
 
-    inc y
-
 proc renderBlockContext(grid: var FlexibleGrid, ctx: BlockBox, x, y: int, term: TermAttributes)
 
 proc renderInlineContext(grid: var FlexibleGrid, ctx: InlineContext, x, y: int, term: TermAttributes) =
diff --git a/src/types/color.nim b/src/types/color.nim
index e5f72158..d0e13544 100644
--- a/src/types/color.nim
+++ b/src/types/color.nim
@@ -211,3 +211,9 @@ converter toRGBColor*(i: RGBAColor): RGBColor =
 
 converter toRGBAColor*(i: RGBColor): RGBAColor =
   return RGBAColor(uint32(i) or 0xFF000000u32)
+
+func `$`*(color: CellColor): string =
+  if color.rgb:
+    "r" & $color.rgbcolor.r & "g" & $color.rgbcolor.g & "b" & $color.rgbcolor.b
+  else:
+    "tcolor" & $color.color