Skip to content

reo101/nix-update.nvim

Repository files navigation

nix-update.nvim

Dynamically and asynchronously update attributes of fetch-like constructions in Nix

License Neovim version

demo

Installation

Requirements

  • nvim with vim.pack support (0.12+)
  • [Optional] nix-prefetch-url (and possibly more, checkout the included prefetchers here)

Lazy.nvim

{
    "reo101/nix-update.nvim",
    -- setup() is optional; call it only when overriding defaults
    opts = {
      update_actions = { "apply", "flash", "notify" },
    },
    config = function(_, opts)
      require("nix-update").setup(opts)
    end,
}

Configuration

setup() is optional and only configures options. Commands and <Plug> mappings are auto-registered on startup.

Recommended global config (source of truth):

vim.g.nix_update = {
  update_actions = { "apply", "notify" },
  extra_prefetchers = {
    -- ...
  },
}

Lua init file (only needed when customizing options):

require("nix-update").setup(opts)

setup(opts) merges your overrides into vim.g.nix_update and reapplies config.

This setup function accepts the following table:

Update Actions

Action Description
apply Apply the update to the buffer (modifies the text)
flash Briefly highlight the changed region using vim.hl.range
notify Show a vim.notify message with update details
preview Show the update as virtual text overlay (without applying)
require("nix-update").setup({
  -- Actions to run on each update (in order)
  -- Default: { "apply", "notify" }
  update_actions = { "apply", "flash", "notify" },

  -- Extra prefetcher commands
  -- table of tables, where each one looks like this:
  extra_prefetchers = {
    ["myFetch"] = {
      -- (array of strings) Array of required system commands
      ["required-cmds"] = { "cmd1", "cmd2" },
      -- (array of strings) Array of required "fetch" keys
      ["required-keys"] = { "repo", "user" },
      -- (function) Function to run to generate a command
      ["prefetcher"] = function(opts)
        -- guaranteed to be non-nil
        local repo = opts.repo
        local user = opts.user
        -- extra (nonrequired/optional) keys, could be nil
        local submodules = opts.submodules

        -- has to return a table of `cmd` and `opts`
        return {
          cmd = "cmd1",
          args = {
            "wrapped_run",
            "--",
            "cmd2",
            repo .. "/" .. user,
          },
        }
      end,
      -- (function) Function to run to extract the new data
      ["extractor"] = function(stdout)
        -- array of lines from funning the corresponding command
        local first_line = stdout[1]

        -- has to return a table with new value for the keys
        return {
          version = "v." .. first_line
        }
      end,
    },
  },
})

NOTES:

  • The table is empty by default
  • required-cmds and required-keys are optional
  • You can override the builtin definitions by using the same name
  • extra_prefetcher_cmds is still accepted as a backwards-compatible alias
  • Invalid options are validated and reported with vim.notify(..., ERROR)

Usage

Run updates with the scoped command:

:NixUpdate prefetch   " update fetch under cursor
:NixUpdate buffer     " update all fetches in current buffer
:NixUpdate health     " open checkhealth output
:NixUpdate help       " open :h nix-update

Legacy alias (kept for compatibility):

:NixPrefetch          " same as :NixUpdate buffer

Bind the provided <Plug> mappings:

vim.keymap.set("n", "<leader>nc", "<Plug>(NixUpdatePrefetch)")
vim.keymap.set("n", "<leader>nC", "<Plug>(NixUpdatePrefetchBuffer)")

Run it in a Nix file with the cursor inside a fetch:

let
  pname = "vim-fmi-cli";
  version = "0.2.0";
in {
  src = fetchFromGitHub {
    owner = "AndrewRadev";
    repo = pname;
    rev = "v${version}";
    sha256 = "sha256-SOMEOUTDATEDHASH";
  };
}

And nix-update will statically evaluate all string arguments to the fetch (string literals and interpolations), precalculate the correct hash and substitute it in the right place.

let
  pname = "vim-fmi-cli";
  version = "0.2.0";
in {
  src = fetchFromGitHub {
    owner = "AndrewRadev";
    repo = pname;
    rev = "v${version}";
    sha256 = "sha256-RAlvDiNvDVRNtex0aD8WESc4R/mAr7FjWtgzHWa4ZSI=";
  };
}

This updating mechanism allows for some pretty wild stuff, like this:

let
  pname = "vim-fmi-cli";
  version = "0.2.0";
  type = "256";
in rec {
  hash = "SOMEOUTDATEDHASH";

  src = fetchFromGitHub {
    owner = "AndrewRadev";
    repo = pname;
    rev = "v${version}";
    inherit sha256;
  };

  sha256 = "sha${type}-${hash}";
}

Running prefetch_fetch on this will figure out how to construct the current value of sha256 AND skip the common prefix when updating it, i.e. the sha256- part (consisting of the string sha, the value of type, a - and finally the value of hash, which are all scattered around lets and recs) and only update the hash variable to the correct suffix of the new sha256:

let
  pname = "vim-fmi-cli";
  version = "0.2.0";
  type = "256";
in rec {
  hash = "RAlvDiNvDVRNtex0aD8WESc4R/mAr7FjWtgzHWa4ZSI=";

  src = fetchFromGitHub {
    owner = "AndrewRadev";
    repo = pname;
    rev = "v${version}";
    inherit sha256;
  };

  sha256 = "sha${type}-${hash}";
}

I'm not really sure how often would this be of help but it's cool to have it nonetheless. 😄

Development

The lua folder is the compilation output of all files from the fnl directory.

Using nfnl (recommended)

The project uses nfnl for Fennel compilation. When you have nfnl installed in Neovim, saving any .fnl file will automatically compile it to lua/.

Nix Development Shell (recommended)

The repository ships a pinned flake.nix (using flake-parts) so local checks and CI run against the same Neovim build from pinned nixpkgs.

Enter the shell:

nix develop .#default

Run checks in the CI-equivalent shell:

nix develop .#ci -c make check

The check runner intentionally requires vim.pack.add and bootstraps nfnl + plenary.nvim through vim.pack only.

To compile all files at once:

:NfnlCompileAllFiles

Or via the Makefile (requires nvim with nfnl in $PATH):

make

Using standalone Fennel

Alternatively, you can compile using the standalone Fennel compiler:

make fennel

Note

Requires fennel (version 1.2.0) in your $PATH

Cleaning

Clear the compilation output (the lua folder):

make clean

Checks

Run project checks (compilation + runtime smoke checks):

make check

For reproducible checks with pinned tooling:

nix develop .#ci -c make check

This check script bootstraps nfnl and plenary.nvim via vim.pack.add.

Use :checkhealth nix-update to inspect runtime/config/dependency health.

TODO

  • More commands
  • More prefetchers
  • Simpler prefetch commands (not just system ones, maybe lua functions)
  • Style guidelines (with optional enforcement)
  • Telescope pickers for selective updating

Credits

About

Reimplementation of https://github.com/jwiegley/nix-update-el for Neovim using Treesitter

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors