aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_screen.py (follow)
Commit message (Collapse)AuthorAge
* pane: shift consumed by the character is consumed, tell the encoderuser2026-09-09
| | | | | | | | | | | | | | | 935fb7e9 taught the legacy decoder to read the shift out of shifted punctuation, so / and ? could be two keys. But pane_send_key never set the consumed mods on the re-encoded event, and libghostty's kitty path only passes text through as text when no unconsumed modifier remains: a kitty-protocol pane (nvim) received : as CSI 59;2u -- shift+semicolon, no text -- and the command line never opened. :wq! was unreachable. Mark shift consumed when the event carries text, shift is held, and the text is not the unshifted character -- the same call a real terminal makes. Text the shift produced goes through as text; chords whose shift is real (shift+up, ctrl+shift+a, back-tab) keep their escapes, and a legacy pane sees no change.
* sl0ppty: ruff format the Pythonuser2026-08-17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The bumpy one, again. Every `.py` outside `vendor/` plus the three `contrib/` scripts that are Python with no extension: 60 files, 6,714 lines rewritten, no behaviour changed. Nothing here is worth reading -- the next commit adds it to `.git-blame-ignore-revs` so `git blame` walks past it. It is a bigger reformat than the C was, 60% of the lines against 17%, and for a reason worth writing down rather than tuning away: ruff and black break a call that does not fit onto one argument per line, where this code aligned the continuation under the opening paren. The test suite is thousands of `check("some prose", cond, detail)` calls, so nearly every one of them changed shape and the suite grew 3,308 lines. Line length was the one lever that moved it -- 120 columns would have been a 40% reformat and no growth at all -- and it was left at 88, because picking an unusual width to dodge a one-time diff is paying forever to avoid paying once. Import sorting came with it. Two files had `import os, pty, uuid, select, struct, sys, termios, time, fcntl, signal` on one line and now have ten. Verified after, not before, because a formatter that alters behaviour would be worse than none: - `make retest`, 1801 checks green -- and this reformats the suite that does the checking, including `harness.py`, which every one of those files imports. - `make test-live`, the tty tests the fast suite skips. They matter here more than usual: `live_input.py` and `live_m0.py` are two of the files whose comma-imports were split, and nothing in the fast suite would have noticed if that broke them. - All 60 files still compile, `make fmt` twice in a row changes nothing the second time, and `ruff check --select I` has nothing left to say.
* sl0ppty: a status line along the bottomuser2026-08-14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | The strip along the top answers "which tab" and "how many panes" — questions about what you could switch to. Nothing answered "what am I looking at", and three of the states a pane can be in were discoverable only by being surprised by them. So the bottom line is about the focused pane and deliberately repeats nothing from the top: the session it lives in, the tab, what the pane calls itself (its purpose if it declared one, otherwise its title), and on the right, the state worth knowing — scrolled back and how far, on an alternate screen, not started yet, or exited. Right side budgeted first, the same rule the tab strip and the pane frame already use, so a long name cannot push the state off the end. The session name had to be plumbed in: the server knows it because it opened the socket under that name, and the app had no reason to until now. Reserving a row is not free and three tests noticed, which is the correct number to notice. Two were arithmetic — a chrome budget written as rows-5 and a six-row session whose pane no longer had room for a frame at all. The third was more useful: test_tiny_panes_have_no_targets asserted "no border hit that is one row tall and under four wide", which is a description of the shape a collapsed header used to have rather than of the thing being tested, and it started catching the perfectly legitimate side border of a pane that was merely short. It now says what it means — no split targets on a *header* row — and is both stricter and immune to the layout getting a row shorter again.
* sl0ptty: M3 — tabs, purposes, and the JSON control APIsl0p2026-08-13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Tabs are layout trees, one per tab, and panes in every tab keep running: fds, pumping and reaping span all of them, only the current one is composed. A one-row strip shows them (M5 makes it the real status bar) and each label registers a hit, so clicking a tab switches to it through the same mechanism as every other click. C-a c new tab C-a n / C-a p cycle C-a 1..9 select Purposes on panes and tabs with D8's trust model implemented as a lock: declared (from a layout or an operator) outranks in-band, cannot be overridden, and everything is sanitised to [A-Za-z0-9_.:/-] on ingest — 'project:foo bar/../$(rm -rf)' becomes 'project:foobar/../rm-rf'. The in-band path arrives with the OSC scanner in M4 and always passes declared:false; the refusal is already tested. The control API is now JSON in, JSON out: {"cmd":"new-tab","name":"agent","purpose":"project:x.deadbeef"} {"ok":true,"id":2} with panes/tabs/snapshot/send/raw/resize/split/focus/close/select-tab/ set-name/set-purpose/alive/quit. Panes are addressed by id, not by 'the focused one', so a background tab can be scripted. The bare-verb form stays as a harness alias and runs the same code, so the tests cannot drift from the API. Adds a small JSON reader (jsonval.c) — we could only emit until now. 129 checks green.
* sl0ptty: M1 — layout tree, splits, focus, frames, and the hit list in usesl0p2026-08-13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | app_t owns the tree, focus, the prefix and what a key means; the interactive client and the headless driver are now both thin front ends over it, so there is exactly one implementation of every command. Layout is the pure function D6 promised: layout(tree, rect) recomputed every frame, remainder spread across children so nothing drifts, and splitting in a direction that already exists grows that split rather than nesting — three splits give three equal columns, not 1/2 + 1/4 + 1/4. Chrome: rounded frames, aspect-corrected gap, centred titles. The split button is budgeted BEFORE the title and hit-tested from the rect it was drawn in, which is the fix the fork needed four attempts to land. Every painted element registers (rect, action) as it is painted; a click is a lookup. Mouse into a pane is translated to pane-local coordinates. Three bugs the harness caught, none of which unit tests would have: - one click on the split button split twice: press *and* release both fired. Chrome now acts on press; forwarding to a pane still gets every event. - callbacks were registered as &fn, but the option is pointer-typed, so the stored 'callback' was a stack address. Any OSC 2 or DSR jumped into a dead frame. Now a hard segfault turned into two regression tests. - a keystroke that changed nothing still emitted a cursor hide/show pair. The cursor is hidden lazily now, so an idle frame costs zero bytes. 96 checks green in 6s (40 unit, 23 screen, 33 layout), plus 9 live.
* sl0ptty: M0.5 — the headless driver and the harness everything rests onsl0p2026-08-13
--script runs the whole compositor with no tty: commands in (send, raw, settle, resize, snapshot, alive), composited screen out as JSON — rows of text, style runs, cursor, and the hit list. Styles are emitted as runs rather than per-cell objects, because a test wants 'columns 0..5 of row 0 are bold red', not 1920 objects. The hit list is empty until M1 paints something clickable, but the type, the JSON, and hit_test() exist now: one geometry, established before there is anything to get wrong. Also fixes two host-policy defaults found by the cursor never appearing. lib-vt is a VT library, not a terminal, so it starts with DECTCEM (mode 25) off and grapheme clustering (2027) off. Both are now set via MODE_DEFAULT, so they survive RIS. tests/harness.py + tests/test_screen.py: 19 checks over composition, styling, wide chars/clusters, scrollback, resize and the full input round trip — including the kitty/legacy re-encoding matrix, which no longer needs a pty. make test is now 2.4s for 59 checks; make test-live keeps the four that genuinely need a tty.