diff options
| author | Barrett Ruth <62671086+barrettruth@users.noreply.github.com> | 2026-06-23 05:19:56 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-06-23 06:19:56 -0400 |
| commit | db30608058db402de37be7a1d33fa60b7fc7de83 (patch) | |
| tree | 20fdd838c3e8237215639b9b3d3bf99556d1a5cd /runtime/lua/vim | |
| parent | e4fbe162c193f926000bc83123e40caf8faccb14 (diff) | |
| download | rneovim-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.lua | 60 | ||||
| -rw-r--r-- | runtime/lua/vim/_meta/events.lua | 1 | ||||
| -rw-r--r-- | runtime/lua/vim/tty.lua | 7 |
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 |