feat(term): trigger TermRequest for APC (#32407)

Co-authored-by: Gregory Anders <greg@gpanders.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
This commit is contained in:
Till Bungert
2025-02-13 15:24:01 +01:00
committed by GitHub
parent e4c6e732fd
commit 93480f7fba
7 changed files with 67 additions and 16 deletions

View File

@@ -1004,8 +1004,8 @@ TermClose When a |terminal| job ends.
Sets these |v:event| keys: Sets these |v:event| keys:
status status
*TermRequest* *TermRequest*
TermRequest When a |:terminal| child process emits an OSC TermRequest When a |:terminal| child process emits an OSC,
or DCS sequence. Sets |v:termrequest|. The DCS or APC sequence. Sets |v:termrequest|. The
|event-data| is the request string. |event-data| is the request string.
*TermResponse* *TermResponse*
TermResponse When Nvim receives an OSC or DCS response from TermResponse When Nvim receives an OSC or DCS response from

View File

@@ -372,6 +372,8 @@ TERMINAL
• The |terminal| has experimental support for the Kitty keyboard protocol • The |terminal| has experimental support for the Kitty keyboard protocol
(sometimes called "CSI u" key encoding). Only the "Disambiguate escape (sometimes called "CSI u" key encoding). Only the "Disambiguate escape
codes" mode is currently supported. codes" mode is currently supported.
• The |terminal| emits a |TermRequest| autocommand event when the child process
emits an APC control sequence.
TREESITTER TREESITTER

View File

@@ -663,7 +663,7 @@ v:t_string Value of |String| type. Read-only. See: |type()|
*v:termrequest* *termrequest-variable* *v:termrequest* *termrequest-variable*
v:termrequest 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|. sent from a process running in the embedded |terminal|.
This can be read in a |TermRequest| event handler to respond This can be read in a |TermRequest| event handler to respond
to queries from embedded applications. to queries from embedded applications.

View File

@@ -700,7 +700,7 @@ vim.v.t_number = ...
--- @type integer --- @type integer
vim.v.t_string = ... 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`. --- sent from a process running in the embedded `terminal`.
--- This can be read in a `TermRequest` event handler to respond --- This can be read in a `TermRequest` event handler to respond
--- to queries from embedded applications. --- to queries from embedded applications.

View File

@@ -183,8 +183,10 @@ struct terminal {
bool color_set[16]; bool color_set[16];
char *selection_buffer; /// libvterm selection buffer char *selection_buffer; ///< libvterm selection buffer
StringBuilder selection; /// Growable array containing full selection data StringBuilder selection; ///< Growable array containing full selection data
StringBuilder termrequest_buffer; ///< Growable array containing unfinished request payload
size_t refcount; // reference count size_t refcount; // reference count
}; };
@@ -307,15 +309,22 @@ static int on_osc(int command, VTermStringFragment frag, void *user)
return 1; return 1;
} }
StringBuilder request = KV_INITIAL_VALUE; if (frag.initial) {
kv_printf(request, "\x1b]%d;", command); kv_size(term->termrequest_buffer) = 0;
kv_concat_len(request, frag.str, frag.len); kv_printf(term->termrequest_buffer, "\x1b]%d;", command);
schedule_termrequest(term, request.items, request.size); }
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; return 1;
} }
static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
{ {
Terminal *term = user;
if (command == NULL || frag.str == NULL) { if (command == NULL || frag.str == NULL) {
return 0; return 0;
} }
@@ -323,10 +332,38 @@ static int on_dcs(const char *command, size_t commandlen, VTermStringFragment fr
return 1; return 1;
} }
StringBuilder request = KV_INITIAL_VALUE; if (frag.initial) {
kv_printf(request, "\x1bP%*s", (int)commandlen, command); kv_size(term->termrequest_buffer) = 0;
kv_concat_len(request, frag.str, frag.len); kv_printf(term->termrequest_buffer, "\x1bP%*s", (int)commandlen, command);
schedule_termrequest(user, request.items, request.size); }
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; return 1;
} }
@@ -335,7 +372,7 @@ static VTermStateFallbacks vterm_fallbacks = {
.csi = NULL, .csi = NULL,
.osc = on_osc, .osc = on_osc,
.dcs = on_dcs, .dcs = on_dcs,
.apc = NULL, .apc = on_apc,
.pm = NULL, .pm = NULL,
.sos = NULL, .sos = NULL,
}; };
@@ -924,6 +961,7 @@ void terminal_destroy(Terminal **termpp)
xfree(term->title); xfree(term->title);
xfree(term->selection_buffer); xfree(term->selection_buffer);
kv_destroy(term->selection); kv_destroy(term->selection);
kv_destroy(term->termrequest_buffer);
vterm_free(term->vt); vterm_free(term->vt);
xfree(term); xfree(term);
*termpp = NULL; // coverity[dead-store] *termpp = NULL; // coverity[dead-store]

View File

@@ -799,7 +799,7 @@ M.vars = {
termrequest = { termrequest = {
type = 'string', type = 'string',
desc = [=[ 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|. sent from a process running in the embedded |terminal|.
This can be read in a |TermRequest| event handler to respond This can be read in a |TermRequest| event handler to respond
to queries from embedded applications. to queries from embedded applications.

View File

@@ -350,6 +350,17 @@ describe(':terminal buffer', function()
eq(termbuf, eval('g:termbuf')) eq(termbuf, eval('g:termbuf'))
end) 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() it('TermRequest synchronization #27572', function()
command('autocmd! nvim.terminal TermRequest') command('autocmd! nvim.terminal TermRequest')
local term = exec_lua([[ local term = exec_lua([[