mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00

Problem: We have an unmaintained Vimscript parser and cmdline highlighting mechanism, with which it is hard to leverage the treesitter highlighter. Long messages result in a hit-enter-prompt. Solution: Implement a vim.ui_attach() UI, that replaces the message grid (orphaning some 3000+ LOC core C code). Introduce an experimental vim._extui module, because removing the message grid at the same time is too risky. The new UI leverages the bundled treesitter highlighter and parser for Vimscript, as well as the matchparen plugin, to highlight the cmdline. Messages are truncated in the cmdline area, or placed in a floating message box in the bottom right corner. Special ("list_cmd") messages and the message history are shown in a, "more prompt" (now a fully interactive regular window). Various default UI elements ('showcmd', 'ruler') are also placed in the cmdline area, as virtual text. `require('vim._extui').enable({})` enables the experimental UI. `{ msg.pos = 'box' }` or `:set cmdheight=0` enables the message box variant. Followup: - Come to a consensus for how best to represent messages (by default). - Start removing message grid when this is deemed a successful replacement. When that is finished, make this new UI the default and update a lot of tests.
88 lines
3.2 KiB
Lua
88 lines
3.2 KiB
Lua
local api, o = vim.api, vim.o
|
|
local M = {
|
|
msg = nil, ---@type vim._extui.messages
|
|
cmd = nil, ---@type vim._extui.cmdline
|
|
ns = api.nvim_create_namespace('nvim._ext_ui'),
|
|
augroup = api.nvim_create_augroup('nvim._ext_ui', {}),
|
|
cmdheight = -1, -- 'cmdheight' option value set by user.
|
|
-- Map of tabpage ID to box/cmd/more/prompt window IDs.
|
|
wins = {}, ---@type { ['box'|'cmd'|'more'|'prompt']: integer }[]
|
|
bufs = { box = -1, cmd = -1, more = -1, prompt = -1 },
|
|
tab = 0, -- Current tabpage.
|
|
cfg = {
|
|
enable = true,
|
|
msg = { -- Options related to the message module.
|
|
---@type 'box'|'cmd' Type of window used to place messages, either in the
|
|
---cmdline or in a separate ephemeral message box window.
|
|
pos = 'cmd',
|
|
box = { -- Options related to the message box window.
|
|
timeout = 4000, -- Time a message is visible.
|
|
},
|
|
},
|
|
},
|
|
}
|
|
local wincfg = { -- Default cfg for nvim_open_win().
|
|
relative = 'laststatus',
|
|
style = 'minimal',
|
|
col = 0,
|
|
row = 1,
|
|
width = 10000,
|
|
height = 1,
|
|
noautocmd = true,
|
|
zindex = 300,
|
|
}
|
|
|
|
--- Ensure the various buffers and windows have not been deleted.
|
|
function M.tab_check_wins()
|
|
M.tab = api.nvim_get_current_tabpage()
|
|
if not M.wins[M.tab] then
|
|
M.wins[M.tab] = { box = -1, cmd = -1, more = -1, prompt = -1 }
|
|
end
|
|
|
|
for _, type in ipairs({ 'box', 'cmd', 'more', 'prompt' }) do
|
|
if not api.nvim_buf_is_valid(M.bufs[type]) then
|
|
M.bufs[type] = api.nvim_create_buf(false, true)
|
|
if type == 'cmd' then
|
|
-- Attach highlighter to the cmdline buffer.
|
|
local parser = assert(vim.treesitter.get_parser(M.bufs.cmd, 'vim', {}))
|
|
M.cmd.highlighter = vim.treesitter.highlighter.new(parser)
|
|
elseif type == 'more' then
|
|
-- Close more window with Ctrl-C.
|
|
vim.keymap.set('n', '<C-c>', '<C-w>c', { buffer = M.bufs.more })
|
|
end
|
|
end
|
|
|
|
local setopt = false
|
|
if not api.nvim_win_is_valid(M.wins[M.tab][type]) then
|
|
local top = { vim.opt.fcs:get().horiz or o.ambw == 'single' and '─' or '-', 'WinSeparator' }
|
|
local border = (type == 'more' or type == 'prompt') and { '', top, '', '', '', '', '', '' }
|
|
local cfg = vim.tbl_deep_extend('force', wincfg, {
|
|
focusable = type == 'more',
|
|
mouse = type ~= 'cmd' and true or nil,
|
|
anchor = type ~= 'cmd' and 'SE' or nil,
|
|
hide = type ~= 'cmd' or M.cmdheight == 0 or nil,
|
|
title = type == 'more' and 'Messages' or nil,
|
|
border = type == 'box' and not o.termguicolors and 'single' or border or 'none',
|
|
_cmdline_offset = type == 'cmd' and 0 or nil,
|
|
})
|
|
M.wins[M.tab][type] = api.nvim_open_win(M.bufs[type], false, cfg)
|
|
api.nvim_win_set_hl_ns(M.wins[M.tab][type], M.ns)
|
|
setopt = true
|
|
elseif api.nvim_win_get_buf(M.wins[M.tab][type]) ~= M.bufs[type] then
|
|
api.nvim_win_set_buf(M.wins[M.tab][type], M.bufs[type])
|
|
setopt = true
|
|
end
|
|
|
|
if setopt then
|
|
if type == 'box' and o.termguicolors then
|
|
vim.wo[M.wins[M.tab][type]].winblend = 30
|
|
end
|
|
vim.wo[M.wins[M.tab][type]].linebreak = false
|
|
vim.wo[M.wins[M.tab][type]].smoothscroll = true
|
|
vim.wo[M.wins[M.tab][type]].eventignorewin = 'all'
|
|
end
|
|
end
|
|
end
|
|
|
|
return M
|