about summary refs log tree commit diff stats
path: root/src/local
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2024-10-10 17:22:15 +0200
committerbptato <nincsnevem662@gmail.com>2024-10-10 17:31:18 +0200
commit8c64d7cb4e54c289a6c18f6c144125196d888296 (patch)
tree4cc8de480912beebe62ccf9cf9ba826dc160fe2c /src/local
parentcf31d252ffe0aa195909a03efa714c6fc6e5fddf (diff)
downloadchawan-8c64d7cb4e54c289a6c18f6c144125196d888296.tar.gz
layout, pager: preserve tabs on display, selection & output
Substitute tabs with one of eight PUA characters based on their width,
and convert them back in the pager:

* TUI mode always prints spaces, but now handles tabs appropriately on
  cursor movement
* dump mode tries to preserve hard tabs, but uses soft tabs when that is
  not possible (e.g. tabs after a margin, tab with background color,
  etc)
* selection mode always outputs hard tabs.
Diffstat (limited to 'src/local')
-rw-r--r--src/local/container.nim9
-rw-r--r--src/local/pager.nim59
2 files changed, 45 insertions, 23 deletions
diff --git a/src/local/container.nim b/src/local/container.nim
index 88becc71..6a8471df 100644
--- a/src/local/container.nim
+++ b/src/local/container.nim
@@ -1346,9 +1346,7 @@ proc getSelectionText(container: Container; hl: Highlight = nil):
   if hl == nil:
     return nil
   if hl.t != hltSelect:
-    let p = newPromise[string]()
-    p.resolve("")
-    return p
+    return newResolvedPromise("")
   let startx = hl.startx
   let starty = hl.starty
   let endx = hl.endx
@@ -1381,7 +1379,7 @@ proc getSelectionText(container: Container; hl: Highlight = nil):
         if i > 0:
           s &= '\n'
         s &= line.str
-    return s
+    return s.expandPUATabsHard()
   )
 
 proc markURL(container: Container) {.jsfunc.} =
@@ -1751,6 +1749,9 @@ proc drawLines*(container: Container; display: var FixedGrid; hlcolor: CellColor
         nf = line.findNextFormat(pw)
       if u <= 0xFF and char(u) in Controls:
         display[dls + k].str &= '^' & char(u).getControlLetter()
+      elif u in TabPUARange:
+        for i in 0 ..< uw:
+          display[dls + k].str &= ' '
       else:
         for j in pi ..< i:
           display[dls + k].str &= line.str[j]
diff --git a/src/local/pager.nim b/src/local/pager.nim
index f757f1d0..e8bb92bf 100644
--- a/src/local/pager.nim
+++ b/src/local/pager.nim
@@ -479,28 +479,49 @@ proc showAlerts*(pager: Pager) =
       pager.inputBuffer == "" and pager.precnum == 0:
     pager.refreshStatusMsg()
 
+proc drawBufferAdvance(s: openArray[char]; bgcolor: CellColor; oi, ox: var int;
+    ex: int): string =
+  var ls = newStringOfCap(s.len)
+  var i = oi
+  var x = ox
+  while x < ex and i < s.len:
+    let pi = i
+    let u = s.nextUTF8(i)
+    let uw = u.width()
+    x += uw
+    if u in TabPUARange:
+      # PUA tabs can be expanded to hard tabs if
+      # * they are correctly aligned
+      # * they don't have a bgcolor (terminals will fail to output bgcolor with
+      #   tabs)
+      if bgcolor == defaultColor and (x and 7) == 0:
+        ls &= '\t'
+      else:
+        for i in 0 ..< uw:
+          ls &= ' '
+    else:
+      for i in pi ..< i:
+        ls &= s[i]
+  oi = i
+  ox = x
+  return ls
+
 proc drawBuffer*(pager: Pager; container: Container; ofile: File) =
   var format = Format()
   container.readLines(proc(line: SimpleFlexibleLine) =
-    if line.formats.len == 0:
-      ofile.writeLine(line.str)
-    else:
-      var x = 0
-      var w = -1
-      var i = 0
-      var s = ""
-      for f in line.formats:
-        let si = i
-        while x < f.pos:
-          let u = line.str.nextUTF8(i)
-          x += u.width()
-        s.processOutputString(pager.term, line.str.toOpenArray(si, i - 1), w)
-        s.processFormat(pager.term, format, f.format)
-      if i < line.str.len:
-        s.processOutputString(pager.term,
-          line.str.toOpenArray(i, line.str.high), w)
-      s.processFormat(pager.term, format, Format())
-      ofile.writeLine(s)
+    var x = 0
+    var w = -1
+    var i = 0
+    var s = ""
+    for f in line.formats:
+      let ls = line.str.drawBufferAdvance(format.bgcolor, i, x, f.pos)
+      s.processOutputString(pager.term, ls, w)
+      s.processFormat(pager.term, format, f.format)
+    if i < line.str.len:
+      let ls = line.str.drawBufferAdvance(format.bgcolor, i, x, int.high)
+      s.processOutputString(pager.term, ls, w)
+    s.processFormat(pager.term, format, Format())
+    ofile.writeLine(s)
   )
   ofile.flushFile()