summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/fs.md116
-rw-r--r--docs/tags.md3
2 files changed, 88 insertions, 31 deletions
diff --git a/docs/fs.md b/docs/fs.md
index 83906d0a..ca02082b 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -15,6 +15,15 @@ separate editor and withholds `PARDES_PID` from its pane shells, so a pardes
started in one of them runs a session of its own; its 9P service stays
available.
+A forwarded launch returns at once, as acme's `B` does. `--wait` (`-w`)
+returns only once the pane the file landed in -- the one already showing
+it, if any -- is deleted (exit 0), or the session goes away (exit 1), as
+acme's `E` does: what an `$EDITOR` must do, since fish's Ctrl-O
+(`edit_command_buffer`), `git commit` and `crontab -e` read the file back
+when the editor exits. Set `EDITOR='pardes --wait'`; `GIT_EDITOR` follows
+`EDITOR` when unset. Outside pardes `--wait` changes nothing, a session
+blocking anyway.
+
Look resolves the OS filesystem first, then the editor's virtual filesystem.
Explicit paths bypass that search:
@@ -135,7 +144,9 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC.
/layout one line per column (16 at most; the board 6; Newcol past that fails,
`no space for a column: 16 max`, ENOSPC; and each at least 10 cells
wide, so Newcol from a column under 20 fails `this one is too
- narrow to split`, ENOSPC), left to right: serial index x width current|notcurrent
+ narrow to split`, ENOSPC; the root's Newcol halves the active column,
+ so reaching 16 means running Newcol from the widest column's tag),
+ left to right: serial index x width current|notcurrent
(the column with the keyboard now) empty|full pane-serials...; then active
<serial>: acme's activecol, which the keyboard leaving for another column's
tag does not move, so the two can differ -- the active column, where
@@ -220,7 +231,8 @@ before the answer was read; the log is the authority): dial again -- a 9ns
mount is one such connection, so after a Restore stop it and start 9ns
again, or every file under it fails -- and the new
log names the restored panes and
-`restore <path>`. Restored panes have new serials (`restored <old>
+`restore <path>`. Undo history does not survive a Restore: a restored pane
+starts with none. Restored panes have new serials (`restored <old>
<new>` maps them) and so may columns (`restoredcol`; a fresh editor counts
column serials from 1 again, so they often come back the same). A command
pane comes back showing what it showed, its tag saying how it ended,
@@ -255,7 +267,11 @@ takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir
<the directory in effect>`, `LocationsConfig ...`), so writing what it reads
back changes nothing; a setting the frontend cannot show (`Lift`,
`GripWidth` on a terminal) is refused as `Lift is GUI-only, invalid here`
-(EINVAL: a request this build cannot take), and so is a builtin only the
+(EINVAL: a request this build cannot take); acme's own words run as
+pardes's where it has one -- `Put` is `Save`, `Delete` a `Del` that does
+not ask -- and the rest (`Get`, `Putall`, `Snarf`, `Cut`, `Paste`, `Zerox`,
+`Sort`, `Load`, `ID`, `Send`) are refused, EINVAL, `invalid: acme's Snarf
+is not a pardes builtin`, never run as shell commands; and so is a builtin only the
GUI has (`Fonts`), written to an `exec` too, where it would otherwise run as
a shell command; a `Shell` or `Tty` naming no
shell says `shell "x" not found` (ENOENT); a `DumpDir` whose last
@@ -269,7 +285,12 @@ below, `Save f`, `Collapse`, which folds that pane, `Undo` and
`Redo`, which step its body through its last 256 edits -- with none left
they say `Undo: nothing to undo` and the write still succeeds, as acme's
Undo is silent --, `Find pat`)
-beside acme's `get`, `lock` and `unlock`. The column words are pane words
+beside acme's `get`, `lock` and `unlock`; acme's other ctl words are taken
+too, done by the file or builtin that replaces each: `name x` (the `name`
+file), `put` (`Save`), `clean` and `dirty` (`dirty`), `del` (`Del`),
+`delete` (a `Del` that does not ask), `dot=addr`, `addr=dot`,
+`limit=addr`, `mark`, `nomark` (`mark`), `show` and `cleartag`; `dump`,
+`dumpdir`, `font`, `menu` and `nomenu` are refused with why, EINVAL. The column words are pane words
too, acting on the column that pane is in: `Delcol`, `DelAbove`, `DelBelow`
and the focus moves `Left`/`Right`/`Up`/`Down` from it. `Joincol` and
`Newcol` are the root's: they act on the column of the pane with the
@@ -518,12 +539,13 @@ A write of command lines -- to `look`, `exec`, `tagexec`, a `ctl` of the
root, a pane or a column, or a column's `exec` -- runs each line once it is
whole: a mount cuts a big write at its message size (4 KiB through the
kernel's, 8 KiB from a client that asks), anywhere, and each piece comes as
-a write of its own, so the open keeps a last line with no newline yet, or
-an Edit block whose text has not ended, until its next write, and runs it
-then; what is left when it closes runs at the close -- so a line written
-with no newline (`printf Save > exec`) runs only as the file closes, after
-the write has been answered, and its failure is in the log alone: end each
-line with its newline to have the write itself fail -- where an Edit block
+a write of its own. A write that fills its piece may go on in the next, so
+the open keeps its last line with no newline until then; a write shorter
+than a piece is the whole of what was written, as acme takes each write, so
+its last line runs with it even with no newline (`printf Save > exec`), and
+a failure is that write's. Only what needs more is held: an Edit block
+whose `{` or `a`/`c`/`i` text has not ended waits for its next write, and
+what is left when the file closes runs at the close -- where an Edit block
whose `{` or `a`/`c`/`i` text never ended fails and changes nothing
(``unmatched `{'``, or `a, c or i text not ended by a . line`, logged as an
`err`): sam takes the end of input for a `.`, but a block that reaches Edit
@@ -561,8 +583,9 @@ directory. A name alone is no edit: the pane's `dirty` stays what its text made
(a renamed clean file is still 0, and nothing asks about it at Exit,
Restore or Del, which ask only about text edited), and `Save` writes it
under the new name all the same. An open's writes are one name: held
-until its newline, or its close when it has none, then applied once, however
-the writes cut it; nothing else is trimmed. Two lines are refused, EINVAL,
+until its newline, applied once however the writes cut it; a write with
+no newline that is whole in its Twrite is the name then, so a bad one fails
+that write, not the close. Nothing else is trimmed. Two lines are refused, EINVAL,
in one write or as a second line on the same open (bash's `printf
'a\nb\n' > name` writes a line at a time: the first names it). A blank inside a name is taken
(`two words.zig`); refused, EINVAL, in acme's words (xfid.c:650) and why,
@@ -574,7 +597,9 @@ terminal's `body` is its history as plain text, frozen per open, in logical
lines: a row the terminal wrapped is joined back to the row before it (the
wrap is ghostty's, as `pty/run`'s output unwraps), and the last line ends
with a newline. `sel`
-reads the selected text and writing it replaces the selection. `errors`
+reads the selected text and writing it replaces the selection, leaving dot
+just past the text written (a `data` write moves dot as its text moves it,
+so a dot at the address it wrote at ends just past that text too). `errors`
appends to the directory's `+Errors` pane; with no room for that pane in
any column, what it would have shown is logged as `msg` records instead, a
line each, and the write still succeeds. Holding `event` open redirects the
@@ -593,10 +618,12 @@ it was read too, and for an empty range acts on its text, which is how such
a line is done. A click in a file's body carries the offsets of the text it
took; one in a terminal's body cannot, since that body is a history
snapshot, and is also at `0 0` with its text. A click that takes no text
-sends nothing, as in acme. `ctl` reads acme's window status line — serial, tag length, body
-length, a reserved zero, the dirty flag, the width in cells, the font and the
-tab width — followed by rio's `current` or `notcurrent` (rio(4), `wctl`):
-whether the pane has the keyboard. It takes the pane's builtins (below),
+sends nothing, as in acme. `ctl` reads acme's window status line, field for
+field as plan9port's — serial, tag length, body length, isdir (0), the dirty
+flag, the width in cells, the font, the tab width, whether Undo has a step
+(1) and whether Redo has one — followed by pardes's own: rio's `current` or
+`notcurrent` (rio(4), `wctl`), whether the pane has the keyboard, and a
+REPL's id. It takes the pane's builtins (below),
`get`, which reloads the buffer from the name it
carries (unsaved edits are refused once, listed in `+Unsaved` with a short
notice as Exit's are, the write failing `<name>: Modified (get again to
@@ -704,7 +731,8 @@ lines, so `^` and `$` match at the start and end of any line, `.` and a
negated class never match a newline, and `$` also matches at the end of a
text with no final newline. A pattern that names a newline (`\n`) runs over
the whole text instead, its `.` kept to one line; there a leading `^` still
-matches at every line start, and `$` may stand just before a `\n` (where it
+matches at every line start (`$` on a CRLF line sits before the `\r`,
+as the line's end is its `\r\n`), and `$` may stand just before a `\n` (where it
changes nothing). Any other `^` or `$` in such a pattern, `(^|\n)def` or
`a\nb$`, is refused, EINVAL, with `bad regular expression: in a pattern with \n, ^ can only come first and $
only just before a \n`, since mvzr would read it as the start or end of the
@@ -712,7 +740,15 @@ whole text: never a search that silently finds nothing. An alternation
whose every branch starts with `^` (`^def|^ `) finds a line that starts
either way (it is taken as `^(def| )`); one that mixes anchored and
unanchored branches (`^def|x`) is refused, `bad regular expression`, since
-mvzr keeps `^` only first. An expression is
+mvzr keeps `^` only first. A pattern may be up to 512 of mvzr's operations,
+about 512 characters (mvzr's own is 64; pardes builds it with more); a
+longer one is refused, `bad regular expression: longer than mvzr's 512
+operations`. A class may hold non-ASCII runes (`[éa-z]`, `[à-ÿ]`): it is
+taken as an alternation of them (`(é|[a-z])`), a range of up to 256 runes
+spelled out, and the 512 counts the pattern as rewritten. A wider range
+(`a range of runes wider than 256 in [...] is not supported`) and a negated
+class with a non-ASCII rune (`[^é]`) are refused, since mvzr's classes hold
+bytes. An expression is
evaluated from the current address, the range last written to `addr` (or
left by the last `data` write, just past it), as acme evaluates it from
`w->addr` (xfid.c:446): `.` is that address, not the selection (`dot` is
@@ -729,9 +765,10 @@ pattern that spans lines, `^`, `$` and `[^...]` keep mvzr's own meaning.
mvzr backtracks without bound of its own (`a?` twenty times then twenty
`a`s is 2^20 steps from each place it tries), and a search holds the editor, so pardes patches a
step budget into mvzr's matcher (build.zig): a search that spends it,
-about 300 ms, fails with `regular expression search gave up, backtracking
-past its step budget` rather
-than answer a match it is not sure of. Ordinary patterns spend a few
+about 300 ms, fails with `regular expression search took too much time,
+gave up` rather
+than answer a match it is not sure of. The budget is each search's: an
+Edit `x` over 100k lines makes 100k searches, each with its own. Ordinary patterns spend a few
thousand steps; what runs out is exponential backtracking, and a quadratic
pattern over a very long line (`\s*(\w+)\s*=` over 20 KB of letters).
pardes has no regex engine of its own on purpose; these are its limits.
@@ -763,7 +800,11 @@ shell writes it a line at a time), and the next open starts another. The
pane has one history, so two opens writing at once take turns in it: each
open's first write after the other's starts a step of its own; to
make a loop's writes one step, write `1` (an undo point here), then `0`,
-the writes, then `1` again.
+the writes, then `1` again. An open's writes in a row to one place -- an
+append to `body`, inserts going on at `data`'s address -- are held and go
+in as one edit when anything else comes (another request, the close, or
+the editor's step once they pause 20 ms), so a 10 MB write is one copy, not
+one per 8 KB piece; any read sees them.
One rule for what fails: a write fails whenever what it asked for fails,
whether it came to a `ctl` (the root's, a pane's, a column's, a pane's
@@ -776,7 +817,7 @@ name (ENOENT for a pane, file or directory gone -- `look .` from a pane
whose directory is gone says `look: <dir>: no such directory`, and a
`./zz.txt` or `../x` that is not there `look: ./zz.txt: no such file`, while a
plain `zz.txt` is looked for as text, a miss logged as any look's -- and for a
-Find or Grep that finds nothing, `grep: pattern not found`; Grep walks
+Find or Grep that finds nothing, `Grep: pattern not found`; Grep walks
every pane's directory on this host, passing over panes of the served
tree (`/virtual/`, a peer's `/n/<name>/`) and directories not there,
so none of them spoils the rest; Find, Grep and a language server's lists
@@ -787,9 +828,17 @@ nothing empties it rather than leave the last rows), while a plain
word's `LookWord list` search keeps a pane a pattern, ENOSPC for no room or
slot, EBUSY for a held lock) -- and logs its reason exactly once, as `err <serial|->
<file>: <why>`, with no `msg` for it. A builtin a click runs (Save, get's
-`Modified`, Tty with no room, Edit) is no exception. What is not a
+`Modified`, Tty with no room, Edit) is no exception. The rule is a write's:
+a refused open or truncation -- an OTRUNC open, such as `data`'s after a
+failed `addr` --, create or remove answers its error and
+logs no `err`, and so do a write to `pane/new` (`permission denied`: it is
+only read) and a write on a fid opened OREAD (`bad use of fid`). Every
+refusal is said in words, Plan 9's where pardes has none of its own
+(`permission denied`, `file does not exist`, `bad argument`), never a C
+library string such as `Operation not permitted`. What is not a
failure: a look that finds nothing answers nothing and logs one `err`
-(`look: no match for ...`, the same miss again counted, `(x2)`, as any
+(`look: no match for ...`, quoting what was written in every form --
+`no match for "zzq:#3"`, `no match for "zzq:2"` -- the same miss again counted, `(x2)`, as any
repeated `err` is), the write succeeding; and a command line run in
a command pane ends in its own time, told by its `exit` record.
@@ -797,7 +846,9 @@ a command pane ends in its own time, told by its `exit` record.
page (no mark for unsaved text: the grip shows that, and `dirty` says it),
then the text you may edit. An image's tag begins with its mode words, not
its path: `img petscii:off palette:commodore ascii:on <path>` by default, the modes
-it is drawn in, then the file. A write appends to that text,
+it is drawn in, then the file. The editable text is 4096 bytes at most: a
+write that would pass that is refused whole, ENOSPC, `tag: over 4096
+bytes`. 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 -- the default
words (`Save Tty Collapse Del` and the rest) with it, since they are that text until
@@ -873,7 +924,8 @@ itself, is counted rather than repeated (`err 3 addr: no match for regexp
write does not push the rest out of the ring. A record a follower has
already read is never rewritten: the next repeat is a line of its own
carrying the running total, `(x5)`, and counting goes on from there, so a
-follower sees each count as a new line. Through a kernel mount a client sees only an errno, which 9ns reads from the
+follower sees each count as a new line: with a follower attached, a repeat
+is a new line carrying its `(xN)` count, never an edit of the one read. Through a kernel mount a client sees only an errno, which 9ns reads from the
error's words (cloud9's 9ns/src/nine.zig, `enameToErrno`): a malformed write
-- an unknown or ill-formed control message, `bad address syntax`, `bad
regular expression` -- is EINVAL; a lock another open holds, EBUSY; a pane
@@ -958,7 +1010,9 @@ acme's window keeps one `eventx`; a second read on it meanwhile fails with
`pty/run` runs one line at a terminal's prompt and answers how it ended, on
the same open (factotum's `rpc` shape): write the line, then read `exit N`
once the command has ended and the shell is back at a prompt, followed by
-what it printed. The output is what the screen showed between the command's
+what it printed. One line a run: two lines in one write, or a next line on
+the open before the last answer is read (bash writes `printf 'a\nb\n'` a
+line at a time), are refused, EINVAL. The output is what the screen showed between the command's
start and end marks: stderr interleaved, `\r` progress collapsed to its last
state, no colour, tabs as the spaces they drew, trailing spaces trimmed and
trailing blank lines dropped (`printf 'a\n\n\n'` answers `a`), leading
@@ -984,7 +1038,9 @@ which is also when the third field of `pty/status` reads 1. `pty/status` is
one line, three right-aligned fields and a newline: the pty's columns and
rows, then busy (0 or 1). `pty/ctl` takes `winsize C R`, `sig
INT|TERM|HUP|QUIT|KILL` and `exec`, which starts the pane's shell again in
-its directory: one that is gone is refused before anything runs, `exec:
+its directory (a command pane's child is its command, which does not
+restart: `exec` there is refused, EINVAL, `a command pane does not
+restart`): one that is gone is refused before anything runs, `exec:
<dir>: no such directory` (ENOENT), and a shell the host cannot start (not
there, not executable, a script whose interpreter is not there) fails the
write with why -- `shell: shell not found`, or `shell: interpreter
diff --git a/docs/tags.md b/docs/tags.md
index 3ff5e0ef..7671e056 100644
--- a/docs/tags.md
+++ b/docs/tags.md
@@ -92,7 +92,8 @@ acme's Del warns (exec.c del, wind.c winclean): a short notice, `1 unsaved
pane — Del again to discard`, the pane listed in a kept `+Unsaved` (`<name>:
Modified`, then `Del again to discard`), and the same `Del` again, nothing edited since, closes it and
throws the text away; `Delcol` refuses a column holding such a pane the same
-way (exec.c delcol). Each word warns on its own, from a key, a tag, `exec`
+way (exec.c delcol), but changes nothing else in refusing -- no `+Unsaved`
+opens, no focus moves; its `unsaved` records and notice say which. Each word warns on its own, from a key, a tag, `exec`
or a ctl, where the refusal fails the write with EIO. `Del k` (or `DelAbove`) gives them to the nearest
expanded pane above, `Del j` (or `DelBelow`) to the one below, each falling
back to the other side. Bare `Del` from the keyboard (`SPC d`, or Enter on