about summary refs log tree commit diff stats
path: root/twtio.nim
diff options
context:
space:
mode:
Diffstat (limited to 'twtio.nim')
-rw-r--r--twtio.nim111
1 files changed, 74 insertions, 37 deletions
diff --git a/twtio.nim b/twtio.nim
index 4c02cfb0..32e4d55c 100644
--- a/twtio.nim
+++ b/twtio.nim
@@ -1,6 +1,7 @@
 import terminal
 import tables
 import strutils
+import unicode
 
 import twtstr
 import config
@@ -9,6 +10,13 @@ template print*(s: varargs[string, `$`]) =
   for x in s:
     stdout.write(x)
 
+template printesc*(s: string) =
+  for ruby in s:
+    if ($ruby)[0].isControlChar():
+      stdout.write(($($ruby)[0].getControlLetter()).ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset())
+    else:
+      stdout.write($ruby)
+
 template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}:
   var a = false
   for x in s:
@@ -36,10 +44,11 @@ proc readLine*(prompt: string, current: var string): bool =
   var new = current
   print(prompt)
   print(' ')
-  print(new)
+  printesc(new)
   var s = ""
   var feedNext = false
-  var cursor = new.len
+  var escNext = false
+  var cursor = new.runeLen
   while true:
     if not feedNext:
       s = ""
@@ -47,7 +56,10 @@ proc readLine*(prompt: string, current: var string): bool =
       feedNext = false
     let c = getch()
     s &= c
-    let action = getLinedAction(s)
+    var rl = new.runeLen()
+    var action = getLinedAction(s)
+    if escNext:
+      action = NO_ACTION
     case action
     of ACTION_LINED_CANCEL:
       return false
@@ -56,70 +68,95 @@ proc readLine*(prompt: string, current: var string): bool =
       return true
     of ACTION_LINED_BACKSPACE:
       if cursor > 0:
-        print(' '.repeat(new.len - cursor + 1))
-        print('\b'.repeat(new.len - cursor + 1))
+        print(' '.repeat(rl - cursor + 1))
+        print('\b'.repeat(rl - cursor + 1))
         print("\b \b")
-        new = new.substr(0, cursor - 2) & new.substr(cursor, new.len)
+        new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor, rl)
+        rl = new.runeLen()
         cursor -= 1
-        print(new.substr(cursor, new.len))
-        print('\b'.repeat(new.len - cursor))
+        printesc(new.runeSubstr(cursor, rl))
+        print('\b'.repeat(rl - cursor))
     of ACTION_LINED_ESC:
-      new &= c
-      print("^[".ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset())
+      escNext = true
     of ACTION_LINED_CLEAR:
-      print(' '.repeat(new.len - cursor + 1))
-      print('\b'.repeat(new.len - cursor + 1))
+      print(' '.repeat(rl - cursor + 1))
+      print('\b'.repeat(rl - cursor + 1))
       print('\b'.repeat(cursor))
       print(' '.repeat(cursor))
       print('\b'.repeat(cursor))
-      new = new.substr(cursor, new.len)
-      print(new)
-      print('\b'.repeat(new.len))
+      new = new.runeSubstr(cursor, rl)
+      rl = new.runeLen()
+      printesc(new)
+      print('\b'.repeat(rl))
       cursor = 0
     of ACTION_LINED_KILL:
-      print(' '.repeat(new.len - cursor + 1))
-      print('\b'.repeat(new.len - cursor + 1))
-      new = new.substr(0, cursor - 1)
+      print(' '.repeat(rl - cursor + 1))
+      print('\b'.repeat(rl - cursor + 1))
+      new = new.runeSubstr(0, cursor)
     of ACTION_LINED_BACK:
       if cursor > 0:
         cursor -= 1
         print("\b")
     of ACTION_LINED_FORWARD:
-      if cursor < new.len:
-        print(new[cursor])
+      if cursor < rl:
+        var rune: Rune
+        new.fastRuneAt(cursor, rune, false)
+        printesc($rune)
         cursor += 1
     of ACTION_LINED_PREV_WORD:
       while cursor > 0:
         print('\b')
         cursor -= 1
-        if new[cursor] == ' ':
+        var rune: Rune
+        new.fastRuneAt(cursor, rune, false)
+        if rune == runeSpace:
           break
     of ACTION_LINED_NEXT_WORD:
-      while cursor < new.len:
-        print(new[cursor])
+      while cursor < rl:
+        var rune: Rune
+        new.fastRuneAt(cursor, rune, false)
+        printesc($rune)
         cursor += 1
-        if cursor < new.len and new[cursor] == ' ':
-          break
+        if cursor < rl:
+          new.fastRuneAt(cursor, rune, false)
+          if rune == runeSpace:
+            break
     of ACTION_LINED_KILL_WORD:
       var chars = 0
       while cursor > chars:
         chars += 1
-        if new[cursor - chars] == ' ':
+        var rune: Rune
+        new.fastRuneAt(cursor - chars, rune, false)
+        if rune == runeSpace:
           break
       if chars > 0:
-        print(' '.repeat(new.len - cursor + 1))
-        print('\b'.repeat(new.len - cursor + 1))
+        print(' '.repeat(rl - cursor + 1))
+        print('\b'.repeat(rl - cursor + 1))
         print("\b \b".repeat(chars))
-        new = new.substr(0, cursor - 1 - chars) & new.substr(cursor, new.len)
+        new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor, rl)
+        rl = new.runeLen()
         cursor -= chars
-        print(new.substr(cursor, new.len))
-        print('\b'.repeat(new.len - cursor))
+        printesc(new.runeSubstr(cursor, rl))
+        print('\b'.repeat(rl - cursor))
     of ACTION_FEED_NEXT:
       feedNext = true
-    else:
-      print(' '.repeat(new.len - cursor + 1))
-      print('\b'.repeat(new.len - cursor + 1))
-      new = new.substr(0, cursor - 1) & c & new.substr(cursor, new.len)
-      print(new.substr(cursor, new.len))
-      print('\b'.repeat(new.len - cursor - 1))
+    elif validateUtf8(s) == -1:
+      var cs = ""
+      for c in s:
+        if not c.isControlChar():
+          cs &= c
+        elif escNext:
+          cs &= c
+          escNext = false
+      escNext = false
+      if cs.len == 0:
+        continue
+      print(' '.repeat(rl - cursor + 1))
+      print('\b'.repeat(rl - cursor + 1))
+      new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor, rl)
+      rl = new.runeLen()
+      printesc(new.runeSubstr(cursor, rl))
+      print('\b'.repeat(rl - cursor - 1))
       cursor += 1
+    else:
+      feedNext = true