about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/io/buffer.nim226
-rw-r--r--src/io/cell.nim13
-rw-r--r--src/utils/twtstr.nim15
3 files changed, 167 insertions, 87 deletions
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index ec61999b..b4d19e36 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -160,6 +160,15 @@ func numLines*(buffer: Buffer): int = buffer.lines.len
 
 func lastVisibleLine*(buffer: Buffer): int = min(buffer.fromy + buffer.height, buffer.numLines)
 
+func currentLineWidth*(buffer: Buffer): int =
+  if buffer.cursory > buffer.lines.len:
+    return 0
+  return buffer.lines[buffer.cursory].width()
+
+func maxfromy(buffer: Buffer): int = max(buffer.numLines - buffer.height, 0)
+
+func maxfromx(buffer: Buffer): int = max(buffer.currentLineWidth() - buffer.width, 0)
+
 func acursorx(buffer: Buffer): int =
   return max(0, buffer.cursorx - buffer.fromx)
 
@@ -185,14 +194,7 @@ func cell(buffer: Buffer): FixedCell =
   let row = (buffer.cursory - buffer.fromy) * buffer.width
   return buffer.display[row + buffer.acursorx]
 
-func currentRune(buffer: Buffer): Rune =
-  let cell = buffer.currentDisplayCell()
-  if cell.runes.len == 0:
-    return Rune(' ')
-  return cell.runes[0]
-
-func getCursorLink(buffer: Buffer): Element =
-  let nodes = buffer.currentDisplayCell().nodes
+func getLink(nodes: seq[Node]): Element =
   for node in nodes:
     if node.nodeType == ELEMENT_NODE:
       let elem = Element(node)
@@ -200,10 +202,8 @@ func getCursorLink(buffer: Buffer): Element =
         return elem
   return nil
 
-func currentLineWidth*(buffer: Buffer): int =
-  if buffer.cursory > buffer.lines.len:
-    return 0
-  return buffer.lines[buffer.cursory].width()
+func getCursorLink(buffer: Buffer): Element =
+  return buffer.currentDisplayCell().nodes.getLink()
 
 func currentLine(buffer: Buffer): string =
   return buffer.lines[buffer.cursory].str
@@ -322,69 +322,94 @@ proc cursorTo*(buffer: Buffer, x, y: int) =
     buffer.fromx = buffer.cursorx
     buffer.redraw = true
 
-proc cursorDown*(buffer: Buffer) =
-  if buffer.cursory < buffer.numLines - 1:
-    inc buffer.cursory
-    buffer.restoreCursorX()
-    if buffer.cursory - buffer.height >= buffer.fromy:
-      inc buffer.fromy
-      buffer.redraw = true
-
-proc cursorUp*(buffer: Buffer) =
-  if buffer.cursory > 0:
-    dec buffer.cursory
-    buffer.restoreCursorX()
-    if buffer.cursory < buffer.fromy:
-      dec buffer.fromy
-      buffer.redraw = true
+proc setCursorXB*(buffer: Buffer, byte: int) =
+  var b = buffer.currentCursorBytes()
+  if byte == b:
+    return
 
-#TODO these don't exactly work as intended, I think
-proc cursorRight*(buffer: Buffer) =
-  let cellwidth = buffer.cell().ow
-  let lw = buffer.currentLineWidth()
-  if buffer.cursorx < lw - 1:
-    buffer.cursorx = min(lw - 1, buffer.cursorx + cellwidth)
-    assert buffer.cursorx >= 0
-    buffer.xend = buffer.cursorx
-    if buffer.cursorx - buffer.width + (cellwidth - 1) >= buffer.fromx:
-      buffer.fromx += cellwidth
-      buffer.redraw = true
-    if buffer.cursorx == buffer.fromx:
-      inc buffer.cursorx
+  var w = buffer.fromx + buffer.cursorx
+  if b < byte:
+    while b < byte:
+      var r: Rune
+      fastRuneAt(buffer.currentLine, b, r)
+      w += r.width()
+  else:
+    while b > byte:
+      let (r, o) = lastRune(buffer.currentLine, b)
+      w -= r.width()
+      b -= o
 
-proc cursorLeft*(buffer: Buffer) =
-  let cellorigin = buffer.fromx + buffer.currentCellOrigin()
-  let lw = buffer.currentLineWidth()
-  if buffer.fromx > buffer.cursorx:
-    buffer.cursorx = min(max(lw - 1, 0), cellorigin - 1)
-    buffer.fromx = buffer.cursorx
+  let x = w
+  if x - buffer.fromx >= 0 and x - buffer.width < buffer.fromx:
+    buffer.cursorx = x
+  else:
+    if x > buffer.cursorx:
+      buffer.fromx = max(x - buffer.width + 1, 0)
+    elif x < buffer.cursorx:
+      buffer.fromx = min(x, buffer.maxfromx)
+    buffer.cursorx = x
     buffer.redraw = true
-  elif buffer.cursorx > 0:
-    buffer.cursorx = max(0, cellorigin - 1)
-    if buffer.fromx > buffer.cursorx - (buffer.cell().ow - 1):
-      buffer.fromx = max(buffer.cursorx - max(0, buffer.cell().ow - 1), 0)
-      buffer.redraw = true
-
   buffer.xend = buffer.cursorx
 
+
 proc setCursorX*(buffer: Buffer, x: int) =
+  if buffer.cursorx == x:
+    return
   var b = buffer.currentCursorBytes()
   var w = buffer.fromx + buffer.cursorx
   while b < buffer.currentLine.len and w < x:
     var r: Rune
     fastRuneAt(buffer.currentLine, b, r)
     w += r.width()
+
+  b = min(b, max(buffer.currentLine.len - 1, 0))
   while b > 0 and w > x:
     let (r, o) = lastRune(buffer.currentLine, b)
     w -= r.width()
     b -= o
 
-  if w - buffer.fromx <= buffer.width:
-    buffer.cursorx = w - buffer.fromx
+  if x - buffer.fromx >= 0 and x - buffer.width < buffer.fromx:
+    buffer.cursorx = x
   else:
-    buffer.fromx = w - buffer.width
-    buffer.cursorx = w - buffer.fromx
+    if x > buffer.cursorx:
+      buffer.fromx = max(x - buffer.width + 1, 0)
+    elif x < buffer.cursorx:
+      buffer.fromx = min(x, buffer.maxfromx)
+    buffer.cursorx = x
     buffer.redraw = true
+  buffer.xend = buffer.cursorx
+
+proc setCursorY*(buffer: Buffer, y: int) =
+  if buffer.cursory == y:
+    return
+  if y - buffer.fromy >= 0 and y - buffer.height < buffer.fromy:
+    buffer.cursory = y
+  else:
+    if y > buffer.cursory:
+      buffer.fromy = max(y - buffer.height + 1, 0)
+    else:
+      buffer.fromy = min(y, buffer.maxfromy)
+    buffer.cursory = y
+    buffer.redraw = true
+  buffer.restoreCursorX()
+
+proc cursorDown*(buffer: Buffer) =
+  if buffer.cursory < buffer.numLines - 1:
+    buffer.setCursorY(buffer.cursory + 1)
+
+proc cursorUp*(buffer: Buffer) =
+  if buffer.cursory > 0:
+    buffer.setCursorY(buffer.cursory - 1)
+
+proc cursorRight*(buffer: Buffer) =
+  let cellwidth = buffer.cell().ow
+  if buffer.cursorx < buffer.currentLineWidth() - 1:
+    buffer.setCursorX(buffer.cursorx + cellwidth)
+
+proc cursorLeft*(buffer: Buffer) =
+  let cellwidth = buffer.cell().ow
+  if buffer.cursorx >= cellwidth:
+    buffer.setCursorX(buffer.cursorx - cellwidth)
 
 proc cursorLineBegin*(buffer: Buffer) =
   buffer.cursorx = 0
@@ -397,12 +422,12 @@ proc cursorLineEnd*(buffer: Buffer) =
   buffer.cursorx = max(buffer.currentLineWidth() - 1, 0)
   buffer.xend = buffer.cursorx
   buffer.fromx = max(buffer.cursorx - buffer.width + 1, 0)
-  buffer.redraw = buffer.fromx > 0
+  buffer.redraw = buffer.redraw or buffer.fromx > 0
 
 proc cursorNextWord*(buffer: Buffer) =
   var r: Rune
   var b = buffer.currentCursorBytes()
-  var x = buffer.fromx + buffer.cursorx
+  var x = buffer.cursorx
   while b < buffer.currentLine.len:
     let pb = b
     fastRuneAt(buffer.currentLine, b, r)
@@ -430,9 +455,9 @@ proc cursorNextWord*(buffer: Buffer) =
 
 proc cursorPrevWord*(buffer: Buffer) =
   var b = buffer.currentCursorBytes()
-  var x = buffer.fromx + buffer.cursorx
+  var x = buffer.cursorx
   if buffer.currentLine.len > 0:
-    var r: Rune
+    b = min(b, buffer.currentLine.len - 1)
     while b >= 0:
       let (r, o) = lastRune(buffer.currentLine, b)
       if r.breaksWord():
@@ -459,12 +484,73 @@ proc cursorPrevWord*(buffer: Buffer) =
       buffer.cursorLineBegin()
 
 proc cursorNextLink*(buffer: Buffer) =
-  #TODO
-  discard
+  let line = buffer.lines[buffer.cursory]
+  var i = line.findFormatN(buffer.currentCursorBytes()) - 1
+  var link: Element = nil
+  if i >= 0:
+    link = line.formats[i].nodes.getLink()
+  inc i
+
+  while i < line.formats.len:
+    let format = line.formats[i]
+    let fl = format.nodes.getLink()
+    if fl != nil and fl != link:
+      buffer.setCursorXB(format.pos)
+      return
+    inc i
+
+  for y in (buffer.cursory + 1)..(buffer.numLines - 1):
+    let line = buffer.lines[y]
+    i = 0
+    while i < line.formats.len:
+      let format = line.formats[i]
+      let fl = format.nodes.getLink()
+      if fl != nil and fl != link:
+        buffer.setCursorY(y)
+        buffer.setCursorXB(format.pos)
+        return
+      inc i
 
 proc cursorPrevLink*(buffer: Buffer) =
-  #TODO
-  return
+  let line = buffer.lines[buffer.cursory]
+  var i = line.findFormatN(buffer.currentCursorBytes()) - 1
+  var link: Element = nil
+  if i >= 0:
+    link = line.formats[i].nodes.getLink()
+  dec i
+
+  while i >= 0:
+    let format = line.formats[i]
+    let fl = format.nodes.getLink()
+    if fl != nil and fl != link:
+      buffer.setCursorXB(format.pos)
+      return
+    dec i
+
+  for y in countdown(buffer.cursory - 1, 0):
+    let line = buffer.lines[y]
+    i = line.formats.len - 1
+    while i >= 0:
+      let format = line.formats[i]
+      let fl = format.nodes.getLink()
+      if fl != nil and fl != link:
+        #go to beginning of link
+        var ly = y #last y
+        var lx = format.pos #last x
+        for iy in countdown(ly - 1, 0):
+          let line = buffer.lines[iy]
+          i = line.formats.len - 1
+          while i >= 0:
+            let format = line.formats[i]
+            let nl = format.nodes.getLink()
+            if nl == fl:
+              ly = iy
+              lx = format.pos
+            dec i
+        buffer.setCursorY(ly)
+        buffer.setCursorXB(lx)
+        return
+      dec i
 
 proc cursorFirstLine*(buffer: Buffer) =
   if buffer.fromy > 0:
@@ -698,6 +784,11 @@ proc updateHover(buffer: Buffer) =
         elem.hover = true
         buffer.reshape = true
         elem.refreshStyle()
+    let link = nodes.getLink()
+    if link != nil:
+      buffer.hovertext = "(link)"
+    else:
+      buffer.hovertext = ""
     for node in buffer.prevnodes:
       var elem: Element
       if node of Element:
@@ -809,12 +900,11 @@ proc setStatusMessage*(buffer: Buffer, str: string) =
     inc i
 
 proc statusMsgForBuffer(buffer: Buffer) =
-  var msg = ($(buffer.cursory + 1) & "/" & $buffer.numLines & " (" &
-            $buffer.atPercentOf() & "%) " &
-            "<" & buffer.title & ">").ansiStyle(styleReverse).ansiReset().join()
+  var msg = $(buffer.cursory + 1) & "/" & $buffer.numLines & " (" &
+            $buffer.atPercentOf() & "%) " & "<" & buffer.title & ">"
   if buffer.hovertext.len > 0:
     msg &= " " & buffer.hovertext
-  buffer.setStatusMessage(msg)
+  buffer.setStatusMessage(msg.ansiStyle(styleReverse).ansiReset())
 
 proc displayBufferSwapOutput(buffer: Buffer) =
   print(buffer.generateSwapOutput())
diff --git a/src/io/cell.nim b/src/io/cell.nim
index 9e657bb6..f8b2b3cb 100644
--- a/src/io/cell.nim
+++ b/src/io/cell.nim
@@ -94,24 +94,23 @@ func width*(cell: FixedCell): int =
 func newFormatting*(): Formatting =
   return Formatting(fgcolor: defaultColor, bgcolor: defaultColor)
 
-func findFormat*(line: FlexibleLine, pos: int): FormattingCell =
+func findFormatN*(line: FlexibleLine, pos: int): int =
   var i = 0
   while i < line.formats.len:
     if line.formats[i].pos > pos:
       break
     inc i 
-  dec i
+  return i
+
+func findFormat*(line: FlexibleLine, pos: int): FormattingCell =
+  let i = line.findFormatN(pos) - 1
   if i != -1:
     result = line.formats[i]
   else:
     result.pos = -1
 
 func findNextFormat*(line: FlexibleLine, pos: int): FormattingCell =
-  var i = 0
-  while i < line.formats.len:
-    if line.formats[i].pos > pos:
-      break
-    inc i 
+  let i = line.findFormatN(pos)
   if i < line.formats.len:
     result = line.formats[i]
   else:
diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim
index 9e88b9cd..01f3cfb4 100644
--- a/src/utils/twtstr.nim
+++ b/src/utils/twtstr.nim
@@ -11,27 +11,18 @@ import sequtils
 when defined(posix):
   import posix
 
-func ansiStyle*(str: string, style: Style): seq[string] =
+func ansiStyle*(str: string, style: Style): string =
   result &= ansiStyleCode(style)
   result &= str
 
-func ansiFgColor*(str: string, color: ForegroundColor): seq[string] =
+func ansiFgColor*(str: string, color: ForegroundColor): string =
   result &= ansiForegroundColorCode(color)
   result &= str
 
-func ansiReset*(str: string): seq[string] =
+func ansiReset*(str: string): string =
   result &= str
   result &= ansiResetCode
 
-func ansiStyle*(str: seq[string], style: Style): seq[string] =
-  return ansiStyleCode(style) & str
-
-func ansiFgColor*(str: seq[string], color: ForegroundColor): seq[string] =
-  return ansiForegroundColorCode(color) & str
-
-func ansiReset*(str: seq[string]): seq[string] =
-  return str & ansiResetCode
-
 func maxString*(str: string, max: int): string =
   if max < str.runeLen():
     return str.runeSubstr(0, max - 2) & "$"