Bazel integration for Neovim: browse, run, test, and build bazel targets from a snacks.nvim picker, with streaming output and LSP support for bazel-managed dependencies.
*_test) or runnable (*_binary) rule kinds*.bazelproject
directory, or the current package)bazel test //dir/...):Bzl sync refreshes targets and configures the language server to
resolve bazel-managed dependencies (see
supported languages)Early development (pre-1.0). Commands and configuration may change between minor versions.
bazel_cmd)With lazy.nvim:
{
"JulOuellet/bzl.nvim",
dependencies = { "folke/snacks.nvim" },
cmd = "Bzl",
opts = {},
}
With Neovim's built-in package manager (0.12+):
vim.pack.add({
"https://github.com/folke/snacks.nvim",
"https://github.com/JulOuellet/bzl.nvim",
})
Calling setup() (or using opts) is optional; defaults apply otherwise.
| Command | Description |
|---|---|
:Bzl targets [testable|runnable] [here] |
Flat fuzzy picker over targets |
:Bzl tree [testable|runnable] [here] |
Collapsible directory tree of targets |
:Bzl sync |
Re-query targets and refresh language server import paths |
:Bzl rerun |
Repeat the last run or test |
Arguments can be combined in any order:
testable — only *_test targetsrunnable — only *_binary targetshere — scope to the current project: the nearest directory containing
a *.bazelproject view file, or the current package when none exists.
Scoped trees open fully expanded.| Key | On a target | On a directory (tree only) |
|---|---|---|
<CR> |
Test, run, or build, chosen by rule kind | Expand / collapse |
<C-t> |
bazel test |
Test the subtree (bazel test //dir/...) |
<C-r> |
bazel run |
— |
<C-b> |
bazel build (background, notifies result) |
Build the subtree |
<C-g> |
Jump to the target's BUILD definition | — |
Run and test output streams into a terminal split at the bottom; builds run
in the background and report through vim.notify.
No global keymaps are created. Example mappings:
keys = {
{ "<leader>bt", function() require("bzl").targets("testable", "here") end, desc = "Bazel Tests (project)" },
{ "<leader>bT", function() require("bzl").tree("testable") end, desc = "Bazel Tests (workspace)" },
{ "<leader>br", function() require("bzl").targets("runnable", "here") end, desc = "Bazel Runnables (project)" },
{ "<leader>bR", function() require("bzl").tree("runnable") end, desc = "Bazel Runnables (workspace)" },
{ "<leader>bb", function() require("bzl").targets() end, desc = "Bazel All Targets" },
{ "<leader>bs", function() require("bzl").sync() end, desc = "Bazel Sync" },
},
Browsing, running, testing, and building targets is language-agnostic:
every target bazel query returns shows up in the pickers, whatever
language it builds. Language support below refers to :Bzl sync
configuring the language server to resolve imports that bazel manages;
Python is the only language wired up so far.
:Bzl sync makes pyright resolve imports that bazel manages, mirroring what
bazel itself puts on sys.path at run time:
imports attribute of
py_* rulesThe discovered paths are pushed as python.analysis.extraPaths to the
pyright/basedpyright clients attached to the workspace. Sync adds the paths
bazel makes necessary and touches nothing else: all other LSP settings are
preserved.
Supported setups: rules_python with the site-packages repository layout
(bzlmod or WORKSPACE) and pyright or basedpyright. Other setups degrade
gracefully; :checkhealth bzl reports what was found.
Defaults:
require("bzl").setup({
-- Binary used for all bazel invocations, e.g. "bazelisk" or an absolute path.
bazel_cmd = "bazel",
runner = {
-- Height of the terminal split that shows run/test output.
height = 15,
},
})
:checkhealth bzl verifies the Neovim version, the bazel binary, and the
snacks.nvim dependency.
With nix, nix develop provides bazel, stylua, and make. Otherwise, have
Neovim >= 0.10, bazelisk, stylua, and make on your PATH.
make test — run the test suite headless (clones mini.nvim into deps/
on first run)make fmt / make fmt-check — format / check lua sourcestests/fixture/ — a small bazel workspace used as an integration-test bed