diff --git a/src/Grid.zig b/src/Grid.zig index 6cfd49993..fb41064aa 100644 --- a/src/Grid.zig +++ b/src/Grid.zig @@ -20,6 +20,7 @@ const log = std.log.scoped(.grid); // separately for alt screens. By storing that in the key, we very likely // have the cache already for when the primary screen is reactivated. const CellsLRU = lru.AutoHashMap(struct { + selection: ?terminal.Selection, screen: terminal.Terminal.ScreenType, row_id: terminal.Screen.RowHeader.Id, }, std.ArrayListUnmanaged(GPUCell)); @@ -384,8 +385,26 @@ pub fn rebuildCells(self: *Grid, term: *Terminal) !void { while (rowIter.next()) |row| { defer y += 1; + // Our selection value is only non-null if this selection happens + // to contain this row. If the selection changes for any reason, + // then we invalidate the cache. + const selection = sel: { + if (term.selection) |sel| { + const screen_point = (terminal.point.Viewport{ + .x = 0, + .y = y, + }).toScreen(&term.screen); + + // If we are selected, we our colors are just inverted fg/bg + if (sel.containsRow(screen_point)) break :sel sel; + } + + break :sel null; + }; + // Get our value from the cache. const gop = try self.cells_lru.getOrPut(self.alloc, .{ + .selection = selection, .screen = term.active_screen, .row_id = row.getId(), }); diff --git a/src/terminal/Selection.zig b/src/terminal/Selection.zig index c37aca743..8edf90215 100644 --- a/src/terminal/Selection.zig +++ b/src/terminal/Selection.zig @@ -40,6 +40,14 @@ pub fn contains(self: Selection, p: ScreenPoint) bool { return p.y > tl.y and p.y < br.y; } +/// Returns true if the selection contains the row of the given point, +/// regardless of the x value. +pub fn containsRow(self: Selection, p: ScreenPoint) bool { + const tl = self.topLeft(); + const br = self.bottomRight(); + return p.y >= tl.y and p.y <= br.y; +} + /// Returns the top left point of the selection. pub fn topLeft(self: Selection) ScreenPoint { return switch (self.order()) { @@ -78,6 +86,9 @@ test "Selection: contains" { try testing.expect(sel.contains(.{ .x = 1, .y = 2 })); try testing.expect(!sel.contains(.{ .x = 1, .y = 1 })); try testing.expect(!sel.contains(.{ .x = 5, .y = 2 })); + try testing.expect(!sel.containsRow(.{ .x = 1, .y = 3 })); + try testing.expect(sel.containsRow(.{ .x = 1, .y = 1 })); + try testing.expect(sel.containsRow(.{ .x = 5, .y = 2 })); } // Reverse diff --git a/src/terminal/point.zig b/src/terminal/point.zig index a1c59e156..3c1023be2 100644 --- a/src/terminal/point.zig +++ b/src/terminal/point.zig @@ -76,6 +76,12 @@ pub const ScreenPoint = struct { (self.y == other.y and self.x < other.x); } + /// Returns true if this screen point is currently in the active viewport. + pub fn inViewport(self: ScreenPoint, screen: *const Screen) bool { + return self.y >= screen.viewport and + self.y < screen.viewport + screen.rows; + } + /// Converts this to a viewport point. If the point is above the /// viewport this will move the point to (0, 0) and if it is below /// the viewport it'll move it to (cols - 1, rows - 1).