Repository navigation
Releases: nmfirdausw/batu.nvim
Release list
v0.10.0
What's Changed
Features
Find
- Add fuzzy :find over the cwd, with optional fff backend by @nmfirdausw in #11
Highlights
- Fuzzy
:find.:findnow fuzzy searches every file under the current directory, instead of matching a path pattern.:find cfgldopenssrc/deep/config_loader.lua, and completion lists the fuzzy matches, best first. - fff as an option. Pass
{ fff = true }to search with fff. Batu installs it, downloads its binary and callsfff.file_search(). - Outside the cwd. A path that leaves the cwd, such as
:find ../other/cfg, searches that directory./abs/path/and~/work too. - Native by default. No new dependencies unless you choose fff.
Usage
require("batu").setup({
find = true, -- fuzzy :find (default)
-- find = { fff = true }, -- search with fff
-- find = { fff = { max_threads = 8 } }, -- fff with its own options
-- find = false, -- leave :find alone
})| Option | Value | Effect |
|---|---|---|
find |
true (default) |
Fuzzy :find over the cwd |
{ fff = true } |
Search with fff | |
{ fff = { ... } } |
fff with your options | |
false |
Leave :find alone |
How it works
:find is powered by 'findfunc', so opening a file and its completion use the same search. The file list comes from rg, fd or git ls-files, so .git and ignored files are skipped. Without any of them it walks the directory and skips only .git. The list is built once per command line, so new files show up the next time.
With fff, Batu installs it with vim.pack, downloads its prebuilt binary (or builds it with cargo) on install and update, and calls file_search(). While fff is still indexing the cwd, or if it fails, the file list answers instead, so :find never blocks. A path that leaves the cwd always uses the file list, so fff keeps its index of the cwd.
Upgrading
find is on by default, so setup() now sets 'findfunc'. If you set your own, pass find = false, or remove yours and let Batu do it. fff is opt-in and adds nothing unless you set find = { fff = true }; building it needs cargo only when no prebuilt binary is available.
Full Changelog: v0.9.0...v0.10.0
v0.9.0
What's Changed
Features
Format
- Add format setup with optional format on save by @nmfirdausw in #10
Highlights
- Formatting that follows
gq. A newformatoption formats with Neovim's own settings: yourformatprgor a customformatexprwhen set, otherwise the language server. - Format on save, off by default. Turn it on with
format = { on_save = true }, or toggle it while you work. - Per filetype, from an ftplugin. Set
vim.b.format_on_save = true(orfalse) in a filetype's ftplugin to opt that buffer in or out. - A failing formatter is safe. If
formatprgfails, such as a missingstylua, your text is kept and the formatter's error is shown. Before, the buffer was replaced with the error output.
Usage
require("batu").setup({
format = {
on_save = true, -- format before every write (default: false)
timeout_ms = 1000, -- how long a language server may take
},
-- format = false, -- leave formatting alone
})An external formatter for one filetype, in ftplugin/lua.lua:
vim.opt_local.formatprg = "stylua --indent-type Spaces --indent-width 2 --quote-style AutoPreferDouble -"
vim.b.format_on_save = true -- format this filetype on save, whatever `on_save` is| Option | Default | Effect |
|---|---|---|
format |
true |
false leaves formatting alone, a table sets the options below |
on_save |
false |
true formats before every write |
timeout_ms |
1000 |
How long a language server may take to format |
Keys
| Key | Mode | Action |
|---|---|---|
<leader>cf |
normal, visual | Format the buffer, or the selection |
<leader>tf |
normal | Toggle format on save for the current buffer |
<leader>tF |
normal | Toggle format on save for every buffer |
gq gqq |
normal, visual | Format, keeping the text when the formatter fails |
A buffer's own setting (vim.b.format_on_save, or <leader>tf) wins over the global one.
How it works
If formatprg or a custom formatexpr is set, that formatter runs over the buffer as gggqG would. A formatprg wins over the server's default formatexpr, which is attached after your ftplugin runs, so one set there works. Otherwise the language server formats, with timeout_ms, and only when a server can format the buffer. gq would otherwise fall back to wrapping lines at textwidth. Special buffers and URL-style ones such as oil:// are skipped on save.
Upgrading
format is on by default, but nothing formats on save until you set on_save = true. gq, gqq and visual gq are now Batu mappings. To keep Neovim's own, set them to false in the keymaps table. If you use another formatter plugin, pass format = false.
Full Changelog: v0.8.0...v0.9.0
v0.8.0
What's Changed
Features
Completion
- Add documentation and signature_help options by @nmfirdausw in #9
Refactors
Completion
- Add autoshow option, off by default by @nmfirdausw in #8
Highlights
- No more menu while typing. The completion menu now opens only on request, for both the native engine and blink.cmp. Open it with
<C-x><C-o>,<C-n>or<C-p>in insert mode, or<Tab>on the command line. autoshowbrings the old behaviour back. Setautoshow = trueand the menu opens as you type, as in v0.7.0.- Documentation and signature help. Two new options, off by default, show the selected item's documentation and the signature of the call you are typing.
- Same keys on both engines. The native engine now has blink's
<C-k>to toggle signature help, and<C-b>/<C-f>to scroll the documentation.
Usage
require("batu").setup({
completion = {
autoshow = false, -- open the menu as you type (default: on request)
documentation = false, -- show docs next to the menu
signature_help = false, -- show the signature while typing a call
-- blink = true, -- use blink.cmp instead of the built-in
},
})| Option | Default | Effect |
|---|---|---|
autoshow |
false |
true opens the menu as you type, on the command line and in LSP completion |
documentation |
false |
true shows the selected item's documentation beside the menu |
signature_help |
false |
true shows signature help on trigger characters and after accepting an item |
Keys
| Key | Mode | Action |
|---|---|---|
<C-x><C-o> |
insert | LSP omni completion |
<C-n> <C-p> |
insert | Open the menu (with blink, the first item is selected) |
<Tab> |
command line | Open the menu |
<C-k> |
insert | Toggle signature help |
<C-b> <C-f> |
insert | Scroll the documentation |
How it works
On the native engine, documentation adds popup to completeopt, and signature_help calls vim.lsp.buf.signature_help() when a trigger character is typed and after an item is accepted. With autoshow off, LSP completion is attached without autotrigger, and o is added to complete so <C-n> / <C-p> include the server's items. On blink, the options set completion.menu.auto_show, completion.documentation.auto_show and signature.enabled, with signature.trigger.show_on_accept so the signature shows after accepting an item.
Upgrading
The menu no longer opens as you type. To keep the old behaviour, set completion = { autoshow = true }. On the native engine, <C-k> in insert mode now toggles signature help instead of entering digraphs.
Full Changelog: v0.7.0...v0.8.0
v0.7.0
What's Changed
Features
Completion
- Add native and blink.cmp completion by @nmfirdausw in #7
Highlights
- Completion out of the box. A new
completionoption turns on Neovim's built-in completion: the command-line menu opens as you type, and LSP completion triggers on every letter, digit and_, not only after.or:. - blink.cmp as an option. Pass
{ blink = true }to use blink.cmp v2 instead. Batu installs it, builds its Rust matcher and wires up LSP capabilities. - Yours on top. Pass a table of blink options and they are merged over Batu's defaults.
- Native by default. No new dependencies unless you choose blink.
Usage
require("batu").setup({
completion = true, -- built-in (default)
-- completion = { blink = true }, -- blink.cmp
-- completion = { blink = { keymap = { preset = "super-tab" } } },
-- completion = false, -- leave it alone
})| Option | Value | Effect |
|---|---|---|
completion |
true (default) |
Neovim's built-in completion |
{ blink = true } |
blink.cmp v2 with Batu's defaults | |
{ blink = { ... } } |
blink.cmp with your options merged on top | |
false |
Leave completion alone |
blink.cmp defaults
- The command-line menu shows as you type, like the native one.
- Nothing is preselected.
<CR>accepts the selected item, and acts as usual when none is selected.
Blink options go under blink, and command-line options under blink.cmdline.
How it works
The built-in mode adds each server's trigger characters once per client, so attaching to more buffers doesn't grow the list. The blink mode installs blink.cmp (main) and blink.lib with vim.pack, builds the Rust fuzzy matcher on first start and after each update, and sets blink's capabilities before servers are enabled.
Requirements
blink.cmp v2 has no prebuilt binaries, so completion = { blink = true } needs cargo and errors without it. The first start, and the first after each blink update, compiles the matcher, which can take a minute. The built-in mode needs nothing extra.
Upgrading
completion is on by default, so setup() now enables built-in completion without any change to your config. If you use another completion plugin, pass completion = false.
Full Changelog: v0.6.0...v0.7.0
v0.6.0
What's Changed
Features
Diagnostics
- Add diagnostic config and keymaps by @nmfirdausw in #6
Highlights
- Diagnostic defaults. A new
diagnosticoption appliesseverity_sort,update_in_insertandvirtual_textthroughvim.diagnostic.config(). - Yours on top. Pass a table and it is merged over the defaults. Pass
falseto leave Neovim's diagnostic settings alone. - Keymaps. Five default maps to peek at, toggle and list diagnostics.
- Native. No new dependencies.
Usage
require("batu").setup({
diagnostic = { update_in_insert = false }, -- merge on top of the defaults
keymaps = { n = { ["<leader>dq"] = false } }, -- skip a diagnostic keymap
})| Option | Value | Effect |
|---|---|---|
diagnostic |
true (default) |
Apply Batu's diagnostic defaults |
| a table | Apply the defaults with your vim.diagnostic.config() values merged on top |
|
false |
Leave Neovim's diagnostic settings alone |
Keymaps
| Mode | Key | Action |
|---|---|---|
n |
<C-w>d |
Open the diagnostic float of the line, hiding inline diagnostics until it closes |
n |
<leader>dv |
Toggle virtual text and virtual lines |
n |
<leader>dt |
Toggle diagnostics |
n |
<leader>dl |
Send buffer diagnostics to the location list |
n |
<leader>dq |
Send workspace diagnostics to the quickfix list |
They are part of the default keymaps, so they follow the usual rules: keymaps = false sets none, and false on a single key skips it.
How it works
<C-w>d replaces Neovim's own map of the same key. It still opens the float, and also hides virtual text and lines while the float is open, then restores them when it closes. <leader>dv switches between virtual text and virtual lines, never both.
Upgrading
diagnostic and its keymaps are on by default, so setup() now sets diagnostic options and five keymaps without any change to your config. To keep your current behavior, pass diagnostic = false and skip the keymaps you don't want.
Full Changelog: v0.5.0...v0.6.0
v0.5.0
What's Changed
Features
LSP
- Add language server setup with inlay hints, code lenses and toggle keymaps by @nmfirdausw in #5
Highlights
- Language servers. A new
lspoption enables servers through Neovim's nativevim.lsp.config()andvim.lsp.enable(). - Bring your own configs. Use nvim-lspconfig, which Batu can install for you, or write your own in
lsp/<name>.lua. - Inlay hints and code lenses. Each turns on in buffers whose server supports it.
- Toggle keymaps.
grhandgrltoggle inlay hints and code lenses per buffer. - Native. Batu installs no servers and adds no dependencies beyond the optional nvim-lspconfig.
Usage
require("batu").setup({
lsp = {
lspconfig = true,
servers = {
lua_ls = true, -- use the config Neovim finds
gopls = { settings = { gopls = { staticcheck = true } } }, -- merge on top of it
pyright = false, -- skip
},
inlay_hints = true,
codelens = true,
},
})| Option | Value | Effect |
|---|---|---|
lsp |
false (default) |
Do nothing |
| a table | Set up language servers with the options below | |
lsp.lspconfig |
false (default) |
Leave server configs to you, in lsp/<name>.lua |
true |
Install and load nvim-lspconfig with vim.pack |
|
lsp.servers |
a table | Servers to enable, by name. true uses the config Neovim finds, a table is merged on top of it, false skips the server |
lsp.inlay_hints |
false (default) |
Don't enable inlay hints |
true |
Enable them where the server supports it | |
lsp.codelens |
false (default) |
Don't enable code lenses |
true |
Enable them where the server supports it | |
lsp.keymaps |
true (default) |
Set the toggle keymaps below |
false |
Set none |
Keymaps
| Mode | Key | Action |
|---|---|---|
n |
grh |
Toggle inlay hints in the buffer |
n |
grl |
Toggle code lenses in the buffer |
Both are set only in buffers whose server supports the feature. Neither clashes with Neovim's own gr* maps, and grx still runs the code lens on the current line.
How it works
Batu enables a feature when the server attaches, after checking that the server supports it. A server table such as lua_ls = require("lsp.lua_ls") is passed to vim.lsp.config() and then enabled.
Batu sets no diagnostic config, which is left for a later release.
Upgrading
Nothing changes for existing configs. lsp is off by default, so setup() does not enable any server or set any keymap until you pass an lsp table.
Full Changelog: v0.4.0...v0.5.0
v0.4.0
What's Changed
Features
Autocmds
- Add default autocmds with per-autocmd opt-out by @nmfirdausw in #4
Highlights
- Default autocmds. Batu now ships a small set of autocmds that smooth over everyday editing.
- Yours to turn off. The
autocmdsoption disables any of them by name, or all of them at once. - Native. Plain
vim.api.nvim_create_autocmdcalls under onebatu.autocmdsaugroup, with no dependencies.
Default autocmds
| Name | Event | Action |
|---|---|---|
highlight_yank |
TextYankPost |
Briefly highlight yanked text |
restore_cursor |
BufReadPost |
Restore the cursor to its last known position when reopening a file |
auto_create_dir |
BufWritePre |
Create missing parent directories before writing a file |
auto_reload |
FocusGained, BufEnter |
Reload buffers changed on disk outside of Neovim |
close_with_q |
FileType |
Close help, quickfix, man and similar buffers with q |
Usage
require("batu").setup({
autocmds = {
highlight_yank = false, -- skip a default
},
})| Option | Value | Effect |
|---|---|---|
autocmds |
true (default) |
Create Batu's default autocmds |
false |
Create none, and never load the module | |
| a table | Create the defaults except those set to false, by name |
How it works
All autocmds share the batu.autocmds augroup, so they are cleared and recreated cleanly if setup() runs again. A name in the table that Batu doesn't know produces a warning instead of being ignored silently.
auto_create_dir skips URL-style buffers such as oil:// or scp://, so it never creates stray local directories for them. close_with_q applies to the help, qf, man, lspinfo and checkhealth filetypes.
Upgrading
Like v0.3.0, existing configs will notice this release. autocmds is on by default, so setup() now creates the autocmds above.
- To keep your current setup, use
setup({ autocmds = false }). - To drop one autocmd, set it to
false, for exampleautocmds = { auto_reload = false }. - If you already create similar autocmds yourself, they run alongside Batu's. Disable the matching one to avoid doing the work twice.
Full Changelog: v0.3.0...v0.4.0
v0.3.0
What's Changed
Features
Keymaps
- Add default keymaps and a default leader by @nmfirdausw in #3
Highlights
- Default keymaps. Batu now ships a small set of keymaps that smooth over everyday editing.
- Yours to change. The
keymapsoption skips, replaces or adds keymaps per mode and key, with anyvim.keymap.setoption. - Space as leader. A new
leaderoption defaults to a space for<leader>and<localleader>. - Native. Plain
vim.keymap.setcalls, with no dependencies.
Default keymaps
| Mode | Key | Action |
|---|---|---|
x |
<, > |
Shift while keeping the selection |
n, x |
j, k, <Down>, <Up> |
Move by visual line when wrapped, by count otherwise |
x |
J, K |
Move the selected lines down or up |
x |
p |
Paste over the selection without overwriting the register |
n |
Y |
Yank to the end of the line |
n |
<C-d>, <C-u>, n, N |
Keep the cursor centered |
Usage
require("batu").setup({
leader = ",",
keymaps = {
x = { [">"] = false }, -- skip a default
n = {
["<leader>w"] = { "<cmd>write<CR>", desc = "Save" }, -- add your own
},
},
})| Option | Value | Effect |
|---|---|---|
keymaps |
true (default) |
Apply Batu's default keymaps |
false |
Apply none | |
| a table | Apply the defaults, plus your own and changes to them. Each value is false to skip, a string or function to replace the right-hand side, or { rhs, desc = "...", ... } |
|
leader |
" " (default) |
Use space for <leader> and <localleader> |
| another string | Use that key instead | |
false |
Leave them alone |
How it works
leader is set first in setup(), before any mapping is defined, so Batu's keymaps and the ones you add through keymaps use it. Batu never overrides a leader you have already set, so vim.g.mapleader = "," before setup() still wins. Your own <leader> mappings pick it up only if you define them after setup().
Upgrading
Unlike v0.2.0, existing configs will notice this release. keymaps and leader are on by default, so setup() now maps the keys above and sets the leader to a space if you haven't set one.
- To keep your current setup, use
setup({ keymaps = false, leader = false }). - To drop one keymap, set it to
false, for examplekeymaps = { x = { p = false } }. - If you map any of these keys yourself after
setup(), your mapping wins.
Full Changelog: v0.2.0...v0.3.0
v0.2.0
What's Changed
Features
OSC7
- Sync cwd from terminal OSC 7 by @nmfirdausw in #2
Highlights
- New
osc7option. One line insetup()keeps Neovim's working directory in sync with your terminal. - Opt-in. It defaults to
false, so nothing changes unless you enable it. - Native. It uses Neovim's built-in
TermRequestevent and adds no dependencies.
Usage
require("batu").setup({ osc7 = true })| Value | Effect |
|---|---|
false (default) |
Do nothing |
true |
Sync Neovim's working directory to the one the terminal reports via OSC 7 |
How it works
Many shells and terminals emit an OSC 7 escape sequence whenever the working directory changes. With osc7 = true, Batu listens for it in :terminal buffers and changes Neovim's directory to match. Relative paths, file pickers and new terminals then start where you are.
This needs a shell that emits OSC 7. Many shells do by default or through a config option. If yours doesn't, the setting has no effect.
Upgrading
No changes are needed. Existing configs behave exactly as in v0.1.0.
Full Changelog: v0.1.0...v0.2.0
v0.1.0
What's Changed
Features
Add setup entry point and default options by @nmfirdausw in #1
Highlights
- Install and edit. One
setup()call gives you a modern editor with no extra configuration. - Thoughtful defaults. Indentation, search, completion, folding and interface options are tuned and grouped by purpose.
- Pure Neovim. Batu sets native
vim.optvalues and pulls in no dependencies. - Fully overridable. Change any value with a table, or turn the defaults off with
options = false. - Treesitter folds. Code folds by syntax and starts fully expanded.
Installation
Requires Neovim 0.12+ (uses vim.pack).
vim.pack.add({ "https://github.com/nmfirdausw/batu.nvim" })
require("batu").setup()Options guide
setup() applies Batu's defaults through vim.opt. The options field controls what happens:
| Value | Effect |
|---|---|
true (default) |
Apply Batu's defaults |
false |
Apply none |
| a table | Apply the defaults, with your values merged on top |
-- Batu's defaults
require("batu").setup()
-- No defaults: you set every option yourself
require("batu").setup({ options = false })
-- Defaults, with your own values on top
require("batu").setup({
options = {
shiftwidth = 2,
tabstop = 2,
softtabstop = 2,
relativenumber = false,
},
})Options in your table override Batu's values. Options you leave out keep Batu's defaults. You can also pass options Batu doesn't set.
What the defaults set
Editing
| Option | Value | Why |
|---|---|---|
expandtab |
true |
Insert spaces instead of tabs |
shiftwidth, tabstop, softtabstop |
4 |
4-space indentation |
shiftround |
true |
Round indents to a multiple of shiftwidth |
smartindent |
true |
Indent automatically on new lines |
virtualedit |
"block" |
Move freely in visual block mode |
Behaviour
| Option | Value | Why |
|---|---|---|
clipboard |
"unnamedplus" |
Share the system clipboard |
confirm |
true |
Ask before discarding unsaved changes |
undofile |
true |
Keep undo history across sessions |
timeoutlen |
300 |
Shorter wait for mapped key sequences |
updatetime |
250 |
Faster swap writes and CursorHold events |
Interface
| Option | Value | Why |
|---|---|---|
number, relativenumber |
true |
Hybrid line numbers |
cursorline, cursorlineopt |
true, "number" |
Highlight only the current line number |
cmdheight |
0 |
Hide the command line until needed |
laststatus |
3 |
One global statusline |
signcolumn |
"yes" |
Avoid layout shifts when signs appear |
list, listchars |
true, tab » , trail ·, nbsp ␣ |
Show whitespace |
scrolloff, sidescrolloff |
8 |
Keep context around the cursor |
pumheight |
10 |
Limit popup menu height |
splitbelow, splitright |
true |
Open splits below and to the right |
Search
| Option | Value | Why |
|---|---|---|
ignorecase, smartcase |
true |
Ignore case unless you type capitals |
inccommand |
"split" |
Preview :s results live in a split |
Completion
| Option | Value | Why |
|---|---|---|
completeopt |
"menuone,noselect" |
Show the menu without preselecting an item |
wildignorecase |
true |
Case-insensitive command-line completion |
wildmode |
"noselect:lastused,full" |
Offer last-used matches first |
Folding
| Option | Value | Why |
|---|---|---|
foldmethod |
"expr" |
Fold by expression |
foldexpr |
"v:lua.vim.treesitter.foldexpr()" |
Treesitter-based folds |
foldlevelstart |
99 |
Open all folds when a file opens |
Treesitter folds need a parser for the filetype. Without one, nothing folds.
New Contributors
- @nmfirdausw made their first contribution in #1
Full Changelog: https://github.com/nmfirdausw/batu.nvim/commits/v0.1.0