mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal: selectionString takes a struct for opts
This commit is contained in:
@ -946,7 +946,10 @@ pub fn selectionString(self: *Surface, alloc: Allocator) !?[]const u8 {
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
const sel = self.io.terminal.screen.selection orelse return null;
|
||||
return try self.io.terminal.screen.selectionString(alloc, sel, false);
|
||||
return try self.io.terminal.screen.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the pwd of the terminal, if any. This is always copied because
|
||||
@ -1075,11 +1078,10 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
|
||||
}
|
||||
}
|
||||
|
||||
const buf = self.io.terminal.screen.selectionString(
|
||||
self.alloc,
|
||||
sel,
|
||||
self.config.clipboard_trim_trailing_spaces,
|
||||
) catch |err| {
|
||||
const buf = self.io.terminal.screen.selectionString(self.alloc, .{
|
||||
.sel = sel,
|
||||
.trim = self.config.clipboard_trim_trailing_spaces,
|
||||
}) catch |err| {
|
||||
log.err("error reading selection string err={}", .{err});
|
||||
return;
|
||||
};
|
||||
@ -2536,11 +2538,10 @@ fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool {
|
||||
const link, const sel = try self.linkAtPos(pos) orelse return false;
|
||||
switch (link.action) {
|
||||
.open => {
|
||||
const str = try self.io.terminal.screen.selectionString(
|
||||
self.alloc,
|
||||
sel,
|
||||
false,
|
||||
);
|
||||
const str = try self.io.terminal.screen.selectionString(self.alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer self.alloc.free(str);
|
||||
try internal_os.open(self.alloc, str);
|
||||
},
|
||||
@ -3104,11 +3105,10 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
// We can read from the renderer state without holding
|
||||
// the lock because only we will write to this field.
|
||||
if (self.io.terminal.screen.selection) |sel| {
|
||||
const buf = self.io.terminal.screen.selectionString(
|
||||
self.alloc,
|
||||
sel,
|
||||
self.config.clipboard_trim_trailing_spaces,
|
||||
) catch |err| {
|
||||
const buf = self.io.terminal.screen.selectionString(self.alloc, .{
|
||||
.sel = sel,
|
||||
.trim = self.config.clipboard_trim_trailing_spaces,
|
||||
}) catch |err| {
|
||||
log.err("error reading selection string err={}", .{err});
|
||||
return true;
|
||||
};
|
||||
|
@ -1106,22 +1106,25 @@ pub fn clearSelection(self: *Screen) void {
|
||||
self.selection = null;
|
||||
}
|
||||
|
||||
pub const SelectionString = struct {
|
||||
/// The selection to convert to a string.
|
||||
sel: Selection,
|
||||
|
||||
/// If true, trim whitespace around the selection.
|
||||
trim: bool,
|
||||
};
|
||||
|
||||
/// Returns the raw text associated with a selection. This will unwrap
|
||||
/// soft-wrapped edges. The returned slice is owned by the caller and allocated
|
||||
/// using alloc, not the allocator associated with the screen (unless they match).
|
||||
pub fn selectionString(
|
||||
self: *Screen,
|
||||
alloc: Allocator,
|
||||
sel: Selection,
|
||||
trim: bool,
|
||||
) ![:0]const u8 {
|
||||
pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) ![:0]const u8 {
|
||||
// Use an ArrayList so that we can grow the array as we go. We
|
||||
// build an initial capacity of just our rows in our selection times
|
||||
// columns. It can be more or less based on graphemes, newlines, etc.
|
||||
var strbuilder = std.ArrayList(u8).init(alloc);
|
||||
defer strbuilder.deinit();
|
||||
|
||||
const sel_ordered = sel.ordered(self, .forward);
|
||||
const sel_ordered = opts.sel.ordered(self, .forward);
|
||||
const sel_start = start: {
|
||||
var start = sel_ordered.start();
|
||||
const cell = start.rowAndCell().cell;
|
||||
@ -1199,7 +1202,7 @@ pub fn selectionString(
|
||||
// Remove any trailing spaces on lines. We could do optimize this by
|
||||
// doing this in the loop above but this isn't very hot path code and
|
||||
// this is simple.
|
||||
if (trim) {
|
||||
if (opts.trim) {
|
||||
var it = std.mem.tokenizeScalar(u8, strbuilder.items, '\n');
|
||||
|
||||
// Reset our items. We retain our capacity. Because we're only
|
||||
@ -6020,7 +6023,10 @@ test "Screen: selectionString basic" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "2EFGH\n3IJ";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6042,7 +6048,10 @@ test "Screen: selectionString start outside of written area" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 6 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6064,7 +6073,10 @@ test "Screen: selectionString end outside of written area" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 6 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "3IJKL";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6087,7 +6099,10 @@ test "Screen: selectionString trim space" {
|
||||
);
|
||||
|
||||
{
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "1AB\n2EF";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6095,7 +6110,10 @@ test "Screen: selectionString trim space" {
|
||||
|
||||
// No trim
|
||||
{
|
||||
const contents = try s.selectionString(alloc, sel, false);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "1AB \n2EF";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6118,7 +6136,10 @@ test "Screen: selectionString trim empty line" {
|
||||
);
|
||||
|
||||
{
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "1AB\n\n2EF";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6126,7 +6147,10 @@ test "Screen: selectionString trim empty line" {
|
||||
|
||||
// No trim
|
||||
{
|
||||
const contents = try s.selectionString(alloc, sel, false);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "1AB \n \n2EF";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6148,7 +6172,10 @@ test "Screen: selectionString soft wrap" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 2 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "2EFGH3IJ";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6170,7 +6197,10 @@ test "Screen: selectionString wide char" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = str;
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6182,7 +6212,10 @@ test "Screen: selectionString wide char" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = str;
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6194,7 +6227,10 @@ test "Screen: selectionString wide char" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 3, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "⚡";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6216,7 +6252,10 @@ test "Screen: selectionString wide char with header" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = str;
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6247,7 +6286,10 @@ test "Screen: selectionString empty with soft wrap" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "👨";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6280,7 +6322,10 @@ test "Screen: selectionString with zero width joiner" {
|
||||
s.pages.pin(.{ .screen = .{ .x = 1, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
const expected = "👨";
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
@ -6312,7 +6357,10 @@ test "Screen: selectionString, rectangle, basic" {
|
||||
;
|
||||
try s.testWriteString(str);
|
||||
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
}
|
||||
@ -6344,7 +6392,10 @@ test "Screen: selectionString, rectangle, w/EOL" {
|
||||
;
|
||||
try s.testWriteString(str);
|
||||
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
}
|
||||
@ -6380,7 +6431,10 @@ test "Screen: selectionString, rectangle, more complex w/breaks" {
|
||||
;
|
||||
try s.testWriteString(str);
|
||||
|
||||
const contents = try s.selectionString(alloc, sel, true);
|
||||
const contents = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = true,
|
||||
});
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings(expected, contents);
|
||||
}
|
||||
@ -6398,13 +6452,19 @@ test "Screen: lineIterator" {
|
||||
var iter = s.lineIterator(s.pages.pin(.{ .viewport = .{} }).?);
|
||||
{
|
||||
const sel = iter.next().?;
|
||||
const actual = try s.selectionString(alloc, sel, false);
|
||||
const actual = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(actual);
|
||||
try testing.expectEqualStrings("1ABCD", actual);
|
||||
}
|
||||
{
|
||||
const sel = iter.next().?;
|
||||
const actual = try s.selectionString(alloc, sel, false);
|
||||
const actual = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(actual);
|
||||
try testing.expectEqualStrings("2EFGH", actual);
|
||||
}
|
||||
@ -6423,13 +6483,19 @@ test "Screen: lineIterator soft wrap" {
|
||||
var iter = s.lineIterator(s.pages.pin(.{ .viewport = .{} }).?);
|
||||
{
|
||||
const sel = iter.next().?;
|
||||
const actual = try s.selectionString(alloc, sel, false);
|
||||
const actual = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(actual);
|
||||
try testing.expectEqualStrings("1ABCD2EFGH", actual);
|
||||
}
|
||||
{
|
||||
const sel = iter.next().?;
|
||||
const actual = try s.selectionString(alloc, sel, false);
|
||||
const actual = try s.selectionString(alloc, .{
|
||||
.sel = sel,
|
||||
.trim = false,
|
||||
});
|
||||
defer alloc.free(actual);
|
||||
try testing.expectEqualStrings("3ABCD", actual);
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ pub const Pin = PageList.Pin;
|
||||
pub const Screen = @import("Screen.zig");
|
||||
pub const ScreenType = Terminal.ScreenType;
|
||||
pub const Selection = @import("Selection.zig");
|
||||
//pub const StringMap = @import("StringMap.zig");
|
||||
pub const Style = style.Style;
|
||||
pub const Terminal = @import("Terminal.zig");
|
||||
pub const Stream = stream.Stream;
|
||||
|
Reference in New Issue
Block a user