diff options
-rw-r--r-- | runtime/doc/autocmd.txt | 4 | ||||
-rw-r--r-- | runtime/doc/news.txt | 2 | ||||
-rw-r--r-- | runtime/doc/vvars.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vvars.lua | 2 | ||||
-rw-r--r-- | src/nvim/terminal.c | 60 | ||||
-rw-r--r-- | src/nvim/vvars.lua | 2 | ||||
-rw-r--r-- | test/functional/terminal/buffer_spec.lua | 11 |
7 files changed, 67 insertions, 16 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index a82bac5de5..3ffbdf310f 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1004,8 +1004,8 @@ TermClose When a |terminal| job ends. Sets these |v:event| keys: status *TermRequest* -TermRequest When a |:terminal| child process emits an OSC - or DCS sequence. Sets |v:termrequest|. The +TermRequest When a |:terminal| child process emits an OSC, + DCS or APC sequence. Sets |v:termrequest|. The |event-data| is the request string. *TermResponse* TermResponse When Nvim receives an OSC or DCS response from diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index cfda05e6f5..c98084adb6 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -372,6 +372,8 @@ TERMINAL • The |terminal| has experimental support for the Kitty keyboard protocol (sometimes called "CSI u" key encoding). Only the "Disambiguate escape codes" mode is currently supported. +• The |terminal| emits a |TermRequest| autocommand event when the child process + emits an APC control sequence. TREESITTER diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index 0ebb54e38a..e4f6b10ef7 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -663,7 +663,7 @@ v:t_string Value of |String| type. Read-only. See: |type()| *v:termrequest* *termrequest-variable* v:termrequest - The value of the most recent OSC or DCS control sequence + The value of the most recent OSC, DCS or APC control sequence sent from a process running in the embedded |terminal|. This can be read in a |TermRequest| event handler to respond to queries from embedded applications. diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index c1b8695bbf..d7a80911c6 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -700,7 +700,7 @@ vim.v.t_number = ... --- @type integer vim.v.t_string = ... ---- The value of the most recent OSC or DCS control sequence +--- The value of the most recent OSC, DCS or APC control sequence --- sent from a process running in the embedded `terminal`. --- This can be read in a `TermRequest` event handler to respond --- to queries from embedded applications. diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 897c393488..959126dd24 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -183,8 +183,10 @@ struct terminal { bool color_set[16]; - char *selection_buffer; /// libvterm selection buffer - StringBuilder selection; /// Growable array containing full selection data + char *selection_buffer; ///< libvterm selection buffer + StringBuilder selection; ///< Growable array containing full selection data + + StringBuilder termrequest_buffer; ///< Growable array containing unfinished request payload size_t refcount; // reference count }; @@ -307,15 +309,22 @@ static int on_osc(int command, VTermStringFragment frag, void *user) return 1; } - StringBuilder request = KV_INITIAL_VALUE; - kv_printf(request, "\x1b]%d;", command); - kv_concat_len(request, frag.str, frag.len); - schedule_termrequest(term, request.items, request.size); + if (frag.initial) { + kv_size(term->termrequest_buffer) = 0; + kv_printf(term->termrequest_buffer, "\x1b]%d;", command); + } + kv_concat_len(term->termrequest_buffer, frag.str, frag.len); + if (frag.final) { + char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size); + schedule_termrequest(user, payload, term->termrequest_buffer.size); + } return 1; } static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) { + Terminal *term = user; + if (command == NULL || frag.str == NULL) { return 0; } @@ -323,10 +332,38 @@ static int on_dcs(const char *command, size_t commandlen, VTermStringFragment fr return 1; } - StringBuilder request = KV_INITIAL_VALUE; - kv_printf(request, "\x1bP%*s", (int)commandlen, command); - kv_concat_len(request, frag.str, frag.len); - schedule_termrequest(user, request.items, request.size); + if (frag.initial) { + kv_size(term->termrequest_buffer) = 0; + kv_printf(term->termrequest_buffer, "\x1bP%*s", (int)commandlen, command); + } + kv_concat_len(term->termrequest_buffer, frag.str, frag.len); + if (frag.final) { + char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size); + schedule_termrequest(user, payload, term->termrequest_buffer.size); + } + return 1; +} + +static int on_apc(VTermStringFragment frag, void *user) +{ + Terminal *term = user; + if (frag.str == NULL || frag.len == 0) { + return 0; + } + + if (!has_event(EVENT_TERMREQUEST)) { + return 1; + } + + if (frag.initial) { + kv_size(term->termrequest_buffer) = 0; + kv_printf(term->termrequest_buffer, "\x1b_"); + } + kv_concat_len(term->termrequest_buffer, frag.str, frag.len); + if (frag.final) { + char *payload = xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size); + schedule_termrequest(user, payload, term->termrequest_buffer.size); + } return 1; } @@ -335,7 +372,7 @@ static VTermStateFallbacks vterm_fallbacks = { .csi = NULL, .osc = on_osc, .dcs = on_dcs, - .apc = NULL, + .apc = on_apc, .pm = NULL, .sos = NULL, }; @@ -924,6 +961,7 @@ void terminal_destroy(Terminal **termpp) xfree(term->title); xfree(term->selection_buffer); kv_destroy(term->selection); + kv_destroy(term->termrequest_buffer); vterm_free(term->vt); xfree(term); *termpp = NULL; // coverity[dead-store] diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 056e281c0b..55f979364f 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -799,7 +799,7 @@ M.vars = { termrequest = { type = 'string', desc = [=[ - The value of the most recent OSC or DCS control sequence + The value of the most recent OSC, DCS or APC control sequence sent from a process running in the embedded |terminal|. This can be read in a |TermRequest| event handler to respond to queries from embedded applications. diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 4635259e33..f2d679bd5d 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -350,6 +350,17 @@ describe(':terminal buffer', function() eq(termbuf, eval('g:termbuf')) end) + it('emits TermRequest events for APC', function() + local term = api.nvim_open_term(0, {}) + + -- cwd will be inserted in a file URI, which cannot contain backs + local cwd = t.fix_slashes(fn.getcwd()) + local parent = cwd:match('^(.+/)') + local expected = '\027_Gfile://host' .. parent + api.nvim_chan_send(term, string.format('%s\027\\', expected)) + eq(expected, eval('v:termrequest')) + end) + it('TermRequest synchronization #27572', function() command('autocmd! nvim.terminal TermRequest') local term = exec_lua([[ |