Skip to content

Commit f3352dd

Browse files
committed
core: copy the proper format to the clipboard as configured
1 parent 9a198b4 commit f3352dd

File tree

1 file changed

+120
-34
lines changed

1 file changed

+120
-34
lines changed

‎src/Surface.zig‎

Lines changed: 120 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,20 +1958,103 @@ fn copySelectionToClipboards(
19581958
self: *Surface,
19591959
sel: terminal.Selection,
19601960
clipboards: []const apprt.Clipboard,
1961-
) void {
1962-
const buf = self.io.terminal.screen.selectionString(self.alloc, .{
1963-
.sel = sel,
1961+
format: input.Binding.Action.CopyToClipboard,
1962+
) !void {
1963+
// Create an arena to simplify memory management here.
1964+
var arena = ArenaAllocator.init(self.alloc);
1965+
errdefer arena.deinit();
1966+
const alloc = arena.allocator();
1967+
1968+
// The options we'll use for all formatting. We'll just override the
1969+
// emit format.
1970+
const opts: terminal.formatter.Options = .{
1971+
.emit = .plain, // We'll override this below
1972+
.unwrap = true,
19641973
.trim = self.config.clipboard_trim_trailing_spaces,
1965-
}) catch |err| {
1966-
log.err("error reading selection string err={}", .{err});
1967-
return;
1974+
.background = self.io.terminal.colors.background.get(),
1975+
.foreground = self.io.terminal.colors.foreground.get(),
1976+
.palette = &self.io.terminal.colors.palette.current,
19681977
};
1969-
defer self.alloc.free(buf);
19701978

1971-
for (clipboards) |clipboard| self.rt_surface.setClipboard(clipboard, &.{.{
1972-
.mime = "text/plain",
1973-
.data = buf,
1974-
}}, false) catch |err| {
1979+
const ScreenFormatter = terminal.formatter.ScreenFormatter;
1980+
var aw: std.Io.Writer.Allocating = .init(alloc);
1981+
var contents: std.ArrayList(apprt.ClipboardContent) = .empty;
1982+
switch (format) {
1983+
.plain => {
1984+
var formatter: ScreenFormatter = .init(&self.io.terminal.screen, opts);
1985+
formatter.content = .{ .selection = sel };
1986+
try formatter.format(&aw.writer);
1987+
try contents.append(alloc, .{
1988+
.mime = "text/plain",
1989+
.data = try aw.toOwnedSliceSentinel(0),
1990+
});
1991+
},
1992+
1993+
.vt => {
1994+
var formatter: ScreenFormatter = .init(&self.io.terminal.screen, opts: {
1995+
var copy = opts;
1996+
copy.emit = .vt;
1997+
break :opts copy;
1998+
});
1999+
formatter.content = .{ .selection = sel };
2000+
try formatter.format(&aw.writer);
2001+
try contents.append(alloc, .{
2002+
.mime = "text/plain",
2003+
.data = try aw.toOwnedSliceSentinel(0),
2004+
});
2005+
},
2006+
2007+
.html => {
2008+
var formatter: ScreenFormatter = .init(&self.io.terminal.screen, opts: {
2009+
var copy = opts;
2010+
copy.emit = .html;
2011+
break :opts copy;
2012+
});
2013+
formatter.content = .{ .selection = sel };
2014+
try formatter.format(&aw.writer);
2015+
try contents.append(alloc, .{
2016+
.mime = "text/html",
2017+
.data = try aw.toOwnedSliceSentinel(0),
2018+
});
2019+
},
2020+
2021+
.mixed => {
2022+
var formatter: ScreenFormatter = .init(&self.io.terminal.screen, opts);
2023+
formatter.content = .{ .selection = sel };
2024+
try formatter.format(&aw.writer);
2025+
try contents.append(alloc, .{
2026+
.mime = "text/plain",
2027+
.data = try aw.toOwnedSliceSentinel(0),
2028+
});
2029+
2030+
assert(aw.written().len == 0);
2031+
formatter = .init(&self.io.terminal.screen, opts: {
2032+
var copy = opts;
2033+
copy.emit = .html;
2034+
2035+
// We purposely don't emit background/foreground for mixed
2036+
// mode because the HTML contents is often used for rich text
2037+
// input and with trimmed spaces it looks pretty bad.
2038+
copy.background = null;
2039+
copy.foreground = null;
2040+
2041+
break :opts copy;
2042+
});
2043+
formatter.content = .{ .selection = sel };
2044+
try formatter.format(&aw.writer);
2045+
try contents.append(alloc, .{
2046+
.mime = "text/html",
2047+
.data = try aw.toOwnedSliceSentinel(0),
2048+
});
2049+
},
2050+
}
2051+
2052+
assert(contents.items.len > 0);
2053+
for (clipboards) |clipboard| self.rt_surface.setClipboard(
2054+
clipboard,
2055+
contents.items,
2056+
false,
2057+
) catch |err| {
19752058
log.err(
19762059
"error setting clipboard string clipboard={} err={}",
19772060
.{ clipboard, err },
@@ -2000,17 +2083,23 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
20002083
.false => unreachable, // handled above with an early exit
20012084

20022085
// Both standard and selection clipboards are set.
2003-
.clipboard => {
2004-
self.copySelectionToClipboards(sel, &.{ .standard, .selection });
2005-
},
2086+
.clipboard => try self.copySelectionToClipboards(
2087+
sel,
2088+
&.{ .standard, .selection },
2089+
.mixed,
2090+
),
20062091

20072092
// The selection clipboard is set if supported, otherwise the standard.
20082093
.true => {
20092094
const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection))
20102095
.selection
20112096
else
20122097
.standard;
2013-
self.copySelectionToClipboards(sel, &.{clipboard});
2098+
try self.copySelectionToClipboards(
2099+
sel,
2100+
&.{clipboard},
2101+
.mixed,
2102+
);
20142103
},
20152104
}
20162105
}
@@ -3748,14 +3837,22 @@ pub fn mouseButtonCallback(
37483837
},
37493838
.copy => {
37503839
if (self.io.terminal.screen.selection) |sel| {
3751-
self.copySelectionToClipboards(sel, &.{.standard});
3840+
try self.copySelectionToClipboards(
3841+
sel,
3842+
&.{.standard},
3843+
.mixed,
3844+
);
37523845
}
37533846

37543847
try self.setSelection(null);
37553848
try self.queueRender();
37563849
},
37573850
.@"copy-or-paste" => if (self.io.terminal.screen.selection) |sel| {
3758-
self.copySelectionToClipboards(sel, &.{.standard});
3851+
try self.copySelectionToClipboards(
3852+
sel,
3853+
&.{.standard},
3854+
.mixed,
3855+
);
37593856
try self.setSelection(null);
37603857
try self.queueRender();
37613858
} else {
@@ -4659,26 +4756,15 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
46594756
self.renderer_state.terminal.fullReset();
46604757
},
46614758

4662-
.copy_to_clipboard => {
4759+
.copy_to_clipboard => |format| {
46634760
// We can read from the renderer state without holding
46644761
// the lock because only we will write to this field.
46654762
if (self.io.terminal.screen.selection) |sel| {
4666-
const buf = self.io.terminal.screen.selectionString(self.alloc, .{
4667-
.sel = sel,
4668-
.trim = self.config.clipboard_trim_trailing_spaces,
4669-
}) catch |err| {
4670-
log.err("error reading selection string err={}", .{err});
4671-
return true;
4672-
};
4673-
defer self.alloc.free(buf);
4674-
4675-
self.rt_surface.setClipboard(.standard, &.{.{
4676-
.mime = "text/plain",
4677-
.data = buf,
4678-
}}, false) catch |err| {
4679-
log.err("error setting clipboard string err={}", .{err});
4680-
return true;
4681-
};
4763+
try self.copySelectionToClipboards(
4764+
sel,
4765+
&.{.standard},
4766+
format,
4767+
);
46824768

46834769
// Clear the selection if configured to do so.
46844770
if (self.config.selection_clear_on_copy) {

0 commit comments

Comments
 (0)