summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAge
...
* | An edit that changes nothing, an empty range deleted or a range replaced by ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | what it holds, makes no undo step A shell's : > data on an empty address, echo abc > data over abc, or Edit s/d/d/ each pushed an undo step and a new revision, so Undo spent a press on nothing and the pane read as edited. The splice of the 9P writes and the Edit builtin now see the text is unchanged and stop. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A refused write to dot after a shell's truncation changes nothing: the ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | truncation waits for the write, as limit's does echo /nomatch/ > dot truncated dot to an empty range at the top before the write was refused, so a failed address still moved the selection and scrolled. The truncation is now held until the write that follows it: that write sets dot, or, refused, leaves it as it was. A truncation with no write after it (: > dot) empties it when its open goes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Jumplist fills its one +Jumps again, as Recent does, never opening a secondGabriel Schneider29 hours
| | | | | | | | | | | | | | | | Each Jumplist opened another +Jumps below the asking pane, so a few clicks stacked stale copies of the list. Recent's refill of its own pane is now shared, and Jumplist uses it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | After a ThemeFile, the root ctl and DumpConfig say ThemeFile and its path, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | so what they report writes back Both said Theme and the name inside the .zon, which no Theme knows, so writing the report back to /ctl failed with "no theme". While a loaded ThemeFile is the theme in effect they now say the word that loaded it. A Dump carries the line too, so a Restore reloads the file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A builtin for one kind of pane is refused on another with why: Petscii, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | Palette, Ascii, Filter, Togglettymode, the Pdf words, Mode and TreeContext Each of these did nothing, silently, on the wrong kind of pane, so a ctl or exec write of Palette to a text file answered ok as if it had acted. A builtin now declares the one kind it is for (needs), the registry reads it, and runBuiltinFrom refuses the rest before dispatch, as Repl and Edit already refused theirs. Mode and TreeContext, which take more than one kind, say their own refusal. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Save and Save path on a PDF or image pane are refused, "a PDF pane has no ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | text to save", never a silent success A bare Save on a PDF or an image did nothing, and `Save path` returned without writing, both answered as done. Such a pane shows a file it does not hold as text, so there is nothing of its own to write. Save now refuses it with why ("Save: a PDF pane has no text to save", or an image pane's), and so fails the write that asked. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Joincol is refused, as Newcol is, when the joined column cannot give each ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pane its tag and 2 rows Joincol moved every pane of the keyboard's column into the next one whether they fit or not. Two columns of two panes in a 12-row window became one column of four, wanting 12 rows where 10 were free, a layout that `size` then refused as it stood. Joincol now tries the join in place first (layout.joinFits: the panes and width moved, minimaFit asked, everything put back, nothing dropped or logged). A join that does not fit is refused, "Joincol: no space: the joined column cannot give each pane its tag and 2 rows". Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A selection deep in a long line costs a render what one at its start does: 5 ↵Gabriel Schneider29 hours
|/ | | | | | | | | | | | | | | | | | | | | | | | | MB in, /screen goes from 14.7 s to 5 ms Every render measured a selection's and the cursor's columns from the start of their line (lineDisplayOffset walked the whole prefix twice), so a selection near the end of a 5 MB line stalled each frame, 3 s at 1 MB in and 14.7 s at 5 MB. The cost grew with the offset, with Wrap on or off. Three changes make it independent of where the selection is: - display width adds (a tab is tab_width wherever it stands), so the offset between two columns is the width of the text between them, never the prefix; - the selection's rows are clamped to what is shown: a row wholly before the selection is skipped, a start before the row's first column starts there, and an end past its last column ends there; - a head or cursor whose column lies past its visible row (the last visible row of a line owns every column after its start) is not measured, since it is not drawn. Measured on a Debug build, 5 MB line: offset 10, 1 MB and 5 MB now take 11, 6 and 5 ms per /screen. A new perf gate case, deep-sel, renders with the selection 2 MB into a line: 83 us on Debug. The baselines were re-recorded for the new harness. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Release Pardes 0.11Gabriel Schneider29 hours
| | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A diff look names a file as the repository does: git's side prefix dropped ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | by git's rules, a plain diff's names as written In a git section (one with a `diff --git` line) the one-letter side prefix git writes, `a/`/`b/` or with diff.mnemonicPrefix `c/`, `i/`, `w/`, `o/`, is dropped from its paths only when both paths of that line carry one, different ones: `--no-prefix` writes none, so its `a/x a/x` is a real directory `a` and is kept. A plain `diff -u` never strips: its names are used as written, past the timestamp. `/dev/null` stays itself. The walker does this once (diff.Walk.side, diff.gitHeader), so the path a look resolves, and the one a miss says, are the repository's name: `Look: src/x.zig: no such file here`, `Look: lib/old.zig: deleted by this diff`. The look no longer tries a stripped name beside the written one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Release Pardes 0.10Gabriel Schneider29 hours
| | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fs.md says a body write to a terminal is typed input, never a bracketed pasteGabriel Schneider29 hours
| | | | | | | | | | | A write to a terminal's body goes to its child as typed keys: no bracketed-paste marks around it, even when the program set 2004, so a newline in it is Enter and a shell runs each line. fs.md did not say which, and a script cannot guess. It does now, and a test writes to a terminal whose program asked for bracketed paste and checks the bytes sent are only the ones written. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Save path logs a save record naming the path it wroteGabriel Schneider29 hours
| | | | | | | | | | | Save of a pane's own file logged `save <serial> <name>`, and so did a scratch's `Save path` (the pane takes that name). A `Save path` that writes a copy of a file pane, or a terminal's scrollback, elsewhere logged nothing, so a follower could not tell the file was written. It now logs `save <serial> <path>` with the path written, once the host has written it (events.noteLogAs). fs.md's log table says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A rename (mv) of a pane's directory or any file of the tree is refused as ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | not permitted, EPERM, not an EIO cloud9's engine passes a wstat that changes a name to the backend, and the tree's setattr ignored the name: the rename answered as done, the name did not change, and a mount then gave `mv` EIO. The tree names its own files, so a setattr with a name is now refused, "rename not permitted", which 9ns reads as EPERM. A pane's buffer is still renamed through its name file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* name refuses a directory, EISDIR, when written, not at the Save that could ↵Gabriel Schneider29 hours
| | | | | | | | | | | | only fail `name ~`, `name /` or `name foo/` renamed the pane to a directory, and only its Save failed later. A name ending in `/`, the root, or one naming a directory that is there is now refused at once, with words a mount reads as EISDIR (`name: <path> is a directory, not a file`). The ctl word name and a forwarded `pardes ~` go through the same check. fs.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A terminal's word looks refill the directory's one +Search, as +Lsp is ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | shared, rather than stacking one per word Each word looked for in a terminal (clicked, or written to look), where no file of that name was, opened another +Search in the same directory, one per word, until the column filled. Such a search now refills the directory's +Search, as the file searches and the +Lsp answers already share theirs. A `/` search keeps a +Search per pattern, which n and N walk across (psearch.snap), so the sharing is marked on the word look alone (Pardes.word_look). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pane refused for want of room spends no pane serial, as a refused Newcol ↵Gabriel Schneider29 hours
| | | | | | | | | | | spends no column serial A pane/new, look or exec refused ENOSPC made its pane before placing it. The refusal closed the pane unannounced, but its serial was gone, so the next pane skipped one. When sync removes a refused pane that holds the newest serial, it now puts the counter back. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fs.md says that in a > to a tag cut into several writes, those taken before ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | a refused one stand A `>` to a tag that a client sends in several writes is checked a write at a time. When a later one is refused, the earlier accepted ones stay in the tag. Buffering the whole open to apply it or none would cost more than it saves, so fs.md now says what happens: each write stands on its own, and only the refused one changes nothing. A test covers a truncation, an accepted write, then a refused one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An addr, dot or limit pair out of order or past the text is refused as the ↵Gabriel Schneider29 hours
| | | | | | | | | | | | address form is, not clamped `#5,#2` and `#2,#99` were refused (addresses out of order, address out of range), but the pair form `5 2` became 5..5 and `2 99` became 2..end, silently a different range from the one asked for. A pair is now checked the same way: past the text is out of range, and an end before its start is out of order. fs.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Restore logs and names the whole path of the dump it reads, not the name ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | as typed `Restore x.dump.zon` read the DumpDir's x.dump.zon but logged `restore x.dump.zon`, and `~/d.zon` logged the tilde. A follower could not tell which file was restored. The builtin now resolves the name once (fs.restorePath: `~` expanded, a relative name the DumpDir's when one is there, else the current directory's) and hands that path to the host, which reads it and logs `restore <path>`. Its messages name it too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A tag rewrite counts its text without the newline it drops: echo of exactly ↵Gabriel Schneider29 hours
| | | | | | | | | | | | 4096 bytes fits `echo <4096 bytes> > tag` wrote 4097 bytes and was refused "over 4096 bytes", although a rewrite drops that one trailing newline and would have held exactly 4096. The limit now counts a truncating write's text without the newline it drops, for a pane's, a column's and the workspace's tag. An append still counts its newline, which it keeps. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Every line of a diff section that deletes its file looks for a file "deleted ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | by this diff", the diff --git and --- lines too In a section that deletes its file, a look at a removed or hunk line said "<file>: deleted by this diff". The `diff --git` and `---` lines said "no such file here", because they come before the `+++ /dev/null` (or `deleted file mode`) that says so, and the walk had not reached it yet. They now look ahead within the section, so every line gives the one message. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An empty range written back to event acts on the word a click there expands ↵Gabriel Schneider29 hours
| | | | | | | | | | | | to, as acme's expand does `ML960 960` or `MX1 1` written back with no text acted on the empty range between the offsets: a look of nothing, an exec of nothing. A click at a point expands to the word or file name under it, and acme's written-back event does the same. It now expands with look.expandedWord, as a no-drag click does, on its line of the body or tag. fs.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A focus moved over 9P tells the programs that asked for focus reports, as a ↵Gabriel Schneider29 hours
| | | | | | | | | | | | click does A program that set DECSET 1004 heard `CSI O` and `CSI I` when the keyboard moved by a click, a key or the window's focus, because update() checks after each event. A /focus write went through serveFs, which never did, so the program kept believing it had focus until the next input event. serveFs now runs the same check once a request has settled. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* diff -u in a command pane names its files though the terminal turned the tab ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | before each timestamp into blanks A `---`/`+++` path was cut at the tab before diff -u's timestamp. Run in a command pane, the terminal had turned that tab into blanks, so the name took the timestamp with it and a look at a changed line opened nothing. The name is now also cut where blanks run up to a timestamp, either GNU diff's `2026-09-30 10:00:00...` or ctime's `Wed Sep 30 10:00:00 2026`. One blank is enough, since a tab can expand to a single blank, and the stamp is strict, so a name with blanks and no stamp after them stays whole. Tested on the parsed text and, in fs.py, on real `diff -u` output in a command pane, where a look at `+TWO` opens the new file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A 9P look keeps its leading blanks: an indented line, or blanks alone, is ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | looked for as written Every line written to look or exec went through Messages.next, which trimmed blanks from both ends. A look of ` indented` then found the first bare `indented`, and a diff's blank context line ` ` looked at nothing. A look's line is now its text: only its `\r` and newline go. lookAt keeps the blanks for the word search too; it expands `~` only for a look that starts with one, and the path, address and command readings trim for themselves as before. runSearch keeps a pattern's blanks, since a builtin's argument comes trimmed already. An exec line is trimmed as before. fs.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A restored pane whose file is gone from disk comes back dirty, and Del asks ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | before throwing its text away A dump keeps a pane's text. Restored after its file was deleted, the pane came back clean: the watch's baseline read of the missing file failed quietly, and Del closed it without a word. Its text was then nowhere at all. Restore now checks the file: a local path with nothing on disk comes back as one deleted on disk, as the watch would have said. It says so once, logs `changed N deleted`, and is dirty, so Del, Exit and Restore ask first. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A 9P write scrolls only the view: dot moves as acme's textinsert moves it, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | never to the end of the write writeData, flushBatch and writeBody showed what they wrote with showOffset, which put the cursor at the write's end. The cursor is dot's head, so a data write or a body append moved dot there, and a `sel` write after it replaced the wrong text. showOffset now scrolls the view to the offset and leaves the cursor, pinned, where it was. Dot shifts only by the rule it already followed, acme's: an insert before dot shifts it, one at its start or inside it grows it, one at its end or after it leaves it. The test covers each case, then a body append followed by a sel write. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Release Pardes 0.9Gabriel Schneider29 hours
|\ \ | | | | | | | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
| | * The window's F1-F24, Insert, Print Screen, Scroll Lock, Pause and Menu keys ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | reach a terminal's program, which they never did; every key a program gets follows its cursor keys' mode and kitty keyboard flags, pinned for both shells The keys went through ghostty's encoder already (DECCKM, the kitty flags); the GUI's native key map simply dropped the function and editing keys it did not name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| | * A paste into a terminal is xterm's paste: bracketed when the program set ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 2004, and the control bytes a paste could smuggle a command in by, ESC first, become spaces, so no ESC[201~ in the text ends the brackets early Every paste path goes through edit.typeToTty (the GUI's and the tty's paste, p from a register, the acme chord's paste, a REPL's send), which now takes ghostty's paste encoder. A 9P write to a terminal's body or pty/data stays typed input, not a paste. The GUI test shell's stdin now takes a bracketed paste as one paste, as the tty does, not as keys. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| | * A program that asked for focus reports (1004) hears CSI I when its pane ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | takes the keyboard and CSI O when it gives it up, to another pane, a tag or another window The core has a window-focus event now, from SDL's window focus and from the tty's own focus reports (vaxis turns 1004 on outside); the detached wire carries it: version 10. The test shell's ESC]777;mouse;focus;<1|0> fakes the window's. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| | * A program that tracks the mouse gets its clicks, drags and moves over its ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | grid, in the format it chose; Shift keeps pardes's select, execute and look, and chrome is never the program's Presses and releases under 1000, drags with a button down under 1002, every move once per cell under 1003, X10 presses under 9, in raw terminal input only. A press the program took keeps its drag and release, reported clamped to the grid's edge wherever the pointer goes. The press also makes the pane the active one. docs/tags.md says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| | * A mouse event carries Shift and Alt as well as Ctrl, from SDL and from the ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | tty's input; a program's wheel report carries them as xterm counts them, and Shift keeps the wheel pardes's own scroll The reports are ghostty's encoder now (shift+4, meta+8, ctrl+16), with the mode and format ghostty tracks. The detached wire carries the two new bits: version 9. The test shell's ESC]777;mouse takes an optional fifth field of modifiers. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A right click on a diff line opens the place it names: its file, the hunk's ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | new line, the line itself In a diff pane (a .diff/.patch file, or a command pane's diff output), the look's own expansion, which its hover shows, takes a whole line for a `diff --git`, `---`, `+++` or `@@` line wherever the pointer is on it, markers included, and for a hunk line when the pointer is on its `+`, `-` or space. Then the look opens the file (a/ b/ dropped, timestamps cut) at the line from the hunk header and the lines above it: `@@` the hunk's first new line, context and added lines their own, a removed line the new line now where it was. `diff --git` and `+++` open the new file; `---` the old one, unless the `+++` under it names another. On a hunk line's code the expansion is its words, the prefix never one of them, and looks as ever. Other panes are untouched. Paths resolve in the repository (walking up to `.git` or `.jj` from the diff's directory, or a command pane's), then that directory. A file not here opens nothing and says `Look: b/x: no such file here`, or `deleted by this diff`. A 9P look writing a whole line of the diff does the same. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A diff reads as the code it changes: hunks in their file's language, added ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | and removed rows tinted A .diff or .patch pane, and a command pane whose output is a diff (`git diff`, `git show`, `diff -u`; told by a `diff --git` line, or `---`/`+++` with a `@@` under them, in its first rows), colour each hunk's code in the language of the file its section names (`+++ b/<path>`, `--- a/<path>` for a deleted file), with tree-sitter, as highlightLocations does for results. src/diff.zig walks a unified diff by its `@@` counts, so a removed line that reads `--- x` is no header. Each side of a hunk is parsed as one text (context and removed lines the old side, context and added lines the new), so a string or comment across lines colours as it does in the file; a side no line takes its colours from is not parsed. A section's hunks share a parse, in pieces of about 40 lines (a hunk past 80 is cut), only those in view, and each piece's colours are kept by its bytes, so scrolling back and a terminal's every repaint parse nothing again. Added and removed lines carry a flag in their style byte; the painter tints their rows to the pane's edge, a little way from the page toward the theme's ANSI green or red, and draws the prefix in that hue pushed to read on it. A file in no known language keeps the old line colours. A command pane is read once its command has finished: its rows are copied once (File.DiffOutput, dropped when the pane runs again), so no frame dumps the scrollback, and painted over what git printed. A running command is shown as it prints. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A write of a multiple of 4096 bytes holds the partial line it ends with, as ↵Gabriel Schneider29 hours
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | one that fills its Twrite does: a burst through a mount no longer runs its cut lines as two commands selfmount's 1000-line burst reported 943 lines, on main too. The log ring was not losing records: a follower opened before the burst heard exactly 943 and no `lost`, and the burst itself exited 1 with `err 1 exec: wrong #args in control message "Msg"`. `seq ... > exec` through the mount arrives as stdio's 4096-byte writes, each shorter than a Twrite. The rule that such a write is whole ran each one's cut last line at once, so both halves of the line ran as commands (hence `exit 127` records). At 12288 bytes the cut left a bare `Msg`, which was refused and failed the write and the rest of the burst. A write of a multiple of 4096 bytes is where a writer's buffer (stdio, a page cache) filled, so it may go on: its unended tail now waits for the next write or the close, like one that fills its Twrite. `printf Save > exec` stays whole at once. fs.md says so, the 9P fuzzer's model of served lines follows it, and a unit test cuts a `Msg` at 4096 bytes. A follower does not lose records silently: a ring that outruns one already reads it `lost N` first (documented, tested). selfmount now follows the log from before the burst, so it checks every line heard once, in order, or a loss said, not a fresh open of a 64 KiB ring. It passed 3 in 3 and joins the gates with the new 9ns. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Release Pardes 0.8Gabriel Schneider29 hours
|\ \ | | | | | | | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A background job killed while the tty probe walks it is not the tty's owner, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | however far into exiting it is host_io's "a background job is not the tty's owner" failed now and then, 2 runs in 20 under load, always just after `kill %1`. The probe walks the shell's children and reads each one's exe. A killed background sleep, in the moment it is exiting (its exe link gone, not yet a zombie), has no exe to read and is not a zombie to skip, so the probe called the tty taken. A process outside the foreground process group holds nothing whatever state it is in, so one whose exe cannot be read is now skipped when its group is not the foreground one. Under the same load the test passed 50 runs in a row. The unit-test step's perf gate trips under that load, which is not this test. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | The 9P socket appears already listening: it is listened on under a name of ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | its own and renamed into place A client that waits for the socket file and then connects, as fs.py's session helper and scripts do, was sometimes refused under load. The listener bound the socket at its final name, so the file existed a moment before listen(2), and a connect in that window got ECONNREFUSED. Now it listens under `<name>.<pid>`, is chmodded, then renamed over the final name, so that name only ever names a listening socket. A final name held by a live listener is still refused, and a name with no room for the suffix is bound in place as before. The registry test checks the socket is listening where it appears and that the temporary name is gone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | pardes --wait for a new name waits on the pane it made, by serial, and a ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pane closed before it looks is done, not a failure fs.py's `pardes --wait 'wait new.txt'` failed two runs in three on the 0.0.8 merge. The stress case repeats the test's steps: 4 of 50 failed under load, each with "pardes: --wait: no pane shows that file". A new name's launch makes its pane, names it, then found it again by path in /index. The script had already seen the name, saved and removed the pane by then, so the lookup missed and --wait exited 1. Now the launch hands --wait the serial pane/new gave it. After a look, finding no pane on the file means that pane is already closed, which is the end --wait waits for: exit 0. The follow with its /index recheck covers a close in between, as before. Under the same load the stress case went 0 of 100. The same load showed a test race too, at fs.py's second Restore. The exec write is answered before the hang-up, but the client's clunk after it can meet the connection already cut. The test now requires the answer and tolerates the reset. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A path typed with a leading ~ is the home directory, at every entry point ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | that takes one, through one helper Only DumpDir (dump.zig) and ShaderBuild expanded `~/`, each its own way. So a look at `~/notes.txt`, `name ~/x`, `Save ~/x`, ThemeFile, Restore and a quoted `pardes '~/x'` all took `~` as a directory of that name under the pane's. There is now one rule, filesystem.expandHome. A leading `~`, alone or before `/` or `:`, is $HOME, else the passwd entry's home; `~user` is that user's (getpwnam). As in a shell, it applies even beside a file named `~`, and `./~` names that file. Every entry point runs its typed path through it: - look, B3 and 9P, before any parsing, so `~/x:12`, `~/x:12:3` and `~/x:/re/` all work; - name, and a tag's rename; - Save <path>; - ThemeFile, DumpDir, Restore and ShaderBuild's shader files; - the forwarding launch. Recent rows are not shown with `~` and need nothing. How names are shown is unchanged. Tests: expandHome's cases, and HOME unset (passwd), with HOME passed in rather than set, since setenv moves the environ a spawned child reads. fs.py covers Save into missing directories under `~`, `name ~/x`, a look at `~/x:3`, and a forwarded `pardes '~/x'`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Merge: round 26 + codex wheel (the 0.8 candidate)Gabriel Schneider29 hours
|\ \ \ | |/ / |/| / | |/ Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * A wheel over a terminal whose program owns it goes to the program: its mouse ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | report when it tracks the mouse, arrow keys on the alternate screen with alternate scroll, so codex, htop and less scroll codex 0.158 moved its chat onto the alternate screen with any-event SGR mouse tracking (1049, 1000/1002/1003/1006, 1007 off); its sign-in screen keeps 1007 on with tracking off. pardes never forwarded a wheel to a program, only scrolled ghostty's scrollback, which the alternate screen does not have, so the wheel did nothing there. A shell on the main screen still scrolls its scrollback as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A detached session no client watches draws no frames: yes | head -c 50M ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | through pty/run goes from over 30 s to 11 s Profiling the 50 MB run showed 70% of its time in draw.render, recolorAnsi alone 43%, walking cells through PageList.pin. The detached session drew a frame after every pty read, with no client attached to see it. A host now says whether anyone watches (Host.VTable.watched). The detached server answers whether a client is attached, and while none is the core keeps the frame owed instead of drawing it. A /screen read draws its own, and a client attaching gets one (hello sets needs_frame). Debug, 80x24: 10 MB went from 7.36 s to 2.10 s, and 50 MB from over the 30 s client timeout (about 37 s by rate) to 11.08 s. What is left is the terminal's own parsing and scrollback growth. A session a frontend shows still draws after every read. Pacing those frames is the next step if that case matters. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | pane/new takes rows from a pane in another column before refusing ENOSPC, as ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | acme's placement does A pane/new whose column had no room was refused "no space for a pane in that column", even with another column holding a tall pane that could give rows. acme takes rows wherever a window has them before saying there are none. pane/new now tries the other columns as +Errors already did: an empty one, else its tallest pane's bottom half, last column first. It is refused only when no pane anywhere can give the rows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Kill with a name and nothing running says "Kill: nothing running" and ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | succeeds, as bare Kill does Bare `Kill` with nothing running is a message, not a failure: a script's Kill has nothing left to do. `Kill make` in the same state failed the write with "no running command has that first word", as though make were the mistake. With nothing running at all, named or not, Kill is now the message. It still fails when commands run but none has the word. The two tests that used `Kill zzz` as a builtin that always fails now use `Unmount zzz`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | An event record written back without its trailing newline is taken, as every ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | other line file's last line is `printf 'MX0 10' > event` was refused as a malformed record, while exec, look and every ctl take a last line with no newline. The event file now does too: a write not ending in a newline is read as though it did, and the write still counts the bytes that came. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A tag write over the limit is refused "no space: over 4096 bytes", so its ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | err record says tag: once The refusal's text began with "tag: ", and the log's err record puts the file's name before the reason. So the record read `err - col/1/tag: tag: no space: over 4096 bytes`. The text is now the reason alone, as the control-character refusal's is, and the record names the file once. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Save makes the directories its file goes in, always: for the pane's own name ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | and Save path alike Whether a Save made missing directories depended on how the name had come about. Config's pane did, a name written into a missing directory did (since kkrqxtsp), and `Save path` and every other name did not, failing ENOENT instead. There is now one rule: a Save makes the parent directories of the file it writes, a pane's own or a terminal's scrollback included, and the make_dirs flag is gone. A directory it cannot make still fails the write with why, as fs.py's /nonexistent-pardes-dir case shows. fs.py now also saves into missing directories by Save path and by a written name. Co-Authored-By: Claude Opus 5.5 <[email protected]>