aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim
diff options
context:
space:
mode:
authorBarrett Ruth <62671086+barrettruth@users.noreply.github.com>2026-06-23 05:19:56 -0500
committerGitHub <noreply@github.com>2026-06-23 06:19:56 -0400
commitdb30608058db402de37be7a1d33fa60b7fc7de83 (patch)
tree20fdd838c3e8237215639b9b3d3bf99556d1a5cd /runtime/lua/vim
parente4fbe162c193f926000bc83123e40caf8faccb14 (diff)
downloadrneovim-db30608058db402de37be7a1d33fa60b7fc7de83.tar.gz
rneovim-db30608058db402de37be7a1d33fa60b7fc7de83.tar.bz2
rneovim-db30608058db402de37be7a1d33fa60b7fc7de83.zip
fix(tui): attribute `TermResponse` to source channel #40330
Problem: Attach-time terminal probes cannot distinguish responses from different attached UIs. Solution: Identify the UI by RPC channel id in `TermResponse` and make `vim.tty.request()` filter responses by channel.
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r--runtime/lua/vim/_core/defaults.lua60
-rw-r--r--runtime/lua/vim/_meta/events.lua1
-rw-r--r--runtime/lua/vim/tty.lua7
3 files changed, 39 insertions, 29 deletions
diff --git a/runtime/lua/vim/_core/defaults.lua b/runtime/lua/vim/_core/defaults.lua
index 73d85a7283..b508827c3b 100644
--- a/runtime/lua/vim/_core/defaults.lua
+++ b/runtime/lua/vim/_core/defaults.lua
@@ -922,7 +922,7 @@ do
--- @param sync boolean When true (a TTY is present at startup), also send a
--- DSR probe and synchronously wait so 'background' is set before user config,
--- warning (E1568) if the terminal never answers the DSR.
- local function detect_background(sync)
+ local function detect_background(sync, chan)
-- Re-create (clear) the handler's augroup on each call so only the
-- most-recently-attached TUI's handler remains.
local bg_group = vim.api.nvim_create_augroup('nvim.tty.background', {})
@@ -938,36 +938,40 @@ do
-- also reacts to runtime theme changes; the per-response bg_user_set() guard
-- stops it once the user pins 'background'.
local did_dsr_response = false
- vim.tty.request(osc11 .. (sync and dsr or ''), { group = bg_group, timeout = 0 }, function(resp)
- -- DSR response that should come after the OSC 11 response if the terminal
- -- supports it.
- if sync and string.match(resp, '^\027%[0n$') then
- did_dsr_response = true
- -- Don't stop listening: the bg response may come after the DSR response
- -- if the terminal handles requests out of sequence. In that case, the bg
- -- will simply be set later in the startup sequence.
- return
- end
+ vim.tty.request(
+ osc11 .. (sync and dsr or ''),
+ { group = bg_group, timeout = 0, chan = chan },
+ function(resp)
+ -- DSR response that should come after the OSC 11 response if the terminal
+ -- supports it.
+ if sync and string.match(resp, '^\027%[0n$') then
+ did_dsr_response = true
+ -- Don't stop listening: the bg response may come after the DSR response
+ -- if the terminal handles requests out of sequence. In that case, the bg
+ -- will simply be set later in the startup sequence.
+ return
+ end
- -- Never override an explicit user value: stop once the user pins it.
- if bg_user_set() then
- return true
- end
+ -- Never override an explicit user value: stop once the user pins it.
+ if bg_user_set() then
+ return true
+ end
- local r, g, b = parseosc11(resp)
- if r and g and b then
- local rr = parsecolor(r)
- local gg = parsecolor(g)
- local bb = parsecolor(b)
-
- if rr and gg and bb then
- local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
- local bg = luminance < 0.5 and 'dark' or 'light'
- -- Use :noautocmd to suppress OptionSet event; OSC11 response may arrive after VimEnter.
- vim.cmd('noautocmd set background=' .. bg)
+ local r, g, b = parseosc11(resp)
+ if r and g and b then
+ local rr = parsecolor(r)
+ local gg = parsecolor(g)
+ local bb = parsecolor(b)
+
+ if rr and gg and bb then
+ local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
+ local bg = luminance < 0.5 and 'dark' or 'light'
+ -- Use :noautocmd to suppress OptionSet event; OSC11 response may arrive after VimEnter.
+ vim.cmd('noautocmd set background=' .. bg)
+ end
end
end
- end)
+ )
if not sync then
return
@@ -1133,7 +1137,7 @@ do
-- runtime theme changes (mode 2031 -> TUI re-queries -> |TermResponse|);
-- the per-response bg_user_set() guard preserves an explicit user value.
if vim.o.ttyfast then
- detect_background(false)
+ detect_background(false, ui.chan)
end
end)
end
diff --git a/runtime/lua/vim/_meta/events.lua b/runtime/lua/vim/_meta/events.lua
index 364d706bf6..a88464ac5c 100644
--- a/runtime/lua/vim/_meta/events.lua
+++ b/runtime/lua/vim/_meta/events.lua
@@ -60,4 +60,5 @@ error('Cannot require a meta file')
--- @field cursor integer[]
--- @class vim.event.termresponse.data
+--- @field chan integer
--- @field sequence string
diff --git a/runtime/lua/vim/tty.lua b/runtime/lua/vim/tty.lua
index 6771efe849..06b8eddcec 100644
--- a/runtime/lua/vim/tty.lua
+++ b/runtime/lua/vim/tty.lua
@@ -10,10 +10,11 @@ local M = {}
---
---@param payload string Sequence to send via nvim_ui_send(). Use empty string ('') to just register
--- a listener (no sending).
----@param opts? { timeout?: integer, on_timeout?: fun(), group?: integer|string }
+---@param opts? { timeout?: integer, on_timeout?: fun(), group?: integer|string, chan?: integer }
--- - `timeout` (default: 1000) ms to wait before giving up, or 0 for never (caller must remove the autocmd).
--- - `on_timeout` optional fn called when the timeout fires.
--- - `group`: augroup for the TermResponse autocmd.
+--- - `chan`: only handle responses from this channel.
---@param on_response fun(resp:string):boolean? Called for each TermResponse. Return `true` to stop listening.
---@return integer # autocmd id of the TermResponse handler.
function M.request(payload, opts, on_response)
@@ -32,7 +33,11 @@ function M.request(payload, opts, on_response)
'TermResponse',
opts.group,
{ nested = true },
+ ---@param ev {data: vim.event.termresponse.data}
function(ev)
+ if opts.chan and ev.data.chan ~= opts.chan then
+ return
+ end
local stop = on_response(ev.data.sequence)
-- If on_response is done, cancel the timeout so on_timeout doesn't fire spuriously.
if stop and timer and not timer:is_closing() then