summaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAge
...
* Reuse bounded source analysis and speed up result context traversalGabriel Schneider32 hours
|
* Default location results to stacked addresses and source previewsGabriel Schneider32 hours
|
* Bound preceding result context by its outermost tree-sitter declarationGabriel Schneider32 hours
|
* Highlight exact source occurrences in LSP references and goto resultsGabriel Schneider32 hours
|
* Show result declaration locations only on their first header lineGabriel Schneider32 hours
|
* Mark omitted source after result declaration context with ellipsesGabriel Schneider32 hours
|
* Align search and LSP results in groups of eight matchesGabriel Schneider32 hours
|
* Release Pardes 0.3 and stamp the macOS package versionGabriel Schneider32 hours
|
* Center GUI pane indicators using cached font ink boundsGabriel Schneider2026-09-15
|
* Separate pane and column grip hit areas from tagline text with a small gapGabriel Schneider2026-09-15
|
* Keep column grips blank and muted in every focus stateGabriel Schneider2026-09-15
|
* Cycle pane input modes with Mode and retain terminal tag accessGabriel Schneider2026-09-15
|
* Reorder columns using aligned and distinctly colored tag gripsGabriel Schneider2026-09-15
|
* Make location highlighting an explicit output producer choiceGabriel Schneider2026-09-15
|
* Normalize keypad Enter in native and attached TTY inputGabriel Schneider2026-09-15
|
* Separate tag text geometry from physical pane gripsGabriel Schneider2026-09-15
|
* Ignore slash-only comment markers in general Look detectionGabriel Schneider2026-09-15
|
* Use result metadata when walking output locations with n and NGabriel Schneider2026-09-15
|
* Separate discontinuous pinned source context with thin bordersGabriel Schneider2026-09-15
|
* Hide redundant locations on neighboring result contextGabriel Schneider2026-09-15
|
* Align search and LSP context locations flush leftGabriel Schneider2026-09-15
|
* Match filename tint lightness in every canonical theme and focus stateGabriel Schneider2026-09-15
|
* Mute result declaration context and show nesting after locationsGabriel Schneider2026-09-15
|
* Add optional compact tagline styling for source contextGabriel Schneider2026-09-15
|
* Lead terminal tags with a tinted Tty commandGabriel Schneider2026-09-15
|
* Distinguish filename hues while preserving tag brightnessGabriel Schneider2026-09-15
|
* Expose LocationsConfig in location result pane tagsGabriel Schneider2026-09-15
|
* Follow embedded PDF links through LookGabriel Schneider2026-09-15
|
* Remember tag cursors and reserve uppercase pane navigationGabriel Schneider2026-09-15
|
* Tint pane tag filenames with an optional theme colorGabriel Schneider2026-09-15
|
* Align location results and configure source contextGabriel Schneider2026-09-15
|
* Add optional tree-sitter declaration contextGabriel Schneider2026-09-15
|
* Mirror raw terminal mouse selections for text transferGabriel Schneider2026-09-15
|
* Align file clicks and insertion cursorsGabriel Schneider2026-09-15
|
* Route mouse thumb buttons to jump historyGabriel Schneider2026-09-15
|
* Preserve selections when navigating jump historyGabriel Schneider2026-09-15
|
* Add Linux Tty9p mounted terminals and forward raw TTY keysGabriel Schneider2026-09-15
|
* 9p: use cloud9 protocol sessions and transportsGabriel Schneider2026-09-15
|
* trunk: resume before the Reload experimentGabriel Schneider2026-09-15
| | | | Empty marker on the last pre-Reload change. Keep the Reload experiment on reload (3801914), its first change on reload-start (200a1fc), and the unfinished performance investigation on reload-perf-wip.
* Resolve relative restores in the dump directory and propagate LSP probe errorsGabriel Schneider2026-09-15
|
* macos: merge local trackpad gestures with current upstreamGabriel Schneider2026-09-07
|\
| * macos: use two-finger click for Exec and deep press for LookGabriel Schneider2026-09-07
| |
* | Refactor panes and filesystem; replace FUSE with 9PGabriel Schneider2026-09-07
| | | | | | | | | | | | Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples. Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
* | syntax: a results buffer is coloured as the code it quotesGabriel Schneider2026-09-06
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +Grep, +Search and every language answer render rows like src/look.zig:718:12-16 fn grepText(path: []const u8, text: []const u8... — a location, a space, and a piece of some file. The location names the file, the file names the grammar, and the rest of the row is a fragment of that language, so a grep over Zig reads as Zig and one Markdown row in the same buffer does not pretend otherwise. The location itself is left uncoloured: it is not code, and painting it as code is how a path starts looking like a keyword. `look.parsePathLine` decides what counts as a location — the same primitive n/N already walks these buffers with, so the two agree by construction about which rows are locations. NOT `lookableLineSpan`, which is n/N's whole heuristic: it calls `resolve`, and a `realpath` per row per scroll is not something a render path can afford. A bare filename is refused too; only `path:line` counts, or a prose line whose first word ended in `.md` would colour the rest of a sentence. THE BUFFER IS COLOURED WHOLE, ONCE, WHEN IT IS FILLED. An adversarial pass measured the obvious per-window implementation and it was untenable: the rows are independent, so a window pass buys no fidelity, only amortisation, and pays a burst on every scroll that outran the covered range. Grammars compile their highlights query on first use — zig 26.9ms, cpp 18.9ms, rust 14.3ms — so a polyglot grep showing six languages stalled a frame by 66ms, moving a cost the syntax module had deliberately put on "opening a file" onto a scroll. It also raised tree-sitter's allocation rate 3.5x (8,785 per refresh against 2,454) into a 16 MiB bump arena that only reclaims LIFO, so ~16 scroll re-highlights exhausted it — and that arena is shared with real file panes, so a results pane could evict editing. A grep is capped at 512 rows; colouring it once makes the covered-range check true forever after and scrolling free. The rest of that pass, in the same spirit: injections off for a single row (both build a SECOND parser, per fenced block and per inline node, which is absurd for one truncated row that almost never contains a fence), one query cursor for the buffer instead of one per row, a one-entry extension memo so non-matching rows stop paying a 29-spec scan, and NO highlights at all when nothing painted — an all-zero run is not the same as none, and it defeated `recolorSyntax`'s fast path, making every +Help and +Config walk its graphemes every frame to paint nothing. One correctness bug from the same pass: a failed `setLanguage` has already nulled the parser's language, so leaving `held` on the previous grammar made every later row of it skip the call and silently lose colour. Documents keep `.source`: a New scratch and a real file are output-shaped but have one language and an edit per keystroke, and `saves` is the line between the two. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | messages: a fixed log of what the rows said, and a word to read it backGabriel Schneider2026-09-06
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A message row is cleared by the next keystroke, so anything reported while you were looking at another pane was gone before you could read it — a save that failed, a watcher's reload, a builtin's complaint. `setMessage` now records into a fixed ring first: no allocation and no failure path, because it sits underneath `reportError`, which is reached from sites that are reporting an allocation failure. `Messages` (`SPC h m`) reads it back oldest-first. Three things an adversarial pass found, each of which defeated the feature: PROGRESS IS NOT A MESSAGE. A language server emits `Indexing 47%` several times a second, and every tick is a distinct string BY CONSTRUCTION, so no de-duplication can collapse it: at the client's one-per-150ms throttle it takes about nineteen seconds to push every real message out of the ring. A log that one indexing run empties is not a log. That path is `setStatus` now — the row, and nothing else. THE CLOCK MADE EVERY HOST MESSAGE UNIQUE. `message.stamp` prefixes `HH:MM:SS`, so `saved /x.zig` at 14:32:07 and at :09 compared unequal and the ring filled with rows that look identical and each say (x1) — exactly the case the de-duplication exists for. It compares `message.body` now, the row without its clock, and the newest wording wins so the row carries the last time it happened rather than the first. It also keys on the PANE (one pane's failure must not be recorded as another's) and compares the truncated form, so two identical messages over 256 bytes stop being two rows. AND THE CAPACITY BELONGS IN limits.zig. 128 entries is 32.75 KiB that is allocated whether or not anybody reads it — 8.5% of the ESP32-P4's whole 384 KiB heap, about the size of its effect ring. The board takes sixteen. The builtins/leader goldens move because the listing gains a row, and builtins.snap middle-clicks a SCREEN COORDINATE that Tutor moved out of; both updated selectively and verified against a fresh run. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | pipe: helix's other three shell commands, and its newline ruleGabriel Schneider2026-09-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `|` was the only one of helix's five. The other four differ in exactly two things — whether the selection is stdin, and where the output lands — so they are one action carrying a `PipeBehavior` rather than four code paths: | stdin is the selection, output REPLACES it (had this) A-| stdin is the selection, output discarded shell_pipe_to ! no stdin, output inserted BEFORE each selection shell_insert_output A-! no stdin, output appended AFTER each selection shell_append_output Each arms the same visible tag-tail prompt with its own marker (`|`, `|-`, `!`, `!+`) so the prompt says which one you are in — they take the same command line and do very different things to the buffer. Two helix rules came with them. A behaviour that sends no stdin runs the command ONCE and every cursor gets that one answer (helix's `shell_output` cache): ten cursors and `date` give ten identical stamps rather than ten forks racing to produce one. And a command that put a trailing newline on a selection which did not have one has it taken back off — that is what keeps a one-line `| tr a-z A-Z` from becoming two lines. The existing multi-range test moved with that rule and now pins it deliberately. In all three writing behaviours the OUTPUT is what ends up selected, keeping the original range's direction, so an operator can follow straight on from what the command just produced. `$` (`shell_keep_pipe` — drop the selections whose command exited nonzero) is still missing: it needs a per-selection verdict and the runner's answer is atomic. Noted in docs/helix-keys.md beside the `$` divergence already there. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | pipe: a filter that fails says so, and `| head -1` stops failingGabriel Schneider2026-09-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two things made the selection pipe feel like it had never worked. It runs — test/snapshots/pipe.snap drives the real binary through a pty and filters `alpha beta` to `ALPHA BETA` — but it had no way to tell you when it did not, and one of its failure conditions was not a failure at all. IT NOW SAYS WHY. `runOne` read the command's stderr into memory and freed it two lines later, unread; every caller answered a failed filter with a bare `return`; `pipeResponse` had eight more silent exits under that. So `| trr` (a typo), `| grep nomatch` (exit 1), `| jq .` on bad JSON — all did nothing, said nothing, and left the text alone with no way to find out why. The runner carries a `Failure` home instead: which selection, what became of the command, and its own stderr. The core turns that into an `+Errors` buffer — acme's name for output that came from the program rather than from a word anybody clicked: | trr exit status 127 sh: line 1: trr: command not found An output buffer rather than the message row because the useful half of a shell failure is the text the shell wrote, and a 256-byte row would keep the label and throw away the reason. Focus stays with the file: `openRead` moves `p.active` to what it opens, which is right for a Grep you asked to read and wrong for a report you did not — you want to fix the command and press `|` again. A host with no `pull_pipe` at all (the detached daemon, the browser, the board) now says that too, instead of answering failure into the void. `| head -1` NOW WORKS. `writer_context.ok` was part of the success condition, so a command that stopped reading its stdin failed the filter even though it had done exactly its job: `head` takes the line it wants and closes the pipe, the write gets EPIPE, and a selection bigger than the 64 KiB pipe buffer was enough to trigger it. helix joins its input task and ignores the result for this reason; the exit status is the whole verdict. Also reported rather than swallowed: the ten-second timeout, the output ceilings, and a file edited while the filter ran — one keystroke during a slow command used to discard the result in a way indistinguishable from the filter doing nothing. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | chords: Look and Exec act once per selection, and say when they cannotGabriel Schneider2026-09-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Both acme chords read the PRIMARY range and dropped every other cursor on the floor — the one thing a multi-cursor editor must not do with a command the user aimed at all of them. They are also structurally outside the machinery that would have handled it: the Enter/Tab chord is intercepted before `handleNormal` so it never becomes an Action with a scope, and `runBuiltin` bails on `multiOnce` anyway, because a builtin is per-keystroke rather than per-cursor. So `chordEachSel` takes the `submitPipe` shape instead: `paneRanges` once, forward in document order, every range's bytes COPIED before the first builtin runs. The copy is not caution — a `Look` opens panes and an `Exec` can run a builtin that edits or closes the pane those offsets point into, and a selection whose text is `Del` is a legal Exec. The loop re-checks the slot and its serial between iterations, the same guard `replaySels` makes for the same reason. With one cursor it returns false on the first line and the old path runs untouched. FOCUS FOLLOWS THE PRIMARY. `lookAt` sets `p.active` for every target it opens, so `Look` over four selections used to leave you at whichever one happened to sort last — an accident rather than an answer. ...AND A LOOK WITH NOWHERE TO PUT ITS ANSWER SAYS SO. All four slot checks in `lookAt` were a bare `orelse return`: with one selection that merely felt like a dead key, and with several it means "I opened nine of your fourteen and told you nothing". They report `NoPaneSlots` now, through the channel output_pane.zig already raises it on and a test already pins. The three openers report their own failure too, so a file that will not open says whether it was permission, a pipe, or size — which `look.readFile` only started distinguishing this week. Known and left: N selections that all resolve to nothing run N searches over one +Search buffer. Wasteful, converges, and worth its own change. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | grep: one file it cannot read is not the end of the searchGabriel Schneider2026-09-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `look.grep` aborted the whole walk and returned `OpenFailed` the moment any file refused to open. One root-owned 0600 file in the tree — or one deleted between the walk and the read, which is routine in a build tree — turned a search of ten thousand files into zero results and a word on the message row that explains nothing. A grep is a question about the files you can read, and the ones you cannot are not an answer to it: they are skipped now, along with a read that fails partway. Opened NONBLOCK for the reason readFile has it, so a FIFO in the tree cannot stop the search until somebody writes to it. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
* | errors: a mistyped flag is a sentence, and a pipe is not a documentGabriel Schneider2026-09-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Every argv refusal in main.zig was `return error.BadArgs` out of `main`, which std prints as `error: BadArgs` with a return trace under it — the same shape a real crash has, for the most ordinary thing a person can do. It also said `BadArgs` and nothing about which argument, at sites that knew exactly: pardes: no such option: --hepl pardes: -n takes 1 or 3, not 'abc' pardes: one file or directory at a time, and 'b' is the second pardes: --detach and --attach are opposites: one runs the session, the other joins one Try 'pardes --help'. stderr rather than the `+Errors` pane one function down, because argv is read before a core exists and the person who mistyped a flag is looking at the prompt they typed it into. `--attach`'s refusal already answered this way; now all eleven do. `getcwd` failing is no longer reported as an argument problem, and a `.url` or `@pN` positional says why a LAUNCH cannot act on it rather than being swept into the same word as a typo. AND `Look` ON A FIFO NO LONGER FREEZES THE EDITOR. `readFile` opened with a plain blocking `open`, so a named pipe with no writer waited forever — inside the keystroke that asked, with no frame, no message row and, in the tty shell, no Ctrl-C either, because the terminal is in raw mode. It is `O_NONBLOCK` now, the read loops answer `EAGAIN` rather than waiting, and `lseek` answering ESPIPE — a pipe, a socket, a terminal — is refused as `NotAFile`, which the boot pane spells "that is a pipe or a device, not a document". Without that last part an unwritten FIFO read as EOF and opened a silent empty pane, which says less than the hang did. The zero-size files worth streaming (procfs and its kin) seek fine and are untouched. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf