Skip to content

Releases: nmfirdausw/batu.nvim

v0.10.0

Choose a tag to compare

@nmfirdausw nmfirdausw released this 21 Oct 14:36
4d69f65

What's Changed

Features

Find

  • Add fuzzy :find over the cwd, with optional fff backend by @nmfirdausw in #11

Highlights

  • Fuzzy :find. :find now fuzzy searches every file under the current directory, instead of matching a path pattern. :find cfgld opens src/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 calls fff.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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 21 Oct 11:53
5123258

What's Changed

Features

Format

Highlights

  • Formatting that follows gq. A new format option formats with Neovim's own settings: your formatprg or a custom formatexpr when 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 (or false) in a filetype's ftplugin to opt that buffer in or out.
  • A failing formatter is safe. If formatprg fails, such as a missing stylua, 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 21 Oct 10:54
c28d63e

What's Changed

Features

Completion

  • Add documentation and signature_help options by @nmfirdausw in #9

Refactors

Completion

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.
  • autoshow brings the old behaviour back. Set autoshow = true and 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 20 Oct 10:53
bcd77ea

What's Changed

Features

Completion

Highlights

  • Completion out of the box. A new completion option 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 20 Oct 07:34
f518201

What's Changed

Features

Diagnostics

Highlights

  • Diagnostic defaults. A new diagnostic option applies severity_sort, update_in_insert and virtual_text through vim.diagnostic.config().
  • Yours on top. Pass a table and it is merged over the defaults. Pass false to 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 20 Oct 06:18
1535ddc

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 lsp option enables servers through Neovim's native vim.lsp.config() and vim.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. grh and grl toggle 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 19 Oct 15:55
77043f3

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 autocmds option disables any of them by name, or all of them at once.
  • Native. Plain vim.api.nvim_create_autocmd calls under one batu.autocmds augroup, 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 example autocmds = { 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 19 Oct 15:14
93479f5

What's Changed

Features

Keymaps

Highlights

  • Default keymaps. Batu now ships a small set of keymaps that smooth over everyday editing.
  • Yours to change. The keymaps option skips, replaces or adds keymaps per mode and key, with any vim.keymap.set option.
  • Space as leader. A new leader option defaults to a space for <leader> and <localleader>.
  • Native. Plain vim.keymap.set calls, 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 example keymaps = { 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 19 Oct 14:12
995c765

What's Changed

Features

OSC7

Highlights

  • New osc7 option. One line in setup() 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 TermRequest event 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

Choose a tag to compare

@nmfirdausw nmfirdausw released this 19 Oct 13:54
57576b4

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.opt values 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

Full Changelog: https://github.com/nmfirdausw/batu.nvim/commits/v0.1.0