| Commit message (Collapse) | Author | Age |
| |
|
|
| |
stops rewriting the suite
|
| |
|
|
| |
takes a path argument
|
| |
|
|
| |
walk wrapped rows
|
| |
|
|
| |
optional methods
|
| | |
|
| | |
|
| |
|
|
| |
docs
|
| |
|
|
| |
over grid cells
|
| |
|
|
| |
+ snapshot refresh
|
| |
|
|
| |
snapshots
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
glslc is the one build input that wants a tool a stock machine does not have,
and it is also the input that changes least often: eight GLSL files that have
outlived several rewrites of everything around them. Asking every machine that
wants to run the SDL shell for shaderc is the wrong trade.
The SPIR-V is now COMMITTED, under shaders/prebuilt/, and -Dprebuilt-shaders
embeds that copy instead of shelling out. The default stays the honest one --
compile the shaders that are actually in the tree -- because the flag trades a
dependency for a freshness problem: with it on, the .glsl sources are not build
inputs at all, so editing one changes nothing.
`zig build shaders` is the other half, and it is deliberately independent of
-Dplatform: it recompiles every shader and writes the result back into the
tracked directory, so whoever changes a shader refreshes the cache on a machine
that has the compiler and commits the diff. `jj diff shaders/prebuilt` after it
is the freshness check -- empty means the cache was already current.
The shader list is also spelled once now (gui_shaders): the eight embeds, the
eight glslc runs and the refresh step all read it, so adding a shader is a name
there plus the @embedFile in gui.zig, not three edits in two places.
Verified: -Dplatform=gui -Dprebuilt-shaders builds with glslc absent from PATH,
and image-harness passes on that binary -- real SDL GPU pipelines built from the
committed SPIR-V, 512 source pixels read back. The default gui build still runs
the eight glslc steps; tty runs none. The committed bytes are identical to a
fresh glslc run, and `zig build shaders` is idempotent.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The documentation had gone stale in the ordinary way -- claims that were true
when they were written and that nothing since had been obliged to re-read.
Some of them were load-bearing.
THE TUTOR. It still said there is no multi-cursor, that NextColor cycles three
themes, and that its practice blocks "are also run as unit tests (generated
from this file by tutor_gen)" -- a tool that appears nowhere in the tree, and
nothing anywhere parses a `# keys:` block. Left alone, that claim is what
makes the next wrong block survive.
Three of those blocks WERE wrong, and all three for one reason: since the
helix motion model landed, w/e/f/t SELECT the range they cross, so `i` after
one inserts at the SELECTION'S START. `w i Z esc` on "foo bar" gives
"Zfoo bar", not the "foo Zbar" the file promised. They were written against a
vim reading of the same keys. Every block in the file has now been run through
`zig build hxdiff` against the real core and matches byte for byte, and the
trap itself is written down in 3.3 rather than left to be rediscovered.
The tutor gains a PART 4 for everything added since it was written -- PDF
panes, the in-process ZLS backend, themes and fonts, the startup file -- and
PART 3 gains counts (and which keys ignore one), f/F/t/T, the whole g table
(bare `G` is a no-op; `ge` is the START of the last line), multiple cursors
and the s/S regex pair, `m`, `]`/`[`, `|`, insert mode, and all fifty leader
paths.
THE REST. design.typ's line table claimed 7,626 lines against a real 38,048,
and its rows did not sum to its own total; its Event/Effect boundary contract
-- the part a shell author writes against -- named four variants that do not
exist and omitted fourteen that do. lsp.md's probe count. config.md's
theme-name rules, which as written could not reach a zed theme at all.
helix-keys.md's Skipped section, holding five families that have since landed.
macos.md's menu bar, undocumented, along with sixteen other claims. web.md on
what the browser build can actually do.
SOURCE COMMENTS that had rotted alongside them: `tag_normal` is a space, not
the `•` its own comment describes; Wrap is ON by default, not off; a FontSel
row is SELECTED by n and RUN by Tab, not run by n; the SPC paths in lsp.zig
lost their `l` group prefix when the language group moved; and the
differential suites are 481 and 561 cases, not 360 and 440.
TWO THINGS FOUND BY DOCUMENTING THEM, both left standing and written down
rather than papered over. Typing `[^\n]` at an s/S prompt panics: the live
preview compiles every prefix, and `[^\` indexes an empty slice in mvzr's
parseCharSet. Both the tutor and a waiver recommended that pattern as the
workaround for `.` matching a newline; they now say what it costs and what
would make it sayable. And `Exec` is a builtin, so an `Exec` line in the
startup config types that command into a shell before the first frame -- the
tutor said nothing in that file is ever sent to one.
Nine adversarial reviews over two rounds, each with the hxdiff harness to
execute what it doubted. The second round exists because the first round's
fixes needed checking too, and it caught three regressions of my own -- one of
them a probe count I had "corrected" away from the truth.
Verified: unit-test, snap 87/87, hxdiff 481/0, hxparity 561/0, mupdf-check.
docs/design.pdf regenerated. The tutor's first seventeen lines are byte-
identical, which is what tutor.golden pins.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three changes that all turned out to be the same shape -- a feature that
worked in one direction, or for one pane kind, and quietly did not in the
others.
CLIPBOARD. Every register write emitted set_clipboard, so deleting one
character threw away whatever the desktop was holding; multi-cursor yank took
the join's early return and emitted nothing at all, so the same key reached
the clipboard on one cursor and not on two. Nothing could READ the clipboard:
the SDL shell had no SDL_GetClipboardText anywhere in it, and the tty shell
never asked for OSC 52, so `p` from another application was dead in both.
Now it is helix's split. y/d/c/p/P/R and the acme chords are the DEFAULT
REGISTER and nothing else; the system clipboard is five words on helix's own
letters -- SPC y, SPC Y, SPC p, SPC P, SPC R -- spelled as builtins so they
land in Help and are executable like every other verb. The one exception is
the tag `y` chord, which still mirrors out because a tag is always insert, so
SPC cannot be pressed there, and copying the path out is the whole point of
the chord.
Reading is a new read_clipboard effect answered by an ordinary Event.paste, so
the round trip is honest about being one: SDL and NSPasteboard answer inside
the same drain, the browser answers a promise, and a terminal answers over
OSC 52 or -- far more often -- refuses. A refused read is a paste that does
not happen, and the request dies at the next keystroke rather than landing
minutes late in whatever pane is focused by then.
The tty shell also enables BRACKETED PASTE now and coalesces
paste_start..paste_end into one event. Before this a paste arrived as a flood
of individual key presses: plausible in insert mode, and in normal mode every
pasted character ran as a command.
n/N. They stepped the armed results buffer and immediately Looked each row, so
you could not walk past a hit without opening it. They are a MOTION now:
select the next look-able text, open nothing, and let Enter decide. What they
step is the largest whitespace-delimited run look.resolve can act on
(look.lookableSpan, wrapper punctuation peeled), over a RING of panes -- every
pane that has performed a look, most recent first, then the output buffers
that have not, newest first, and only if both are empty the pane in front of
you. N is the exact inverse of n, computed rather than remembered: both
directions ask the same question about the same spans and compare against the
column the walk parks on, so x presses one way and x back land exactly where
you started, pane boundaries and the ring's seam included.
A ring rather than a list with two ends because a shell's cursor sits at the
prompt, below everything it has printed, so a walk that could not come round
would have nowhere to go on the very first press -- which is the case n/N were
written for.
One motion everywhere, no pane-kind or buffer-kind special case. The only
thing a buffer may change is the GRAIN of what a step selects, and it does it
with one flag rather than a branch: output_pane.Traits.commands (renamed from
`executes`, which named one reader's behaviour rather than the fact) makes a
row select WHOLE, because a ThemeSel line is a word to run and has no path
inside it to pick out. `]d`/`[d` are not n/N -- they are helix's diagnostic
motions, their job is to ARRIVE, and they still reach searchStep.
THE TTY PROMPT. Leaving raw tty blanked the prompt row, and the command you
had typed at that prompt shares the row, so it went too -- a shell out of tty
read as output only. OSC 133 marks the row CELL by cell, so the two are
separable: config.tty_blank = .prompt cuts the prompt's own columns and leaves
the command, left-hugged at column 0 in line with the output under it rather
than in a bay of blanks. .prompt_and_input is the old behaviour, kept.
Because the row is now something you can put a cursor in, enterTty adds the
hidden prompt width back before asking ghostty to walk the shell's own cursor
to it -- the modal column on a cut row is short by exactly that much.
Verified: unit-test 186/186 (nine new), snap 87/87 (new ttyprompt.snap),
hxdiff 481 and hxparity 561 with 0 mismatches, tty and gui both build. And
against the real binaries rather than the harness: in a pty, SPC y emits OSC
52 carrying exactly the selection while plain y emits nothing, SPC p issues
the read and pastes the reply, and a bracketed paste of "dd..." inserts text
instead of deleting two lines. In a real SDL window, SPC y then SPC p round
trips through the system clipboard while the default register holds different
text. Setting tty_blank back to .prompt_and_input reproduces all 86 old
goldens byte for byte.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Esc ran Toggleterm, which hopped between the newest DOC and the newest
TERMINAL. That distinction never earned its keep. It made Esc unpredictable —
which of three panes you landed on depended on their kinds, not on where you
had been — and it could not alternate between two files at all, which is the
case you hit most. Editing two files, Esc did nothing.
The replacement already existed. Last (SPC j j) is "the pane you were in
before this one, whichever it was": it walks the jump stack for the newest
entry naming a different pane and restores its line and column. So Esc, and
Shift-Esc in tty, now run Last, and Toggleterm is deleted rather than renamed
— a third implementation of "go to the other pane" was the thing to avoid.
SPC w t goes with it; the w group is the four directional moves, and the jump
group already had SPC j j.
Held down, Esc alternates. Two files, a file and its shell, a file and a
+Search — all the same, because Last has no notion of kind to get wrong.
This depends on the swap in the same series: Last reads the stack backwards,
and until hopping stopped appending, the pane you came from could fall off it.
windownav.snap needed only its keys and prose changed — its golden did not
move at all, which is the useful evidence here: for the one scenario the old
builtin handled well, Last produces an identical focus sequence.
Coverage for what it did not handle is new: a unit test opens a second FILE by
looking its name and asserts Esc alternates between two panes of the SAME
kind, which is the case that used to be a no-op.
Docs follow: tutor.txt, docs/helix-keys.md, docs/design.typ, and the builtin
index goldens, which are now one row shorter.
75/75 snapshots, both unit suites, and the macOS ABI build all pass.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
228 in the ring: 3 pardes ships plus all 214 helix runtime themes and all 11
zed variants across its three families. No exclusions.
inherits is what this cost. 76 themes need it, so the reader is two passes now,
and the merge is WHOLE-KEY rather than field-wise because helix merges the
theme body at depth 1 — the three *_transparent themes clear their parent's
background with an empty table, and a field-wise merge would leave it painted.
I had that wrong until I read helix-view/src/theme.rs.
At 214 files the generator meets themes that are SPARSE rather than broken, and
the old fatal-on-anything-missing would have rejected them. Every fallback is
helix's own rule: no background at all means wear the terminal's, so bg and fg
go null (13 themes, correctly); the twelve RGB fields that cannot be null fall
back to ghostty's default palette entries 7 and 0, which is exactly what a
pardes terminal already paints an unstyled ANSI index as. fatal is kept for
what genuinely cannot be read.
Three real bugs surfaced only at scale: #ccc shorthand doubles the nibble
(helix's rule, 58 colours), a quoted inline-table key silently dropped a
selection colour, and four themes name a cursor background EQUAL to the page
because they are about to reverse that cell — taking it painted an invisible
move box. 0 degenerate themes out of 225 now: no fg==bg, no syntax colour on
its own background, no invisible box.
Collisions get an unconditional rule rather than a clever one: a zed variant is
always <name>_zed, because both projects ship gruvbox, ayu and one, and a
conditional tag would move a name when the other source changes. A comptime
assert holds it.
Cost: no-op build 0.257 -> 0.276s, a pardes.zig change 46.8 -> 47.5s, binary
+0.98%. The generator does 217 sources in 54ms.
NextColor stays and is no longer a way to REACH a theme — but the browse got
better, not worse: the generated half sorts by name, so its neighbours are that
theme's own family. theme.golden's fourth click moved from ayu_dark to acid,
which sorting 225 names does; themesel gained a tail capture proving row 228
renders.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
| |
web snapshot tooling
|
| | |
|
|
|
test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options).
|