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