From be7fb126aefedee221d0d32109acb9fc4b71d474 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 23:53:38 -0300 Subject: The docs take the 9P agent's round 30: log stats 0, names escape every control byte, $winid and the pager defaults, the one client rule, and Shift-Esc said one way in the guide and the cheatsheet Co-Authored-By: Claude Opus 5.5 --- docs/typ/scripting.typ | 77 +++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 48 deletions(-) (limited to 'docs/typ/scripting.typ') diff --git a/docs/typ/scripting.typ b/docs/typ/scripting.typ index a00ee612..8b5831b8 100644 --- a/docs/typ/scripting.typ +++ b/docs/typ/scripting.typ @@ -18,32 +18,30 @@ Each session listens on a Unix socket, (the editor's pid), `PARDES_9P` (the socket) and `PARDES_PANE` (their pane's serial). -Two clients do the rest. `9ns` comes from cloud9 -(`git.sr.ht/~gbrls/cloud9`; its `zig build` installs `9ns` on Linux) and -mounts a 9P tree through FUSE in a private namespace, no root needed; -`9p` is plan9port's, and reads and writes without a mount. - -#cmd("9ns --mntgen # every posted session, under $NINE_MOUNT") -#cmd("9ns --unix \"$PARDES_9P\" -- sh -c 'cat \"$NINE_MOUNT/index\"' # one session, for that command") -#cmd("9p -a \"unix!$PARDES_9P\" read index # no mount") - -Under `9ns --mntgen` find this pane's session from its socket: - -#cmd("[ -n \"$NINE_MOUNT\" ] || echo 'no 9P mount: use 9p or the Python client'") -#cmd("s=${PARDES_9P##*/pardes-9p-}; m=$NINE_MOUNT/pardes/${s%.sock}") -#cmd("cat \"$m/index\"") - -Under `9ns --unix` the session is `$NINE_MOUNT` itself (`m=$NINE_MOUNT`), -and in a #word("Tty9p") terminal (#key("SPC n 9")) it is `$PARDES_MOUNT`. -A dead session's registry entry stays listed and answers `Input/output error`: name the session, never glob. A running session serves the binary -that started it. - -#word("Tty9p") opens a terminal with the session kernel-mounted (Linux -v9fs): it asks for your sudo password in the pane, mounts the socket in a -private mount namespace and starts your shell as you, with -`PARDES_MOUNT` set. Only that shell sees the mount, and each takes one of -the session's 16 connections. It needs the `9p` and `9pnet_fd` kernel -modules and the `pardes-v9fs` helper the build installs beside `pardes`. +Pane shells and command panes alike can reach the session; one rule picks +the client: + +- *With a mount* (`$NINE_MOUNT` set, as under cloud9's `9ns --mntgen`; + `git.sr.ht/~gbrls/cloud9`, whose `zig build` installs `9ns` on Linux), + the session is a directory and plain `cat` and `echo >` work: + #cmd("s=${PARDES_9P##*/pardes-9p-}; m=$NINE_MOUNT/pardes/${s%.sock}\ncat \"$m/index\"") + (Under `9ns --unix SOCK -- cmd` the session is `$NINE_MOUNT` itself.) +- *Without one*, plan9port's `9p` talks to the socket: + #cmd("9p -a \"unix!$PARDES_9P\" read index\necho Save | 9p -a \"unix!$PARDES_9P\" write pane/3/ctl") + +The recipes below use `$m`; with `9p`, `cat $m/x` is `9p ... read x` and +`echo y > $m/x` is `echo y | 9p ... write x`. A dead session's registry +entry stays listed and answers `Input/output error`: name the session, +never glob. A running session serves the binary that started it. The +reference has the other ways in: a #word("Tty9p") terminal's kernel mount, +and the Python client in the source tree. + +A command run from a pane (#btn("B2") on a line in its tag or text) also +gets `$winid`, the serial of that pane, as acme's commands do; it is unset +for one run from a column's or the workspace's tag, and `PARDES_PANE` +stays the command pane's own. Every pane's child, a terminal's shell or a +command, gets `PAGER=cat` and `GIT_PAGER=cat` unless your environment sets +them, so nothing waits in a pager. = Recipes @@ -62,6 +60,7 @@ The paths below are under `$m`; `p=$m/pane/$n` for a pane. [run at a prompt], [#cmd("t=$(awk '$2==\"term\"{print $1; exit}' $m/index)\nexec 3<>$m/pane/$t/pty/run; echo ls >&3; cat <&3; exec 3<&-")], [type, interrupt], [#cmd("printf 'q' > $m/pane/$t/pty/data # \\r Enter, \\x03 Ctrl-C\necho 'sig INT' > $m/pane/$t/pty/ctl")], [ask the language server], [#cmd("echo /myFunc/ > $p/addr; echo dot=addr > $p/ctl\necho Hover > $p/exec # also Rename new, Symbols")], + [act on the pane a command came from], [#cmd("#!/bin/sh\n# fmt-here: put its name in a pane's tag, click it with B2\nn=$winid; f=$(cat $m/pane/$n/name)\necho Save > $m/pane/$n/ctl && zig fmt \"$f\" && echo get > $m/pane/$n/ctl")], [follow what happens], [#cmd("exec 3<>$m/log; echo 'follow new' >&3\ntimeout 30 cat <&3; exec 3<&-")], ) @@ -103,6 +102,10 @@ pardes the editor of every program run there. the bad one fails. Send a block as one write (`cat block > $p/ctl`), and end every write whose result matters with a newline. - plan9port's `9p write` opens with OTRUNC: `echo x | 9p write pane/3/body` replaces the whole body. Append with `>>` through a mount. +- #file("pty/run") runs in the terminal's own shell, so a command that + pages waits in the pager when your environment names one (pardes sets + `PAGER` and `GIT_PAGER` to `cat` only when it does not): run + `git --no-pager ...` or `PAGER=cat ...`. - `tail -f log` never sees anything new: write `follow` on the open you read. Through a FUSE mount bash's `read -t` cannot time out: wrap the loop in `timeout`. @@ -120,28 +123,6 @@ pardes the editor of every program run there. - A #word("Restore") hangs up every connection: dial again and restart the mount. -= No mount: the Python client - -`test/ninep.py` in the source tree speaks 9P itself, for when nothing is -mounted or a fid must stay open (#file("event"), #file("pty/data"), a -followed #file("log")). Its paths are the served root: - -```python -import sys -from ninep import Client # PYTHONPATH=test -with Client(sys.argv[1]) as c: # a socket path, or (ip, port) - print(c.read('/index').decode(), end='') - n = int(c.read('/pane/new')) - c.write(f'/pane/{n}/body', b'hello\n', truncate=True) - fid = c.open(f'/pane/{n}/event', 0) # hold event: clicks come here - c.write(f'/pane/{n}/exec', b'Msg hi\n') - print(c.read_fid(fid, 0, 4096)) # b'FX0 0 1 6 Msg hi\n' - c.close(fid) - c.remove(f'/pane/{n}') -``` - -`client.screen()` returns the parsed #file("/screen"). - = An isolated session Never experiment on a session someone is using. Strip every `PARDES_*` -- cgit v1.3 From 69cb06ed45ef2a26d7f5a3259df743f76bf53021 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 00:23:38 -0300 Subject: The docs describe the pager that landed: pardes - and its +Pager pane, a terminal's PAGER, command panes' cat, Pager pardes|off, and pty/run no longer waiting in one Co-Authored-By: Claude Opus 5.5 --- docs/typ/guide.typ | 4 ++++ docs/typ/reference.typ | 12 +++++++++++- docs/typ/scripting.typ | 13 ++++++------- docs/typ/setup.typ | 18 +++++++++--------- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'docs/typ/scripting.typ') diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index 55f11a80..6eeba822 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -184,6 +184,10 @@ A terminal is a pane like any other. - #word("Save") asks for a path on the pane's notice band and writes the terminal's scrollback there as text; `Save path` writes it at once. - #word("Filter") maps the program's colours through the theme. +- A paging command in a terminal (`git log`, `man`) opens its text in a + `+Pager` pane: a terminal's `PAGER` and `GIT_PAGER` are `pardes -` unless + your environment sets them. `Pager off` keeps your own pager, for + terminals started after it. - In raw mode Ctrl-V types what you yanked into the program (with nothing yanked, the program gets the key), and Ctrl-Shift-V the desktop clipboard. diff --git a/docs/typ/reference.typ b/docs/typ/reference.typ index 28f0c756..08fb1d83 100644 --- a/docs/typ/reference.typ +++ b/docs/typ/reference.typ @@ -296,6 +296,7 @@ word clicked in a tag does. The same lines go in the startup file [`Verbose`], [on: a builtin announces its name on the message row], [`MessageAnimation`], [on: messages ease in and dissolve], [`MessageLinger`, `MessageFall`, `MessageDissolve`], [800, 180, 150 milliseconds, at most 60000], + [`Pager pardes|off`], [`pardes`: what a terminal's commands page through, `pardes -`; `off` leaves `PAGER` and `GIT_PAGER` as the environment has them. It applies to terminals started after it], [`Placement acme|pardes`], [`acme`: where new panes go (#doc("tags", section: "new-panes"))], [`BootShell keep|replace`], [`keep`; `replace` closes the untouched lone shell a dragged document lands beside], [`LookWord search|list`], [`search`: a looked-at word selects its next place, or lists all in `+Search`], @@ -642,7 +643,8 @@ Terminal panes also have #file("pty/"): open: #cmd("exec 3<>$m/pane/$n/pty/run; echo make >&3; cat <&3; exec 3<&-") The answer's first line is the header: `exit N` then the command's output (as the screen showed it: no colour, tabs expanded to blanks, `\r` - progress collapsed, trailing blanks dropped; the last 64 KiB, `exit N cut M` when M bytes were left + progress collapsed, trailing blanks dropped; a paging command's text + goes to a `+Pager` pane through the terminal's `pardes -` pager; the last 64 KiB, `exit N cut M` when M bytes were left out, bare `cut` when the start scrolled away or was cleared). Or: `busy: is running` (bare `busy` when text is typed at the prompt), `exit ?` (no status reported, not a success), `error not run` (the shell refused the line, e.g. a fish syntax error), `error shell gone`, `error no prompt marks`, and on a command pane `error a command runs here, not a shell` (`error command done; not a shell` once it ended). A line written @@ -708,6 +710,14 @@ at 200, ending in `…`. Control characters become spaces. `-Dembed-sources=true`; `EffectCode ` lists an effect's files under `/virtual`. += pardes - + +`pardes -` reads stdin to its end, strips terminal escapes (colour, +hyperlinks, a man page's overstrike), and shows it in a `+Pager` pane: +inside a pardes pane, a clean `/+Pager` in the outer session, +returning at once; outside one, a new editor whose first pane it is. Input +past a file's size limit is cut, and the pane says so at its end. + = Other ways in The scripting chapter's rule (a mount, else plan9port's `9p`) covers most diff --git a/docs/typ/scripting.typ b/docs/typ/scripting.typ index 8b5831b8..d3e42573 100644 --- a/docs/typ/scripting.typ +++ b/docs/typ/scripting.typ @@ -39,9 +39,12 @@ and the Python client in the source tree. A command run from a pane (#btn("B2") on a line in its tag or text) also gets `$winid`, the serial of that pane, as acme's commands do; it is unset for one run from a column's or the workspace's tag, and `PARDES_PANE` -stays the command pane's own. Every pane's child, a terminal's shell or a -command, gets `PAGER=cat` and `GIT_PAGER=cat` unless your environment sets -them, so nothing waits in a pager. +stays the command pane's own. + +A paging command run through #file("pty/run") does not hang: the +terminal's pager is `pardes -`, which puts the text in a `+Pager` pane and +returns. A command pane pages through `cat`; its output is a pane already. +Both hold only where your environment names no pager. = Recipes @@ -102,10 +105,6 @@ pardes the editor of every program run there. the bad one fails. Send a block as one write (`cat block > $p/ctl`), and end every write whose result matters with a newline. - plan9port's `9p write` opens with OTRUNC: `echo x | 9p write pane/3/body` replaces the whole body. Append with `>>` through a mount. -- #file("pty/run") runs in the terminal's own shell, so a command that - pages waits in the pager when your environment names one (pardes sets - `PAGER` and `GIT_PAGER` to `cat` only when it does not): run - `git --no-pager ...` or `PAGER=cat ...`. - `tail -f log` never sees anything new: write `follow` on the open you read. Through a FUSE mount bash's `read -t` cannot time out: wrap the loop in `timeout`. diff --git a/docs/typ/setup.typ b/docs/typ/setup.typ index 8cf8b091..4769eb49 100644 --- a/docs/typ/setup.typ +++ b/docs/typ/setup.typ @@ -75,15 +75,15 @@ command line with Ctrl-O in fish instead, add `bind ctrl-o edit_command_buffer` = A pager -// PENDING: written to the 9P agent's pager design, which is not on main -// yet; check every line against its change when it lands. -`pardes -` reads its standard input into a `+Pager` pane. Terminal panes -get `PAGER='pardes -'` and `GIT_PAGER='pardes -'` by themselves, unless -your environment sets them, so `git log` or `man` output arrives as a pane -to search and click in; command panes get `cat`. `Pager off` (on the root -ctl, or a line in the startup file) turns it off, `Pager pardes` back on. -To keep your own pager in pardes's terminals, set `PAGER` in your -environment; to use pardes's outside them, set it to `pardes -` yourself. +`pardes -` reads its standard input into a `+Pager` pane, escapes +stripped. Terminals get `PAGER` and `GIT_PAGER` set to it (this pardes's +own path, then `-`) by themselves unless your environment sets them, so +`git log` or `man` output arrives as a pane to search and click in, and +the terminal is back at once; command panes page through `cat`. To keep +your own pager, either set `PAGER` in your environment, or put `Pager off` +in the startup file (or write it to the root ctl); it applies to terminals +started after it. Outside pardes, `PAGER='pardes -'` opens a new editor on +the text. = yazi in a terminal pane -- cgit v1.3