mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-28 15:55:20 +00:00
libghostty: add Selection equal and validate
This commit is contained in:
@@ -270,6 +270,53 @@ GHOSTTY_API GhosttyResult ghostty_terminal_selection_contains(
|
||||
GhosttyPoint point,
|
||||
bool* out_contains);
|
||||
|
||||
/**
|
||||
* Test whether two selection snapshots are equal.
|
||||
*
|
||||
* Equality uses the terminal's internal selection semantics: both endpoint
|
||||
* pins must match and both selections must have the same rectangular/block
|
||||
* state. This avoids requiring callers to compare raw GhosttyGridRef internals.
|
||||
*
|
||||
* Both selections' start and end grid refs must be valid untracked snapshots
|
||||
* for the given terminal's currently active screen. In practice, they must
|
||||
* come from that terminal and screen, and no mutating terminal call may have
|
||||
* occurred since the refs were produced or reconstructed from tracked refs.
|
||||
* Passing refs from another terminal, another screen, or stale refs returns
|
||||
* GHOSTTY_INVALID_VALUE.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param a First selection snapshot to compare
|
||||
* @param b Second selection snapshot to compare
|
||||
* @param[out] out_equal On success, receives whether the selections are equal
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the terminal,
|
||||
* selections, selection references, or output pointer are invalid
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_selection_equal(
|
||||
GhosttyTerminal terminal,
|
||||
const GhosttySelection* a,
|
||||
const GhosttySelection* b,
|
||||
bool* out_equal);
|
||||
|
||||
/**
|
||||
* Validate that a selection snapshot is representable for a terminal.
|
||||
*
|
||||
* A valid selection has both endpoint grid refs resolved in the terminal's
|
||||
* currently active screen/page list. Malformed refs, stale refs, refs from
|
||||
* another terminal, or refs from an inactive screen return GHOSTTY_INVALID_VALUE.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param selection Selection snapshot to validate
|
||||
* @return GHOSTTY_SUCCESS if the selection is valid for the terminal's active
|
||||
* screen/page list, otherwise GHOSTTY_INVALID_VALUE
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_selection_validate(
|
||||
GhosttyTerminal terminal,
|
||||
const GhosttySelection* selection);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -243,6 +243,8 @@ comptime {
|
||||
@export(&c.terminal_selection_order, .{ .name = "ghostty_terminal_selection_order" });
|
||||
@export(&c.terminal_selection_ordered, .{ .name = "ghostty_terminal_selection_ordered" });
|
||||
@export(&c.terminal_selection_contains, .{ .name = "ghostty_terminal_selection_contains" });
|
||||
@export(&c.terminal_selection_equal, .{ .name = "ghostty_terminal_selection_equal" });
|
||||
@export(&c.terminal_selection_validate, .{ .name = "ghostty_terminal_selection_validate" });
|
||||
@export(&c.terminal_grid_ref, .{ .name = "ghostty_terminal_grid_ref" });
|
||||
@export(&c.terminal_grid_ref_track, .{ .name = "ghostty_terminal_grid_ref_track" });
|
||||
@export(&c.terminal_point_from_grid_ref, .{ .name = "ghostty_terminal_point_from_grid_ref" });
|
||||
|
||||
@@ -174,6 +174,8 @@ pub const terminal_selection_adjust = selection.adjust;
|
||||
pub const terminal_selection_order = selection.order;
|
||||
pub const terminal_selection_ordered = selection.ordered;
|
||||
pub const terminal_selection_contains = selection.contains;
|
||||
pub const terminal_selection_equal = selection.equal;
|
||||
pub const terminal_selection_validate = selection.validate;
|
||||
pub const terminal_grid_ref = terminal.grid_ref;
|
||||
pub const terminal_grid_ref_track = terminal.grid_ref_track;
|
||||
pub const terminal_point_from_grid_ref = terminal.point_from_grid_ref;
|
||||
|
||||
@@ -110,6 +110,36 @@ pub fn contains(
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn equal(
|
||||
terminal: terminal_c.Terminal,
|
||||
a: ?*const CSelection,
|
||||
b: ?*const CSelection,
|
||||
out_equal: ?*bool,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const sel_a = (a orelse return .invalid_value).toZig() orelse
|
||||
return .invalid_value;
|
||||
const sel_b = (b orelse return .invalid_value).toZig() orelse
|
||||
return .invalid_value;
|
||||
const out = out_equal orelse return .invalid_value;
|
||||
if (!valid(t, sel_a) or !valid(t, sel_b)) return .invalid_value;
|
||||
|
||||
out.* = sel_a.eql(sel_b);
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn validate(
|
||||
terminal: terminal_c.Terminal,
|
||||
selection: ?*const CSelection,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const sel = (selection orelse return .invalid_value).toZig() orelse
|
||||
return .invalid_value;
|
||||
if (!valid(t, sel)) return .invalid_value;
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
fn valid(t: *terminal_c.ZigTerminal, sel: Selection) bool {
|
||||
const screen = t.screens.active;
|
||||
return screen.pages.pointFromPin(.screen, sel.start()) != null and
|
||||
|
||||
@@ -1552,6 +1552,97 @@ test "selection_contains" {
|
||||
try testing.expect(contains);
|
||||
}
|
||||
|
||||
test "selection_equal and selection_validate" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib.alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
var other_t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib.alloc.test_allocator,
|
||||
&other_t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(other_t);
|
||||
|
||||
vt_write(t, "Hello", 5);
|
||||
vt_write(other_t, "Hello", 5);
|
||||
|
||||
var start_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 0, .y = 0 } },
|
||||
}, &start_ref));
|
||||
|
||||
var end_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 1, .y = 0 } },
|
||||
}, &end_ref));
|
||||
|
||||
var other_end_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 2, .y = 0 } },
|
||||
}, &other_end_ref));
|
||||
|
||||
var cross_terminal_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(other_t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 1, .y = 0 } },
|
||||
}, &cross_terminal_ref));
|
||||
|
||||
const sel: selection_c.CSelection = .{
|
||||
.start = start_ref,
|
||||
.end = end_ref,
|
||||
};
|
||||
const equal_sel: selection_c.CSelection = .{
|
||||
.start = start_ref,
|
||||
.end = end_ref,
|
||||
};
|
||||
const different_endpoint: selection_c.CSelection = .{
|
||||
.start = start_ref,
|
||||
.end = other_end_ref,
|
||||
};
|
||||
const different_rectangle: selection_c.CSelection = .{
|
||||
.start = start_ref,
|
||||
.end = end_ref,
|
||||
.rectangle = true,
|
||||
};
|
||||
const cross_terminal: selection_c.CSelection = .{
|
||||
.start = start_ref,
|
||||
.end = cross_terminal_ref,
|
||||
};
|
||||
|
||||
try testing.expectEqual(Result.success, selection_c.validate(t, &sel));
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.validate(t, &cross_terminal));
|
||||
|
||||
var equal: bool = undefined;
|
||||
try testing.expectEqual(Result.success, selection_c.equal(t, &sel, &equal_sel, &equal));
|
||||
try testing.expect(equal);
|
||||
|
||||
try testing.expectEqual(Result.success, selection_c.equal(t, &sel, &different_endpoint, &equal));
|
||||
try testing.expect(!equal);
|
||||
|
||||
try testing.expectEqual(Result.success, selection_c.equal(t, &sel, &different_rectangle, &equal));
|
||||
try testing.expect(!equal);
|
||||
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.equal(t, &sel, &cross_terminal, &equal));
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.equal(t, &sel, &equal_sel, null));
|
||||
}
|
||||
|
||||
test "selection_order invalid values" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
|
||||
Reference in New Issue
Block a user