| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
| |
The served README, the skill and docs/fs.md now say where an address search
starts, that an event record's text may hold newlines and is read by its
count, that truncating tag clears its default words too (append to keep them,
u in the tag to get them back), how Dump and Restore behave and where dumps
go, that Kill stops only the foreground job (the rest of 'sleep 30; echo done'
still runs) and that Joincol needs a column to the right. The skill and
fs.md had Collapse as a column word; it folds the pane it is given.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Both did nothing without a word, from a click or a ctl write alike, and a
script could not tell a no-op from success. Joincol in the rightmost column
now fails 'Joincol: no column to the right'. A Theme name that is none says so
and names the themes sharing its first letter, since all of them (ThemeSel
lists them) are too many for a message or a refused write; a startup config
line still fails quietly, as its other bad lines do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
and the error words keep it so
The served README said every refused write reads as EINVAL, but a mount's
errno is 9ns's reading of the error's words, so a no-match or out-of-range
address and a refused Exit came back EIO, as the dogfood run saw. That split
is the useful one, malformed against failed, so the docs now state it rather
than the words being bent to EINVAL; a test pins each error's words to its
side of it. It caught one: 'regular expression search took too long' read as
ENAMETOOLONG, and now says it gave up past its step budget.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
After a pane made by Tty or Newcol written to a ctl, /look and /exec still
read the serials of the last look or exec, and a script took the old pane for
the new one. A ctl write now resets them and records what each line made, or
else the pane it ran at, as a look or exec does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
is refused
-/re/ searched forward, since only ?re? set the direction, where acme's
address() takes the direction a minus set (addr.c). And #100,#50 was taken,
leaving data to act on an empty range at 100; sam refuses it as 'addresses out
of order', which acme leaves out, and so does pardes now. The rest of the
dogfood report on addresses already held and is now tested: an expression is
evaluated from the current address, as acme evaluates it from w->addr
(xfid.c:446), so a search starts where the last address or data write left
it and . is that address. Taking . as the selection instead, as the report
asked, would part from acme, which has dot only in its ctl messages; the docs
say which is which.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
are tested for their offsets
Dogfooding saw a body right-click reported as ML0 0 2 3 qty. On file panes
the offsets were right in every case tried here (a scratch made over 9P and
clicked while another pane is active, after a hover, scrolled, soft-wrapped,
tab-indented, inside parentheses); the test now holds them. The 0 0 form
is a terminal's body, which has no offsets to give (its body is a history
snapshot); since the event file now takes a record back whole, such a record
can still be written back and done, and the docs say so. What the test did
find: a click on whitespace sent an empty record, where acme's look3 and
execute return without a word on an empty expansion (look.c:37, exec.c:141).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
the pane
A script writing to pane/N/exec bypassed the client holding that pane's event
file, which a middle click on it would not: acme sends a window's clicks to its
event reader (look.c:53, exec.c:173). Such a line is now an F record at 0 0
carrying its text; the root's look and exec still act at once. acme takes back
only origin, action and range, which a record with no place in the text cannot
use, so the whole record as read is taken too, and for an empty range acts on
its text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and restores
A client that wrote Restore saw its connection cut with no answer, and could
not tell a Restore from a crash. The listener now lets the writer's answer out
before the cut, and cuts only the old editor's connections, refusing their
requests meanwhile; a client that dials during it is the new editor's and
stays. Dump logs 'dump <path>' and the restored editor's log 'restore <path>'.
Keeping connections across a Restore was weighed and left: the fids name the
old editor's panes and opens, so it would mean carrying serials and open
records into the new one, where acme's Load only adds windows. tty's Restore
also closed its shells' ptys without reaping them; it retires them now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
not windows and a repeat cap
The windows returned wrong matches: a candidate reaching a window's edge was
left to the next window, half a window on, which could answer a match starting
mid-token rather than the leftmost, and addr then pointed data's next write at
the wrong bytes. The repeat cap missed mvzr's own worst case, a chain of a?,
and alternation under a repeat, each exponential inside one mvzr call the
deadline could not interrupt; and it refused ordinary s/S patterns. build.zig
now patches the fetched mvzr at build time with a step counter on its
backtracking recursion (matchPattern), so a fresh fetch keeps it and a moved
anchor stops the build; regexp.zig gives each compiled pattern a budget, about
300 ms here, and a search that spends it fails as taking too long. Windows,
the cap and their special cases are gone, and matches are exact again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
'<name>: Modified' read like a failure to retry, and a script that retried
Exit or Restore threw the text away without knowing it. The refusal now names
the word asking and what repeating it does, '<name>: Modified (Exit again to
discard)', and the skill sets it apart from lock's 'file in use', which is
the one to retry.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Restore replaces every pane with a dump's, so a pane edited since its last save
lost that text without a word, where Exit asks. acme's Load only adds a dump's
windows and so never asks; Restore now asks what Exit asks (acme's rowclean and
winclean, wind.c:511-529): each modified pane says so once, and Restore again
with nothing edited since goes ahead. Exit and Restore share the check and its
warned-at revision, as acme's winclean clears one dirty flag for any asker.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
waiting pty/run is cancelled
Review asked the docs to say what a script trips on: acme ignores OTRUNC on
data (fsys.c:543), so truncating it is an extension; addr sits past each write,
so a second echo x > data inserts after the first; and a pty/run line waiting
for a fresh terminal's first prompt waits for ever if none is drawn, so the way
out is to interrupt the read. The lock's retry advice was already there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
A client retrying a write that fails the same way pushed one err line after
another until the log's ring held nothing else. The same err as the newest
record is now that record with a count, (x4), as +Messages counts repeats; a
follower that has already read the record still gets each repeat as its own
line, since it may be waiting on exactly that.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Kill queued a signal effect for later, so the job it saw running by its marks
could have ended, and another started, before the signal went; and with job
control off the job shares the shell's process group, so the host skipped it
and Kill reported nothing. Kill now calls the host's kill_job while it holds the
turn, and when the only job is the shell's own group says 'Kill: no job to
signal', which also fails a write of Kill to ctl.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
windowed, 300 ms deadline
mvzr backtracks with no bound on its work (a*a*a*a*x over a hundred a's takes a
second, each repeat multiplying by the haystack length), and a search runs holding
the editor's turn, so one pasted pattern froze the editor. pardes does not write or
vendor a regex engine, so regexp.zig bounds what it hands mvzr: more than four
repeats is refused, a line longer than a window sized from measured worst cases is
searched in half-overlapping windows, and a deadline stops the search. addr names
each failure; normal s/S keeps what it found. The prescan also stops reading an
escaped backslash before n as a newline, and ends a class where mvzr does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Kill quit the editor, which in acme is Exit; acme's Kill stops the
commands it started. Exit now quits as acme's does (exec.c, rowclean):
it refuses once, naming each pane with unsaved text, and quits when
asked again with nothing edited since (a small scratch is not asked
about). Kill, bare or with names, stops the commands pardes typed into a
terminal (an exec, a middle click, a pty/run) while their shell's marks
say they run, by SIGTERM to the terminal's foreground job, never to the
shell (acme posts the kill note, which terminates). Both are session
builtins; the topbar's Kill becomes Exit, same width, and every golden's
topbar row changed by exactly that word (checked line by line); the
builtins script scrolls one more row for the index's new line.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The sam-style calling convention for mvzr (a line per haystack, . made
[^\\n] only when a pattern names \\n) lived in addr.zig; normal mode's s
and S called mvzr over the raw selection, where ^ meant the selection's
start and . crossed lines. src/regexp.zig now holds the one Regex
(compile, find) both call, and fs.zig and pardes.zig drop an unused mvzr
import. hxdiff and hxparity stay at their known 17 and 6 mismatches, none
of them regex cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A first-time user through a mount could not find how to get a terminal,
read path++text as a typo, did not know > on data replaces only the
addressed range, which regexps addr takes, that lock fails fast, what
errors is, where an unknown exec word goes (typed into a terminal, not an
error), that new panes are +New and a column's last pane leaves one, or
which 9ns mount other processes can see. The README (still 45 lines),
docs/fs.md and the skill now say.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
the reason
Through a mount every refusal reads as Invalid argument, so a shell user
could not tell a missing regexp match from a bad ctl word. Every write or
truncation the tree refuses or that fails now adds err <serial|-> <file>:
<why> to /log. No per-pane readable error file: acme's errors only takes
text, and one stream is simpler to watch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
The log said new N / and later del N <cwd> for a terminal, with no rename
in between, though a terminal is named by its directory. setCwd now logs
the rename once the pane has been announced. A terminal's ctl line gave
body length 0 while its body reads its history; it now counts that
history (without keeping a copy).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
A contended lock parked until the holder unlocked, but through a kernel
or FUSE mount the kernel serialises writes to one file, so the parked
lock held up the holder's own unlock and close on that ctl, and the two
deadlocked. acme's qlock blocks; here the second lock is refused at once
with file in use (EBUSY) and the client retries, and nothing parks on
the lock any more.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
answering busy
A new terminal's shell takes tens of milliseconds to draw its first
prompt, and a run written meanwhile answered busy, so a script's first
command was lost, while pty/status said busy 0. A line written before the
first prompt now waits for it and is sent then (a respawn in between
keeps it waiting for the new shell), and pty/status's third field says
what a run would be told: busy while a command runs or text is typed at
the prompt, not before the first prompt.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
failed addr leaves no address
addr's regexps ran mvzr over text[from..hi]: ^ and $ anchored only at the
slice's ends, . matched newlines, the search never wrapped, and every
failure read as bad address syntax. The regexps stay mvzr's, called the
way sam searches (editors/acme/regx.c): one line per haystack, so ^ and $
fall at line boundaries and . never crosses a newline; a pattern naming
\\n runs over the whole text with its . made [^\\n]; /re/ wraps unless
limit is set, and ?re? takes the last match before the range. The ceiling
(mvzr's first alternative, not sam's longest) is documented. A failed
address says why (no match for regexp, address out of range, bad regular
expression), and a failed write to addr leaves no address, so data and
xdata refuse until the next good one instead of writing at the old range.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
A shell's echo NEW > data opens data with OTRUNC, and truncate() spliced
the whole body away before the write replaced the addressed range: a
five-byte replacement erased a buffer. data and xdata hold what addr
names, so truncating one now deletes that range and nothing else; > on
data replaces the range and : > data deletes it, and only truncating
body clears the buffer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
Kill in pardes is acme's Exit, not acme's Kill, which only stops
commands; the README, docs/fs.md and the skill now say so where the root
ctl lists it. Delcol, Collapse and the other column words take a pane's
ctl and act on that pane's column; Newcol and Joincol are the root's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
in the root listing's natural place
A write to /focus leaves a folded pane folded, as rio keeps current apart
from unhide; while a column or workspace tag has the keyboard no pane
does, so /focus reads empty and every pane's ctl says notcurrent (an
empty read also no longer answers stale staged bytes). focus, ctl and
commands now list after status, and the discovery test checks the root
as a set rather than by position.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
argument is refused before anything runs
A ctl write failed only on a malformed line: Mount x reported its error
in the editor while the write succeeded, and a bare Mount failed only as
it ran, after earlier lines of the write. acme's ctl answers a command's
error (editors/acme/xfid.c:700). Builtins now declare requires_arg beside
takes_arg (settings: those with a value to set), and the check refuses a
bare one as wrong #args before any line runs; while a ctl runs, the first
error a builtin reports fails the write, quoted with its line, and the
prompt refusal quotes its line too. Docs say what a failure mid-write
leaves done.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
A script could learn the builtin words only from source or the Help
window. /commands is generated from the registry: one line per builtin,
Word or Word arg, then root or pane for the ctl that takes it, so it is
always this build's own list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and reads the settings, a pane's ctl its own
Every builtin could only be clicked, or written to exec, and the settings
could be read only as the Config window's prose. acme keeps window verbs on
a window's ctl, and webfs and upas/fs keep session settings on a root ctl.
Each builtin now declares its scope (scope = .session; settings are all
session, the rest pane), read by the registry. The root /ctl takes session
builtins and reads every setting in the words a write takes, so its read
written back changes nothing (panel and scene effects now take on/off like
the toggles, to make that true); a pane's ctl takes the pane's builtins
beside get, lock and unlock. Writes are checked whole and refused in Plan
9's ctl words (unknown control message "X", wrong #args ...), which 9ns
now maps to EINVAL (cloud9 re-pinned at a8c7a715). A builtin that would
prompt for its argument fails the write instead, and a refusal is answered
at once, not after the frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
ctl says whether it is current
Scripts had no way to ask which pane has the keyboard or to give it one.
rio answers both through a window's wctl: its read ends with current or
notcurrent, and a write of current takes the keyboard (rio(4)). A pane's
ctl line now ends the same way, and since there is one keyboard for the
whole tree, /focus reads the focused pane's serial and takes a serial to
move the keyboard there, off any header that had it; a serial no pane has
fails with "no such window". fs-bench's stale readdir check is fixed too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
| |
Truncating the tag file replaced its text without an undo point, so the
cleared text was gone for good. It is recorded now like a typed edit, the
history is kept, and docs/fs.md says so.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
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]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
shut down
The held read is now kept by the Ticket cloud9's Conn.hold() gives its
park and answered through Conn.answerWith(), which makes the answer only
while that very park still waits, instead of walking the engine's slots
by tag; pardes no longer reaches into the engine for it. A second read on
an open whose read is held fails with file in use rather than sitting
parked where nothing answers it, and a read that waits with no open
record to hold it is logged and asserted on. A read on an event or
pty/data open whose pane closed answers acme's "window shut down"
(editors/acme/xfid.c:1005). The pane keeps its run's and its lock's open
handles, checked through openOf on use, not record indices. Docs: lock
from a shell needs a held fd, and a command that clears the screen may
read as cut.
Needs cloud9 zvuqvnzy (cca47d63), which adds Conn.hold, waiting and
answerWith; build.zig.zon still pins 82d8152c until that is pushed and
re-pinned.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A clear or reset while the command ran, a start or end mark that came on
the alternate screen, or a garbage end pin read as a complete answer;
now any of them, like a start that scrolled out, answers exit N cut.
The output is read from at most output_rows rows above its end instead
of the whole history on the editor's thread (cut if that clips it), a D
with no status answers exit ? rather than exit 0, and running out of
memory answers error out of memory rather than cut.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A client doing an edit of several writes to addr and data had no way to
keep another client's from landing in between. acme's window ctl takes
lock and unlock for this (editors/acme/xfid.c:603-611): a qlock that
blocks a second locker, owned by the fid that wrote it and given up when
that fid is clunked, binding only clients that ask. pardes does the same:
the open's record holds it, a second lock parks until unlock, close or
the pane closing, and no other write is refused for it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Snapshots (and /log's cursor), runs, and the reader_handle constants for
event and pty/data each reused the open handle and each validated
handle and node on its own. Now p.fs.opens is one table of 64 records,
each the node it was opened on and a tagged union of what it holds, like
lib9p's per-fid aux and acme's Fid (editors/acme/dat.h:373-385): one
lookup (openOf), one release, ENFILE when full. A held read lives in
its open's record, so it goes with the release. Opens that hold nothing
answer handle 0 and take no record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
A following log, event, pty/data or a pty/run before its answer used to
answer .again and wait for a wakeAll, which only the parked-write path
asked for, so the band-aid had every queue push set turn.parked. Now the
core keeps such a read (ctlfs.hold) and, as the turn is given up after
anything that queued a record, ran a command out or closed a pane,
answers it on its own connection, the way factotum answers the log reads
it keeps and acme an event read. Only a read the engine still holds
parked is answered, because cloud9 tells the backend nothing of a
Tflush, so a flushed read spends no record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and E.BUSY
The answer is now the header line (exit N, or exit N cut M when only the
last 64 KiB were kept, or exit N cut when its start scrolled out of the
history), then what the command printed: the screen text between its C and
D marks, which the marks handler pins so scrolling keeps them.
Also from review: a plain /log open honours its offset until follow is
written, so tail -n and less work; whitespace-only run lines are refused.
cloud9 is pinned at 82d8152c: 9ns keeps up to 32 requests in flight per
mount so a waiting read no longer freezes the rest of it, and E.BUSY
replaces the errno pardes spelled locally.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
/pane/<n>/pty/run takes factotum's rpc shape: write one line on an open,
read the answer on that same open. The answer is exit N once the command
ended and the shell is back at a prompt; busy at once when a command runs,
text is typed at the prompt, or the shell has not drawn its first prompt;
error not run when the shell refused the line without running it (fish on
a syntax error keeps it on the prompt, so it is taken back with Ctrl-U);
error shell gone when the pane closes or its shell is replaced; error no
prompt marks for a shell pardes could not instrument.
The end comes from the shell's OSC 133 marks. ghostty parses D's exit
status and drops it, so the stream now runs through a handler that wraps
ghostty's and follows prompt -> input -> running -> done. The marks pardes
injects into bash and fish carry aid=pardes and only those count, so fish
4's own marks (which doubled ours), a nested shell's, and a stray 133;D in
printed output are ignored.
Checked end to end against real bash and fish: false, exit 7, a syntax
error, sleep, busy while running, and the pane closing mid-command.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
file; pty/ctl reads back
A read parked on a queue was retried only when some unrelated write had
to wait, so a follower of /log (and a reader of event or pty/data) slept
until then. Pushing a record now marks the turn parked, and giving the
turn up wakes them (measured: stuck past 3 s before, 0 s after).
event and pty/data consume what they read, so a second open for reading
is refused with rio's "file in use" (EBUSY through 9ns); writers still
get in, and pty/data queues output only for an actual reader. pty/ctl
reads back "winsize C R", in the words it takes.
/log fixes from review: a record longer than a read comes in pieces (a
shell read loop failed on long lines), every repeated message is logged,
a record bigger than the ring is cut to fit instead of emptying it, the
ring is reserved at boot so recording never allocates, and panes present
at boot are recorded first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
/log is now one ring (64 KiB, 4 KiB on the board) that records new, del,
rename and save, and a msg line for everything the editor says. An open
freezes it, so cat log shows what happened lately and ends; writing
follow to that same open makes reads past it wait for newer records, and
a follower the ring outran reads lost N first.
The message log keeps the pane's serial, not its reusable slot.
A test now fails when the tree serves a file /README never mentions; it
caught pty/status, now documented along with typing through pty/data.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The editor's loop was the only thing that could answer a 9P request, which
made the editor's own syscalls through a mount of its own tree -- a Look at
/mnt/9p/pardes/<me>/anything under a `9ns --mntgen` view, a Save into it --
requests only the blocked loop could serve. The name-based refusal that
followed (ownMountSuffix) and the in-process routing of a mount of oneself
(Client.sameSession) were patches over that, and both are gone, with the
mailbox that shipped every request to the editor's thread.
One rule replaces them, `pardes.turn`: the core is single-threaded, the
editor's thread has the turn by default and gives it up in two kinds of gap
-- while it waits for input and while a step of it is out in a host syscall
-- and a cloud9 connection task takes it in those gaps to answer. `out`
counts the steps that are out, from any thread: while one is, the core reads
consistently but that step still holds pointers into it, so a request that
would change a pane (a write, a truncation, an rmdir) is parked in the
engine and retried when the turn is next given up with nothing out, and the
editor's own wake waits for the count to reach zero. It is never a write of
its own that a step waits on out there -- writes come from a shell
performing a save between steps -- so a parked request is never the
syscall's own, and making a pane or rendering a screen need not park:
every yield sits before its step's mutation, so the layout and the surface
are whole under it. A changing request that queued effects is answered
once the editor has performed them (`echo Save > exec` returns with the
file written, as acme's `put` does), and it settles the way a step does,
because without that a /log reader waited for the user's next keystroke.
Every host syscall on a user path has to give the turn up, not fs.zig's
alone: the first end-to-end run hung in `inotify_add_watch` performing the
new pane's watch effect. PDFs and images are read whole at open, so no
draw goes out into the host. The core's allocator takes its fixed buffer
through the lock-free interface, since a connection task allocates while
the editor's thread is out in a syscall that allocates too. A Restore puts
the replacement in first and releases every task waiting on the old core.
cloud9 (pinned at eb1a104) parks an open, a truncating wstat, a clunk and a
remove on `again`, not only reads and writes, and answers a parked job
whose fid was clunked without asking the backend.
Verified: test/selfmount.py runs the editor under `9ns --mntgen` and
Looks at, reads and Saves its own tree through the mount; a unit test pins
that a change parks while the editor is out mid-step and lands when it
rests, while a read is answered in the window. 9P over the Unix socket
against a tty session, same machine, Debug builds: a read of /index 278us
-> 61us, a truncating body write 1184us -> 609us, exec Save 718us -> 583us;
the gesture benchmark is unchanged (geometric mean 0.997 over 53 cells).
Also from the reviews: a notice chip over an image or PDF pane was painted
out by the picture drawn after the cells, so pictures give up the rows; in
the GUI a tree-sitter context band painted over the chip, so body layers
are emitted first; a message is one row of printable text, its 256-byte
cut never leaves half a glyph, and one wider than its pane keeps its tail
(the file name, the reason) rather than its head.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Opening a path inside this editor's own 9P tree hung the session outright, and
it is easy to do by accident: a file manager whose $EDITOR is pardes, or a Look
at anything under /mnt/9p/pardes/<me>/. The realpath, the stat and the read all
leave through the mount and come back as 9P requests only this editor's loop
can answer, while that loop is blocked making them. The filesystem then stops
answering anybody, which is what made it look frozen rather than slow.
The answer has to come from the NAME, before any syscall, because the syscall
is the thing that never returns: the listener notes the name it is posted under
and `resolveOs` refuses a path containing `/pardes/<that name>/`, with
`readLimit` refusing it too for anything that gets past resolution. Another
session's mount stays perfectly usable, and `/n/self/...` is the way to reach
your own tree -- the editor serves it from memory without leaving the process.
Reproduced before and after: the 9P write that never returned now returns, the
editor stays responsive, and it spends four CPU ticks doing it.
The transient lines also now look like what they were modelled on. They carried
the context band's bookkeeping -- one list, rows reserved the way sticky
headers reserve them -- but still painted as ordinary body text, so a message
read as a stray line at the bottom of the pane rather than as part of its
chrome. They take the tagline font and the tagline's own colours now, verified
on a running editor: the announcement lands with font_role=tagline.
Two tests the features never had: a builtin announcing itself, reaching the
notice list, being overridden by Msg's own text and silenced by Verbose; and
the own-mount guard, including that a session with no listener refuses nothing.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The registry sweep could delete a live socket, anywhere on the filesystem. A
reviewer reproduced it: a socket that is bound but has not reached listen(2)
answers ECONNREFUSED exactly like a dead one -- that window is every server's
startup -- and the sweep then followed the entry's symlink and unlinked
whatever absolute path it named. It now follows a target only into the
directory our own sockets live in and only to a `pardes-9p-*.sock` name, it
re-probes immediately before deleting rather than trusting a probe that is by
then several syscalls old, and a readlink that exactly filled its buffer is
treated as the truncation it is. The test grew a case for an entry whose
target is not ours: the entry goes, the file does not.
Ctrl-V in raw tty mode was a black hole when the yank register was empty --
neither typed nor forwarded -- so vim's visual block, readline's quoted-insert
and every other program's Ctrl-V simply vanished. With nothing to paste the
chord belongs to the program again.
The lone-ESC flush added earlier was dead code. vaxis already returns Escape
for a one-byte 0x1b (`Parser.parseGround` asserts `input.len == 1`), so the
carried byte it waited for can never exist; a reviewer showed a 3 ms gap and a
60 ms gap behaving identically. Removed rather than left to imply a guarantee
it never provided.
A shell whose editor is gone can start one again. Naming a live but
unreachable session made `pardes <file>` exit 1, which let a stale environment
variable lock someone out of their own editor; it falls through to an ordinary
session, as it did before the variable existed.
Also: the macOS ABI check for `pardes_topbar_pane_border_px` had been replaced
by a duplicate of the line above it; `--startup` now fails on a leak the way
every other measurement in that file does, and stops calling its maximum a p95
below twenty samples; the served README and the skill no longer tell you to
write to `data` with `>`, which truncates the whole body before the write
lands; `docs/v9fs.md` described the allocate-on-walk design that was rejected;
and `test/fs.py` keys nesting off `PARDES_PID`, so its forwarding case stops
passing only when the runner happens to be inside a live pardes.
fs-test now reaches its one documented pre-existing failure instead of dying
early. Suite 778/783 with the two known crashes.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Tcreate that replaced acme's /new was a step away from the idiom dressed
up as a step toward it. A pane is named by a server-assigned serial, so the
create ignored the client's name: `mkdir /pane/foo` succeeded and left you
/pane/12. A mkdir that does not make the directory you named is worse than the
read-with-side-effect it replaced, and it broke in the shell workflow that
motivated the change. `create` is out of the declared features, so Tcreate is
EPERM again; Tremove stays, since `rm` to close a pane is unambiguously right.
/pane/new is now opened, not created: the open makes the pane, the read of
that fid answers its serial, two reads agree, and closing it leaves the pane.
That is /net/tcp/clone's mechanism (kernel/network/ip/devip.c, in ipopen),
not acme's, and the difference is deliberate. acme allocates during the walk
and lands inside the new window, so /dev/new/body works in one step, and it
can afford to list `new` because a Plan 9 directory read carries every entry's
stat and nothing walks. A kernel or FUSE mount walks and stats each name a
listing gave it, so allocate-on-walk would make a pane per `ls -l`. Allocating
on open keeps `new` listed -- a stat is not an open -- at the cost of the
one-step new/body. `new` stays unreachable from an editor path, because that
resolution serves Look hover previews.
The idiom pass behind it, read out of the Plan 9 tree at
~/05-genizah/principia-softwarica rather than recalled:
Rerror carries a string, not an errno (man 5 error: `ename[s]`), and acme
names every refusal. The five refusals pardes shares with acme now say what
they mean; the generic sites keep their bare errno rather than invent strings
acme does not have. body and tag declare DMAPPEND, which they had always
behaved as (acme(4): "always appended; the file offset is ignored"), checked
first against Linux's fs/9p, which never maps the bit. excl stays unset
everywhere, because acme sets DMEXCL on nothing. Blocking reads, per-object
addr scope and the readable pane ctl were already right. Real stat sizes and
qid versions stay: acme reports length 0 and version 0 for everything, and
Linux clients need better.
One bug fell out of it. open reset the addr range, so `echo '#0,#5' >addr;
cat addr` answered `0 0` and `cp addr dot` copied zeros. acme(4) makes the
contract explicit -- "a regular expression may be evaluated by writing it to
addr and reading it back" -- and acme gets away with resetting on the 0-to-1
open only because its clients hold the fid across both. A shell cannot: that
is two opens. The register is cleared by truncating it now.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The 9P tree stops being a command language wearing a filesystem. /new
created a pane as a side effect of a *read*; it is now Tcreate in /pane,
with Tremove to close, which cloud9's engine has always supported and the
editor never declared: tree.zig now says
`features = .{ .create = true, .remove = true }`. Eleven pane ctl verbs
become files that can be read as well as written -- dot, limit, dirty,
mark, scroll, look, exec -- leaving ctl with `get`, the one verb no file
would say better. Root /ctl splits into a read-only /status and the
/look and /exec files whose write IS the click. stat carries real sizes
where it used to answer 0, and qid versions track a pane's revision, so a
client can poll for change without re-reading the body.
Commit a624a56 moved raw-tty keys to an early-return branch that knew only
Ctrl-B and bare Escape, and in the same edit deleted the paste branch below
it. That cost Shift-Escape (the unconditional way out of tty mode) and both
paste chords: Ctrl-V and Ctrl-Shift-V reached the child as keystrokes, so an
agent CLI running in a pane took Ctrl-V for its image-paste binding and
answered "No image found in clipboard". Both are restored, with tests.
Nested detection was not subtly broken but deleted: 60367d8 removed
nested.zig's process-ancestry walk and left "am I inside pardes" derived from
PARDES_FORWARD_LOOK, which read "0" both for --nested and for "the listener
did not come up". PARDES_PID now answers that question on its own, checked
with kill(pid, 0); PARDES_9P and PARDES_PANE answer how to reach it; the flag
is gone. The posted-9P registry also self-heals now -- a session that aborts
cannot unlink its own socket, so posting sweeps entries whose target refuses
a connection, symlinks only and on a definite ECONNREFUSED only.
Elsewhere: tty scrolling is sticky-bottom, following new output only from
the last row, with typing and entering raw mode snapping back to live; the
boot layouts are a Boot enum instead of a chain of ifs, and the bare tty
startup (Boot.tty, which main.zig names) opens an empty text pane under the
shell while tests keep Boot.tty_shell; builtins announce themselves on the
message row under a Verbose setting that is on by default; Config prints
each setting the way you would type it back, so WindowOpacity 70 rather than
"WindowOpacity: 70%"; LocationsConfig opens its window only when called bare;
every tagline puts the word that closes the thing last, and a column now
outlives its panes -- closing the last one leaves an empty pane, and only
Delcol, newly on the column tagline, takes the column away.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
The hand-written poll loop for Unix/TCP listeners is replaced by
cloud9.serve.Runner; requests are queued to the editor thread, which answers
them under the connection lock on each frame and retries parked reads as
before. QUIC keeps the poll path (its adapter is fd based). The detached
server no longer loses a wake that lands between frames. The firmware path
keeps driving the engine with push/step. cloud9 re-pinned.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
src/9p.zig shrinks to an 88-line alias: the engine and the backend contract
(Req, Reply/ReplyWith, Op, Status, Attr, E, error strings) now come from
cloud9.fs. The editor's limits become cloud9.fs.Options values; the ESP32-P4
board backend drops its own copies of the contract types. No behaviour
change; fs-bench still allocates nothing per request. cloud9 re-pinned to
the commit that carries the engine.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The served tree loses the self/ level: /index /ctl /new /log /screen
/listeners /pane/<n>/... /os, with /src only in -Dembed-sources=true builds
(default off, on for esp32p4). ctl speaks the editor's own language with two
lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/
factory directory becomes one clone file; cons is gone (exec Msg); name and
sel are files; stats report real lengths, modes and mtimes; /log streams
pane new/del/rename/save events. The tree code lives in src/ninep/
(tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host
access, mounts, resolution and find/grep. Same engine and transports.
README (fs-help.txt) and docs rewritten; tests updated and extended.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| | |
|