Quality-of-life editor modules for Neovim: file, symbol and call trees, a key-hint popup, completion, a statusline, LSP and Treesitter setup, and a set of editor behaviour flags.
Each module is independent — it can be required and configured on its own — and
none is active unless you name it in setup().
Requires Neovim ≥ 0.11. No other plugins required.
With Neovim ≥ 0.12
vim.pack.add({ "https://github.com/mbfoss/keystone.nvim" })
-- Every module, with a starting point for which to enable. Flip any of these.
require("keystone").setup({
-- Language support
lspconfig = true, -- enables the LSP servers configured in lsp/
tsconfig = true, -- treesitter highlighting and folding
completion = true, -- drives insert-mode completion
-- Editor behaviour
tweaks = true, -- Behaviour tweaks (yank highlight, cursor restore, ...)
largefile = true, -- skips treesitter/LSP/ftplugins on large files
marksigns = true, -- shows the marks that are set in the sign column (0.12+)
animate = false, -- interpolated scrolling
-- Replaces something built in
statusline = true, -- sets 'statusline'
select = true, -- replaces vim.ui.select
notify = true, -- replaces vim.notify, adds :Notifications
clue = true, -- popup of the keys that can follow a trigger
-- Adds a command, does nothing until you run it
filetree = true, -- :FileTree
explore = true, -- :FileSelector
symboltree = false, -- :SymbolTree
calltree = false, -- :CallTree
unsaved = false, -- :DiffUnsaved
bufdelete = false, -- :BDelete, :BWipeout, :BDeleteHidden, :BWipeoutHidden
})Any other plugin manager works too — just point it at
mbfoss/keystone.nvim and call setup() yourself.
Installing only puts keystone on the runtimepath; the setup() call is what
decides which modules run. The four groups differ in how intrusive they are: the
last group only registers a command, while the "replaces something built in"
group takes over a global, so those are the ones to turn off if you already have
a statusline, a vim.notify or a key-hint plugin of your own.
You configure Keystone with a single setup() call. The table you pass has one
key per module you want to turn on. Nothing is enabled unless you list it —
modules you leave out stay off.
The value you give a module says how to turn it on:
| Value | Meaning |
|---|---|
true |
Enable the module with its default options. |
{ ... } |
Enable the module, overriding only the options you name. |
false |
Leave the module off (same as omitting it). |
So these two are equivalent — both enable filetree with its defaults:
require("keystone").setup({ filetree = true })
require("keystone").setup({ filetree = {} })A fuller example:
require("keystone").setup({
clue = true, -- on, with defaults
filetree = { width_ratio = 0.2 }, -- on, with one option changed
tweaks = { highlight_on_yank = false }, -- on, with one option changed
notify = false, -- off (could also just omit it)
})Each module's available options are documented on its own page under Modules below.
The setup() above is a convenience wrapper. Every module is standalone, so it
can be configured directly instead — the table you pass is that module's
options, the same table that would follow its key above:
require("keystone.filetree").setup({ width_ratio = 0.2 })Each module has its own page in docs/:
| Module | What it does |
|---|---|
| filetree | A file explorer in a side window |
| explore | A file selector for navigating the filesystem |
| calltree | The LSP call hierarchy of the symbol under the cursor |
| symboltree | The LSP document symbols of the current buffer |
| clue | A popup listing the keys that can follow a trigger |
| completion | LSP-driven autocompletion with <Tab>/<CR> |
| statusline | A statusline assembled from configurable sections |
| lspconfig | Enables configured LSP servers, with log rotation |
| tsconfig | Treesitter highlighting and folding, per buffer |
| marksigns | Shows the marks that are set in the sign column |
| largefile | Opens large files without Treesitter, LSP or ftplugins |
| notify | A floating notification UI |
| select | A floating vim.ui.select prompt with fuzzy filtering |
| unsaved | Diff modified buffers against disk |
| bufdelete | Delete or wipe buffers, keeping the window layout |
| animate | Interpolated scrolling |
| tweaks | Seven editor behaviour flags |
Enabling the relevant module registers its command:
| Command | Module | Purpose |
|---|---|---|
:FileTree |
filetree | Toggle the file-tree side window |
:FileSelector |
explore | Open the file selector |
:CallTree |
calltree | Show the call hierarchy of the symbol under the cursor |
:SymbolTree |
symboltree | Toggle the document-symbol side window |
:Notifications |
notify | List or clear the notification history |
:DiffUnsaved |
unsaved | Diff unsaved buffers against disk |
:BDelete :BWipeout :BDeleteHidden :BWipeoutHidden |
bufdelete | Delete or wipe buffers, keeping the window layout |
:checkhealth keystoneReports the Neovim version, the modules whose setup() has run (active) and
those it has not (inactive), then a section per active module listing only the
options you changed from that module's defaults. Unrecognised option names are
reported as warnings: setup() merges the table you pass wholesale, so a
misspelled option would otherwise be accepted without complaint.
Some modules add a deeper check of their own: :checkhealth keystone.tsconfig
reports the installed parsers and any missing queries.
The module pages cover the common cases. For the complete, authoritative list,
each module documents every field as a Config class annotation near the top of
its file (lua/keystone/<module>.lua).
MIT. See ATTRIBUTIONS.md for third-party credits.
Contributing and internals: see DEVELOPMENT.md.