| Commit message (Collapse) | Author | Age |
| ... | |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
placeDoc picked its source by SLOT INDEX — it scanned panes from 0 and took the
first doc it found — so which column your file landed in depended on internal
id allocation, not on where you had been. It now walks the jump stack backwards
to the newest live doc pane.
No new state: jumps already IS the record of where the keyboard has been, and
Pane.serial is already the "this slot was reused" test. The slot-order scan
stays as the fallback, deliberately: a restored dump has one jump entry, and if
that pane is a shell the stack knows no doc — without the fallback the next
file would claim a second column with docs already open.
lookpanes moved: a file now lands under the doc that was actually last read
rather than under whichever doc happened to hold the lowest pane id. New
golden doccol pins the case the line exists for, and it fails at exactly one
capture with the walk disabled.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Not a second builtin: Help already rendered "every builtin with a leader path
and the key that runs it", and the ask is "every builtin and every shortcut" —
the same listing with three more rows and one more column. A new builtin would
have been a second renderer over a superset of the same rows, and the two would
drift the first time a column moved. The prefix filter is now the degenerate
case of the index rather than a separate mode: SPC h ? reads the same array
with prefix "h", and the path-less builtins fall out on their own because an
empty path starts with nothing.
The shortcut column is folded, never typed: chordName spells a chord from Key's
OWN declarations, so a new special key names itself and an unnamed
non-printable codepoint is a compile error instead of garbage; shortcuts()
walks the cmd columns of window_keys and jump_keys, look_cmd/exec_cmd with
their keys and buttons, and topbar_str. Retarget a binding in config.zig and
this re-renders.
So Look reads `enter, right-click`, Back reads `C-o`, Kill reads `topbar`, and
Theme reads nothing at all — which is the information.
Not a picker, deliberately: n/N stepping a list holding Kill, Del and Delcol
would end the session. The names are live text, so middle-click the one you
want.
Alt-n, Alt-c, the tty toggle and the cut/paste chords cannot appear — they are
inline handlers that reach no builtin, so there is no row to put them on. Make
one a struct with a cmd column beside its binding and it appears for free.
Three goldens moved, all of them the +Help buffer's contents.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Each theme is a .zig file of pure data and nothing enumerates them by hand —
fold() walks the container's declarations, so a theme is a file and that is the
whole registration. Field by field rather than a wholesale coercion, which
makes a missing field a compile error that names it.
tools/gen_themes.zig reads helix .toml and zed .json out of vendor/themes and
emits one .zig each; build.zig reads that directory, so adding a theme is
dropping a file in. Output goes to the build cache rather than the tree, so zig
owns the freshness check and a deleted source cannot leave a stale theme
behind. Vendored, not read from the genizah: the build stays offline. A source
it cannot map is a hard error naming the file and the key, never a silently
black-on-black theme.
ThemeSel is the clever half. Traits gained an `executes` column, so n/N over
that buffer hands the whole line to Exec instead of the leading word to Look —
both arms the ordinary builtin, so a stepped row does exactly what the matching
mouse button on it would. Stepping the list previews each theme live. The trait
is a pane property, not an output-pane branch, so any pane whose lines are
commands can opt in.
Goldens: leader gains SPC t t; theme's fourth NextColor no longer wraps to
helix because the ring is fifteen long. New: themesel.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
One container, not two: focus_hist (a stack of pane ids rebuilt every sync) is
now jumps[] + a current pointer, and the +Jumps buffer is a RENDERING of that
array — nothing copies it, nothing shadows it. prevFocus, Toggleterm, Look's
directory order, Back/Forward, Last and Jumplist all walk the one list.
A pane id is reused, so a location that only remembered an id would retarget
after a respawn: panes now carry a monotonic serial and an entry whose slot
holds a different serial is dead. trackJump compacts those out and fixes the
pointer in the same pass.
The push rule lives in ONE place and says: a location is worth remembering
when you cannot see it any more — a different pane, or more than a bodyful of
rows away in the same one. So hjkl never grows the list and 100G, a search hit
and a goto-definition do.
Ctrl-o/Ctrl-i walk it, SPC j j toggles the last two, SPC j l lists them.
Ctrl-i IS Tab on a legacy host, where the binding simply never fires and
Tab-executes is untouched; kitty reports them apart. SPC j o/i work anywhere.
Two goldens moved, both the SPC ? Help listing gaining four rows.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Their only special feature is now the keys they are assigned. `Look main.zig`
typed in a tag is the same look a right click is; config.look_cmd/exec_cmd
point the two buttons and Enter/Tab at them, so retargeting Enter to Grep is
one line. actOnSelection — a hand-written cascade with the builtin dispatch
nested inside it — is gone, and the `button` parameter it threaded through five
KEYBOARD call sites went with it.
New syntax, spelled once in config.zig: @`ls -la` names a command to run
rather than a file to open. Word expansion takes the quoted run whole, the way
acme does for its own </|/> words, so a click inside one does not hand Look the
fragment `ls`.
And it nests: @`Look .` unwraps, re-enters the dispatcher, and looks at the
directory. Guarded at depth 8, which is unreachable today (every re-entry
strips a word or a delimiter pair, so the string strictly shrinks) and exists
for a future syntax that does not shrink.
leader_path is now optional: a builtin may have no SPC path when its shortcut
is a key and a mouse button. New golden cmdword; the other 59 unmoved.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
An output buffer was a file pane with a flag, and everything special about it
was re-derived from outside by asking the wrong question: the buffer NAME
("+Search", "+Help"), a parallel search_kind field, and f.output predicates
sprinkled through kind-agnostic code.
Now it carries its Origin — the Builtin, or the lsp.Kind for the five gotos
helix binds as motions rather than words — plus that command's argument, and
one Traits table answers every question the scattered code used to ask: what
it is called, whether n/N steps it, whether it is a doc for layout, whether
Esc dismisses it, whether it saves. The name is derived FROM the command
instead of being the thing that identifies it.
Adding a kind of output pane is adding a row. No golden moved.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Retargeting a key, a mouse chord or a spelling is now an edit to src/config.zig
and nothing else. Three parts in reading order: pardes's own bindings (where a
reader lands), the Look/Exec syntax, then the helix keymap under a banner
saying hxdiff/hxparity are differential suites against real helix, so a key
moved there is a divergence and not a tweak.
The bindings are data a comptime loop can walk, because the builtin index is
going to walk them.
Duplication this cut: is/isC/isA became one hit(key, chords) over ~132 call
sites, and a binding being a LIST collapses the letter-or-arrow chains;
swap_enter_tab is gone, replaced by look_key/exec_key that can point anywhere;
the four focus builtins' h/j/k/l lived hardcoded in two places and is now one
table.
No golden moved.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
their own files
The core now lies FLAT at src/ and every subdirectory is one backend, so a
file being in no directory at all is what says it is core. Pane-kind bodies
leave pardes.zig for term_pane.zig / file_pane.zig / output_pane.zig, leaving
it the layout, the event/effect machine and the generic render loop.
Builtins are one struct each in builtins.zig, and the enum is folded out of
the file's own declaration list at comptime — a zig file IS a struct, so the
list of builtins and the builtins themselves are the same text. Adding one is
writing a struct. Key paths deliberately stay one table for the config pass.
Pure refactor: no golden moved.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
THE SEAM TAKES A WRITER. `lsp.query`'s `out` is a `*std.Io.Writer`, not a
`*std.ArrayList(u8)`. The shell owns the buffer behind it (an
`Io.Writer.Allocating`), so a backend never allocates the result, never frees
it, and cannot get the allocator wrong — the invalid-free class of bug has
nowhere left to live. It also deleted a parameter from five functions: they
only ever took a `gpa` to allocate rows, and 0.16's unused-parameter error
found every one. `lsp.row()` lost its allocator argument too.
Since a Writer cannot rewind or be counted, `query` renders into a scratch
Allocating first: the log wants an exact row count, and `explain` throws the
rows away and prints narration in their place.
INTROSPECTION. `SPC l i` (Lspinfo) and `SPC l w` (Lspwhy), in the `l` group
that now holds every language command (see below).
They exist because of the seam's own contract: a backend never fails loudly,
which is right for an editor, but it makes a broken backend and a correct one
that found nothing look identical from the outside. Every query now leaves a
record — kind, file, offset, duration, row count, and THE ERROR `run` returned,
which `catch {}` swallowed and which was visible nowhere. Lspinfo prints those,
plus which ZLS is compiled in, which zig lib dir and whether it actually opens
(the usual cause of "gd does nothing in std"), and what the backend answers
versus refuses. It answers from ANY pane, including one with no file, because
it is about the backend — which matters precisely when the pane you are sitting
in is the problem; both shells now send status for a file-less pane.
Lspwhy narrates the REAL resolution path. The trace is threaded through `goto`
itself, so what it prints is the position context the analyser returned and the
branch that actually stopped. A debug view that re-derives the logic beside it
is one that can disagree with it.
CTRL-CLICK IS gd. Mouse gained a `ctrl` field, set by both shells (SDL asked
directly via GetModState rather than read off key-event bookkeeping, which a
click with no prior keypress would miss). The flag rides the drag rather than
firing on the press: a click does not place the modal cursor until RELEASE, so
a query asked at press time would answer about wherever the cursor previously
sat. A ctrl-DRAG still selects.
The snapshot DSL gained a `ctrl-` button prefix (SGR bit 4, what a terminal
sends and what vaxis decodes). test/snapshots/lspdebug.snap covers all three,
including a PLAIN click in the same spot that must NOT jump — without it the
test would pass on a bug that made every click a goto. Durations cannot live in
a golden, so PARDES_LSP_NOTIME (set by the harness, like PARDES_DUMP) omits
them.
58 snapshot scripts, hxdiff 360, hxparity 440, unit 46, gui build: all green.
lspbench: 17/17, 0 false claims.
THE WHOLE LANGUAGE GROUP LIVES UNDER SPC l.
pardes keeps its own leader letters back. `SPC d` is Del again, `SPC k` is
Kill, `SPC s d`/`SPC s r` are Dump/Restore and `SPC h t` is Tutor — exactly
where they were before the language work touched them.
The previous pass put the LSP commands on helix's bare `<space>` letters and
moved pardes's builtins out of the way (Kill k->q, Del d->wc, Dump/Restore
s?->f?, Tutor ht->T). That was the wrong trade. Those five are the most-pressed
keys in the editor and predate the language work; an LSP command is something
you reach for deliberately and can afford one keystroke more.
So every LSP command keeps HELIX'S OWN LETTER and gains the `l` prefix:
`<space>k` -> `SPC l k` (hover), `<space>d` -> `SPC l d` (diagnostics),
r/a/h/s/S/D likewise. Nothing to re-learn but the prefix, and `Lspinfo`/
`Lspwhy` were already there.
THE GOTOS ARE UNTOUCHED. `gd` `gD` `gy` `gi` `gr`, `]d`/`[d`, `]D`/`[D`, `=`
and ctrl-click all stay exactly as helix has them — they never collided with
anything, so there was never a reason to move them, and they are the ones you
actually press mid-edit.
leader.snap is restored to the pre-LSP script (its `key q` unmapped-key step
works again now that Kill is back on `k`) plus one new step for `SPC l ?`. Its
`SPC ?` root listing had to stop waiting on Restore: the full list grew to 33
rows and row 21 falls off the pane, so it watches an early row instead.
DEPENDENCY IMPORTS NOW RESOLVE. `gd` on `@import("vaxis")` opens vaxis's root
file; before, it silently did nothing while `std` worked perfectly.
The asymmetry was not a wiring mistake. ZLS's uriFromImportStr answers exactly
three ways: a relative `.zig`/`.zon` path from disk, `std` from `zig_lib_dir`
(one directory, which we supply), and EVERY OTHER NAME only by running
`zig build --build-runner` to discover the module graph. That last branch needs
`zig_exe_path`, which this backend sets to null on purpose — so every
dependency import returned `.none`. Confirmed twice over: in ZLS's source, and
by `SPC l w` on the import string, which printed the STOP line naming exactly
that branch. (The introspection builtin diagnosing its own backend on its first
real outing is a decent argument for having built it.)
We never needed a compiler for this: build.zig IS the module graph. It folds
`root_mod.import_table` into a name -> root-source-file table at configure
time and passes it as a build option; the backend consults it precisely where
ZLS gave up. Correct by construction — a dependency added or renamed in
build.zig cannot forget to update it — and it costs no subprocess, no build
step and no runtime work. `SPC l i` now lists the table, since "is this name
even importable" is the first question when a jump does nothing.
Two limits, both stated in the code: a module whose root is a GENERATED file
is skipped (it has no path until make() runs), and a file inside a dependency
importing that dependency's OWN internal module name is still a miss — that
would mean running its build.zig.
TRAP: the table is folded out of root_mod.import_table, so `addOptions` had to
move BELOW every `addImport` call. Attached where it was, the table is empty.
TOPBAR GAINS `Help`, WHICH IS WHY `SPC ?` LOOKED BROKEN.
A bare `pardes` boots straight into tty mode (main.zig: `args.len == 1`), where
every printable key belongs to the shell — so SPC never reaches the leader, and
`SPC ?`, the one thing that would tell you the leader exists, is exactly the
thing you cannot press. Ctrl-b first and it all works; nothing was broken. But
"the help is unreachable until you already know the escape hatch" is a bad
answer, and there was no mouse route either: Help was the one builtin missing
from the bar.
Row 0 is not a pane, so a middle-click there is dispatched before any pane's
mode is consulted — the word works in tty mode, which is the only reason it
earns the width. APPENDED, not inserted, so every existing topbar word keeps
its column and no golden's click coordinates move. test/snapshots/ttyhelp.snap
pins it from a bare boot: click Help, get the list, shell still TTY at its
prompt, then Ctrl-b + SPC ? for the keyboard route.
All 58 goldens carry row 0, so all 58 moved. Verified mechanically that the
only changes are the row-0 text and the row-0 style run (0-47 -> 0-52), plus:
dump/restore record the topbar inside their .zon, and tagnav's `$`+Enter now
executes `Help` rather than `Grep` because the bar's last word changed — still
exactly what that step's comment claims it tests.
THE DEPENDENCY FIX HAS A CEILING, NOW STATED. The module map is consulted from
OUR goto handler, not from inside ZLS, so the analyser still cannot type the
`vaxis` const: `gd` on `@import("vaxis")` opens the file, `gd` on `vaxis.init`
finds nothing. That is now spelled out at the top of lsp_zls.zig and on
moduleRoot rather than left implied, and `SPC l w` detects the case by name —
if the left side of a failed field access is a known dependency it says so,
instead of the generic "could not resolve". Lifting it means giving ZLS a real
BuildConfig, either by letting it run the build runner (a subprocess, and with
no cross-query cache that is once per keypress) or by synthesizing one into
BuildFile.impl. Both are real work and neither is smuggled in.
Also fixed while there: the field-access miss was only explained when ZLS
returned null, but it returns an EMPTY SLICE when it typed the left side and
found no such member. Both are "gd did nothing" from the outside; both are
explained now.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The seam's `query` now calls ZLS's analyser directly, on the worker thread,
in this process. There is no zls binary, no subprocess, no JSON-RPC, no
`initialize` handshake and no `Server` — `gd` is a function call whose answer
comes back as rows. ZLS's build.zig already publishes its guts as an
importable module (`b.modules.put("zls", ...)`), so this is a path dependency
on the local 0.16.x checkout plus one new file, `src/lsp_zls.zig`.
Construction is ZLS's own (tests/analysis_check.zig does exactly this):
InternPool.init -> DiagnosticsCollection -> DocumentStore struct literal ->
Analyser.init. `zig_exe_path` is null on purpose — shelling out to the
compiler is the thing this backend exists to avoid — and `zig_lib_dir` is
baked in from `b.graph.zig_lib_directory`, so `gd` on `std.mem.count` opens
the same mem.zig the compiler used, with ZIG_LIB_DIR overriding at runtime.
Offsets are `.@"utf-8"`, not ZLS's utf-16 default: `+Search` rows are byte
columns and we are not on a wire.
Seventeen probes, seventeen answering, no false claims, 5.9 MiB peak RSS.
The features that were already Server-free are calls (hover, document
symbols, code actions); the ones welded to `*Server` are reimplemented thin
on top of public primitives — goto is gotoHandler minus the protocol,
diagnostics is the in-process `std.zig.AstGen` branch of
getAstCheckDiagnostics, references is symbolReferences' algorithm from the
outside (offer every same-named identifier token back to the analyser and
keep the ones that resolve to the same decl, so a shadowed local is not a
false hit).
What it does not do, deliberately:
- Nothing is cached between queries. Each `query` builds a DocumentStore,
resolves imports and throws it away, because the arena dies on return and
`req.source` is a snapshot of a buffer the user is still typing into. So
cold IS warm — there is no index to warm up. It is also fast enough not to
need one: 124us for a local goto, 2.8ms into the stdlib, 8ms for
references over a 5000-line file. A cross-query cache is a real design
(a global, a mutex, an invalidation story), not a line of code, and it is
the obvious next step rather than something smuggled in here.
- References, rename and select-refs are THIS FILE only. Workspace-wide
means loading every project file into the store and running the analyser
over each; DocumentStore's own workspace iteration has the same limit
(it can only see handles already loaded). Workspace symbols and workspace
diagnostics DO walk the tree, because neither needs the analyser — a
parse and a tree walk each.
- Rename previews, format reports, code actions list. The seam hands back
rows, not edits, so there is no channel through which a backend could
rewrite the buffer. These answer the question the keypress asks and change
nothing.
- Without a zig binary, `@import("builtin")`, `@import("<pkg>")` and
`@cImport` resolve to nothing — silently, which is ZLS's behaviour, not a
bug introduced here. Relative imports and `std` work.
- Non-.zig files answer nothing. The core does not gate the keymap by file
type, so the gate is here: `gd` in a README must find nothing rather than
parse prose as Zig and confidently resolve a word out of it.
A whole-file report that ran and found nothing says so ("no diagnostics",
"already formatted", "no code actions") rather than returning zero rows,
because in this seam zero rows already means "no backend" — `lspResponse`
opens nothing for an empty answer, so silence cannot also mean "checked,
clean". Location queries keep the opposite rule: unresolvable is no rows.
test/snapshots/lsp.snap covers the round trip end to end — gd jumping on a
single result, SPC k opening +Hover, SPC s opening the +Search list that n
steps, and gd on a keyword answering nothing without opening anything. The
whole backend was also fuzzed at 20k queries over real, truncated and
byte-smashed sources across every kind; that found two crashes (a decl's
name token indexes its own file, not the requesting one, and is not
necessarily an identifier at all on a half-typed line) which are fixed.
emscripten does not get the backend: the web shell has no threads and
no-ops the lsp effect, so it keeps the empty one the base tree shipped.
DEPENDENCY: ZLS is FETCHED by the build system (build.zig.zon .url + .hash,
pinned to commit 3e0d0820 on the 0.16.x branch) rather than a path
dependency on the local genizah checkout, so it lands in zig-pkg/ like
every other dependency and the build is reproducible from the .zon alone.
Also passes -Dversion-string: ZLS's build.zig names itself by shelling out
to `git describe`, and a fetched package is an extracted tarball with no
.git, so every build printed a 'Failed to run git describe' warning. We
pin the commit, so we already know the answer.
|
| |
|
|
| |
Plus docs/lsp-evaluation.md: the three backends measured head to head.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The base every language backend plugs into. Three parts:
ASYNC. The core had no request/response shape - every effect was
fire-and-forget or instantaneous. A language query is the first thing
that answers later, so: Effect .lsp -> shell worker -> Event .lsp_resp.
tty.zig uses io.concurrent + the vaxis queue, gui.zig a detached thread
+ the mutex queue it already had for ptys; web no-ops it. The worker
never touches the core (path/source/arg are snapshotted into an LspJob),
one query in flight identified by a monotonic id so a second press makes
the first answer stale, and no rows is a legal answer.
KEYMAP. Helix's, verified against its default.rs rather than recalled.
gd/gD/gy/gi/gr and ]d/[d had no conflicts. The SPC letters did, so
pardes's own builtins moved instead of helix's: Kill k->q, Del d->wc
(closing a pane is a window op, and c is helix's own close), Dump/Restore
s?->f?, Tutor ht->T. A three-exception muscle-memory map is not a map.
RESULTS ARE +SEARCH ROWS. path:LINE:COL text, absolute. That is what
look.zig resolves and n/N step, so one row from a goto jumps and several
open a buffer - helix's multi-result picker needed no picker code.
Backends supply exactly one function (lsp.query) plus a supports set and
a name; the base has none on purpose. zig build lspbench scores them on
the same corpus: feature matrix (trusting results, not the supports
flag - a claimed-but-empty kind is reported as a false claim), cold and
warm latency, peak RSS.
Two snapshot scripts moved. leader.snap encoded the old key paths.
chordcut.snap's last two steps clicked column 5, which lands on a FILE
pane, so 'key c-b' toggled nothing and the typed text was being read as
normal-mode keys - the golden recorded no TTY pane and no cat -v output
anywhere. Pointing them at an actual shell makes both steps assert what
their comments claim, and the tty paste chord is now covered for the
first time.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
| |
selects but never edits
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
asked, never into a column of their own
placeDoc classified any file pane as a doc, so a +Search opened from a lone
shell took layoutInsertColumn(0) - a whole new leftmost column, shoving every
existing column sideways - and an open results list was then eligible as the
split parent for a real file, stacking documents under the list. An output
buffer has no file behind it and no Save; it is the result list belonging to
the pane that asked for it. Now it is that on both sides of the rule: it
splits below from_id whatever kind of pane that is (a source that died falls
back to stackDocLeft, the same last resort a full column bar uses), and it is
skipped when a real doc hunts for its split parent, so a file opened from a
search row joins the docs instead of the results.
Real docs are untouched: fsearch/rsearch/look-file/tutor/lookpanes all pass
unchanged. psearch is the proof - its results snap was two columns (the shell
pushed to x=70, its gutter run at 70-71) and is now one 140-wide column with
the +Search stacked below at the same x origin.
|
| |
|
|
| |
most-recently-focused, search only when all fail
|
| |
|
|
|
|
|
|
| |
Colors lifted from the user's own helix theme (~/.config/helix/themes/pardes.toml,
pinned against a real hx render through a pty). Theme gains box/box_dim so a
restrained theme can turn the accent down; the hardcoded gray.* overlay greys
were the dark theme's own fields spelled twice and are gone. The old default is
still one NextColor away.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The image tag spelled out `Petscii <C64|Term> Ascii` and actOnSelection
matched those words by hand before the builtin dispatch. They are three
real Builtins now (SPC t p/l/a), so the by-word matching is gone, the tag
is a plain `img <path> Del`, and with nothing live left in it the image
early-returns in curTail, enterTagEdit and the tag-click guard go too — an
image tag edits like every other pane's.
Colors and Crt leave the topbar for SPC t c / SPC t r: set-once display
switches should not spend width in a bar you read every frame. NextColor
stays, it is the one you cycle repeatedly. Both remain full builtins.
The snap scripts that clicked those words now press the leader instead, and
images.snap finally exercises the palette and ascii toggles for real: its
old clicks at x=57 landed on the neighbouring shell's tag, so that toggle
had never been covered. The test PPM grew to 512x512 with LCG noise so the
ASCII bitmaps actually win cells and dropping them visibly changes the art.
|
| |
|
|
| |
painting over the UI
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Left/Down/Up/Right run the existing focusDir (now taking the pane the
builtin ran on, so an executed name acts on ITS pane); the Ctrl-w prefix
routes through the same four builtins instead of calling focusDir itself.
Toggleterm hops between the most recently focused document and terminal
off focus_hist, the MRU the core already rebuilds each sync.
Ctrl-w stays: a tty-mode pane never sees SPC, so it is the only keyboard
way out of one. Its snapshot step now moves focus somewhere (c-w l), which
the old c-w h in that layout did not.
|
| |
|
|
| |
Save moves w -> f s to free the w group for window navigation.
|
| | |
|
| | |
|
| | |
|
| |\ |
|
| | | |
|
| |/
|
|
| |
web snapshot tooling
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
| |
the next left click instead of stretching to it (explicit v/x selections keep click-extend), and in a tty-mode shell 1-3 now pastes into the pty like a terminal emulator — the click is forwarded first (SGR or X10, only when the app listens for mouse) so mouse-aware programs paste at the click point, then the register arrives bracketed (mode 2004) or with \n->\r when unbracketed; 1-2 stays inert there. chordcut.snap pins both (a cat -v probe shows the exact wire bytes); tutor updated.
|
| |
|
|
| |
(file:LINE works) opens it focused in the left column with a greeting terminal on the launch dir at right, a DIRECTORY arg chdirs so shells spawn there. New argv.snap; boot/restore.snap pin -n 1 to keep the classic boot.
|
| |
|
|
| |
the new pane under 2 body rows, splitParent swaps in a qualifying pane (same column first), else the tallest pane anywhere; applied to Alt-n, look-dir, placeDoc and ttyForDir. New splitparent.snap.
|
| |
|
|
| |
BOX_H (the tag row doubles as the re-enlarge handle; the gutter move box wins over the handle so a tag-only pane stays movable), a hidden body's pty/vt is never resized so re-enlarging restores it intact, and cursor/modal-selection paints never land on the tag row. New shrinktag.snap; tutor updated.
|
| |
|
|
| |
middle-drag pane has no selection of its own (kept left sel, else explicit v/x n/N), the active pane is checked first (it holds the most recently made selection), then slot order. New selarg.snap covers borrowing a kept left selection and a v selection from another pane; tutor updated.
|
| |
|
|
| |
looked word (and its cwd-join) is matched against loaded file/image pane paths first, honoring :NN recentering — so the fs-less web build can focus dump-loaded panes on look, and native dedup no longer needs the file on disk. New lookloaded.snap: look at a path after rm'ing it still focuses/recenters the loaded pane.
|
| |
|
|
| |
blocked in every thread and consumed on a dedicated sigwait thread instead of vaxis's signal handler, which locked std.Io mutexes from signal context and panicked the Io.Threaded machinery; pane shells unblock it after fork so bash/vim still see resizes. winch.snap storms WINCH from a pane (crashed pre-fix) and checks a real resize still reflows.
|
| |
|
|
| |
(both in one hold = Snarf/copy; the chorded left release is inert), plus 2-3 / 3-1 / 3-2 cancel an execute or look drag mid-gesture (the 2-1 argument chord already existed). chordcut.snap covers cut/copy/paste and both cancels; tutor documents the chords.
|
| |
|
|
| |
scrolled view even when the pane isn't active (the old gate only protected the active pane; tag edits use the remembered body mode). New ttyfollow.snap: background job streams into a scrolled-up normal-mode pane without moving it, tty mode follows again.
|
| |
|
|
| |
longer force-drops to normal (it only restores the mode a tag edit hijacked, remembered in pane.tag_mode), and the left-release cursor pin keeps the terminal's mode (files still click into normal). New ttyclick.snap covers tty body click, tty tag round trip, insert body click.
|
| | |
|
| |
|
|
| |
(rust/cpp/python/...; e.g. opening agave cpi.rs or any tracy .cpp), Debug AND ReleaseSafe. Root cause: clang -fsanitize=function (in zig's default C UBSan set) traps at the runtime's indirect call of the scanner because grammars declare external_scanner_create() with EMPTY PARENS — a K&R non-prototype whose type hash differs from the void*(*)(void) pointer type. The ud1 trap lands on a bogus inlined line (stack.c:746), which cost the diagnosis a detour through rr (its gdbserver dies replaying past the task exit — core dump + coredumpctl worked; ud1 0x6(%eax) = SanitizerHandler kind 6 = function_type_mismatch; scanner-less c/zig grammars never crashed). Fix per review direction: -fno-sanitize=function on the grammar TUs in build.zig — uninstrumented callees make the runtime's call-site checks skip; the rest of UBSan stays live. Second half: fatal signals (SIGILL/SEGV/BUS/FPE) never run defers and bypassed the panic hook, leaving the terminal raw after a crash — root.debug.handleSegfault override now runs vaxis.recover() before std.debug.defaultHandleSegfault, verified in a raw pty (kill -ILL $PPID: rmcup + mouse resets precede the trace). Verified: rust/cpp/python opens work with real highlighting (snapstyle: keywords/strings/comments colored), agave cpi.js 2.7k-line open fine, suite 30/30, ReleaseSafe build opens rust identically.
|
| |
|
|
| |
mode. Surface.cursor grew a bar flag: the core sets it from the active pane mode (insert -> bar; normal/tty -> block) for both the modal/file cursor and the vt-tracking terminal cursor, and unconditionally for the tag cursor. tty shell: DECSCUSR via vaxis setCursorShape (beam for insert, the terminal default otherwise). gui shell: the insert cursor draws the left-eighth-block glyph in cell colors instead of the reverse-video block (ponytail: it hides the char under it; a dedicated quad if that ever matters). Tag editing: enterTagEdit goes straight to insert, Esc (or anything that exits the tag) restores the body normal mode; Enter looks / Tab executes the word at the cursor from inside the tag (tagChordText kept); the whole normal-mode tag machinery — handleTagKey motions, v/x selection, d/c/y/p, tagEnterInsert/tagDelete/tagYank/tagPaste — deleted as unreachable (~120 lines; multi-word tag execution stays the mouse middle-drag). tag.snap rewritten: INS on click, type/backspace straight in, Esc exit with the text persisting, Enter-look opens the file. Tutor documents it. Cursor SHAPE is not capturable by the harness (position only) — verify by eye: bar in insert/tags, block elsewhere, both shells. 30/30.
|
| |
|
|
| |
scroll): pane.hscroll cuts content columns after the lineno gutter in bodyText (post edit-splice, so typed runs shift with the text), no scrollbar. Drivers: horizontal wheel (new wheel_left/right Mouse.Buttons, SGR 66/67 via vaxis on the tty, SDL wheel.x on the gui, ±4 cols, clamped at 0, right overscroll allowed) and keyboard movement — ensureCursorVisible grew the horizontal mirror of the scrolloff reconcile, so $ pulls the view right with 3 columns of margin and 0 reels it home; the wheel never moves the cursor. Shifted alongside: syntax recolor pass, modal cursor paint (hidden when off-view), vsel highlight, click->content-col mapping, cursorWordSel. Byte columns like the rest of the file-pane code. New hscroll.snap golden: wheel shift with fixed gutter, left clamp, $ margin follow, 0 home. 30/30.
|