diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 23:04:26 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | fb3f5ab5a7758a628a6e343f24a6510a58802599 (patch) | |
| tree | 8387bf3c7fd4561da29681418ffb4e595e3b3cac /docs | |
| parent | e61abc001db6902807f8241465c113c834ecf86b (diff) | |
| download | pardes-fb3f5ab5a7758a628a6e343f24a6510a58802599.tar.gz pardes-fb3f5ab5a7758a628a6e343f24a6510a58802599.zip | |
The guide walks a newcomer through pardes day to day, and the cheatsheet says what Esc and Shift-Esc really do
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/helix-keys.md | 8 | ||||
| -rw-r--r-- | docs/lsp.md | 73 | ||||
| -rw-r--r-- | docs/tags.md | 222 | ||||
| -rw-r--r-- | docs/typ/cheatsheet.typ | 14 | ||||
| -rw-r--r-- | docs/typ/guide.typ | 303 | ||||
| -rw-r--r-- | docs/ui-review.md | 4 |
6 files changed, 315 insertions, 309 deletions
diff --git a/docs/helix-keys.md b/docs/helix-keys.md index b1035ad6..6249fc15 100644 --- a/docs/helix-keys.md +++ b/docs/helix-keys.md @@ -126,8 +126,8 @@ language-backend queries, and the shell pipe. | `Esc` (insert) | back to normal mode, cursor right after the insertion (no vim left-step) | | helix-verified | | `Enter` (normal) | acme **look** chord: EXPLICIT selection, else file-ish word under cursor | pardes-specific, keep (helix normal-mode Enter unbound). Covers helix `gf`. Implicit motion residue falls back to the cursor word | pardes-specific | | `Tab` (normal) | acme **execute** chord | pardes-specific, keep; explicit-selection rule as Enter | pardes-specific | -| `:` (normal, body or tag) | in the body, focuses the pane's tag in normal mode at its remembered cursor (the first time, on `Save`); in the tag, goes back to the body; in a column or workspace tag, back to the active pane. The tag's normal and insert modes ARE the body's: every motion, selection, edit and undo works there, and `0` goes to the line's start, the path's. Tab runs the word under the cursor or the selection and Enter looks it up (in a column or workspace tag Enter runs it too), and either hands the keyboard back to the body first. Clicks choose a new cursor position and type into the tag. | Each tag keeps its own cursor during the session. The computed path/marker/page is reachable and yankable but read-only: an edit into it is refused, and typing into a file's path drafts a new name. File-name changes are staged as described in [editable tags](tags.md). | pardes-specific | -| `Ctrl-w k` / `SPC w k` (pane with nothing above) | focuses its column's tag, then the workspace tag. `Ctrl-w j` walks back to the panes, `Ctrl-w h`/`l` walk the column tags. Headers edit exactly as a pane tag does. | Column commands target that column's active pane, or its first pane when coming from elsewhere. Workspace and column text are independently editable and persist in dumps. See [editable tags](tags.md). | pardes-specific | +| `:` (normal, body or tag) | in the body, focuses the pane's tag in normal mode at its remembered cursor (the first time, on `Save`); in the tag, goes back to the body; in a column or workspace tag, back to the active pane. The tag's normal and insert modes ARE the body's: every motion, selection, edit and undo works there, and `0` goes to the line's start, the path's. Tab runs the word under the cursor or the selection and Enter looks it up (in a column or workspace tag Enter runs it too), and either hands the keyboard back to the body first. Clicks choose a new cursor position and type into the tag. | Each tag keeps its own cursor during the session. The computed path/marker/page is reachable and yankable but read-only: an edit into it is refused, and typing into a file's path drafts a new name. File-name changes are staged as described in [editable tags](typ/guide.typ). | pardes-specific | +| `Ctrl-w k` / `SPC w k` (pane with nothing above) | focuses its column's tag, then the workspace tag. `Ctrl-w j` walks back to the panes, `Ctrl-w h`/`l` walk the column tags. Headers edit exactly as a pane tag does. | Column commands target that column's active pane, or its first pane when coming from elsewhere. Workspace and column text are independently editable and persist in dumps. See [editable tags](typ/guide.typ). | pardes-specific | | `Ctrl-w` + `h/j/k/l`/arrows | directional pane focus prefix — editor normal mode only | pardes' own window handling (helix window mode skipped, section C). Runs the SAME `Left`/`Down`/`Up`/`Right` builtins `SPC w h/j/k/l` runs; Raw **tty** mode forwards Ctrl-w to the child. Insert mode owns `Ctrl-w` = delete-word-back, so a tag being TYPED into swallows it; from a tag in normal mode it moves focus to the neighbour's BODY, as from a body, and from the top pane `Up` reaches the column and workspace tags | pardes-specific | | `Alt-n` | new terminal below (outside raw tty) | shadows helix `Alt-n` (select next sibling), which pardes spells `Alt-right` alone | pardes-specific | | `Alt-c` | move active terminal to a fresh column (outside raw tty) | helix `Alt-c` is change-noyank; the pardes window op wins (do-not-touch contract). `Alt-d` + `i` covers the behavior | waived (`alt-c-window-op`) | @@ -135,7 +135,7 @@ language-backend queries, and the shell pipe. | `SPC y` `SPC Y` `SPC p` `SPC P` `SPC R` | helix's clipboard menu on helix's own letters: yank the selection to the system clipboard (`ClipYank`) or the PRIMARY selection alone (`ClipYankMain`), paste the system clipboard after (`ClipPaste`) / before (`ClipPasteBefore`) the selection, replace the selection with it (`ClipReplace`) | the same as `"+y`, `"+p`, `"+P`, `"+R`: register `+` (and `*`) IS the desktop's clipboard, the only register that reaches it. `SPC y` writes `+` alone, as helix's does, not the default register too. Builtins rather than bare chords because a leader path names a builtin: they land in Help's index and are executable words like every other verb. The clipboard holds one text, so N values go out joined by newlines, as helix sends them. On a tty the write is OSC 52 out and the READ is OSC 52 back, which many terminals refuse or gate — so `SPC y` works there and `SPC p` can be a no-op | out of corpus | | a paste from the OUTER terminal | one `Event.paste`, spliced in at the cursor | the tty shell enables bracketed paste and coalesces `paste_start`..`paste_end` into a single event; before that the bytes arrived as individual key presses and normal mode RAN them, which is how a pasted `d` deleted a line. The bytes deliberately never enter the yank register — clipboard and default register are separate stores in both directions | pardes-specific | | `/` (any pane) | pardes' own plain-substring search into a `+Search` output buffer: the pattern is typed on a line of its own on the pane's notice band, Enter fills the buffer, and its rows are ordinary look targets. Enter also GOES to the first row — the buffer is focused and then the step `n` is and the look Enter is run in it (`Pardes.lookFirstHit`), so `/foo` lands on the first hit with the matched span selected. A pattern that matched nothing opens its empty buffer and moves nothing | KEEP, do not touch; not in the corpus (helix `/` is regex search). Find and Grep answer with OTHER files and deliberately do NOT jump. The stepping half is the next row | pardes-specific | -| `n` / `N` (any pane) | MOTION, not a jump: move the SELECTION to the next / previous look-able text and open NOTHING. Enter — the look chord — on what it leaves selected is what opens it | KEEP, do not touch; not in the corpus (helix's `n`/`N` walk regex search hits). What a step selects is the pane's GRAIN (`output_pane.Grain`, read in `Pardes.lookSpanIn`): in FREE TEXT — a terminal, a file, a PDF, a prose answer buffer — the largest whitespace-delimited run `look.resolve` can act on (`look.lookableSpan`, wrapper punctuation peeled off both ends), several to a line; in a RESULTS BUFFER one stop per ROW, the largest run its head resolves as (`look.lookableLineSpan`), because a row there IS one location and the words after it are the match rather than a second place to go; in a COMMAND list the whole line. The walk is a RING across PANES: every pane that has performed a Look, most recent first (`Pardes.look_src`), then the output buffers that have not, newest first, and only when both are empty the active pane. Exhausting a pane enters the next at its first (forward) / last (backward) span and the end wraps to the start, so `N` is the exact inverse of `n`. What it lands on becomes an EXPLICIT `vsel` with the cursor on its FIRST column, in the pane the walk focuses. ONE motion in every pane kind and every buffer kind — a PDF steps the `+Search` buffer its own search filled, `n` to select the row and Enter to jump. The single thing a buffer may change is that grain, and it changes it by BEING a kind of buffer rather than by a branch: `output_pane.Traits.steps` (a list of locations) makes a step take one row at a time, and `Traits.commands` makes it take the WHOLE LINE, because a command list (`Themes`/`Fonts`) holds words to run and there is no path inside `Theme gruvbox` to pick out. Tab on what `n` selected wears the theme, which is the same middle click on the row is. `]d`/`[d` are helix's diagnostic motions, a different binding, and they do still jump to each diagnostic (`docs/lsp.md`) | pardes-specific | +| `n` / `N` (any pane) | MOTION, not a jump: move the SELECTION to the next / previous look-able text and open NOTHING. Enter — the look chord — on what it leaves selected is what opens it | KEEP, do not touch; not in the corpus (helix's `n`/`N` walk regex search hits). What a step selects is the pane's GRAIN (`output_pane.Grain`, read in `Pardes.lookSpanIn`): in FREE TEXT — a terminal, a file, a PDF, a prose answer buffer — the largest whitespace-delimited run `look.resolve` can act on (`look.lookableSpan`, wrapper punctuation peeled off both ends), several to a line; in a RESULTS BUFFER one stop per ROW, the largest run its head resolves as (`look.lookableLineSpan`), because a row there IS one location and the words after it are the match rather than a second place to go; in a COMMAND list the whole line. The walk is a RING across PANES: every pane that has performed a Look, most recent first (`Pardes.look_src`), then the output buffers that have not, newest first, and only when both are empty the active pane. Exhausting a pane enters the next at its first (forward) / last (backward) span and the end wraps to the start, so `N` is the exact inverse of `n`. What it lands on becomes an EXPLICIT `vsel` with the cursor on its FIRST column, in the pane the walk focuses. ONE motion in every pane kind and every buffer kind — a PDF steps the `+Search` buffer its own search filled, `n` to select the row and Enter to jump. The single thing a buffer may change is that grain, and it changes it by BEING a kind of buffer rather than by a branch: `output_pane.Traits.steps` (a list of locations) makes a step take one row at a time, and `Traits.commands` makes it take the WHOLE LINE, because a command list (`Themes`/`Fonts`) holds words to run and there is no path inside `Theme gruvbox` to pick out. Tab on what `n` selected wears the theme, which is the same middle click on the row is. `]d`/`[d` are helix's diagnostic motions, a different binding, and they do still jump to each diagnostic (`docs/typ/guide.typ`, Keys) | pardes-specific | | insert: printable text | file: real edit; terminal: typed run splice | | helix-verified (file) | | insert: `Enter` | newline; keeps the current full indent levels and adds one 4-space logical tab when the text before the cursor ends in `(`, `[`, `{`, or `)` (including `})`) | plain lines match `insert_newline`; delimiter heuristic is pardes-specific | helix-verified (plain) / pardes-specific (delimiter) | | insert: `Backspace` (+ `Shift-Backspace`) | delete prev char, joins lines at col 0; in a line's leading blanks, back to the previous 4-column indent stop (a whole unit when on one; a tab still goes alone) | matches `delete_char_backward` and its dedent; `Ctrl-h` alias in B | helix-verified | @@ -271,7 +271,7 @@ compiles a runtime pattern with no allocator, called the way sam searches haystack, so `^` and `$` match at every line's start and end and `.` never crosses a newline, and a pattern naming `\n` runs over the whole selection. Where helix's `^` is only the selection's start, this is sam's. mvzr -backtracks without bound of its own, so the step budget docs/fs.md gives +backtracks without bound of its own, so the step budget docs/typ/reference.typ gives for `addr` holds here too: a search that runs out of it (about 300 ms) keeps the matches it found so far. diff --git a/docs/lsp.md b/docs/lsp.md deleted file mode 100644 index 22cf1b90..00000000 --- a/docs/lsp.md +++ /dev/null @@ -1,73 +0,0 @@ -# Language intelligence - -Native hosts, including detached sessions, run language queries on workers. -Every language, Zig included, goes through the protocol client in -`src/lsp/lsp_client.zig`, which runs its server as a child process: Zig's is -`zls`, which must be on PATH. Web and board builds have no language backend. - -`Lspinfo` (`SPC l i`) reports backend versions, server state, capabilities, -recent timings and errors. `Lspwhy` (`SPC l w`) traces resolution at the cursor. - -## Commands - -| Keys | Action | -|---|---| -| `gd`, `gD`, `gy`, `gi`, `gr` | Definition, declaration, type, implementation, references | -| `SPC l k` | Hover | -| `SPC l r` | Rename | -| `SPC l a` | Code action | -| `SPC l h` | Select references | -| `SPC l s`, `SPC l S` | Document and workspace symbols | -| `SPC l d`, `SPC l D` | Document and workspace diagnostics | -| `]d`, `[d`, `]D`, `[D` | Next, previous, last and first diagnostic | -| `=` | Format | -| Ctrl-click | Definition | -| Insert-mode Tab after `.` | Candidate declarations | -| `SPC l c`, `SPC l C` | Incoming and outgoing calls | -| `SPC l t`, `SPC l T` | Supertypes and subtypes | - -A single goto result jumps directly; several results open a reusable search -buffer. `n`/`N` select result rows and Enter opens them. Hover opens prose. -Locations use one-based `path:line:column` or `path:line:column-endcolumn`. -Paths below the requesting pane's directory are relative to that directory; -other paths stay absolute. - -Completion lists declarations and inserts nothing. An unanswered Tab indents -only if the cursor has not moved since the request. Multicursor Tab indents -without starting a query. - -Formatting and same-file rename apply as one undo transaction. A workspace -rename spanning other files opens a preview; it does not partially apply the -rename. - -## Configuration and testing - -`PARDES_LSP_ZIG`, `_RS`, `_C`, `_GO`, `_TS` and `_PY` override the server -executable for each language. An empty value disables that server. `Lspinfo` -shows the effective settings. - -The protocol client keeps one child server per configured language, negotiates -position encoding, and handles both push and pull diagnostics. Startup and -request waits have deadlines; failed starts back off before retrying. Worker -status messages reach the host event queue. - -`zig build lspbench -- probe gd <file> <line>:<column>` queries the same backend from -the command line. `zig build lspbench` measures it. The Zig snapshot tests -(lsp, lspcomplete, lspdebug and the rest) run the real `zls`; the protocol -ones use a Zig mock server with deterministic answers while exercising the real client, -including process startup, framing, edits and undo. `zig build fs-test` also -checks language-worker delivery in TTY and detached sessions over 9P. - -## Ownership - -`host_io.Lsp.Job` owns copies of the source, path, argument and root. Workers -never read the live core. A request ID and pane serial reject obsolete replies; -mutating replies also require the original file revision. Restore cancels -owned work before replacing the core. - -Backends implement `query`, `speaks` and `supports` in `src/lsp/`. The first -backend supporting the file and query answers; status queries visit all of -them. Results are written to the caller's writer. Use `lsp.row` or `spanRow` -for locations, `edit` for rename ranges, and `put` for replacement text. -Edit records use half-open byte offsets into the request's source snapshot. -The core validates the complete response before applying it. diff --git a/docs/tags.md b/docs/tags.md deleted file mode 100644 index 5d5bfab2..00000000 --- a/docs/tags.md +++ /dev/null @@ -1,222 +0,0 @@ -# Tags and columns - -Pardes has three levels of command text: the workspace tag, a tag per -column, and each pane's tag. The rule for the workspace and column tags: - -- A builtin that acts on a pane (`Del`, `Save`, `Undo`) acts on the - column's active pane (its first pane when focus comes from another - column); from the workspace tag, on the pane with the keyboard. -- A column's own words (`New`, `Tty`, `Delcol`) act on that column. -- A shell command runs as a command pane of its own, with `run` and `exit` - records, in the session's directory (where pardes started), as acme's row - and column tags have none of their own. It is never typed into the - terminal that has the keyboard. `Tty` starts there too. -- Over 9P (`/tagexec`, `col/<n>/exec`) there is no click to say which pane - is meant, so a pane's word written there is refused, pointing at - `pane/<n>/ctl`; the tag's own words and shell commands run as above. - -One run from a pane's tag or text starts in that pane's directory, and a -shell command there is typed into the pane when it is a terminal at its -prompt. Over 9P the tags are `/tag`, `/col/<n>/tag` and -`/pane/<n>/tag` ([fs.md](fs.md#columns-and-tags)). - -## Default words - -- Workspace: `Newcol Joincol Find Grep Help Changelog Tutor Dump Themes - Config Debug Exit`. -- Column: `New Tty Find Grep Joincol Delcol`. -- File pane: `Save Tty Collapse Del`; a source file with a grammar adds - `TreeContext`, a result list `LocationsConfig`. PDF: `manual.pdf [1/12] - Tty PdfSections PdfTint Collapse Del`. -- Terminal: `Tty+bash Save Mode Filter Collapse Del`. `Tty+bash` is one word - for `Tty bash`, opening another terminal on that shell (only `Tty` reads a - `+` so). `Mode` cycles raw terminal input, normal mode and insert mode. -- Command pane: `<dir> (<line>) running`, then `exit N`, and `Kill`. - -`Undo`, `Redo` and `Mode` (on files) work typed or clicked though they are -not in the default tags. Customized tags keep their text. - -A tag word runs as acme's does, in the pane's directory with no file named: -`wc` alone waits on stdin. There is no `$%`; name the file (`wc notes.txt`), -or select the name and middle-click `wc`, which takes a held selection as its -argument. - -A program that tracks the mouse gets B1 and the wheel; B2 and B3 stay -pardes's; Shift swaps each. In raw terminal input, a program that asked for -the mouse (xterm's 1000, 1002 or 1003: htop, vim with `mouse=a`, codex) -gets B1's clicks and drags and the wheel over its grid, reported in the -format it chose and with its modifiers; Shift-B1 selects as ever and -Shift-wheel scrolls pardes's scrollback. B2 and B3 execute and look there -as in any pane, sweeps and chords included; Shift-B2 and Shift-B3 go to the -program as its buttons 2 and 3, the Shift left out. A full-screen program -that does not track the mouse gets the wheel as arrow keys (xterm's -alternate scroll, 1007). Tags, grips and gutters stay pardes's. - -`Repl python` in a terminal's tag binds it as that language's REPL -([fs.md](fs.md#repls)). `Repl` takes the languages a code fence names: ada, -bash, c, c_sharp, clojure, cpp, css, elixir, erlang, fortran, go, haskell, -html, java, javascript, json, kotlin, ocaml, markdown, pascal, php, -powershell, python, ruby, rust, scala, typst, zig, and aliases such as `py` -and `sh`. - -## Pane builtins - -`Collapse` folds a pane to its tagline, giving its rows to the nearest -expanded pane above (else below); again, it takes them back. Its text and -process are kept. - -`Del` closes a pane, giving its rows to one neighbour. `Del k` (`DelAbove`) -gives them to the nearest expanded pane above, `Del j` (`DelBelow`) below, -each falling back to the other side. Bare `Del` from the keyboard (`SPC d`), -with expanded panes above and below, asks on the notice band (`k`/Up above, -`j`/Down below, any other key keeps the pane); a click, a 9P write or an -`init` line never asks and gives the rows above. - -A pane with unsaved text is refused once: a notice `1 unsaved pane — Del -again to discard`, the pane listed in `+Unsaved`, and over 9P the write -fails (EIO). The same `Del` again, nothing edited since, discards. `Delcol` -refuses a column holding such a pane the same way, without opening -`+Unsaved` or moving focus. `Exit` and `Restore` do the same over the whole -session. A `+New` scratch under 100 bytes is never asked about. - -`Edit` runs sam's command language on the body ([fs.md](fs.md#edit)). -`Undo` and `Redo` step the body through its last 256 edits, as `u` and `U`. - -Unsaved text shows on the pane's grip, as acme's modbutton, not in the tag. -In a terminal the grip is two cells: the pane's mode (blank normal, `^` -insert, `$` tty mode), then `*` while unsaved. - -## Reviewing diffs - -Open a `.diff` or `.patch` file, or run `git diff` (or `git show`, `diff -u -old new`) as a command from any tag: once the command has finished, output -that starts as a diff does (a `diff --git` line, or a `--- ` line with `+++ -` and `@@` under it) is shown as one. Each hunk's code is coloured in the language of the file its -section names (`+++ b/<path>`, or `--- a/<path>` for a deleted file), its -old side (context and removed lines) and new side (context and added lines) -each parsed as one text, so a string or comment across lines colours as it -does in the file. Added and removed rows are tinted to the pane's edge, -their `+`/`-` in the tint's hue (the theme's ANSI green and red where it -has them); a file in no language pardes knows keeps the plain line colours. -Only the hunks in view are parsed, a few dozen lines at a time, each once. - -Then right-click to jump: a diff line looks up the path and line it -names, as any look does. The hover shows the whole line; the look is of -the address it names, exactly as if that text had been selected by hand -and right-clicked, with the same resolution (the pane's directory, then -the places the jumplist has been), placement and errors. On a `diff ---git`, `---` or `+++` line, anywhere on it, markers included, that is the -file: `diff --git a/x b/y` and `+++` name the new one, `---` the old one -unless the `+++` under it names another (a rename, a new file, `diff -u -old/x new/x`), the old name being the one likely gone. A `@@ -a,b +c,d -@@` line names `path:c`. On a hunk line, the `+`, `-` or space in its -first column names `path:` its new line; for a removed line, the new line -now standing where it was (the one after it, or the hunk's last when it -went from the end of the file). The code after it is words, looked at as -ever. A 9P `look` of a whole line of the diff is the same look. - -The path is the repository's name for the file. In a git section (one -with a `diff --git` line) git's side prefix is dropped when both paths on -that line carry one, different ones: `a/`/`b/`, or with -`diff.mnemonicPrefix` `c/`, `i/`, `w/`, `o/`. `--no-prefix` writes none, -so there `a/x a/x` is a real directory `a`, kept. A plain `diff -u`'s names -are used as written, past the timestamp. - -## Editing tags - -A tag is text like a body, with the body's normal and insert modes and undo. -A pane tag may hold several lines and wraps, taking a row per shown line up -to eight and leaving its body at least one; a collapsed pane shows the first. -Up on its first row in insert mode (or Alt-Up in either mode) folds a tag to -one row, Down on its last (Alt-Down) unfolds it. Column and workspace tags are -one line: a newline typed, pasted or written into one becomes a space. - -The path (and a PDF's page) at the start of a pane tag is computed and -read-only; `0` goes to its start, and motions select and yank across it. -Typing into a file's path, or clicking it, starts a draft of a new name: -Enter or Tab confirms it, Escape or leaving the pane cancels. Confirming -changes the buffer's save target and marks it unsaved; nothing on disk is -renamed until `Save`. A pane-tag command confirms a valid draft first. - -Left-click a tag to type at the click, in insert mode. Esc is normal mode: -there Tab executes the word under the cursor (or the selection); Enter looks -it up in a pane's tag and runs it in a column or workspace tag. Executing -gives the keyboard back to the body first. Paste goes to the focused tag. - -A look at a relative path is tried in this pane's directory, then in the -directories of places you've visited (the jump list). - -`:` in a body's normal mode focuses its tag (the first time, on `Save`); `:` -in a tag goes back. The window keys (`Ctrl-w`, `SPC w` with `h/j/k/l`) move -between panes; up from the top pane reaches its column's tag, then the -workspace's, and Left/Right walk the column tags. Search, `s`/`S`, pipe and -Save's path prompt get a line on the pane's notice band; in a tag `s`, `S` -and `|` act on the tag's own text, while `/` searches the body. - -## Moving and resizing - -Drag a pane's grip up or down its column and its top follows, the pane -above giving or taking rows, down to its tag alone; drop it in another -column and it moves there. In the GUI the 2 px rule between panes drags too. -A column's right edge drags its width. Drag a column's grip past a -neighbour's middle to move the whole column there; short of that it moves -the column's left edge. - -A terminal keeps its tag and 2 body rows: no drag, squeeze or smaller -window takes it below that, and `pty/ctl`'s `winsize` gives a pty 2 rows at -least. A text pane can be dragged down to its tag. - -## Empty columns - -A column can hold no pane, as acme's can: its tag stands over blank space in -the theme's `empty_col` colour. `Newcol` makes one right of the keyboard's -and gives its tag the keyboard. Closing a column's last pane leaves the -column empty, the keyboard on its tag. Only `Delcol` and `Joincol` take a -column away; `Joincol` keeps the right column's tag, its panes below. A pane -dragged onto an empty column fills it. `Delcol` of the last column leaves -the workspace tag alone; `Newcol` or `New` starts again. Unlike acme, pardes -quits when the session's last pane closes. - -## Where new panes go - -Every new pane goes through one placement, chosen by `Placement acme` (the -default) or `Placement pardes` (bare flips it; `SPC c p`). - -No placement leaves a pane shorter than its tag and 2 body rows. Where the -chosen place has not that room, the column's tallest pane is halved; where -no one pane can give it but the column holds every pane's minimum with the -new one's, the rows are shared out again; otherwise the pane is refused, -`no space for a pane in that column: each keeps its tag and 2 rows` (ENOSPC -over 9P). A pane alone in its column always fits. - -`Placement acme` is acme's makenewwindow. The active column is the one last -typed or clicked in, dropped into, whose tag was given the keyboard, or that -was given the last new pane; a Look moves the keyboard, not the active -column. A new pane goes into the column whose tag the command came from, -else the active column, never into a new column: - -- an empty column it takes whole; -- from a tag, or 9P's `pane/new`, it takes the bottom half of the column's - last pane; -- from a pane's text (a Look, `Tty`, `Alt-n`, a Grep or Find listing), it - goes under the text of the pane with the most blank rows when that is - more than 15 rows, or more than 3 and more than half the biggest pane; - otherwise it halves the biggest pane, or the asking pane when that is in - the column and not much smaller; -- `New` goes to the bottom half of its own column's last pane; -- a command pane or `+Errors` pane goes to the last column's last pane (a - command from a column's tag to that column, reusing a finished command - pane only there). With no room anywhere, `+Errors` text is logged as `msg` - records. - -`Placement pardes`: an empty column whose tag asked, or has the keyboard, is -filled; a scratch goes under the pane that asked; a shell under it or the -nearest pane with room; a document beside the last one read, or in a column -of its own on the left when there is none and the column is at least 200 -cells wide; a command pane at the foot of the last column. - -## Saved workspaces - -`Dump` and `Restore` keep workspace and column tags (empty ones too) and -empty columns ([config.md](config.md#dumps)). The column row stays above -the panes with `Tagbottom` on; on screens under three rows it is left out. diff --git a/docs/typ/cheatsheet.typ b/docs/typ/cheatsheet.typ index d9c3f573..5069614a 100644 --- a/docs/typ/cheatsheet.typ +++ b/docs/typ/cheatsheet.typ @@ -4,8 +4,6 @@ // against the registry, the keymap and the default tags. #import "style.typ": key, btn, chord, word, tag, addr, file, cmd, doc -= pardes #h(1fr) _acme's tags and three buttons, helix's keys, terminals as panes, the editor as a 9P filesystem_ - == The mouse / #btn("B1"): select, and focus the pane. On a tag: type there. / #btn("B2"): *execute* the word or selection: a builtin, else a shell line. @@ -46,7 +44,7 @@ Typed anywhere, then #btn("B3") or #key("Enter"): Addresses are sam's: `#n`, `/re/`, `?re?`, `$`, `a,b`. #doc("fs", section: "look-and-exec") == Keys: normal mode -Helix-style: motions *select*, then an edit acts on the selection. Mode box: blank normal, `^` insert, `$` tty. +Helix-style: motions *select*, then an edit acts on the selection. Mode box: blank normal, `^` insert, `$` raw terminal. #table(columns: 2, [#key("h j k l") #key("w b e")], [move; words select], [#key("g g") #key("g e")], [first line, last line], @@ -71,9 +69,9 @@ Helix-style: motions *select*, then an edit acts on the selection. Mode box: bla == Keys: panes #table(columns: 2, - [#key("Ctrl-w") #key("h j k l")], [focus a neighbour (anywhere)], - [#key("Esc")], [normal mode: hop to the previous pane], - [#key("Shift-Esc")], [leave, whatever Esc means here], + [#key("Ctrl-w") #key("h j k l")], [focus a neighbour (not in insert or raw `$`)], + [#key("Esc")], [back to the previous pane (from raw `$` only at an empty shell prompt)], + [#key("Shift-Esc")], [back to the previous pane from raw `$` or a PDF; in a terminal's normal mode, into raw], [#key("Ctrl-o") #key("Ctrl-i")], [jump history back, forward], [#key("Alt-n")], [new terminal below], [#key("Alt-c")], [move the pane to a new column], @@ -97,8 +95,8 @@ Helix-style: motions *select*, then an edit acts on the selection. Mode box: bla ) == Terminals and commands -- A terminal is a pane. #key("Ctrl-b") toggles raw input (`$`) and editor mode, where the same keys move over its text, prompts hidden. -- Raw: keys go to the program; #key("Esc") at a shell prompt hops away, #key("Shift-Esc") always. +- A terminal is a pane. #key("Ctrl-b") switches it between raw (`$`) and normal mode, where the same keys move over its text, prompts hidden. +- Raw: keys go to the program; #key("Esc") at an empty shell prompt goes back to the previous pane, #key("Shift-Esc") always. - #word("Mode") cycles raw, normal, insert. #word("Filter") maps its colours to the theme. #word("Tty+bash") opens another on that shell. - #btn("B2") on any non-builtin line (`make`, `git log`) runs it with #word("Shell") `-c` in the pane's directory, in a *command pane*: #tag("Kill Save Collapse Del", path: "/home/me/src (make) exit 0") - #word("Kill") stops what pardes started (`Kill make`: those lines); #word("Exit") quits. diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ new file mode 100644 index 00000000..080ed5ea --- /dev/null +++ b/docs/typ/guide.typ @@ -0,0 +1,303 @@ +// The guide: pardes day to day, read once in about ten minutes. The +// cheatsheet is the index of keys and words; the reference has every +// 9P file; scripting has the recipes. +#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs + +pardes is a screen of columns. Each column holds panes, and each pane is a +tag (a line of words) over a body: a file, a terminal, a PDF or an image. +Above the columns runs the workspace tag, and each column has a tag of its +own. Any text anywhere can be clicked: #btn("B2") runs it, #btn("B3") looks +at it. The keys are helix's, in modes. + += Modes <modes> + +Each pane has its own mode and keeps it while you are elsewhere: leave a +terminal at its `$` and it is still `$` when you come back. The box at the +left of a pane's tag shows the mode. + +#pairs( + [normal (box blank)], [keys move and select; an edit acts on the selection. File and PDF panes start here, and so does a terminal from #key("Alt-n").], + [insert (`^`)], [keys type. #keys("i", "a", "o") and the rest enter it.], + [raw (`$`), terminals only], [keys go to the program. A terminal from #word("Tty"), the shell a bare `pardes` starts with, and a command pane start here.], +) + +#key("Ctrl-b") switches a terminal between raw and normal; in normal mode +the shell's prompts are hidden and its text is a page to move over and +copy from. #word("Mode") in the tag steps raw, normal, insert. Esc and +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 `$`.], + [a PDF], [#key("Esc"): clears the selection and the search highlights. #key("Shift-Esc"): back to the previous pane.], +) + +"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 +program, so #key("Ctrl-w") and #key("Alt-n") need you out of raw mode +first (and #key("Ctrl-w") out of insert mode, where it deletes a word). + += The mouse <mouse> + +#pairs( + btn("B1"), [select; a plain click puts the cursor there and gives the pane the keyboard. In a tag it starts typing at the click, in insert mode. A double click selects the word, the line (at a line's start or end), or up to the matching bracket or quote.], + btn("B2"), [execute: a builtin word runs, anything else is a shell line.], + btn("B3"), [look: open the file, address, directory or URL, or else find the word's next place.], +) + +A #btn("B2") or #btn("B3") click with no drag takes the word under it, +where a word is the run of letters, digits, non-ASCII and `. - + / : @ _ ~` +around the click; a trailing `:` is dropped. So #btn("B3") anywhere on +`src/bar.c:12:5:` in a compiler's `src/bar.c:12:5: error` opens +`src/bar.c` at line 12, byte column 5, while a click on `error` finds +`error`. Drag instead to say exactly what you mean. #key("Enter") and +#key("Tab") in normal mode look and execute the selection, or the word +under the cursor. The chords are on the cheatsheet. + += Tags <tags> + +There are three kinds, and which directory a command runs in depends on +which one you click: + +#tag("Newcol Joincol Find Grep Help Changelog Tutor Dump Themes Config Debug Exit") +#tag("New Tty Find Grep Joincol Delcol") +#tag("Save Tty Collapse Del", path: "/home/me/notes.txt") + +- The workspace tag and the column tags run commands in the session's + directory (where pardes started). A column's own words (#word("New"), + #word("Tty"), #word("Delcol")) act on that column; a pane word + (#word("Save"), #word("Del")) acts on the column's pane with the + keyboard, or its first; from the workspace tag, on the pane with the + keyboard. +- A pane's tag runs commands in the pane's directory: a file's directory, a + terminal's current directory. + +A tag is text with undo: type a word into it and click it, or delete the +defaults. #key(":") moves the keyboard between a body and its tag. A pane +tag may wrap onto several lines; column and workspace tags are one line. +The path at the start of a pane's tag is computed: typing into it, or +clicking it, drafts a new name, #key("Enter") confirms and the next +#word("Save") writes there; #key("Esc") cancels. #word("Collapse") folds a +pane to its tag. Unsaved text shows on the grip, the box left of the tag. +Drag the grip up or down to resize, or onto another column to move the +pane there. + += The active column and the keyboard <active-column> + +Two things are easy to confuse: + +- *The pane with the keyboard* is where your keys go. +- *The active column* is where new panes go. It is the column you last + typed in, #btn("B1")-clicked in, dropped a pane into or gave the keyboard + to its tag, or the one that got the last new pane. A look does not move + it. + +They usually agree. Here is how they split. You are typing in column 1, so +column 1 is active. You click #word("New") in column 2's tag: a scratch +pane opens in column 2 and takes the keyboard, and column 2 is now +active. In it you #btn("B3") `x.txt`, which is already open in column 1: +the keyboard jumps to that pane in column 1, but the active column stays +2, so the next pane a look opens lands in column 2, away from where you +are. Type or click in column 1 and it is active again. + +== Where new panes go <new-panes> + +#word("Placement") picks the rules: `acme` (the default) or `pardes`. +Under `acme`, a new pane goes into the column whose tag asked, else the +active column, never into a new column. An empty column it takes whole; +#word("New") and 9P's #file("pane/new") take the bottom half of the +column's last pane; a pane opened from a pane's text (a look, #word("Tty"), +#key("Alt-n")) goes under the pane with the most blank rows, or halves the +biggest; a command pane goes to the last column. #word("New") in a pane's +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. + +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 +away. Closing the session's last pane quits pardes. + += Looking <looking> + +#btn("B3") (or #key("Enter")) on a path opens it, or goes to the pane that +already shows it; `file:12` goes to line 12. The address forms are on the +cheatsheet. A directory types `ls` into a terminal idle there, else opens +a terminal there. A URL opens in the browser. + +A relative path is looked for where the click was, then where you have +been: first in the looking pane's own directory, then in the directory of +each pane on the jump list, most recent first. The first that names a +file, or a pane open on that path, wins. `./x` and `../x` look only in the +pane's own directory. + += Command panes <command-panes> + +#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 +terminal's own text is typed into its shell. Anywhere else it runs in a +command pane, a terminal of its own: + +#tag("Kill Save Collapse Del", path: "/home/me/src (make) exit 0") + +Its tag says `running`, then `exit N`. The next command for the same +directory reuses a command pane that has finished (from a column tag, only +one in that column), below what it showed; a pane still running, or one a +background job still prints to, is never reused, so a second command +meanwhile gets a pane of its own. #word("Kill") stops what pardes started +(`Kill make`: those whose line starts with `make`); #word("Exit") quits +pardes. + += Unsaved panes <unsaved-panes> + +A pane whose text is unsaved is marked on its grip. #word("Del") on it +refuses once: a notice says `1 unsaved pane — Del again to discard` and the +pane is listed in `+Unsaved`. The same #word("Del") again, with nothing +edited since, discards it. #word("Exit"), #word("Restore") and +#word("Delcol") refuse once over unsaved panes the same way. A `+New` +scratch under 100 bytes and a command's output never hold anything up. + +From the keyboard (#key("SPC d")), #word("Del") on a pane with panes above +and below it also asks which neighbour takes its rows: #key("k") above, +#key("j") below, any other key keeps the pane. A click never asks, and +gives the rows to the pane above. + += Terminals <terminals> + +A terminal is a pane like any other. + +#tag("Tty+bash Save Mode Filter Collapse Del", path: "/home/me/src") + +- `Tty+bash` opens another terminal on that shell; a bare #word("Tty") + runs the #word("Shell") setting, `$SHELL` when it is executable, else + `/bin/sh`. +- #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. +- 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. +- A program that tracks the mouse (htop, vim with `mouse=a`) gets + #btn("B1")'s clicks and drags and the wheel over its grid; #btn("B2") and + #btn("B3") stay pardes's. Hold Shift to swap: #btn("B1", shift: true) + selects and Shift-wheel scrolls pardes's scrollback, while + #btn("B2", shift: true) and #btn("B3", shift: true) go to the program as + its buttons 2 and 3. A full-screen program that does not track the mouse + gets the wheel as arrow keys. Tags, grips and gutters stay pardes's. +- `Repl python` in a terminal's tag makes #btn("B2") on a `.py` pane send + the text to that REPL instead of running it. + += Reviewing diffs <reviewing-diffs> + +Open a `.diff` or `.patch`, or run `git diff` (or `git show`, `diff -u`) as +a command: once it has finished, output that starts as a diff is shown as +one, each hunk coloured in its file's language and its added and removed +rows tinted. Then #btn("B3") to jump, with the usual look resolution: + +- a `diff --git`, `---` or `+++` line opens the file (the new one, or the + old one on a `---` line unless the `+++` under it names another); +- a `@@ -a,b +c,d @@` line goes to line `c` of the new file; +- the `+`, `-` or space in a hunk line's first column goes to that line in + the new file (for a removed line, the line now standing where it was); +- the code after it is ordinary words. + +git's `a/` and `b/` prefixes are dropped (and `c/ i/ w/ o/` with +`diff.mnemonicPrefix`); a `--no-prefix` diff's paths are kept as written. + += pardes FILE and --wait <editor> + +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. + +`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 +close it. Bare `pardes` inside a pane refuses and names `--nested`, which +starts a separate session whose shells do not forward to it. + += Keys <keys> + +Motions select what they cross, and an edit acts on the selection: #key("w") +then #key("d") deletes a word. #key("x") selects lines, #key("v") extends +characters, #key(";") collapses to the cursor. #key("s") makes a cursor +per regex match inside the selection, and every edit then acts at each. +#key("/") is a case-insensitive substring search, one hit a line; the +regexes are on #key("s") and #key("S"). #keys("n", "N") step through +everything a look would open, across panes. Line end is #key("g l"), and +#key("$") is helix's keep-pipe. #key("SPC") starts the leader, +#key("SPC ?") lists every path, and #word("Help") lists every key and +builtin. #word("Tutor") (#key("SPC h t")) practises them. + +The language servers run as child processes, one per language when it is +on `PATH`: `zls`, `rust-analyzer`, `clangd`, `gopls`, +`typescript-language-server` and `pyright-langserver` +(`PARDES_LSP_ZIG`, `_RS`, `_C`, `_GO`, `_TS`, `_PY` name another, empty +turns one off): + +#pairs( + [#keys("g d", "g D", "g y", "g i", "g r")], [definition, declaration, type, implementation, references; Ctrl-#btn("B1") is a definition too], + [#keys("SPC l k", "SPC l r", "SPC l a", "SPC l h")], [hover, rename, code action, select the references], + [#keys("SPC l s", "SPC l S", "SPC l d", "SPC l D")], [symbols and diagnostics, of the file and the workspace], + [#keys("SPC l c", "SPC l C", "SPC l t", "SPC l T")], [incoming and outgoing calls, supertypes and subtypes], + [#keys("] d", "[ d")], [next and previous diagnostic], + [#key("=")], [format], + [#keys("SPC l i", "SPC l w")], [#word("Lspinfo"): the servers' state; #word("Lspwhy"): why the last query found what it did], +) + +One answer jumps; several open a list where #key("Enter") on a row goes +there. In insert mode #key("Tab") after a `.` lists the candidate +declarations and inserts nothing. A format or a rename within the file is +one undo step; a rename that reaches other files opens a preview instead +of changing them. + += Sessions <sessions> + +``` +pardes --detach=work & a session with no screen of its own +pardes --attach=work show it here +pardes-gui --attach=work the SDL window can attach too +``` + +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, +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. + += Config <config> + +#word("Config") (#key("SPC f c")) opens the startup file, +`~/.config/pardes/init` (`$XDG_CONFIG_HOME/pardes/init` when that is +absolute; on macOS `~/Library/Application Support/pardes/init`). Each line +is one builtin, run at start as if executed; `#` starts a comment, and a +line that fails is skipped silently. Text that is no builtin does not run +as a shell command. + +``` +Theme atelier +Shell zsh +Placement pardes +``` + +#word("DumpConfig") opens every live setting as the line that sets it, so +it pastes back into `init` as is. The settings are listed with the root +ctl in the reference (#doc("fs", section: "settings")). Keys are +compile-time, in `src/config.zig`. + +#word("Dump") writes the workspace to `pardes-<date>-<time>.zon` in +#word("DumpDir"), and `Restore path` (or `pardes -l dump.zon`) brings it +back: panes, columns, tags, selections, theme and changed settings; a +terminal returns with its last MiB of output and a new shell in its old +directory. Undo history and REPL bindings are not kept. A crash appends +two lines (build, time, platform, pid; the panic message) to `crashes` +beside `init`. diff --git a/docs/ui-review.md b/docs/ui-review.md index 6702ce31..cbc3c0d8 100644 --- a/docs/ui-review.md +++ b/docs/ui-review.md @@ -55,7 +55,7 @@ Pointer targets and the TTY grid are unchanged. `test/font_size.py` checks startup sizing, fractional sizing, retaining the size when omitted, and rejecting invalid requests without partial changes. -Six [classic-inspired themes](themes.md) add `forge`, `lagoon`, `solarium`, +Six [classic-inspired themes](typ/themes.typ) add `forge`, `lagoon`, `solarium`, `spectrum`, `harvest` and `clay`. ANSI colors retain their terminal meanings instead of borrowing a similarly positioned syntax color. Optional SDL `Pet cat` and `Pet frog` companions walk, idle and reverse in the unused @@ -98,7 +98,7 @@ tests and 797 SDL tests; SDL image/PDF and Kitty PDF rendering harnesses pass. ## Column and editable-tag follow-up The follow-up adds editable workspace and column command rows, compact pane -tags, staged buffer-name edits, and caret reveal for long tags. See [editable tags](tags.md) for the +tags, staged buffer-name edits, and caret reveal for long tags. See [editable tags](typ/guide.typ) for the exact interaction and save-target rules. `test/column_tags.py` exercises isolated SDL and TTY sessions against the same |
