diff options
author | Gregory Anders <8965202+gpanders@users.noreply.github.com> | 2024-06-11 13:18:06 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-11 13:18:06 -0500 |
commit | 3ad977f01d97e84b576e6965c5c9e4f75c10cb35 (patch) | |
tree | 352ed37a4738a1f5a78f892c77817e9937d58a28 /src | |
parent | 39d8651283c0458c20b755d2140c8a3cb7b581c5 (diff) | |
download | rneovim-3ad977f01d97e84b576e6965c5c9e4f75c10cb35.tar.gz rneovim-3ad977f01d97e84b576e6965c5c9e4f75c10cb35.tar.bz2 rneovim-3ad977f01d97e84b576e6965c5c9e4f75c10cb35.zip |
feat(terminal): add support for copying with OSC 52 in embedded terminal (#29117)
When libvterm receives the OSC 52 escape sequence it ignores it because
Nvim does not set any selection callbacks. Install selection callbacks
that forward to the clipboard provider, so that setting the clipboard
with OSC 52 in the embedded terminal writes to the system clipboard
using the configured clipboard provider.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/base64.c | 1 | ||||
-rw-r--r-- | src/nvim/terminal.c | 69 |
2 files changed, 69 insertions, 1 deletions
diff --git a/src/nvim/base64.c b/src/nvim/base64.c index 39e4ec4872..99d3c5a33e 100644 --- a/src/nvim/base64.c +++ b/src/nvim/base64.c @@ -142,6 +142,7 @@ char *base64_encode(const char *src, size_t src_len) /// @param [out] out_lenp Returns the length of the decoded string /// @return Decoded string char *base64_decode(const char *src, size_t src_len, size_t *out_lenp) + FUNC_ATTR_NONNULL_ALL { assert(src != NULL); assert(out_lenp != NULL); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 027ff79696..818f8abbb5 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -112,6 +112,9 @@ typedef struct { // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 +#define TEXTBUF_SIZE 0x1fff +#define SELECTIONBUF_SIZE 0x0400 + static TimeWatcher refresh_timer; static bool refresh_pending = false; @@ -127,7 +130,7 @@ struct terminal { // buffer used to: // - convert VTermScreen cell arrays into utf8 strings // - receive data from libvterm as a result of key presses. - char textbuf[0x1fff]; + char textbuf[TEXTBUF_SIZE]; ScrollbackLine **sb_buffer; // Scrollback storage. size_t sb_current; // Lines stored in sb_buffer. @@ -166,6 +169,9 @@ struct terminal { // When there is a pending TermRequest autocommand, block and store input. StringBuilder *pending_send; + char *selection_buffer; /// libvterm selection buffer + StringBuilder selection; /// Growable array containing full selection data + size_t refcount; // reference count }; @@ -179,6 +185,12 @@ static VTermScreenCallbacks vterm_screen_callbacks = { .sb_popline = term_sb_pop, }; +static VTermSelectionCallbacks vterm_selection_callbacks = { + .set = term_selection_set, + // For security reasons we don't support querying the system clipboard from the embedded terminal + .query = NULL, +}; + static Set(ptr_t) invalidated_terminals = SET_INIT; static void emit_termrequest(void **argv) @@ -315,6 +327,11 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(term->vts, 1); vterm_output_set_callback(term->vt, term_output_callback, term); + + term->selection_buffer = xcalloc(SELECTIONBUF_SIZE, 1); + vterm_state_set_selection_callbacks(state, &vterm_selection_callbacks, term, + term->selection_buffer, SELECTIONBUF_SIZE); + // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called term->invalid_start = 0; @@ -769,6 +786,8 @@ void terminal_destroy(Terminal **termpp) } xfree(term->sb_buffer); xfree(term->title); + xfree(term->selection_buffer); + kv_destroy(term->selection); vterm_free(term->vt); xfree(term); *termpp = NULL; // coverity[dead-store] @@ -1198,6 +1217,54 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) return 1; } +static void term_clipboard_set(void **argv) +{ + VTermSelectionMask mask = (VTermSelectionMask)(long)argv[0]; + char *data = argv[1]; + + char regname; + switch (mask) { + case VTERM_SELECTION_CLIPBOARD: + regname = '+'; + break; + case VTERM_SELECTION_PRIMARY: + regname = '*'; + break; + default: + regname = '+'; + break; + } + + list_T *lines = tv_list_alloc(1); + tv_list_append_allocated_string(lines, data); + + list_T *args = tv_list_alloc(3); + tv_list_append_list(args, lines); + + const char regtype = 'v'; + tv_list_append_string(args, ®type, 1); + + tv_list_append_string(args, ®name, 1); + eval_call_provider("clipboard", "set", args, true); +} + +static int term_selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) +{ + Terminal *term = user; + if (frag.initial) { + kv_size(term->selection) = 0; + } + + kv_concat_len(term->selection, frag.str, frag.len); + + if (frag.final) { + char *data = xmemdupz(term->selection.items, kv_size(term->selection)); + multiqueue_put(main_loop.events, term_clipboard_set, (void *)mask, data); + } + + return 1; +} + // }}} // input handling {{{ |