diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index a8f95cf44e..b5a3cffe2f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -163,6 +163,9 @@ struct terminal { bool color_set[16]; + // When there is a pending TermRequest autocommand, block and store input. + StringBuilder *pending_send; + size_t refcount; // reference count }; @@ -178,11 +181,12 @@ static VTermScreenCallbacks vterm_screen_callbacks = { static Set(ptr_t) invalidated_terminals = SET_INIT; -static void emit_term_request(void **argv) +static void emit_termrequest(void **argv) { - char *payload = argv[0]; - size_t payload_length = (size_t)argv[1]; - Terminal *term = argv[2]; + Terminal *term = argv[0]; + char *payload = argv[1]; + size_t payload_length = (size_t)argv[2]; + StringBuilder *pending_send = argv[3]; buf_T *buf = handle_get_buffer(term->buf_handle); String termrequest = { .data = payload, .size = payload_length }; @@ -190,6 +194,25 @@ static void emit_term_request(void **argv) set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length); apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data); xfree(payload); + + StringBuilder *term_pending_send = term->pending_send; + term->pending_send = NULL; + if (kv_size(*pending_send)) { + terminal_send(term, pending_send->items, pending_send->size); + kv_destroy(*pending_send); + } + if (term_pending_send != pending_send) { + term->pending_send = term_pending_send; + } + xfree(pending_send); +} + +static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length) +{ + term->pending_send = xmalloc(sizeof(StringBuilder)); + kv_init(*term->pending_send); + multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length, + term->pending_send); } static int on_osc(int command, VTermStringFragment frag, void *user) @@ -197,24 +220,30 @@ static int on_osc(int command, VTermStringFragment frag, void *user) if (frag.str == NULL) { return 0; } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } StringBuilder request = KV_INITIAL_VALUE; kv_printf(request, "\x1b]%d;", command); kv_concat_len(request, frag.str, frag.len); - multiqueue_put(main_loop.events, emit_term_request, request.items, (void *)request.size, user); + schedule_termrequest(user, request.items, request.size); return 1; } static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) { - if ((command == NULL) || (frag.str == NULL)) { + if (command == NULL || frag.str == NULL) { return 0; } + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } StringBuilder request = KV_INITIAL_VALUE; kv_printf(request, "\x1bP%*s", (int)commandlen, command); kv_concat_len(request, frag.str, frag.len); - multiqueue_put(main_loop.events, emit_term_request, request.items, (void *)request.size, user); + schedule_termrequest(user, request.items, request.size); return 1; } @@ -747,6 +776,10 @@ static void terminal_send(Terminal *term, const char *data, size_t size) if (term->closed) { return; } + if (term->pending_send) { + kv_concat_len(*term->pending_send, data, size); + return; + } term->opts.write_cb(data, size, term->opts.data); } diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 8dc52a4d5e..376b7b849e 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -319,8 +319,7 @@ describe(':terminal buffer', function() end) it('emits TermRequest events #26972', function() - command('split') - command('enew') + command('new') local term = api.nvim_open_term(0, {}) local termbuf = api.nvim_get_current_buf() @@ -336,6 +335,35 @@ describe(':terminal buffer', function() eq(expected, eval('v:termrequest')) eq(termbuf, eval('g:termbuf')) end) + + it('TermReqeust synchronization #27572', function() + command('new') + command('autocmd! nvim_terminal TermRequest') + local term = exec_lua([[ + _G.input = {} + local term = vim.api.nvim_open_term(0, { + on_input = function(_, _, _, data) + table.insert(_G.input, data) + end, + force_crlf = false, + }) + vim.api.nvim_create_autocmd('TermRequest', { + callback = function(args) + if args.data == '\027]11;?' then + table.insert(_G.input, '\027]11;rgb:0000/0000/0000\027\\') + end + end + }) + return term + ]]) + api.nvim_chan_send(term, '\027]11;?\007\027[5n\027]11;?\007\027[5n') + eq({ + '\027]11;rgb:0000/0000/0000\027\\', + '\027[0n', + '\027]11;rgb:0000/0000/0000\027\\', + '\027[0n', + }, exec_lua('return _G.input')) + end) end) describe('No heap-buffer-overflow when using', function()