terminal: highlights
parent
56b69ff0fd
commit
ec5bdf1a5a
|
|
@ -26,6 +26,7 @@ pub const point = terminal.point;
|
||||||
pub const color = terminal.color;
|
pub const color = terminal.color;
|
||||||
pub const device_status = terminal.device_status;
|
pub const device_status = terminal.device_status;
|
||||||
pub const formatter = terminal.formatter;
|
pub const formatter = terminal.formatter;
|
||||||
|
pub const highlight = terminal.highlight;
|
||||||
pub const kitty = terminal.kitty;
|
pub const kitty = terminal.kitty;
|
||||||
pub const modes = terminal.modes;
|
pub const modes = terminal.modes;
|
||||||
pub const page = terminal.page;
|
pub const page = terminal.page;
|
||||||
|
|
|
||||||
|
|
@ -3729,7 +3729,11 @@ pub const PageIterator = struct {
|
||||||
|
|
||||||
pub const Chunk = struct {
|
pub const Chunk = struct {
|
||||||
node: *List.Node,
|
node: *List.Node,
|
||||||
|
|
||||||
|
/// Start y index (inclusive) of this chunk in the page.
|
||||||
start: size.CellCountInt,
|
start: size.CellCountInt,
|
||||||
|
|
||||||
|
/// End y index (exclusive) of this chunk in the page.
|
||||||
end: size.CellCountInt,
|
end: size.CellCountInt,
|
||||||
|
|
||||||
pub fn rows(self: Chunk) []Row {
|
pub fn rows(self: Chunk) []Row {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
//! Highlights are any contiguous sequences of cells that should
|
||||||
|
//! be called out in some way, most commonly for text selection but
|
||||||
|
//! also search results or any other purpose.
|
||||||
|
//!
|
||||||
|
//! Within the terminal package, a highlight is a generic concept
|
||||||
|
//! that represents a range of cells.
|
||||||
|
|
||||||
|
// NOTE: The plan is for highlights to ultimately replace Selection
|
||||||
|
// completely. Selection is deeply tied to various parts of the Ghostty
|
||||||
|
// internals so this may take some time.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = @import("../quirks.zig").inlineAssert;
|
||||||
|
const size = @import("size.zig");
|
||||||
|
const PageList = @import("PageList.zig");
|
||||||
|
const PageChunk = PageList.PageIterator.Chunk;
|
||||||
|
const Pin = PageList.Pin;
|
||||||
|
const Screen = @import("Screen.zig");
|
||||||
|
|
||||||
|
/// An untracked highlight is a highlight that stores its highlighted
|
||||||
|
/// area as a top-left and bottom-right screen pin. Since it is untracked,
|
||||||
|
/// the pins are only valid for the current terminal state and may not
|
||||||
|
/// be safe to use after any terminal modifications.
|
||||||
|
///
|
||||||
|
/// For rectangle highlights/selections, the downstream consumer of this
|
||||||
|
/// code is expected to interpret the pins in whatever shape they want.
|
||||||
|
/// For example, a rectangular selection would interpret the pins as
|
||||||
|
/// setting the x bounds for each row between start.y and end.y.
|
||||||
|
///
|
||||||
|
/// To simplify all operations, start MUST be before or equal to end.
|
||||||
|
pub const Untracked = struct {
|
||||||
|
start: Pin,
|
||||||
|
end: Pin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A tracked highlight is a highlight that stores its highlighted
|
||||||
|
/// area as tracked pins within a screen.
|
||||||
|
///
|
||||||
|
/// A tracked highlight ensures that the pins remain valid even as
|
||||||
|
/// the terminal state changes. Because of this, tracked highlights
|
||||||
|
/// have more operations available to them.
|
||||||
|
///
|
||||||
|
/// There is more overhead to creating and maintaining tracked highlights.
|
||||||
|
/// If you're manipulating highlights that are untracked and you're sure
|
||||||
|
/// that the terminal state won't change, you can use the `initAssume`
|
||||||
|
/// function.
|
||||||
|
pub const Tracked = struct {
|
||||||
|
start: *Pin,
|
||||||
|
end: *Pin,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
screen: *Screen,
|
||||||
|
start: Pin,
|
||||||
|
end: Pin,
|
||||||
|
) Allocator.Error!Tracked {
|
||||||
|
const start_tracked = try screen.pages.trackPin(start);
|
||||||
|
errdefer screen.pages.untrackPin(start_tracked);
|
||||||
|
const end_tracked = try screen.pages.trackPin(end);
|
||||||
|
errdefer screen.pages.untrackPin(end_tracked);
|
||||||
|
return .{
|
||||||
|
.start = start_tracked,
|
||||||
|
.end = end_tracked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes a tracked highlight by assuming that the provided
|
||||||
|
/// pins are already tracked. This allows callers to perform tracked
|
||||||
|
/// operations without the overhead of tracking the pins, if the
|
||||||
|
/// caller can guarantee that the pins are already tracked or that
|
||||||
|
/// the terminal state will not change.
|
||||||
|
///
|
||||||
|
/// Do not call deinit on highlights created with this function.
|
||||||
|
pub fn initAssume(
|
||||||
|
start: *Pin,
|
||||||
|
end: *Pin,
|
||||||
|
) Tracked {
|
||||||
|
return .{
|
||||||
|
.start = start,
|
||||||
|
.end = end,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(
|
||||||
|
self: Tracked,
|
||||||
|
screen: *Screen,
|
||||||
|
) void {
|
||||||
|
screen.pages.untrackPin(self.start);
|
||||||
|
screen.pages.untrackPin(self.end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A flattened highlight is a highlight that stores its highlighted
|
||||||
|
/// area as a list of page chunks. This representation allows for
|
||||||
|
/// traversing the entire highlighted area without needing to read any
|
||||||
|
/// terminal state or dereference any page nodes (which may have been
|
||||||
|
/// pruned).
|
||||||
|
pub const Flattened = struct {
|
||||||
|
/// The page chunks that make up this highlight. This handles the
|
||||||
|
/// y bounds since chunks[0].start is the first highlighted row
|
||||||
|
/// and chunks[len - 1].end is the last highlighted row (exclsive).
|
||||||
|
chunks: std.MultiArrayList(PageChunk),
|
||||||
|
|
||||||
|
/// The x bounds of the highlight. `bot_x` may be less than `top_x`
|
||||||
|
/// for typical left-to-right highlights: can start the selection right
|
||||||
|
/// of the end on a higher row.
|
||||||
|
top_x: size.CellCountInt,
|
||||||
|
bot_x: size.CellCountInt,
|
||||||
|
|
||||||
|
/// Exposed for easier type references.
|
||||||
|
pub const Chunk = PageChunk;
|
||||||
|
|
||||||
|
pub const empty: Flattened = .{
|
||||||
|
.chunks = .empty,
|
||||||
|
.top_x = 0,
|
||||||
|
.bot_x = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
alloc: Allocator,
|
||||||
|
start: Pin,
|
||||||
|
end: Pin,
|
||||||
|
) Allocator.Error!Flattened {
|
||||||
|
var result: std.MultiArrayList(PageChunk) = .empty;
|
||||||
|
errdefer result.deinit(alloc);
|
||||||
|
var it = start.pageIterator(.right_down, end);
|
||||||
|
while (it.next()) |chunk| try result.append(alloc, chunk);
|
||||||
|
return .{
|
||||||
|
.chunks = result,
|
||||||
|
.top_x = start.x,
|
||||||
|
.end_x = end.x,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to an Untracked highlight.
|
||||||
|
pub fn untracked(self: Flattened) Untracked {
|
||||||
|
const slice = self.chunks.slice();
|
||||||
|
const nodes = slice.items(.node);
|
||||||
|
const starts = slice.items(.start);
|
||||||
|
const ends = slice.items(.end);
|
||||||
|
return .{
|
||||||
|
.start = .{
|
||||||
|
.node = nodes[0],
|
||||||
|
.x = self.top_x,
|
||||||
|
.y = starts[0],
|
||||||
|
},
|
||||||
|
.end = .{
|
||||||
|
.node = nodes[nodes.len - 1],
|
||||||
|
.x = self.bot_x,
|
||||||
|
.y = ends[ends.len - 1] - 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -15,6 +15,7 @@ pub const point = @import("point.zig");
|
||||||
pub const color = @import("color.zig");
|
pub const color = @import("color.zig");
|
||||||
pub const device_status = @import("device_status.zig");
|
pub const device_status = @import("device_status.zig");
|
||||||
pub const formatter = @import("formatter.zig");
|
pub const formatter = @import("formatter.zig");
|
||||||
|
pub const highlight = @import("highlight.zig");
|
||||||
pub const kitty = @import("kitty.zig");
|
pub const kitty = @import("kitty.zig");
|
||||||
pub const modes = @import("modes.zig");
|
pub const modes = @import("modes.zig");
|
||||||
pub const page = @import("page.zig");
|
pub const page = @import("page.zig");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue