# Chawan's command API As described in the [config](config.md) documentation , keypress combinations can be bound to actions. An action can be either a JavaScript expression, or a command defined in the `[cmd]` section of config.toml. For example, the following works: ``` gpn = 'n => pager.alert(n)' # e.g. 2gpn prints `2' to the status line ``` Note however, that JavaScript functions must be called with an appropriate `this` value. Unfortunately, this also means that the following does not work: ``` gpn = 'pager.alert' # broken!!! ``` To work around this limitation, actions have to wrap the target function in a closure, as above. However, this has very poor reusability; for more complex actions, you would have to copy and paste the entire function every time you re-bind it or call it from a different function. To fix this, it is possible to define a command in the `[cmd]` section: ```toml [cmd.my.namespace] showNumber = 'n => pager.alert(n)' ``` `my.namespace` can be anything you want; it is to avoid collisions when including multiple configs. Avoid setting it to `pager` or `line`, because these are used by the default config. Now you can call `cmd.my.namespace.showNumber()` from any other function, or just include it in an action: ```toml 'gpn' = 'cmd.my.namespace.showNumber' ``` ## Interfaces ### Client The global object (`globalThis`) implements the `Client` interface. Documented functions of this are:
Property Description
`quit()` Exit the browser.
`suspend()` Temporarily suspend the browser, by delivering the client process a SIGTSTP signal.
Note: this suspends the entire process group.
`Client` also implements various web standards normally available on the `Window` object on websites, e.g. fetch(). Note however that it does *not* give access to JS objects in buffers, so e.g. `globalThis.document` is not available. ### Pager `Pager` is a separate interface from `Client` that gives access to the pager (i.e. browser chrome). It is accessible as `globalThis.pager`, or simply `pager`. Following properties (functions/getters) are defined by `Pager`:
Property Description
`load(url)` Put the specified address into the URL bar, and optionally load it.
Note that this performs auto-expansion of URLs, so Chawan will expand any matching omni-rules (e.g. search), try to open schemeless URLs with the default scheme/local files, etc.
Opens a prompt with the current URL when no parameters are specified; otherwise, the string passed is displayed in the prompt. If this string ends with a newline (e.g. `pager.load("about:chawan\n")`), the URL is loaded directly.
`loadSubmit(url)` Act as if `url` had been input into the address bar.
Same as `pager.load(url + "\n")`.
`gotoURL(url, options = {replace: null, contentType: null})` Go to the specified URL immediately (without a prompt). This differs from `load` and `loadSubmit` in that it *does not* try to correct the URL.
When `replace` is set, the new buffer may replace the old one if it loads successfully. When `contentType` is set, the new buffer's content type is forcefully set to that string.
Use this for loading automatically retrieved (i.e. non-user-provided) URLs.
`nextBuffer()`, `prevBuffer()`, `nextSiblingBufer()`, `prevSiblingBuffer()`, `parentBuffer()` Traverse the buffer tree.
`nextBuffer()`, `prevBuffer()` do a depth-first traversal; ``nextSiblingBufer()`, `prevSiblingBuffer()` cycle through siblings, and `parentBuffer()` returns to the parent.
`dupeBuffer()` Duplicate the current buffer by loading its source to a new buffer.
`discardBuffer(buffer = pager.buffer, dir = pager.navDirection)` Discard `buffer`, then move back to the buffer opposite to `dir`. Possible values of `dir` are: "prev", "next", "prev-sibling", "next-sibling", "parent", "first-child", "any".
`discardTree()` Discard all child buffers of the current buffer.
`reload()` Open a new buffer with the current buffer's URL, replacing the current buffer.
`reshape()` Reshape the current buffer (=render the current page anew.)
`redraw()` Redraw screen contents. Useful if something messed up the display.
`toggleSource()` If viewing an HTML buffer, open a new buffer with its source. Otherwise, open the current buffer's contents as HTML.
`lineInfo()` Display information about the current line.
`searchForward()` Search for a string in the current buffer.
`searchBackward()` Search for a string, backwards.
`isearchForward()` Incremental-search for a string, highlighting the first result.
`isearchBackward()` Incremental-search and highlight the first result, backwards.
`gotoLine(n?)` Go to the line passed as the first argument.
If no arguments were specified, an input window for entering a line is shown.
`searchNext(n = 1)` Jump to the nth next search result.
`searchPrev(n = 1)` Jump to the nth previous search result.
`peek()` Display an alert message of the current URL.
`peekCursor()` Display an alert message of the URL or title under the cursor. Multiple calls allow cycling through the two. (i.e. by default, press u once -> title, press again -> URL)
`showFullAlert()` Show the last alert inside the line editor.
`ask(prompt)` Ask the user for confirmation. Returns a promise which resolves to a boolean value indicating whether the user responded with yes.
Can be used to implement an exit prompt like this: ``` q = 'pager.ask("Do you want to exit Chawan?").then(x => x ? pager.quit() : void(0))' ```
`askChar(prompt)` Ask the user for any character.
Like `pager.ask`, but the return value is a character.
`saveLink()` Save URL pointed to by the cursor.
`saveSource()` Save the source of the current buffer.
`extern(cmd, options = {setenv: true, suspend: true, wait: false})` Run an external command `cmd`. The `$CHA_URL` and `$CHA_CHARSET` variables are set when `options.setenv` is true. `options.suspend` suspends the pager while the command is being executed, and `options.wait` makes it so the user must press a key before the pager is resumed.
Returns true if the command exit successfully, false otherwise.
Warning: this has a bug where the output is written to stdout even if suspend is true. Redirect to /dev/null in the command if this is not desired. (This will be fixed in the future.)
`externCapture(cmd)` Like extern(), but redirect the command's stdout string into the result. null is returned if the command wasn't executed successfully, or if the command returned a non-zero exit value.
`externInto(cmd, ins)` Like extern(), but redirect `ins` into the command's standard input stream. `true` is returned if the command exits successfully, otherwise the return value is `false`.
`externFilterSource(cmd, buffer = null, contentType = null)` Redirects the specified (or if `buffer` is null, the current) buffer's source into `cmd`.
Then, it pipes the output into a new buffer, with the content type `contentType` (or, if `contentType` is null, the original buffer's content type).
Returns `undefined`. (It should return a promise; TODO.)
`buffer` Getter for the currently displayed buffer. Returns a `Buffer` object; see below.
### Buffer Each buffer is exposed as an object that implements the `Buffer` interface. To get a reference to the currently displayed buffer, use `pager.buffer`. Important: there exists a quirk of questionable value on pager, where accessing properties that do not exist on the pager will dispatch those to the current buffer (`pager.buffer`). So if you see e.g. `pager.url`, that is actually equivalent to `pager.buffer.url`, because `Pager` has no `url` getter. Following properties (functions/getters) are defined by `Buffer`:
Property Description
`cursorUp(n = 1)`, `cursorDown(n = 1)` Move the cursor upwards/downwards by n lines, or if n is unspecified, by 1.
`cursorLeft(n = 1)`, `cursorRight(n = 1)` Move the cursor to the left/right by n cells, or if n is unspecified, by 1.
Note: `n` right now represents cells, but really it should represent characters. (The difference is that right now numbered cursorLeft/cursorRight is broken for double-width chars.)
`cursorLineBegin()`, `cursorLineEnd()` Move the cursor to the first/last cell of the line.
`cursorLineTextStart()` Move the cursor to the first non-blank character of the line.
`cursorNextWord()`, `cursorNextViWord()`, `cursorNextBigWord()` Move the cursor to the beginning of the next [word](#word-types).
`cursorPrevWord()`, `cursorPrevViWord()`, `cursorPrevBigWord()` Move the cursor to the end of the previous [word](#word-types).
`cursorWordEnd()`, `cursorViWordEnd()`, `cursorBigWordEnd()` Move the cursor to the end of the current [word](#word-types), or if already there, to the end of the next word.
`cursorWordBegin()`, `cursorViWordBegin()`, `cursorBigWordBegin()` Move the cursor to the beginning of the current [word](#word-types), or if already there, to the end of the previous word.
`cursorNextLink()`, `cursorPrevLink()` Move the cursor to the beginning of the next/previous clickable element.
`cursorNextParagraph(n = 1)`, `cursorPrevParagraph(n = 1)` Move the cursor to the beginning/end of the nth next/previous paragraph.
`cursorNthLink(n = 1)` Move the cursor to the nth link of the document.
`cursorRevNthLink(n = 1)` Move the cursor to the nth link of the document, counting backwards from the document's last line.
`pageUp(n = 1)`, `pageDown(n = 1)`, `pageLeft(n = 1)`, `pageRight(n = 1)` Scroll up/down/left/right by n pages.
`halfPageUp(n = 1)`, `halfPageDown(n = 1)`, `halfPageLeft(n = 1)`, `halfPageRight(n = 1)` Scroll up/down/left/right by n half pages.
`scrollUp(n = 1)`, `scrollDown(n = 1)`, `scrollLeft(n = 1)`, `scrollRight(n = 1)` Scroll up/down/left/right by n lines.
`click()` Click the HTML element currently under the cursor.
`cursorFirstLine()`, `cursorLastLine()` Move to the first/last line in the buffer.
`cursorTop()` Move to the first line on the screen. (Equivalent to H in vi.)
`cursorMiddle()` Move to the line in the middle of the screen. (Equivalent to M in vi.)
`cursorBottom()` Move to the last line on the screen. (Equivalent to L in vi.)
`lowerPage(n = this.cursory)` Move cursor to line n, then scroll up so that the cursor is on the top line on the screen. (`zt` in vim.)
`lowerPageBegin(n = this.cursory)` Move cursor to the first non-blank character of line n, then scroll up so that the cursor is on the top line on the screen. (`z` in vi.)
`centerLine(n = this.cursory)` Center screen around line n. (`zz` in vim.)
`centerLineBegin(n = this.cursory)` Center screen around line n, and move the cursor to the line's first non-blank character. (`z.` in vi.)
`raisePage(n = this.cursory)` Move cursor to line n, then scroll down so that the cursor is on the top line on the screen. (zb in vim.)
`lowerPageBegin(n = this.cursory)` Move cursor to the first non-blank character of line n, then scroll up so that the cursor is on the last line on the screen. (`z^` in vi.)
`nextPageBegin(n = this.cursory)` If n was given, move to the screen before the nth line and raise the page. Otherwise, go to the previous screen's last line and raise the page. (`z+` in vi.)
`cursorLeftEdge()`, `cursorMiddleColumn()`, `cursorRightEdge()` Move to the first/middle/last column on the screen.
`centerColumn()` Center screen around the current column.
`findNextMark(x = this.cursorx, y = this.cursory)` Find the next mark after `x`, `y`, if any; and return its id (or null if none were found.)
`findPrevMark(x = this.cursorx, y = this.cursory)` Find the previous mark before `x`, `y`, if any; and return its id (or null if none were found.)
`setMark(id, x = this.cursorx, y = this.cursory)` Set a mark at (x, y) using the name `id`.
Returns true if no other mark exists with `id`. If one already exists, it will be overridden and the function returns false.
`clearMark(id)` Clear the mark with the name `id`. Returns true if the mark existed, false otherwise.
`gotoMark(id)` If the mark `id` exists, jump to its position and return true. Otherwise, do nothing and return false.
`gotoMarkY(id)` If the mark `id` exists, jump to the beginning of the line at its Y position and return true. Otherwise, do nothing and return false.
`getMarkPos(id)` If the mark `id` exists, return its position as an array where the first element is the X position and the second element is the Y position. If the mark does not exist, return null.
`markURL()` Convert URL-like strings to anchors on the current page.
`url` Getter for the buffer's URL. Note: this returns a `URL` object, not a string.
`hoverTitle`, `hoverLink`, `hoverImage` Getter for the string representation of the element title/link/image currently under the cursor. Returns the empty string if no title is found.
### LineEdit The line editor at the bottom of the screen is exposed to the JavaScript context as `globalThis.line`, or simply `line`, and implements the `LineEdit` interface. Note that there is no single `LineEdit` object; a new one is created every time the line editor is opened, and when the line editor is closed, `globalThis.line` simply returns `null`. Following properties (functions/getters) are defined by `LineEdit`:
Property Description
`submit()` Submit line.
`cancel()` Cancel operation.
`backspace()` Delete character before cursor.
`delete()` Delete character after cursor.
`clear()` Clear text before cursor.
`kill()` Clear text after cursor.
`clearWord()` Delete word before cursor.
`killWord()` Delete word after cursor.
`backward()`, `forward()` Move cursor backward/forward by one character.
`nextWord()`, `prevWord()` Move cursor to the next/previous word by one character.
`begin()`, `end()` Move cursor to the beginning/end of the line.
`escape()` Ignore keybindings for next character.
`nextHist()`, `prevHist()` Jump to the previous/next history entry.