diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/fs.md | 116 | ||||
| -rw-r--r-- | docs/tags.md | 3 |
2 files changed, 88 insertions, 31 deletions
@@ -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 |
