about summary refs log tree commit diff stats
path: root/src/io/buffer.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-07-23 16:54:02 +0200
committerbptato <nincsnevem662@gmail.com>2022-07-23 17:39:06 +0200
commite7b53775a3f52cb5d8da865213c5dc38b954e33c (patch)
tree200741f8ed965f407c1a6af8823705db133bf3d4 /src/io/buffer.nim
parentc7f25b2fe470849e028f9502d3da0851f149f065 (diff)
downloadchawan-e7b53775a3f52cb5d8da865213c5dc38b954e33c.tar.gz
Improved incremental search: support unicode
Diffstat (limited to 'src/io/buffer.nim')
-rw-r--r--src/io/buffer.nim79
1 files changed, 37 insertions, 42 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 065a85a8..b3022615 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -38,12 +38,6 @@ type
     y*: int
     str*: string
 
-  Mark* = object
-    x: int
-    y: int
-    format: Format
-    grid: FixedGrid
-
   Buffer* = ref object
     contenttype*: string
     title*: string
@@ -73,7 +67,6 @@ type
     next*: Buffer
     userstyle*: CSSStylesheet
     loader*: FileLoader
-    marks*: seq[Mark]
 
 proc newBuffer*(): Buffer =
   new(result)
@@ -195,14 +188,6 @@ func generateStatusMessage*(buffer: Buffer): string =
   if w < buffer.width:
     result &= EL()
 
-func generateMark*(buffer: Buffer, mark: Mark): string =
-  var format = newFormat()
-  var w = 0
-  for cell in mark.grid:
-    result &= format.processFormat(cell.format)
-    result &= $cell.runes
-    w += cell.width()
-
 func numLines(buffer: Buffer): int = buffer.lines.len
 
 func lastVisibleLine(buffer: Buffer): int = min(buffer.fromy + buffer.height, buffer.numLines)
@@ -332,27 +317,36 @@ proc refreshDisplay(buffer: Buffer) =
 
   for line in buffer.lines[buffer.fromy..
                            buffer.lastVisibleLine - 1]:
-    var w = 0
-    var i = 0
+    var w = 0 # width of the row so far
+    var i = 0 # byte in line.str
+
+    # Skip cells till buffer.fromx.
     while w < buffer.fromx and i < line.str.len:
       fastRuneAt(line.str, i, r)
       w += r.width()
 
-    let dls = y * buffer.width
+    let dls = y * buffer.width # starting position of row in display
+
+    # Fill in the gap in case we skipped more cells than fromx mandates (i.e.
+    # we encountered a double-width character.)
     var k = 0
-    var cf = line.findFormat(w)
-    var nf = line.findNextFormat(w)
     if w > buffer.fromx:
       while k < w - buffer.fromx:
         buffer.display[dls + k].runes.add(Rune(' '))
         inc k
 
+    var cf = line.findFormat(w)
+    var nf = line.findNextFormat(w)
+
+    let startw = w # save this for later
+
+    # Now fill in the visible part of the row.
     while i < line.str.len:
       let pw = w
       fastRuneAt(line.str, i, r)
       w += r.width()
       if w > buffer.fromx + buffer.width:
-        break
+        break # die on exceeding the width limit
       if nf.pos != -1 and nf.pos <= pw:
         cf = nf
         nf = line.findNextFormat(pw)
@@ -364,6 +358,14 @@ proc refreshDisplay(buffer: Buffer) =
       while k < tk and k < buffer.width - 1:
         inc k
 
+    # Then, for each cell that has a mark, override its formatting with that
+    # specified by the mark.
+    let aw = buffer.width - (startw - buffer.fromx) # actual width
+    for mark in line.marks:
+      if mark.x >= startw + aw or mark.x + mark.width < startw: continue
+      for i in max(mark.x, startw)..<min(mark.x + mark.width, startw + aw):
+        buffer.display[dls + i].format = mark.format
+
     inc y
 
 proc setCursorX(buffer: Buffer, x: int, refresh = true, save = true) =
@@ -698,19 +700,17 @@ proc gotoAnchor*(buffer: Buffer) =
         return
       inc i
 
-proc addMark*(buffer: Buffer, x, y: int, str: string) =
+proc addMark*(buffer: Buffer, x, y, width: int): Mark =
   assert y < buffer.lines.len
   var format = newFormat()
   format.reverse = true
-  #TODO get rid of the string part; marks should only consist of a position and
-  # a length.
-  var grid = newFixedGrid(str.width())
-  var i = 0
-  for r in str.runes:
-    grid[i].runes.add(r)
-    grid[i].format = format
-    i += r.width()
-  buffer.marks.add(Mark(x: x, y: y, format: format, grid: grid))
+  result = Mark(x: x, width: width, format: format)
+  buffer.lines[y].marks.add(result)
+
+proc removeMark*(buffer: Buffer, y: int, mark: Mark) =
+  let i = buffer.lines[y].marks.find(mark)
+  if i != -1:
+    buffer.lines[y].marks.delete(i)
 
 proc cursorNextMatch(buffer: Buffer, regex: Regex, sy, ey: int, wrap = false): BufferMatch =
   for y in sy..ey:
@@ -721,10 +721,11 @@ proc cursorNextMatch(buffer: Buffer, regex: Regex, sy, ey: int, wrap = false): B
     let res = regex.exec(buffer.lines[y].str, s)
     if res.success and res.captures.len > 0:
       let cap = res.captures[0]
-      buffer.setCursorXY(cap.s, y)
+      let x = buffer.lines[y].str.width(cap.s)
+      buffer.setCursorXY(x, y)
       result.success = true
       result.y = y
-      result.x = buffer.cursorBytes(y, cap.s)
+      result.x = x
       result.str = buffer.lines[y].str.substr(cap.s, cap.e - 1)
       return
 
@@ -752,10 +753,11 @@ proc cursorPrevMatch*(buffer: Buffer, regex: Regex, sy, ey: int, wrap = false):
       for i in countdown(res.captures.high, 0):
         let cap = res.captures[i]
         if cap.s < e:
-          buffer.setCursorXY(cap.s, y)
+          let x = buffer.lines[y].str.width(cap.s)
+          buffer.setCursorXY(x, y)
           result.success = true
           result.y = y
-          result.x = buffer.cursorBytes(y, cap.s)
+          result.x = x
           result.str = buffer.lines[y].str.substr(cap.s, cap.e - 1)
           return
 
@@ -1214,13 +1216,6 @@ proc refreshBuffer*(buffer: Buffer, peek = false) =
     buffer.refreshDisplay()
     buffer.displayBufferSwapOutput()
 
-  for mark in buffer.marks:
-    if mark.y in buffer.fromy..(buffer.fromy + buffer.height) and mark.x in buffer.fromx..(buffer.fromx + buffer.width):
-      print(HVP(mark.y - buffer.fromy + 1, mark.x - buffer.fromx + 1))
-      print(SGR())
-      print(buffer.generateMark(mark))
-      print(SGR())
-
   if not peek:
     if not buffer.nostatus:
       buffer.statusMsgForBuffer()