nvumi is a Neovim plugin that integrates the numi natural language calculator with Snacks.nvim's scratch buffer. It lets you construct natural language expressions and see the results evaluated inline as you type.
{
"josephburgess/nvumi",
dependencies = { "folke/snacks.nvim" },
opts = {
virtual_text = "newline", -- or "inline"
prefix = " ๐ ", -- prefix shown before the output
date_format = "iso", -- or: "uk", "us", "long"
keys = {
run = "<CR>", -- run/refresh calculations
reset = "R", -- reset buffer
yank = "<leader>y", -- yank output of current line
yank_all = "<leader>Y", -- yank all outputs
},
-- see below for more on custom conversions/functions
custom_conversions = {},
custom_functions = {}
}
}
numi-cli
You will also need numi-cli.
brew install nikolaeu/numi/numi-cli
curl -sSL https://s.numi.app/cli | sh
nvumi
nvumi does not have a default keybinding to open the scratch buffer. You can set one:
vim.keymap.set("n", "<leader>on", "<CMD>Nvumi<CR>", { desc = "[O]pen [N]vumi" })
:Nvumi
to open a scratch buffer.20 inches in cm
).<CR>
to refresh calculations if you need.<leader>y
to yank the current result (or <leader>Y
for all results).nvumi supports variables, allowing you to store values and reuse them later. Variable names must start with a letter or underscore, followed by letters, numbers, or underscores.
x = 20 inches in cm
y = 5000
x * y
x + 5
y meters in kilometers
x
stores the result of 20 inches in cm
y
holds 5000
x * y
(which equals 254000.00 cm
, btw)Pressing <R>
to reset the buffer will also clear all stored variables.
nvumi allows you to define custom unit conversions beyond what numi-cli
provides. This feature was inspired by the plugins that exist for the numi desktop app. These should be compatible with nvumi
.
๐ก How It Works:
base_unit
(e.g., "speed"
, "volume"
).{
opts = {
custom_conversions = {
{
id = "kmh",
phrases = "kmh, kmph, klicks, kilometers per hour",
base_unit = "speed",
format = "km/h",
ratio = 1,
},
{
id = "mph",
phrases = "mph, miles per hour",
base_unit = "speed",
format = "mph",
ratio = 1.609344, -- 1 mph = 1.609344 km/h
},
},
}
}
Input | Output |
---|---|
10 gallons in liters |
37.8541 L |
5 kmh in mph |
3.10686 mph |
nvumi supports user-defined functions.
๐ก How It Works:
{
opts = {
custom_functions = {
{
def = { phrases = "square, sqr" },
fn = function(args)
if #args < 1 or type(args[1]) ~= "number" then
return { error = "square requires a single numeric argument" }
end
return { result = args[1] * args[1] }
end,
},
{
def = { id = "greet", phrases = "hello, hi" },
fn = function(args)
local name = args[1] or "stranger"
return { result = "Hello, " .. name .. "!" }
end,
},
{
def = { phrases = "coinflip, flip" },
fn = function()
return { result = (math.random() > 0.5) and "Heads" or "Tails" }
end,
},
},
}
}
Input | Output |
---|---|
square(5) |
25 |
square("abc") |
"Error: square requires a single numeric argument" |
hello("Joe") |
"Hello, Joe!" |
flip() |
Heads / Tails |
{}
Evaluations**nvumi now supports inline evaluations using {}
curly braces. Expressions inside {}
are evaluated first, and the result is inserted into the full line before processing. This in particular
This isn't always necessary, for example when assigning variables or doing normal math expressions, numi should be sufficient, but for custom functions/custom conversions this allows greater interoperability between expressions and your custom setup.
Input | Step 1 - Evaluate {} |
Step 2 - Final Output |
---|---|---|
log({10*10}, {5+5}) |
log(100, 10) |
2 |
{10+20} mph in kmh |
30 mph in kmh |
48.28032 km/h |
nvumi supports two virtual text modes:
Format | Example Output |
---|---|
"iso" |
2025-02-21 |
"us" |
02/21/2025 |
"uk" |
21/02/2025 |
"long" |
February 21, 2025 |
Set this in your config:
opts = {
date_format = "iso", -- or: "uk", "us", "long"
}
There are three extra (possibly useless) commands included with nvumi:
Command | Description |
---|---|
NvumiEvalLine |
Run nvumi on any line in any buffer. |
NvumiEvalBuf |
Run nvumi on the entire buffer anywhere. โ ๏ธ Can be messy! |
NvumiClear |
Clears the buffer's virtual text. |
.nvumi
filetypenvumi was built around a made-up filetype .nvumi
. This was so that the autocommands used by the plugin under the hood would not start trying to evaluate random files.
The fun side-effect of this, however, is that you can create/save .nvumi
files outside of the scratch buffer and they will function exactly the same!
This README hopefully had a good enough outline of current features and examples to get you started, however there is also a Wiki being expanded with more in-depth info.
In particular it includes a Recipes page with some example custom conversions/functions.
This is my first attempt at a Neovim plugin, so contributions are more than welcome! If you encounter issues or have ideas for improvements, please open an issue or submit a pull request on GitHub.
A few things I'm thinking about adding as I continue trying to expand my knowledge of lua
and plugin development:
=
, โ
, ๐
)MIT License. See LICENSE for details.
lua
code runner built into the Scratch buffer inspired this idea in the first place! Thanks also also for your super-human contributions to the community in general.