mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-05 15:18:40 +00:00
terminal: skip reflow pin scans for rows without pins
Reflow scanned the full tracked pin list for every source cell it copied, twice per cell in the wide-character case, even though pins are rare and at most a handful exist. Each check also went through node.page(), which can restore a compressed page just to compare pointers. reflowRow now determines once per row whether any tracked pin is on the source row and skips the per-cell pin scans entirely when there is none, which is the overwhelmingly common case. The comparisons use node identity instead of pages: a node owns exactly one page, so they are equivalent, and this avoids the restore hazard. 1.09x faster on ghostty-bench +terminal-resize --mode=cols (120x80 terminal, 10k-line scrollback, shrink/grow column reflow cycles).
This commit is contained in:
@@ -1566,12 +1566,21 @@ const ReflowCursor = struct {
|
||||
if (cols_len == 0 and src_row.semantic_prompt != .none) cols_len = 1;
|
||||
}
|
||||
|
||||
// Handle tracked pin adjustments.
|
||||
// Handle tracked pin adjustments. We also note whether any
|
||||
// tracked pin is on this row at all so that the per-cell loop
|
||||
// below can skip pin scans entirely for the overwhelmingly
|
||||
// common case of a row with no pins. Note we compare nodes
|
||||
// rather than pages since a node owns exactly one page; this
|
||||
// is cheaper and avoids `page()` restoring unrelated
|
||||
// compressed nodes purely for a comparison.
|
||||
var row_has_pins = false;
|
||||
{
|
||||
const pin_keys = list.tracked_pins.keys();
|
||||
for (pin_keys) |p| {
|
||||
if (p.node.page() != src_page or
|
||||
p.y != src_y) continue;
|
||||
if (p.node != row.node or p.y != src_y) continue;
|
||||
|
||||
// This row has pins
|
||||
row_has_pins = true;
|
||||
|
||||
if (cursor_pin != null and p == cursor_pin.?) continue;
|
||||
|
||||
@@ -1593,7 +1602,7 @@ const ReflowCursor = struct {
|
||||
// If the cursor is after blanks on the right, those cells are still
|
||||
// before the next write and must reflow with it.
|
||||
if (cursor_pin) |p| {
|
||||
if (p.node.page() == src_page and p.y == src_y) {
|
||||
if (p.node == row.node and p.y == src_y) {
|
||||
cols_len = @max(cols_len, p.x + 1);
|
||||
}
|
||||
}
|
||||
@@ -1641,10 +1650,10 @@ const ReflowCursor = struct {
|
||||
}
|
||||
|
||||
// Move any tracked pins from the source.
|
||||
{
|
||||
if (row_has_pins) {
|
||||
const pin_keys = list.tracked_pins.keys();
|
||||
for (pin_keys) |p| {
|
||||
if (p.node.page() != src_page or
|
||||
if (p.node != row.node or
|
||||
p.y != src_y or
|
||||
p.x != x) continue;
|
||||
|
||||
@@ -1667,16 +1676,15 @@ const ReflowCursor = struct {
|
||||
.skip_next => {
|
||||
// Remap any tracked pins at the skipped position (x+1)
|
||||
// since we won't process that cell in the loop.
|
||||
const pin_keys = list.tracked_pins.keys();
|
||||
for (pin_keys) |p| {
|
||||
if (p.node.page() != src_page or
|
||||
if (row_has_pins) for (list.tracked_pins.keys()) |p| {
|
||||
if (p.node != row.node or
|
||||
p.y != src_y or
|
||||
p.x != x + 1) continue;
|
||||
|
||||
p.node = self.node;
|
||||
p.x = self.x;
|
||||
p.y = self.y;
|
||||
}
|
||||
};
|
||||
|
||||
x += 2;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user