diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-11-26 23:15:17 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2017-11-27 09:45:32 +0100 |
commit | 944e3c06193f6d10baa9ba3021e01626337dd884 (patch) | |
tree | 949c05f0259f1d746f68b932139b4573225e179b | |
parent | a043899ba255524b7421579b9bd6112801f09247 (diff) | |
download | rneovim-944e3c06193f6d10baa9ba3021e01626337dd884.tar.gz rneovim-944e3c06193f6d10baa9ba3021e01626337dd884.tar.bz2 rneovim-944e3c06193f6d10baa9ba3021e01626337dd884.zip |
tui: expose terminal type in 'term' option
Since "builtin" terminfo definitions were implemented (7cbf52db1bdf),
the decisions made by tui.c and terminfo.c are more relevant. Exposing
that decision in the 'term' option helps with troubleshooting.
Also: remove code that allowed setting t_Co. `:set t_Co=…` has never
worked; the highlight_spec test asserting that nvim_set_option('t_Co')
_does_ work makes no sense, and should not have worked.
-rw-r--r-- | runtime/doc/vim_diff.txt | 8 | ||||
-rw-r--r-- | src/nvim/option.c | 73 | ||||
-rw-r--r-- | src/nvim/tui/terminfo.c | 28 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 22 | ||||
-rw-r--r-- | test/functional/helpers.lua | 10 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 49 | ||||
-rw-r--r-- | test/functional/ui/highlight_spec.lua | 2 |
7 files changed, 147 insertions, 45 deletions
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 026ff6a0fb..c8155f7a68 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -333,9 +333,11 @@ terminal capabilities. Instead Nvim treats the terminal as any other UI. For example, 'guicursor' sets the terminal cursor style if possible. *'term'* *E529* *E530* *E531* -The 'term' option has a fixed value, present only for script compatibility and -intentionally not the same as any known terminal type name. It should be a -rare case in Nvim where one needs |term-dependent-settings|. +'term' reflects the terminal type derived from |$TERM| and other environment +checks. For debugging only; not reliable during startup. > + :echo &term +"builtin_x" means one of the |builtin-terms| was chosen, because the expected +terminfo file was not found on the system. *termcap* Nvim never uses the termcap database, only |terminfo| and |builtin-terms|. diff --git a/src/nvim/option.c b/src/nvim/option.c index 65ab7a54a6..07114c9507 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -105,6 +105,9 @@ typedef enum { */ #define VAR_WIN ((char_u *)-1) +static char *p_term = NULL; +static char *p_ttytype = NULL; + /* * These are the global values for options which are also local to a buffer. * Only to be used in option.c! @@ -4530,13 +4533,17 @@ int findoption_len(const char *const arg, const size_t len) bool is_tty_option(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; + return (name[0] == 't' && name[1] == '_') + || strequal(name, "term") + || strequal(name, "ttytype"); } #define TCO_BUFFER_SIZE 8 +/// @param name TUI-related option +/// @param[out,allocated] value option string value bool get_tty_option(char *name, char **value) { - if (!strcmp(name, "t_Co")) { + if (strequal(name, "t_Co")) { if (value) { if (t_colors <= 1) { *value = xstrdup(""); @@ -4548,9 +4555,16 @@ bool get_tty_option(char *name, char **value) return true; } - if (!strcmp(name, "term") || !strcmp(name, "ttytype")) { + if (strequal(name, "term")) { if (value) { - *value = xstrdup("nvim"); + *value = p_term ? xstrdup(p_term) : xstrdup("nvim"); + } + return true; + } + + if (strequal(name, "ttytype")) { + if (value) { + *value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim"); } return true; } @@ -4566,25 +4580,25 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(const char *name, const char *value) +bool set_tty_option(const char *name, char *value) { - if (!strcmp(name, "t_Co")) { - int colors = atoi(value); - - // Only reinitialize colors if t_Co value has really changed to - // avoid expensive reload of colorscheme if t_Co is set to the - // same value multiple times - if (colors != t_colors) { - t_colors = colors; - // We now have a different color setup, initialize it again. - init_highlight(true, false); + if (strequal(name, "term")) { + if (p_term) { + xfree(p_term); } + p_term = value; + return true; + } + if (strequal(name, "ttytype")) { + if (p_ttytype) { + xfree(p_ttytype); + } + p_ttytype = value; return true; } - return (is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype")); + return false; } /// Find index for an option @@ -4597,18 +4611,15 @@ static int findoption(const char *const arg) return findoption_len(arg, strlen(arg)); } -/* - * Get the value for an option. - * - * Returns: - * Number or Toggle option: 1, *numval gets value. - * String option: 0, *stringval gets allocated string. - * Hidden Number or Toggle option: -1. - * hidden String option: -2. - * unknown option: -3. - */ -int -get_option_value ( +/// Gets the value for an option. +/// +/// @returns: +/// Number or Toggle option: 1, *numval gets value. +/// String option: 0, *stringval gets allocated string. +/// Hidden Number or Toggle option: -1. +/// hidden String option: -2. +/// unknown option: -3. +int get_option_value ( char_u *name, long *numval, char_u **stringval, /* NULL when only checking existence */ @@ -4791,8 +4802,8 @@ char *set_option_value(const char *const name, const long number, const char *const string, const int opt_flags) FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option(name, string)) { - return NULL; + if (is_tty_option(name)) { + return NULL; // Fail silently; many old vimrcs set t_xx options. } int opt_idx; diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 75e9a2d8da..fdc33f0a77 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -9,6 +9,7 @@ #include <unibilium.h> #include "nvim/log.h" +#include "nvim/memory.h" #include "nvim/tui/terminfo.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -94,51 +95,72 @@ bool terminfo_is_term_family(const char *term, const char *family) /// Loads a built-in terminfo db when we (unibilium) failed to load a terminfo /// record from the environment (termcap systems, unrecognized $TERM, …). /// We do not attempt to detect xterm pretenders here. -static unibi_term *terminfo_builtin(const char *term) +/// +/// @param term $TERM value +/// @param[out,allocated] termname decided builtin 'term' name +/// @return [allocated] terminfo structure +static unibi_term *terminfo_builtin(const char *term, char **termname) { if (terminfo_is_term_family(term, "xterm")) { + *termname = xstrdup("builtin_xterm"); return unibi_from_mem((const char *)xterm_256colour_terminfo, sizeof xterm_256colour_terminfo); } else if (terminfo_is_term_family(term, "screen")) { + *termname = xstrdup("builtin_screen"); return unibi_from_mem((const char *)screen_256colour_terminfo, sizeof screen_256colour_terminfo); } else if (terminfo_is_term_family(term, "tmux")) { + *termname = xstrdup("builtin_tmux"); return unibi_from_mem((const char *)tmux_256colour_terminfo, sizeof tmux_256colour_terminfo); } else if (terminfo_is_term_family(term, "rxvt")) { + *termname = xstrdup("builtin_rxvt"); return unibi_from_mem((const char *)rxvt_256colour_terminfo, sizeof rxvt_256colour_terminfo); } else if (terminfo_is_term_family(term, "putty")) { + *termname = xstrdup("builtin_putty"); return unibi_from_mem((const char *)putty_256colour_terminfo, sizeof putty_256colour_terminfo); } else if (terminfo_is_term_family(term, "linux")) { + *termname = xstrdup("builtin_linux"); return unibi_from_mem((const char *)linux_16colour_terminfo, sizeof linux_16colour_terminfo); } else if (terminfo_is_term_family(term, "interix")) { + *termname = xstrdup("builtin_interix"); return unibi_from_mem((const char *)interix_8colour_terminfo, sizeof interix_8colour_terminfo); } else if (terminfo_is_term_family(term, "iterm") || terminfo_is_term_family(term, "iterm2") || terminfo_is_term_family(term, "iTerm.app") || terminfo_is_term_family(term, "iTerm2.app")) { + *termname = xstrdup("builtin_iterm"); return unibi_from_mem((const char *)iterm_256colour_terminfo, sizeof iterm_256colour_terminfo); } else if (terminfo_is_term_family(term, "st")) { + *termname = xstrdup("builtin_st"); return unibi_from_mem((const char *)st_256colour_terminfo, sizeof st_256colour_terminfo); } else if (terminfo_is_term_family(term, "gnome") || terminfo_is_term_family(term, "vte")) { + *termname = xstrdup("builtin_vte"); return unibi_from_mem((const char *)vte_256colour_terminfo, sizeof vte_256colour_terminfo); } else { + *termname = xstrdup("builtin_ansi"); return unibi_from_mem((const char *)ansi_terminfo, sizeof ansi_terminfo); } } -unibi_term *terminfo_from_builtin(const char *term) +/// @param term $TERM value +/// @param[out,allocated] termname decided builtin 'term' name +/// @return [allocated] terminfo structure +unibi_term *terminfo_from_builtin(const char *term, char **termname) { - unibi_term *ut = terminfo_builtin(term); + unibi_term *ut = terminfo_builtin(term, termname); + if (*termname == NULL) { + *termname = xstrdup("builtin_?"); + } // Disable BCE by default (for built-in terminfos). #7624 // https://github.com/kovidgoyal/kitty/issues/160#issuecomment-346470545 unibi_set_bool(ut, unibi_back_color_erase, false); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index c2e597c36c..732a86d0fb 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -23,6 +23,7 @@ #include "nvim/map.h" #include "nvim/main.h" #include "nvim/memory.h" +#include "nvim/option.h" #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" #include "nvim/event/loop.h" @@ -166,6 +167,13 @@ static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, return unibi_run(str, data->params, buf, len); } +static void termname_set_event(void **argv) +{ + char *termname = argv[0]; + set_tty_option("term", termname); + // Do not free termname, it is freed by set_tty_option. +} + static void terminfo_start(UI *ui) { TUIData *data = ui->data; @@ -190,12 +198,20 @@ static void terminfo_start(UI *ui) data->unibi_ext.reset_cursor_style = -1; data->out_fd = 1; data->out_isatty = os_isatty(data->out_fd); - // setup unibilium + + // Set up unibilium/terminfo. const char *term = os_getenv("TERM"); data->ut = unibi_from_env(); + char *termname = NULL; if (!data->ut) { - data->ut = terminfo_from_builtin(term); + data->ut = terminfo_from_builtin(term, &termname); + } else { + termname = xstrdup(term); } + // Update 'term' option. + loop_schedule_deferred(&main_loop, + event_create(termname_set_event, 1, termname)); + // None of the following work over SSH; see :help TERM . const char *colorterm = os_getenv("COLORTERM"); const char *termprg = os_getenv("TERM_PROGRAM"); @@ -344,7 +360,7 @@ static void tui_scheduler(Event event, void *d) { UI *ui = d; TUIData *data = ui->data; - loop_schedule(data->loop, event); + loop_schedule(data->loop, event); // `tui_loop` local to tui_main(). } #ifdef UNIX diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 272d2045c9..da334d4ac6 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -603,6 +603,15 @@ local function get_pathsep() return funcs.fnamemodify('.', ':p'):sub(-1) end +-- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS. +-- Useful for communicating with child instances. +local function new_pipename() + -- HACK: Start a server temporarily, get the name, then stop it. + local pipename = nvim_eval('serverstart()') + funcs.serverstop(pipename) + return pipename +end + local function missing_provider(provider) if provider == 'ruby' then local prog = funcs['provider#' .. provider .. '#Detect']() @@ -732,6 +741,7 @@ local module = { missing_provider = missing_provider, alter_slashes = alter_slashes, hexdump = hexdump, + new_pipename = new_pipename, } return function(after_each) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 777ef65d9e..723325c040 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1,12 +1,16 @@ -- TUI acceptance tests. -- Uses :terminal as a way to send keys and assert screen state. local global_helpers = require('test.helpers') +local uname = global_helpers.uname local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local feed_data = thelpers.feed_data local feed_command = helpers.feed_command +local clear = helpers.clear local nvim_dir = helpers.nvim_dir local retry = helpers.retry +local nvim_prog = helpers.nvim_prog +local nvim_set = helpers.nvim_set if helpers.pending_win32(pending) then return end @@ -14,7 +18,7 @@ describe('tui', function() local screen before_each(function() - helpers.clear() + clear() screen = thelpers.screen_setup(0, '["'..helpers.nvim_prog ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]') -- right now pasting can be really slow in the TUI, especially in ASAN. @@ -372,7 +376,7 @@ end) -- does not initialize the TUI. describe("tui 't_Co' (terminal colors)", function() local screen - local is_freebsd = (string.lower(global_helpers.uname()) == 'freebsd') + local is_freebsd = (string.lower(uname()) == 'freebsd') local function assert_term_colors(term, colorterm, maxcolors) helpers.clear({env={TERM=term}, args={}}) @@ -384,7 +388,7 @@ describe("tui 't_Co' (terminal colors)", function() (colorterm ~= nil and "COLORTERM="..colorterm or ""), helpers.nvim_prog)) - thelpers.feed_data(":echo &t_Co\n") + feed_data(":echo &t_Co\n") helpers.wait() local tline if maxcolors == 8 or maxcolors == 16 then @@ -628,3 +632,42 @@ describe("tui 't_Co' (terminal colors)", function() end) end) + +-- These tests require `thelpers` because --headless/--embed +-- does not initialize the TUI. +describe("tui 'term' option", function() + local screen + local is_bsd = not not string.find(string.lower(uname()), 'bsd') + + 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) + + local full_timeout = screen.timeout + screen.timeout = 250 -- We want screen:expect() to fail quickly. + retry(nil, 2 * full_timeout, function() -- Wait for TUI thread to set 'term'. + feed_data(":echo 'term='.(&term)\n") + screen:expect('term='..term_expected, nil, nil, nil, true) + end) + end + + it('gets builtin term if $TERM is invalid', function() + assert_term("foo", "builtin_ansi") + end) + + it('gets system-provided term if $TERM is valid', function() + if is_bsd then -- BSD lacks terminfo, we always use builtin there. + assert_term("xterm", "builtin_xterm") + else + assert_term("xterm", "xterm") + end + end) + +end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index d1357ea525..12d71a1603 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -14,8 +14,6 @@ describe('colorscheme compatibility', function() it('t_Co is set to 256 by default', function() eq('256', eval('&t_Co')) - request('nvim_set_option', 't_Co', '88') - eq('88', eval('&t_Co')) end) end) |