+
+
+
+
+## :grey_question: Why kickstart-nix.nvim
+
+If Nix and Neovim have one thing in common,
+it's that many new users don't know where to get started.
+Most Nix-based Neovim setups assume deep expertise in both realms,
+abstracting away Neovim's core functionalities
+as well as the Nix internals used to build a Neovim config.
+Frameworks and module-based DSLs are opinionated and difficult to diverge from
+with one's own modifications.
+
+`kickstart-nix.nvim` is different:
+It's geared for users of all levels,
+making the migration of Neovim configurations to Nix straightforward.
+This project aims to be as simple as possible, while allowing
+for maximum flexibility.
+
+> [!NOTE]
+>
+> Similar to [`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim),
+> this repository is meant to be used by **you** to begin your
+> **Nix**/Neovim journey; remove the things you don't use and add what you miss.
+
+## :milky_way: Philosophy
+
+- KISS principle with sane defaults.
+- Manage plugins + external dependencies using Nix
+ (managing plugins shouldn't be the responsibility of a plugin).
+- Configuration entirely in Lua[^1] (Vimscript is also possible).
+ This makes it easy to migrate from non-nix dotfiles.
+- Use Neovim's built-in loading mechanisms. See:
+ - [`:h initialization`](https://neovim.io/doc/user/starting.html#initialization)
+ - [`:h runtimepath`](https://neovim.io/doc/user/options.html#'runtimepath')
+ - [`:h packadd`](https://neovim.io/doc/user/repeat.html#%3Apackadd)
+- Use Neovim's built-in LSP client, with Nix managing language servers.
+
+[^1]: The absence of a Nix module DSL for Neovim configuration is deliberate.
+ If you were to copy the `nvim` directory to `$XDG_CONFIG_HOME`,
+ and install the plugins, it would work out of the box.
+
+## :star2: Features
+
+- Use either nixpkgs or flake inputs as plugin sources.
+- Usable on any device with Neovim and Nix installed.
+- Create multiple derivations with different sets of plugins,
+ and simple regex filters to exclude config files.
+- Uses Nix to generate a `.luarc.json` in the devShell's `shellHook`.
+ This sets up lua-language-server to recognize all plugins
+ and the Neovim API.
+
+## :bicyclist: Test drive
+
+If you have Nix installed (with [flakes](https://wiki.nixos.org/wiki/Flakes) enabled),
+you can test drive this by running:
+
+```console
+nix run "github:nix-community/kickstart-nix.nvim"
+```
+
+## :books: Usage
+
+1. Click on [Use this template](https://github.com/nix-community/kickstart-nix.nvim/generate)
+to start a repo based on this template. **Do _not_ fork it**.
+1. Add/remove plugins to/from the [Neovim overlay](./nix/neovim-overlay.nix).
+1. Add/remove plugin configs to/from the `nvim/plugin` directory.
+1. Modify as you wish (you will probably want to add a color theme, ...).
+ See: [Design](#robot-design).
+1. You can create more than one package using the `mkNeovim` function by
+ - Passing different plugin lists.
+ - Adding `ignoreConfigRegexes` (e.g. `= [ "^ftplugin/.*.lua" ]`).
+
+> [!TIP]
+>
+> The nix and lua files contain comments explaining
+> what everything does in detail.
+
+## :zap: Installation
+
+### :snowflake: NixOS (with flakes)
+
+1. Add your flake to you NixOS flake inputs.
+1. Add the overlay provided by this flake.
+
+```nix
+nixpkgs.overlays = [
+ # replace with the name you chose
+ .overlays.default
+];
+```
+
+You can then add the overlay's output(s) to the `systemPackages`:
+
+```nix
+environment.systemPackages = with pkgs; [
+ nvim-pkg # The default package added by the overlay
+];
+```
+
+> [!IMPORTANT]
+>
+> This flake uses `nixpkgs.wrapNeovimUnstable`, which has an
+> unstable signature. If you set `nixpkgs.follows = "nixpkgs";`
+> when importing this into your flake.nix, it may break.
+> Especially if your nixpkgs input pins a different branch.
+
+### :penguin: Non-NixOS
+
+With Nix installed (flakes enabled), from the repo root:
+
+```console
+nix profile install .#nvim
+```
+
+## :robot: Design
+
+Directory structure:
+
+```sh
+── flake.nix
+── nvim # Neovim configs (lua), equivalent to ~/.config/nvim
+── nix # Nix configs
+```
+
+### :open_file_folder: Neovim configs
+
+- Set options in `init.lua`.
+- Source autocommands, user commands, keymaps,
+ and configure plugins in individual files within the `plugin` directory.
+- Filetype-specific scripts (e.g. start LSP clients) in the `ftplugin` directory.
+- Library modules in the `lua/user` directory.
+
+Directory structure:
+
+```sh
+── nvim
+ ├── ftplugin # Sourced when opening a file type
+ │ └── .lua
+ ├── init.lua # Always sourced
+ ├── lua # Shared library modules
+ │ └── user
+ │ └── .lua
+ ├── plugin # Automatically sourced at startup
+ │ ├── autocommands.lua
+ │ ├── commands.lua
+ │ ├── keymaps.lua
+ │ ├── plugins.lua # Plugins that require a `setup` call
+ │ └── .lua # Plugin configurations
+ └── after # Empty in this template
+ ├── plugin # Sourced at the very end of startup (rarely needed)
+ └── ftplugin # Sourced when opening a filetype, after sourcing ftplugin scripts
+```
+
+> [!IMPORTANT]
+>
+> - Configuration variables (e.g. `vim.g.`) should go in `nvim/init.lua`
+> or a module that is `require`d in `init.lua`.
+> - Configurations for plugins that require explicit initialization
+> (e.g. via a call to a `setup()` function) should go in `nvim/plugin/.lua`
+> or `nvim/plugin/plugins.lua`.
+> - See [Initialization order](#initialization-order) for details.
+
+### :open_file_folder: Nix
+
+You can declare Neovim derivations in `nix/neovim-overlay.nix`.
+
+There are two ways to add plugins:
+
+- The traditional way, using `nixpkgs` as the source.
+- By adding plugins as flake inputs (if you like living on the bleeding-edge).
+ Plugins added as flake inputs must be built in `nix/plugin-overlay.nix`.
+
+Directory structure:
+
+```sh
+── flake.nix
+── nix
+ ├── mkNeovim.nix # Function for creating the Neovim derivation
+ └── neovim-overlay.nix # Overlay that adds Neovim derivation
+```
+
+### :mag: Initialization order
+
+This derivation creates an `init.lua` as follows:
+
+1. Add `nvim/lua` to the `runtimepath`.
+1. Add the content of `nvim/init.lua`.
+1. Add `nvim/*` to the `runtimepath`.
+1. Add `nvim/after` to the `runtimepath`.
+
+This means that modules in `nvim/lua` can be `require`d in `init.lua` and `nvim/*/*.lua`.
+
+Modules in `nvim/plugin/` are sourced automatically, as if they were plugins.
+Because they are added to the runtime path at the end of the resulting `init.lua`,
+Neovim sources them _after_ loading plugins.
+
+## :electric_plug: Pre-configured plugins
+
+This configuration comes with [a few plugins pre-configured](./nix/neovim-overlay.nix).
+
+You can add or remove plugins by
+
+- Adding/Removing them in the [Nix list](./nix/neovim-overlay.nix).
+- Adding/Removing the config in `nvim/plugin/.lua`.
+
+## :anchor: Syncing updates
+
+If you have used this template and would like to fetch updates
+that were added later...
+
+Add this template as a remote:
+
+```console
+git remote add upstream git@github.com:nix-community/kickstart-nix.nvim.git
+```
+
+Fetch and merge changes:
+
+```console
+git fetch upstream
+git merge upstream/main --allow-unrelated-histories
+```
+
+## :pencil: Editing your config
+
+When your neovim setup is a nix derivation, editing your config
+demands a different workflow than you are used to without nix.
+Here is how I usually do it:
+
+- Perform modifications and stage any new files[^2].
+- Run `nix run /path/to/neovim/#nvim`
+ or `nix run /path/to/neovim/#nvim -- `
+
+[^2]: When adding new files, nix flakes won't pick them up unless they
+ have been committed or staged.
+
+This requires a rebuild of the `nvim` derivation, but has the advantage
+that if anything breaks, it's only broken during your test run.
+
+If you want an impure, but faster feedback loop,
+you can use `$XDG_CONFIG_HOME/$NVIM_APPNAME`[^3], where `$NVIM_APPNAME`
+defaults to `nvim` if the `appName` attribute is not set
+in the `mkNeovim` function.
+
+[^3]: Assuming Linux. Refer to `:h initialization` for Darwin.
+
+This has one caveat: The wrapper which nix generates for the derivation
+calls `nvim` with `-u /nix/store/path/to/generated-init.lua`.
+So it won't source a local `init.lua` file.
+To work around this, you can put scripts in the `plugin` or `after/plugin` directory.
+
+> [!TIP]
+>
+> If you are starting out, and want to test things without having to
+> stage or commit new files for changes to take effect,
+> you can remove the `.git` directory and re-initialize it (`git init`)
+> when you are done.
+
+## :link: Alternative / similar projects
+
+- [`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim):
+ Single-file Neovim configuration template with a similar philosophy to this project.
+ Does not use Nix to manage plugins.
+- [`neovim-flake`](https://github.com/jordanisaacs/neovim-flake):
+ Configured using a Nix module DSL.
+- [`NixVim`](https://github.com/nix-community/nixvim):
+ A module system for Neovim, with a focus on plugin configs.
+- [`nixCats-nvim`](https://github.com/BirdeeHub/nixCats-nvim):
+ A project that organises plugins into categories.
+ It also separates lua and nix configuration.
+- [`lz.n`](https://github.com/nvim-neorocks/lz.n):
+ A plugin-manager agnostic Lua library for lazy-loading plugins.
+ Can be used with Nix.
+
+> [!NOTE]
+>
+> When comparing with projects in the "non-Nix world", this
+> repository would be more comparable to `kickstart.nvim` (hence the name),
+> while the philosophies of `neovim-flake` and `NixVim` are more in line with
+> a Neovim distribution like [`LunarVim`](https://www.lunarvim.org/)
+> or [`LazyVim`](https://www.lazyvim.org/)
+> (though they are far more minimal by default).
+
+
+[neovim-shield]: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white
+[neovim-url]: https://neovim.io/
+[nix-shield]: https://img.shields.io/badge/nix-0175C2?style=for-the-badge&logo=NixOS&logoColor=white
+[nix-url]: https://nixos.org/
+[lua-shield]: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white
+[lua-url]: https://www.lua.org/
+[license-shield]: https://img.shields.io/github/license/nix-community/kickstart-nix.nvim.svg?style=for-the-badge
+[license-url]: https://github.com/nix-community/kickstart-nix.nvim/blob/master/LICENSE
+[issues-shield]: https://img.shields.io/github/issues/nix-community/kickstart-nix.nvim.svg?style=for-the-badge
+[issues-url]: https://github.com/nix-community/kickstart-nix.nvim/issues
+[license-shield]: https://img.shields.io/github/license/nix-community/kickstart-nix.nvim.svg?style=for-the-badge
+[license-url]: https://github.com/nix-community/kickstart-nix.nvim/blob/master/LICENSE
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..f8bb672
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,223 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1717285511,
+ "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "gen-luarc": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "git-hooks": "git-hooks",
+ "luvit-meta": "luvit-meta",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1724097937,
+ "narHash": "sha256-Q4tgm8ZHAQUdvsNft86MqIbHQAm7OF7RT/wwYWXqSdY=",
+ "owner": "mrcjkb",
+ "repo": "nix-gen-luarc-json",
+ "rev": "b36b69c4ded9f31b079523bc452e23458734cf00",
+ "type": "github"
+ },
+ "original": {
+ "owner": "mrcjkb",
+ "repo": "nix-gen-luarc-json",
+ "type": "github"
+ }
+ },
+ "git-hooks": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "gen-luarc",
+ "nixpkgs"
+ ],
+ "nixpkgs-stable": "nixpkgs-stable"
+ },
+ "locked": {
+ "lastModified": 1723803910,
+ "narHash": "sha256-yezvUuFiEnCFbGuwj/bQcqg7RykIEqudOy/RBrId0pc=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "bfef0ada09e2c8ac55bbcd0831bd0c9d42e651ba",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "gen-luarc",
+ "git-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "luvit-meta": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1705776742,
+ "narHash": "sha256-zAAptV/oLuLAAsa2zSB/6fxlElk4+jNZd/cPr9oxFig=",
+ "owner": "Bilal2453",
+ "repo": "luvit-meta",
+ "rev": "ce76f6f6cdc9201523a5875a4471dcfe0186eb60",
+ "type": "github"
+ },
+ "original": {
+ "owner": "Bilal2453",
+ "repo": "luvit-meta",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1718714799,
+ "narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "lastModified": 1717284937,
+ "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
+ "type": "tarball",
+ "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
+ },
+ "original": {
+ "type": "tarball",
+ "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1720386169,
+ "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-24.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1731319897,
+ "narHash": "sha256-PbABj4tnbWFMfBp6OcUK5iGy1QY+/Z96ZcLpooIbuEI=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "dc460ec76cbff0e66e269457d7b728432263166c",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "gen-luarc": "gen-luarc",
+ "nixpkgs": "nixpkgs_2"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..7f03010
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,73 @@
+{
+ description = "Neovim derivation";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ flake-utils.url = "github:numtide/flake-utils";
+ gen-luarc.url = "github:mrcjkb/nix-gen-luarc-json";
+
+ # Add bleeding-edge plugins here.
+ # They can be updated with `nix flake update` (make sure to commit the generated flake.lock)
+ # wf-nvim = {
+ # url = "github:Cassin01/wf.nvim";
+ # flake = false;
+ # };
+ };
+
+ outputs = inputs @ {
+ self,
+ nixpkgs,
+ flake-utils,
+ gen-luarc,
+ ...
+ }: let
+ supportedSystems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
+
+ # This is where the Neovim derivation is built.
+ neovim-overlay = import ./nix/neovim-overlay.nix {inherit inputs;};
+ in
+ flake-utils.lib.eachSystem supportedSystems (system: let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = [
+ # Import the overlay, so that the final Neovim derivation(s) can be accessed via pkgs.
+ neovim-overlay
+ # This adds a function can be used to generate a .luarc.json
+ # containing the Neovim API all plugins in the workspace directory.
+ # The generated file can be symlinked in the devShell's shellHook.
+ gen-luarc.overlays.default
+ ];
+ };
+ shell = pkgs.mkShell {
+ name = "nvim-devShell";
+ buildInputs = with pkgs; [
+ # Tools for Lua and Nix development, useful for editing files in this repo
+ lua-language-server
+ nil
+ stylua
+ luajitPackages.luacheck
+ ];
+ shellHook = ''
+ # symlink the .luarc.json generated in the overlay
+ ln -fs ${pkgs.nvim-luarc-json} .luarc.json
+ '';
+ };
+ in {
+ packages = rec {
+ default = nvim;
+ nvim = pkgs.nvim-pkg;
+ };
+ devShells = {
+ default = shell;
+ };
+ })
+ // {
+ # You can add this overlay to your NixOS configuration
+ overlays.default = neovim-overlay;
+ };
+}
diff --git a/nix/mkNeovim.nix b/nix/mkNeovim.nix
new file mode 100644
index 0000000..efd41c9
--- /dev/null
+++ b/nix/mkNeovim.nix
@@ -0,0 +1,214 @@
+# Function for creating a Neovim derivation
+{
+ pkgs,
+ lib,
+ stdenv,
+ # Set by the overlay to ensure we use a compatible version of `wrapNeovimUnstable`
+ pkgs-wrapNeovim ? pkgs,
+}:
+with lib;
+ {
+ # NVIM_APPNAME - Defaults to 'nvim' if not set.
+ # If set to something else, this will also rename the binary.
+ appName ? null,
+ # The Neovim package to wrap
+ neovim-unwrapped ? pkgs-wrapNeovim.neovim-unwrapped,
+ plugins ? [], # List of plugins
+ # List of dev plugins (will be bootstrapped) - useful for plugin developers
+ # { name = ; url = ; }
+ devPlugins ? [],
+ # Regexes for config files to ignore, relative to the nvim directory.
+ # e.g. [ "^plugin/neogit.lua" "^ftplugin/.*.lua" ]
+ ignoreConfigRegexes ? [],
+ extraPackages ? [], # Extra runtime dependencies (e.g. ripgrep, ...)
+ # The below arguments can typically be left as their defaults
+ # Additional lua packages (not plugins), e.g. from luarocks.org.
+ # e.g. p: [p.jsregexp]
+ extraLuaPackages ? p: [],
+ extraPython3Packages ? p: [], # Additional python 3 packages
+ withPython3 ? true, # Build Neovim with Python 3 support?
+ withRuby ? false, # Build Neovim with Ruby support?
+ withNodeJs ? false, # Build Neovim with NodeJS support?
+ withSqlite ? true, # Add sqlite? This is a dependency for some plugins
+ # You probably don't want to create vi or vim aliases
+ # if the appName is something different than "nvim"
+ # Add a "vi" binary to the build output as an alias?
+ viAlias ? appName == null || appName == "nvim",
+ # Add a "vim" binary to the build output as an alias?
+ vimAlias ? appName == null || appName == "nvim",
+ }: let
+ # This is the structure of a plugin definition.
+ # Each plugin in the `plugins` argument list can also be defined as this attrset
+ defaultPlugin = {
+ plugin = null; # e.g. nvim-lspconfig
+ config = null; # plugin config
+ # If `optional` is set to `false`, the plugin is installed in the 'start' packpath
+ # set to `true`, it is installed in the 'opt' packpath, and can be lazy loaded with
+ # ':packadd! {plugin-name}
+ optional = false;
+ runtime = {};
+ };
+
+ externalPackages = extraPackages ++ (optionals withSqlite [pkgs.sqlite]);
+
+ # Map all plugins to an attrset { plugin = ; config = ; optional = ; ... }
+ normalizedPlugins = map (x:
+ defaultPlugin
+ // (
+ if x ? plugin
+ then x
+ else {plugin = x;}
+ ))
+ plugins;
+
+ # This nixpkgs util function creates an attrset
+ # that pkgs.wrapNeovimUnstable uses to configure the Neovim build.
+ neovimConfig = pkgs-wrapNeovim.neovimUtils.makeNeovimConfig {
+ inherit extraPython3Packages withPython3 withRuby withNodeJs viAlias vimAlias;
+ plugins = normalizedPlugins;
+ };
+
+ # This uses the ignoreConfigRegexes list to filter
+ # the nvim directory
+ nvimRtpSrc = let
+ src = ../nvim;
+ in
+ lib.cleanSourceWith {
+ inherit src;
+ name = "nvim-rtp-src";
+ filter = path: tyoe: let
+ srcPrefix = toString src + "/";
+ relPath = lib.removePrefix srcPrefix (toString path);
+ in
+ lib.all (regex: builtins.match regex relPath == null) ignoreConfigRegexes;
+ };
+
+ # Split runtimepath into 3 directories:
+ # - lua, to be prepended to the rtp at the beginning of init.lua
+ # - nvim, containing plugin, ftplugin, ... subdirectories
+ # - after, to be sourced last in the startup initialization
+ # See also: https://neovim.io/doc/user/starting.html
+ nvimRtp = stdenv.mkDerivation {
+ name = "nvim-rtp";
+ src = nvimRtpSrc;
+
+ buildPhase = ''
+ mkdir -p $out/nvim
+ mkdir -p $out/lua
+ rm init.lua
+ '';
+
+ installPhase = ''
+ cp -r lua $out/lua
+ rm -r lua
+ # Copy nvim/after only if it exists
+ if [ -d "after" ]; then
+ cp -r after $out/after
+ rm -r after
+ fi
+ # Copy rest of nvim/ subdirectories only if they exist
+ if [ ! -z "$(ls -A)" ]; then
+ cp -r -- * $out/nvim
+ fi
+ '';
+ };
+
+ # The final init.lua content that we pass to the Neovim wrapper.
+ # It wraps the user init.lua, prepends the lua lib directory to the RTP
+ # and prepends the nvim and after directory to the RTP
+ # It also adds logic for bootstrapping dev plugins (for plugin developers)
+ initLua =
+ ''
+ vim.loader.enable()
+ -- prepend lua directory
+ vim.opt.rtp:prepend('${nvimRtp}/lua')
+ ''
+ # Wrap init.lua
+ + (builtins.readFile ../nvim/init.lua)
+ # Bootstrap/load dev plugins
+ + optionalString (devPlugins != []) (
+ ''
+ local dev_pack_path = vim.fn.stdpath('data') .. '/site/pack/dev'
+ local dev_plugins_dir = dev_pack_path .. '/opt'
+ local dev_plugin_path
+ ''
+ + strings.concatMapStringsSep
+ "\n"
+ (plugin: ''
+ dev_plugin_path = dev_plugins_dir .. '/${plugin.name}'
+ if vim.fn.empty(vim.fn.glob(dev_plugin_path)) > 0 then
+ vim.notify('Bootstrapping dev plugin ${plugin.name} ...', vim.log.levels.INFO)
+ vim.cmd('!${pkgs.git}/bin/git clone ${plugin.url} ' .. dev_plugin_path)
+ end
+ vim.cmd('packadd! ${plugin.name}')
+ '')
+ devPlugins
+ )
+ # Prepend nvim and after directories to the runtimepath
+ # NOTE: This is done after init.lua,
+ # because of a bug in Neovim that can cause filetype plugins
+ # to be sourced prematurely, see https://github.com/neovim/neovim/issues/19008
+ # We prepend to ensure that user ftplugins are sourced before builtin ftplugins.
+ + ''
+ vim.opt.rtp:prepend('${nvimRtp}/nvim')
+ vim.opt.rtp:prepend('${nvimRtp}/after')
+ '';
+
+ # Add arguments to the Neovim wrapper script
+ extraMakeWrapperArgs = builtins.concatStringsSep " " (
+ # Set the NVIM_APPNAME environment variable
+ (optional (appName != "nvim" && appName != null && appName != "")
+ ''--set NVIM_APPNAME "${appName}"'')
+ # Add external packages to the PATH
+ ++ (optional (externalPackages != [])
+ ''--prefix PATH : "${makeBinPath externalPackages}"'')
+ # Set the LIBSQLITE_CLIB_PATH if sqlite is enabled
+ ++ (optional withSqlite
+ ''--set LIBSQLITE_CLIB_PATH "${pkgs.sqlite.out}/lib/libsqlite3.so"'')
+ # Set the LIBSQLITE environment variable if sqlite is enabled
+ ++ (optional withSqlite
+ ''--set LIBSQLITE "${pkgs.sqlite.out}/lib/libsqlite3.so"'')
+ );
+
+ luaPackages = neovim-unwrapped.lua.pkgs;
+ resolvedExtraLuaPackages = extraLuaPackages luaPackages;
+
+ # Native Lua libraries
+ extraMakeWrapperLuaCArgs =
+ optionalString (resolvedExtraLuaPackages != [])
+ ''--suffix LUA_CPATH ";" "${concatMapStringsSep ";" luaPackages.getLuaCPath resolvedExtraLuaPackages}"'';
+
+ # Lua libraries
+ extraMakeWrapperLuaArgs =
+ optionalString (resolvedExtraLuaPackages != [])
+ ''--suffix LUA_PATH ";" "${concatMapStringsSep ";" luaPackages.getLuaPath resolvedExtraLuaPackages}"'';
+
+ # wrapNeovimUnstable is the nixpkgs utility function for building a Neovim derivation.
+ neovim-wrapped = pkgs-wrapNeovim.wrapNeovimUnstable neovim-unwrapped (neovimConfig
+ // {
+ luaRcContent = initLua;
+ wrapperArgs =
+ escapeShellArgs neovimConfig.wrapperArgs
+ + " "
+ + extraMakeWrapperArgs
+ + " "
+ + extraMakeWrapperLuaCArgs
+ + " "
+ + extraMakeWrapperLuaArgs;
+ wrapRc = true;
+ });
+
+ isCustomAppName = appName != null && appName != "nvim";
+ in
+ neovim-wrapped.overrideAttrs (oa: {
+ buildPhase =
+ oa.buildPhase
+ # If a custom NVIM_APPNAME has been set, rename the `nvim` binary
+ + lib.optionalString isCustomAppName ''
+ mv $out/bin/nvim $out/bin/${lib.escapeShellArg appName}
+ '';
+ meta.mainProgram
+ = if isCustomAppName
+ then appName
+ else oa.meta.mainProgram;
+ })
diff --git a/nix/neovim-overlay.nix b/nix/neovim-overlay.nix
new file mode 100644
index 0000000..4c300d3
--- /dev/null
+++ b/nix/neovim-overlay.nix
@@ -0,0 +1,118 @@
+# This overlay, when applied to nixpkgs, adds the final neovim derivation to nixpkgs.
+{inputs}: final: prev:
+with final.pkgs.lib; let
+ pkgs = final;
+
+ # Use this to create a plugin from a flake input
+ mkNvimPlugin = src: pname:
+ pkgs.vimUtils.buildVimPlugin {
+ inherit pname src;
+ version = src.lastModifiedDate;
+ };
+
+ # Make sure we use the pinned nixpkgs instance for wrapNeovimUnstable,
+ # otherwise it could have an incompatible signature when applying this overlay.
+ pkgs-wrapNeovim = inputs.nixpkgs.legacyPackages.${pkgs.system};
+
+ # This is the helper function that builds the Neovim derivation.
+ mkNeovim = pkgs.callPackage ./mkNeovim.nix { inherit pkgs-wrapNeovim; };
+
+ # A plugin can either be a package or an attrset, such as
+ # { plugin = ; # the package, e.g. pkgs.vimPlugins.nvim-cmp
+ # config = ; # String; a config that will be loaded with the plugin
+ # # Boolean; Whether to automatically load the plugin as a 'start' plugin,
+ # # or as an 'opt' plugin, that can be loaded with `:packadd!`
+ # optional = ; # Default: false
+ # ...
+ # }
+ all-plugins = with pkgs.vimPlugins; [
+ # plugins from nixpkgs go in here.
+ # https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=vimPlugins
+ nvim-treesitter.withAllGrammars
+ luasnip # snippets | https://github.com/l3mon4d3/luasnip/
+ # nvim-cmp (autocompletion) and extensions
+ nvim-cmp # https://github.com/hrsh7th/nvim-cmp
+ cmp_luasnip # snippets autocompletion extension for nvim-cmp | https://github.com/saadparwaiz1/cmp_luasnip/
+ lspkind-nvim # vscode-like LSP pictograms | https://github.com/onsails/lspkind.nvim/
+ cmp-nvim-lsp # LSP as completion source | https://github.com/hrsh7th/cmp-nvim-lsp/
+ cmp-nvim-lsp-signature-help # https://github.com/hrsh7th/cmp-nvim-lsp-signature-help/
+ cmp-buffer # current buffer as completion source | https://github.com/hrsh7th/cmp-buffer/
+ cmp-path # file paths as completion source | https://github.com/hrsh7th/cmp-path/
+ cmp-nvim-lua # neovim lua API as completion source | https://github.com/hrsh7th/cmp-nvim-lua/
+ cmp-cmdline # cmp command line suggestions
+ cmp-cmdline-history # cmp command line history suggestions
+ # ^ nvim-cmp extensions
+ # git integration plugins
+ diffview-nvim # https://github.com/sindrets/diffview.nvim/
+ neogit # https://github.com/TimUntersberger/neogit/
+ gitsigns-nvim # https://github.com/lewis6991/gitsigns.nvim/
+ vim-fugitive # https://github.com/tpope/vim-fugitive/
+ # ^ git integration plugins
+ # telescope and extensions
+ telescope-nvim # https://github.com/nvim-telescope/telescope.nvim/
+ telescope-fzy-native-nvim # https://github.com/nvim-telescope/telescope-fzy-native.nvim
+ # telescope-smart-history-nvim # https://github.com/nvim-telescope/telescope-smart-history.nvim
+ # ^ telescope and extensions
+ # UI
+ lualine-nvim # Status line | https://github.com/nvim-lualine/lualine.nvim/
+ nvim-navic # Add LSP location to lualine | https://github.com/SmiteshP/nvim-navic
+ statuscol-nvim # Status column | https://github.com/luukvbaal/statuscol.nvim/
+ nvim-treesitter-context # nvim-treesitter-context
+ # ^ UI
+ # language support
+ # ^ language support
+ # navigation/editing enhancement plugins
+ vim-unimpaired # predefined ] and [ navigation keymaps | https://github.com/tpope/vim-unimpaired/
+ eyeliner-nvim # Highlights unique characters for f/F and t/T motions | https://github.com/jinh0/eyeliner.nvim
+ nvim-surround # https://github.com/kylechui/nvim-surround/
+ nvim-treesitter-textobjects # https://github.com/nvim-treesitter/nvim-treesitter-textobjects/
+ nvim-ts-context-commentstring # https://github.com/joosepalviste/nvim-ts-context-commentstring/
+ # ^ navigation/editing enhancement plugins
+ # Useful utilities
+ nvim-unception # Prevent nested neovim sessions | nvim-unception
+ # ^ Useful utilities
+ # libraries that other plugins depend on
+ sqlite-lua
+ plenary-nvim
+ nvim-web-devicons
+ vim-repeat
+ # ^ libraries that other plugins depend on
+ # bleeding-edge plugins from flake inputs
+ # (mkNvimPlugin inputs.wf-nvim "wf.nvim") # (example) keymap hints | https://github.com/Cassin01/wf.nvim
+ # ^ bleeding-edge plugins from flake inputs
+ which-key-nvim
+ ];
+
+ extraPackages = with pkgs; [
+ # language servers, etc.
+ lua-language-server
+ nil # nix LSP
+ ];
+in {
+ # This is the neovim derivation
+ # returned by the overlay
+ nvim-pkg = mkNeovim {
+ plugins = all-plugins;
+ inherit extraPackages;
+ };
+
+ # This can be symlinked in the devShell's shellHook
+ nvim-luarc-json = final.mk-luarc-json {
+ plugins = all-plugins;
+ };
+
+ # You can add as many derivations as you like.
+ # Use `ignoreConfigRegexes` to filter out config
+ # files you would not like to include.
+ #
+ # For example:
+ #
+ # nvim-pkg-no-telescope = mkNeovim {
+ # plugins = [];
+ # ignoreConfigRegexes = [
+ # "^plugin/telescope.lua"
+ # "^ftplugin/.*.lua"
+ # ];
+ # inherit extraPackages;
+ # };
+}
diff --git a/nvim-nix.svg b/nvim-nix.svg
new file mode 100644
index 0000000..882567a
--- /dev/null
+++ b/nvim-nix.svg
@@ -0,0 +1,317 @@
+
+
diff --git a/nvim/after/README.md b/nvim/after/README.md
new file mode 100644
index 0000000..a4b4ff2
--- /dev/null
+++ b/nvim/after/README.md
@@ -0,0 +1,8 @@
+## `after` directory
+
+Scripts put in
+
+- `after/plugin` will be sourced *after* init.lua
+ or any other scripts are sourced during startup.
+- `after/ftplugin` will be sourced when opening a
+ filetype, *after* any other `ftplugin` scripts are sourced.
diff --git a/nvim/ftplugin/lua.lua b/nvim/ftplugin/lua.lua
new file mode 100644
index 0000000..125c422
--- /dev/null
+++ b/nvim/ftplugin/lua.lua
@@ -0,0 +1,55 @@
+vim.bo.comments = ':---,:--'
+
+local lua_ls_cmd = 'lua-language-server'
+
+-- Check if lua-language-server is available
+if vim.fn.executable(lua_ls_cmd) ~= 1 then
+ return
+end
+
+local root_files = {
+ '.luarc.json',
+ '.luarc.jsonc',
+ '.luacheckrc',
+ '.stylua.toml',
+ 'stylua.toml',
+ 'selene.toml',
+ 'selene.yml',
+ '.git',
+}
+
+vim.lsp.start {
+ name = 'luals',
+ cmd = { lua_ls_cmd },
+ root_dir = vim.fs.dirname(vim.fs.find(root_files, { upward = true })[1]),
+ capabilities = require('user.lsp').make_client_capabilities(),
+ settings = {
+ Lua = {
+ runtime = {
+ version = 'LuaJIT',
+ },
+ diagnostics = {
+ -- Get the language server to recognize the `vim` global, etc.
+ globals = {
+ 'vim',
+ 'describe',
+ 'it',
+ 'assert',
+ 'stub',
+ },
+ disable = {
+ 'duplicate-set-field',
+ },
+ },
+ workspace = {
+ checkThirdParty = false,
+ },
+ telemetry = {
+ enable = false,
+ },
+ hint = { -- inlay hints (supported in Neovim >= 0.10)
+ enable = true,
+ },
+ },
+ },
+}
diff --git a/nvim/ftplugin/nix.lua b/nvim/ftplugin/nix.lua
new file mode 100644
index 0000000..8691e2a
--- /dev/null
+++ b/nvim/ftplugin/nix.lua
@@ -0,0 +1,18 @@
+-- Exit if the language server isn't available
+if vim.fn.executable('nil') ~= 1 then
+ return
+end
+
+local root_files = {
+ 'flake.nix',
+ 'default.nix',
+ 'shell.nix',
+ '.git',
+}
+
+vim.lsp.start {
+ name = 'nil_ls',
+ cmd = { 'nil' },
+ root_dir = vim.fs.dirname(vim.fs.find(root_files, { upward = true })[1]),
+ capabilities = require('user.lsp').make_client_capabilities(),
+}
diff --git a/nvim/init.lua b/nvim/init.lua
new file mode 100644
index 0000000..ecd6583
--- /dev/null
+++ b/nvim/init.lua
@@ -0,0 +1,104 @@
+local cmd = vim.cmd
+local fn = vim.fn
+local opt = vim.o
+local g = vim.g
+
+-- key. Defaults to `\`. Some people prefer space.
+-- g.mapleader = ' '
+-- g.maplocalleader = ' '
+
+opt.compatible = false
+
+-- Enable true colour support
+if fn.has('termguicolors') then
+ opt.termguicolors = true
+end
+
+-- See :h