diff options
author | bptato <nincsnevem662@gmail.com> | 2023-10-13 17:41:02 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-10-13 17:42:08 +0200 |
commit | 1cfe1655e1ef6f7ae69b9c87731d571e1ef3ca8c (patch) | |
tree | f62e05768e4a4205710cb9727b40b8d0cb04f632 /src | |
parent | 9ff482dd8d5b1b252e77712e9418b5b253f4bbf8 (diff) | |
download | chawan-1cfe1655e1ef6f7ae69b9c87731d571e1ef3ca8c.tar.gz |
Refactor Console
* merge dom.console & client.Console * move client-specific stuff out of Console (into callbacks when necessary)
Diffstat (limited to 'src')
-rw-r--r-- | src/html/dom.nim | 43 | ||||
-rw-r--r-- | src/html/env.nim | 6 | ||||
-rw-r--r-- | src/js/console.nim | 58 | ||||
-rw-r--r-- | src/local/client.nim | 166 | ||||
-rw-r--r-- | src/local/container.nim | 9 | ||||
-rw-r--r-- | src/local/pager.nim | 2 |
6 files changed, 168 insertions, 116 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim index 008a4e8a..3931ae69 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -17,6 +17,7 @@ import img/bitmap import img/painter import img/path import img/png +import js/console import js/domexception import js/error import js/fromjs @@ -91,7 +92,7 @@ type Window* = ref object of EventTarget attrs*: WindowAttributes - console* {.jsget.}: console + console* {.jsget.}: Console navigator* {.jsget.}: Navigator settings*: EnvironmentSettings loader*: Option[FileLoader] @@ -110,13 +111,6 @@ type MimeTypeArray* = object - # "For historical reasons, console is lowercased." - # Also, for a more practical reason: so the javascript macros don't confuse - # this and the Client console. - # TODO: merge those two - console* = ref object - err*: Stream - NamedNodeMap = ref object element: Element attrlist: seq[Attr] @@ -372,7 +366,6 @@ jsDestructor(PluginArray) jsDestructor(MimeTypeArray) jsDestructor(Window) -jsDestructor(console) jsDestructor(Element) jsDestructor(HTMLElement) jsDestructor(HTMLInputElement) @@ -2698,34 +2691,6 @@ proc fetchClassicScript(element: HTMLScriptElement, url: URL, let script = createClassicScript(source, url, options, false) element.markAsReady(ScriptResult(t: RESULT_SCRIPT, script: script)) -proc log*(console: console, ss: varargs[string]) {.jsfunc.} = - var s = "" - for i in 0..<ss.len: - s &= ss[i] - console.err.write(ss[i]) - if i != ss.high: - s &= ' ' - console.err.write(' ') - console.err.write('\n') - console.err.flush() - -proc clear*(console: console) {.jsfunc.} = - # Do nothing. By design, we do not allow buffers to clear the console. - discard - -# For now, these are the same as log(). -proc debug*(console: console, ss: varargs[string]) {.jsfunc.} = - console.log(ss) - -proc error*(console: console, ss: varargs[string]) {.jsfunc.} = - console.log(ss) - -proc info*(console: console, ss: varargs[string]) {.jsfunc.} = - console.log(ss) - -proc warn*(console: console, ss: varargs[string]) {.jsfunc.} = - console.log(ss) - proc execute*(element: HTMLScriptElement) = let document = element.document if document != element.preparationTimeDocument: @@ -3037,10 +3002,6 @@ proc jsReflectSet(ctx: JSContext, this, val: JSValue, magic: cint): JSValue {.cd element.attrulgz(entry.attrname, x.get) return JS_DupValue(ctx, val) -proc addconsoleModule*(ctx: JSContext) = - #TODO console should not have a prototype - ctx.registerType(console, nointerface = true) - func getReflectFunctions(tags: set[TagType]): seq[TabGetSet] = for tag in tags: if tag in TagReflectMap: diff --git a/src/html/env.nim b/src/html/env.nim index 49f6bba3..24e1482d 100644 --- a/src/html/env.nim +++ b/src/html/env.nim @@ -7,6 +7,7 @@ import html/dom import html/event import io/promise import js/base64 +import js/console import js/domexception import js/error import js/intl @@ -130,7 +131,7 @@ proc addScripting*(window: Window, selector: Selector[int]) = ctx.setGlobal(global, window) JS_FreeValue(ctx, global) ctx.addDOMExceptionModule() - ctx.addconsoleModule() + ctx.addConsoleModule() ctx.addNavigatorModule() ctx.addEventModule() ctx.addDOMModule() @@ -150,9 +151,10 @@ proc runJSJobs*(window: Window) = proc newWindow*(scripting: bool, selector: Selector[int], attrs: WindowAttributes, navigate: proc(url: URL) = nil, loader = none(FileLoader)): Window = + let err = newFileStream(stderr) let window = Window( attrs: attrs, - console: console(err: newFileStream(stderr)), + console: newConsole(err), navigator: Navigator(), loader: loader, settings: EnvironmentSettings( diff --git a/src/js/console.nim b/src/js/console.nim new file mode 100644 index 00000000..c4c65975 --- /dev/null +++ b/src/js/console.nim @@ -0,0 +1,58 @@ +import std/streams + +import js/javascript + +type Console* = ref object + err*: Stream + clearFun: proc() + showFun: proc() + hideFun: proc() + +jsDestructor(Console) + +proc newConsole*(err: Stream, clearFun: proc() = nil, showFun: proc() = nil, + hideFun: proc() = nil): Console = + return Console( + err: err, + clearFun: clearFun, + showFun: showFun, + hideFun: hideFun + ) + +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 clear(console: Console) {.jsfunc.} = + if console.clearFun != nil: + console.clearFun() + +# For now, these are the same as log(). +proc debug(console: Console, ss: varargs[string]) {.jsfunc.} = + console.log(ss) + +proc error(console: Console, ss: varargs[string]) {.jsfunc.} = + console.log(ss) + +proc info(console: Console, ss: varargs[string]) {.jsfunc.} = + console.log(ss) + +proc warn(console: Console, ss: varargs[string]) {.jsfunc.} = + console.log(ss) + +proc show(console: Console, ss: varargs[string]) {.jsfunc.} = + if console.showFun != nil: + console.showFun() + +proc hide(console: Console, ss: varargs[string]) {.jsfunc.} = + if console.hideFun != nil: + console.hideFun() + +proc addConsoleModule*(ctx: JSContext) = + #TODO console should not have a prototype + # "For historical reasons, console is lowercased." + ctx.registerType(Console, nointerface = true, name = "console") diff --git a/src/local/client.nim b/src/local/client.nim index 937b4a8b..788ea32f 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -26,6 +26,7 @@ import io/posixstream import io/promise import io/socketstream import js/base64 +import js/console import js/domexception import js/error import js/fromjs @@ -42,8 +43,8 @@ import local/pager import server/forkserver import types/blob import types/cookie -import types/url import types/opt +import types/url import utils/twtstr import xhr/formdata import xhr/xmlhttprequest @@ -54,10 +55,11 @@ type Client* = ref object alive: bool config {.jsget.}: Config - console {.jsget.}: Console + consoleWrapper: ConsoleWrapper fdmap: Table[int, Container] feednext: bool forkserver: ForkServer + ibuf: string jsctx: JSContext jsrt: JSRuntime loader: FileLoader @@ -66,25 +68,25 @@ type selector: Selector[int] timeouts: TimeoutState - Console = ref object - err: Stream - pager: Pager + ConsoleWrapper = object + console: Console container: Container prev: Container - ibuf: string - tty: File jsDestructor(Client) -jsDestructor(Console) -proc readChar(console: Console): char = - if console.ibuf == "": +func console(client: Client): Console {.jsfget.} = + return client.consoleWrapper.console + +proc readChar(client: Client): char = + if client.ibuf == "": try: - return console.tty.readChar() + return client.pager.tty.readChar() except EOFError: quit(1) - result = console.ibuf[0] - console.ibuf = console.ibuf.substr(1) + else: + result = client.ibuf[0] + client.ibuf.delete(0..0) proc finalize(client: Client) {.jsfin.} = if client.jsctx != nil: @@ -102,14 +104,14 @@ proc fetch[T: Request|string](client: Client, req: T, proc interruptHandler(rt: JSRuntime, opaque: pointer): cint {.cdecl.} = let client = cast[Client](opaque) - if client.console == nil or client.console.tty == nil: return + if client.console == nil or client.pager.tty == nil: return try: - let c = client.console.tty.readChar() + let c = client.pager.tty.readChar() if c == char(3): #C-c - client.console.ibuf = "" + client.ibuf = "" return 1 else: - client.console.ibuf &= c + client.ibuf &= c except IOError: discard return 0 @@ -139,14 +141,14 @@ proc command0(client: Client, src: string, filename = "<command>", if not silence: let str = fromJS[string](client.jsctx, ret) if str.isSome: - client.console.err.write(str.get & '\n') - client.console.err.flush() + client.console.log(str.get) JS_FreeValue(client.jsctx, ret) proc command(client: Client, src: string) = client.command0(src) - client.console.container.requestLines().then(proc() = - client.console.container.cursorLastLine()) + let container = client.consoleWrapper.container + container.requestLines().then(proc() = + container.cursorLastLine()) proc suspend(client: Client) {.jsfunc.} = client.pager.term.quit() @@ -223,7 +225,7 @@ proc handleCommandInput(client: Client, c: char) = proc input(client: Client) = client.pager.term.restoreStdin() while true: - let c = client.console.readChar() + let c = client.readChar() if client.pager.askpromise != nil: if c == 'y': client.pager.fulfillAsk(true) @@ -280,26 +282,19 @@ proc clearInterval(client: Client, id: int32) {.jsfunc.} = let SIGWINCH {.importc, header: "<signal.h>", nodecl.}: cint -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 showConsole(client: Client) {.jsfunc.} = + let container = client.consoleWrapper.container + if client.pager.container != container: + client.consoleWrapper.prev = client.pager.container + client.pager.setContainer(container) + container.requestLines() -proc show(console: Console) {.jsfunc.} = - if console.pager.container != console.container: - console.prev = console.pager.container - console.pager.setContainer(console.container) - console.container.requestLines() +proc hideConsole(client: Client) {.jsfunc.} = + if client.pager.container == client.consoleWrapper.container: + client.pager.setContainer(client.consoleWrapper.prev) -proc hide(console: Console) {.jsfunc.} = - if console.pager.container == console.container: - console.pager.setContainer(console.prev) - -proc buffer(console: Console): Container {.jsfget.} = - return console.container +proc consoleBuffer(client: Client): Container {.jsfget.} = + return client.consoleWrapper.container proc acceptBuffers(client: Client) = while client.pager.unreg.len > 0: @@ -324,7 +319,7 @@ proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize_t): cint {. importc: "setvbuf", header: "<stdio.h>", tags: [].} proc handleRead(client: Client, fd: int) = - if client.console.tty != nil and fd == client.console.tty.getFileHandle(): + if client.pager.tty != nil and fd == client.pager.tty.getFileHandle(): client.input() client.handlePagerEvents() elif fd == client.forkserver.estream.fd: @@ -360,7 +355,7 @@ proc flushConsole*(client: Client) {.jsfunc.} = client.handleRead(client.forkserver.estream.fd) proc handleError(client: Client, fd: int) = - if client.console.tty != nil and fd == client.console.tty.getFileHandle(): + if client.pager.tty != nil and fd == client.pager.tty.getFileHandle(): #TODO do something here... stderr.write("Error in tty\n") quit(1) @@ -378,21 +373,21 @@ proc handleError(client: Client, fd: int) = else: if fd in client.fdmap: let container = client.fdmap[fd] - if container != client.console.container: + if container != client.consoleWrapper.container: client.console.log("Error in buffer", $container.location) else: - client.console.container = nil + client.consoleWrapper.container = nil client.selector.unregister(fd) client.fdmap.del(fd) - if client.console.container != nil: - client.console.show() + if client.consoleWrapper.container != nil: + client.showConsole() else: doAssert false proc inputLoop(client: Client) = let selector = client.selector - discard c_setvbuf(client.console.tty, nil, IONBF, 0) - selector.registerHandle(int(client.console.tty.getFileHandle()), {Read}, 0) + discard c_setvbuf(client.pager.tty, nil, IONBF, 0) + selector.registerHandle(int(client.pager.tty.getFileHandle()), {Read}, 0) let sigwinch = selector.registerSignal(int(SIGWINCH), 0) while true: let events = client.selector.select(-1) @@ -403,14 +398,14 @@ proc inputLoop(client: Client) = client.handleError(event.fd) if Signal in event.events: assert event.fd == sigwinch - let attrs = getWindowAttributes(client.console.tty) + let attrs = getWindowAttributes(client.pager.tty) client.pager.windowChange(attrs) if selectors.Event.Timer in event.events: let r = client.timeouts.runTimeoutFd(event.fd) assert r client.runJSJobs() - client.console.container.requestLines().then(proc() = - client.console.container.cursorLastLine()) + client.pager.container.requestLines().then(proc() = + client.pager.container.cursorLastLine()) client.loader.unregistered.setLen(0) client.acceptBuffers() if client.pager.scommand != "": @@ -483,27 +478,51 @@ proc readFile(client: Client, path: string): string {.jsfunc.} = proc writeFile(client: Client, path: string, content: string) {.jsfunc.} = writeFile(path, content) -proc newConsole(pager: Pager, tty: File): Console = - new(result) - if tty != nil: +const ConsoleTitle = "Browser Console" + +proc addConsole(pager: Pager, interactive: bool, clearFun, showFun, hideFun: + proc()): ConsoleWrapper = + if interactive: var pipefd: array[0..1, cint] 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"), CHARSET_UNKNOWN, - pipefd[0], option(url.get(nil)), "Browser console", - canreinterpret = false) - 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.err.flush() - result.pager = pager - result.tty = tty - pager.registerContainer(result.container) + let url = newURL("javascript:console.show()").get + let container = pager.readPipe0(some("text/plain"), CHARSET_UNKNOWN, + pipefd[0], some(url), ConsoleTitle, canreinterpret = false) + let err = newPosixStream(pipefd[1]) + err.writeLine("Type (M-c) console.hide() to return to buffer mode.") + err.flush() + pager.registerContainer(container) + let console = newConsole( + err, + clearFun = clearFun, + showFun = showFun, + hideFun = hideFun + ) + return ConsoleWrapper( + console: console, + container: container + ) else: - result.err = newFileStream(stderr) + let err = newFileStream(stderr) + return ConsoleWrapper( + console: newConsole(err) + ) + +proc clearConsole(client: Client) = + var pipefd: array[0..1, cint] + if pipe(pipefd) == -1: + raise newException(Defect, "Failed to open console pipe.") + let url = newURL("javascript:console.show()").get + let pager = client.pager + let replacement = pager.readPipe0(some("text/plain"), CHARSET_UNKNOWN, + pipefd[0], some(url), ConsoleTitle, canreinterpret = false) + replacement.replace = client.consoleWrapper.container + pager.registerContainer(replacement) + client.consoleWrapper.container = replacement + let console = client.consoleWrapper.console + console.err.close() + console.err = newPosixStream(pipefd[1]) proc dumpBuffers(client: Client) = client.headlessLoop() @@ -540,7 +559,14 @@ proc launchClient*(client: Client, pages: seq[string], selector.unregister(fd) client.selector = selector client.pager.launchPager(tty) - client.console = newConsole(client.pager, tty) + let clearFun = proc() = + client.clearConsole() + let showFun = proc() = + client.showConsole() + let hideFun = proc() = + client.hideConsole() + client.consoleWrapper = addConsole(client.pager, interactive = tty != nil, + clearFun, showFun, hideFun) #TODO passing console.err here makes it impossible to change it later. maybe # better associate it with jsctx client.timeouts = newTimeoutState(client.selector, client.jsctx, @@ -597,6 +623,7 @@ func line(client: Client): LineEdit {.jsfget.} = proc addJSModules(client: Client, ctx: JSContext) = ctx.addDOMExceptionModule() + ctx.addConsoleModule() ctx.addCookieModule() ctx.addURLModule() ctx.addEventModule() @@ -643,6 +670,5 @@ proc newClient*(config: Config, forkserver: ForkServer, mainproc: Pid): Client = jsctx.registerType(Client, asglobal = true) setGlobal(jsctx, global, client) JS_FreeValue(jsctx, global) - jsctx.registerType(Console) client.addJSModules(jsctx) return client diff --git a/src/local/container.nim b/src/local/container.nim index 1d13ef68..a04b8b88 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -367,8 +367,13 @@ proc setNumLines(container: Container, lines: int, finish = false) = container.updateCursor() container.triggerEvent(STATUS) -proc requestLines*(container: Container, w = container.lineWindow): auto {.discardable.} = - return container.iface.getLines(w).then(proc(res: tuple[numLines: int, lines: seq[SimpleFlexibleLine]]) = +proc requestLines*(container: Container, w = container.lineWindow): EmptyPromise + {.discardable.} = + if container.iface == nil: + let res = EmptyPromise() + res.resolve() + return res + return container.iface.getLines(w).then(proc(res: GetLinesResult) = container.lines.setLen(w.len) container.lineshift = w.a for y in 0 ..< min(res.lines.len, w.len): diff --git a/src/local/pager.nim b/src/local/pager.nim index 1333e32f..3630f08f 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -81,7 +81,7 @@ type siteconf: seq[SiteConfig] statusgrid*: FixedGrid term*: Terminal - tty: File + tty*: File #TODO this is already referenced in term, remove. unreg*: seq[(Pid, SocketStream)] urimethodmap: URIMethodMap username: string |