diff options
author | bptato <nincsnevem662@gmail.com> | 2022-09-12 00:30:21 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-09-12 00:30:21 +0200 |
commit | 51ea622d58bfca19212fac1800cfb033bb85ec39 (patch) | |
tree | b75891690f67b190c60584751f2a30c96f342fdc /src/client.nim | |
parent | e38402dfa1bbc33db6b9d9736517eb45533d595c (diff) | |
download | chawan-51ea622d58bfca19212fac1800cfb033bb85ec39.tar.gz |
Add JS binding generation
Diffstat (limited to 'src/client.nim')
-rw-r--r-- | src/client.nim | 564 |
1 files changed, 0 insertions, 564 deletions
diff --git a/src/client.nim b/src/client.nim deleted file mode 100644 index eb48a30a..00000000 --- a/src/client.nim +++ /dev/null @@ -1,564 +0,0 @@ -import options -import os -import streams -import terminal -import unicode - -import css/sheet -import config/config -import io/buffer -import io/cell -import io/lineedit -import io/loader -import io/request -import io/term -import js/javascript -import js/regex -import types/url -import utils/twtstr - -type - Client* = ref ClientObj - ClientObj = object - buffer: Buffer - feednext: bool - s: string - iserror: bool - errormessage: string - userstyle: CSSStylesheet - loader: FileLoader - jsrt: JSRuntime - jsctx: JSContext - regex: Option[Regex] - revsearch: bool - needsauth: bool - redirecturl: Option[Url] - - ActionError = object of IOError - LoadError = object of ActionError - InterruptError = object of LoadError - -proc statusMode(client: Client) = - print(HVP(client.buffer.height + 1, 1)) - print(EL()) - -proc js_console_log(ctx: JSContext, this: JSValue, argc: int, argv: ptr JSValue): JSValue {.cdecl.} = - let opaque = ctx.getOpaque() - for i in 0..<argc: - let arg = getJSObject(ctx, argv, i) - if i != 0: - opaque.err &= ' ' - let str = arg.toString() - if str.isnone: - return JS_EXCEPTION - opaque.err &= str.get - opaque.err &= '\n' - return JS_UNDEFINED - -proc `=destroy`(client: var ClientObj) = - if client.jsctx != nil: - free(client.jsctx) - if client.jsrt != nil: - free(client.jsrt) - -proc newClient*(): Client = - new(result) - result.loader = newFileLoader() - let rt = newJSRuntime() - let ctx = rt.newJSContext() - result.jsrt = rt - result.jsctx = ctx - var global = ctx.getGlobalObject() - let console = newJSObject(result.jsctx) - console.setFunctionProperty("log", js_console_log) - global.setProperty("console", console) - free(global) - -proc loadError(s: string) = - raise newException(LoadError, s) - -proc actionError(s: string) = - raise newException(ActionError, s) - -proc interruptError() = - raise newException(InterruptError, "Interrupted") - -proc addBuffer(client: Client) = - if client.buffer == nil: - client.buffer = newBuffer() - else: - let oldnext = client.buffer.next - client.buffer.next = newBuffer() - if oldnext != nil: - oldnext.prev = client.buffer.next - client.buffer.next.prev = client.buffer - client.buffer.next.next = oldnext - client.buffer = client.buffer.next - client.buffer.loader = client.loader - client.buffer.userstyle = client.userstyle - client.buffer.markcolor = gconfig.markcolor - -proc prevBuffer(client: Client) = - if client.buffer.prev != nil: - client.buffer = client.buffer.prev - client.buffer.redraw = true - -proc nextBuffer(client: Client) = - if client.buffer.next != nil: - client.buffer = client.buffer.next - client.buffer.redraw = true - -proc discardBuffer(buffer: Buffer) = - if buffer.next != nil: - if buffer.sourcepair != nil: - buffer.sourcepair.sourcepair = nil - buffer.next.prev = buffer.prev - buffer.redraw = true - elif buffer.prev != nil: - if buffer.sourcepair != nil: - buffer.sourcepair.sourcepair = nil - buffer.prev.next = buffer.next - buffer.redraw = true - -proc discardBuffer(client: Client) = - if client.buffer.next != nil: - if client.buffer.sourcepair != nil: - client.buffer.sourcepair.sourcepair = nil - client.buffer.next.prev = client.buffer.prev - client.buffer = client.buffer.next - client.buffer.redraw = true - elif client.buffer.prev != nil: - if client.buffer.sourcepair != nil: - client.buffer.sourcepair.sourcepair = nil - client.buffer.prev.next = client.buffer.next - client.buffer = client.buffer.prev - client.buffer.redraw = true - else: - actionError("Can't discard last buffer!") - -proc setupBuffer(client: Client) = - let buffer = client.buffer - buffer.load() - buffer.render() - buffer.gotoAnchor() - buffer.redraw = true - -proc dupeBuffer(client: Client, location = none(Url)) = - let prev = client.buffer - client.addBuffer() - client.buffer.contenttype = prev.contenttype - client.buffer.ispipe = prev.ispipe - client.buffer.istream = newStringStream(prev.source) - if location.issome: - client.buffer.location = location.get - else: - client.buffer.location = prev.location - client.buffer.document = prev.document - client.setupBuffer() - -proc readPipe(client: Client, ctype: string) = - client.addBuffer() - client.buffer.contenttype = if ctype != "": ctype else: "text/plain" - client.buffer.ispipe = true - client.buffer.istream = newFileStream(stdin) - const url = parseUrl("file://-").get - client.buffer.location = url - client.buffer.load() - #TODO is this portable at all? - if reopen(stdin, "/dev/tty", fmReadWrite): - client.setupBuffer() - else: - client.buffer.drawBuffer() - -# Load request in a new buffer. -var g_client: Client -proc gotoUrl(client: Client, request: Request, prevurl = none(Url), force = false, ctype = "") = - setControlCHook(proc() {.noconv.} = - raise newException(InterruptError, "Interrupted")) - if force or prevurl.isnone or not prevurl.get.equals(request.url, true) or - prevurl.get.equals(request.url) or request.httpmethod != HTTP_GET: - let page = client.loader.doRequest(request) - client.needsauth = page.status == 401 # Unauthorized - client.redirecturl = page.redirect - if page.body != nil: - client.addBuffer() - g_client = client - setControlCHook(proc() {.noconv.} = - if g_client.buffer.prev != nil or g_client.buffer.next != nil: - g_client.discardBuffer() - interruptError()) - client.buffer.contenttype = if ctype != "": ctype else: page.contenttype - client.buffer.istream = page.body - client.buffer.location = request.url - client.setupBuffer() - else: - loadError("Couldn't load " & $request.url & " (" & $page.res & ")") - elif client.buffer != nil and prevurl.issome and prevurl.get.equals(request.url, true): - if client.buffer.hasAnchor(request.url.anchor): - client.dupeBuffer(request.url.some) - else: - loadError("Couldn't find anchor " & request.url.anchor) - -# Relative gotoUrl: either to prevurl, or if that's none, client.buffer.url. -proc gotoUrl(client: Client, url: string, prevurl = none(Url), force = false, ctype = "") = - var prevurl = prevurl - if prevurl.isnone and client.buffer != nil: - prevurl = client.buffer.location.some - let newurl = parseUrl(url, prevurl) - if newurl.isnone: - loadError("Invalid URL " & url) - client.gotoUrl(newRequest(newurl.get), prevurl, force, ctype) - -# When the user has passed a partial URL as an argument, they might've meant -# either: -# * file://$PWD/<file> -# * https://<url> -# So we attempt to load both, and see what works. -# (TODO: make this optional) -proc loadUrl(client: Client, url: string, ctype = "") = - let firstparse = parseUrl(url) - if firstparse.issome: - client.gotoUrl(newRequest(firstparse.get), none(Url), true, ctype) - else: - let cdir = parseUrl("file://" & getCurrentDir() & DirSep) - try: - # attempt to load local file - client.gotoUrl(url, cdir, true, ctype) - except LoadError: - try: - # attempt to load local file (this time percent encoded) - client.gotoUrl(percentEncode(url, LocalPathPercentEncodeSet), cdir, true, ctype) - except LoadError: - # attempt to load remote page - client.gotoUrl("https://" & url, none(Url), true, ctype) - -# Reload the page in a new buffer, then kill the previous buffer. -proc reloadPage(client: Client) = - let buf = client.buffer - client.gotoUrl(newRequest(client.buffer.location), none(Url), true, client.buffer.contenttype) - discardBuffer(buf) - -# Open a URL prompt and visit the specified URL. -proc changeLocation(client: Client) = - let buffer = client.buffer - var url = buffer.location.serialize(true) - client.statusMode() - let status = readLine("URL: ", url, buffer.width) - if status: - client.loadUrl(url) - -proc click(client: Client) = - let req = client.buffer.click() - if req.issome: - client.gotoUrl(req.get, client.buffer.location.some) - -proc toggleSource*(client: Client) = - let buffer = client.buffer - if buffer.sourcepair != nil: - client.buffer = buffer.sourcepair - client.buffer.redraw = true - else: - client.addBuffer() - client.buffer.sourcepair = client.buffer.prev - client.buffer.sourcepair.sourcepair = client.buffer - client.buffer.source = client.buffer.prev.source - client.buffer.streamclosed = true - client.buffer.location = client.buffer.sourcepair.location - client.buffer.ispipe = client.buffer.sourcepair.ispipe - let prevtype = client.buffer.prev.contenttype - if prevtype == "text/html": - client.buffer.contenttype = "text/plain" - else: - client.buffer.contenttype = "text/html" - client.setupBuffer() - -proc command(client: Client) = - var iput: string - client.statusMode() - let status = readLine("COMMAND: ", iput, client.buffer.width) - if status and iput.len > 0: - let ret = client.jsctx.eval(iput, "<stdin>", JS_EVAL_TYPE_GLOBAL) - let opaque = client.jsctx.getOpaque() - if ret.isException(): - let ex = client.jsctx.getException() - let str = ex.toString() - if str.issome: - opaque.err &= str.get & '\n' - var stack = ex.getProperty("stack") - if not stack.isUndefined(): - let str = stack.toString() - if str.issome: - opaque.err &= str.get & '\n' - free(stack) - free(ex) - else: - let str = ret.toString() - if str.issome: - opaque.err &= str.get & '\n' - free(ret) - client.addBuffer() - g_client = client - setControlCHook(proc() {.noconv.} = - if g_client.buffer.prev != nil or g_client.buffer.next != nil: - g_client.discardBuffer() - interruptError()) - client.buffer.istream = newStringStream(opaque.err) - client.buffer.contenttype = "text/plain" - client.setupBuffer() - -proc searchNext(client: Client) = - if client.regex.issome: - if not client.revsearch: - discard client.buffer.cursorNextMatch(client.regex.get) - else: - discard client.buffer.cursorPrevMatch(client.regex.get) - -proc searchPrev(client: Client) = - if client.regex.issome: - if not client.revsearch: - discard client.buffer.cursorPrevMatch(client.regex.get) - else: - discard client.buffer.cursorNextMatch(client.regex.get) - -proc search(client: Client) = - client.statusMode() - var iput: string - let status = readLine("/", iput, client.buffer.width) - if status: - if iput.len != 0: - client.regex = compileSearchRegex(iput) - client.revsearch = false - client.searchNext() - -proc searchBack(client: Client) = - client.statusMode() - var iput: string - let status = readLine("?", iput, client.buffer.width) - if status: - if iput.len != 0: - client.regex = compileSearchRegex(iput) - client.revsearch = true - client.searchNext() - -proc isearch(client: Client) = - client.statusMode() - var iput: string - let cpos = client.buffer.cpos - var mark: Mark - template del_mark() = - if mark != nil: - client.buffer.removeMark(mark) - - let status = readLine("/", iput, client.buffer.width, {}, false, (proc(state: var LineState): bool = - del_mark - let regex = compileSearchRegex($state.news) - client.buffer.cpos = cpos - if regex.issome: - let match = client.buffer.cursorNextMatch(regex.get) - if match.success: - mark = client.buffer.addMark(match.x, match.y, match.str.width()) - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - else: - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - return true - false - )) - - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - if status: - client.regex = compileSearchRegex(iput) - else: - client.buffer.cpos = cpos - -proc isearchBack(client: Client) = - client.statusMode() - var iput: string - let cpos = client.buffer.cpos - var mark: Mark - template del_mark() = - if mark != nil: - client.buffer.removeMark(mark) - let status = readLine("?", iput, client.buffer.width, {}, false, (proc(state: var LineState): bool = - del_mark - let regex = compileSearchRegex($state.news) - client.buffer.cpos = cpos - if regex.issome: - let match = client.buffer.cursorPrevMatch(regex.get) - if match.success: - mark = client.buffer.addMark(match.x, match.y, match.str.width()) - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - else: - del_mark - client.buffer.redraw = true - client.buffer.refreshBuffer(true) - print(HVP(client.buffer.height + 1, 2)) - print(SGR()) - return true - false - )) - del_mark - client.buffer.redraw = true - if status: - client.regex = compileSearchRegex(iput) - else: - client.buffer.cpos = cpos - -proc quit(client: Client) = - eraseScreen() - print(HVP(0, 0)) - quit(0) - -proc input(client: Client) = - let buffer = client.buffer - if not client.feednext: - client.s = "" - else: - client.feednext = false - let c = getch() - client.s &= c - let action = getNormalAction(client.s) - case action - of ACTION_QUIT: client.quit() - of ACTION_CURSOR_LEFT: buffer.cursorLeft() - of ACTION_CURSOR_DOWN: buffer.cursorDown() - of ACTION_CURSOR_UP: buffer.cursorUp() - of ACTION_CURSOR_RIGHT: buffer.cursorRight() - of ACTION_CURSOR_LINEBEGIN: buffer.cursorLineBegin() - of ACTION_CURSOR_LINEEND: buffer.cursorLineEnd() - of ACTION_CURSOR_NEXT_WORD: buffer.cursorNextWord() - of ACTION_CURSOR_PREV_WORD: buffer.cursorPrevWord() - of ACTION_CURSOR_NEXT_LINK: buffer.cursorNextLink() - of ACTION_CURSOR_PREV_LINK: buffer.cursorPrevLink() - of ACTION_PAGE_DOWN: buffer.pageDown() - of ACTION_PAGE_UP: buffer.pageUp() - of ACTION_PAGE_RIGHT: buffer.pageRight() - of ACTION_PAGE_LEFT: buffer.pageLeft() - of ACTION_HALF_PAGE_DOWN: buffer.halfPageDown() - of ACTION_HALF_PAGE_UP: buffer.halfPageUp() - of ACTION_CURSOR_FIRST_LINE: buffer.cursorFirstLine() - of ACTION_CURSOR_LAST_LINE: buffer.cursorLastLine() - of ACTION_CURSOR_TOP: buffer.cursorTop() - of ACTION_CURSOR_MIDDLE: buffer.cursorMiddle() - of ACTION_CURSOR_BOTTOM: buffer.cursorBottom() - of ACTION_CURSOR_LEFT_EDGE: buffer.cursorLeftEdge() - of ACTION_CURSOR_VERT_MIDDLE: buffer.cursorVertMiddle() - of ACTION_CURSOR_RIGHT_EDGE: buffer.cursorRightEdge() - of ACTION_CENTER_LINE: buffer.centerLine() - of ACTION_SCROLL_DOWN: buffer.scrollDown() - of ACTION_SCROLL_UP: buffer.scrollUp() - of ACTION_SCROLL_LEFT: buffer.scrollLeft() - of ACTION_SCROLL_RIGHT: buffer.scrollRight() - of ACTION_CLICK: client.click() - of ACTION_CHANGE_LOCATION: client.changeLocation() - of ACTION_DUPE_BUFFER: client.dupeBuffer() - of ACTION_LINE_INFO: buffer.lineInfo() - of ACTION_FEED_NEXT: client.feednext = true - of ACTION_RELOAD: client.reloadPage() - of ACTION_RESHAPE: buffer.reshape = true - of ACTION_REDRAW: buffer.redraw = true - of ACTION_TOGGLE_SOURCE: client.toggleSource() - of ACTION_PREV_BUFFER: client.prevBuffer() - of ACTION_NEXT_BUFFER: client.nextBuffer() - of ACTION_DISCARD_BUFFER: client.discardBuffer() - of ACTION_COMMAND: client.command() - of ACTION_SEARCH: client.search() - of ACTION_SEARCH_BACK: client.searchBack() - of ACTION_ISEARCH: client.isearch() - of ACTION_ISEARCH_BACK: client.isearchBack() - of ACTION_SEARCH_NEXT: client.searchNext() - of ACTION_SEARCH_PREV: client.searchPrev() - else: discard - -proc followRedirect(client: Client) - -proc checkAuth(client: Client) = - if client.needsauth: - client.buffer.refreshBuffer() - client.statusMode() - var username = "" - let ustatus = readLine("Username: ", username, client.buffer.width) - if not ustatus: - client.needsauth = false - return - client.statusMode() - var password = "" - let pstatus = readLine("Password: ", password, client.buffer.width, hide = true) - if not pstatus: - client.needsauth = false - return - var url = client.buffer.location - url.username = username - url.password = password - var buf = client.buffer - client.gotoUrl(newRequest(url), prevurl = some(client.buffer.location)) - discardBuffer(buf) - client.followRedirect() - -proc followRedirect(client: Client) = - while client.redirecturl.issome: - client.statusMode() - print("Redirecting to ", $client.redirecturl.get) - stdout.flushFile() - client.buffer.refreshBuffer(true) - var buf = client.buffer - let redirecturl = client.redirecturl.get - client.redirecturl = none(Url) - client.gotoUrl(newRequest(redirecturl), prevurl = some(client.buffer.location)) - discardBuffer(buf) - if client.needsauth: - client.checkAuth() - -proc inputLoop(client: Client) = - while true: - g_client = client - setControlCHook(proc() {.noconv.} = - g_client.buffer.setStatusMessage("Interrupted rendering procedure") - g_client.buffer.redraw = true - g_client.buffer.reshape = false - g_client.inputLoop()) - client.followRedirect() - client.checkAuth() - client.buffer.refreshBuffer() - if client.needsauth: # Unauthorized - client.checkAuth() - try: - client.input() - except ActionError as e: - client.buffer.setStatusMessage(e.msg) - -proc launchClient*(client: Client, pages: seq[string], ctype: string, dump: bool) = - client.userstyle = gconfig.stylesheet.parseStylesheet() - if not stdin.isatty: - client.readPipe(ctype) - try: - for page in pages: - client.loadUrl(page, ctype) - except LoadError as e: - eprint e.msg - quit(1) - - if stdout.isatty and not dump: - when defined(posix): - enableRawMode() - client.inputLoop() - else: - var buffer = client.buffer - while buffer.next != nil: - buffer = buffer.next - - buffer.drawBuffer() - while buffer.prev != nil: - buffer = buffer.prev - buffer.drawBuffer() |