commit 981ea41abbecf6bc7a32062986ebcad13959d895
parent 2700f6642aef8f6f51e7623da8701a542fddb82b
Author: Justin M. Keyes <justinkz@gmail.com>
Date: Mon, 15 Dec 2025 02:07:51 -0500
feat(tui): ghostty builtin terminfo #36963
Problem:
The builtin terminfo defs don't include xterm-ghostty, so features like
`kTerm_set_underline_style` are missing when building without unibilium.
Solution:
- Add ghostty to `gen_terminfo.lua`.
- Note: The ncurses defs are somewhat different than what ghostty ships.
- Special-case ghostty in `terminfo_from_builtin`.
Diffstat:
4 files changed, 157 insertions(+), 8 deletions(-)
diff --git a/runtime/doc/tui.txt b/runtime/doc/tui.txt
@@ -61,11 +61,13 @@ unlike most other environment variables.
>
For this terminal Set $TERM to |builtin-terms|
-------------------------------------------------------------------------
- anything libvte-based vte, vte-256color Y
- (e.g. GNOME Terminal) (aliases: gnome, gnome-256color)
+ ansi (unknown) Y
+ Ghostty ghostty, xterm-ghostty Y
iTerm (original) iterm, iTerm.app N
iTerm2 (new capabilities) iterm2, iTerm2.app Y
Konsole konsole-256color N
+ libvte-based vte, vte-256color Y
+ (e.g. GNOME Terminal) (aliases: gnome, gnome-256color)
Linux virtual terminal linux, linux-256color Y
PuTTY putty, putty-256color Y
rxvt rxvt, rxvt-256color Y
@@ -76,22 +78,26 @@ unlike most other environment variables.
Windows/ConEmu conemu Y
Windows/Cygwin-built Nvim cygwin Y
Windows/Interix interix Y
- Windows/VTP console vtpcon Y
Windows/legacy console win32con Y
+ Windows/VTP console vtpcon Y
xterm or compatible xterm, xterm-256color Y
<
*builtin-terms* *builtin_terms*
-If a |terminfo| database is not available or there is no entry for the current
-terminal, Nvim will map |$TERM| to a builtin entry according to the above
-table, or "ansi" if there is no match. For example "TERM=putty-256color" will
-be mapped to the builtin "putty" entry. See also |tui-colors|.
+Nvim will map |$TERM| to a builtin entry according to the above table if:
+
+- a |terminfo| database is not available on the system
+- no terminfo entry found for the current detected terminal
+- Nvim was built without terminfo support (`has('terminfo')==0`)
The builtin terminfo is not combined with any external terminfo database, nor
can it be used in preference to one. You can thus entirely override any
omissions or out-of-date information in the builtin terminfo database by
supplying an external one with entries for the terminal type.
+For example "TERM=putty-256color" will be mapped to the builtin "putty" entry.
+See also |tui-colors|.
+
Settings depending on terminal *term-dependent-settings*
If you want to set terminal-dependent options or mappings, you can do this in
diff --git a/src/gen/gen_terminfo.lua b/src/gen/gen_terminfo.lua
@@ -20,6 +20,7 @@ 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' },
diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c
@@ -70,7 +70,10 @@ bool terminfo_is_bsd_console(const char *term)
/// @return [allocated] terminfo structure
const TerminfoEntry *terminfo_from_builtin(const char *term, char **termname)
{
- if (terminfo_is_term_family(term, "xterm")) {
+ if (strequal(term, "ghostty") || strequal(term, "xterm-ghostty")) {
+ *termname = "ghostty";
+ return &ghostty_terminfo;
+ } else if (terminfo_is_term_family(term, "xterm")) {
*termname = "xterm";
return &xterm_256colour_terminfo;
} else if (terminfo_is_term_family(term, "screen")) {
diff --git a/src/nvim/tui/terminfo_builtin.h b/src/nvim/tui/terminfo_builtin.h
@@ -82,6 +82,145 @@ static const TerminfoEntry ansi_terminfo = {
},
};
+static const TerminfoEntry ghostty_terminfo = {
+ .bce = true,
+ .has_Tc_or_RGB = false,
+ .Su = false,
+ .max_colors = 0x100,
+ .lines = 24,
+ .columns = 80,
+ .defs = {
+ [kTerm_carriage_return] = "\r",
+ [kTerm_change_scroll_region] = "\033[%i%p1%d;%p2%dr",
+ [kTerm_clear_screen] = "\033[H\033[2J",
+ [kTerm_clr_eol] = "\033[K",
+ [kTerm_clr_eos] = "\033[J",
+ [kTerm_cursor_address] = "\033[%i%p1%d;%p2%dH",
+ [kTerm_cursor_down] = "\n",
+ [kTerm_cursor_invisible] = "\033[?25l",
+ [kTerm_cursor_left] = "\b",
+ [kTerm_cursor_home] = "\033[H",
+ [kTerm_cursor_normal] = "\033[?12l\033[?25h",
+ [kTerm_cursor_up] = "\033[A",
+ [kTerm_cursor_right] = "\033[C",
+ [kTerm_delete_line] = "\033[M",
+ [kTerm_enter_bold_mode] = "\033[1m",
+ [kTerm_enter_ca_mode] = "\033[?1049h",
+ [kTerm_enter_italics_mode] = "\033[3m",
+ [kTerm_enter_reverse_mode] = "\033[7m",
+ [kTerm_enter_standout_mode] = "\033[7m",
+ [kTerm_enter_underline_mode] = "\033[4m",
+ [kTerm_erase_chars] = "\033[%p1%dX",
+ [kTerm_exit_attribute_mode] = "\033(B\033[m",
+ [kTerm_exit_ca_mode] = "\033[?1049l",
+ [kTerm_from_status_line] = "\a",
+ [kTerm_insert_line] = "\033[L",
+ [kTerm_keypad_local] = "\033[?1l\033>",
+ [kTerm_keypad_xmit] = "\033[?1h\033=",
+ [kTerm_parm_delete_line] = "\033[%p1%dM",
+ [kTerm_parm_down_cursor] = "\033[%p1%dB",
+ [kTerm_parm_insert_line] = "\033[%p1%dL",
+ [kTerm_parm_left_cursor] = "\033[%p1%dD",
+ [kTerm_parm_right_cursor] = "\033[%p1%dC",
+ [kTerm_parm_up_cursor] = "\033[%p1%dA",
+ [kTerm_set_a_background] = "\033[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
+ [kTerm_set_a_foreground] = "\033[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
+ [kTerm_set_attributes] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p5%t;2%;%?%p7%t;8%;m",
+ [kTerm_set_lr_margin] = "\033[?69h\033[%i%p1%d;%p2%ds",
+ [kTerm_to_status_line] = "\033]2;",
+ [kTerm_reset_cursor_style] = "\033[2 q",
+ [kTerm_set_cursor_style] = "\033[%p1%d q",
+ [kTerm_enter_strikethrough_mode] = "\033[9m",
+ [kTerm_set_rgb_foreground] = NULL,
+ [kTerm_set_rgb_background] = NULL,
+ [kTerm_set_cursor_color] = NULL,
+ [kTerm_reset_cursor_color] = NULL,
+ [kTerm_set_underline_style] = "\033[4\072%p1%dm",
+ },
+ .keys = {
+ [kTermKey_backspace] = {"\177", NULL},
+ [kTermKey_beg] = {NULL, NULL},
+ [kTermKey_btab] = {"\033[Z", NULL},
+ [kTermKey_clear] = {NULL, NULL},
+ [kTermKey_dc] = {"\033[3~", "\033[3;2~"},
+ [kTermKey_end] = {"\033OF", "\033[1;2F"},
+ [kTermKey_find] = {NULL, NULL},
+ [kTermKey_home] = {"\033OH", "\033[1;2H"},
+ [kTermKey_ic] = {"\033[2~", "\033[2;2~"},
+ [kTermKey_npage] = {"\033[6~", NULL},
+ [kTermKey_ppage] = {"\033[5~", NULL},
+ [kTermKey_select] = {NULL, NULL},
+ [kTermKey_suspend] = {NULL, NULL},
+ [kTermKey_undo] = {NULL, NULL},
+ },
+ .f_keys = {
+ // note: offset by one, f_keys[0] is F1 and so on
+ [0] = "\033OP",
+ [1] = "\033OQ",
+ [2] = "\033OR",
+ [3] = "\033OS",
+ [4] = "\033[15~",
+ [5] = "\033[17~",
+ [6] = "\033[18~",
+ [7] = "\033[19~",
+ [8] = "\033[20~",
+ [9] = "\033[21~",
+ [10] = "\033[23~",
+ [11] = "\033[24~",
+ [12] = "\033[1;2P",
+ [13] = "\033[1;2Q",
+ [14] = "\033[1;2R",
+ [15] = "\033[1;2S",
+ [16] = "\033[15;2~",
+ [17] = "\033[17;2~",
+ [18] = "\033[18;2~",
+ [19] = "\033[19;2~",
+ [20] = "\033[20;2~",
+ [21] = "\033[21;2~",
+ [22] = "\033[23;2~",
+ [23] = "\033[24;2~",
+ [24] = "\033[1;5P",
+ [25] = "\033[1;5Q",
+ [26] = "\033[1;5R",
+ [27] = "\033[1;5S",
+ [28] = "\033[15;5~",
+ [29] = "\033[17;5~",
+ [30] = "\033[18;5~",
+ [31] = "\033[19;5~",
+ [32] = "\033[20;5~",
+ [33] = "\033[21;5~",
+ [34] = "\033[23;5~",
+ [35] = "\033[24;5~",
+ [36] = "\033[1;6P",
+ [37] = "\033[1;6Q",
+ [38] = "\033[1;6R",
+ [39] = "\033[1;6S",
+ [40] = "\033[15;6~",
+ [41] = "\033[17;6~",
+ [42] = "\033[18;6~",
+ [43] = "\033[19;6~",
+ [44] = "\033[20;6~",
+ [45] = "\033[21;6~",
+ [46] = "\033[23;6~",
+ [47] = "\033[24;6~",
+ [48] = "\033[1;3P",
+ [49] = "\033[1;3Q",
+ [50] = "\033[1;3R",
+ [51] = "\033[1;3S",
+ [52] = "\033[15;3~",
+ [53] = "\033[17;3~",
+ [54] = "\033[18;3~",
+ [55] = "\033[19;3~",
+ [56] = "\033[20;3~",
+ [57] = "\033[21;3~",
+ [58] = "\033[23;3~",
+ [59] = "\033[24;3~",
+ [60] = "\033[1;4P",
+ [61] = "\033[1;4Q",
+ [62] = "\033[1;4R",
+ },
+};
+
static const TerminfoEntry interix_8colour_terminfo = {
.bce = true,
.has_Tc_or_RGB = false,