mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
feat(terminal): respond to OSC background and foreground request (#17197)
The motivation for this update is Issue #15365, where background=light is not properly set for Nvim running from an Nvim :terminal. This can be encountered when e.g., opening a terminal to make git commits, which opens EDITOR=nvim in the nested terminal. Under the implementation of this commit, the OSC response always indicates a black or white foreground/background. While this may not reflect the actual foreground/background color, it permits 'background' to be retained for a nested Nvim instance running in the terminal emulator. The behaviour matches Vim.
This commit is contained in:
parent
9c202b9392
commit
7589336120
@ -287,6 +287,8 @@ The following new APIs and features were added.
|
||||
• Terminal buffers emit a |TermRequest| autocommand event when the child
|
||||
process emits an OSC or DCS control sequence.
|
||||
|
||||
• Terminal buffers respond to OSC background and foreground requests. |default-autocmds|
|
||||
|
||||
==============================================================================
|
||||
CHANGED FEATURES *news-changed*
|
||||
|
||||
|
@ -145,6 +145,12 @@ nvim_terminal:
|
||||
- BufReadCmd: Treats "term://" buffers as |terminal| buffers. |terminal-start|
|
||||
- TermClose: A |terminal| buffer started with no arguments (which thus uses
|
||||
'shell') and which exits with no error is closed automatically.
|
||||
- TermRequest: The terminal emulator responds to OSC background and foreground
|
||||
requests, indicating (1) a black background and white foreground when Nvim
|
||||
option 'background' is "dark" or (2) a white background and black foreground
|
||||
when 'background' is "light". While this may not reflect the actual
|
||||
foreground/background color, it permits 'background' to be retained for a
|
||||
nested Nvim instance running in the terminal emulator.
|
||||
|
||||
nvim_cmdwin:
|
||||
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|.
|
||||
|
@ -155,6 +155,30 @@ do
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd('TermRequest', {
|
||||
group = nvim_terminal_augroup,
|
||||
desc = 'Respond to OSC foreground/background color requests',
|
||||
callback = function(args)
|
||||
local fg_request = args.data == '\027]10;?'
|
||||
local bg_request = args.data == '\027]11;?'
|
||||
if fg_request or bg_request then
|
||||
-- WARN: This does not return the actual foreground/background color,
|
||||
-- but rather returns:
|
||||
-- - fg=white/bg=black when Nvim option 'background' is 'dark'
|
||||
-- - fg=black/bg=white when Nvim option 'background' is 'light'
|
||||
local red, green, blue = 0, 0, 0
|
||||
local bg_option_dark = vim.o.background == 'dark'
|
||||
if (fg_request and bg_option_dark) or (bg_request and not bg_option_dark) then
|
||||
red, green, blue = 65535, 65535, 65535
|
||||
end
|
||||
local command = fg_request and 10 or 11
|
||||
local data = string.format('\027]%d;rgb:%04x/%04x/%04x\007', command, red, green, blue)
|
||||
local channel = vim.bo[args.buf].channel
|
||||
vim.api.nvim_chan_send(channel, data)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd('CmdwinEnter', {
|
||||
pattern = '[:>]',
|
||||
desc = 'Limit syntax sync to maxlines=1 in the command window',
|
||||
|
@ -1369,12 +1369,19 @@ describe('inccommand on ex mode', function()
|
||||
local screen
|
||||
screen = Screen.new(60, 10)
|
||||
screen:attach()
|
||||
local id = fn.termopen(
|
||||
{ nvim_prog, '-u', 'NONE', '-c', 'set termguicolors', '-E', 'test/README.md' },
|
||||
{
|
||||
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
|
||||
}
|
||||
)
|
||||
local id = fn.termopen({
|
||||
nvim_prog,
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'-c',
|
||||
'set termguicolors background=dark',
|
||||
'-E',
|
||||
'test/README.md',
|
||||
}, {
|
||||
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
|
||||
})
|
||||
fn.chansend(id, '%s/N')
|
||||
screen:expect {
|
||||
grid = [[
|
||||
|
@ -8,6 +8,7 @@ local helpers = require('test.functional.helpers')(after_each)
|
||||
local thelpers = require('test.functional.terminal.helpers')
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
local eq = helpers.eq
|
||||
local feed_command = helpers.feed_command
|
||||
local feed_data = thelpers.feed_data
|
||||
local clear = helpers.clear
|
||||
local command = helpers.command
|
||||
@ -2126,7 +2127,7 @@ describe('TUI FocusGained/FocusLost', function()
|
||||
'--cmd',
|
||||
'colorscheme vim',
|
||||
'--cmd',
|
||||
'set noswapfile noshowcmd noruler notermguicolors',
|
||||
'set noswapfile noshowcmd noruler notermguicolors background=dark',
|
||||
})
|
||||
|
||||
screen:expect([[
|
||||
@ -2776,10 +2777,73 @@ describe('TUI', function()
|
||||
end)
|
||||
|
||||
describe('TUI bg color', function()
|
||||
local screen
|
||||
before_each(clear)
|
||||
|
||||
local function setup_bg_test()
|
||||
clear()
|
||||
local attr_ids = {
|
||||
[1] = { reverse = true },
|
||||
[2] = { bold = true },
|
||||
[3] = { reverse = true, bold = true },
|
||||
[4] = { foreground = tonumber('0x00000a') },
|
||||
}
|
||||
|
||||
it('is properly set in a nested Nvim instance when background=dark', function()
|
||||
command('highlight clear Normal')
|
||||
command('set background=dark') -- set outer Nvim background
|
||||
local screen = thelpers.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'--cmd',
|
||||
'colorscheme vim',
|
||||
'--cmd',
|
||||
'set noswapfile',
|
||||
})
|
||||
screen:set_default_attr_ids(attr_ids)
|
||||
retry(nil, 30000, function() -- wait for automatic background processing
|
||||
screen:sleep(20)
|
||||
feed_command('set background?') -- check nested Nvim background
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{2:~} |
|
||||
{2:~} |
|
||||
{2:~} |
|
||||
{3:[No Name] 0,0-1 All}|
|
||||
background=dark |
|
||||
{4:-- TERMINAL --} |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
it('is properly set in a nested Nvim instance when background=light', function()
|
||||
command('highlight clear Normal')
|
||||
command('set background=light') -- set outer Nvim background
|
||||
local screen = thelpers.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'--cmd',
|
||||
'colorscheme vim',
|
||||
'--cmd',
|
||||
'set noswapfile',
|
||||
})
|
||||
retry(nil, 30000, function() -- wait for automatic background processing
|
||||
screen:sleep(20)
|
||||
feed_command('set background?') -- check nested Nvim background
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{3:~} |
|
||||
{3:~} |
|
||||
{3:~} |
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
background=light |
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
it('queries the terminal for background color', function()
|
||||
exec_lua([[
|
||||
vim.api.nvim_create_autocmd('TermRequest', {
|
||||
callback = function(args)
|
||||
@ -2791,8 +2855,7 @@ describe('TUI bg color', function()
|
||||
end,
|
||||
})
|
||||
]])
|
||||
|
||||
screen = thelpers.setup_child_nvim({
|
||||
thelpers.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
@ -2800,109 +2863,38 @@ describe('TUI bg color', function()
|
||||
'--cmd',
|
||||
'colorscheme vim',
|
||||
'--cmd',
|
||||
'set noswapfile notermguicolors',
|
||||
'-c',
|
||||
'autocmd OptionSet background echo "did OptionSet, yay!"',
|
||||
'set noswapfile',
|
||||
})
|
||||
end
|
||||
|
||||
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([[
|
||||
it('triggers OptionSet from automatic background processing', function()
|
||||
local screen = thelpers.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'--cmd',
|
||||
'colorscheme vim',
|
||||
'--cmd',
|
||||
'set noswapfile',
|
||||
'-c',
|
||||
'autocmd OptionSet background echo "did OptionSet, yay!"',
|
||||
})
|
||||
retry(nil, 30000, function() -- wait for automatic background processing
|
||||
screen:sleep(20)
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{3:~} |
|
||||
{3:~} |
|
||||
{3:~} |
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
did OptionSet, yay! |
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
feed_data('\027]11;rgb:ffff/ffff/ffff\027\\')
|
||||
screen:expect { any = 'did OptionSet, yay!' }
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=light' }
|
||||
|
||||
setup_bg_test()
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
feed_data('\027]11;rgba:ffff/ffff/ffff/8000\027\\')
|
||||
screen:expect { any = 'did OptionSet, yay!' }
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=light' }
|
||||
end)
|
||||
|
||||
it('triggers OptionSet event with split terminal-response', function()
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
-- Send a background response with the OSC command part split.
|
||||
feed_data('\027]11;rgb')
|
||||
feed_data(':ffff/ffff/ffff\027\\')
|
||||
screen:expect { any = 'did OptionSet, yay!' }
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=light' }
|
||||
|
||||
setup_bg_test()
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
-- Send a background response with the Pt portion split.
|
||||
feed_data('\027]11;rgba:ffff/fff')
|
||||
feed_data('f/ffff/8000\027\\')
|
||||
screen:expect { any = 'did OptionSet, yay!' }
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=light' }
|
||||
end)
|
||||
|
||||
it('not triggers OptionSet event with invalid terminal-response', function()
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
feed_data('\027]11;rgb:ffff/ffff/ffff/8000\027\\')
|
||||
screen:expect_unchanged()
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=dark' }
|
||||
|
||||
setup_bg_test()
|
||||
screen:expect([[
|
||||
{1: } |
|
||||
{4:~ }|*3
|
||||
{5:[No Name] 0,0-1 All}|
|
||||
|
|
||||
{3:-- TERMINAL --} |
|
||||
]])
|
||||
feed_data('\027]11;rgba:ffff/foo/ffff/8000\027\\')
|
||||
screen:expect_unchanged()
|
||||
|
||||
feed_data(':echo "new_bg=".&background\n')
|
||||
screen:expect { any = 'new_bg=dark' }
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user