aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/autocmd.txt5
-rw-r--r--runtime/doc/news.txt3
-rw-r--r--runtime/doc/vvars.txt9
-rw-r--r--runtime/lua/vim/_meta/vvars.lua11
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/eval.c1
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/terminal.c49
-rw-r--r--src/nvim/vvars.lua12
-rw-r--r--test/functional/terminal/buffer_spec.lua12
-rw-r--r--test/functional/terminal/tui_spec.lua55
11 files changed, 156 insertions, 4 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 45d7a4244c..ce3af01073 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -986,6 +986,11 @@ TermLeave After leaving |Terminal-mode|.
TermClose When a |terminal| job ends.
Sets these |v:event| keys:
status
+ *TermRequest*
+TermRequest When a |terminal| job emits an OSC or DCS
+ sequence. Sets |v:termrequest|. When used from
+ Lua, the request string is included in the
+ "data" field of the autocommand callback.
*TermResponse*
TermResponse When Nvim receives an OSC or DCS response from
the terminal. Sets |v:termresponse|. When used
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index d3ace5f33b..98b782a105 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -284,6 +284,9 @@ The following new APIs and features were added.
• |vim.deepcopy()| has a `noref` argument to avoid hashing table values.
+• Terminal buffers emit a |TermRequest| autocommand event when the child
+ process emits an OSC or DCS control sequence.
+
==============================================================================
CHANGED FEATURES *news-changed*
diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt
index 7e96d356d0..e71e31abf8 100644
--- a/runtime/doc/vvars.txt
+++ b/runtime/doc/vvars.txt
@@ -647,9 +647,16 @@ v:t_number Value of |Number| type. Read-only. See: |type()|
*v:t_string* *t_string-variable*
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
+ 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.
+
*v:termresponse* *termresponse-variable*
v:termresponse
- The value of the most recent OSC or DCS escape sequence
+ The value of the most recent OSC or DCS control sequence
received by Nvim from the terminal. This can be read in a
|TermResponse| event handler after querying the terminal using
another escape sequence.
diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index e3b89aeff7..ca87fb9d15 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -687,11 +687,18 @@ vim.v.t_number = ...
--- @type integer
vim.v.t_string = ...
---- The value of the most recent OSC or DCS escape sequence
+--- The value of the most recent OSC or DCS 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.
+--- @type string
+vim.v.termrequest = ...
+
+--- The value of the most recent OSC or DCS control sequence
--- received by Nvim from the terminal. This can be read in a
--- `TermResponse` event handler after querying the terminal using
--- another escape sequence.
---- @type any
+--- @type string
vim.v.termresponse = ...
--- Must be set before using `test_garbagecollect_now()`.
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 11d1597236..b931907fe3 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -109,6 +109,7 @@ return {
'TermEnter', -- after entering Terminal mode
'TermLeave', -- after leaving Terminal mode
'TermOpen', -- after opening a terminal buffer
+ 'TermRequest', -- after an unhandled OSC sequence is emitted
'TermResponse', -- after setting "v:termresponse"
'TextChanged', -- text was modified
'TextChangedI', -- text was modified in Insert mode(no popup)
@@ -166,6 +167,7 @@ return {
TabNewEntered = true,
TermClose = true,
TermOpen = true,
+ TermRequest = true,
UIEnter = true,
UILeave = true,
},
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5069f00df0..451e258356 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -185,6 +185,7 @@ static struct vimvar {
VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT + VV_RO),
VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
+ VV(VV_TERMREQUEST, "termrequest", VAR_STRING, VV_RO),
VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
VV(VV_LANG, "lang", VAR_STRING, VV_RO),
VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index c46a895c06..abe2685424 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -86,6 +86,7 @@ typedef enum {
VV_THIS_SESSION,
VV_VERSION,
VV_LNUM,
+ VV_TERMREQUEST,
VV_TERMRESPONSE,
VV_FNAME,
VV_LANG,
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index c6a2cb3354..ee482f7104 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -169,6 +169,54 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
static Set(ptr_t) invalidated_terminals = SET_INIT;
+static void emit_term_request(void **argv)
+{
+ char *payload = argv[0];
+ size_t payload_length = (size_t)argv[1];
+
+ String termrequest = { .data = payload, .size = payload_length };
+ Object data = STRING_OBJ(termrequest);
+ set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length);
+ apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, curbuf, NULL, &data);
+ xfree(payload);
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ if (frag.str == NULL) {
+ return 0;
+ }
+
+ 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);
+ return 1;
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ if ((command == NULL) || (frag.str == NULL)) {
+ return 0;
+ }
+
+ 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);
+ return 1;
+}
+
+static VTermStateFallbacks vterm_fallbacks = {
+ .control = NULL,
+ .csi = NULL,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = NULL,
+ .pm = NULL,
+ .sos = NULL,
+};
+
void terminal_init(void)
{
time_watcher_init(&main_loop, &refresh_timer, NULL);
@@ -222,6 +270,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
vterm_screen_enable_reflow(rv->vts, true);
// delete empty lines at the end of the buffer
vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);
+ vterm_screen_set_unrecognised_fallbacks(rv->vts, &vterm_fallbacks, rv);
vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);
vterm_screen_reset(rv->vts, 1);
vterm_output_set_callback(rv->vt, term_output_callback, rv);
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index d1b5e13401..6410df1a57 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -770,13 +770,23 @@ M.vars = {
desc = 'Value of |String| type. Read-only. See: |type()|',
},
termresponse = {
+ type = 'string',
desc = [=[
- The value of the most recent OSC or DCS escape sequence
+ The value of the most recent OSC or DCS control sequence
received by Nvim from the terminal. This can be read in a
|TermResponse| event handler after querying the terminal using
another escape sequence.
]=],
},
+ termrequest = {
+ type = 'string',
+ desc = [=[
+ The value of the most recent OSC or DCS 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.
+ ]=],
+ },
testing = {
desc = [=[
Must be set before using `test_garbagecollect_now()`.
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 480ca96289..d0462b5619 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -317,6 +317,18 @@ describe(':terminal buffer', function()
pcall_err(command, 'write test/functional/fixtures/tty-test.c')
)
end)
+
+ it('emits TermRequest events', function()
+ command('split')
+ command('enew')
+ local term = meths.open_term(0, {})
+ -- cwd will be inserted in a file URI, which cannot contain backs
+ local cwd = funcs.getcwd():gsub('\\', '/')
+ local parent = cwd:match('^(.+/)')
+ local expected = '\027]7;file://host' .. parent
+ meths.chan_send(term, string.format('%s\027\\', expected))
+ eq(expected, eval('v:termrequest'))
+ end)
end)
describe('No heap-buffer-overflow when using', function()
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index f6ee0f9c77..e65e57bc7f 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -28,6 +28,7 @@ local new_pipename = helpers.new_pipename
local spawn_argv = helpers.spawn_argv
local set_session = helpers.set_session
local write_file = helpers.write_file
+local eval = helpers.eval
if helpers.skip(is_os('win')) then
return
@@ -2736,6 +2737,42 @@ describe('TUI', function()
unchanged = true,
}
end)
+
+ it('queries the terminal for truecolor support', function()
+ clear()
+ exec_lua([[
+ vim.api.nvim_create_autocmd('TermRequest', {
+ callback = function(args)
+ local req = args.data
+ local payload = req:match('^\027P%+q([%x;]+)$')
+ if payload then
+ vim.g.xtgettcap = true
+ return true
+ end
+ end,
+ })
+ ]])
+ screen = thelpers.setup_child_nvim({
+ '-u',
+ 'NONE',
+ '-i',
+ 'NONE',
+ }, {
+ env = {
+ VIMRUNTIME = os.getenv('VIMRUNTIME'),
+
+ -- Force COLORTERM to be unset and use a TERM that does not contain Tc or RGB in terminfo.
+ -- This will force the nested nvim instance to query with XTGETTCAP
+ COLORTERM = '',
+ TERM = 'xterm-256colors',
+ },
+ })
+
+ retry(nil, 1000, function()
+ eq(true, eval("get(g:, 'xtgettcap', v:false)"))
+ eq(1, eval('&termguicolors'))
+ end)
+ end)
end)
describe('TUI bg color', function()
@@ -2743,6 +2780,18 @@ describe('TUI bg color', function()
local function setup_bg_test()
clear()
+ exec_lua([[
+ vim.api.nvim_create_autocmd('TermRequest', {
+ callback = function(args)
+ local req = args.data
+ if req == '\027]11;?' then
+ vim.g.oscrequest = true
+ return true
+ end
+ end,
+ })
+ ]])
+
screen = thelpers.setup_child_nvim({
'-u',
'NONE',
@@ -2759,6 +2808,12 @@ describe('TUI bg color', function()
before_each(setup_bg_test)
+ it('queries the terminal for background color', function()
+ retry(nil, 1000, function()
+ eq(true, eval("get(g:, 'oscrequest', v:false)"))
+ end)
+ end)
+
it('triggers OptionSet event on unsplit terminal-response', function()
screen:expect([[
{1: } |