summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 00:42:04 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit425d9beb9c977df364ed83743cb2efdef9429478 (patch)
treeeaba2e20ff041b50539d1b47fdeb498e59237cf0 /docs
parent1912878e753ce12fdc1ac72ae2e83ddfc18df002 (diff)
downloadpardes-425d9beb9c977df364ed83743cb2efdef9429478.tar.gz
pardes-425d9beb9c977df364ed83743cb2efdef9429478.zip
File the tag code into tagline.zig and draw tags beside bodies
With tags reduced to Texts, what is left of them is the computed prefix, the default and saved tails, entering and leaving a tag and the headers: that goes to tagline.zig, as acme keeps the tag half of a window in wind.c. Tag and header drawing moves next to body drawing in body_layer.zig, and the tag hit helpers go to tag_layer.zig with the Hit they read, where sameCell now also tells the lines of a taller tag apart. The docs describe the tag as a Text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs')
-rw-r--r--docs/design.typ44
-rw-r--r--docs/fs.md7
-rw-r--r--docs/helix-keys.md8
-rw-r--r--docs/tags.md60
4 files changed, 73 insertions, 46 deletions
diff --git a/docs/design.typ b/docs/design.typ
index 82bafb49..8e5f7e6e 100644
--- a/docs/design.typ
+++ b/docs/design.typ
@@ -530,9 +530,9 @@ Each pane kind keeps its storage and operations together in its own file
image pane in `image.zig`), reached through `panes.zig` beside `Pane`.
`layout.zig` owns placement and presentation state. `pardes.zig` handles input
and cross-pane state directly, without a pane vtable; the rest of the editor
-is split by thing as acme is: `edit.zig`, `normal.zig`, `look.zig`, `exec.zig`,
-`mouse.zig`, `Messages.zig`, `Pipe.zig`, `colors.zig`, `surface.zig`,
-`body_layer.zig` and `dump.zig`. Integration fixtures live
+is split by thing as acme is: `Text.zig`, `edit.zig`, `normal.zig`, `tagline.zig`,
+`look.zig`, `exec.zig`, `mouse.zig`, `Messages.zig`, `Pipe.zig`, `colors.zig`,
+`surface.zig`, `body_layer.zig`, `tag_layer.zig` and `dump.zig`. Integration fixtures live
in `test/panes.zig`, `test/output.zig`, and `test/pdf.zig`.
= State
@@ -770,33 +770,35 @@ pair per theme, and the three per-button tints and the dimmed extra cursors are
mixed off it, so what stays fixed is the distinction between buttons and not the
colours.
-`Drag` is a `union(enum)` with six arms (`pardes.Drag`): `none`,
-`border_v`, `border_h`, `move`, `tag`, `select`. `border_v` carries an optional
+`Drag` is a `union(enum)` with six arms (`mouse.Drag`): `none`,
+`border_v`, `border_h`, `move`, `column_move`, `select`. `border_v` carries an optional
`corner`: when the press lands on a cell that is both a column's vertical border
and one of the two adjoining columns' own horizontal borders, the one drag moves
*both* boundaries — never three, and when both columns happen to be split at the
grabbed row the left one wins, so the gesture that existed before is bit-for-bit
unchanged.
-== The tag is a command line
+== The tag is a text
-A tag is one line: a live prefix (mode indicator, cwd or path) plus an editable
-tail, and the tail gets the full modal editor. The coordinate space is UTF-8 byte
-offsets into the *whole* rendered tag, prefix ++ tail, always on grapheme
-boundaries (`Pane.tag_col`). The prefix is live chrome, so it is
-selectable, yankable and executable but READ-ONLY: every edit op measures from
-`edit0` — the first editable byte — and does nothing left of it.
+A tag is a `Text` (`Text.zig`), as a body is: acme's `Text`, one per
+`what` — the body, the pane's tag, the prompt line, a column's tag and the
+workspace tag. Each holds its own cursor, selections, mode and undo, so tags
+are edited by the same `edit.zig` and `normal.zig` code as bodies, and the one
+key they do not share is `:`, which moves between a pane's body and its tag.
+A tag holds any number of lines; a pane's tag takes a row per line up to
+`MAX_TAG_ROWS`, each drawn as a tag layer of its own.
-`tag_tail` is a fixed `[max_tag_tail]u8`, and the bound IS the storage
-(`limits.max_tag_tail`): every writer — `appendTag`, `tagInsert`,
-`restoreDumpTail`, the 9P `tag` file — refuses input that does not fit
-rather than truncating it. The schema limit and the buffer therefore can never
-disagree, which is what lets a dump reader reject data before copying it into a
-pane.
+A pane's tag shows a live prefix (path, dirty marker, PDF page) before the text
+it owns, and that prefix is computed at every read, never stored
+(`tagline.tagPrefix`). The Text's coordinates are offsets into its own text
+alone, so a rename or a save never moves its cursor. The one translation
+point is the whole line as shown: render, the mouse, Look, Exec and the 9P
+`tag` file see prefix ++ text, while keyboard motions and edits see only the
+text. Editing the path is a separate draft (`Pane.prompt = .name`) committed
+by Enter, since a buffer's name is not text it owns.
-A tag edit is always insert mode, so it hijacks the body's mode; `tag_mode`
-remembers what it hijacked and terminals restore it on exit, which is why
-clicking a tag never changes a pane's mode.
+The tag's text is bounded by `limits.max_tag_tail`: the 9P `tag` file and a
+dump reader refuse input that does not fit rather than truncating it.
= Diffing and presenting a frame
diff --git a/docs/fs.md b/docs/fs.md
index da4f4e68..c43034f6 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -180,7 +180,12 @@ back evaluates it, which is what acme(4) promises of its own `addr`.
The three flag files `dirty`, `mark` and `scroll` read `0` or `1` and take
`0` or `1`: whether the buffer differs from its file, whether a write pushes
an undo point (writing `1` pushes one now), and whether a write scrolls the
-pane. Truncating `tag` clears the part of the tag you may edit.
+pane.
+
+`tag` reads the whole tag as the pane shows it: the computed path, dirty
+marker or PDF page, then the text you may edit. A write appends to that text,
+newlines included, and a tag with more than one line takes a row per line on
+screen; truncating `tag` clears it, as acme's `cleartag` does.
Stats report real lengths for `index`, `status`, `look`, `exec`, `listeners`,
`name`, `body`, `tag`, `sel`, `ctl`, the range files and the flag files, and
diff --git a/docs/helix-keys.md b/docs/helix-keys.md
index 0813c193..64fa2e24 100644
--- a/docs/helix-keys.md
+++ b/docs/helix-keys.md
@@ -117,15 +117,15 @@ 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) | focuses the pane's tag in normal mode at its remembered cursor position (first entry starts at the commands). `h`/`l`, arrows, word motions and `0`/`$`/`^` move within the tag; `k` stays on its single line; `j` or `Esc` returns to the body. Uppercase `H`/`J`/`K`/`L` run `Left`/`Down`/`Up`/`Right` and enter the neighboring pane's tag. `K` beyond the top pane reaches the headers below. `y` yanks; Enter/Tab Look/Exec; `i`/`a`/`I`/`A` enter insert mode, where letters type normally. Explicit mouse clicks choose a new cursor position. | Each tag keeps its own cursor position during the session. Positions are UTF-8 byte offsets in the rendered prefix plus editable tail; file-name changes are staged as described in [editable tags](tags.md). | pardes-specific |
-| `K` (tag normal, topmost tagline) | focuses the column tag, then another `K` reaches the workspace tag. With `ColumnTags` disabled it goes directly to the workspace. `J` walks back down. Header arrows and `h`/`l` move by grapheme; word motions and `0`/`$`/`^` work too. `j`/`Esc` returns to the panel; lowercase `k` stays put. Enter/Tab executes in normal mode; `i`/`a`/`I`/`A` enter editing, where Enter is Look and Tab is Exec. | 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 |
-| `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, while the bare letters `h/j/k/l` there move to its TAGLINE (next row) | 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, `0` meaning the start of the editable text. Enter/Tab Look/Exec the word under the cursor or the selection, and executing 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. Cursor, selections and undo are offsets in the tag's own text; the computed path/marker/page before it is seen by the mouse, Look and Exec but not by keyboard motions. 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; with `ColumnTags` disabled it goes directly to the workspace. `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 |
+| `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` TS sibling-select — skipped anyway (tree-sitter) | 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`) |
| `Space` (normal, body) | starts the pardes LEADER: a key path runs the same builtin words used by tags and the topbar. The main groups are `f` files, `h` docs, `c` columns, `t` toggles/effects, `a` panel animations, `s` session, `j` jumps, `l` language, and `w` directional focus; `SPC ?` lists every path and `<prefix> ?` lists one group in `+Help`. The pending path appears on the active pane's transient body/message row. Esc or an unmapped key abandons it. Paths, Help rows, and dispatch are all generated from the builtin registry at comptime; the leader applies only to a BODY in normal mode because tags and tty programs own their input | pardes-specific; helix spends Space on pickers/LSP (section C), while pardes uses acme-style executable words |
| `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 ONLY five words in pardes that touch the desktop's clipboard — `y`/`d`/`c`/`p`/`P`/`R` and the acme cut/paste chords are the internal register alone. 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. One divergence: pardes keeps a single register VALUE where helix keeps one per range, so `SPC y` at N cursors joins them with newlines (`Pardes.setYank`, the divergence `msel-yank-paste` already waives). 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 tag takes the pattern, 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 |
+| `/` (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 (`ThemeSel`/`FontSel`) 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 |
| 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) |
diff --git a/docs/tags.md b/docs/tags.md
index f299c859..ecca046a 100644
--- a/docs/tags.md
+++ b/docs/tags.md
@@ -44,34 +44,54 @@ asks; there bare `Del` gives the rows to the nearest document above, as it
always has. A collapsed pane is never asked about, and collapsed neighbors
are passed over: they only keep a weight for later.
-Left-click a tag to place its caret; drag to select text, then type to
-replace it. Arrow keys, Home, End, Backspace and Delete edit the line.
-Tags reveal the caret horizontally when text is wider than their column.
-Escape returns to the body, keeping command-text edits. The existing Exec
-and Look gestures still apply; the workspace tag is no longer an inert
-left-click target. Pasted text goes to the focused tag, not to the file or
-embedded shell beneath it.
+A tag is a text like a pane's body: pane tags, column tags and the
+workspace tag all edit with the body's own normal and insert modes, undo
+included, and may hold more than one line. A tag with a newline in it takes
+a row per line, up to eight: a pane's tag leaves its body at least one row
+and a collapsed pane shows only the first line; the column and workspace
+tags take no more than a third of the screen, and the panes below move down
+to make room. The path, the dirty marker and a PDF's page at the start of a
+pane tag are computed, never stored: the tag's cursor, selections and undo
+live in the text after them, so a rename or a save never moves the cursor.
+The mouse still sees the whole line -- a sweep selects across the path and
+the commands, and Look and Exec work on either -- but the keyboard moves only
+in the tag's own text: `0` goes to its start, not the path's.
-Keyboard entry returns to the tag's last cursor position. The first entry into
-a pane tag starts at its commands; clicking always chooses a new position.
-Each pane, column and workspace tag remembers its own position during the
-session. Shortened text clamps the saved cursor to a valid character boundary.
+Left-click a tag or a header to type into it at the click, in insert mode;
+tags reveal the caret horizontally when text is wider than their column.
+Esc is normal mode, where everything a body's normal mode does works, and
+Enter looks up and Tab executes the word under the cursor (or an explicit
+selection), in either mode. Executing gives the keyboard back to the body
+first, so `Del`, `Kill` and `Restore` never return into a tag that is gone.
+Pasted text goes to the focused tag, not to the file or embedded shell
+beneath it.
-In normal mode, `h` and `l` move within the tag, `k` stays on the single line,
-and `j` returns to the panel like Escape. Uppercase `HJKL` move between pane
-tags. From the top pane's `:` tag, `K` reaches the column and another `K` reaches
-the workspace; `J` walks back down. Enter or Tab on a header command in
-normal mode executes it, as before. `i`/`a`/`I`/`A` switch to editing the
-header. In insert mode, letters type normally, Enter is Look and Tab is Exec,
-as in pane tags.
+`:` is the one key a tag and a body do not share: in the body's normal mode
+it focuses the pane's tag in normal mode, and in the tag's normal mode it
+goes back to the body; in a column or workspace tag it goes back to the
+active pane. Each pane, column and workspace tag remembers its own cursor
+during the session; the first `:` into a pane tag starts on its `Save`. A
+tag's text changing under it (a 9P write, a shorter tag) pulls the cursor
+back inside it.
+
+Moving between tags is the window keys' job, as it is between bodies
+(`Ctrl-w` or `SPC w` with `h/j/k/l`): they move to the neighbouring pane's
+body. Up from a pane with nothing above it reaches its column's tag, then
+the workspace's; Down comes back the same way, and Left and Right walk the
+column tags.
+
+Search (`/`), pipe (`|`) and Save's path prompt are not typed into the tag:
+each gets a line of its own on the pane's notice band, with the body's
+insert-mode keys, and leaves the tag as it was.
Pane filenames and commands now have a single separator space rather than
generated right-alignment padding. Intentionally customized spacing is kept.
## File names
-Editing a file pane's name creates a draft. Enter confirms the new buffer
-name; Escape or leaving the tag cancels the draft. Confirmation changes the
+Clicking a file pane's name starts a draft of it, typed into at the click.
+Enter or Tab confirms the new buffer name; Escape or leaving the pane
+cancels the draft. Confirmation changes the
buffer's save target and marks it unsaved. It does **not** rename, create or
overwrite a disk file. A subsequent explicit `Save` writes the buffer to its
committed name. Executing a pane-tag command confirms a valid name draft