feat(ux): sexy intro #38378

Problem: Intro is not sexy.

Solution: Make it sexy.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
Evgeni Chasnovski
2026-03-20 22:56:00 +02:00
committed by GitHub
parent 9986360065
commit 9595f07425
2 changed files with 162 additions and 77 deletions

View File

@@ -26,6 +26,7 @@
#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@@ -4188,20 +4189,24 @@ bool may_show_intro(void)
void intro_message(bool colon)
{
static char *(lines[]) = {
N_(NVIM_VERSION_LONG),
"│ ╲ ││",
"││╲╲││",
"││ ╲ │",
"",
N_(NVIM_VERSION_LONG),
"────────────────────────────────────────────",
N_("Nvim is open source and freely distributable"),
"https://neovim.io/#chat",
"",
N_("type :help nvim<Enter> if you are new! "),
N_("type :checkhealth<Enter> to optimize Nvim"),
N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "),
"",
N_("type :help news<Enter> to see changes in v%s.%s"),
"",
"────────────────────────────────────────────",
N_("type :help nvim<Enter> if you are new! "),
N_("type :checkhealth<Enter> to optimize Nvim"),
N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "),
"────────────────────────────────────────────",
N_("type :help news<Enter> for v%s.%s notes "),
"────────────────────────────────────────────",
N_("Help poor children in Uganda!"),
N_("type :help Kuwasha<Enter> for information "),
N_("type :help Kuwasha<Enter> for information "),
};
// blanklines = screen height - # message lines
@@ -4247,7 +4252,7 @@ void intro_message(bool colon)
}
if (*mesg != NUL) {
do_intro_line(row, mesg, colon);
do_intro_line(row, mesg, colon, i < 3);
}
row++;
@@ -4258,22 +4263,64 @@ void intro_message(bool colon)
}
}
static void do_intro_line(int row, char *mesg, bool colon)
/// Adds extra highlighting.
static void do_intro_line(int row, char *mesg, bool colon, bool is_logo)
{
int l;
// Center the message horizontally.
int col = vim_strsize(mesg);
col = (Columns - col) / 2;
if (col < 0) {
col = 0;
}
grid_line_start((!colon && ui_has(kUIMultigrid)) ? &firstwin->w_grid : &default_gridview, row);
// Split up in parts to highlight <> items differently.
// Compute special highlighting attributes
int id_attr = syn_id2attr(syn_name2id("Identifier"));
int nontext_attr = syn_id2attr(syn_name2id("NonText"));
int special_attr = syn_id2attr(syn_name2id("Special"));
int string_attr = syn_id2attr(syn_name2id("String"));
// Handle logo lines
if (is_logo) {
bool seen_diagonal = false;
for (char *p = mesg; *p != NUL;) {
int clen = utfc_ptr2len(p);
int attr = 0;
// Multi-byte (box-drawing) character.
if ((uint8_t)(*p) >= 0x80) {
// Found "╲" diagonal logo part.
seen_diagonal = seen_diagonal || (clen == 3 && utf_ptr2char(p) == 0x2572);
attr = seen_diagonal ? string_attr : special_attr;
}
col += grid_line_puts(col, p, clen, attr);
p += clen;
}
grid_line_flush();
return;
}
// Try highlighting full line:
// - Version starts with "NVIM".
// - Separator line consists from ─ (UTF-8: E2 94 80).
bool is_version = mesg[0] == 'N' && mesg[1] == 'V' && mesg[2] == 'I' && mesg[3] == 'M';
bool is_sep = utfc_ptr2len(mesg) == 3 && utf_ptr2char(mesg) == 0x2500;
if (is_version || is_sep) {
int clen = is_sep ? 3 : 1;
int attr = is_sep ? nontext_attr : string_attr;
for (char *p = mesg; *p != NUL;) {
col += grid_line_puts(col, p, clen, attr);
p += clen;
}
grid_line_flush();
return;
}
// Highlight `:...<Enter>` differently.
for (char *p = mesg; *p != NUL; p += l) {
for (l = 0;
p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>'));
@@ -4281,7 +4328,25 @@ static void do_intro_line(int row, char *mesg, bool colon)
l += utfc_ptr2len(p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
col += grid_line_puts(col, p, l, *p == '<' ? HL_ATTR(HLF_8) : 0);
if (*p == '<') {
col += grid_line_puts(col, p, l, HL_ATTR(HLF_8));
} else {
// Check for ":command" pattern before a <key> segment.
char *colon_pos = memchr(p, ':', (size_t)l);
if (colon_pos != NULL && p[l] == '<') {
// No highlight for "type ".
int prefix_len = (int)(colon_pos - p);
col += grid_line_puts(col, p, prefix_len, 0);
// Highlight ":".
col += grid_line_puts(col, colon_pos, 1, HL_ATTR(HLF_8));
// Highlight "command" (after the ":").
int cmd_len = l - prefix_len - 1;
col += grid_line_puts(col, colon_pos + 1, cmd_len, id_attr);
} else {
col += grid_line_puts(col, p, l, 0);
}
}
}
grid_line_flush();
}

View File

@@ -2150,22 +2150,26 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
command('set cmdheight=0')
feed(':intro<CR>')
screen:expect([[
|*5
{MATCH:.*}|
|*3
{16:│} {26:╲} {26:││} |
{16:││}{26:╲╲││} |
{16:││} {26:╲} {26:│} |
|
{MATCH:.*}|
{1:────────────────────────────────────────────} |
Nvim is open source and freely distributable |
https://neovim.io/#chat |
|
type :help nvim{18:<Enter>} if you are new! |
type :checkhealth{18:<Enter>} to optimize Nvim |
type :q{18:<Enter>} to exit |
type :help{18:<Enter>} for help |
|
{MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}|
|
{1:────────────────────────────────────────────} |
type {18::}{25:help nvim}{18:<Enter>} if you are new! |
type {18::}{25:checkhealth}{18:<Enter>} to optimize Nvim |
type {18::}{25:q}{18:<Enter>} to exit |
type {18::}{25:help}{18:<Enter>} for help |
{1:────────────────────────────────────────────} |
type {18::}{25:help news}{18:<Enter>} for v{MATCH:%d+%.%d+} notes{MATCH: *}|
{1:────────────────────────────────────────────} |
Help poor children in Uganda! |
type :help Kuwasha{18:<Enter>} for information |
|*4
type {18::}{25:help Kuwasha}{18:<Enter>} for information |
|*2
^ |
]])
feed('<CR>')
@@ -2208,22 +2212,26 @@ describe('ui/ext_messages', function()
-- Note parts of it depends on version or is indeterministic. We ignore those parts.
local introscreen = [[
^ |
{1:~ }|*4
{MATCH:.*}|
{1:~ }|*2
{1:~ }{16:│} {26:╲} {26:││}{1: }|
{1:~ }{16:││}{26:╲╲││}{1: }|
{1:~ }{16:││} {26:╲} {26:│}{1: }|
{1:~ }|
{1:~{MATCH: +}}{26:NVIM {MATCH:%S+}}{1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Nvim is open source and freely distributable{1: }|
{1:~ }https://neovim.io/#chat{1: }|
{1:~ }|
{1:~ }type :help nvim{18:<Enter>} if you are new! {1: }|
{1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type :q{18:<Enter>} to exit {1: }|
{1:~ }type :help{18:<Enter>} for help {1: }|
{1:~ }|
{1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}|
{1:~ }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help nvim}{18:<Enter>} if you are new! {1: }|
{1:~ }type {18::}{25:checkhealth}{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type {18::}{25:q}{18:<Enter>} to exit {1: }|
{1:~ }type {18::}{25:help}{18:<Enter>} for help {1: }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help news}{18:<Enter>} for v{MATCH:%d+%.%d+} notes {1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Help poor children in Uganda!{1: }|
{1:~ }type :help Kuwasha{18:<Enter>} for information {1: }|
{1:~ }|*5
{1:~ }type {18::}{25:help Kuwasha}{18:<Enter>} for information {1: }|
{1:~ }|*3
]]
local showmode = { { '-- INSERT --', 5, 'ModeMsg' } }
screen:expect(introscreen)
@@ -2244,22 +2252,26 @@ describe('ui/ext_messages', function()
grid = [[
^ |
{1:~ }{4: }{1: }|
{1:~ }|*3
{MATCH:.*}|
{1:~ }|
{1:~ }{16:│} {26:╲} {26:││}{1: }|
{1:~ }{16:││}{26:╲╲││}{1: }|
{1:~ }{16:││} {26:╲} {26:│}{1: }|
{1:~ }|
{1:~{MATCH: +}}{26:NVIM {MATCH:%S+}}{1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Nvim is open source and freely distributable{1: }|
{1:~ }https://neovim.io/#chat{1: }|
{1:~ }|
{1:~ }type :help nvim{18:<Enter>} if you are new! {1: }|
{1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type :q{18:<Enter>} to exit {1: }|
{1:~ }type :help{18:<Enter>} for help {1: }|
{1:~ }|
{1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}|
{1:~ }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help nvim}{18:<Enter>} if you are new! {1: }|
{1:~ }type {18::}{25:checkhealth}{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type {18::}{25:q}{18:<Enter>} to exit {1: }|
{1:~ }type {18::}{25:help}{18:<Enter>} for help {1: }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help news}{18:<Enter>} for v{MATCH:%d+%.%d+} notes {1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Help poor children in Uganda!{1: }|
{1:~ }type :help Kuwasha{18:<Enter>} for information {1: }|
{1:~ }|*5
{1:~ }type {18::}{25:help Kuwasha}{18:<Enter>} for information {1: }|
{1:~ }|*3
]],
showmode = showmode,
}
@@ -2281,22 +2293,26 @@ describe('ui/ext_messages', function()
screen:expect {
grid = [[
^ |
|*4
{MATCH:.*}|
|*2
{16:│} {26:╲} {26:││} |
{16:││}{26:╲╲││} |
{16:││} {26:╲} {26:│} |
|
{MATCH: +}{26:NVIM {MATCH:%S+}}{MATCH: +}|
{1:────────────────────────────────────────────} |
Nvim is open source and freely distributable |
https://neovim.io/#chat |
|
type :help nvim{18:<Enter>} if you are new! |
type :checkhealth{18:<Enter>} to optimize Nvim |
type :q{18:<Enter>} to exit |
type :help{18:<Enter>} for help |
|
{MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}|
|
{1:────────────────────────────────────────────} |
type {18::}{25:help nvim}{18:<Enter>} if you are new! |
type {18::}{25:checkhealth}{18:<Enter>} to optimize Nvim |
type {18::}{25:q}{18:<Enter>} to exit |
type {18::}{25:help}{18:<Enter>} for help |
{1:────────────────────────────────────────────} |
type {18::}{25:help news}{18:<Enter>} for v{MATCH:%d+%.%d+} notes {MATCH: +}|
{1:────────────────────────────────────────────} |
Help poor children in Uganda! |
type :help Kuwasha{18:<Enter>} for information |
|*5
type {18::}{25:help Kuwasha}{18:<Enter>} for information |
|*3
]],
}
@@ -2411,22 +2427,26 @@ it('ui/ext_multigrid supports intro screen', function()
[3:--------------------------------------------------------------------------------]|
## grid 2
^ |
{1:~ }|*4
{MATCH:.*}|
{1:~ }|*2
{1:~ }{16:│} {26:╲} {26:││}{1: }|
{1:~ }{16:││}{26:╲╲││}{1: }|
{1:~ }{16:││} {26:╲} {26:│}{1: }|
{1:~ }|
{1:~{MATCH: +}}{26:NVIM {MATCH:%S+}}{1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Nvim is open source and freely distributable{1: }|
{1:~ }https://neovim.io/#chat{1: }|
{1:~ }|
{1:~ }type :help nvim{18:<Enter>} if you are new! {1: }|
{1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type :q{18:<Enter>} to exit {1: }|
{1:~ }type :help{18:<Enter>} for help {1: }|
{1:~ }|
{1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}|
{1:~ }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help nvim}{18:<Enter>} if you are new! {1: }|
{1:~ }type {18::}{25:checkhealth}{18:<Enter>} to optimize Nvim{1: }|
{1:~ }type {18::}{25:q}{18:<Enter>} to exit {1: }|
{1:~ }type {18::}{25:help}{18:<Enter>} for help {1: }|
{1:~ ──────────────────────────────────────────── }|
{1:~ }type {18::}{25:help news}{18:<Enter>} for v{MATCH:%d+%.%d+} notes {1:{MATCH: +}}|
{1:~ ──────────────────────────────────────────── }|
{1:~ }Help poor children in Uganda!{1: }|
{1:~ }type :help Kuwasha{18:<Enter>} for information {1: }|
{1:~ }|*4
{1:~ }type {18::}{25:help Kuwasha}{18:<Enter>} for information {1: }|
{1:~ }|*2
## grid 3
|
]],