terminal: selectionString takes a struct for opts

This commit is contained in:
Mitchell Hashimoto
2024-03-15 20:07:49 -07:00
parent 2de86ce500
commit bca51ee771
3 changed files with 112 additions and 45 deletions

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;