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