Repository navigation
fix(screen): linear searches, and a JSON reader that refuses what no termlens wrote - #613
Merged
Merged
Conversation
searchable_row asked whether the whole screen was ASCII on every row it built, a pass over every cell, so find, find_all, locate, mask_matching and the regex matches and mask were quadratic in the size of the screen: one find on 1000x1000 took 3 s in a release build where contains took 6 ms. contains was quadratic too once the first non-ASCII cell came late. The question is now asked per cell, where it is cheap because an ASCII cell is NFC already, and a needle is folded on its own, since its folding never depended on the screen. contains keeps its one whole-screen check per query. find on 1000x1000 now takes 5 ms in release and 74 ms in debug. The test times every search against the work it cannot avoid on the same screen, one pass over the grid or building the masked screen, rather than against a clock: the old code ran at 27 to 464 times that, the new at under 2, and the bound is 10. Closes #584 Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Deserialize for Screen checked the grid's shape and the cursor but took every cell as given, so a saved screen whose cell held an escape sequence loaded, and termlens render --text, --ansi and diff wrote it to the reader's terminal: an OSC set the window title, ESC [2J cleared the screen. A cell of three characters, a wide flag in the wrong place, an empty grid and mouse_modes 255 loaded too. Each cell, in the grid and in the styled history, is now held to the rules Screen::parse builds cells by, as far as they can be checked without a width table: no control character, by the text reader's own test; no printable ASCII after the first character; no empty cell marked wide, nor an ASCII one without a U+FE0F; no second column of a wide character that holds text or follows none. A screen of no columns or rows is refused. MouseModes drops the bits no mode occupies, through a hand-written Deserialize in the derive's shape. The JSON is a persisted format, so every check refuses only what no termlens wrote. vt100 0.16 and 0.18 drop control characters before the grid and append only zero-width characters to a cell, the tracker sets four mode bits, and spawn and resize refuse a grid under 2x2. What a writer did produce reads as before: a width the tables of another unicode-width release give differently (0.2.1 to 0.2.2 moved 197 code points), a keycap that U+FE0F makes two columns under vt100 0.18, and a wide character vt100 0.16's resize left in the last column. Closes #582 Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Resolves the CHANGELOG conflict with #612: both sets of entries kept. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
vyncint
added a commit
that referenced
this pull request
Oct 27, 2026
Resolves the CHANGELOG conflict with #613: both sets of entries kept. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two screen-format issues.
#584 — searches were quadratic.
searchable_rowaskedis_ascii()— a pass over every cell — for every row a search visited. Now each cell decides for itself whether it needs NFC (an ASCII cell is NFC already), the needle is folded on its own, andcontainskeeps its one whole-screen check per query. Every public entry point (find,find_all,locate,mask_matching, and withregexmatches,find_match,find_all_matches,mask_matches) goes through these.findcontains, last cell non-ASCIIThe regression test times all entry points on 200x1000 against a reference on the same screen (one pass for searches, building the masked screen for masks), best-of-N, bound 10x: old code 27–464x, new 1.1–1.6x. Stress is dispatched on this branch because the test is timing-based.
#582 — the JSON reader accepted cells the text reader refuses. A cell holding
ESC]0;PWNED BEL ESC[2Jloaded, andrender --text/diffthen drove the reader's terminal. Now refused, naming the cell, in the grid and the styled history: a control character (C0/DEL/C1, by the samecell_widthruleScreen::parsenow shares), printable ASCII after a cell's first character, an empty or ASCII-first cell marked wide without U+FE0F, and a continuation that holds text or follows no wide cell; and 0x0 screens. UnknownMouseModesbits are masked, solen()anditer()agree.The compatibility promise decided every rule: each refusal is something no release could write (verified against vt100 0.16.2 and 0.18.1, vte 0.15, and unicode-width 0.2.0–0.2.2 over every code point). Kept readable on purpose: keycaps like
1️⃣(0.18 writes them wide),⚠️in both its 0.16 and 0.18 forms, U+FFFD, cells whose width only a newer unicode-width table disputes, and a wide character 0.16 stranded in the last column after a resize. The compat corpus passes; removing each rule in turn turns exactly one test red.Not in scope (noted): the window title and the text history are not validated; an old JSON file with a stranded wide character loads but its
render --textoutput is refused byScreen::parse(pre-existing).Closes #582
Closes #584