TL;DR

Open cargo info in Vim or neovim for the package under the cursor using these 4 lines of Lua.

Cargo info

Rust 1.82 was released a couple of days ago. It’s packed with improvements, but one in particular caught my eye. Cargo now has a info sub-command. It displays details about a package in the registry from the comfort of your terminal. Here is an example:

$ cargo info lazy_static
lazy_static #macro #lazy #static
A macro for declaring lazily evaluated statics in Rust.
version: 1.5.0
license: MIT OR Apache-2.0
rust-version: unknown
documentation: https://docs.rs/lazy_static
repository: https://github.com/rust-lang-nursery/lazy-static.rs
crates.io: https://crates.io/crates/lazy_static/1.5.0
features:
  spin        = [dep:spin]
  spin_no_std = [spin]
note: to see how you depend on lazy_static, run `cargo tree --invert --package lazy_static@1.5.0`

Vim and neovim generally composes well with other terminal tools. So how can we easily integrate cargo info and neovim?

Demo

The goal is to press a key and display the cargo info for the crate under the cursor in a Cargo.toml file. Like this:

Introducing keywordprg

In the above demonstration, we press K over a crates name. Then neovim executes the program set in keywordprg, appending the work under the cursor. So if the cursor is on lazy_static

[dev-dependencies]
iai = "0.1"
insta = "1.40.0"
lazy_static = "1.5.0"
#   ^ cursor is here when we press K
mktemp = "0.5"

and keywordprg is set to cargo info, the command cargo info lazy_static is executed in a new terminal.

We can further improve this. Since we want a fast answer (without checking the remote repository), let’s use cargo info --offline. And we would like pretty colors, let’s force that using --color=always.

We only want to alter keywordprg when a Cargo.toml file is edited, because it makes no sense to call cargo info in a Python file. We can use a ftplugin for toml files for that1, so that the plugin is only loaded and executed when a toml file is executed. Furthermore we can change the setting only when opening a file named “Cargo.toml”, instead of changing it for every toml file. We can put the necessary configuration in ~/.config/nvim/ftplugin/toml.lua2, like so:

if vim.endswith(vim.fn.bufname(), "Cargo.toml") then
  vim.opt_local.keywordprg = "cargo info --color=always --offline"
end

-- Add chars that are often part of keys, especially in rust crates
-- (https://toml.io/en/v1.0.0#keys)
vim.opt_local.iskeyword:append "-"

The Lua code above also changes the iskeyword preference. This is so that tokio-test is considered as one word (a “keyword” in Vim parlance), instead of two (tokio and test). With this set, doing K on tokio-test will behave properly, because tokio-test is passed to the command, instead of just tokio.

One final note: the above configuration was the only thing sourced in the demo. All the rest is default, vanilla, neovim 0.10.2. And this does not even require recent neovim features, it has been supported in Vim for many years.

Learnings

Obviously this could be further refined. Other options could be passed to cargo info, like -v to make it more verbose and list dependencies. Some more scripting could add useful features to further improve the integration. For this particular problem, there are even a full plugin with many more features. But the point is to showcase a simple and robust integration3, applicable in a wide variety of contexts. In particular when the use case is too niche, a full plugin is too costly to write and maintain for the benefits it provides.

Let’s highlight the takeaways from this post that are applicable in a wide variety of contexts.

First, a number of Neovim commands can do something with the keyword under the cursor. Adjusting what counts as a keyword boundary is often, but not always, done in the syntax file of a particular language. Or you may just want to make a different trade off and set your own iskeyword value.

Second, the K mapping is often used to look up a word in a documentation (it is set to open man pages by default). It even works in visual mode, looking up the selected text. And arbitrary Vim or shell commands can be used, instead of the default :Man.

Please do read the corresponding parts of the Vim manual linked in this section to learn more. Next time, you may come up with your own quick integration between Vim and another tool. Happy hacking!


EDIT 2024-10-22: Clarify why we use nvim settings instead of a plugin like crates.nvim


  1. To keep the example simple, we don’t clean-up the local preferences that are set here. See the documentation for best practices when sharing ftplugins. ↩︎

  2. It’s also possible to do this in Vimscript instead of Lua. ↩︎

  3. Thanks to @dpom@fosstodon.org for pointing out that crates.nvim has that feature already. ↩︎