about summary refs log tree commit diff stats
path: root/src/display/client.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-09-08 20:07:58 +0200
committerbptato <nincsnevem662@gmail.com>2023-09-08 20:23:42 +0200
commit39e9d80a49124067edb38c4711c58d5cb790c91e (patch)
treee4c676814f13078ad77ffaa5101f852870d05809 /src/display/client.nim
parent099550625e55ad59a6ed6bef54ad0d86470cdd91 (diff)
downloadchawan-39e9d80a49124067edb38c4711c58d5cb790c91e.tar.gz
Add vi-style numeric prefixes, make gotoLine 1-based
* it is now possible to jump to the nth line by typing {n}G
* gotoLine is now 1-based, so to go to the first line you would use
  pager.gotoLine(1)
* it is now allowed to return a function from a keybinding (which will be
  subsequently executed as a regular keybinding)
Diffstat (limited to 'src/display/client.nim')
-rw-r--r--src/display/client.nim51
1 files changed, 43 insertions, 8 deletions
diff --git a/src/display/client.nim b/src/display/client.nim
index d5140de8..72a42124 100644
--- a/src/display/client.nim
+++ b/src/display/client.nim
@@ -42,10 +42,12 @@ import js/intl
 import js/javascript
 import js/module
 import js/timeout
+import js/tojs
 import types/blob
 import types/cookie
 import types/url
 import utils/opt
+import utils/twtstr
 import xhr/formdata
 import xhr/xmlhttprequest
 
@@ -63,13 +65,15 @@ type
     fdmap: Table[int, Container]
     feednext: bool
     forkserver: ForkServer
+    notnum: bool # has a non-numeric character been input already?
     jsctx: JSContext
     jsrt: JSRuntime
     line {.jsget.}: LineEdit
     loader: FileLoader
     mainproc: Pid
     pager {.jsget.}: Pager
-    s: string
+    precnum: int32 # current number prefix (when vi-numeric-prefix is true)
+    s: string # current input buffer
     selector: Selector[Container]
     ssock: ServerSocket
     store {.jsget, jsset.}: Document
@@ -190,6 +194,42 @@ proc handlePagerEvents(client: Client) =
   if container != nil:
     client.pager.handleEvents(container)
 
+proc evalAction(client: Client, action: string, arg0: int32) =
+  let ret = client.evalJS(action, "<command>")
+  let ctx = client.jsctx
+  if JS_IsFunction(ctx, ret):
+    if arg0 != 0: # no precnum
+      let arg0 = toJS(ctx, arg0)
+      JS_FreeValue(ctx, JS_Call(ctx, ret, JS_UNDEFINED, 1, addr arg0))
+      JS_FreeValue(ctx, arg0)
+    else:
+      JS_FreeValue(ctx, JS_Call(ctx, ret, JS_UNDEFINED, 0, nil))
+  JS_FreeValue(ctx, ret)
+
+# The maximum number we are willing to accept.
+# This should be fine for 32-bit signed ints (which precnum currently is).
+# We can always increase it further (e.g. by switching to uint32, uint64...) if
+# it proves to be too low.
+const MaxPrecNum = 100000000
+
+proc handleCommandInput(client: Client, c: char) =
+  if client.config.input.vi_numeric_prefix and not client.notnum:
+    if client.precnum != 0 and c == '0' or c in '1' .. '9':
+      if client.precnum < MaxPrecNum: # better ignore than eval...
+        client.precnum *= 10
+        client.precnum += cast[int32](decValue(c))
+      return
+    else:
+      client.notnum = true
+  client.s &= c
+  let action = getNormalAction(client.config, client.s)
+  client.evalAction(action, client.precnum)
+  if not client.feedNext:
+    client.precnum = 0
+    client.notnum = false
+    client.handlePagerEvents()
+    client.pager.refreshStatusMsg()
+
 proc input(client: Client) =
   restoreStdin(client.console.tty.getFileHandle())
   while true:
@@ -217,18 +257,13 @@ proc input(client: Client) =
           else:
             client.feedNext = true
         elif not client.feednext:
-          client.evalJSFree(action, "<command>")
+          client.evalAction(action, 0)
         if client.pager.lineedit.isNone:
           client.line = nil
         if not client.feedNext:
           client.pager.updateReadLine()
     else:
-      client.s &= c
-      let action = getNormalAction(client.config, client.s)
-      client.evalJSFree(action, "<command>")
-      if not client.feedNext:
-        client.handlePagerEvents()
-        client.pager.refreshStatusMsg()
+      client.handleCommandInput(c)
     if not client.feednext:
       client.s = ""
       break