| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
workspace tag reads Dump Themes Config Debug Exit
Config (SPC f c) opens init, found as the settings report finds it
(opts.startup_config_path: $XDG_CONFIG_HOME/pardes/init or
~/.config/pardes/init, and the macOS and Windows paths config.md gives). A
pane that already holds it is gone to instead; a file on disk is looked at;
with none, an empty pane is named for it, and Save makes its directory and
any above it first (File.make_dirs, fs.makeDirs) before writing. Without a
configuration directory it says so and opens nothing.
The report Config used to open, the startup path and every live setting, is
DumpConfig, in a +DumpConfig pane. No alias keeps the old meaning.
The workspace tag's default words put Themes where NextColor was, NextColor
staying a builtin, and Config beside it. A tag the user edited is kept as it
was: a dump stores only an edited one (topbar_custom), and a restore puts
back only that.
theme.snap types NextColor onto the workspace tag to click it, and tagnav's
walk back to Dump takes five long-word steps. The rest of the re-recorded
goldens differ in the workspace words and their widths only.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |\ \ |
|
| | |/
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
rules
acme takes plan9port's own colours (acme.c iconinit, draw.h): selections
#eeee9e in the body and #9eeeee in the tag, black ink; exec and look sweeps
#aa0000 and #006600, white ink; a #99994c scrollbar under a #ffffea thumb;
black rules; a #8888cc tag/body rule and column button.
Every theme: the grip is acme's button, its tag row's full height, in one
scroll column with the scrollbar (rail_px, 12 at a 17px tagline, scaled,
times GripWidth, a percent, default 150; same x0/x1 to the pixel):
focused clean = solid box; focused dirty = box_dirty filled to a box ring
(acme's modbutton); unfocused clean = a box_border ring round the tag's
ground; unfocused dirty = box_dirty a pixel inside that ring. The grid, which
has no ring, marks dirty with a bold * on box_dirty in the grip's second cell
and fills an unfocused grip halfway to its ring. Regions carry a dirty flag.
Column buttons are the same shape, solid. 2px rules between columns and
stacked panes (inside the tag band's slack), none at the window edge; a 1px
tag/body rule halfway between tag and page.
Theme data: tag_sel_bg, sweep_bg/fg, tag_rule, rule_px, rail_px, box_border,
box_dirty. Chrome: rule_px, tag_rule, rail_px, grip_border, grip_focus_ring;
a comptime guard on Chrome's layout forces a wire bump once v8 ships.
Structural rules are exempt from the focus-indicator rule (docs/effects.md).
Tests: acme widths; stacked rule in the slack at two tagline sizes; every
grip-column pixel is band, button, ring or rule, and the button shares the
scrollbar's x0/x1 (4 metrics x top/bottom x focus x dirty); the four grip
states distinct in every native theme with a non-colour dirty cue in the
grid; wire round-trip of the new fields.
Shared files: src/colors.zig, src/surface.zig, src/body_layer.zig,
src/tagline.zig, src/detached/wire.zig, docs/themes.md, docs/config.md,
docs/effects.md, 21 snapshot goldens (the grip cells only),
test/gui-goldens.txt.
Motion flavours differ by design, and drive notice drops and pane moves
|
| |/
|
|
|
|
| |
The grip button now shows a pane's unsaved text in every state, as acme's modbutton does, and acme's tag has no asterisk. The computed prefix is the name alone, so tag reads, event offsets that count the path and the tag's text lose the " *". dirty, index's flag and the refuse-once guards are unchanged; a dump from before still restores its tags past the old mark.
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]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A message, a leader chord and a prompt used to share one row of body text at
the bottom of a pane, wearing the tagline font and nothing else about a
tagline. Now each one is a TagLayer of its own, emitted through the same
renderHeaderLayer the pane and column tags go through, so it gets the tagline
height, the small-font metrics, the band offset and the border for free --
none of which a body-grid row can have by changing its font role. The text is
right aligned. The prompt stays on the canonical grid because it owns a
cursor, and a cursor has to sit on a real cell.
The body starts BELOW the bands rather than under them, the way tree-sitter
context rows already worked. Pane.body_offset is how many rows they took and
Pardes.bodyTop(pane, rect) is the one place that answers "where does the body
begin" -- replacing fifteen copies of `if (tag_bottom) r.y else r.y + BOX_H`
spread across the paint, hit-test, scroll, PDF and image paths, which is what
let the bands and the text under them come adrift. Every notice is painted on
the grid as well, because the grid is what a terminal client draws and a band
it cannot see is a message it never gets; the GUI skips grid cells a tag layer
covers, so nothing is drawn twice.
Three bugs the bands exposed, fixed here:
- a prompt band flush with the right edge put its caret one column past the
pane, which the detached wire refuses -- so every frame was dropped for as
long as the prompt was open. The band now reserves that column.
- a click on a band mapped to Sel row 0, which is the TAG row: clicking
chrome expanded a word out of the tagline and ran it as a builtin.
- a watched file reloading under the editor changed the core without going
through update, so needs_frame was never set and the reload was never
drawn. Pardes.invalidate() is the name for that, and the file and theme
reloads call it.
A session can now drive its own 9P namespace instead of being refused one:
ownMountSuffix answers what a path names inside this editors own tree and
resolve, readLimit and write serve it from memory rather than making the
syscall that never returns. The match is anchored to whole components under
the registrys 9p/pardes/<name>, because a bare /pardes/<name> anywhere in a
string would claim ~/src/pardes/<name>/README -- and, before write learned the
same trick, write the trees bytes over the real file. readFileLimit and
writeFile refuse instead, having no core to answer from.
A toggle setting SETS when given `on` or `off` and only flips when it is bare,
so the report LocationsConfig prints can be fed back as configuration and mean
what it says.
Snapshots: 97/98, from 0/98. The goldens were several commits stale and 17
scripts had stopped running; `config <line>` is a new script command that
appends to the per-script startup config, so a script that clicks body
coordinates pins `Verbose off` instead of counting the rows an announcement
moves. nested-optout is left failing on purpose: two levels of nesting prepend
vaxis F3 codepoints to typed lines, which is a real bug and is written down in
docs/divergences.md with a repro.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
| |
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.
|
| |
|
|
| |
stops rewriting the suite
|
| |
|
|
| |
optional methods
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
config.tag_normal became a space when the mode moved into the layout box,
and 66 goldens still spelled the old bullet — the whole suite was red on
one character. Regenerated serially, and every file's diff is that
character and nothing else: replacing the bullets in the OLD goldens with
spaces is byte-identical to the new ones, so nothing else moved under
cover of the churn. The one bullet left in tutor.golden is the tutor's own
sentence about the badge, which the app still draws.
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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 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.
|
| |
|