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/guide.typ | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'docs/typ/guide.typ') diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index b247ab6b..39e99aa6 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -29,10 +29,13 @@ Shift-Esc depend on the mode: #pairs( [normal], [#key("Esc"): back to the previous pane (#word("Last")). #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.], [insert], [#key("Esc"): back to normal. #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.], - [raw `$`], [#key("Esc"): to the program, except at an idle, empty shell prompt, where it goes back to the previous pane. #key("Shift-Esc"): always back to the previous pane. Either way the terminal stays `$`.], + [raw `$`], [#key("Esc"): to the program, except at a shell prompt with nothing typed on it, where it goes back to the previous pane. #key("Shift-Esc"): always back to the previous pane. Either way the terminal stays `$`.], [a PDF], [#key("Esc"): clears the selection and the search highlights. #key("Shift-Esc"): back to the previous pane.], ) +In short, #key("Shift-Esc") is as Esc, but always back to the previous pane from raw `$` or a PDF, and into raw from a terminal's normal or insert mode. "A shell prompt with nothing +typed on it" is one pardes knows from the prompt marks it injects into bash +and fish (and, for other shells, from no program holding the terminal). "The previous pane" is the last other pane on the jump list; with none, the next pane down its column (wrapping), else any other pane. In raw mode every key but #key("Ctrl-b"), Esc and the paste chords goes to the @@ -115,7 +118,10 @@ tag opens a `+New` scratch named in that pane's directory, in that pane's column; from a column tag, in that column and the session's directory. Every new pane but a command pane takes the keyboard. No pane is made shorter than its tag and two rows; where there is no room the pane is -refused. +refused. `Placement pardes` instead fills an empty column whose tag asked +or has the keyboard, puts a scratch or a shell under the pane that asked, +a document beside the last one read (or in a column of its own on a wide +screen), and a command pane at the foot of the last column. A column can be empty, as in acme: closing its last pane leaves it, its tag over blank space. #word("Delcol") and #word("Joincol") take columns @@ -138,7 +144,7 @@ pane's own directory. #btn("B2") on a line that is no builtin (`make`, `git log`) runs it with the #word("Shell") setting's `-c`, in the directory of the tag or pane it -came from. In a terminal idle at an empty prompt, a line from that +came from. In a terminal at a shell prompt with nothing typed on it, a line from that terminal's own text is typed into its shell. Anywhere else it runs in a command pane, a terminal of its own: @@ -213,7 +219,8 @@ git's `a/` and `b/` prefixes are dropped (and `c/ i/ w/ o/` with In a pane's shell, `pardes FILE` opens FILE in this session and returns at once, as acme's `B` does. A FILE not there yet opens an empty pane named for it; #word("Save") creates it, making its directories first. Something -the session refuses (a bad name) is printed and the command exits 1. +the session refuses (a bad name, or a new name in a directory that is there +but may not be read or written) is printed and the command exits 1. `pardes --wait FILE` (`-w`) returns when the pane showing FILE is closed (exit 0) or the session goes away (exit 1), as acme's `E` does. Set @@ -269,7 +276,8 @@ The session owns the panes, shells and files; frontends come and go. #word("Attach") `work` (#key("SPC s a")) switches this window to that session, and #word("Detach") (#key("SPC s D")) leaves it running, shells and all. Bare `--detach` names the session after its pid; bare `--attach` -needs exactly one session. Every attached frontend sees the same screen, +needs exactly one session; a second `--detach=NAME` while NAME runs says so +and exits 1. Every attached frontend sees the same screen, at the smallest common size. With none attached, `size C R` on the root ctl sets the screen and messages clear by the clock. A detached session ends when its last pane closes, as any session does. -- cgit v1.3 From 06770c54a408057c92c0381b75039d70c85625d6 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 00:02:33 -0300 Subject: Setting up your environment: a 9ns mount, pardes as the editor, pager and yazi opener, the shells with prompt marks, language servers, fonts and plan9port, each snippet tried Co-Authored-By: Claude Opus 5.5 --- README.md | 3 +- docs/typ/guide.typ | 2 +- docs/typ/setup.typ | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 docs/typ/setup.typ (limited to 'docs/typ/guide.typ') diff --git a/README.md b/README.md index cc78f508..22ba334a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ Install the terminal build with Zig 0.16: `zig build -Dplatform=tty --prefix ~/.local` puts `pardes` in `~/.local/bin` (a bare `zig build` builds the SDL window too and installs both there). Run `pardes`, or `pardes FILE`. -Read next: the [guide](docs/typ/guide.typ) (10 minutes), the +Read next: the [guide](docs/typ/guide.typ) (10 minutes), +[setup](docs/typ/setup.typ) for the shell, editor and mount around it, the [cheatsheet](docs/typ/cheatsheet.typ) (3 minutes), then [scripting](docs/typ/scripting.typ) (5 minutes); the [reference](docs/typ/reference.typ) and [themes](docs/typ/themes.typ) when diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index 39e99aa6..55f11a80 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -225,7 +225,7 @@ but may not be read or written) is printed and the command exits 1. `pardes --wait FILE` (`-w`) returns when the pane showing FILE is closed (exit 0) or the session goes away (exit 1), as acme's `E` does. Set `EDITOR='pardes --wait'` (`GIT_EDITOR` follows it), and `git commit`, -`crontab -e` and fish's Ctrl-O open in a pane and read the file once you +`crontab -e` and fish's Alt-E open in a pane and read the file once you close it. Bare `pardes` inside a pane refuses and names `--nested`, which starts a separate session whose shells do not forward to it. diff --git a/docs/typ/setup.typ b/docs/typ/setup.typ new file mode 100644 index 00000000..8cf8b091 --- /dev/null +++ b/docs/typ/setup.typ @@ -0,0 +1,166 @@ +// Setting up your environment: what to put outside pardes (the shell's +// startup files, a desktop launcher, other programs' configs) so that it +// works best. Each snippet says in one line why it is there. +#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs + +pardes runs as it is, but a few lines elsewhere make it fit: a mount so +scripts see the session as files, pardes as the editor and file opener of +other programs, and the servers and fonts it looks for. + += A 9P mount: 9ns + +`9ns` is cloud9's 9P namespace tool: it mounts a 9P tree through FUSE in a +private mount namespace, as a plain user, and runs a program inside. Get it +from cloud9 (`git.sr.ht/~gbrls/cloud9`): its `zig build` builds `9ns` on +Linux into `zig-out/bin`, and Linux needs `/dev/fuse`. With `--mntgen` it +mounts the whole registry of posted 9P servers at `/mnt/9p` and exports +`$NINE_MOUNT`; every pardes session posts itself there, as +`$NINE_MOUNT/pardes/`. + +Run pardes inside one, and the editor and everything it starts (its +terminals, command panes, the programs they run) see `$NINE_MOUNT` and the +session tree, so scripting is just files: + +#cmd("9ns --mntgen -- pardes-gui") + +A desktop launcher (`~/.local/share/applications/pardes.desktop`) that +does it, and still starts pardes on a machine with no 9ns or no +`/dev/fuse`. Write your own absolute paths: a launcher's `Exec` line takes +a `$` only escaped, and a desktop session's `PATH` may not hold +`~/.local/bin`. + +``` +[Desktop Entry] +Type=Application +Name=pardes +Exec=/bin/sh -c "if [ -x /home/me/.local/bin/9ns ] && [ -e /dev/fuse ]; then exec /home/me/.local/bin/9ns --mntgen -- /home/me/.local/bin/pardes-gui; else exec /home/me/.local/bin/pardes-gui; fi" +Terminal=false +Categories=Development; +``` + +A shell can wrap itself, so every interactive shell has the mount (and the +terminal pardes runs in, with the pardes inside it). It checks `$NINE_MOUNT` +first, so a shell already inside a mount does not wrap again. fish, in +`~/.config/fish/config.fish`: + +``` +if status is-interactive; and not set -q NINE_MOUNT + and test -e /dev/fuse; and type -q 9ns + exec 9ns --mntgen -- fish +end +``` + +bash, in `~/.bashrc`: + +``` +if [[ $- == *i* && -z $NINE_MOUNT && -e /dev/fuse ]] && command -v 9ns >/dev/null; then + exec 9ns --mntgen -- bash +fi +``` + += pardes as your editor + +`pardes --wait FILE` opens FILE in the session whose pane it runs in and +returns when you close that pane; outside pardes it starts an editor of its +own, which also returns when you are done. Set it as the editor, and every +program that asks for one opens a pane: + +#cmd("set -gx VISUAL 'pardes --wait'; set -gx EDITOR 'pardes --wait' # fish") +#cmd("export VISUAL='pardes --wait' EDITOR='pardes --wait' # bash, zsh") + +`git commit` (`GIT_EDITOR`, then `core.editor`, then these), `crontab -e` +and fish's `edit_command_buffer` (Alt-E or Alt-V) read `VISUAL` before +`EDITOR`, so set both, or a `VISUAL` left from elsewhere wins. To edit the +command line with Ctrl-O in fish instead, add `bind ctrl-o edit_command_buffer` to `fish_user_key_bindings`. + += 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. + += yazi in a terminal pane + +yazi, run in a terminal pane, can open files as panes of the session: an +opener that runs `pardes` forwards each file to the session and returns at +once. In `~/.config/yazi/yazi.toml` (yazi 26): + +``` +[opener] +pardes = [ + { run = 'pardes %s', desc = "Open in pardes", for = "unix" }, +] + +[open] +prepend_rules = [ + { mime = "text/*", use = "pardes" }, +] +``` + +`%s` is the selected files (older yazi spelled it `"$@"`). yazi's own +`edit` opener runs `$EDITOR` and waits, so with `EDITOR='pardes --wait'` +it already opens a pane and blocks until you close it; the opener above +does not wait. Without a file manager, #btn("B3") on a path in any pane +(an `ls`, a `find`, a compiler's message) does the same. + += Shells + +pardes injects OSC 133 prompt marks into bash and fish, sourcing your own +`~/.bashrc` and `config.fish` first: they tell it where each prompt and +command starts and ends. That is what makes #key("Esc") at a prompt go back +a pane only when nothing is typed, and #file("pty/run") answer `exit N` +and the output. Other +shells (zsh, sh, dash, ksh, nu and the rest) get no marks, and marks a shell +emits on its own do not count: #file("pty/run") answers `error no prompt marks`, and a prompt counts as free whenever no program holds the terminal, +typed text or not. Running `exec zsh` inside a bash pane loses the marks +the same way. The #word("Shell") setting picks the shell; bare, it is +`$SHELL` if that is executable, else `/bin/sh`. + += Language servers + +pardes starts a language server as a child process for a file it knows, +looking for the program on `PATH`; `PARDES_LSP_` names another +program, and an empty value turns that language off. + +#pairs( + [`.zig`], [`zls` (`PARDES_LSP_ZIG`)], + [`.rs`], [`rust-analyzer` (`PARDES_LSP_RS`)], + [`.c .h .cc .cpp .hpp .cxx .hxx`], [`clangd` (`PARDES_LSP_C`)], + [`.go`], [`gopls` (`PARDES_LSP_GO`)], + [`.ts .tsx .js .jsx .mjs .cjs`], [`typescript-language-server --stdio` (`PARDES_LSP_TS`)], + [`.py`], [`pyright-langserver --stdio`, from pyright (`PARDES_LSP_PY`)], +) + +#word("Lspinfo") (#key("SPC l i")) says which server serves the file and +what state it is in. + += Fonts and themes + +The terminal build draws in your terminal's font. The SDL window +(`pardes-gui`) carries Adwaita Mono and finds others in `/usr/share/fonts`, +`/usr/local/share/fonts`, `~/.local/share/fonts` and `~/.fonts`, with no +fontconfig: `Fonts` lists them, and `Font name:size` (size +8-72) picks one, a line for the startup file. Themes are a word away: +`Theme `, or `ThemeFile themes/mine.zon` for your own, reloaded as +you save it (#doc("themes")). + += plan9port + +plan9port's `9p` reads and writes a session without any mount, which is +what a script uses when `$NINE_MOUNT` is not set: + +#cmd("9p -a \"unix!$PARDES_9P\" read index") +#cmd("echo Save | 9p -a \"unix!$PARDES_9P\" write pane/3/ctl") + +Its `write` always opens with OTRUNC, so `9p write pane/3/body` replaces the +whole body where acme would append: append through a mount with `>>`. +acme habits carry over: `pardes FILE` in a pane is acme's `B`, +`pardes --wait` is `E`, the files have acme's names (`body`, `tag`, `ctl`, +`addr`, `data`, `event`), and a program holding a pane's #file("event") +takes its clicks, as acme's `win` and its friends do. -- 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/guide.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