Built inside my www.github.com/Manny-S-04/NvimSetup config, the commit history is there.
LSP completion in a one-line bar at the bottom of the screen, minibuffer / vertico style - instead of a popup that covers the code you're typing next to.
var task = Tas▌
──────────────────────────────────────────────────────────────────────────────
Task Task<> TaskAsyncEnumerableExtensions TaskCanceledException TaskCompletionSource …
No completion framework underneath: it talks to Neovim's built-in LSP client directly.
Full reference inside Neovim: :help mini_complete.
- Candidates from every attached LSP server, plus LuaSnip snippets (optional)
- Accept does what the server intended:
textEditranges,itemDefaults, snippet expansion (${1:arg}placeholders), auto-imports (additionalTextEdits, e.g.using System.Text.Json;) and server commands (Roslyn's complex edits) - Opens after member-access tokens (
obj.) with nothing typed yet - Documentation window above the bar: toggle, scroll, or jump into it to read/yank
- Debounced, and in-flight requests are cancelled as you type
- Closes when you move away; doesn't reopen on the word you just completed
- Fallthrough keys: when the bar is closed every mapped key does its normal job, macros and
.repeat included - Follows your colorscheme (or set your own colours)
- Neovim 0.11+
- An LSP server attached to the buffer
- Optional: LuaSnip
{
"Manny-S-04/mini_complete.nvim",
event = "InsertEnter",
opts = {},
}Other plugin managers: add the repo and call require("mini_complete").setup({}).
Use it instead of nvim-cmp / blink.cmp / built-in vim.lsp.completion autotrigger, two
completion UIs will fight over <CR>.
Advertise snippet support so servers send snippets (and full function signatures):
local caps = vim.lsp.protocol.make_client_capabilities()
caps.textDocument.completion.completionItem.snippetSupport = true
vim.lsp.config("*", { capabilities = caps })| Key | Action |
|---|---|
<C-j> / <C-k> |
next / previous candidate |
<CR> |
accept |
<Esc> |
close the bar (and leave insert mode as usual) |
<F6> |
toggle documentation |
<F7> |
jump into the documentation window (q / <Esc> to come back) |
<C-f> / <C-b> |
scroll documentation |
Every key falls through to its normal behaviour when the bar (or the docs window) isn't open.
Defaults:
require("mini_complete").setup({
debounce_ms = 30,
min_prefix = 1, -- word characters before the bar opens
-- open right after these tokens (when the server also advertises the trigger character);
-- servers advertise far more ("(", ",", " " ...), which would open the bar constantly.
-- Lua users may want ":" too. false to disable.
trigger_characters = { ".", "->", "::" },
resolve_timeout_ms = 300, -- max wait for completionItem/resolve on accept
enabled = function(bufnr) -- false = no completion in this buffer
local bt = vim.bo[bufnr].buftype
return bt == "" or bt == "acwrite"
end,
exclude_filetypes = {},
luasnip = true, -- list LuaSnip snippets, expand LSP snippets with LuaSnip
notify_on_attach = false, -- vim.notify when a completion server attaches
docs = { max_height = 12, border = "rounded" },
keymaps = { -- set any to false to leave that key alone
next = "<C-j>",
prev = "<C-k>",
accept = "<CR>",
cancel = "<Esc>",
toggle_docs = "<F6>",
focus_docs = "<F7>",
scroll_docs_down = "<C-f>",
scroll_docs_up = "<C-b>",
snippet_next = false, -- e.g. "<C-l>": next LuaSnip placeholder
snippet_prev = false, -- e.g. "<C-h>"
},
highlights = {}, -- overrides, see below
})| Group | Default link |
|---|---|
MiniCompleteNormal |
Pmenu |
MiniCompleteSelected |
PmenuSel |
MiniCompleteSnippet |
Special |
MiniCompleteSnippetSelected |
PmenuSel |
MiniCompleteDocNormal |
NormalFloat |
MiniCompleteDocBorder |
FloatBorder |
Override in setup (re-applied after every :colorscheme):
highlights = {
MiniCompleteSelected = { bg = "#45475a", fg = "#f5e0dc", bold = true },
}local mc = require("mini_complete")
mc.is_visible() -- bar open?
mc.select_next() mc.select_prev()
mc.accept() mc.cancel()
mc.toggle_docs() mc.focus_docs():checkhealth mini_complete lists the running completion servers and whether LuaSnip is used.
The bar and docs live in hidden scratch buffers named mini-complete://bar and
mini-complete://docs (visible in :ls!). They are nofile and unmodifiable, so :wall and
friends ignore them.
TextChangedI→ debounce →textDocument/completionto all attached servers (vim.lsp.buf_request_all), cancelling the previous request.- Items are merged with the list's
itemDefaults, filtered byfilterText/labelagainst the word before the cursor, LuaSnip snippets are added, and everything is sorted bysortText. - On accept: resolve the item if the server supports it (to get auto-import edits), delete the typed
word, apply
additionalTextEditswhile an extmark tracks the cursor position, insert thetextEdit/insertText(expanding snippets), then run the item'scommand.
Tests drive the real plugin in a headless Neovim against an in-process fake language server: keys are typed as a user would, so debouncing, autocmds, mappings and LSP round trips all run for real. No dependencies. From the plugin root:
nvim --headless -u NONE -c "luafile tests/run.lua"They cover option merging, highlights (incl. after :colorscheme), opening/filtering/sorting, min_prefix,
excluded filetypes, the trigger-character allowlist, selection keys, <Esc> and key fall-through, and
accepting: labels, itemDefaults + textEditText, LSP snippets and auto-import edits that arrive on resolve.
MIT, see LICENSE.