about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-11-19 17:30:53 +0100
committerbptato <nincsnevem662@gmail.com>2022-11-19 17:30:53 +0100
commit986522a915233dc068e00b47ca8b16e42cb50c7f (patch)
tree2c3d76a1a3f9829a6057d0c070f9d87ef1e0b1ce /src
parentd4e20ebe854083110a2ed732276848dc0dbd9eb3 (diff)
downloadchawan-986522a915233dc068e00b47ca8b16e42cb50c7f.tar.gz
Re-implement highlighting
Diffstat (limited to 'src')
-rw-r--r--src/buffer/buffer.nim4
-rw-r--r--src/buffer/container.nim54
-rw-r--r--src/config/config.nim24
-rw-r--r--src/display/client.nim3
-rw-r--r--src/display/pager.nim27
5 files changed, 82 insertions, 30 deletions
diff --git a/src/buffer/buffer.nim b/src/buffer/buffer.nim
index 6c2654dd..40527d94 100644
--- a/src/buffer/buffer.nim
+++ b/src/buffer/buffer.nim
@@ -770,7 +770,7 @@ proc runBuffer(buffer: Buffer, istream, ostream: Stream) =
         istream.sread(wrap)
         let match = buffer.findPrevMatch(regex, cx, cy, wrap)
         if match.success:
-          buffer.writeCommand(JUMP, match.x, match.y)
+          buffer.writeCommand(JUMP, match.x, match.y, match.x + match.str.width() - 1)
       of FIND_NEXT_MATCH:
         var cx, cy: int
         var regex: Regex
@@ -781,7 +781,7 @@ proc runBuffer(buffer: Buffer, istream, ostream: Stream) =
         istream.sread(wrap)
         let match = buffer.findNextMatch(regex, cx, cy, wrap)
         if match.success:
-          buffer.writeCommand(JUMP, match.x, match.y)
+          buffer.writeCommand(JUMP, match.x, match.y, match.x + match.str.width() - 1)
       of READ_SUCCESS:
         var s: string
         istream.sread(s)
diff --git a/src/buffer/container.nim b/src/buffer/container.nim
index beb5fedc..4232ae09 100644
--- a/src/buffer/container.nim
+++ b/src/buffer/container.nim
@@ -40,6 +40,12 @@ type
       request*: Request
     else: discard
 
+  Highlight* = ref object
+    x*, y*: int
+    endy*, endx*: int
+    rect*: bool
+    clear*: bool
+
   Container* = ref object
     attrs*: TermAttributes
     width*: int
@@ -51,6 +57,7 @@ type
     children*: seq[Container]
     pos: CursorPosition
     bpos: seq[CursorPosition]
+    highlights: seq[Highlight]
     parent*: Container
     sourcepair*: Container
     istream*: Stream
@@ -66,6 +73,7 @@ type
     redirect*: Option[URL]
     ispipe: bool
     jump: bool
+    hlon*: bool
     pipeto: Container
     tty: FileHandle
 
@@ -94,6 +102,7 @@ proc newBuffer*(config: Config, source: BufferSource, tty: FileHandle, ispipe =
         raise newException(Defect, "Failed to open input handle")
       if not open(writef, pipefd_out[1], fmWrite):
         raise newException(Defect, "Failed to open output handle")
+      discard c_setvbuf(writef, nil, IONBF, 0)
       let istream = newFileStream(readf)
       let ostream = newFileStream(writef)
       launchBuffer(config, source, attrs, istream, ostream)
@@ -226,6 +235,37 @@ func lineWindow(container: Container): Slice[int] =
     y = container.numLines
   return max(x, 0) .. min(y, container.numLines - 1)
 
+func contains*(hl: Highlight, x, y: int): bool =
+  if hl.rect:
+    let rx = hl.x .. hl.endx
+    let ry = hl.y .. hl.endy
+    return x in rx and y in ry
+  else:
+    return (y > hl.y or y == hl.y and x >= hl.x) and
+      (y < hl.endy or y == hl.endy and x <= hl.endx)
+
+func contains*(hl: Highlight, y: int): bool =
+  return y in hl.y .. hl.endy
+
+func colorArea*(hl: Highlight, y: int, limitx: Slice[int]): Slice[int] =
+  if hl.rect:
+    if y in hl.y .. hl.endy:
+      return max(hl.x, limitx.a) .. min(hl.endx, limitx.b)
+  else:
+    if y in hl.y + 1 .. hl.endy - 1:
+      return limitx
+    if y == hl.y and y == hl.endy:
+      return max(hl.x, limitx.a) .. min(hl.endx, limitx.b)
+    if y == hl.y:
+      return max(hl.x, limitx.a) .. limitx.b
+    if y == hl.endy:
+      return limitx.a .. min(hl.endx, limitx.b)
+
+func findHighlights*(container: Container, y: int): seq[Highlight] =
+  for hl in container.highlights:
+    if y in hl:
+      result.add(hl)
+
 macro writeCommand(container: Container, cmd: BufferCommand, args: varargs[typed]) =
   result = newStmtList()
   result.add(quote do: `container`.ostream.swrite(`cmd`))
@@ -560,6 +600,11 @@ proc windowChange*(container: Container, attrs: TermAttributes) =
   container.height = attrs.height - 1
   container.writeCommand(WINDOW_CHANGE, attrs)
 
+proc clearSearchHighlights*(container: Container) =
+  for i in countdown(container.highlights.high, 0):
+    if container.highlights[i].clear:
+      container.highlights.del(i)
+
 proc handleEvent*(container: Container): ContainerEvent =
   var cmd: ContainerCommand
   container.istream.sread(cmd)
@@ -610,10 +655,15 @@ proc handleEvent*(container: Container): ContainerEvent =
     container.istream.sread(pwd)
     return ContainerEvent(t: READ_LINE, prompt: prompt, value: str, password: pwd)
   of JUMP:
-    var x, y: int
+    var x, y, ex: int
     container.istream.sread(x)
     container.istream.sread(y)
-    if container.jump and x >= 0 and y >= 0:
+    container.istream.sread(ex)
+    if container.jump:
+      if container.hlon:
+        let hl = Highlight(x: x, y: y, endx: ex, endy: y, clear: true)
+        container.highlights.add(hl)
+        container.hlon = false
       container.setCursorXY(x, y)
       container.jump = false
       return ContainerEvent(t: UPDATE)
diff --git a/src/config/config.nim b/src/config/config.nim
index a3b121ba..9db896e5 100644
--- a/src/config/config.nim
+++ b/src/config/config.nim
@@ -15,7 +15,7 @@ type
     stylesheet*: string
     startup*: string
     ambiguous_double*: bool
-    markcolor*: CellColor
+    hlcolor*: CellColor
 
 func getRealKey(key: string): string =
   var realk: string
@@ -115,17 +115,17 @@ proc parseConfig(config: Config, dir: string, t: TomlValue) =
       config.stylesheet &= css["inline"].s
   if "display" in t:
     let display = t["display"]
-    if "mark-color" in display:
-      case display["mark-color"].s
-      of "black": config.markcolor = CellColor(rgb: false, color: 40u8)
-      of "red": config.markcolor = CellColor(rgb: false, color: 41u8)
-      of "green": config.markcolor = CellColor(rgb: false, color: 42u8)
-      of "yellow": config.markcolor = CellColor(rgb: false, color: 43u8)
-      of "blue": config.markcolor = CellColor(rgb: false, color: 44u8)
-      of "magenta": config.markcolor = CellColor(rgb: false, color: 45u8)
-      of "cyan": config.markcolor = CellColor(rgb: false, color: 46u8)
-      of "white": config.markcolor = CellColor(rgb: false, color: 47u8)
-      of "terminal": config.markcolor = CellColor(rgb: false, color: 0)
+    if "highlight-color" in display:
+      case display["highlight-color"].s
+      of "black": config.hlcolor = CellColor(rgb: false, color: 40u8)
+      of "red": config.hlcolor = CellColor(rgb: false, color: 41u8)
+      of "green": config.hlcolor = CellColor(rgb: false, color: 42u8)
+      of "yellow": config.hlcolor = CellColor(rgb: false, color: 43u8)
+      of "blue": config.hlcolor = CellColor(rgb: false, color: 44u8)
+      of "magenta": config.hlcolor = CellColor(rgb: false, color: 45u8)
+      of "cyan": config.hlcolor = CellColor(rgb: false, color: 46u8)
+      of "white": config.hlcolor = CellColor(rgb: false, color: 47u8)
+      of "terminal": config.hlcolor = CellColor(rgb: false, color: 0)
 
 proc parseConfig(config: Config, dir: string, stream: Stream) =
   config.parseConfig(dir, parseToml(stream))
diff --git a/src/display/client.nim b/src/display/client.nim
index 2f4771f8..a45e64e3 100644
--- a/src/display/client.nim
+++ b/src/display/client.nim
@@ -172,9 +172,10 @@ proc input(client: Client) =
           client.feedNext = true
       elif not client.feedNext:
         client.evalJSFree(action, "<command>")
-      client.pager.updateReadLine()
       if client.pager.lineedit.isNone:
         client.line = nil
+      if not client.feedNext:
+        client.pager.updateReadLine()
   else:
     let action = getNormalAction(client.config, client.s)
     client.evalJSFree(action, "<command>")
diff --git a/src/display/pager.nim b/src/display/pager.nim
index 1b98b0e2..e34d94eb 100644
--- a/src/display/pager.nim
+++ b/src/display/pager.nim
@@ -153,6 +153,8 @@ proc refreshDisplay*(pager: Pager, container = pager.container) =
   var r: Rune
   var by = 0
   pager.clearDisplay()
+  var hlformat = newFormat()
+  hlformat.bgcolor = pager.config.hlcolor
   for line in container.ilines(container.fromy ..< min(container.fromy + pager.bheight, container.numLines)):
     var w = 0 # width of the row so far
     var i = 0 # byte in line.str
@@ -187,19 +189,13 @@ proc refreshDisplay*(pager: Pager, container = pager.container) =
       let tk = k + r.width()
       while k < tk and k < pager.bwidth - 1:
         inc k
-    # Then, for each cell that has a mark, override its formatting with that
-    # specified by the mark.
-    #TODO honestly this was always broken anyways. not sure about how to re-implement it
-    #var l = 0
-    #while l < pager.marks.len and buffer.marks[l].y < by:
-    #  inc l # linear search to find the first applicable mark
-    #let aw = buffer.width - (startw - buffer.fromx) # actual width
-    #while l < buffer.marks.len and buffer.marks[l].y == by:
-    #  let mark = buffer.marks[l]
-    #  inc l
-    #  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 - startw].format = mark.format
+    # Finally, override cell formatting for highlighted cells.
+    let hls = container.findHighlights(by)
+    let aw = container.width - (startw - container.fromx) # actual width
+    for hl in hls:
+      let area = hl.colorArea(by, startw .. startw + aw)
+      for i in area:
+        pager.display[dls + i - startw].format = hlformat
     inc by
 
 func generateStatusMessage*(pager: Pager): string =
@@ -474,15 +470,18 @@ proc updateReadLineISearch(pager: Pager, linemode: LineMode) =
   of CANCEL:
     pager.iregex = none(Regex)
     pager.container.popCursorPos()
+    pager.container.clearSearchHighlights()
   of EDIT:
     let x = $lineedit.news
     if x != "": pager.iregex = compileSearchRegex(x)
+    pager.container.clearSearchHighlights()
     pager.container.popCursorPos()
     if pager.iregex.isSome:
       if linemode == ISEARCH_F:
         pager.container.cursorNextMatch(pager.iregex.get, true)
       else:
         pager.container.cursorPrevMatch(pager.iregex.get, true)
+      pager.container.hlon = true
     pager.container.pushCursorPos()
     pager.displayPage()
     pager.statusMode()
@@ -491,6 +490,8 @@ proc updateReadLineISearch(pager: Pager, linemode: LineMode) =
     if pager.iregex.isSome:
       pager.regex = pager.iregex
     pager.reverseSearch = linemode == ISEARCH_B
+    pager.container.clearSearchHighlights()
+    pager.redraw()
 
 proc updateReadLine*(pager: Pager) =
   let lineedit = pager.lineedit.get