render viewport matches
parent
6c8ffb5fc1
commit
dd9ed531ad
|
|
@ -1191,6 +1191,23 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
log.warn("error searching for regex links err={}", .{err});
|
||||
};
|
||||
|
||||
// Clear our highlight state and update.
|
||||
if (self.search_matches_dirty or self.terminal_state.dirty != .false) {
|
||||
for (self.terminal_state.row_data.items(.highlights)) |*highlights| {
|
||||
highlights.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
if (self.search_matches) |m| {
|
||||
self.terminal_state.updateHighlightsFlattened(
|
||||
self.alloc,
|
||||
m.matches,
|
||||
) catch |err| {
|
||||
// Not a critical error, we just won't show highlights.
|
||||
log.warn("error updating search highlights err={}", .{err});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Build our GPU cells
|
||||
try self.rebuildCells(
|
||||
critical.preedit,
|
||||
|
|
@ -2366,6 +2383,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
const row_cells = row_data.items(.cells);
|
||||
const row_dirty = row_data.items(.dirty);
|
||||
const row_selection = row_data.items(.selection);
|
||||
const row_highlights = row_data.items(.highlights);
|
||||
|
||||
// If our cell contents buffer is shorter than the screen viewport,
|
||||
// we render the rows that fit, starting from the bottom. If instead
|
||||
|
|
@ -2381,7 +2399,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
row_cells[0..row_len],
|
||||
row_dirty[0..row_len],
|
||||
row_selection[0..row_len],
|
||||
) |y_usize, row, *cells, *dirty, selection| {
|
||||
row_highlights[0..row_len],
|
||||
) |y_usize, row, *cells, *dirty, selection, highlights| {
|
||||
const y: terminal.size.CellCountInt = @intCast(y_usize);
|
||||
|
||||
if (!rebuild) {
|
||||
|
|
@ -2526,6 +2545,15 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
|
||||
// True if this cell is selected
|
||||
const selected: bool = selected: {
|
||||
// If we're highlighted, then we're selected. In the
|
||||
// future we want to use a different style for this
|
||||
// but this to get started.
|
||||
for (highlights.items) |hl| {
|
||||
if (x >= hl[0] and x <= hl[1]) {
|
||||
break :selected true;
|
||||
}
|
||||
}
|
||||
|
||||
const sel = selection orelse break :selected false;
|
||||
const x_compare = if (wide == .spacer_tail)
|
||||
x -| 1
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
|||
const fastmem = @import("../fastmem.zig");
|
||||
const color = @import("color.zig");
|
||||
const cursor = @import("cursor.zig");
|
||||
const highlight = @import("highlight.zig");
|
||||
const point = @import("point.zig");
|
||||
const size = @import("size.zig");
|
||||
const page = @import("page.zig");
|
||||
|
|
@ -191,6 +192,10 @@ pub const RenderState = struct {
|
|||
|
||||
/// The x range of the selection within this row.
|
||||
selection: ?[2]size.CellCountInt,
|
||||
|
||||
/// The x ranges of highlights within this row. Highlights are
|
||||
/// applied after the update by calling `updateHighlights`.
|
||||
highlights: std.ArrayList([2]size.CellCountInt),
|
||||
};
|
||||
|
||||
pub const Cell = struct {
|
||||
|
|
@ -348,6 +353,7 @@ pub const RenderState = struct {
|
|||
.cells = .empty,
|
||||
.dirty = true,
|
||||
.selection = null,
|
||||
.highlights = .empty,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
|
@ -630,6 +636,63 @@ pub const RenderState = struct {
|
|||
s.dirty = .{};
|
||||
}
|
||||
|
||||
/// Update the highlights in the render state from the given flattened
|
||||
/// highlights. Because this uses flattened highlights, it does not require
|
||||
/// reading from the terminal state so it should be done outside of
|
||||
/// any critical sections.
|
||||
///
|
||||
/// This will not clear any previous highlights, so the caller must
|
||||
/// manually clear them if desired.
|
||||
pub fn updateHighlightsFlattened(
|
||||
self: *RenderState,
|
||||
alloc: Allocator,
|
||||
hls: []const highlight.Flattened,
|
||||
) Allocator.Error!void {
|
||||
// Fast path, we have no highlights!
|
||||
if (hls.len == 0) return;
|
||||
|
||||
// This is, admittedly, horrendous. This is some low hanging fruit
|
||||
// to optimize. In my defense, screens are usually small, the number
|
||||
// of highlights is usually small, and this only happens on the
|
||||
// viewport outside of a locked area. Still, I'd love to see this
|
||||
// improved someday.
|
||||
const row_data = self.row_data.slice();
|
||||
const row_arenas = row_data.items(.arena);
|
||||
const row_pins = row_data.items(.pin);
|
||||
const row_highlights_slice = row_data.items(.highlights);
|
||||
for (
|
||||
row_arenas,
|
||||
row_pins,
|
||||
row_highlights_slice,
|
||||
) |*row_arena, row_pin, *row_highlights| {
|
||||
for (hls) |hl| {
|
||||
const chunks_slice = hl.chunks.slice();
|
||||
const nodes = chunks_slice.items(.node);
|
||||
const starts = chunks_slice.items(.start);
|
||||
const ends = chunks_slice.items(.end);
|
||||
for (0.., nodes) |i, node| {
|
||||
// If this node doesn't match or we're not within
|
||||
// the row range, skip it.
|
||||
if (node != row_pin.node or
|
||||
row_pin.y < starts[i] or
|
||||
row_pin.y >= ends[i]) continue;
|
||||
|
||||
// We're a match!
|
||||
var arena = row_arena.promote(alloc);
|
||||
defer row_arena.* = arena.state;
|
||||
const arena_alloc = arena.allocator();
|
||||
try row_highlights.append(
|
||||
arena_alloc,
|
||||
.{
|
||||
if (i == 0) hl.top_x else 0,
|
||||
if (i == nodes.len - 1) hl.bot_x else self.cols - 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const StringMap = std.ArrayListUnmanaged(point.Coordinate);
|
||||
|
||||
/// Convert the current render state contents to a UTF-8 encoded
|
||||
|
|
|
|||
Loading…
Reference in New Issue