Extends Neovim's ui2 system with a new target: msgarea
This lets you:
Open cmdline completions in a vertico + marginalia style layout.
(currently only blink.cmp is supported, working on native completions...)
|
Open arbitrary ephemeral windows (like your favorite picker) in the msgarea view. (mini.pick is shown here)
|
Route ui-messages to the msgarea view either as ephemeral or persistent windows.
|
Open as many persistent msgarea windows as you like in an organized, tabbed view.
|
and more...
- Route
ui-messagesto the msgarea as ephemeral or persistent windows. See:h msgarea-ephemeralfor distinction between ephemeral vs persistent. - Open persistent or ephemeral windows in the msgarea with the native
nvim_open_win()api. This plugin patchesnvim_open_winandnvim_win_set_configto accept a new option,relative = "msgarea". - Integrate with any plugin that exposes
win_configoptions somewhere in the plugin config. See:h msgarea-integrating-with-pluginsfor details.
- Neovim >= 0.12
Warning
This is an experimental plugin built on top of the already experimental ui2.
As this plugin monkey-patches several ui2 functions and aims to follow the development of ui2 closely, any upstream
breaking changes may introduce breaking changes here.
vim.pack (recommended)
vim.pack.add({
"https://github.com/edisj/msgarea.nvim",
})lazy.nvim
{
"edisj/msgarea.nvim",
opts = {},
}Make sure ui2 is enabled
-- highly recommended to set the default target to "msg",
-- otherwise "cmd" messages will be covered by msgarea windows
-- for nvim-0.12:
require "vim._core.ui2".enable({
enable = true,
msg = { target = "msg" }
})
-- for nvim nightly:
require("vim._core.ui2").enable({
enable = true,
msg = {
targets = {
default = "msg",
-- you can set msgarea targets here or in config.msgarea_targets
-- lua_print = "msgarea",
-- lua_error = "msgarea",
}
}
})Add the following to your init.lua or somewhere in your config
-- *highly* recommended to use one of these settings,
-- otherwise screen lines bounce around a lot when the view is opened/closed
-- vim.o.splitkeep = "topline"
-- vim.o.splitkeep = "screen"
-- pass no argument or `{}` to use default config
require("msgarea").setup()You may want to set a keymap to close the msgarea
vim.keymap.set("n", "<C-w>m", function()
require("msgarea").close_all()
end, { desc = "close msgarea" })require("msgarea").setup({
-- Whether to enable the plugin. Can be disabled at runtime
-- with `require("msgarea").config({ enable = false })`
enable = true,
-- List of message `kind`s that should be sent to the msgarea
-- NOTE: equivalent to setting <kind> = "msgarea" in ui2.cfg.msg.targets
-- Valid message kinds:
-- - "lua_print"
-- - "lua_error"
-- - "list_cmd"
-- - ...
-- (see :h ui-messages for all valid kinds)
msgarea_targets = {},
-- Title for persistent messages in message area
-- These messages are routed through `<kind> = "msgarea"` in ui2 config.
-- Can either be:
-- string - A static title to use for all messages.
-- function - A callable that receives parameter `kind` (:h ui-messages)
-- and returns a string or `nil`.
-- If return is `nil`, message is treated as ephemeral.
-- Examples:
-- - to treat `lua_print` and `lua_error` kinds as persistent
-- and everything else as ephemeral:
-- message_title = function(kind)
-- local titles = { lua_print = " Lua Print ", lua_error = " Lua Error " }
-- return titles[kind]
-- end
-- - to make every message persistent with a static title:
-- message_title = " Messages "
message_title = function(kind) end,
-- View options
view = {
-- Determines whether to place the view below statusline
-- or as a regular split.
-- Valid styles are:
-- msgarea - Open below statusline in the message area.
-- split - Open as a regular "below" split.
style = "msgarea",
-- Min and max height of the view, if fraction between 0-1,
-- it is % of editor size, otherwise absolute size
min_height = 1,
max_height = 0.3,
-- Determines position of tabs in winbar.
-- Analagous to `title_pos` in win config (:h nvim_open_win())
-- Valid positions are: "left" | "center" | "right"
winbar_pos = "left",
-- Optional separator between tabs in winbar.
winbar_separator = "",
-- Minimum number of msgarea windows needed to show tabs.
-- e.g. if 2, then tab is hidden when only a single window is open.
winbar_min_tabs = 1,
},
-- Cmdline completion options
cmdline = {
-- Whether to enable cmdline completion behaviors.
-- If using an external cmdline like `tiny-cmdline.nvim`,
-- you probably want to disable this.
enable = false,
-- Which completion plugin you use.
-- Valid providers:
-- "native" - builtin cmdline completions
-- "mini.cmdline" - https://github.com/nvim-mini/mini.cmdline
-- "blink.cmp" - https://github.com/saghen/blink.cmp
cmp_provider = "native",
-- Whether to dynamically resize cmdheight as completion window changes height.
-- If `false`, the height is set to `vim.o.pumheight`, otherwise
-- `vim.o.pumheight` is used as the max height.
dynamic_height = false,
-- Debounce in ms for resizing the cmdheight. If set to 0, typing quickly
-- will cause the the cmdheight to bounce rapidly.
-- If `dynamic_height = false` then this has no effect.
resize_throttle_ms = 200,
-- Whether to add description text to cmdline completions.
-- For ex-cmds, parsed directly from `index.txt` (:h ex-cmds-index),
-- for usercmds, obtained from `vim.api.nvim_get_commands({})`.
-- Notes:
-- - If you find some usercmds are missing descriptions, they may have
-- been lazy loaded after cache was populated.
-- Call `require("msgarea.cache").refresh()` to repopulate cache at any time.
-- - If using `blink.cmp` and you are not seeing descriptions, make sure in
-- your bilnk config, `cmdline.completion.menu.draw.columns` includes "label_description".
-- - Subject to change depending on https://github.com/neovim/neovim/pull/39672
descriptions = true,
},
})The Neovim 0.12 ui2 system introduced a new way for routing messages to different "views" as targets (e.g. "cmd", "pager", "msg").
While it's fantastic and eliminated the annoying "press enter" blocking messages, some messages are a bit too ephermal.
Routing to msg only shows the message for a few seconds and requires you to click on the message window with your mouse to keep it alive.
Routing to cmd shows the message in a convenient bottom view, but is immediately removed on cursor move.
Of course, you can always press g< to refocus the last message in the pager, but in some cases, I find myself having to reopen and close the last message several times to reread because it doesn't stick around when exiting (for example when reading a lua error and then going to the line referred to in stack trace).
The solution presented here is a new view (msgarea) that is sits somewhere in between pager and cmd in terms of function.
Setting a message target to msgarea will now open a "Messages" window below the statusline and automatically handle setting/resetting cmdheight when message content is emitted to the handler.
Focusing the pager with e.g. g< still closes the "Messages" window, so that behavior is the same.
When I say msgarea, I mean the view or region of the screen below the statusline, where the "Messages" window is just one window that can be opened in the view.
Here, any arbitrary window can be opened in the msgarea (see Usage and Examples), where the winbar provides clickable tabs to see which windows are active.
- Open a terminal window in the msgarea
- Open quickfix/loclist in the msgarea
- Open a picker in the msgarea
- Emulating Emacs
M-x find-filewith 'mini.pick'
Close all active msgarea windows.
Show or refresh the msgarea view.
Hide, but do not close, all msgarea windows.
Subsequent require("msgarea").show() will restore saved view state.



