diff options
author | Gregory Anders <8965202+gpanders@users.noreply.github.com> | 2023-12-06 10:55:50 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-06 10:55:50 -0800 |
commit | 08545bd45b80e3056fc8c6c4eb25e2ef1fddd897 (patch) | |
tree | 798ac51dc30ebc95617ffdbaaa4c474d70b3d226 | |
parent | ca7f8786a0eb578895400e23cd21e25cc0f91800 (diff) | |
parent | a5a346678a8211ea07f318de42e557ad3909f65e (diff) | |
download | rneovim-08545bd45b80e3056fc8c6c4eb25e2ef1fddd897.tar.gz rneovim-08545bd45b80e3056fc8c6c4eb25e2ef1fddd897.tar.bz2 rneovim-08545bd45b80e3056fc8c6c4eb25e2ef1fddd897.zip |
Merge pull request #26407 from gpanders/default-tgc
feat(defaults): enable 'termguicolors' by default when supported by terminal
-rw-r--r-- | runtime/doc/news.txt | 3 | ||||
-rw-r--r-- | runtime/doc/options.txt | 4 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/_defaults.lua | 255 | ||||
-rw-r--r-- | runtime/lua/vim/_meta.lua | 1 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/options.lua | 4 | ||||
-rw-r--r-- | runtime/lua/vim/termcap.lua | 52 | ||||
-rw-r--r-- | runtime/plugin/osc52.lua | 8 | ||||
-rw-r--r-- | src/nvim/options.lua | 4 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 47 | ||||
-rw-r--r-- | src/nvim/ui_client.c | 9 | ||||
-rw-r--r-- | test/functional/autocmd/focus_spec.lua | 11 | ||||
-rw-r--r-- | test/functional/core/main_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/helpers.lua | 3 | ||||
-rw-r--r-- | test/functional/terminal/api_spec.lua | 8 | ||||
-rw-r--r-- | test/functional/terminal/buffer_spec.lua | 12 | ||||
-rw-r--r-- | test/functional/terminal/cursor_spec.lua | 29 | ||||
-rw-r--r-- | test/functional/terminal/helpers.lua | 24 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 225 | ||||
-rw-r--r-- | test/functional/ui/output_spec.lua | 8 |
20 files changed, 494 insertions, 217 deletions
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index e38707fa76..3006287e62 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -98,6 +98,9 @@ The following changes may require adaptations in user config or plugins. • Default color scheme has been updated to be "Neovim branded" and accessible. Use `:colorscheme vim` to revert to the old legacy color scheme. +• 'termguicolors' is enabled by default when Nvim is able to determine that + the host terminal emulator supports 24-bit color. + ============================================================================== BREAKING CHANGES IN HEAD *news-breaking-dev* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index f47093782c..355c8cc99a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6495,6 +6495,10 @@ A jump table for the options with a short description can be found at |Q_op|. attributes instead of "cterm" attributes. |guifg| Requires an ISO-8613-3 compatible terminal. + Nvim will automatically attempt to determine if the host terminal + supports 24-bit color and will enable this option if it does + (unless explicitly disabled by the user). + *'termpastefilter'* *'tpf'* 'termpastefilter' 'tpf' string (default "BS,HT,ESC,DEL") global diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index f6dfe3b14a..220505f573 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -75,6 +75,8 @@ Defaults *nvim-defaults* - 'switchbuf' defaults to "uselast" - 'tabpagemax' defaults to 50 - 'tags' defaults to "./tags;,tags" +- 'termguicolors' is enabled by default if Nvim can detect support from the + host terminal - 'ttimeoutlen' defaults to 50 - 'ttyfast' is always set - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index c3bb36fc36..b73681be04 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -165,91 +165,92 @@ do }) end ---- Guess value of 'background' based on terminal color. ---- ---- We write Operating System Command (OSC) 11 to the terminal to request the ---- terminal's background color. We then wait for a response. If the response ---- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then ---- compute the luminance[1] of the RGB color and classify it as light/dark ---- accordingly. Note that the color components may have anywhere from one to ---- four hex digits, and require scaling accordingly as values out of 4, 8, 12, ---- or 16 bits. Also note the A(lpha) component is optional, and is parsed but ---- ignored in the calculations. ---- ---- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 -do - --- Parse a string of hex characters as a color. - --- - --- The string can contain 1 to 4 hex characters. The returned value is - --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. - --- - --- For instance, if only a single hex char "a" is used, then this function - --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / - --- 256). - --- - --- @param c string Color as a string of hex chars - --- @return number? Intensity of the color - local function parsecolor(c) - if #c == 0 or #c > 4 then - return nil - end - - local val = tonumber(c, 16) - if not val then - return nil - end - - local max = tonumber(string.rep('f', #c), 16) - return val / max +-- Only do the following when the TUI is attached +local tty = nil +for _, ui in ipairs(vim.api.nvim_list_uis()) do + if ui.chan == 1 and ui.stdout_tty then + tty = ui + break end +end - --- Parse an OSC 11 response - --- - --- Either of the two formats below are accepted: - --- - --- OSC 11 ; rgb:<red>/<green>/<blue> - --- - --- or - --- - --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> +if tty then + --- Guess value of 'background' based on terminal color. --- - --- where + --- We write Operating System Command (OSC) 11 to the terminal to request the + --- terminal's background color. We then wait for a response. If the response + --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then + --- compute the luminance[1] of the RGB color and classify it as light/dark + --- accordingly. Note that the color components may have anywhere from one to + --- four hex digits, and require scaling accordingly as values out of 4, 8, 12, + --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but + --- ignored in the calculations. --- - --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh - --- - --- The alpha component is ignored, if present. - --- - --- @param resp string OSC 11 response - --- @return string? Red component - --- @return string? Green component - --- @return string? Blue component - local function parseosc11(resp) - local r, g, b - r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') - if not r and not g and not b then - local a - r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') - if not a or #a > 4 then - return nil, nil, nil + --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 + do + --- Parse a string of hex characters as a color. + --- + --- The string can contain 1 to 4 hex characters. The returned value is + --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. + --- + --- For instance, if only a single hex char "a" is used, then this function + --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / + --- 256). + --- + --- @param c string Color as a string of hex chars + --- @return number? Intensity of the color + local function parsecolor(c) + if #c == 0 or #c > 4 then + return nil end - end - if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then - return r, g, b + local val = tonumber(c, 16) + if not val then + return nil + end + + local max = tonumber(string.rep('f', #c), 16) + return val / max end - return nil, nil, nil - end + --- Parse an OSC 11 response + --- + --- Either of the two formats below are accepted: + --- + --- OSC 11 ; rgb:<red>/<green>/<blue> + --- + --- or + --- + --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> + --- + --- where + --- + --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh + --- + --- The alpha component is ignored, if present. + --- + --- @param resp string OSC 11 response + --- @return string? Red component + --- @return string? Green component + --- @return string? Blue component + local function parseosc11(resp) + local r, g, b + r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') + if not r and not g and not b then + local a + r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') + if not a or #a > 4 then + return nil, nil, nil + end + end + + if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then + return r, g, b + end - local tty = false - for _, ui in ipairs(vim.api.nvim_list_uis()) do - if ui.chan == 1 and ui.stdout_tty then - tty = true - break + return nil, nil, nil end - end - if tty then local timer = assert(vim.uv.new_timer()) ---@param bg string New value of the 'background' option @@ -300,7 +301,7 @@ do io.stdout:write('\027]11;?\007') timer:start(1000, 0, function() - -- No response received. Delete the autocommand + -- Delete the autocommand if no response was received vim.schedule(function() -- Suppress error if autocommand has already been deleted pcall(vim.api.nvim_del_autocmd, id) @@ -311,4 +312,108 @@ do end end) end + + --- If the TUI (term_has_truecolor) was able to determine that the host + --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the + --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's + --- response indicates that it does support truecolor enable 'termguicolors', + --- but only if the user has not already disabled it. + do + if tty.rgb then + -- The TUI was able to determine truecolor support + vim.o.termguicolors = true + else + --- Enable 'termguicolors', but only if it was not already set by the user. + local function settgc() + if not vim.api.nvim_get_option_info2('termguicolors', {}).was_set then + vim.o.termguicolors = true + end + end + + local caps = {} ---@type table<string, boolean> + require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found) + if not found then + return + end + + caps[cap] = true + if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then + settgc() + end + end) + + local timer = assert(vim.uv.new_timer()) + + -- Arbitrary colors to set in the SGR sequence + local r = 1 + local g = 2 + local b = 3 + + local id = vim.api.nvim_create_autocmd('TermResponse', { + nested = true, + callback = function(args) + local resp = args.data ---@type string + local decrqss = resp:match('^\027P1%$r([%d;:]+)m$') + + if decrqss then + -- The DECRQSS SGR response first contains attributes separated by + -- semicolons, followed by the SGR itself with parameters separated + -- by colons. Some terminals include "0" in the attribute list + -- unconditionally; others do not. Our SGR sequence did not set any + -- attributes, so there should be no attributes in the list. + local attrs = vim.split(decrqss, ';') + if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then + return true + end + + -- The returned SGR sequence should begin with 48:2 + local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$') + if not sgr then + return true + end + + -- The remaining elements of the SGR sequence should be the 3 colors + -- we set. Some terminals also include an additional parameter + -- (which can even be empty!), so handle those cases as well + local params = vim.split(sgr, ':') + if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then + return true + end + + if + tonumber(params[#params - 2]) == r + and tonumber(params[#params - 1]) == g + and tonumber(params[#params]) == b + then + settgc() + end + + return true + end + end, + }) + + -- Write SGR followed by DECRQSS. This sets the background color then + -- immediately asks the terminal what the background color is. If the + -- terminal responds to the DECRQSS with the same SGR sequence that we + -- sent then the terminal supports truecolor. + local decrqss = '\027P$qm\027\\' + if os.getenv('TMUX') then + decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027')) + end + io.stdout:write(string.format('\027[48;2;%d;%d;%dm%s', r, g, b, decrqss)) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + end) + + if not timer:is_closing() then + timer:close() + end + end) + end + end end diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index e3b99f6b3d..bb9ed722e2 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -20,6 +20,7 @@ vim.lsp = require('vim.lsp') vim.re = require('vim.re') vim.secure = require('vim.secure') vim.snippet = require('vim.snippet') +vim.text = require('vim.text') vim.treesitter = require('vim.treesitter') vim.ui = require('vim.ui') vim.version = require('vim.version') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index d2bdab4d28..5e65ca6b1b 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -6941,6 +6941,10 @@ vim.go.tbidi = vim.go.termbidi --- attributes instead of "cterm" attributes. `guifg` --- Requires an ISO-8613-3 compatible terminal. --- +--- Nvim will automatically attempt to determine if the host terminal +--- supports 24-bit color and will enable this option if it does +--- (unless explicitly disabled by the user). +--- --- @type boolean vim.o.termguicolors = false vim.o.tgc = vim.o.termguicolors diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua index 862cc52149..b88d9ac9ba 100644 --- a/runtime/lua/vim/termcap.lua +++ b/runtime/lua/vim/termcap.lua @@ -12,7 +12,10 @@ local M = {} --- emulator supports the XTGETTCAP sequence. --- --- @param caps string|table A terminal capability or list of capabilities to query ---- @param cb function(cap:string, seq:string) Function to call when a response is received +--- @param cb function(cap:string, found:bool, seq:string?) Callback function which is called for +--- each capability in {caps}. {found} is set to true if the capability was found or false +--- otherwise. {seq} is the control sequence for the capability if found, or nil for +--- boolean capabilities. function M.query(caps, cb) vim.validate({ caps = { caps, { 'string', 'table' } }, @@ -23,21 +26,33 @@ function M.query(caps, cb) caps = { caps } end - local count = #caps + local pending = {} ---@type table<string, boolean> + for _, v in ipairs(caps) do + pending[v] = true + end + + local timer = assert(vim.uv.new_timer()) - vim.api.nvim_create_autocmd('TermResponse', { + local id = vim.api.nvim_create_autocmd('TermResponse', { callback = function(args) local resp = args.data ---@type string - local k, v = resp:match('^\027P1%+r(%x+)=(%x+)$') - if k and v then + local k, rest = resp:match('^\027P1%+r(%x+)(.*)$') + if k and rest then local cap = vim.text.hexdecode(k) - local seq = - vim.text.hexdecode(v):gsub('\\E', '\027'):gsub('%%p%d', ''):gsub('\\(%d+)', string.char) + local seq ---@type string? + if rest:match('^=%x+$') then + seq = vim.text + .hexdecode(rest:sub(2)) + :gsub('\\E', '\027') + :gsub('%%p%d', '') + :gsub('\\(%d+)', string.char) + end + + cb(cap, true, seq) - cb(cap, seq) + pending[cap] = nil - count = count - 1 - if count == 0 then + if next(pending) == nil then return true end end @@ -57,6 +72,23 @@ function M.query(caps, cb) end io.stdout:write(query) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + + -- Call the callback for all capabilities that were not found + for k in pairs(pending) do + cb(k, false, nil) + end + end) + + if not timer:is_closing() then + timer:close() + end + end) end return M diff --git a/runtime/plugin/osc52.lua b/runtime/plugin/osc52.lua index 374b70066f..7a90518966 100644 --- a/runtime/plugin/osc52.lua +++ b/runtime/plugin/osc52.lua @@ -6,7 +6,11 @@ if not tty or vim.g.clipboard ~= nil or vim.o.clipboard ~= '' or not os.getenv(' return end -require('vim.termcap').query('Ms', function(cap, seq) +require('vim.termcap').query('Ms', function(cap, found, seq) + if not found then + return + end + assert(cap == 'Ms') -- Check 'clipboard' and g:clipboard again to avoid a race condition @@ -16,7 +20,7 @@ require('vim.termcap').query('Ms', function(cap, seq) -- If the terminal reports a sequence other than OSC 52 for the Ms capability -- then ignore it. We only support OSC 52 (for now) - if not seq:match('^\027%]52') then + if not seq or not seq:match('^\027%]52') then return end diff --git a/src/nvim/options.lua b/src/nvim/options.lua index daaf73d241..50371b8bf3 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -8764,6 +8764,10 @@ return { Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight| attributes instead of "cterm" attributes. |guifg| Requires an ISO-8613-3 compatible terminal. + + Nvim will automatically attempt to determine if the host terminal + supports 24-bit color and will enable this option if it does + (unless explicitly disabled by the user). ]=], full_name = 'termguicolors', redraw = { 'ui_option' }, diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index c71eb633e9..d625c22c76 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -148,7 +148,8 @@ static bool cursor_style_enabled = false; # include "tui/tui.c.generated.h" #endif -void tui_start(TUIData **tui_p, int *width, int *height, char **term) +void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) + FUNC_ATTR_NONNULL_ALL { TUIData *tui = xcalloc(1, sizeof(TUIData)); tui->is_starting = true; @@ -177,6 +178,7 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term) *width = tui->width; *height = tui->height; *term = tui->term; + *rgb = tui->rgb; } void tui_set_key_encoding(TUIData *tui) @@ -334,6 +336,9 @@ static void terminfo_start(TUIData *tui) int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) : (konsole ? 1 : 0); + // truecolor support must be checked before patching/augmenting terminfo + tui->rgb = term_has_truecolor(tui, colorterm); + patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm); augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm); tui->can_change_scroll_region = @@ -1439,7 +1444,7 @@ void tui_suspend(TUIData *tui) tui_mouse_on(tui); } stream_set_blocking(tui->input.in_fd, false); // libuv expects this - ui_client_attach(tui->width, tui->height, tui->term); + ui_client_attach(tui->width, tui->height, tui->term, tui->rgb); #endif } @@ -1752,6 +1757,44 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) return -1; } +/// Determine if the terminal supports truecolor or not: +/// +/// 1. If $COLORTERM is "24bit" or "truecolor", return true +/// 2. Else, check terminfo for Tc, RGB, setrgbf, or setrgbb capabilities. If +/// found, return true +/// 3. Else, return false +static bool term_has_truecolor(TUIData *tui, const char *colorterm) +{ + // Check $COLORTERM + if (strequal(colorterm, "truecolor") || strequal(colorterm, "24bit")) { + return true; + } + + // Check for Tc and RGB + for (size_t i = 0; i < unibi_count_ext_bool(tui->ut); i++) { + const char *n = unibi_get_ext_bool_name(tui->ut, i); + if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) { + return true; + } + } + + // Check for setrgbf and setrgbb + bool setrgbf = false; + bool setrgbb = false; + for (size_t i = 0; i < unibi_count_ext_str(tui->ut) && (!setrgbf || !setrgbb); i++) { + const char *n = unibi_get_ext_str_name(tui->ut, i); + if (n) { + if (!setrgbf && !strcmp(n, "setrgbf")) { + setrgbf = true; + } else if (!setrgbb && !strcmp(n, "setrgbb")) { + setrgbb = true; + } + } + } + + return setrgbf && setrgbb; +} + /// Patches the terminfo records after loading from system or built-in db. /// Several entries in terminfo are known to be deficient or outright wrong; /// and several terminal emulators falsely announce incorrect terminal types. diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index eb32c16881..d744560a86 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -70,14 +70,14 @@ uint64_t ui_client_start_server(int argc, char **argv) return channel->id; } -void ui_client_attach(int width, int height, char *term) +void ui_client_attach(int width, int height, char *term, bool rgb) { MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ(width)); ADD_C(args, INTEGER_OBJ(height)); MAXSIZE_TEMP_DICT(opts, 9); - PUT_C(opts, "rgb", BOOLEAN_OBJ(true)); + PUT_C(opts, "rgb", BOOLEAN_OBJ(rgb)); PUT_C(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT_C(opts, "ext_termcolors", BOOLEAN_OBJ(true)); if (term) { @@ -111,9 +111,10 @@ void ui_client_run(bool remote_ui) ui_client_is_remote = remote_ui; int width, height; char *term; - tui_start(&tui, &width, &height, &term); + bool rgb; + tui_start(&tui, &width, &height, &term, &rgb); - ui_client_attach(width, height, term); + ui_client_attach(width, height, term, rgb); // os_exit() will be invoked when the client channel detaches while (true) { diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua index c72842f14b..204bfcaa12 100644 --- a/test/functional/autocmd/focus_spec.lua +++ b/test/functional/autocmd/focus_spec.lua @@ -2,7 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local luv = require('luv') local clear = helpers.clear -local nvim_prog = helpers.nvim_prog local feed_command = helpers.feed_command local feed_data = thelpers.feed_data @@ -14,10 +13,12 @@ describe('autoread TUI FocusGained/FocusLost', function() before_each(function() clear() - screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "set noswapfile noshowcmd noruler"]' - ) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + }) end) teardown(function() diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index d705d56575..c8d800f89a 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -62,7 +62,7 @@ describe('command-line option', function() screen:attach() local args = { nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', - '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix"', + '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix notermguicolors"', '-s', '-' } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b50df4c677..5780d0a978 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -30,7 +30,8 @@ module.nvim_prog = ( module.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent startofline' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid') + ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid' + ..' notermguicolors') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', -- XXX: find treesitter parsers. diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index c278b2ad0e..68082ba4fa 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -11,8 +11,12 @@ describe('api', function() before_each(function() helpers.clear() os.remove(socket_name) - screen = child_session.screen_setup(0, '["'..helpers.nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "'..helpers.nvim_set..'"]') + screen = child_session.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', helpers.nvim_set, + }) end) after_each(function() os.remove(socket_name) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 423e7bdf21..b92f1b1592 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -17,7 +17,6 @@ local sleep = helpers.sleep local funcs = helpers.funcs local is_os = helpers.is_os local skip = helpers.skip -local nvim_prog = helpers.nvim_prog describe(':terminal buffer', function() local screen @@ -446,10 +445,13 @@ end) describe('terminal input', function() it('sends various special keys with modifiers', function() clear() - local screen = thelpers.screen_setup( - 0, - string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--cmd", 'colorscheme vim', "--cmd", "startinsert"]]=], nvim_prog) - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set notermguicolors', + '--cmd', 'startinsert', + }) screen:expect{grid=[[ {1: } | {4:~ }| diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 95081b7189..37bb0ee817 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -3,7 +3,6 @@ local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local testprg, command = helpers.testprg, helpers.command -local nvim_prog = helpers.nvim_prog local eq, eval = helpers.eq, helpers.eval local matches = helpers.matches local poke_eventloop = helpers.poke_eventloop @@ -184,10 +183,18 @@ describe('buffer cursor position is correct in terminal without number column', local screen local function setup_ex_register(str) - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '-E', + '--cmd', string.format('let @r = "%s"', str), -- <Left> and <Right> don't always work - ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + '--cmd', 'cnoremap <C-X> <Left>', + '--cmd', 'cnoremap <C-O> <Right>', + '--cmd', 'set notermguicolors', + }, { + cols = 70, + }) screen:set_default_attr_ids({ [1] = {foreground = 253, background = 11}; [3] = {bold = true}, @@ -570,10 +577,18 @@ describe('buffer cursor position is correct in terminal with number column', fun local screen local function setup_ex_register(str) - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '-E', + '--cmd', string.format('let @r = "%s"', str), -- <Left> and <Right> don't always work - ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + '--cmd', 'cnoremap <C-X> <Left>', + '--cmd', 'cnoremap <C-O> <Right>', + '--cmd', 'set notermguicolors', + }, { + cols = 70, + }) screen:set_default_attr_ids({ [1] = {foreground = 253, background = 11}; [3] = {bold = true}, diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 62d3dd67a3..4ae054daa8 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -6,6 +6,7 @@ local Screen = require('test.functional.ui.screen') local testprg = helpers.testprg local exec_lua = helpers.exec_lua local nvim = helpers.nvim +local nvim_prog = helpers.nvim_prog local function feed_data(data) if type(data) == 'table' then @@ -122,6 +123,26 @@ local function screen_setup(extra_rows, command, cols, opts) return screen end +local function setup_child_nvim(args, opts) + opts = opts or {} + + local argv = { nvim_prog, unpack(args) } + local cmd = string.format('[%s]', vim.iter(argv):map(function(s) + return string.format('\'%s\'', s) + end):join(', ')) + + if opts.env then + local s = {} + for k, v in pairs(opts.env) do + table.insert(s, string.format('%s: \'%s\'', k, v)) + end + + cmd = string.format('%s, #{env: #{%s}}', cmd, table.concat(s, ', ')) + end + + return screen_setup(0, cmd, opts.cols) +end + return { feed_data = feed_data, feed_termcode = feed_termcode, @@ -141,5 +162,6 @@ return { clear_attrs = clear_attrs, enable_mouse = enable_mouse, disable_mouse = disable_mouse, - screen_setup = screen_setup + screen_setup = screen_setup, + setup_child_nvim = setup_child_nvim, } diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3748f820d4..0c984069b3 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -39,13 +39,13 @@ describe('TUI', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.screen_setup(0, - string.format( - [=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark", "--cmd", "colorscheme vim"]]=], - nvim_prog, - child_server, - nvim_set - )) + screen = thelpers.setup_child_nvim({ + '--listen', child_server, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + '--cmd', 'colorscheme vim' + }) screen:expect([[ {1: } | {4:~ }| @@ -1830,7 +1830,7 @@ describe('TUI', function() }) screen:attach() exec([[ - call termopen([v:progpath, '--clean', '--cmd', 'colorscheme vim', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) + call termopen([v:progpath, '--clean', '--cmd', 'set notermguicolors', '--cmd', 'colorscheme vim', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) sleep 500m vs new ]]) @@ -1856,30 +1856,29 @@ describe('TUI', function() write_file(script_file, [=[ local ffi = require('ffi') ffi.cdef([[int execl(const char *, const char *, ...);]]) - ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean', '--cmd', 'colorscheme vim') + ffi.C.execl(vim.v.progpath, 'Xargv0nvim', '--clean') ]=]) finally(function() os.remove(script_file) end) - local screen = thelpers.screen_setup(0, string.format([=[["%s", "--clean", "-l", "%s"]]=], - nvim_prog, script_file)) + local screen = thelpers.setup_child_nvim({'--clean', '-l', script_file}) screen:expect{grid=[[ {1: } | - {4:~ }| - {4:~ }| - {4:~ }| - {5:[No Name] 0,0-1 All}| + ~ | + ~ | + ~ | + [No Name] 0,0-1 All| | {3:-- TERMINAL --} | ]]} feed_data(':put =v:argv + [v:progname]\n') screen:expect{grid=[[ + Xargv0nvim | + --embed | --clean | - --cmd | - colorscheme vim | {1:X}argv0nvim | - {5:[No Name] [+] 7,1 Bot}| - 6 more lines | + [No Name] [+] 5,1 Bot| + 4 more lines | {3:-- TERMINAL --} | ]]} end) @@ -1903,11 +1902,13 @@ describe('TUI', function() end) it('<C-h> #10134', function() - local screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ]] - ..[["--cmd", "set noruler", "--cmd", ':nnoremap <C-h> :echomsg "\<C-h\>"<CR>']]..']' - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noruler notermguicolors', + '--cmd', ':nnoremap <C-h> :echomsg "\\<C-h\\>"<CR>', + }) screen:expect{grid=[[ {1: } | {4:~ }| @@ -1931,12 +1932,13 @@ describe('TUI', function() end) it('draws line with many trailing spaces correctly #24955', function() - local screen = thelpers.screen_setup( - 0, - '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim"]] - ..[[, "--cmd", "call setline(1, ['1st line' .. repeat(' ', 153), '2nd line'])"]]..']', - 80 - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'set notermguicolors', + '--cmd', 'colorscheme vim', + '--cmd', 'call setline(1, ["1st line" .. repeat(" ", 153), "2nd line"])', + }, { cols = 80 }) screen:expect{grid=[[ {1:1}st line | | @@ -1962,16 +1964,16 @@ end) describe('TUI UIEnter/UILeave', function() it('fires exactly once, after VimEnter', function() clear() - local screen = thelpers.screen_setup(0, - '["'..nvim_prog..'", "-u", "NONE", "-i", "NONE"' - ..[[, "--cmd", "colorscheme vim"]] - ..[[, "--cmd", "set noswapfile noshowcmd noruler"]] - ..[[, "--cmd", "let g:evs = []"]] - ..[[, "--cmd", "autocmd UIEnter * :call add(g:evs, 'UIEnter')"]] - ..[[, "--cmd", "autocmd UILeave * :call add(g:evs, 'UILeave')"]] - ..[[, "--cmd", "autocmd VimEnter * :call add(g:evs, 'VimEnter')"]] - ..']' - ) + local screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + '--cmd', 'let g:evs = []', + '--cmd', 'autocmd UIEnter * :call add(g:evs, "UIEnter")', + '--cmd', 'autocmd UILeave * :call add(g:evs, "UILeave")', + '--cmd', 'autocmd VimEnter * :call add(g:evs, "VimEnter")', + }) screen:expect{grid=[[ {1: } | {4:~ }| @@ -2001,11 +2003,14 @@ describe('TUI FocusGained/FocusLost', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "set noswapfile noshowcmd noruler"]', - nvim_prog, child_server)) + screen = thelpers.setup_child_nvim({ + '--listen', child_server, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', 'set noswapfile noshowcmd noruler notermguicolors', + }) + screen:expect([[ {1: } | {4:~ }| @@ -2208,14 +2213,18 @@ describe("TUI 't_Co' (terminal colors)", function() local function assert_term_colors(term, colorterm, maxcolors) clear({env={TERM=term}, args={}}) - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - screen = thelpers.screen_setup(0, string.format( - [=[['sh', '-c', 'LANG=C TERM=%s %s %s -u NONE -i NONE --cmd "colorscheme vim" --cmd "%s"']]=], - term or "", - (colorterm ~= nil and "COLORTERM="..colorterm or ""), - nvim_prog, - nvim_set)) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', nvim_set, + }, { + env = { + LANG = 'C', + TERM = term or '', + COLORTERM = colorterm or '', + }, + }) local tline if maxcolors == 8 then @@ -2483,14 +2492,16 @@ describe("TUI 'term' option", function() local function assert_term(term_envvar, term_expected) clear() - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - local cmd = string.format( - [=[['sh', '-c', 'LANG=C TERM=%s %s -u NONE -i NONE --cmd "%s"']]=], - term_envvar or "", - nvim_prog, - nvim_set) - screen = thelpers.screen_setup(0, cmd) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', nvim_set, + }, { + env = { + LANG = 'C', + TERM = term_envvar or '', + }, + }) local full_timeout = screen.timeout screen.timeout = 250 -- We want screen:expect() to fail quickly. @@ -2540,14 +2551,17 @@ describe("TUI", function() -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI. local function nvim_tui(extra_args) clear() - -- This is ugly because :term/termopen() forces TERM=xterm-256color. - -- TODO: Revisit this after jobstart/termopen accept `env` dict. - local cmd = string.format( - [=[['sh', '-c', 'LANG=C %s -u NONE -i NONE %s --cmd "colorscheme vim" --cmd "%s"']]=], - nvim_prog, - extra_args or "", - nvim_set) - screen = thelpers.screen_setup(0, cmd) + screen = thelpers.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', nvim_set, + extra_args, + }, { + env = { + LANG = 'C', + }, + }) end it('-V3log logs terminfo values', function() @@ -2604,9 +2618,18 @@ describe('TUI bg color', function() local function setup_bg_test() clear() - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "set noswapfile", ' - ..'"-c", "autocmd OptionSet background echo \\"did OptionSet, yay!\\""]') + screen = thelpers.setup_child_nvim({ + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + 'colorscheme vim', + '--cmd', + 'set noswapfile notermguicolors', + '-c', + 'autocmd OptionSet background echo "did OptionSet, yay!"', + }) end before_each(setup_bg_test) @@ -2725,14 +2748,13 @@ describe("TUI as a client", function() set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "%s laststatus=2 background=dark"]', - nvim_prog, - server_pipe, - nvim_set - )) + local screen_server = thelpers.setup_child_nvim({ + '--listen', server_pipe, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + }) feed_data("iHello, World") screen_server:expect{grid=[[ @@ -2756,9 +2778,10 @@ describe("TUI as a client", function() ]]} set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Hello, Worl{1:d} | @@ -2798,9 +2821,10 @@ describe("TUI as a client", function() server:request('nvim_input', 'iHalloj!<Esc>') set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Halloj{1:!} | @@ -2836,9 +2860,10 @@ describe("TUI as a client", function() it("throws error when no server exists", function() clear() - local screen = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "127.0.0.1:2436546", "--remote-ui"]]=], - nvim_prog), 60) + local screen = thelpers.setup_child_nvim({ + '--server', '127.0.0.1:2436546', + '--remote-ui', + }, { cols = 60 }) screen:expect([[ Remote ui failed to start: {MATCH:.*}| @@ -2857,14 +2882,13 @@ describe("TUI as a client", function() set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.screen_setup(0, - string.format( - '["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", ' - ..'"--cmd", "%s laststatus=2 background=dark"]', - nvim_prog, - server_pipe, - nvim_set - )) + local screen_server = thelpers.setup_child_nvim({ + '--listen', server_pipe, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', string.format('%s laststatus=2 background=dark', nvim_set), + }) screen_server:expect{grid=[[ {1: } | {4:~ }| @@ -2897,9 +2921,10 @@ describe("TUI as a client", function() ]]} set_session(client_super) - local screen_client = thelpers.screen_setup(0, - string.format([=[["%s", "--server", "%s", "--remote-ui"]]=], - nvim_prog, server_pipe)) + local screen_client = thelpers.setup_child_nvim({ + '--server', server_pipe, + '--remote-ui', + }) screen_client:expect{grid=[[ Hello, Worl{1:d} | diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 1dbbe76bac..719634aa2f 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -21,8 +21,12 @@ describe("shell command :!", function() local screen before_each(function() clear() - screen = child_session.screen_setup(0, '["'..helpers.nvim_prog.. - '", "-u", "NONE", "-i", "NONE", "--cmd", "colorscheme vim", "--cmd", "'..helpers.nvim_set..'"]') + screen = child_session.setup_child_nvim({ + '-u', 'NONE', + '-i', 'NONE', + '--cmd', 'colorscheme vim', + '--cmd', helpers.nvim_set, + }) screen:expect([[ {1: } | {4:~ }| |