diff options
author | bptato <nincsnevem662@gmail.com> | 2023-09-09 01:44:58 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-09-09 01:52:24 +0200 |
commit | 11219c7d21c8ec7f438d52de063c4ac25f84711d (patch) | |
tree | 36b1308d747cc467dc7dad2713eb2cc546390026 | |
parent | 6c435aa6c186c2fe7ec72cee2a5ae2f1ae8b22a7 (diff) | |
download | chawan-11219c7d21c8ec7f438d52de063c4ac25f84711d.tar.gz |
add extern, refactor some term functions
* Add an extern() call. Maybe it should be defined on client. It certainly should accept a dictionary instead of the enum type we use now. Perhaps it should return the error code? I'll leave it undocumented until I figure this out. * Refactor enableRawMode, unblockStdin, etc. so that they operate on the term object instead of global state. * Move editor to a separate folder, and factor out runprocess into a different module.
-rw-r--r-- | res/config.toml | 5 | ||||
-rw-r--r-- | src/buffer/container.nim | 5 | ||||
-rw-r--r-- | src/display/client.nim | 8 | ||||
-rw-r--r-- | src/display/pager.nim | 30 | ||||
-rw-r--r-- | src/display/term.nim | 101 | ||||
-rw-r--r-- | src/extern/editor.nim (renamed from src/ips/editor.nim) | 20 | ||||
-rw-r--r-- | src/extern/runproc.nim | 29 |
7 files changed, 127 insertions, 71 deletions
diff --git a/res/config.toml b/res/config.toml index 14b746fd..d84f9b69 100644 --- a/res/config.toml +++ b/res/config.toml @@ -131,6 +131,11 @@ C-w = ''' config.search.wrap = !config.search.wrap; pager.alert("Wrap search " + (config.search.wrap ? "on" : "off")); ''' +M-y = ''' +pager.extern('printf \'%s\\n\' "$CHA_URL" | xsel -bi', "no-suspend-setenv") ? + pager.alert("Copied URL to clipboard.") : + pager.alert("Failed to copy URL to clipboard. (Is xsel installed?)"); +''' [line] C-m = 'line.submit()' diff --git a/src/buffer/container.nim b/src/buffer/container.nim index 2a000bbf..edba490f 100644 --- a/src/buffer/container.nim +++ b/src/buffer/container.nim @@ -24,6 +24,8 @@ import types/url import utils/mimeguess import utils/twtstr +import chakasu/charset + type CursorPosition* = object cursorx*: int @@ -124,6 +126,9 @@ proc newBuffer*(forkserver: ForkServer, mainproc: Pid, config: BufferConfig, ) ) +func charset*(container: Container): Charset = + return container.source.charset + func contentType*(container: Container): Option[string] {.jsfget.} = return container.source.contenttype diff --git a/src/display/client.nim b/src/display/client.nim index 72a42124..c8a4ab1f 100644 --- a/src/display/client.nim +++ b/src/display/client.nim @@ -132,16 +132,14 @@ proc runJSJobs(client: Client) = client.jsrt.runJSJobs(client.console.err) proc evalJS(client: Client, src, filename: string, module = false): JSValue = - if client.console.tty != nil: - unblockStdin(client.console.tty.getFileHandle()) + client.pager.term.unblockStdin() let flags = if module: JS_EVAL_TYPE_MODULE else: JS_EVAL_TYPE_GLOBAL result = client.jsctx.eval(src, filename, flags) client.runJSJobs() - if client.console.tty != nil: - restoreStdin(client.console.tty.getFileHandle()) + client.pager.term.restoreStdin() proc evalJSFree(client: Client, src, filename: string) = JS_FreeValue(client.jsctx, client.evalJS(src, filename)) @@ -231,7 +229,7 @@ proc handleCommandInput(client: Client, c: char) = client.pager.refreshStatusMsg() proc input(client: Client) = - restoreStdin(client.console.tty.getFileHandle()) + client.pager.term.restoreStdin() while true: let c = client.console.readChar() if client.pager.askpromise != nil: diff --git a/src/display/pager.nim b/src/display/pager.nim index 9196b4f9..0229c706 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -17,6 +17,8 @@ import config/config import config/mailcap import config/mimetypes import display/term +import extern/editor +import extern/runproc import io/connecterror import io/lineedit import io/loader @@ -24,7 +26,6 @@ import io/promise import io/request import io/tempfile import io/window -import ips/editor import ips/forkserver import ips/socketstream import js/javascript @@ -831,6 +832,33 @@ proc reload(pager: Pager) {.jsfunc.} = pager.gotoURL(newRequest(pager.container.source.location), none(URL), pager.container.contenttype, replace = pager.container) +proc setEnvVars(pager: Pager) {.jsfunc.} = + try: + putEnv("CHA_URL", $pager.container.location) + putEnv("CHA_CHARSET", $pager.container.charset) + except OSError: + pager.alert("Warning: failed to set some environment variables") + +type ExternType = enum + SUSPEND_SETENV = "suspend-setenv" + SUSPEND_SETENV_WAIT = "suspend-setenv-wait" + SUSPEND_NO_SETENV = "suspend-no-setenv" + SUSPEND_NO_SETENV_WAIT = "suspend-no-setenv-wait" + NO_SUSPEND_SETENV = "no-suspend-setenv" + NO_SUSPEND_NO_SETENV = "no-suspend-no-setenv" + +#TODO this could be handled much better. +# * suspend, setenv, wait as dict flags +# * retval as int? +proc extern(pager: Pager, cmd: string, t = SUSPEND_SETENV): bool {.jsfunc.} = + if t in {SUSPEND_SETENV, SUSPEND_SETENV_WAIT, NO_SUSPEND_SETENV}: + pager.setEnvVars() + if t in {NO_SUSPEND_SETENV, NO_SUSPEND_NO_SETENV}: + return runProcess(cmd) + else: + return runProcess(pager.term, cmd, + t in {SUSPEND_SETENV_WAIT, SUSPEND_NO_SETENV_WAIT}) + proc authorize(pager: Pager) = pager.setLineEdit("Username: ", USERNAME) diff --git a/src/display/term.nim b/src/display/term.nim index c9981f42..233c9cd4 100644 --- a/src/display/term.nim +++ b/src/display/term.nim @@ -1,8 +1,10 @@ import options import os +import posix import streams import tables import terminal +import termios import unicode import bindings/termcap @@ -58,6 +60,10 @@ type tc: Termcap tname: string set_title: bool + stdin_unblocked: bool + orig_flags: cint + orig_flags2: cint + orig_termios: Termios func hascap(term: Terminal, c: TermcapCap): bool = term.tc.caps[c] != nil func cap(term: Terminal, c: TermcapCap): string = $term.tc.caps[c] @@ -138,6 +144,11 @@ proc clearDisplay(term: Terminal): string = proc isatty(term: Terminal): bool = term.infile != nil and term.infile.isatty() and term.outfile.isatty() +proc anyKey*(term: Terminal) = + if term.isatty(): + term.outfile.write("[Hit any key]") + discard term.infile.readChar() + proc resetFormat(term: Terminal): string = when termcap_found: if term.isatty(): @@ -556,60 +567,51 @@ proc outputGrid*(term: Terminal) = proc clearCanvas*(term: Terminal) = term.cleared = false -when defined(posix): - import posix - import termios - - # see https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html - var orig_termios: Termios - var stdin_fileno: FileHandle - proc disableRawMode() {.noconv.} = - discard tcSetAttr(stdin_fileno, TCSAFLUSH, addr orig_termios) - - proc enableRawMode(fileno: FileHandle) = - stdin_fileno = fileno - discard tcGetAttr(fileno, addr orig_termios) - var raw = orig_termios - raw.c_iflag = raw.c_iflag and not (BRKINT or ICRNL or INPCK or ISTRIP or IXON) - raw.c_oflag = raw.c_oflag and not (OPOST) - raw.c_cflag = raw.c_cflag or CS8 - raw.c_lflag = raw.c_lflag and not (ECHO or ICANON or ISIG or IEXTEN) - discard tcSetAttr(fileno, TCSAFLUSH, addr raw) - - var orig_flags: cint - var stdin_unblocked = false - proc unblockStdin*(fileno: FileHandle) = - orig_flags = fcntl(fileno, F_GETFL, 0) - let flags = orig_flags or O_NONBLOCK - discard fcntl(fileno, F_SETFL, flags) - stdin_unblocked = true - - proc restoreStdin*(fileno: FileHandle) = - if stdin_unblocked: - discard fcntl(fileno, F_SETFL, orig_flags) - stdin_unblocked = false -else: - proc disableRawMode() = - discard - - proc enableRawMode(fileno: FileHandle) = - discard - - proc unblockStdin*(): cint = - discard - - proc restoreStdin*(flags: cint) = - discard +# see https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html +proc disableRawMode(term: Terminal) = + let fd = term.infile.getFileHandle() + discard tcSetAttr(fd, TCSAFLUSH, addr term.orig_termios) + +proc enableRawMode(term: Terminal) = + let fd = term.infile.getFileHandle() + discard tcGetAttr(fd, addr term.orig_termios) + var raw = term.orig_termios + raw.c_iflag = raw.c_iflag and not (BRKINT or ICRNL or INPCK or ISTRIP or IXON) + raw.c_oflag = raw.c_oflag and not (OPOST) + raw.c_cflag = raw.c_cflag or CS8 + raw.c_lflag = raw.c_lflag and not (ECHO or ICANON or ISIG or IEXTEN) + discard tcSetAttr(fd, TCSAFLUSH, addr raw) + +proc unblockStdin*(term: Terminal) = + if term.isatty(): + let fd = term.infile.getFileHandle() + term.orig_flags = fcntl(fd, F_GETFL, 0) + let flags = term.orig_flags or O_NONBLOCK + discard fcntl(fd, F_SETFL, flags) + term.stdin_unblocked = true + +proc restoreStdin*(term: Terminal) = + if term.stdin_unblocked: + let fd = term.infile.getFileHandle() + discard fcntl(fd, F_SETFL, term.orig_flags) + term.stdin_unblocked = false proc quit*(term: Terminal) = if term.isatty(): - disableRawMode() + term.disableRawMode() if term.smcup: term.write(term.disableAltScreen()) else: term.write(term.cursorGoto(0, term.attrs.height - 1)) term.showCursor() term.cleared = false + if term.stdin_unblocked: + let fd = term.infile.getFileHandle() + term.orig_flags2 = fcntl(fd, F_GETFL, 0) + discard fcntl(fd, F_SETFL, term.orig_flags2 and (not O_NONBLOCK)) + term.stdin_unblocked = false + else: + term.orig_flags2 = -1 term.flush() when termcap_found: @@ -651,14 +653,19 @@ proc detectTermAttributes(term: Terminal) = proc start*(term: Terminal, infile: File) = term.infile = infile if term.isatty(): - enableRawMode(infile.getFileHandle()) + term.enableRawMode() term.detectTermAttributes() if term.smcup: term.write(term.enableAltScreen()) proc restart*(term: Terminal) = if term.isatty(): - enableRawMode(term.infile.getFileHandle()) + term.enableRawMode() + if term.orig_flags2 != -1: + let fd = term.infile.getFileHandle() + discard fcntl(fd, F_SETFL, term.orig_flags2) + term.orig_flags2 = 0 + term.stdin_unblocked = true if term.smcup: term.write(term.enableAltScreen()) diff --git a/src/ips/editor.nim b/src/extern/editor.nim index 19ba965a..58f3d199 100644 --- a/src/ips/editor.nim +++ b/src/extern/editor.nim @@ -1,8 +1,8 @@ import os -import posix import config/config import display/term +import extern/runproc import io/tempfile func formatEditorName(editor, file: string, line: int): string = @@ -31,9 +31,6 @@ func formatEditorName(editor, file: string, line: int): string = result &= ' ' result &= file -proc c_system(cmd: cstring): cint {. - importc: "system", header: "<stdlib.h>".} - proc openEditor*(term: Terminal, config: Config, file: string, line = 1): bool = var editor = config.external.editor if editor == "": @@ -41,20 +38,7 @@ proc openEditor*(term: Terminal, config: Config, file: string, line = 1): bool = if editor == "": editor = "vi %s +%d" let cmd = formatEditorName(editor, file, line) - term.quit() - let wstatus = c_system(cstring(cmd)) - if wstatus == -1: - result = false - else: - result = WIFEXITED(wstatus) and WEXITSTATUS(wstatus) == 0 - if not result: - # Hack. - #TODO this is a very bad idea, e.g. say the editor is writing into the - # file, then receives SIGINT, now the file is corrupted but Chawan will - # happily read it as if nothing happened. - # We should find a proper solution for this. - result = WIFSIGNALED(wstatus) and WTERMSIG(wstatus) == SIGINT - term.restart() + return runProcess(term, cmd) proc openInEditor*(term: Terminal, config: Config, input: var string): bool = try: diff --git a/src/extern/runproc.nim b/src/extern/runproc.nim new file mode 100644 index 00000000..c49d98db --- /dev/null +++ b/src/extern/runproc.nim @@ -0,0 +1,29 @@ +import posix + +import display/term + +proc c_system(cmd: cstring): cint {. + importc: "system", header: "<stdlib.h>".} + +# Run process (without suspending the terminal controller). +proc runProcess*(cmd: string): bool = + let wstatus = c_system(cstring(cmd)) + if wstatus == -1: + result = false + else: + result = WIFEXITED(wstatus) and WEXITSTATUS(wstatus) == 0 + if not result: + # Hack. + #TODO this is a very bad idea, e.g. say the editor is writing into the + # file, then receives SIGINT, now the file is corrupted but Chawan will + # happily read it as if nothing happened. + # We should find a proper solution for this. + result = WIFSIGNALED(wstatus) and WTERMSIG(wstatus) == SIGINT + +# Run process (and suspend the terminal controller). +proc runProcess*(term: Terminal, cmd: string, wait = false): bool = + term.quit() + result = runProcess(cmd) + if wait: + term.anyKey() + term.restart() |