about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-08-06 17:15:17 +0200
committerbptato <nincsnevem662@gmail.com>2021-08-06 17:15:17 +0200
commit6d50a0f7d1af3da77fcea7290ac02a43e8f454e4 (patch)
treed5f61a0c533d535908e4b224774dcc9378b05bf4
parentb94597a68eb8572cf8f521ee9c39cc7d9d310827 (diff)
downloadchawan-6d50a0f7d1af3da77fcea7290ac02a43e8f454e4.tar.gz
Config refactoring, width aware cursor movement
-rw-r--r--src/config.nim65
-rw-r--r--src/io/buffer.nim67
-rw-r--r--src/io/display.nim2
-rw-r--r--src/io/twtio.nim18
-rw-r--r--src/main.nim4
5 files changed, 87 insertions, 69 deletions
diff --git a/src/config.nim b/src/config.nim
index 41d4a6d4..4713db07 100644
--- a/src/config.nim
+++ b/src/config.nim
@@ -1,4 +1,5 @@
 import tables
+import os
 import strutils
 
 import utils/twtstr
@@ -33,11 +34,18 @@ type
     ACTION_LINED_COMPOSE_TOGGLE, ACTION_LINED_ESC
 
   ActionMap = Table[string, TwtAction]
-  ComposeMap = RadixNode[string]
+  StaticConfig = object
+    nmap: ActionMap
+    lemap: ActionMap
+    cmap: Table[string, string]
 
-var normalActionRemap*: ActionMap
-var linedActionRemap*: ActionMap
-var composeRemap*: ComposeMap
+  Config = object
+    nmap*: ActionMap
+    lemap*: ActionMap
+    cmap*: RadixNode[string]
+
+func getConfig(s: StaticConfig): Config =
+  return Config(nmap: s.nmap, lemap: s.lemap, cmap: s.cmap.toRadixTree())
 
 func getRealKey(key: string): string =
   var realk: string
@@ -101,37 +109,30 @@ func constructActionTable*(origTable: ActionMap): ActionMap =
     newTable[realk] = v
   return newTable
 
-proc parseConfigLine(line: string, nmap: var ActionMap, lemap: var ActionMap,
-                     compose: var Table[string, string]) =
+proc parseConfigLine[T](line: string, config: var T) =
   if line.len == 0 or line[0] == '#':
     return
   let cmd = line.split(' ')
   if cmd.len == 3:
     if cmd[0] == "nmap":
-      nmap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+      config.nmap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
     elif cmd[0] == "lemap":
-      lemap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+      config.lemap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
     elif cmd[0] == "comp":
-      compose[getRealKey(cmd[1])] = cmd[2]
+      config.cmap[getRealKey(cmd[1])] = cmd[2]
 
-proc staticReadKeymap(): (ActionMap, ActionMap, Table[string, string]) =
-  let config = staticRead"../res/config"
-  var nmap: ActionMap
-  var lemap: ActionMap
-  var compose: Table[string, string]
-  for line in config.split('\n'):
-    parseConfigLine(line, nmap, lemap, compose)
+proc staticReadConfig(): StaticConfig =
+  let default = staticRead"../res/config"
+  for line in default.split('\n'):
+    parseConfigLine(line, result)
 
-  nmap = constructActionTable(nmap)
-  lemap = constructActionTable(lemap)
-  return (nmap, lemap, compose)
+  result.nmap = constructActionTable(result.nmap)
+  result.lemap = constructActionTable(result.lemap)
 
-const (normalActionMap, linedActionMap, composeMap) = staticReadKeymap()
-normalActionRemap = normalActionMap
-linedActionRemap = linedActionMap
-composeRemap = composeMap.toRadixTree()
+const defaultConfig = staticReadConfig()
+var gconfig* = getConfig(defaultConfig)
 
-proc readConfig*(filename: string): bool =
+proc readConfig(filename: string) =
   var f: File
   let status = f.open(filename, fmRead)
   var nmap: ActionMap
@@ -140,11 +141,13 @@ proc readConfig*(filename: string): bool =
   if status:
     var line: TaintedString
     while f.readLine(line):
-      parseConfigLine(line, nmap, lemap, compose)
+      parseConfigLine(line, gconfig)
+
+    gconfig.nmap = constructActionTable(nmap)
+    gconfig.lemap = constructActionTable(lemap)
+    gconfig.cmap = compose.toRadixTree()
 
-    normalActionRemap = constructActionTable(nmap)
-    linedActionRemap = constructActionTable(lemap)
-    composeRemap = compose.toRadixTree()
-    return true
-  else:
-    return false
+proc readConfig*() =
+  when defined(debug):
+    readConfig("res" / "config")
+  readConfig(getConfigDir() / "twt" / "config")
diff --git a/src/io/buffer.nim b/src/io/buffer.nim
index 31c02aa9..5eb2c71b 100644
--- a/src/io/buffer.nim
+++ b/src/io/buffer.nim
@@ -26,16 +26,19 @@ type
   BufferCell = object of Cell
     rune*: Rune
 
-# xterm supports max 2 characters per cell by default. might make the tuple a
-# seq in the future but for now it's fine like this
+  BufferRow = seq[BufferCell]
+
   DisplayCell = object of Cell
-    runes*: tuple[a: Rune, b: Rune]
+    runes*: seq[Rune]
+
+  DisplayRow = seq[DisplayCell]
 
   Buffer* = ref BufferObj
   BufferObj = object
     title*: string
-    lines*: seq[seq[BufferCell]]
-    display*: seq[DisplayCell]
+    lines*: seq[BufferRow]
+    display*: DisplayRow
+    prevdisplay*: DisplayRow
     hovertext*: string
     width*: int
     height*: int
@@ -73,10 +76,10 @@ func generateFullOutput*(buffer: Buffer): string =
       result &= '\n'
       x = 0
 
-    if cell.runes.a != Rune(0):
-      result &= $cell.runes.a
-    if cell.runes.b != Rune(0):
-      result &= $cell.runes.b
+    for r in cell.runes:
+      if r != Rune(0):
+        result &= $r
+
     inc x
 
 func lastLine*(buffer: Buffer): int =
@@ -89,6 +92,15 @@ func width(line: seq[BufferCell]): int =
   for c in line:
     result += c.rune.width()
 
+func cellWidthOverlap*(buffer: Buffer, x: int, y: int): int =
+  let row = y * buffer.width
+  var ox = x
+  while buffer.display[row + ox].runes.len == 0 and ox > 0:
+    dec ox
+  return buffer.display[row + ox].runes.width()
+
+func currentCellWidth*(buffer: Buffer): int = buffer.cellWidthOverlap(buffer.cursorx - buffer.fromx, buffer.cursory - buffer.fromy)
+
 func currentLineWidth*(buffer: Buffer): int =
   return buffer.lines[buffer.cursory].width()
 
@@ -214,19 +226,22 @@ proc cursorUp*(buffer: Buffer) =
       buffer.redraw = true
 
 proc cursorRight*(buffer: Buffer) =
-  if buffer.cursorx < buffer.currentLineWidth() - 1:
-    inc buffer.cursorx
+  let cellwidth = buffer.currentCellWidth()
+  let lw = buffer.currentLineWidth()
+  if buffer.cursorx < lw - 1:
+    buffer.cursorx = min(lw - 1, buffer.cursorx + cellwidth)
     buffer.xend = buffer.cursorx
     if buffer.cursorx - buffer.width >= buffer.fromx:
       inc buffer.fromx
       buffer.redraw = true
 
 proc cursorLeft*(buffer: Buffer) =
+  let cellwidth = buffer.currentCellWidth()
   if buffer.fromx > buffer.cursorx:
     buffer.fromx = buffer.cursorx
     buffer.redraw = true
   elif buffer.cursorx > 0:
-    dec buffer.cursorx
+    buffer.cursorx = max(0, buffer.cursorx - cellwidth)
     if buffer.fromx > buffer.cursorx:
       buffer.fromx = buffer.cursorx
       buffer.redraw = true
@@ -448,40 +463,40 @@ proc setDisplayText(buffer: Buffer, x: int, y: int, text: seq[Rune]) =
   var n = 0
   while i < text.len:
     if text[i].width() == 0:
-      buffer.display[pos + i - n].runes.b = text[i]
       inc n
-    else:
-      buffer.display[pos + i - n].runes.a = text[i]
+    buffer.display[pos + i - n].runes.add(text[i])
     inc i
 
 proc reshape*(buffer: Buffer) =
   buffer.display = newSeq[DisplayCell](buffer.width * buffer.height)
 
+proc clearDisplay*(buffer: Buffer) =
+  var i = 0
+  while i < buffer.display.len:
+    buffer.display[i].runes.setLen(0)
+    inc i
+
 proc refreshDisplay*(buffer: Buffer) =
   var y = 0
+  buffer.prevdisplay = buffer.display
+  buffer.clearDisplay()
   for line in buffer.lines[buffer.fromy..buffer.lastVisibleLine()]:
     var w = 0
     var i = 0
-    var n = 0
     while w < buffer.fromx and i < line.len:
       w += line[i].rune.width()
       inc i
 
     let dls = y * buffer.width
     var j = 0
+    var n = 0
     while w < buffer.fromx + buffer.width and i < line.len:
       w += line[i].rune.width()
       if line[i].rune.width() == 0:
-        buffer.display[dls + j].runes.b = line[i].rune
-      else:
-        buffer.display[dls + j].runes.a = line[i].rune
+        inc n
+      buffer.display[dls + j - n].runes.add(line[i].rune)
+      j += line[i].rune.width()
       inc i
-      inc j
-
-    while j < buffer.width:
-      buffer.display[dls + j].runes.a = Rune(0)
-      buffer.display[dls + j].runes.b = Rune(0)
-      inc j
 
     inc y
 
@@ -494,6 +509,8 @@ proc renderPlainText*(buffer: Buffer, text: string) =
       buffer.setText(0, y, line.toRunes())
       inc y
       line = ""
+    elif text[i] == '\r':
+      discard
     elif text[i] == '\t':
       line &= ' '.repeat(8)
     else:
diff --git a/src/io/display.nim b/src/io/display.nim
index 6f7be014..f5c9f645 100644
--- a/src/io/display.nim
+++ b/src/io/display.nim
@@ -335,7 +335,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
         buffer.setLocation(parseUri(url))
         return true
     of ACTION_LINE_INFO:
-      statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $(buffer.cursorx + 1) & "/" & $buffer.currentLineWidth(), buffer.width)
+      statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $(buffer.cursorx + 1) & "/" & $buffer.currentLineWidth() & " cell width: " & $buffer.currentCellWidth(), buffer.width)
       nostatus = true
     of ACTION_FEED_NEXT:
       feedNext = true
diff --git a/src/io/twtio.nim b/src/io/twtio.nim
index 8a8bef1c..26c5be02 100644
--- a/src/io/twtio.nim
+++ b/src/io/twtio.nim
@@ -25,13 +25,13 @@ template printesc*(s: string) =
       stdout.write($r)
 
 proc getNormalAction*(s: string): TwtAction =
-  if normalActionRemap.hasKey(s):
-    return normalActionRemap[s]
+  if gconfig.nmap.hasKey(s):
+    return gconfig.nmap[s]
   return NO_ACTION
 
 proc getLinedAction*(s: string): TwtAction =
-  if linedActionRemap.hasKey(s):
-    return linedActionRemap[s]
+  if gconfig.lemap.hasKey(s):
+    return gconfig.lemap[s]
   return NO_ACTION
 
 type LineState = object
@@ -151,16 +151,16 @@ proc insertCompose(state: var LineState, c: char) =
     else:
       cs = state.s.substr(0, state.compa - 1).toRunes()
     state.comps = state.s.substr(state.compa)
-    if state.comps.len > 0 and composeRemap.hasPrefix(state.comps):
+    if state.comps.len > 0 and gconfig.cmap.hasPrefix(state.comps):
       state.compa = state.comps.len
-      state.compn = composeRemap{state.comps}
+      state.compn = gconfig.cmap{state.comps}
       state.s = state.comps
       state.comps = ""
       state.feedNext = true
     else:
       cs &= state.comps.toRunes()
       state.compa = 0
-      state.compn = composeRemap
+      state.compn = gconfig.cmap
       state.comps = ""
 
     state.insertCharseq(cs)
@@ -168,7 +168,7 @@ proc insertCompose(state: var LineState, c: char) =
 proc readLine*(current: var string, minlen: int, maxlen: int): bool =
   var state: LineState
   state.news = current.toRunes()
-  state.compn = composeRemap
+  state.compn = gconfig.cmap
   state.cursor = state.news.len
   state.minlen = minlen
   state.maxlen = maxlen
@@ -293,7 +293,7 @@ proc readLine*(current: var string, minlen: int, maxlen: int): bool =
         state.cursor = state.news.len
     of ACTION_LINED_COMPOSE_TOGGLE:
       state.comp = not state.comp
-      state.compn = composeRemap
+      state.compn = gconfig.cmap
       state.compa = 0
       state.comps = ""
     of ACTION_FEED_NEXT:
diff --git a/src/main.nim b/src/main.nim
index 78d24e8d..8394b5ab 100644
--- a/src/main.nim
+++ b/src/main.nim
@@ -42,9 +42,7 @@ proc main*() =
   if paramCount() != 1:
     eprint "Invalid parameters. Usage:\ntwt <url>"
     quit(1)
-  if not readConfig("res/config"):
-    #eprint "Failed to read keymap, fallback to default"
-    discard
+  readConfig()
   let attrs = getTermAttributes()
   let buffer = newBuffer(attrs)
   let uri = parseUri(paramStr(1))