commit 13177e2c368c5fb5fa0a98d872cc41ef27ab33ad Author: Dmitriy Pleshevskiy Date: Fri Apr 26 02:08:23 2024 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d458357 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# editors +.idea/ +.vscode/ +*.swp +# direnv +.direnv +.envrc +# nix +/result +# custom +test-cfg \ No newline at end of file diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..c435d7f --- /dev/null +++ b/default.nix @@ -0,0 +1,31 @@ +{ config ? { } +, pkgs ? import { } +, nix2lua ? import +}: + + +let + inherit (pkgs.lib) evalModules filter concatMapStringsSep showWarnings; + + allModules = import ./module-list.nix { inherit pkgs; }; + + rawModule = evalModules { + modules = [ config ] ++ allModules; + specialArgs = { + inherit pkgs; + nix2lua = nix2lua.lib; + }; + }; + + failedAssertions = map (x: x.message) (filter (x: !x.assertion) rawModule.config.assertions); + + module = + if failedAssertions != [ ] + then throw "\nFailed assertions:\n${concatMapStringsSep "\n" (x: "- ${x}") failedAssertions}" + else showWarnings rawModule.config.warnings rawModule; +in +{ + inherit (module.config.build) neovim; + inherit (module) config options; + inherit pkgs; +} diff --git a/module-list.nix b/module-list.nix new file mode 100644 index 0000000..326720b --- /dev/null +++ b/module-list.nix @@ -0,0 +1,18 @@ +{ pkgs }: + +[ + ./modules/build/neovim.nix + + ./modules/vim/opts.nix + ./modules/vim/keymap.nix + + ./modules/filetype.nix + ./modules/input.nix + ./modules/plugin.nix + + ./modules/plugins/theme/catppuccin.nix + ./modules/plugins/navigation/telescope.nix + + ################################################## + (pkgs.path + "/nixos/modules/misc/assertions.nix") +] diff --git a/modules/build/neovim.nix b/modules/build/neovim.nix new file mode 100644 index 0000000..07afbba --- /dev/null +++ b/modules/build/neovim.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, nix2lua, ... }: + +let + inherit (builtins) filter attrValues; + + cfg = config.build.neovim; +in +{ + options.build.neovim = with lib; with types; { + luaConfig = mkOption { + type = lines; + readOnly = true; + internal = true; + description = '' + Neovim editor lua configuration. + ''; + }; + plugins = mkOption { + type = listOf package; + readOnly = true; + internal = true; + }; + package = mkOption { + type = types.package; + readOnly = true; + internal = true; + description = '' + Neovim editor derivation with plugins and configurations. + ''; + }; + }; + + config.build.neovim = { + luaConfig = with nix2lua; toLua (spaceBetween (lib.flatten [ + # Global Opts + (lib.flip lib.mapAttrsToList config.vim.g (k: set "vim.g.${k}")) + # Opts + (lib.flip lib.mapAttrsToList config.vim.opt (k: set "vim.opt.${k}")) + # Keymaps + (lib.flip map config.vim.keymap.set ({ mode, lhs, rhs, ... } @ vars: + call "vim.keymap.set" [ mode lhs rhs (removeAttrs vars [ "mode" "lhs" "rhs" ]) ] + )) + # Plugins + (map (v: v.genConfig) (filter (v: !v.isDependency) (attrValues config.plugin))) + # Cmd + (lib.optional (config.vim.cmd != "") (call "vim.cmd" config.vim.cmd)) + ])); + + plugins = lib.mapAttrsToList (k: v: v.package) config.plugin; + + package = pkgs.wrapNeovim pkgs.neovim-unwrapped { + viAlias = false; + vimAlias = false; + + withPython3 = false; + withNodeJs = false; + withRuby = false; + + configure = { + customRC = '' + lua << EOF + ${cfg.luaConfig} + EOF + ''; + + packages.myVimPackages = { start = cfg.plugins; }; + }; + }; + }; +} diff --git a/modules/filetype.nix b/modules/filetype.nix new file mode 100644 index 0000000..945167e --- /dev/null +++ b/modules/filetype.nix @@ -0,0 +1,67 @@ +{ config, lib, ... }: + +let cfg = config.filetype; in +{ + options.filetype = with lib; with types; { + enable = mkOption { + type = bool; + default = true; + description = '' + Vim can detect the type of file that is edited. This is done by checking the + file name and sometimes by inspecting the contents of the file for specific + text. + + `:help filetypes` + `:help filetype-off` + `:help filetype-overview` + ''; + }; + + plugin = mkOption { + type = bool; + default = true; + description = '' + Enable loading the plugin files for specific file types + + `:help :filetype-plugin-on` + `:help :filetype-plugin-off` + ''; + }; + + indent = mkOption { + type = bool; + default = true; + description = '' + Enable loading the indent file for specific file types + + `:help :filetype-indent-on` + `:help :filetype-indent-off` + ''; + }; + + ignore = mkOption { + type = uniq (listOf str); + default = [ "Z" "gz" "bz2" "zip" "tgz" ]; + description = '' + To avoid that certain files are being inspected, the g:ft_ignore_pat variable + is used. The default value is set like this: + + `:help filetype-ignore` + ''; + }; + extraIgnore = mkOption { + type = uniq (listOf str); + default = [ ]; + }; + }; + + config = { + vim.namedCmd.filetype = lib.concatLines + (lib.flip lib.mapAttrsToList { inherit (cfg) enable plugin indent; } + (k: v: ''filetype ${if k == "enable" then "" else k} ${if v then "on" else "off"}'') + ); + + vim.g.ft_ignore_pat = "\\\\.(${lib.concatStringsSep "|" (cfg.ignore ++ cfg.extraIgnore)})$"; + }; + +} diff --git a/modules/input.nix b/modules/input.nix new file mode 100644 index 0000000..ccd2d9b --- /dev/null +++ b/modules/input.nix @@ -0,0 +1,34 @@ +{ config, lib, ... }: + +let + cfg = config.input; + + disableKeymaps = mode: lhss: lib.flip map lhss (lhs: { inherit mode lhs; rhs = ""; }); +in +{ + options.input = with lib; with types; { + exMode.enable = (mkEnableOption "Ex mode") // { default = true; }; + arrowKeys = { + disableInMode = mkOption { + type = oneOf [ str (uniq (listOf str)) ]; + default = [ "n" "v" ]; + }; + }; + pageButtons = { + disableInMode = mkOption { + type = oneOf [ str (uniq (listOf str)) ]; + default = [ "n" "v" "i" ]; + }; + }; + }; + + config.vim.keymap.set = + # Disable the annoying and useless ex-mode + lib.optionals (!cfg.exMode.enable) (disableKeymaps "n" [ "Q" "gQ" ]) + # Disable arrow keys + ++ lib.optionals (cfg.arrowKeys.disableInMode != [ ]) + (disableKeymaps cfg.arrowKeys.disableInMode [ "" "" "" "" ]) + # Disable PageUp / PageDown + ++ lib.optionals (cfg.pageButtons.disableInMode != [ ]) + (disableKeymaps cfg.pageButtons.disableInMode [ "" "" ]); +} diff --git a/modules/plugin.nix b/modules/plugin.nix new file mode 100644 index 0000000..e5fd4a2 --- /dev/null +++ b/modules/plugin.nix @@ -0,0 +1,80 @@ +{ lib, pkgs, nix2lua, ... }: + + +let + pluginOpts = ({ name, config, ... }: { + options = with lib; with types; { + name = mkOption { + type = str; + }; + varName = mkOption { + type = str; + # TODO: add validation + }; + package = mkPackageOption pkgs.vimPlugins name { }; + + isDependency = mkOption { + type = bool; + default = false; + }; + + beforeSetup = mkOption { + type = listOf attrs; + default = [ ]; + }; + setupFnName = mkOption { + type = str; + default = "setup"; + }; + setupSettings = mkOption { + type = nullOr attrs; + default = null; + }; + afterSetup = mkOption { + type = listOf attrs; + default = [ ]; + }; + extra = mkOption { + type = listOf attrs; + default = [ ]; + }; + + genConfig = mkOption { + type = listOf attrs; + readOnly = true; + internal = true; + }; + + luaConfig = mkOption { + type = str; + readOnly = true; + internal = true; + }; + }; + + config = { + name = lib.mkDefault name; + varName = lib.mkDefault (builtins.replaceStrings [ "-" "/" ] [ "_" "_" ] config.name); + + genConfig = with nix2lua; lib.mkIf (!config.isDependency) (lib.flatten [ + (local (set config.varName (require config.name))) + config.beforeSetup + (lib.optional (config.setupSettings != null) + (pipe1 (var config.varName) (call config.setupFnName config.setupSettings)) + ) + config.afterSetup + config.extra + ]); + + luaConfig = with nix2lua; toLua (spaceBetween config.genConfig); + }; + }); + +in +{ + options.plugin = with lib; with types; mkOption { + type = attrsOf (submodule pluginOpts); + default = { }; + }; + +} diff --git a/modules/plugins/navigation/telescope.nix b/modules/plugins/navigation/telescope.nix new file mode 100644 index 0000000..aae1547 --- /dev/null +++ b/modules/plugins/navigation/telescope.nix @@ -0,0 +1,35 @@ +{ config, lib, pkgs, ... }: + +let cfg = config.plugins.navigation.telescope; in +{ + options.plugins.navigation.telescope = with lib; { + enable = mkEnableOption "telescope"; + + package = mkPackageOption pkgs.vimPlugins "telescope-nvim" { }; + + settings = mkOption { + type = types.attrs; + default = { }; + description = '' + See: https://github.com/nvim-telescope/telescope.nvim?tab=readme-ov-file#customization + ''; + example = { + pickers = { + find_files = { + theme = "dropdown"; + }; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + plugin.plenary-nvim = lib.mkDefault { isDependency = true; }; + + plugin.telescope-nvim = { + name = "telescope"; + package = cfg.package; + setupSettings = cfg.settings; + }; + }; +} diff --git a/modules/plugins/theme/catppuccin.nix b/modules/plugins/theme/catppuccin.nix new file mode 100644 index 0000000..202d2ea --- /dev/null +++ b/modules/plugins/theme/catppuccin.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: + +let cfg = config.plugins.theme.catppuccin; in +{ + options.plugins.theme.catppuccin = with lib; { + enable = mkEnableOption "catppuccin"; + + package = mkPackageOption pkgs.vimPlugins "catppuccin-nvim" { }; + + settings = mkOption { + type = types.attrs; + default = { }; + description = '' + See: https://github.com/catppuccin/nvim/?tab=readme-ov-file#configuration + ''; + example = { + flavour = "frappe"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + plugin.catppuccin-nvim = { + name = "catppuccin"; + package = cfg.package; + setupSettings = cfg.settings; + }; + + vim.namedCmd.colorscheme = '' + colorscheme catppuccin + ''; + }; + +} diff --git a/modules/vim/keymap.nix b/modules/vim/keymap.nix new file mode 100644 index 0000000..33a6ee1 --- /dev/null +++ b/modules/vim/keymap.nix @@ -0,0 +1,141 @@ +{ lib, ... }: + +let + modeType = lib.types.enum [ "" "n" "!" "i" "c" "v" "x" "s" "o" "t" "l" ]; + + # TODO: maybe add more options and add descriptions from + # :help vim.keymap.set + # :help nvim_set_keymap + setKeymapType = with lib; with types; submodule { + options = { + lhs = mkOption { + type = str; + description = '' + Left-hand side |{lhs}| of the mapping. + ''; + }; + rhs = mkOption { + type = oneOf [ str attrs ]; + description = '' + Right-hand side |{rhs}| of the mapping, can be a Lua function. + ''; + }; + mode = mkOption { + type = oneOf [ + modeType + (uniq (listOf modeType)) + ]; + default = ""; + description = '' + Mode | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang | + Command +------+-----+-----+-----+-----+-----+------+------+ + "" | yes | - | - | yes | yes | yes | - | - | + "n" | yes | - | - | - | - | - | - | - | + "!" | - | yes | yes | - | - | - | - | - | + "i" | - | yes | - | - | - | - | - | - | + "c" | - | - | yes | - | - | - | - | - | + "v" | - | - | - | yes | yes | - | - | - | + "x" | - | - | - | yes | - | - | - | - | + "s" | - | - | - | - | yes | - | - | - | + "o" | - | - | - | - | - | yes | - | - | + "t" | - | - | - | - | - | - | yes | - | + "l" | - | yes | yes | - | - | - | - | yes | + + `:help map-overview` + ''; + }; + buffer = mkOption { + type = nullOr str; + default = null; + description = '' + The mapping will be effective in the current buffer only. + + `:help :map-buffer` + ''; + }; + nowait = mkOption { + type = bool; + default = false; + description = '' + When defining a buffer-local mapping for "," there may be a global mapping + that starts with ",". Then you need to type another character for Vim to know + whether to use the "," mapping or the longer one. To avoid this add the + argument. Then the mapping will be used when it matches, Vim does + not wait for more characters to be typed. + + `:help :map-nowait` + ''; + }; + silent = mkOption { + type = bool; + default = false; + description = '' + To define a mapping which will not be echoed on the command line. + + `:help :map-silent` + ''; + }; + script = mkOption { + type = bool; + default = false; + description = '' + If the first argument to one of these commands is "