mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-25 06:18:37 +00:00
libghostty: selection word/line/output/all helpers
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <ghostty/vt/grid_ref.h>
|
||||
#include <ghostty/vt/point.h>
|
||||
|
||||
@@ -78,6 +79,57 @@ typedef struct {
|
||||
bool rectangle;
|
||||
} GhosttySelection;
|
||||
|
||||
/**
|
||||
* Options for deriving a word selection from a terminal grid reference.
|
||||
*
|
||||
* This is a sized struct. Use GHOSTTY_INIT_SIZED() to initialize it.
|
||||
* If boundary_codepoints is NULL and boundary_codepoints_len is 0, Ghostty's
|
||||
* default word-boundary codepoints are used. If boundary_codepoints_len is
|
||||
* non-zero, boundary_codepoints must not be NULL.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
typedef struct {
|
||||
/** Size of this struct in bytes. Must be set to sizeof(GhosttyTerminalSelectWordOptions). */
|
||||
size_t size;
|
||||
|
||||
/** Grid reference under which to derive the word selection. */
|
||||
GhosttyGridRef ref;
|
||||
|
||||
/** Optional word-boundary codepoints as uint32_t scalar values. */
|
||||
const uint32_t* boundary_codepoints;
|
||||
|
||||
/** Number of entries in boundary_codepoints. */
|
||||
size_t boundary_codepoints_len;
|
||||
} GhosttyTerminalSelectWordOptions;
|
||||
|
||||
/**
|
||||
* Options for deriving a line selection from a terminal grid reference.
|
||||
*
|
||||
* This is a sized struct. Use GHOSTTY_INIT_SIZED() to initialize it.
|
||||
* If whitespace is NULL and whitespace_len is 0, Ghostty's default line-trim
|
||||
* whitespace codepoints are used. If whitespace_len is non-zero, whitespace
|
||||
* must not be NULL.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
typedef struct {
|
||||
/** Size of this struct in bytes. Must be set to sizeof(GhosttyTerminalSelectLineOptions). */
|
||||
size_t size;
|
||||
|
||||
/** Grid reference under which to derive the line selection. */
|
||||
GhosttyGridRef ref;
|
||||
|
||||
/** Optional codepoints to trim from the start and end of the line. */
|
||||
const uint32_t* whitespace;
|
||||
|
||||
/** Number of entries in whitespace. */
|
||||
size_t whitespace_len;
|
||||
|
||||
/** Whether semantic prompt state changes should bound the line selection. */
|
||||
bool semantic_prompt_boundary;
|
||||
} GhosttyTerminalSelectLineOptions;
|
||||
|
||||
/**
|
||||
* Ordering of a selection's endpoints in terminal coordinates.
|
||||
*
|
||||
@@ -158,6 +210,86 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
GHOSTTY_SELECTION_ADJUST_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttySelectionAdjust;
|
||||
|
||||
/**
|
||||
* Derive a word selection snapshot from a terminal grid reference.
|
||||
*
|
||||
* The returned selection is not installed as the terminal's current
|
||||
* selection. It is a snapshot with the same lifetime rules as GhosttySelection.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param options Word-selection options
|
||||
* @param[out] out_selection On success, receives the derived selection
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the valid ref has
|
||||
* no selectable word content, or GHOSTTY_INVALID_VALUE if the
|
||||
* terminal, options, ref, codepoint pointer, or output pointer are
|
||||
* invalid.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_select_word(
|
||||
GhosttyTerminal terminal,
|
||||
const GhosttyTerminalSelectWordOptions* options,
|
||||
GhosttySelection* out_selection);
|
||||
|
||||
/**
|
||||
* Derive a line selection snapshot from a terminal grid reference.
|
||||
*
|
||||
* The returned selection is not installed as the terminal's current
|
||||
* selection. It is a snapshot with the same lifetime rules as GhosttySelection.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param options Line-selection options
|
||||
* @param[out] out_selection On success, receives the derived selection
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the valid ref has
|
||||
* no selectable line content, or GHOSTTY_INVALID_VALUE if the
|
||||
* terminal, options, ref, codepoint pointer, or output pointer are
|
||||
* invalid.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_select_line(
|
||||
GhosttyTerminal terminal,
|
||||
const GhosttyTerminalSelectLineOptions* options,
|
||||
GhosttySelection* out_selection);
|
||||
|
||||
/**
|
||||
* Derive a selection snapshot covering all selectable terminal content.
|
||||
*
|
||||
* The returned selection is not installed as the terminal's current
|
||||
* selection. It is a snapshot with the same lifetime rules as GhosttySelection.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param[out] out_selection On success, receives the derived selection
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if there is no
|
||||
* selectable content, or GHOSTTY_INVALID_VALUE if the terminal or
|
||||
* output pointer is invalid.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_select_all(
|
||||
GhosttyTerminal terminal,
|
||||
GhosttySelection* out_selection);
|
||||
|
||||
/**
|
||||
* Derive a command-output selection snapshot from a terminal grid reference.
|
||||
*
|
||||
* The returned selection is not installed as the terminal's current
|
||||
* selection. It is a snapshot with the same lifetime rules as GhosttySelection.
|
||||
*
|
||||
* @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param ref Grid reference within command output to select
|
||||
* @param[out] out_selection On success, receives the derived selection
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the valid ref is
|
||||
* not selectable command output, or GHOSTTY_INVALID_VALUE if the
|
||||
* terminal, ref, or output pointer is invalid.
|
||||
*
|
||||
* @ingroup selection
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_select_output(
|
||||
GhosttyTerminal terminal,
|
||||
GhosttyGridRef ref,
|
||||
GhosttySelection* out_selection);
|
||||
|
||||
/**
|
||||
* Adjust a selection snapshot using terminal selection semantics.
|
||||
*
|
||||
|
||||
@@ -49,6 +49,7 @@ const string = @import("string.zig");
|
||||
const terminal = struct {
|
||||
const CursorStyle = @import("../terminal/cursor.zig").Style;
|
||||
const color = @import("../terminal/color.zig");
|
||||
const selection_codepoints = @import("../terminal/selection_codepoints.zig");
|
||||
const style = @import("../terminal/style.zig");
|
||||
const x11_color = @import("../terminal/x11_color.zig");
|
||||
};
|
||||
@@ -6149,32 +6150,8 @@ pub const RepeatableString = struct {
|
||||
pub const SelectionWordChars = struct {
|
||||
const Self = @This();
|
||||
|
||||
/// Default boundary characters: ` \t'"│`|:;,()[]{}<>$`
|
||||
const default_codepoints = [_]u21{
|
||||
0, // null
|
||||
' ', // space
|
||||
'\t', // tab
|
||||
'\'', // single quote
|
||||
'"', // double quote
|
||||
'│', // U+2502 box drawing
|
||||
'`', // backtick
|
||||
'|', // pipe
|
||||
':', // colon
|
||||
';', // semicolon
|
||||
',', // comma
|
||||
'(', // left paren
|
||||
')', // right paren
|
||||
'[', // left bracket
|
||||
']', // right bracket
|
||||
'{', // left brace
|
||||
'}', // right brace
|
||||
'<', // less than
|
||||
'>', // greater than
|
||||
'$', // dollar
|
||||
};
|
||||
|
||||
/// The parsed codepoints. Always includes null (U+0000) at index 0.
|
||||
codepoints: []const u21 = &default_codepoints,
|
||||
codepoints: []const u21 = &terminal.selection_codepoints.default_word_boundaries,
|
||||
|
||||
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
||||
const value = input orelse return error.ValueRequired;
|
||||
|
||||
@@ -239,6 +239,10 @@ comptime {
|
||||
@export(&c.terminal_mode_set, .{ .name = "ghostty_terminal_mode_set" });
|
||||
@export(&c.terminal_get, .{ .name = "ghostty_terminal_get" });
|
||||
@export(&c.terminal_get_multi, .{ .name = "ghostty_terminal_get_multi" });
|
||||
@export(&c.terminal_select_word, .{ .name = "ghostty_terminal_select_word" });
|
||||
@export(&c.terminal_select_line, .{ .name = "ghostty_terminal_select_line" });
|
||||
@export(&c.terminal_select_all, .{ .name = "ghostty_terminal_select_all" });
|
||||
@export(&c.terminal_select_output, .{ .name = "ghostty_terminal_select_output" });
|
||||
@export(&c.terminal_selection_adjust, .{ .name = "ghostty_terminal_selection_adjust" });
|
||||
@export(&c.terminal_selection_order, .{ .name = "ghostty_terminal_selection_order" });
|
||||
@export(&c.terminal_selection_ordered, .{ .name = "ghostty_terminal_selection_ordered" });
|
||||
|
||||
@@ -13,6 +13,7 @@ const tripwire = @import("../tripwire.zig");
|
||||
const unicode = @import("../unicode/main.zig");
|
||||
const Selection = @import("Selection.zig");
|
||||
const PageList = @import("PageList.zig");
|
||||
const selection_codepoints = @import("selection_codepoints.zig");
|
||||
const StringMap = @import("StringMap.zig");
|
||||
const ScreenFormatter = @import("formatter.zig").ScreenFormatter;
|
||||
const osc = @import("osc.zig");
|
||||
@@ -2516,7 +2517,7 @@ pub const SelectLine = struct {
|
||||
|
||||
/// These are the codepoints to consider whitespace to trim
|
||||
/// from the ends of the selection.
|
||||
whitespace: ?[]const u21 = &.{ 0, ' ', '\t' },
|
||||
whitespace: ?[]const u21 = &selection_codepoints.default_line_whitespace,
|
||||
|
||||
/// If true, line selection will consider semantic prompt
|
||||
/// state changing a boundary. State changing is ANY state
|
||||
@@ -2652,10 +2653,10 @@ pub fn selectLine(self: *const Screen, opts: SelectLine) ?Selection {
|
||||
if (!cell.hasText()) continue;
|
||||
|
||||
// Non-empty means we found it.
|
||||
const this_whitespace = std.mem.indexOfAny(
|
||||
const this_whitespace = std.mem.indexOfScalar(
|
||||
u21,
|
||||
whitespace,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
cell.content.codepoint,
|
||||
) != null;
|
||||
if (this_whitespace) continue;
|
||||
|
||||
@@ -2674,10 +2675,10 @@ pub fn selectLine(self: *const Screen, opts: SelectLine) ?Selection {
|
||||
if (!cell.hasText()) continue;
|
||||
|
||||
// Non-empty means we found it.
|
||||
const this_whitespace = std.mem.indexOfAny(
|
||||
const this_whitespace = std.mem.indexOfScalar(
|
||||
u21,
|
||||
whitespace,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
cell.content.codepoint,
|
||||
) != null;
|
||||
if (this_whitespace) continue;
|
||||
|
||||
@@ -2798,10 +2799,10 @@ pub fn selectWord(
|
||||
if (!start_cell.hasText()) return null;
|
||||
|
||||
// Determine if we are a boundary or not to determine what our boundary is.
|
||||
const expect_boundary = std.mem.indexOfAny(
|
||||
const expect_boundary = std.mem.indexOfScalar(
|
||||
u21,
|
||||
boundary_codepoints,
|
||||
&[_]u21{start_cell.content.codepoint},
|
||||
start_cell.content.codepoint,
|
||||
) != null;
|
||||
|
||||
// Go forwards to find our end boundary
|
||||
@@ -2816,10 +2817,10 @@ pub fn selectWord(
|
||||
if (!cell.hasText()) break :end prev;
|
||||
|
||||
// If we do not match our expected set, we hit a boundary
|
||||
const this_boundary = std.mem.indexOfAny(
|
||||
const this_boundary = std.mem.indexOfScalar(
|
||||
u21,
|
||||
boundary_codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
cell.content.codepoint,
|
||||
) != null;
|
||||
if (this_boundary != expect_boundary) break :end prev;
|
||||
|
||||
@@ -2853,10 +2854,10 @@ pub fn selectWord(
|
||||
if (!cell.hasText()) break :start prev;
|
||||
|
||||
// If we do not match our expected set, we hit a boundary
|
||||
const this_boundary = std.mem.indexOfAny(
|
||||
const this_boundary = std.mem.indexOfScalar(
|
||||
u21,
|
||||
boundary_codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
cell.content.codepoint,
|
||||
) != null;
|
||||
if (this_boundary != expect_boundary) break :start prev;
|
||||
|
||||
|
||||
@@ -170,6 +170,10 @@ pub const terminal_mode_get = terminal.mode_get;
|
||||
pub const terminal_mode_set = terminal.mode_set;
|
||||
pub const terminal_get = terminal.get;
|
||||
pub const terminal_get_multi = terminal.get_multi;
|
||||
pub const terminal_select_word = selection.word;
|
||||
pub const terminal_select_line = selection.line;
|
||||
pub const terminal_select_all = selection.all;
|
||||
pub const terminal_select_output = selection.output;
|
||||
pub const terminal_selection_adjust = selection.adjust;
|
||||
pub const terminal_selection_order = selection.order;
|
||||
pub const terminal_selection_ordered = selection.ordered;
|
||||
|
||||
@@ -3,6 +3,7 @@ const testing = std.testing;
|
||||
const lib = @import("../lib.zig");
|
||||
const grid_ref = @import("grid_ref.zig");
|
||||
const point = @import("../point.zig");
|
||||
const selection_codepoints = @import("../selection_codepoints.zig");
|
||||
const Selection = @import("../Selection.zig");
|
||||
const Result = @import("result.zig").Result;
|
||||
const terminal_c = @import("terminal.zig");
|
||||
@@ -34,6 +35,130 @@ pub const CSelection = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// C: GhosttyTerminalSelectWordOptions
|
||||
pub const SelectWordOptions = extern struct {
|
||||
size: usize = @sizeOf(SelectWordOptions),
|
||||
ref: grid_ref.CGridRef,
|
||||
boundary_codepoints: ?[*]const u32 = null,
|
||||
boundary_codepoints_len: usize = 0,
|
||||
};
|
||||
|
||||
/// C: GhosttyTerminalSelectLineOptions
|
||||
pub const SelectLineOptions = extern struct {
|
||||
size: usize = @sizeOf(SelectLineOptions),
|
||||
ref: grid_ref.CGridRef,
|
||||
whitespace: ?[*]const u32 = null,
|
||||
whitespace_len: usize = 0,
|
||||
semantic_prompt_boundary: bool = false,
|
||||
};
|
||||
|
||||
pub fn word(
|
||||
terminal: terminal_c.Terminal,
|
||||
options: ?*const SelectWordOptions,
|
||||
out_selection: ?*CSelection,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const opts = options orelse return .invalid_value;
|
||||
if (opts.size < @sizeOf(SelectWordOptions)) return .invalid_value;
|
||||
const out = out_selection orelse return .invalid_value;
|
||||
|
||||
const boundary_codepoints = codepointSlice(
|
||||
opts.boundary_codepoints,
|
||||
opts.boundary_codepoints_len,
|
||||
) catch return .invalid_value;
|
||||
|
||||
const screen = t.screens.active;
|
||||
const pin = opts.ref.toPin() orelse return .invalid_value;
|
||||
out.* = .fromZig(screen.selectWord(
|
||||
pin,
|
||||
boundary_codepoints orelse &selection_codepoints.default_word_boundaries,
|
||||
) orelse
|
||||
return .no_value);
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn line(
|
||||
terminal: terminal_c.Terminal,
|
||||
options: ?*const SelectLineOptions,
|
||||
out_selection: ?*CSelection,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const opts = options orelse return .invalid_value;
|
||||
if (opts.size < @sizeOf(SelectLineOptions)) return .invalid_value;
|
||||
const out = out_selection orelse return .invalid_value;
|
||||
|
||||
const whitespace = codepointSlice(
|
||||
opts.whitespace,
|
||||
opts.whitespace_len,
|
||||
) catch return .invalid_value;
|
||||
|
||||
const screen = t.screens.active;
|
||||
const pin = opts.ref.toPin() orelse return .invalid_value;
|
||||
out.* = .fromZig(screen.selectLine(.{
|
||||
.pin = pin,
|
||||
.whitespace = whitespace orelse &selection_codepoints.default_line_whitespace,
|
||||
.semantic_prompt_boundary = opts.semantic_prompt_boundary,
|
||||
}) orelse return .no_value);
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn all(
|
||||
terminal: terminal_c.Terminal,
|
||||
out_selection: ?*CSelection,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const out = out_selection orelse return .invalid_value;
|
||||
|
||||
out.* = .fromZig(t.screens.active.selectAll() orelse return .no_value);
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn output(
|
||||
terminal: terminal_c.Terminal,
|
||||
ref: grid_ref.CGridRef,
|
||||
out_selection: ?*CSelection,
|
||||
) callconv(lib.calling_conv) Result {
|
||||
const t = terminal_c.zigTerminal(terminal) orelse return .invalid_value;
|
||||
const out = out_selection orelse return .invalid_value;
|
||||
|
||||
const screen = t.screens.active;
|
||||
const pin = ref.toPin() orelse return .invalid_value;
|
||||
out.* = .fromZig(screen.selectOutput(pin) orelse return .no_value);
|
||||
return .success;
|
||||
}
|
||||
|
||||
/// Return the borrowed C array of `uint32_t` codepoints as a `[]const u21`.
|
||||
///
|
||||
/// `NULL + len 0` returns null, which callers treat as “use the API default
|
||||
/// set.” A non-null pointer with `len 0` returns an empty slice, meaning “use an
|
||||
/// explicitly empty set.” A non-zero length requires a non-null pointer.
|
||||
///
|
||||
/// This is intentionally zero-copy. In the C ABI, codepoints are `uint32_t`,
|
||||
/// but selection internals use Zig's `u21` to represent valid Unicode scalar
|
||||
/// values. Zig currently stores `u21` in the same size and alignment as `u32`,
|
||||
/// so we assert that layout relationship and reinterpret the borrowed slice.
|
||||
/// If Zig ever changes that representation, these comptime assertions fail
|
||||
/// loudly rather than silently making this cast wrong.
|
||||
fn codepointSlice(
|
||||
ptr: ?[*]const u32,
|
||||
len: usize,
|
||||
) error{InvalidValue}!?[]const u21 {
|
||||
comptime {
|
||||
std.debug.assert(@sizeOf(u21) == @sizeOf(u32));
|
||||
std.debug.assert(@alignOf(u21) == @alignOf(u32));
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
const p = ptr orelse return null;
|
||||
_ = p;
|
||||
return &.{};
|
||||
}
|
||||
|
||||
const p = ptr orelse return error.InvalidValue;
|
||||
const cps: [*]const u21 = @ptrCast(p);
|
||||
return cps[0..len];
|
||||
}
|
||||
|
||||
pub fn adjust(
|
||||
terminal: terminal_c.Terminal,
|
||||
selection: ?*CSelection,
|
||||
|
||||
@@ -1393,6 +1393,72 @@ test "set and get selection" {
|
||||
try testing.expectEqual(Result.no_value, get(t, .selection, @ptrCast(&out)));
|
||||
}
|
||||
|
||||
test "selection derivation helpers" {
|
||||
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);
|
||||
|
||||
vt_write(t, " Hello \r\nWorld", 16);
|
||||
|
||||
var out: selection_c.CSelection = undefined;
|
||||
|
||||
var word_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 3, .y = 0 } },
|
||||
}, &word_ref));
|
||||
|
||||
var empty_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 20, .y = 0 } },
|
||||
}, &empty_ref));
|
||||
|
||||
var line_ref: grid_ref_c.CGridRef = .{};
|
||||
try testing.expectEqual(Result.success, grid_ref(t, .{
|
||||
.tag = .active,
|
||||
.value = .{ .active = .{ .x = 0, .y = 0 } },
|
||||
}, &line_ref));
|
||||
|
||||
var word_opts: selection_c.SelectWordOptions = .{
|
||||
.ref = word_ref,
|
||||
};
|
||||
try testing.expectEqual(Result.success, selection_c.word(t, &word_opts, &out));
|
||||
try testing.expectEqual(@as(u16, 2), out.start.toPin().?.x);
|
||||
try testing.expectEqual(@as(u16, 6), out.end.toPin().?.x);
|
||||
|
||||
word_opts.ref = empty_ref;
|
||||
try testing.expectEqual(Result.no_value, selection_c.word(t, &word_opts, &out));
|
||||
|
||||
var line_opts: selection_c.SelectLineOptions = .{
|
||||
.ref = line_ref,
|
||||
};
|
||||
try testing.expectEqual(Result.success, selection_c.line(t, &line_opts, &out));
|
||||
try testing.expectEqual(@as(u16, 2), out.start.toPin().?.x);
|
||||
try testing.expectEqual(@as(u16, 6), out.end.toPin().?.x);
|
||||
|
||||
try testing.expectEqual(Result.success, selection_c.all(t, &out));
|
||||
try testing.expectEqual(@as(u16, 2), out.start.toPin().?.x);
|
||||
try testing.expectEqual(@as(u16, 0), out.start.toPin().?.y);
|
||||
try testing.expectEqual(@as(u16, 4), out.end.toPin().?.x);
|
||||
try testing.expectEqual(@as(u16, 1), out.end.toPin().?.y);
|
||||
|
||||
try testing.expectEqual(Result.no_value, selection_c.output(t, line_ref, &out));
|
||||
|
||||
line_opts.size = @sizeOf(usize) - 1;
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.line(t, &line_opts, &out));
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.word(t, null, &out));
|
||||
try testing.expectEqual(Result.invalid_value, selection_c.word(t, &word_opts, null));
|
||||
}
|
||||
|
||||
test "selection_adjust mutates snapshot end" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
|
||||
@@ -20,30 +20,35 @@ const mouse_encode = @import("mouse_encode.zig");
|
||||
const grid_ref = @import("grid_ref.zig");
|
||||
|
||||
/// All C API structs and their Ghostty C names.
|
||||
pub const structs: std.StaticStringMap(StructInfo) = .initComptime(.{
|
||||
.{ "GhosttyColorRgb", StructInfo.init(color.RGB.C) },
|
||||
.{ "GhosttyDeviceAttributes", StructInfo.init(terminal.DeviceAttributes) },
|
||||
.{ "GhosttyDeviceAttributesPrimary", StructInfo.init(terminal.DeviceAttributes.Primary) },
|
||||
.{ "GhosttyDeviceAttributesSecondary", StructInfo.init(terminal.DeviceAttributes.Secondary) },
|
||||
.{ "GhosttyDeviceAttributesTertiary", StructInfo.init(terminal.DeviceAttributes.Tertiary) },
|
||||
.{ "GhosttyFormatterTerminalOptions", StructInfo.init(formatter.TerminalOptions) },
|
||||
.{ "GhosttySelection", StructInfo.init(selection.CSelection) },
|
||||
.{ "GhosttyFormatterTerminalExtra", StructInfo.init(formatter.TerminalOptions.Extra) },
|
||||
.{ "GhosttyFormatterScreenExtra", StructInfo.init(formatter.ScreenOptions.Extra) },
|
||||
.{ "GhosttyGridRef", StructInfo.init(grid_ref.CGridRef) },
|
||||
.{ "GhosttyMouseEncoderSize", StructInfo.init(mouse_encode.Size) },
|
||||
.{ "GhosttyMousePosition", StructInfo.init(mouse_event.Position) },
|
||||
.{ "GhosttyPoint", StructInfo.init(point.Point.C) },
|
||||
.{ "GhosttyPointCoordinate", StructInfo.init(point.Coordinate) },
|
||||
.{ "GhosttyRenderStateColors", StructInfo.init(render.Colors) },
|
||||
.{ "GhosttySizeReportSize", StructInfo.init(size_report.Size) },
|
||||
.{ "GhosttyString", StructInfo.init(lib.String) },
|
||||
.{ "GhosttyStyle", StructInfo.init(style_c.Style) },
|
||||
.{ "GhosttyStyleColor", StructInfo.init(style_c.Color) },
|
||||
.{ "GhosttyTerminalOptions", StructInfo.init(terminal.Options) },
|
||||
.{ "GhosttyTerminalScrollbar", StructInfo.init(terminal.TerminalScrollbar) },
|
||||
.{ "GhosttyTerminalScrollViewport", StructInfo.init(terminal.ScrollViewport) },
|
||||
});
|
||||
pub const structs: std.StaticStringMap(StructInfo) = structs: {
|
||||
@setEvalBranchQuota(10_000);
|
||||
break :structs .initComptime(.{
|
||||
.{ "GhosttyColorRgb", StructInfo.init(color.RGB.C) },
|
||||
.{ "GhosttyDeviceAttributes", StructInfo.init(terminal.DeviceAttributes) },
|
||||
.{ "GhosttyDeviceAttributesPrimary", StructInfo.init(terminal.DeviceAttributes.Primary) },
|
||||
.{ "GhosttyDeviceAttributesSecondary", StructInfo.init(terminal.DeviceAttributes.Secondary) },
|
||||
.{ "GhosttyDeviceAttributesTertiary", StructInfo.init(terminal.DeviceAttributes.Tertiary) },
|
||||
.{ "GhosttyFormatterTerminalOptions", StructInfo.init(formatter.TerminalOptions) },
|
||||
.{ "GhosttySelection", StructInfo.init(selection.CSelection) },
|
||||
.{ "GhosttyTerminalSelectWordOptions", StructInfo.init(selection.SelectWordOptions) },
|
||||
.{ "GhosttyTerminalSelectLineOptions", StructInfo.init(selection.SelectLineOptions) },
|
||||
.{ "GhosttyFormatterTerminalExtra", StructInfo.init(formatter.TerminalOptions.Extra) },
|
||||
.{ "GhosttyFormatterScreenExtra", StructInfo.init(formatter.ScreenOptions.Extra) },
|
||||
.{ "GhosttyGridRef", StructInfo.init(grid_ref.CGridRef) },
|
||||
.{ "GhosttyMouseEncoderSize", StructInfo.init(mouse_encode.Size) },
|
||||
.{ "GhosttyMousePosition", StructInfo.init(mouse_event.Position) },
|
||||
.{ "GhosttyPoint", StructInfo.init(point.Point.C) },
|
||||
.{ "GhosttyPointCoordinate", StructInfo.init(point.Coordinate) },
|
||||
.{ "GhosttyRenderStateColors", StructInfo.init(render.Colors) },
|
||||
.{ "GhosttySizeReportSize", StructInfo.init(size_report.Size) },
|
||||
.{ "GhosttyString", StructInfo.init(lib.String) },
|
||||
.{ "GhosttyStyle", StructInfo.init(style_c.Style) },
|
||||
.{ "GhosttyStyleColor", StructInfo.init(style_c.Color) },
|
||||
.{ "GhosttyTerminalOptions", StructInfo.init(terminal.Options) },
|
||||
.{ "GhosttyTerminalScrollbar", StructInfo.init(terminal.TerminalScrollbar) },
|
||||
.{ "GhosttyTerminalScrollViewport", StructInfo.init(terminal.ScrollViewport) },
|
||||
});
|
||||
};
|
||||
|
||||
/// The comptime-generated JSON string of all structs.
|
||||
pub const json: [:0]const u8 = json: {
|
||||
|
||||
31
src/terminal/selection_codepoints.zig
Normal file
31
src/terminal/selection_codepoints.zig
Normal file
@@ -0,0 +1,31 @@
|
||||
// This file contains various default word boundaries used for
|
||||
// selection logic. We put it in a separate file so that different
|
||||
// subsystems can import it without introducing a number of
|
||||
// dependencies.
|
||||
|
||||
/// Default boundary characters for word selection: ` \t'"│`|:;,()[]{}<>$`
|
||||
pub const default_word_boundaries = [_]u21{
|
||||
0, // null
|
||||
' ', // space
|
||||
'\t', // tab
|
||||
'\'', // single quote
|
||||
'"', // double quote
|
||||
'│', // U+2502 box drawing
|
||||
'`', // backtick
|
||||
'|', // pipe
|
||||
':', // colon
|
||||
';', // semicolon
|
||||
',', // comma
|
||||
'(', // left paren
|
||||
')', // right paren
|
||||
'[', // left bracket
|
||||
']', // right bracket
|
||||
'{', // left brace
|
||||
'}', // right brace
|
||||
'<', // less than
|
||||
'>', // greater than
|
||||
'$', // dollar
|
||||
};
|
||||
|
||||
/// Default whitespace characters trimmed from line selections.
|
||||
pub const default_line_whitespace = [_]u21{ 0, ' ', '\t' };
|
||||
Reference in New Issue
Block a user