josephburgess/nvumi

github github
utility
stars 38
issues 0
subscribers 1
forks 1
CREATED

2025-02-13

UPDATED

14 days ago


nvumi

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.

๐Ÿ”ง Installation

Using Lazy.nvim

{
  "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 = {}
  }
}

Install numi-cli

You will also need numi-cli.

๐Ÿ–ฅ MacOS

brew install nikolaeu/numi/numi-cli

๐Ÿ“ฆ Linux & Windows

curl -sSL https://s.numi.app/cli | sh

Keybinding to open 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" })

๐Ÿš€ Usage

  1. Run :Nvumi to open a scratch buffer.
  2. Type a natural language expression (20 inches in cm).
  3. The result appears inline or on a new line, based on your settings.
  4. Press <CR> to refresh calculations if you need.
  5. Use <leader>y to yank the current result (or <leader>Y for all results).

๐Ÿ“Œ Variable Assignment

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.

Example

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
  • You can use them in expressions like x * y (which equals 254000.00 cm, btw)

Resetting Variables

Pressing <R> to reset the buffer will also clear all stored variables.

๐Ÿ”„ Custom conversions

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:

  • You can define custom units with aliases, a base unit group, and a conversion ratio.
  • Custom conversions must share the same base_unit (e.g., "speed", "volume").
  • When converting, ratios are relative to the base unit.

Example Configuration

{
  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
      },
    },
  }
}

Examples

Input Output
10 gallons in liters 37.8541 L
5 kmh in mph 3.10686 mph

*๏ฟฝ Custom Functions

nvumi supports user-defined functions.

๐Ÿ’ก How It Works:

  • Define custom functions with aliases.
  • Functions receive arguments (numbers or strings, or nothing) and return computed results.
  • You can include error messages that will surface if something isn't quite right.

Example Configuration

{
  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,
      },
    },
  }
}

Examples

Input Output
square(5) 25
square("abc") "Error: square requires a single numeric argument"
hello("Joe") "Hello, Joe!"
flip() Heads / Tails

** ๐Ÿคน Inline {} 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.

*๏ฟฝ Example Usage

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

๐ŸŽจ Virtual Text Locations

nvumi supports two virtual text modes:

  • Inline (default)
  • Newline

๐Ÿ“… Date Formatting

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"
}

Extra commands

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 filetype

nvumi 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!

๐Ÿ“š Wiki

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.

Contributing

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.

๐Ÿ’ก Roadmap & Planned Features

A few things I'm thinking about adding as I continue trying to expand my knowledge of lua and plugin development:

  • Variable Assignment
  • Custom prefixes/suffixes (=, โ†’, ๐Ÿš€)
  • Auto-evaluate expressions while typing
  • Run on any buffer outside scratch
  • Custom date format
  • Yankable answers (per line/all at once)
  • User-defined unit conversions
  • User-defined maths functions โœ… (latest)

๐Ÿ“œ License

MIT License. See LICENSE for details.

๐Ÿ™Œ Acknowledgements

  • Snacks.nvim: Thanks @folke for the incredible plugin. The 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.
  • numi: Thanks for providing an amazing natural language calculator.