diff options
author | bptato <nincsnevem662@gmail.com> | 2022-11-25 16:38:23 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-11-25 16:38:23 +0100 |
commit | aaacdc350547dd5a5d637f9a054888792781895a (patch) | |
tree | 8a9c0edff45a37a4c31de254b03016e2bfb7fb60 /src/display | |
parent | 8607bb0c1e7653c1249c40fa573f94718b4d5727 (diff) | |
download | chawan-aaacdc350547dd5a5d637f9a054888792781895a.tar.gz |
Improve status messages, fix regressions, etc
Diffstat (limited to 'src/display')
-rw-r--r-- | src/display/client.nim | 106 | ||||
-rw-r--r-- | src/display/pager.nim | 135 |
2 files changed, 119 insertions, 122 deletions
diff --git a/src/display/client.nim b/src/display/client.nim index 05a5a0fc..36761d56 100644 --- a/src/display/client.nim +++ b/src/display/client.nim @@ -36,6 +36,7 @@ import types/url type Client* = ref ClientObj ClientObj* = object + alive: bool attrs: WindowAttributes dispatcher: Dispatcher feednext: bool @@ -120,43 +121,52 @@ proc command(client: Client, src: string) = client.console.container.cursorLastLine() proc quit(client: Client, code = 0) {.jsfunc.} = - client.pager.quit() + if client.alive: + client.alive = false + client.pager.quit() quit(code) proc feedNext(client: Client) {.jsfunc.} = client.feednext = true +proc alert(client: Client, msg: string) {.jsfunc.} = + client.pager.alert(msg) + proc input(client: Client) = restoreStdin(client.console.tty.getFileHandle()) - let c = client.console.readChar() - client.s &= c - if client.pager.lineedit.isSome: - let edit = client.pager.lineedit.get - client.line = edit - if edit.escNext: - edit.escNext = false - if edit.write(client.s): - client.s = "" - else: - let action = getLinedAction(client.config, client.s) - if action == "": + while true: + let c = client.console.readChar() + client.s &= c + if client.pager.lineedit.isSome: + let edit = client.pager.lineedit.get + client.line = edit + if edit.escNext: + edit.escNext = false if edit.write(client.s): client.s = "" - else: - client.feedNext = true - elif not client.feedNext: - client.evalJSFree(action, "<command>") - if client.pager.lineedit.isNone: - client.line = nil + else: + let action = getLinedAction(client.config, client.s) + if action == "": + if edit.write(client.s): + client.s = "" + else: + client.feedNext = true + elif not client.feedNext: + client.evalJSFree(action, "<command>") + if client.pager.lineedit.isNone: + client.line = nil + if not client.feedNext: + client.pager.updateReadLine() + else: + let action = getNormalAction(client.config, client.s) + client.evalJSFree(action, "<command>") if not client.feedNext: - client.pager.updateReadLine() - else: - let action = getNormalAction(client.config, client.s) - client.evalJSFree(action, "<command>") - if not client.feedNext: - client.s = "" - else: - client.feedNext = false + client.pager.refreshStatusMsg() + if not client.feedNext: + client.s = "" + break + else: + client.feedNext = false proc setTimeout[T: JSObject|string](client: Client, handler: T, timeout = 0): int {.jsfunc.} = let id = client.timeoutid @@ -216,6 +226,15 @@ proc clearInterval(client: Client, id: int) {.jsfunc.} = let SIGWINCH {.importc, header: "<signal.h>", nodecl.}: cint proc acceptBuffers(client: Client) = + while client.pager.unreg.len > 0: + let (pid, stream) = client.pager.unreg.pop() + let fd = stream.source.getFd() + if int(fd) in client.fdmap: + client.selector.unregister(fd) + client.fdmap.del(int(fd)) + else: + client.pager.procmap.del(pid) + stream.close() var i = 0 while i < client.pager.procmap.len: let stream = client.ssock.acceptSocketStream() @@ -234,12 +253,20 @@ proc acceptBuffers(client: Client) = #TODO print an error? stream.close() +proc log(console: Console, ss: varargs[string]) {.jsfunc.} = + for i in 0..<ss.len: + console.err.write(ss[i]) + if i != ss.high: + console.err.write(' ') + console.err.write('\n') + console.err.flush() + proc inputLoop(client: Client) = let selector = client.selector selector.registerHandle(int(client.console.tty.getFileHandle()), {Read}, nil) let sigwinch = selector.registerSignal(int(SIGWINCH), nil) + client.acceptBuffers() while true: - client.acceptBuffers() let events = client.selector.select(-1) for event in events: if Read in event.events: @@ -249,13 +276,11 @@ proc inputLoop(client: Client) = else: let container = client.fdmap[event.fd] if not client.pager.handleEvent(container): - disableRawMode() - for msg in client.pager.status: - eprint msg client.quit(1) if Error in event.events: - eprint "Error", event #TODO handle errors + client.alert("Error in selected fds, check console") + client.console.log($event) if Signal in event.events: if event.fd == sigwinch: client.attrs = getWindowAttributes(client.console.tty) @@ -272,7 +297,9 @@ proc inputLoop(client: Client) = if client.pager.scommand != "": client.command(client.pager.scommand) client.pager.scommand = "" + client.pager.refreshStatusMsg() client.pager.draw() + client.acceptBuffers() #TODO this is dumb proc readFile(client: Client, path: string): string {.jsfunc.} = @@ -292,11 +319,12 @@ proc newConsole(pager: Pager, tty: File): Console = if pipe(pipefd) == -1: raise newException(Defect, "Failed to open console pipe.") let url = newURL("javascript:console.show()") - result.container = pager.readPipe0(some("text/plain"), pipefd[0], option(url)) + result.container = pager.readPipe0(some("text/plain"), pipefd[0], option(url), "Browser console") var f: File if not open(f, pipefd[1], fmWrite): raise newException(Defect, "Failed to open file for console pipe.") result.err = newFileStream(f) + result.err.writeLine("Type (M-c) console.hide() to return to buffer mode.") result.pager = pager result.tty = tty pager.registerContainer(result.container) @@ -307,12 +335,11 @@ proc dumpBuffers(client: Client) = client.acceptBuffers() for container in client.pager.containers: container.load() - for msg in client.pager.status: - eprint msg let ostream = newFileStream(stdout) for container in client.pager.containers: container.reshape(true) client.pager.drawBuffer(container, ostream) + client.pager.dumpAlerts() stdout.close() proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], dump: bool) = @@ -329,6 +356,7 @@ proc launchClient*(client: Client, pages: seq[string], ctype: Option[string], du client.selector = newSelector[Container]() client.pager.launchPager(tty) client.console = newConsole(client.pager, tty) + client.alive = true addExitProc((proc() = client.quit())) if client.config.startup != "": let s = if fileExists(client.config.startup): @@ -357,14 +385,6 @@ proc nimGCStats(client: Client): string {.jsfunc.} = proc jsGCStats(client: Client): string {.jsfunc.} = return client.jsrt.getMemoryUsage() -proc log(console: Console, ss: varargs[string]) {.jsfunc.} = - for i in 0..<ss.len: - console.err.write(ss[i]) - if i != ss.high: - console.err.write(' ') - console.err.write('\n') - console.err.flush() - proc show(console: Console) {.jsfunc.} = if console.pager.container != console.container: console.prev = console.pager.container diff --git a/src/display/pager.nim b/src/display/pager.nim index d04981a0..fdce5cc0 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -1,3 +1,4 @@ +import net import options import os import streams @@ -16,6 +17,7 @@ import io/request import io/term import io/window import ips/forkserver +import ips/socketstream import js/javascript import js/regex import types/buffersource @@ -30,7 +32,7 @@ type SEARCH_B, ISEARCH_F, ISEARCH_B Pager* = ref object - attrs: WindowAttributes + alerts: seq[string] commandMode*: bool container*: Container dispatcher*: Dispatcher @@ -42,10 +44,10 @@ type regex: Option[Regex] iregex: Option[Regex] reverseSearch: bool - status*: seq[string] - statusmsg*: FixedGrid + statusgrid*: FixedGrid tty: File procmap*: Table[Pid, Container] + unreg*: seq[(Pid, SocketStream)] icpos: CursorPosition display: FixedGrid redraw*: bool @@ -111,23 +113,15 @@ proc searchPrev(pager: Pager) {.jsfunc.} = else: pager.container.cursorNextMatch(pager.regex.get, true) -#TODO get rid of this -proc statusMode(pager: Pager) = - pager.term.setCursor(0, pager.attrs.height - 1) - pager.term.resetFormat2() - pager.term.eraseLine() - -#TODO ditto -proc setLineEdit*(pager: Pager, edit: LineEdit, mode: LineMode) = - pager.statusMode() - edit.writeStart() - pager.term.flush() +proc setLineEdit(pager: Pager, edit: LineEdit, mode: LineMode) = pager.lineedit = some(edit) pager.linemode = mode proc clearLineEdit(pager: Pager) = pager.lineedit = none(LineEdit) +func attrs(pager: Pager): WindowAttributes = pager.term.attrs + proc searchForward(pager: Pager) {.jsfunc.} = pager.setLineEdit(readLine("/", pager.attrs.width, config = pager.config, tty = pager.tty), SEARCH_F) @@ -142,16 +136,13 @@ proc isearchBackward(pager: Pager) {.jsfunc.} = pager.container.pushCursorPos() pager.setLineEdit(readLine("?", pager.attrs.width, config = pager.config, tty = pager.tty), ISEARCH_B) -func attrs*(pager: Pager): WindowAttributes = pager.term.attrs - proc newPager*(config: Config, attrs: WindowAttributes, dispatcher: Dispatcher): Pager = let pager = Pager( dispatcher: dispatcher, config: config, - attrs: attrs, display: newFixedGrid(attrs.width, attrs.height - 1), - statusmsg: newFixedGrid(attrs.width), - term: newTerminal(stdout, config) + statusgrid: newFixedGrid(attrs.width), + term: newTerminal(stdout, config, attrs) ) return pager @@ -160,15 +151,20 @@ proc launchPager*(pager: Pager, tty: File) = if tty != nil: pager.term.start(tty) +proc dumpAlerts*(pager: Pager) = + for msg in pager.alerts: + eprint msg + proc quit*(pager: Pager, code = 0) = pager.term.quit() + pager.dumpAlerts() proc clearDisplay(pager: Pager) = pager.display = newFixedGrid(pager.display.width, pager.display.height) proc buffer(pager: Pager): Container {.jsfget, inline.} = pager.container -proc refreshDisplay*(pager: Pager, container = pager.container) = +proc refreshDisplay(pager: Pager, container = pager.container) = var r: Rune var by = 0 pager.clearDisplay() @@ -219,36 +215,29 @@ proc refreshDisplay*(pager: Pager, container = pager.container) = pager.display[dls + i - startw].format = hlformat inc by -func generateStatusMessage*(pager: Pager): string = - var format = newFormat() - var w = 0 - for cell in pager.statusmsg: - result &= pager.term.processFormat(format, cell.format) - result &= cell.str - w += cell.width() - if w < pager.statusmsg.width: - result &= EL() - proc clearStatusMessage(pager: Pager) = - pager.statusmsg = newFixedGrid(pager.statusmsg.width) + pager.statusgrid = newFixedGrid(pager.statusgrid.width) proc writeStatusMessage(pager: Pager, str: string, format: Format = Format()) = pager.clearStatusMessage() var i = 0 for r in str.runes: i += r.width() - if i >= pager.statusmsg.len: - pager.statusmsg[^1].str = "$" + if i >= pager.statusgrid.len: + pager.statusgrid[^1].str = "$" break - pager.statusmsg[i].str &= r - pager.statusmsg[i].format = format + pager.statusgrid[i].str &= r + pager.statusgrid[i].format = format proc refreshStatusMsg*(pager: Pager) = let container = pager.container - if pager.status.len > 0: - pager.writeStatusMessage(pager.status[0]) - pager.status.delete(0) - elif container != nil: + if container == nil: return + if container.loadinfo != "": + pager.writeStatusMessage(container.loadinfo) + elif pager.alerts.len > 0: + pager.writeStatusMessage(pager.alerts[0]) + pager.alerts.delete(0) + else: var msg = $(container.cursory + 1) & "/" & $container.numLines & " (" & $container.atPercentOf() & "%) " & "<" & container.getTitle() & ">" if container.hovertext.len > 0: @@ -257,17 +246,6 @@ proc refreshStatusMsg*(pager: Pager) = format.reverse = true pager.writeStatusMessage(msg, format) -#TODO get rid of this -func generateStatusOutput(pager: Pager): string = - return pager.generateStatusMessage() - -#TODO ditto -proc displayStatus*(pager: Pager) = - if pager.lineedit.isNone: - pager.statusMode() - print(pager.generateStatusOutput()) - stdout.flushFile() - proc drawBuffer*(pager: Pager, container: Container, ostream: Stream) = var format = newFormat() for line in container.readLines: @@ -298,14 +276,15 @@ proc draw*(pager: Pager) = pager.term.hideCursor() if pager.redraw or pager.container != nil and pager.container.redraw: pager.refreshDisplay() - pager.term.outputGrid(pager.display) - pager.refreshStatusMsg() - pager.displayStatus() + pager.term.writeGrid(pager.display) + if pager.lineedit.isSome: + pager.term.writeGrid(pager.lineedit.get.generateOutput(), 0, pager.attrs.height - 1) + else: + pager.term.writeGrid(pager.statusgrid, 0, pager.attrs.height - 1) + pager.term.outputGrid() if pager.lineedit.isSome: - pager.statusMode() - pager.lineedit.get.writePrompt() - pager.lineedit.get.fullRedraw() - elif pager.container != nil: + pager.term.setCursor(pager.lineedit.get.getCursorX(), pager.container.attrs.height - 1) + else: pager.term.setCursor(pager.container.acursorx, pager.container.acursory) pager.term.showCursor() pager.term.flush() @@ -358,12 +337,11 @@ proc nextBuffer*(pager: Pager): bool {.jsfunc.} = return true return false -proc setStatusMessage*(pager: Pager, msg: string) = - pager.status.add(msg) - pager.refreshStatusMsg() +proc alert*(pager: Pager, msg: string) {.jsfunc.} = + pager.alerts.add(msg) proc lineInfo(pager: Pager) {.jsfunc.} = - pager.setStatusMessage(pager.container.lineInfo()) + pager.alert(pager.container.lineInfo()) proc deleteContainer(pager: Pager, container: Container) = if container.parent == nil and @@ -399,14 +377,13 @@ proc deleteContainer(pager: Pager, container: Container) = pager.setContainer(nil) container.parent = nil container.children.setLen(0) - container.istream.close() - container.ostream.close() + pager.unreg.add((container.process, SocketStream(container.istream))) pager.dispatcher.forkserver.removeChild(container.process) proc discardBuffer*(pager: Pager) {.jsfunc.} = if pager.container == nil or pager.container.parent == nil and pager.container.children.len == 0: - pager.setStatusMessage("Cannot discard last buffer!") + pager.alert("Cannot discard last buffer!") else: pager.deleteContainer(pager.container) @@ -424,10 +401,9 @@ proc toggleSource*(pager: Pager) {.jsfunc.} = pager.addContainer(container) proc windowChange*(pager: Pager, attrs: WindowAttributes) = - pager.attrs = attrs - pager.display = newFixedGrid(attrs.width, attrs.height - 1) - pager.statusmsg = newFixedGrid(attrs.width) pager.term.windowChange(attrs) + pager.display = newFixedGrid(attrs.width, attrs.height - 1) + pager.statusgrid = newFixedGrid(attrs.width) for container in pager.containers: container.windowChange(attrs) @@ -483,25 +459,25 @@ proc loadURL*(pager: Pager, url: string, ctype = none(string)) = if localurl.isSome: # attempt to load local file urls.add(localurl.get) if urls.len == 0: - pager.setStatusMessage("Invalid URL " & url) + pager.alert("Invalid URL " & url) else: let prevc = pager.container pager.gotoURL(newRequest(urls.pop()), ctype = ctype) if pager.container != prevc: pager.container.retry = urls -proc readPipe0*(pager: Pager, ctype: Option[string], fd: FileHandle, location: Option[URL]): Container = +proc readPipe0*(pager: Pager, ctype: Option[string], fd: FileHandle, location: Option[URL], title: string): Container = let source = BufferSource( t: LOAD_PIPE, fd: fd, contenttype: some(ctype.get("text/plain")), location: location.get(newURL("file://-")) ) - let container = pager.dispatcher.newBuffer(pager.config, source, ispipe = true) + let container = pager.dispatcher.newBuffer(pager.config, source, title) return container proc readPipe*(pager: Pager, ctype: Option[string], fd: FileHandle) = - let container = pager.readPipe0(ctype, fd, none(URL)) + let container = pager.readPipe0(ctype, fd, none(URL), "*pipe*") pager.addContainer(container) proc command(pager: Pager) {.jsfunc.} = @@ -612,11 +588,12 @@ proc handleEvent*(pager: Pager, container: Container): bool = if container.retry.len > 0: pager.gotoURL(newRequest(container.retry.pop()), ctype = container.contenttype) else: - pager.setStatusMessage("Couldn't load " & $container.source.location & " (error code " & $container.code & ")") + pager.alert("Couldn't load " & $container.source.location & " (error code " & $container.code & ")") if pager.container == nil: return false of SUCCESS: container.reshape() + pager.container.loadinfo = "" if container.replace != nil: container.children.add(container.replace.children) for child in container.children: @@ -634,24 +611,24 @@ proc handleEvent*(pager: Pager, container: Container): bool = pager.authorize() of REDIRECT: let redirect = container.redirect.get - pager.setStatusMessage("Redirecting to " & $redirect) + pager.alert("Redirecting to " & $redirect) pager.gotoURL(newRequest(redirect), some(pager.container.source.location), replace = pager.container) of ANCHOR: pager.addContainer(pager.dupeContainer(container, container.redirect)) of NO_ANCHOR: - pager.setStatusMessage("Couldn't find anchor " & container.redirect.get.anchor) + pager.alert("Couldn't find anchor " & container.redirect.get.anchor) of UPDATE: if container == pager.container: pager.redraw = true of READ_LINE: if container == pager.container: - pager.setLineEdit(readLine(event.prompt, pager.statusmsg.width, current = event.value, hide = event.password, config = pager.config, tty = pager.tty), BUFFER) + pager.setLineEdit(readLine(event.prompt, pager.attrs.width, current = event.value, hide = event.password, config = pager.config, tty = pager.tty), BUFFER) of OPEN: pager.gotoURL(event.request, some(container.source.location)) - of INVALID_COMMAND: - if container == pager.container: - if pager.status.len == 0: - pager.setStatusMessage("Invalid command from buffer") + of INVALID_COMMAND: discard + of STATUS: + if pager.container == container: + pager.refreshStatusMsg() of NO_EVENT: discard return true |