pesto.nvim is a Bazel runner plugin for Neovim.
It integrates with Bazel using the Build Event Protocol to find, fetch, and parse error logs for failed build actions, including logs stored remotely.
This approach gives it robust quickfix support for multi-language builds, letting you avoid the task of writing a multi-compiler errorformat.
errorformat system, so logs from most rule sets should be quickfix loadable.rules_javarules_ccrules_gorules_rustrules_scala:help pesto.adding_rule_sets and :help pesto.config.errorformats.pesto.nvim identifies and fetches failed action logs using the BEP logs; as an alternative pesto.nvim also supports loading the quickfix list using bazel's stderr output (see :help pesto.config.quickfix_log_source).bazel wrapper command with autocomplete support::Pesto bazel <bazel-subcommand> [subcommand-args]uv (optional)pesto.nvim ships with a simple gRPC client written in Python to fetch the logs.
See "Remote caching (experimental)" below for more information.vim.pack.add({
'https://github.com/lewistg/pesto.nvim'
})
lazy.nvim{
'lewistg/pesto.nvim',
---@type pesto.Config
opts = {},
-- Pesto is lazy by default (see :h lua-plugin-lazy)
lazy = false,
}
This repository includes a few example Bazel repositories in the ./examples directory.
You can use them to try out pesto.nvim.
Below is a suggested exercise using the example C project.
:checkhealth pesto
The checks for the Neovim version and Bazel executable are the most important.main.c:$ cd ./examples/c-example
$ nvim ./src/main.c
:Pesto bazel build :<Tab>
It should auto-complete to the following::Pesto bazel build :main
The //src package has a cc_binary target named main.<Enter>.<Enter>.:make, Pesto adds the <Enter> keymap to quickly dismiss the build output terminal buffer.main.c or some other source file (e.g., ids.c).bazel by running the build sub-command (:help pesto.commands.build)::Pesto build
You don't need to call a setup function to configure pesto.nvim.
You can instead just set vim.g.pesto.
Here is the default configuration:
---@type pesto.Config
vim.g.pesto = {
--- Name of bazel binary that Pesto invokes. Should be on your `$PATH` or a
--- path to an executable.
bazel_executable = "bazel",
-- Callback invoked to run bazel.
bazel_runner = function(opts)
require("pesto.components").default_runner(opts)
end,
--- Configuration for the `:Pesto build [target_resolver]` subcommand. Defines the possible pre-defined target queries
--- Please see `:help pesto.config.build_target_resolvers` for more details.
build_target_resolvers = {
...
}
--- Logging level (see `:checkhealth pesto` to get the log file's path)
log_level = "info",
--- Indicates which logs Pesto should use to populate the quickfix window
--- following a build.
quickfix_log_source = "bep",
--- When set to true, Pesto will inject the `--build_event_json_file=$BEP_FILE`
--- Bazel command line option. If you use the default runner, then following
--- the build Pesto will parse the resulting build events tree and the quickfix
--- list.
enable_bep_integration = true,
--- When this option is true and when you are using the default runner, a
--- terminal buffer will be opened automatically when bazel is invoked.
auto_open_build_term = true,
--- This list is used to determine the errorformat string used to parse the
--- output of a failed action. It effectively defines a mapping from Bazel
--- action mnemonics to errorformats.
--- See the "Quickfix integration" section below for details.
errorformats = {},
--- The default set of errorformats. Covers some of the major rule
--- sets. You shouldn't need to override this.
default_errorformats = {
...
},
--- This option is still in development. See the "Remote caching" section below
bytestream_client = nil,
--- Configuration for the `:Pesto bazel` subcommand auto-completion
cli_completion = {
--- Completion strategy. There are three modes:
--- * "bash": With this mode completion gets powered by the Bazel's own bash completion script (e.g., /etc/bash_completion.d/bazel)
--- * "lua": A less sophisticated lua implementation of Bazel
--- * "automatic": With this mode, we attempt to first use the bash completion script. If it's unavailable, then we fallback to the lua implementation.
mode = "automatic",
--- For the "bash" completion strategy, this is the amount of time to wait
--- for the bash completion script to finish before timing out.
bash_timeout = 15000,
--- Absolute path to the bash completion script. If the setting is not defined,
--- then Pesto falls back to searching for the script in `/etc/bash_completion.d/`
bash_completion_script = nil,
},
}
If you prefer, however, pesto.nvim does support a setup function:
---@type pesto.Config
local config = {...}
require("pesto").setup(config)
One of pesto.nvim's key features is the way it loads errors into the quickfix list.
Here's how it works by default at a high-level:
pesto.nvim finds the logs for failed build actions.pesto.nvim needs an errorformat string to parse the logs.
pesto.nvim handles this by defining a mapping from action mnemonic to errorformat string.pesto.nvim comes with a default mapping for some of the more popular rule sets (:help pesto.config.default_errorformats) but also lets users extend this mapping through the pesto.config.errorformats config setting.(If you're new to Bazel and the terms "action" and "action mnemonic" are new to you, please see :help pesto.bazel_concepts for a quick primer on these Bazel concepts.)
As an example, here are the failed action logs for a basic Java compilation error:
src/main/java/net/starlark/java/syntax/LambdaExpression.java:43: error: cannot find symbol
public xpression getBody() {
^
symbol: class xpression
location: class LambdaExpression
Compare those logs with the bazel command's raw stderr output:
INFO: Analyzed target //src/main/java/net/starlark/java/syntax:syntax (0 packages loaded, 0 targets configured).
ERROR: /home/user/dev/bazel/src/main/java/net/starlark/java/syntax/BUILD:17:13: Building src/main/java/net/starlark/java/syntax/libsyntax.
jar (42 source files) and running annotation processors (AutoAnnotationProcessor, AutoBuilderProcessor, AutoOneOfProcessor, AutoValuePro
cessor, AutoValueGsonAdapterFactoryProcessor) failed: (Exit 1): java failed: error executing Javac command (from target //src/main/java/
net/starlark/java/syntax:syntax) external/rules_java++toolchains+remotejdk21_linux/bin/java '--add-exports=jdk.compiler/com.sun.tools.ja
vac.api=ALL-UNNAMED' '--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED' ... (remaining 19 arguments skipped)
src/main/java/net/starlark/java/syntax/LambdaExpression.java:43: error: cannot find symbol
public xpression getBody() {
^
symbol: class xpression
location: class LambdaExpression
Target //src/main/java/net/starlark/java/syntax:syntax failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 1.008s, Critical Path: 0.75s
INFO: 2 processes: 2 internal.
ERROR: Build did NOT complete successfully
INFO: Build Event Protocol files produced successfully.
[Process exited 1]
In comparison, the action logs provide a cleaner source for quickfix error parsing. Also, Bazel stores separate log files per failed action. This makes them an ideal source for quickfix parsing since errors from different actions are not interleaved like they are in the stderr output.
When RBE is involved, the latency of downloading action log files from the remote cache can be a disadvantage.
As a fallback, pesto.nvim does support parsing the stderr output directly.
The approach is still based on the action mnemonic.
You can read more about it in :help pesto.config.quickfix_log_source and :help pesto.remote_caching.
This list shows a subset of the commands. For a full list see :help pesto.commands.
" This command is somewhat equivalent to `:!bazel <bazel-subcommand> [subcommand-args]`.
" By default the Bazel command is run asyncronously in a terminal buffer.
" It supports auto-completion for targets.
:Pesto bazel <bazel-subcommand> [subcommand-args]
" Provides a way to quickly invoke a Bazel build without typing out a full bazel
" command. "Target resolvers" are user-defined callbacks that return either a
" target query or target pattern. For more info see `:h pesto.commands.build`.
:Pesto build [target-resolver-id]
" Runs `bazel build --compile_one_dependency <current-file>`
:Pesto compile-one-dep
" Commands to open the `BUILD` or `BUILD.bazel` file for the current source
" file in a horizontal or vertical split.
:Pesto sp-build
:Pesto vs-build
" Yanks the label for the current source file's package.
:Pesto yank-package-label
" Yanks the last run Bazel command to the unnamed ("") and plus registers ("+).
:Pesto copy-last-bazel-command
" Load the quickfix list using a BEP JSON file generated elsewhere (perhaps by
" your CI/CD pipeline).
:Pesto load-quickfix <bep-json-file>
Mature Bazel setups will involve remote execution and remote caching. This means compiler logs will sometimes be stored in a remote cache. Pesto must fetch these logs in order to parse them and populate the quickfix list.
Remote caching services for Bazel serve assets, like the stderr logs, through gRPC-based APIs. Since implementing a gRPC client in Lua would be a significant undertaking, Pesto delegates the remote log fetches to a helper "bytestream" client written in Python. Pesto will prompt you to set up this client before attempting to use it the first time.
Before setting up the helper bytestream client, consider trying pty_output for quickfix_log_source (:help pesto.config.quickfix_log_source).
It is simpler and may work well enough for your needs.
pesto.nvim, now archived.pesto.nvim may have an advantage:neovim-tasks's approach is more "on-rails." pesto.nvim's bazel wrapper command with auto-complete provides more flexibility in comparison.neovim-tasks parses Bazel's stderr to load the quickfix list. If your build mixes compilers (e.g., C++, Java, Rust, etc..), you may need to craft an errorformat that covers all possible error outputs. pesto.nvim's approach to the quickfix list and errorformat lets you avoid this.