mirror of
https://github.com/neovim/neovim.git
synced 2026-08-28 01:51:52 +00:00
feat(tui): $NVIM_TERMDEFS can override builtin terminfo #39555
Problem: - Windows users can't use terminfo to configure their terminal capabilities. #37274 - Terminfo definitions sometimes get out of date or are simply inaccurate. - Eventually, we may want to drop terminfo, relying primarily on built-in definitions. Users will still need some flexibility. Solution: Support $NVIM_TERMDEFS environment variable, which is JSON data that defines "terminfo" definitions that override our builtin terminfo.
This commit is contained in:
@@ -249,6 +249,7 @@ TUI
|
||||
|
||||
• The TUI will re-query the terminal's background color when resuming from
|
||||
a suspended state, and Nvim will update 'background' accordingly.
|
||||
• TUI uses |$NVIM_TERMDEFS| to override terminfo entries.
|
||||
|
||||
UI
|
||||
|
||||
|
||||
@@ -50,6 +50,113 @@ own terminfo is usually as simple as running this:
|
||||
gunzip terminfo.src.gz
|
||||
tic -x terminfo.src
|
||||
<
|
||||
|
||||
*$NVIM_TERMDEFS*
|
||||
To override terminfo entries without modifying terminfo files, use
|
||||
$NVIM_TERMDEFS. This may be useful on Windows, where Unibilium can't read
|
||||
terminfo files. When this is set, terminfo files are not used, but builtin
|
||||
terminfo entries are. Any values in this variable will override the builtin
|
||||
definitions. For example, to disable alternate screen, override the sequence
|
||||
nvim sends to reset the cursor style, and set the sequences that get
|
||||
interpreted as <Home> and <S-Home>: >
|
||||
|
||||
NVIM_TERMDEFS='{
|
||||
"enter_ca_mode": "",
|
||||
"exit_ca_mode": "",
|
||||
"reset_cursor_style": "\u001b[0 q",
|
||||
"key_home": ["\u001bOH", "\u001b[1;2H"]
|
||||
}' nvim
|
||||
<
|
||||
Be careful when putting escape sequences in strings, as JSON uses unicode
|
||||
escapes like `\u001b`. The following fields are supported. For fields of type
|
||||
`string[]`, the first entry is the unshifted variant of the key, and the
|
||||
second entry is the shifted variant.
|
||||
>
|
||||
Field Type
|
||||
------------------------------------------------------------
|
||||
carriage_return string
|
||||
change_scroll_region string
|
||||
clear_screen string
|
||||
clr_eol string
|
||||
clr_eos string
|
||||
cursor_address string
|
||||
cursor_down string
|
||||
cursor_home string
|
||||
cursor_invisible string
|
||||
cursor_left string
|
||||
cursor_normal string
|
||||
cursor_right string
|
||||
cursor_up string
|
||||
delete_line string
|
||||
enter_blink_mode string
|
||||
enter_bold_mode string
|
||||
enter_ca_mode string
|
||||
enter_dim_mode string
|
||||
enter_italics_mode string
|
||||
enter_reverse_mode string
|
||||
enter_secure_mode string
|
||||
enter_standout_mode string
|
||||
enter_underline_mode string
|
||||
erase_chars string
|
||||
exit_attribute_mode string
|
||||
exit_ca_mode string
|
||||
from_status_line string
|
||||
insert_line string
|
||||
keypad_local string
|
||||
keypad_xmit string
|
||||
parm_delete_line string
|
||||
parm_down_cursor string
|
||||
parm_insert_line string
|
||||
parm_left_cursor string
|
||||
parm_right_cursor string
|
||||
parm_up_cursor string
|
||||
set_a_background string
|
||||
set_a_foreground string
|
||||
set_attributes string
|
||||
set_lr_margin string
|
||||
to_status_line string
|
||||
|
||||
enter_strikethrough_mode string
|
||||
reset_cursor_color string
|
||||
reset_cursor_style string
|
||||
set_cursor_color string
|
||||
set_cursor_style string
|
||||
set_rgb_background string
|
||||
set_rgb_foreground string
|
||||
set_underline_style string
|
||||
|
||||
key_backspace string
|
||||
key_btab string
|
||||
key_clear string
|
||||
key_npage string
|
||||
key_ppage string
|
||||
key_select string
|
||||
|
||||
key_beg string[]
|
||||
key_dc string[]
|
||||
key_end string[]
|
||||
key_find string[]
|
||||
key_home string[]
|
||||
key_ic string[]
|
||||
key_left string[]
|
||||
key_right string[]
|
||||
key_suspend string[]
|
||||
key_undo string[]
|
||||
|
||||
key_f1 string
|
||||
...
|
||||
key_f63 string
|
||||
|
||||
RGB bool
|
||||
Su bool
|
||||
Tc bool
|
||||
back_color_erase bool
|
||||
|
||||
columns int
|
||||
lines int
|
||||
max_colors int
|
||||
<
|
||||
|
||||
*$TERM*
|
||||
The $TERM environment variable must match the terminal you are using!
|
||||
Otherwise Nvim cannot know what sequences your terminal expects, and weird
|
||||
|
||||
@@ -158,4 +158,30 @@ function M.query_apc(payload, opts, on_response)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Displays error even when this code runs in the context of the client
|
||||
--- @param msg string
|
||||
local function broadcast_error(msg)
|
||||
vim.rpcnotify(0, 'nvim_echo', {
|
||||
{
|
||||
msg,
|
||||
},
|
||||
}, true, { err = true })
|
||||
end
|
||||
|
||||
--- Get user overrides for terminfo entries as a table. See |$NVIM_TERMDEFS|
|
||||
function M.get_termdefs()
|
||||
local termdefs_raw = os.getenv('NVIM_TERMDEFS')
|
||||
if termdefs_raw ~= nil then
|
||||
local ok, termdefs_or_err = pcall(vim.json.decode, termdefs_raw)
|
||||
if not ok then
|
||||
broadcast_error('E557: Failed to parse $NVIM_TERMDEFS: ' .. vim.inspect(termdefs_or_err))
|
||||
return
|
||||
elseif type(termdefs_or_err) ~= 'table' or vim.isarray(termdefs_or_err) then
|
||||
broadcast_error('E557: expected $NVIM_TERMDEFS to be table. :help $NVIM_TERMDEFS')
|
||||
return
|
||||
end
|
||||
return termdefs_or_err
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -18,104 +18,13 @@ local url = 'https://invisible-island.net/datafiles/current/terminfo.src.gz'
|
||||
local target_gen = 'src/nvim/tui/terminfo_builtin.h'
|
||||
local target_enum = 'src/nvim/tui/terminfo_enum_defs.h'
|
||||
|
||||
local entries = {
|
||||
{ 'ansi', 'ansi_terminfo' },
|
||||
{ 'ghostty', 'ghostty_terminfo' }, -- Note: ncurses defs do not exactly match what ghostty ships.
|
||||
{ 'interix', 'interix_8colour_terminfo' },
|
||||
{ 'iterm2', 'iterm_256colour_terminfo' },
|
||||
{ 'linux', 'linux_16colour_terminfo' },
|
||||
{ 'putty-256color', 'putty_256colour_terminfo' },
|
||||
{ 'rxvt-256color', 'rxvt_256colour_terminfo' },
|
||||
{ 'screen-256color', 'screen_256colour_terminfo' },
|
||||
{ 'st-256color', 'st_256colour_terminfo' },
|
||||
{ 'tmux-256color', 'tmux_256colour_terminfo' },
|
||||
{ 'vte-256color', 'vte_256colour_terminfo' },
|
||||
{ 'xterm-256color', 'xterm_256colour_terminfo' },
|
||||
{ 'cygwin', 'cygwin_terminfo' },
|
||||
{ 'win32con', 'win32con_terminfo' },
|
||||
{ 'conemu', 'conemu_terminfo' },
|
||||
{ 'vtpcon', 'vtpcon_terminfo' },
|
||||
}
|
||||
|
||||
local wanted_numbers = { 'max_colors', 'lines', 'columns' }
|
||||
local wanted_strings = {
|
||||
'carriage_return',
|
||||
'change_scroll_region',
|
||||
'clear_screen',
|
||||
'clr_eol',
|
||||
'clr_eos',
|
||||
'cursor_address',
|
||||
'cursor_down',
|
||||
'cursor_invisible',
|
||||
'cursor_left',
|
||||
'cursor_home',
|
||||
'cursor_normal',
|
||||
'cursor_up',
|
||||
'cursor_right',
|
||||
'delete_line',
|
||||
'enter_blink_mode',
|
||||
'enter_bold_mode',
|
||||
'enter_ca_mode',
|
||||
'enter_dim_mode',
|
||||
'enter_italics_mode',
|
||||
'enter_reverse_mode',
|
||||
'enter_secure_mode',
|
||||
'enter_standout_mode',
|
||||
'enter_underline_mode',
|
||||
'erase_chars',
|
||||
'exit_attribute_mode',
|
||||
'exit_ca_mode',
|
||||
'from_status_line',
|
||||
'insert_line',
|
||||
'keypad_local',
|
||||
'keypad_xmit',
|
||||
'parm_delete_line',
|
||||
'parm_down_cursor',
|
||||
'parm_insert_line',
|
||||
'parm_left_cursor',
|
||||
'parm_right_cursor',
|
||||
'parm_up_cursor',
|
||||
'set_a_background',
|
||||
'set_a_foreground',
|
||||
'set_attributes',
|
||||
'set_lr_margin',
|
||||
'to_status_line',
|
||||
}
|
||||
|
||||
local wanted_strings_ext = {
|
||||
-- the following are our custom name for extensions, see "extmap"
|
||||
{ 'reset_cursor_style', 'Se' },
|
||||
{ 'set_cursor_style', 'Ss' },
|
||||
-- terminfo describes strikethrough modes as rmxx/smxx with respect
|
||||
-- to the ECMA-48 strikeout/crossed-out attributes.
|
||||
{ 'enter_strikethrough_mode', 'smxx' },
|
||||
{ 'set_rgb_foreground', 'setrgbf' },
|
||||
{ 'set_rgb_background', 'setrgbb' },
|
||||
{ 'set_cursor_color', 'Cs' },
|
||||
{ 'reset_cursor_color', 'Cr' },
|
||||
{ 'set_underline_style', 'Smulx' },
|
||||
}
|
||||
|
||||
-- Note: these are only consumed by driver-ti via it's table of "funcs" keys.
|
||||
-- Second value is whether there is a "shift" variant in terminfo.
|
||||
local wanted_termkeys = {
|
||||
{ 'backspace', false },
|
||||
{ 'beg', true }, -- sometimes known as: "begin"
|
||||
{ 'btab', false },
|
||||
{ 'clear', false },
|
||||
{ 'dc', true },
|
||||
{ 'end', true },
|
||||
{ 'find', true },
|
||||
{ 'home', true },
|
||||
{ 'ic', true },
|
||||
{ 'npage', false },
|
||||
{ 'ppage', false },
|
||||
{ 'select', false },
|
||||
{ 'suspend', true },
|
||||
{ 'undo', true },
|
||||
{ 'left', true },
|
||||
{ 'right', true },
|
||||
}
|
||||
local terminfo = require('src.gen.terminfo')
|
||||
local entries = terminfo.builtin_terminals
|
||||
local wanted_strings = terminfo.fields.strings
|
||||
local wanted_strings_ext = terminfo.fields.strings_ext
|
||||
local wanted_bools = terminfo.fields.bools
|
||||
local wanted_ints = terminfo.fields.ints
|
||||
local wanted_termkeys = terminfo.fields.termkeys
|
||||
|
||||
local db = '/tmp/nvim_terminfo'
|
||||
if vim.uv.fs_stat(db) == nil then
|
||||
@@ -173,7 +82,7 @@ f_enum:write([[
|
||||
// but will then be encoded as chords. We might actually prefer that but it is
|
||||
// potentially breaking change.
|
||||
]])
|
||||
local func_key_max = 63
|
||||
local func_key_max = terminfo.fields.func_key_max
|
||||
f_enum:write('#define kTerminfoFuncKeyMax ' .. func_key_max .. '\n')
|
||||
f_enum:write('typedef enum {\n')
|
||||
for _, item in ipairs(wanted_termkeys) do
|
||||
@@ -223,12 +132,11 @@ for _, entry in ipairs(entries) do
|
||||
end
|
||||
|
||||
f_defs:write('\nstatic const TerminfoEntry ' .. target .. ' = {\n')
|
||||
f_defs:write(' .bce = ' .. tostring(bools.back_color_erase or false) .. ',\n')
|
||||
local has_Tc_or_RGB = (bools.Tc or bools.RGB) or false
|
||||
f_defs:write(' .has_Tc_or_RGB = ' .. tostring(has_Tc_or_RGB or false) .. ',\n')
|
||||
f_defs:write(' .Su = ' .. tostring(bools.Su or false) .. ',\n')
|
||||
for _, b in ipairs(wanted_bools) do
|
||||
f_defs:write(' .' .. b .. ' = ' .. tostring(bools[b] or false) .. ',\n')
|
||||
end
|
||||
|
||||
for _, name in ipairs(wanted_numbers) do
|
||||
for _, name in ipairs(wanted_ints) do
|
||||
f_defs:write(' .' .. name .. ' = ' .. (nums[name] or '-1') .. ',\n')
|
||||
end
|
||||
f_defs:write(' .defs = {\n')
|
||||
@@ -269,7 +177,17 @@ for _, entry in ipairs(entries) do
|
||||
f_defs:write('};\n')
|
||||
end
|
||||
|
||||
f_defs:write('\n#define XLIST_TERMINFO_BUILTIN \\\n')
|
||||
f_defs:write('\n#define XLIST_TERMINFO_BOOLS \\\n')
|
||||
for _, item in ipairs(wanted_bools) do
|
||||
f_defs:write(' X(' .. item .. ') \\\n')
|
||||
end
|
||||
f_defs:write('// end of list\n\n')
|
||||
f_defs:write('#define XLIST_TERMINFO_INTS \\\n')
|
||||
for _, item in ipairs(wanted_ints) do
|
||||
f_defs:write(' X(' .. item .. ') \\\n')
|
||||
end
|
||||
f_defs:write('// end of list\n\n')
|
||||
f_defs:write('#define XLIST_TERMINFO_BUILTIN \\\n')
|
||||
for _, name in ipairs(wanted_strings) do
|
||||
f_defs:write(' X(' .. name .. ') \\\n')
|
||||
end
|
||||
@@ -286,7 +204,7 @@ end
|
||||
f_defs:write('// end of list\n\n')
|
||||
f_defs:write('#define XLIST_TERMINFO_FKEYS \\\n')
|
||||
for i = 1, func_key_max do
|
||||
f_defs:write(' X(f' .. i .. ') \\\n')
|
||||
f_defs:write(' X(f' .. i .. ', ' .. i - 1 .. ') \\\n')
|
||||
end
|
||||
f_defs:write('// end of list\n')
|
||||
f_defs:close()
|
||||
|
||||
107
src/gen/terminfo.lua
Normal file
107
src/gen/terminfo.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
local M = {}
|
||||
|
||||
M.fields = {}
|
||||
M.fields.bools = { 'back_color_erase', 'Tc', 'RGB', 'Su' }
|
||||
M.fields.ints = { 'max_colors', 'lines', 'columns' }
|
||||
|
||||
M.builtin_terminals = {
|
||||
{ 'ansi', 'ansi_terminfo' },
|
||||
{ 'ghostty', 'ghostty_terminfo' }, -- Note: ncurses defs do not exactly match what ghostty ships.
|
||||
{ 'interix', 'interix_8colour_terminfo' },
|
||||
{ 'iterm2', 'iterm_256colour_terminfo' },
|
||||
{ 'linux', 'linux_16colour_terminfo' },
|
||||
{ 'putty-256color', 'putty_256colour_terminfo' },
|
||||
{ 'rxvt-256color', 'rxvt_256colour_terminfo' },
|
||||
{ 'screen-256color', 'screen_256colour_terminfo' },
|
||||
{ 'st-256color', 'st_256colour_terminfo' },
|
||||
{ 'tmux-256color', 'tmux_256colour_terminfo' },
|
||||
{ 'vte-256color', 'vte_256colour_terminfo' },
|
||||
{ 'xterm-256color', 'xterm_256colour_terminfo' },
|
||||
{ 'cygwin', 'cygwin_terminfo' },
|
||||
{ 'win32con', 'win32con_terminfo' },
|
||||
{ 'conemu', 'conemu_terminfo' },
|
||||
{ 'vtpcon', 'vtpcon_terminfo' },
|
||||
}
|
||||
|
||||
M.fields.strings = {
|
||||
'carriage_return',
|
||||
'change_scroll_region',
|
||||
'clear_screen',
|
||||
'clr_eol',
|
||||
'clr_eos',
|
||||
'cursor_address',
|
||||
'cursor_down',
|
||||
'cursor_invisible',
|
||||
'cursor_left',
|
||||
'cursor_home',
|
||||
'cursor_normal',
|
||||
'cursor_up',
|
||||
'cursor_right',
|
||||
'delete_line',
|
||||
'enter_blink_mode',
|
||||
'enter_bold_mode',
|
||||
'enter_ca_mode',
|
||||
'enter_dim_mode',
|
||||
'enter_italics_mode',
|
||||
'enter_reverse_mode',
|
||||
'enter_secure_mode',
|
||||
'enter_standout_mode',
|
||||
'enter_underline_mode',
|
||||
'erase_chars',
|
||||
'exit_attribute_mode',
|
||||
'exit_ca_mode',
|
||||
'from_status_line',
|
||||
'insert_line',
|
||||
'keypad_local',
|
||||
'keypad_xmit',
|
||||
'parm_delete_line',
|
||||
'parm_down_cursor',
|
||||
'parm_insert_line',
|
||||
'parm_left_cursor',
|
||||
'parm_right_cursor',
|
||||
'parm_up_cursor',
|
||||
'set_a_background',
|
||||
'set_a_foreground',
|
||||
'set_attributes',
|
||||
'set_lr_margin',
|
||||
'to_status_line',
|
||||
}
|
||||
|
||||
M.fields.strings_ext = {
|
||||
-- the following are our custom name for extensions, see "extmap"
|
||||
{ 'reset_cursor_style', 'Se' },
|
||||
{ 'set_cursor_style', 'Ss' },
|
||||
-- terminfo describes strikethrough modes as rmxx/smxx with respect
|
||||
-- to the ECMA-48 strikeout/crossed-out attributes.
|
||||
{ 'enter_strikethrough_mode', 'smxx' },
|
||||
{ 'set_rgb_foreground', 'setrgbf' },
|
||||
{ 'set_rgb_background', 'setrgbb' },
|
||||
{ 'set_cursor_color', 'Cs' },
|
||||
{ 'reset_cursor_color', 'Cr' },
|
||||
{ 'set_underline_style', 'Smulx' },
|
||||
}
|
||||
|
||||
-- Note: these are only consumed by driver-ti via it's table of "funcs" keys.
|
||||
-- Second value is whether there is a "shift" variant in terminfo.
|
||||
M.fields.termkeys = {
|
||||
{ 'backspace', false },
|
||||
{ 'beg', true }, -- sometimes known as: "begin"
|
||||
{ 'btab', false },
|
||||
{ 'clear', false },
|
||||
{ 'dc', true },
|
||||
{ 'end', true },
|
||||
{ 'find', true },
|
||||
{ 'home', true },
|
||||
{ 'ic', true },
|
||||
{ 'npage', false },
|
||||
{ 'ppage', false },
|
||||
{ 'select', false },
|
||||
{ 'suspend', true },
|
||||
{ 'undo', true },
|
||||
{ 'left', true },
|
||||
{ 'right', true },
|
||||
}
|
||||
|
||||
M.fields.func_key_max = 63
|
||||
|
||||
return M
|
||||
52
src/nvim/tui/termdef_field_defs.h
Normal file
52
src/nvim/tui/termdef_field_defs.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/tui/terminfo_builtin.h"
|
||||
#include "nvim/tui/terminfo_defs.h"
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
ObjectType type; // Lua type in decoded $NVIM_TERMDEFS
|
||||
size_t offset; // offset in TerminfoEntry
|
||||
} TermdefField;
|
||||
|
||||
// uncrustify:off
|
||||
static const TermdefField termdef_field_entries[] = {
|
||||
#define FIELD(n, t, off) { .name = n, .type = kObjectType##t, \
|
||||
.offset = offsetof(TerminfoEntry, off) },
|
||||
#define X(n) FIELD(#n, Boolean, n)
|
||||
XLIST_TERMINFO_BOOLS
|
||||
#undef X
|
||||
#define X(n) FIELD(#n, Integer, n)
|
||||
XLIST_TERMINFO_INTS
|
||||
#undef X
|
||||
#define X(n) FIELD(#n, String, defs[kTerm_##n])
|
||||
XLIST_TERMINFO_BUILTIN
|
||||
#undef X
|
||||
#define X(n, code) FIELD(#n, String, defs[kTerm_##n])
|
||||
XLIST_TERMINFO_EXT
|
||||
#undef X
|
||||
#define X(n) FIELD("key_" #n, String, keys[kTermKey_##n])
|
||||
#define Y(n) FIELD("key_" #n, Array, keys[kTermKey_##n])
|
||||
XYLIST_TERMINFO_KEYS
|
||||
#undef X
|
||||
#undef Y
|
||||
#define X(n, idx) FIELD("key_" #n, String, f_keys[idx])
|
||||
XLIST_TERMINFO_FKEYS
|
||||
#undef X
|
||||
#undef FIELD
|
||||
};
|
||||
// uncrustify:on
|
||||
|
||||
static PMap(cstr_t) termdef_fields = MAP_INIT;
|
||||
static inline void init_termdef_fields(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(termdef_field_entries); i++) {
|
||||
pmap_put(cstr_t)(&termdef_fields, termdef_field_entries[i].name,
|
||||
(TermdefField *)&termdef_field_entries[i]);
|
||||
}
|
||||
}
|
||||
@@ -133,18 +133,21 @@ bool terminfo_from_database(TerminfoEntry *ti, char *termname, Arena *arena)
|
||||
return false;
|
||||
}
|
||||
|
||||
ti->bce = unibi_get_bool(ut, unibi_back_color_erase);
|
||||
ti->back_color_erase = unibi_get_bool(ut, unibi_back_color_erase);
|
||||
ti->max_colors = unibi_get_num(ut, unibi_max_colors);
|
||||
ti->lines = unibi_get_num(ut, unibi_lines);
|
||||
ti->columns = unibi_get_num(ut, unibi_columns);
|
||||
|
||||
// Check for Tc or RGB
|
||||
ti->has_Tc_or_RGB = false;
|
||||
ti->Tc = false;
|
||||
ti->RGB = false;
|
||||
ti->Su = false;
|
||||
for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) {
|
||||
const char *n = unibi_get_ext_bool_name(ut, i);
|
||||
if (n && (!strcmp(n, "Tc") || !strcmp(n, "RGB"))) {
|
||||
ti->has_Tc_or_RGB = true;
|
||||
if (n && !strcmp(n, "Tc")) {
|
||||
ti->Tc = true;
|
||||
} else if (n && !strcmp(n, "RGB")) {
|
||||
ti->RGB = true;
|
||||
} else if (n && !strcmp(n, "Su")) {
|
||||
ti->Su = true;
|
||||
}
|
||||
@@ -200,7 +203,7 @@ bool terminfo_from_database(TerminfoEntry *ti, char *termname, Arena *arena)
|
||||
}
|
||||
|
||||
static const enum unibi_string uni_fkeys[] = {
|
||||
# define X(name) unibi_key_##name,
|
||||
# define X(name, idx) unibi_key_##name,
|
||||
XLIST_TERMINFO_FKEYS
|
||||
# undef X
|
||||
};
|
||||
@@ -239,15 +242,16 @@ String terminfo_info_msg(const TerminfoEntry *ti, const char *termname, bool fro
|
||||
kv_printf(data, "\n");
|
||||
|
||||
kv_printf(data, "Boolean capabilities:\n");
|
||||
kv_printf(data, " back_color_erase: %s\n", fmt(ti->bce));
|
||||
kv_printf(data, " truecolor ('Tc' or 'RGB'): %s\n", fmt(ti->has_Tc_or_RGB));
|
||||
kv_printf(data, " back_color_erase: %s\n", fmt(ti->back_color_erase));
|
||||
kv_printf(data, " truecolor ('Tc'): %s\n", fmt(ti->Tc));
|
||||
kv_printf(data, " RGB: %s\n", fmt(ti->RGB));
|
||||
kv_printf(data, " extended underline ('Su'): %s\n", fmt(ti->Su));
|
||||
kv_printf(data, "\n");
|
||||
|
||||
kv_printf(data, "Numeric capabilities: (-1 for unknown)\n");
|
||||
kv_printf(data, " lines: %d\n", ti->lines);
|
||||
kv_printf(data, " columns: %d\n", ti->columns);
|
||||
kv_printf(data, " max_colors: %d\n", ti->columns);
|
||||
kv_printf(data, " max_colors: %d\n", ti->max_colors);
|
||||
kv_printf(data, "\n");
|
||||
|
||||
kv_printf(data, "String capabilities:\n");
|
||||
@@ -279,7 +283,7 @@ String terminfo_info_msg(const TerminfoEntry *ti, const char *termname, bool fro
|
||||
#undef Y
|
||||
};
|
||||
|
||||
for (size_t i = 0 + 1; i < ARRAY_SIZE(key_names); i++) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(key_names); i++) {
|
||||
const char *s = ti->keys[i][0];
|
||||
if (s) {
|
||||
kv_printf(data, " key_%-27s = ", key_names[i]);
|
||||
@@ -294,12 +298,12 @@ String terminfo_info_msg(const TerminfoEntry *ti, const char *termname, bool fro
|
||||
}
|
||||
|
||||
static const char *fkey_names[] = {
|
||||
#define X(name) #name,
|
||||
#define X(name, idx) #name,
|
||||
XLIST_TERMINFO_FKEYS
|
||||
#undef X
|
||||
};
|
||||
|
||||
for (size_t i = 0 + 1; i < ARRAY_SIZE(fkey_names); i++) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(fkey_names); i++) {
|
||||
const char *s = ti->f_keys[i];
|
||||
if (s) {
|
||||
kv_printf(data, " key_%-27s = ", fkey_names[i]);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
// uncrustify:off
|
||||
|
||||
// Generated by src/gen/gen_terminfo.lua and ncurses 6.6.20251231
|
||||
// Generated by src/gen/gen_terminfo.lua and ncurses 6.6.20251230
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nvim/tui/terminfo_defs.h"
|
||||
|
||||
static const TerminfoEntry ansi_terminfo = {
|
||||
.bce = false,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = false,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 8,
|
||||
.lines = 24,
|
||||
@@ -88,8 +89,9 @@ static const TerminfoEntry ansi_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry ghostty_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -232,8 +234,9 @@ static const TerminfoEntry ghostty_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry interix_8colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 8,
|
||||
.lines = 25,
|
||||
@@ -373,8 +376,9 @@ static const TerminfoEntry interix_8colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry iterm_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -478,8 +482,9 @@ static const TerminfoEntry iterm_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry linux_16colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 8,
|
||||
.lines = -1,
|
||||
@@ -579,8 +584,9 @@ static const TerminfoEntry linux_16colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry putty_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = -1,
|
||||
@@ -680,8 +686,9 @@ static const TerminfoEntry putty_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry rxvt_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -805,8 +812,9 @@ static const TerminfoEntry rxvt_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry screen_256colour_terminfo = {
|
||||
.bce = false,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = false,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -898,8 +906,9 @@ static const TerminfoEntry screen_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry st_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1042,8 +1051,9 @@ static const TerminfoEntry st_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry tmux_256colour_terminfo = {
|
||||
.bce = false,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = false,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1186,8 +1196,9 @@ static const TerminfoEntry tmux_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry vte_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1330,8 +1341,9 @@ static const TerminfoEntry vte_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry xterm_256colour_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1474,8 +1486,9 @@ static const TerminfoEntry xterm_256colour_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry cygwin_terminfo = {
|
||||
.bce = false,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = false,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 8,
|
||||
.lines = -1,
|
||||
@@ -1575,8 +1588,9 @@ static const TerminfoEntry cygwin_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry win32con_terminfo = {
|
||||
.bce = false,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = false,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 8,
|
||||
.lines = -1,
|
||||
@@ -1700,8 +1714,9 @@ static const TerminfoEntry win32con_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry conemu_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1826,8 +1841,9 @@ static const TerminfoEntry conemu_terminfo = {
|
||||
};
|
||||
|
||||
static const TerminfoEntry vtpcon_terminfo = {
|
||||
.bce = true,
|
||||
.has_Tc_or_RGB = false,
|
||||
.back_color_erase = true,
|
||||
.Tc = false,
|
||||
.RGB = false,
|
||||
.Su = false,
|
||||
.max_colors = 0x100,
|
||||
.lines = 24,
|
||||
@@ -1951,6 +1967,19 @@ static const TerminfoEntry vtpcon_terminfo = {
|
||||
},
|
||||
};
|
||||
|
||||
#define XLIST_TERMINFO_BOOLS \
|
||||
X(back_color_erase) \
|
||||
X(Tc) \
|
||||
X(RGB) \
|
||||
X(Su) \
|
||||
// end of list
|
||||
|
||||
#define XLIST_TERMINFO_INTS \
|
||||
X(max_colors) \
|
||||
X(lines) \
|
||||
X(columns) \
|
||||
// end of list
|
||||
|
||||
#define XLIST_TERMINFO_BUILTIN \
|
||||
X(carriage_return) \
|
||||
X(change_scroll_region) \
|
||||
@@ -2026,67 +2055,67 @@ static const TerminfoEntry vtpcon_terminfo = {
|
||||
// end of list
|
||||
|
||||
#define XLIST_TERMINFO_FKEYS \
|
||||
X(f1) \
|
||||
X(f2) \
|
||||
X(f3) \
|
||||
X(f4) \
|
||||
X(f5) \
|
||||
X(f6) \
|
||||
X(f7) \
|
||||
X(f8) \
|
||||
X(f9) \
|
||||
X(f10) \
|
||||
X(f11) \
|
||||
X(f12) \
|
||||
X(f13) \
|
||||
X(f14) \
|
||||
X(f15) \
|
||||
X(f16) \
|
||||
X(f17) \
|
||||
X(f18) \
|
||||
X(f19) \
|
||||
X(f20) \
|
||||
X(f21) \
|
||||
X(f22) \
|
||||
X(f23) \
|
||||
X(f24) \
|
||||
X(f25) \
|
||||
X(f26) \
|
||||
X(f27) \
|
||||
X(f28) \
|
||||
X(f29) \
|
||||
X(f30) \
|
||||
X(f31) \
|
||||
X(f32) \
|
||||
X(f33) \
|
||||
X(f34) \
|
||||
X(f35) \
|
||||
X(f36) \
|
||||
X(f37) \
|
||||
X(f38) \
|
||||
X(f39) \
|
||||
X(f40) \
|
||||
X(f41) \
|
||||
X(f42) \
|
||||
X(f43) \
|
||||
X(f44) \
|
||||
X(f45) \
|
||||
X(f46) \
|
||||
X(f47) \
|
||||
X(f48) \
|
||||
X(f49) \
|
||||
X(f50) \
|
||||
X(f51) \
|
||||
X(f52) \
|
||||
X(f53) \
|
||||
X(f54) \
|
||||
X(f55) \
|
||||
X(f56) \
|
||||
X(f57) \
|
||||
X(f58) \
|
||||
X(f59) \
|
||||
X(f60) \
|
||||
X(f61) \
|
||||
X(f62) \
|
||||
X(f63) \
|
||||
X(f1, 0) \
|
||||
X(f2, 1) \
|
||||
X(f3, 2) \
|
||||
X(f4, 3) \
|
||||
X(f5, 4) \
|
||||
X(f6, 5) \
|
||||
X(f7, 6) \
|
||||
X(f8, 7) \
|
||||
X(f9, 8) \
|
||||
X(f10, 9) \
|
||||
X(f11, 10) \
|
||||
X(f12, 11) \
|
||||
X(f13, 12) \
|
||||
X(f14, 13) \
|
||||
X(f15, 14) \
|
||||
X(f16, 15) \
|
||||
X(f17, 16) \
|
||||
X(f18, 17) \
|
||||
X(f19, 18) \
|
||||
X(f20, 19) \
|
||||
X(f21, 20) \
|
||||
X(f22, 21) \
|
||||
X(f23, 22) \
|
||||
X(f24, 23) \
|
||||
X(f25, 24) \
|
||||
X(f26, 25) \
|
||||
X(f27, 26) \
|
||||
X(f28, 27) \
|
||||
X(f29, 28) \
|
||||
X(f30, 29) \
|
||||
X(f31, 30) \
|
||||
X(f32, 31) \
|
||||
X(f33, 32) \
|
||||
X(f34, 33) \
|
||||
X(f35, 34) \
|
||||
X(f36, 35) \
|
||||
X(f37, 36) \
|
||||
X(f38, 37) \
|
||||
X(f39, 38) \
|
||||
X(f40, 39) \
|
||||
X(f41, 40) \
|
||||
X(f42, 41) \
|
||||
X(f43, 42) \
|
||||
X(f44, 43) \
|
||||
X(f45, 44) \
|
||||
X(f46, 45) \
|
||||
X(f47, 46) \
|
||||
X(f48, 47) \
|
||||
X(f49, 48) \
|
||||
X(f50, 49) \
|
||||
X(f51, 50) \
|
||||
X(f52, 51) \
|
||||
X(f53, 52) \
|
||||
X(f54, 53) \
|
||||
X(f55, 54) \
|
||||
X(f56, 55) \
|
||||
X(f57, 56) \
|
||||
X(f58, 57) \
|
||||
X(f59, 58) \
|
||||
X(f60, 59) \
|
||||
X(f61, 60) \
|
||||
X(f62, 61) \
|
||||
X(f63, 62) \
|
||||
// end of list
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include "nvim/tui/terminfo_enum_defs.h"
|
||||
|
||||
typedef struct {
|
||||
bool bce;
|
||||
bool back_color_erase;
|
||||
// these extended booleans indicate likely 24-color support
|
||||
bool has_Tc_or_RGB;
|
||||
bool Tc;
|
||||
bool RGB;
|
||||
bool Su;
|
||||
|
||||
int max_colors;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "nvim/grid.h"
|
||||
#include "nvim/highlight_defs.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/map_defs.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "nvim/os/os_defs.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/tui/input.h"
|
||||
#include "nvim/tui/termdef_field_defs.h"
|
||||
#include "nvim/tui/terminfo.h"
|
||||
#include "nvim/tui/tui.h"
|
||||
#include "nvim/tui/ugrid.h"
|
||||
@@ -354,6 +356,108 @@ void tui_query_bg_color(TUIData *tui)
|
||||
flush_buf(tui);
|
||||
}
|
||||
|
||||
/// Use $NVIM_TERMDEFS to apply user overrides to terminfo.
|
||||
///
|
||||
/// This is necessary for Windows, where terminfo files are broken in Unibilium. #37274
|
||||
/// If/when Unibilium is removed, this will also be useful for anyone who wants to override
|
||||
/// the built-in definitions.
|
||||
static void apply_termdefs(TUIData *tui)
|
||||
{
|
||||
// We allow empty values just to provide the user with a warning
|
||||
if (!os_env_exists("NVIM_TERMDEFS", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Error lua_err = ERROR_INIT;
|
||||
Object rv = NLUA_EXEC_STATIC("return require('vim.tty').get_termdefs()",
|
||||
(Array)ARRAY_DICT_INIT, kRetObject, NULL, &lua_err);
|
||||
if (rv.type != kObjectTypeDict) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_termdef_fields();
|
||||
|
||||
Arena err_arena = ARENA_EMPTY;
|
||||
Array chunks = ARRAY_DICT_INIT;
|
||||
int err_count = 0;
|
||||
|
||||
#define PUSH_ERR(fmt, ...) \
|
||||
do { \
|
||||
String str = arena_printf(&err_arena, fmt, __VA_ARGS__); \
|
||||
Array err = arena_array(&err_arena, 2); \
|
||||
ADD(err, STRING_OBJ(str)); \
|
||||
ADD(err, STATIC_CSTR_AS_OBJ("ErrorMsg")); \
|
||||
ADD(chunks, ARRAY_OBJ(err)); \
|
||||
err_count++; \
|
||||
} while (0)
|
||||
|
||||
for (size_t i = 0; i < rv.data.dict.size; i++) {
|
||||
KeyValuePair kv = rv.data.dict.items[i];
|
||||
TermdefField *td_field = pmap_get(cstr_t)(&termdef_fields, kv.key.data);
|
||||
if (!td_field || kv.value.type != td_field->type) {
|
||||
PUSH_ERR("skipping invalid field in $NVIM_TERMDEFS: '%s'\n", kv.key.data);
|
||||
continue;
|
||||
}
|
||||
Object val = kv.value;
|
||||
char *dst = (char *)&tui->ti + td_field->offset;
|
||||
|
||||
switch (val.type) {
|
||||
case kObjectTypeBoolean:
|
||||
*(bool *)dst = val.data.boolean;
|
||||
break;
|
||||
case kObjectTypeInteger:
|
||||
*(int *)dst = (int)val.data.integer;
|
||||
break;
|
||||
case kObjectTypeString:
|
||||
*(const char **)dst = arena_strdup(&tui->ti_arena,
|
||||
val.data.string.data);
|
||||
break;
|
||||
case kObjectTypeArray:
|
||||
if (val.data.array.size > 2) {
|
||||
PUSH_ERR(
|
||||
"skipping invalid field definition in $NVIM_TERMDEFS for %s: array should have at most two entries for unshifted and shifted key variants.\n",
|
||||
kv.key.data);
|
||||
continue;
|
||||
}
|
||||
*(const char **)dst = NULL;
|
||||
*(const char **)(dst + sizeof(char *)) = NULL;
|
||||
|
||||
for (size_t key_map_idx = 0; key_map_idx < val.data.array.size;
|
||||
key_map_idx++) {
|
||||
Object key_mapping = val.data.array.items[key_map_idx];
|
||||
if (key_mapping.type != kObjectTypeString) {
|
||||
PUSH_ERR(
|
||||
"skipping invalid field definition in $NVIM_TERMDEFS for %s[%zu]: expected type 'string'\n",
|
||||
kv.key.data, key_map_idx);
|
||||
continue;
|
||||
}
|
||||
size_t offset = key_map_idx * sizeof(char *);
|
||||
*(const char **)(dst + offset) = arena_strdup(&tui->ti_arena, key_mapping.data.string.data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err_count > 0) {
|
||||
MAXSIZE_TEMP_ARRAY(args, 3);
|
||||
ADD_C(args, ARRAY_OBJ(chunks));
|
||||
ADD_C(args, BOOLEAN_OBJ(true)); // history
|
||||
MAXSIZE_TEMP_DICT(opts, 1);
|
||||
PUT_C(opts, "err", BOOLEAN_OBJ(true));
|
||||
ADD_C(args, DICT_OBJ(opts));
|
||||
rpc_send_event(ui_client_channel_id, "nvim_echo", args);
|
||||
}
|
||||
|
||||
arena_mem_free(arena_finish(&err_arena));
|
||||
kv_destroy(chunks);
|
||||
api_free_object(rv);
|
||||
api_clear_error(&lua_err);
|
||||
|
||||
#undef PUSH_ERR
|
||||
}
|
||||
|
||||
/// Enable the alternate screen and emit other control sequences to start the TUI.
|
||||
///
|
||||
/// This is also called when the TUI is resumed after being suspended. We reinitialize all state
|
||||
@@ -396,7 +500,7 @@ static void terminfo_start(TUIData *tui)
|
||||
|
||||
// Set up terminfo.
|
||||
tui->terminfo_found_in_db = false;
|
||||
if (term) {
|
||||
if (term && !os_env_exists("NVIM_TERMDEFS", false)) {
|
||||
if (terminfo_from_database(&tui->ti, term, &tui->ti_arena)) {
|
||||
tui->term = arena_strdup(&tui->ti_arena, term);
|
||||
tui->terminfo_found_in_db = true;
|
||||
@@ -436,6 +540,7 @@ static void terminfo_start(TUIData *tui)
|
||||
|
||||
patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm);
|
||||
augment_terminfo(tui, term, vtev, konsolev, weztermv, iterm_env, nsterm);
|
||||
apply_termdefs(tui);
|
||||
|
||||
#define TI_HAS(name) (tui->ti.defs[name] != NULL)
|
||||
tui->can_change_scroll_region = TI_HAS(kTerm_change_scroll_region);
|
||||
@@ -452,7 +557,7 @@ static void terminfo_start(TUIData *tui)
|
||||
|| terminfo_is_term_family(term, "cygwin")
|
||||
|| terminfo_is_term_family(term, "win32con")
|
||||
|| terminfo_is_term_family(term, "interix");
|
||||
tui->bce = tui->ti.bce;
|
||||
tui->bce = tui->ti.back_color_erase;
|
||||
// Set 't_Co' from the result of terminfo & fix_terminfo.
|
||||
t_colors = tui->ti.max_colors;
|
||||
// Enter alternate screen, save title, and clear.
|
||||
@@ -2033,7 +2138,7 @@ static bool term_has_truecolor(TUIData *tui, const char *colorterm)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tui->ti.has_Tc_or_RGB) {
|
||||
if (tui->ti.Tc || tui->ti.RGB) {
|
||||
// terminfo had one of "Tc" or "RGB" extended boolean capabilities
|
||||
return true;
|
||||
}
|
||||
@@ -2121,7 +2226,7 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo
|
||||
|
||||
if (tmux || screen || kitty) {
|
||||
// Disable BCE in some cases we know it is not working. #8806
|
||||
tui->ti.bce = false;
|
||||
tui->ti.back_color_erase = false;
|
||||
}
|
||||
|
||||
if (xterm || hterm) {
|
||||
|
||||
@@ -3917,8 +3917,11 @@ describe('TUI', function()
|
||||
local screen --[[@type test.functional.ui.screen]]
|
||||
|
||||
-- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI.
|
||||
local function nvim_tui(extra_args)
|
||||
local function nvim_tui(extra_args, extra_env)
|
||||
clear()
|
||||
extra_env = extra_env or {}
|
||||
local env = vim.tbl_extend('force', { LANG = 'C' }, extra_env)
|
||||
|
||||
screen = tt.setup_child_nvim({
|
||||
'--clean',
|
||||
'--cmd',
|
||||
@@ -3927,9 +3930,7 @@ describe('TUI', function()
|
||||
nvim_set .. ' notermguicolors',
|
||||
extra_args,
|
||||
}, {
|
||||
env = {
|
||||
LANG = 'C',
|
||||
},
|
||||
env = env,
|
||||
})
|
||||
end
|
||||
|
||||
@@ -3956,6 +3957,45 @@ describe('TUI', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
it('uses $NVIM_TERMDEFS to override terminfo', function()
|
||||
local logfile = 'Xtest_terminfo_override_verbose_log'
|
||||
finally(function()
|
||||
os.remove(logfile)
|
||||
end)
|
||||
|
||||
local terminfo = {
|
||||
enter_ca_mode = 'ENTER_CA_MODE',
|
||||
reset_cursor_style = '\027[0 q',
|
||||
key_home = { 'ABC', 'DEF' },
|
||||
key_npage = 'npage',
|
||||
key_end = { 'GHI' },
|
||||
key_f1 = 'JKL',
|
||||
key_f63 = 'MNO',
|
||||
Su = true,
|
||||
max_colors = 999,
|
||||
}
|
||||
|
||||
nvim_tui('-V3' .. logfile, { NVIM_TERMDEFS = vim.json.encode(terminfo) })
|
||||
|
||||
assert_log('enter_ca_mode[^\n]*= ' .. terminfo.enter_ca_mode, logfile, 9999)
|
||||
assert_log(
|
||||
'reset_cursor_style[^\n]*= ' .. vim.pesc(terminfo.reset_cursor_style:gsub('\027', '^[')),
|
||||
logfile,
|
||||
9999
|
||||
)
|
||||
assert_log(
|
||||
string.format('key_home%%s*= %s, key_shome = %s', terminfo.key_home[1], terminfo.key_home[2]),
|
||||
logfile,
|
||||
9999
|
||||
)
|
||||
assert_log('key_npage%s*= ' .. terminfo.key_npage, logfile, 9999)
|
||||
assert_log('key_end%s*= ' .. terminfo.key_end[1] .. '\n', logfile, 9999)
|
||||
assert_log('key_f1%s*= ' .. terminfo.key_f1, logfile, 9999)
|
||||
assert_log('key_f63%s*= ' .. terminfo.key_f63, logfile, 9999)
|
||||
assert_log('extended underline[^\n]*: ' .. tostring(terminfo.Su), logfile, 9999)
|
||||
assert_log('max_colors: ' .. terminfo.max_colors, logfile, 9999)
|
||||
end)
|
||||
|
||||
it('does not crash on large inputs #26099', function()
|
||||
nvim_tui()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user