# 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; })