diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 18:40:41 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 9a256d34ebca317e3007adf71c2b94016059d4a3 (patch) | |
| tree | de19eed72424da8b2e43e8cd8f673e6ae71de3d5 /src/File.zig | |
| parent | 14dc0b452cf646fe5c9f060fbe66afb01f153ac0 (diff) | |
| download | pardes-9a256d34ebca317e3007adf71c2b94016059d4a3.tar.gz pardes-9a256d34ebca317e3007adf71c2b94016059d4a3.zip | |
A selection deep in a long line costs a render what one at its start does: 5 MB in, /screen goes from 14.7 s to 5 ms
Every render measured a selection's and the cursor's columns from the
start of their line (lineDisplayOffset walked the whole prefix twice),
so a selection near the end of a 5 MB line stalled each frame, 3 s at
1 MB in and 14.7 s at 5 MB. The cost grew with the offset, with Wrap on
or off. Three changes make it independent of where the selection is:
- display width adds (a tab is tab_width wherever it stands), so the
offset between two columns is the width of the text between them,
never the prefix;
- the selection's rows are clamped to what is shown: a row wholly
before the selection is skipped, a start before the row's first
column starts there, and an end past its last column ends there;
- a head or cursor whose column lies past its visible row (the last
visible row of a line owns every column after its start) is not
measured, since it is not drawn.
Measured on a Debug build, 5 MB line: offset 10, 1 MB and 5 MB now take
11, 6 and 5 ms per /screen. A new perf gate case, deep-sel, renders with
the selection 2 MB into a line: 83 us on Debug. The baselines were
re-recorded for the new harness.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/File.zig')
| -rw-r--r-- | src/File.zig | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/src/File.zig b/src/File.zig index 78c84287..a89db613 100644 --- a/src/File.zig +++ b/src/File.zig @@ -352,11 +352,18 @@ pub fn byteAtDisplayFrom(line_text: []const u8, from_raw: usize, display_col: us return from +| rawAtDisplay(line_text[from..], display_col); } +/// The display columns from `from_raw` to `to_raw` on a line. Widths add +/// (a tab is `tab_width` wherever it stands), so only the text between +/// the two is measured, never the line from its start: a selection a +/// million bytes into a line draws as fast as one at its start. pub fn lineDisplayOffset(line_text: []const u8, from_raw: usize, to_raw: usize) i32 { - const from_display = rawDisplayCol(line_text, from_raw); - const to_display = rawDisplayCol(line_text, to_raw); - if (to_display >= from_display) return @intCast(to_display - from_display); - return -@as(i32, @intCast(from_display - to_display)); + const lo = @min(from_raw, to_raw); + const hi = @max(from_raw, to_raw); + const a = modal.graphemeStart(line_text, @min(lo, line_text.len)); + const b = modal.graphemeStart(line_text, @min(hi, line_text.len)); + const past = (hi -| line_text.len) - (lo -| line_text.len); + const width: usize = displayWidth(line_text[a..b]) +| past; + return if (to_raw >= from_raw) @intCast(width) else -@as(i32, @intCast(width)); } pub fn lineDisplayEndOffset(line_text: []const u8, from_raw: usize, at_raw: usize) i32 { |
