From a61c8fbc3d845fb7a5966fc9d25021d2a8264edf Mon Sep 17 00:00:00 2001 From: Gaetan Lepage Date: Sat, 3 Feb 2024 23:22:06 +0100 Subject: [PATCH] plugins/nvim-cmp: refactoring (init filetype and cmdline options) --- plugins/colorschemes/catppuccin.nix | 2 +- .../{nvim-cmp => cmp}/cmp-helpers.nix | 61 +- plugins/completion/cmp/default.nix | 83 +++ plugins/completion/cmp/deprecations.nix | 154 +++++ plugins/completion/cmp/options/default.nix | 82 +++ .../cmp/options/settings-options.nix | 350 +++++++++++ .../completion/cmp/options/sources-option.nix | 107 ++++ .../{nvim-cmp => cmp}/sources/cmp-tabby.nix | 0 .../{nvim-cmp => cmp}/sources/cmp-tabnine.nix | 0 .../sources/codeium-nvim.nix | 0 .../{nvim-cmp => cmp}/sources/copilot-cmp.nix | 0 .../{nvim-cmp => cmp}/sources/crates-nvim.nix | 0 .../{nvim-cmp => cmp}/sources/default.nix | 8 +- plugins/completion/lspkind.nix | 4 +- plugins/completion/nvim-cmp/default.nix | 578 ------------------ plugins/completion/nvim-cmp/setup-options.nix | 148 ----- plugins/default.nix | 3 +- plugins/utils/obsidian.nix | 8 +- .../example-configurations/issues.nix | 63 +- .../plugins/completion/cmp-tabby.nix | 8 +- tests/test-sources/plugins/completion/cmp.nix | 272 +++++++++ .../plugins/completion/codeium-nvim.nix | 4 +- .../plugins/completion/copilot-lua.nix | 4 +- .../plugins/completion/lspkind.nix | 2 +- .../plugins/completion/nvim-cmp.nix | 171 ------ tests/test-sources/plugins/utils/obsidian.nix | 2 +- 26 files changed, 1166 insertions(+), 948 deletions(-) rename plugins/completion/{nvim-cmp => cmp}/cmp-helpers.nix (52%) create mode 100644 plugins/completion/cmp/default.nix create mode 100644 plugins/completion/cmp/deprecations.nix create mode 100644 plugins/completion/cmp/options/default.nix create mode 100644 plugins/completion/cmp/options/settings-options.nix create mode 100644 plugins/completion/cmp/options/sources-option.nix rename plugins/completion/{nvim-cmp => cmp}/sources/cmp-tabby.nix (100%) rename plugins/completion/{nvim-cmp => cmp}/sources/cmp-tabnine.nix (100%) rename plugins/completion/{nvim-cmp => cmp}/sources/codeium-nvim.nix (100%) rename plugins/completion/{nvim-cmp => cmp}/sources/copilot-cmp.nix (100%) rename plugins/completion/{nvim-cmp => cmp}/sources/crates-nvim.nix (100%) rename plugins/completion/{nvim-cmp => cmp}/sources/default.nix (75%) delete mode 100644 plugins/completion/nvim-cmp/default.nix delete mode 100644 plugins/completion/nvim-cmp/setup-options.nix create mode 100644 tests/test-sources/plugins/completion/cmp.nix delete mode 100644 tests/test-sources/plugins/completion/nvim-cmp.nix diff --git a/plugins/colorschemes/catppuccin.nix b/plugins/colorschemes/catppuccin.nix index 55a0d779..41be28ac 100644 --- a/plugins/colorschemes/catppuccin.nix +++ b/plugins/colorschemes/catppuccin.nix @@ -173,7 +173,7 @@ in { # } # bufferline = helpers.defaultNullOpts.mkBool false ""; - cmp = helpers.defaultNullOpts.mkBool config.plugins.nvim-cmp.enable ""; + cmp = helpers.defaultNullOpts.mkBool config.plugins.cmp.enable ""; coc_nvim = helpers.defaultNullOpts.mkBool false ""; diff --git a/plugins/completion/nvim-cmp/cmp-helpers.nix b/plugins/completion/cmp/cmp-helpers.nix similarity index 52% rename from plugins/completion/nvim-cmp/cmp-helpers.nix rename to plugins/completion/cmp/cmp-helpers.nix index e05ddea2..39fc1923 100644 --- a/plugins/completion/nvim-cmp/cmp-helpers.nix +++ b/plugins/completion/cmp/cmp-helpers.nix @@ -6,7 +6,7 @@ ... }: with helpers.vim-plugin; -with lib; { +with lib; rec { mkCmpSourcePlugin = { name, extraPlugins ? [], @@ -16,6 +16,7 @@ with lib; { mkVimPlugin config { inherit name; extraPlugins = extraPlugins ++ (lists.optional useDefaultPackage pkgs.vimPlugins.${name}); + maintainers = [maintainers.GaetanLepage]; }; @@ -63,4 +64,62 @@ with lib; { "vsnip" = "cmp-vsnip"; "zsh" = "cmp-zsh"; }; + + extractSourcesFromOptionValue = sources: + if isList sources + then sources + else []; + + autoInstallSourcePluginsModule = cfg: let + # cfg.setup.sources + setupSources = extractSourcesFromOptionValue cfg.settings.sources; + # cfg.filetype..sources + filetypeSources = + mapAttrsToList + (_: filetypeConfig: + extractSourcesFromOptionValue filetypeConfig.sources) + cfg.filetype; + # cfg.cmdline..sources + cmdlineSources = + mapAttrsToList + (_: cmdlineConfig: + extractSourcesFromOptionValue cmdlineConfig.sources) + cfg.cmdline; + + # [{name = "foo";} {name = "bar"; x = 42;} ...] + allSources = flatten (setupSources ++ filetypeSources ++ cmdlineSources); + + # Take only the names from the sources provided by the user + # ["foo" "bar"] + foundSources = + lists.unique + ( + map + (source: source.name) + allSources + ); + + # A list of known source names + knownSourceNames = attrNames pluginAndSourceNames; + attrsEnabled = listToAttrs (map + (name: { + # Name of the corresponding plugin to enable + name = pluginAndSourceNames.${name}; + # Whether or not we enable it + value.enable = mkIf (elem name foundSources) true; + }) + knownSourceNames); + lspCapabilities = + mkIf + (elem "nvim_lsp" foundSources) + { + lsp.capabilities = '' + capabilities = vim.tbl_deep_extend("force", capabilities, require('cmp_nvim_lsp').default_capabilities()) + ''; + }; + in + mkMerge [ + (mkIf cfg.autoEnableSources attrsEnabled) + lspCapabilities + ]; } diff --git a/plugins/completion/cmp/default.nix b/plugins/completion/cmp/default.nix new file mode 100644 index 00000000..dd4e675a --- /dev/null +++ b/plugins/completion/cmp/default.nix @@ -0,0 +1,83 @@ +{ + lib, + helpers, + pkgs, + config, + ... +} @ args: let + cmpOptions = import ./options {inherit lib helpers;}; +in + with lib; + helpers.neovim-plugin.mkNeovimPlugin config { + name = "cmp"; + originalName = "nvim-cmp"; + defaultPackage = pkgs.vimPlugins.nvim-cmp; + + maintainers = [maintainers.GaetanLepage]; + + # Introduced on 2024 February 21 + # TODO: remove ~June 2024 + imports = [ + ./deprecations.nix + ./sources + ]; + deprecateExtraOptions = true; + + extraOptions = { + autoEnableSources = mkOption { + type = types.bool; + default = true; + description = '' + Scans the sources array and installs the plugins if they are known to nixvim. + ''; + }; + + inherit (cmpOptions) filetype cmdline; + }; + + inherit (cmpOptions) settingsOptions settingsExample; + + callSetup = false; + extraConfig = cfg: { + warnings = optional (cfg.autoEnableSources && (helpers.nixvimTypes.isRawType cfg.settings.sources)) '' + Nixvim (plugins.cmp): You have enabled `autoEnableSources` that tells Nixvim to automatically + enable the source plugins with respect to the list of sources provided in `settings.sources`. + However, the latter is proveded as a raw lua string which is not parseable by Nixvim. + + If you want to keep using raw lua for defining your sources: + - Ensure you enable the relevant plugins manually in your configuration; + - Dismiss this warning by explicitly setting `autoEnableSources` to `false`; + ''; + + extraConfigLua = + '' + local cmp = require('cmp') + cmp.setup(${helpers.toLuaObject cfg.settings}) + + '' + + ( + concatStringsSep "\n" + ( + mapAttrsToList + ( + filetype: settings: "cmp.setup.filetype('${filetype}', ${helpers.toLuaObject settings})\n" + ) + cfg.filetype + ) + ) + + ( + concatStringsSep "\n" + ( + mapAttrsToList + ( + cmdtype: settings: "cmp.setup.cmdline('${cmdtype}', ${helpers.toLuaObject settings})\n" + ) + cfg.cmdline + ) + ); + + # If autoEnableSources is set to true, figure out which are provided by the user + # and enable the corresponding plugins. + plugins = (import ./cmp-helpers.nix args).autoInstallSourcePluginsModule cfg; + }; + } diff --git a/plugins/completion/cmp/deprecations.nix b/plugins/completion/cmp/deprecations.nix new file mode 100644 index 00000000..4598eafc --- /dev/null +++ b/plugins/completion/cmp/deprecations.nix @@ -0,0 +1,154 @@ +{lib, ...}: +with lib; let + oldPluginBasePath = ["plugins" "nvim-cmp"]; + newPluginBasePath = ["plugins" "cmp"]; + settingsPath = newPluginBasePath ++ ["settings"]; + + renamedOptions = [ + {old = ["performance" "debounce"];} + {old = ["performance" "throttle"];} + { + old = ["performance" "fetchingTimeout"]; + new = ["performance" "fetching_timeout"]; + } + { + old = ["performance" "asyncBudget"]; + new = ["performance" "async_budget"]; + } + { + old = ["performance" "maxViewEntries"]; + new = ["performance" "max_view_entries"]; + } + {old = ["mapping"];} + { + old = ["completion" "keywordLength"]; + new = ["completion" "keyword_length"]; + } + { + old = ["completion" "keywordPattern"]; + new = ["completion" "keyword_pattern"]; + } + {old = ["completion" "autocomplete"];} + {old = ["completion" "completeopt"];} + { + old = ["confirmation" "getCommitCharacters"]; + new = ["confirmation" "get_commit_characters"]; + } + { + old = ["formatting" "expandableIndicator"]; + new = ["formatting" "expandable_indicator"]; + } + {old = ["formatting" "fields"];} + {old = ["formatting" "format"];} + { + old = ["matching" "disallowFuzzyMatching"]; + new = ["matching" "disallow_fuzzy_matching"]; + } + { + old = ["matching" "disallowFullfuzzyMatching"]; + new = ["matching" "disallow_fullfuzzy_matching"]; + } + { + old = ["matching" "disallowPartialFuzzyMatching"]; + new = ["matching" "disallow_partial_fuzzy_matching"]; + } + { + old = ["matching" "disallowPartialMatching"]; + new = ["matching" "disallow_partial_matching"]; + } + { + old = ["matching" "disallowPrefixUnmatching"]; + new = ["matching" "disallow_prefix_unmatching"]; + } + { + old = ["sorting" "priorityWeight"]; + new = ["sorting" "priority_weight"]; + } + {old = ["view" "entries"];} + { + old = ["view" "docs" "autoOpen"]; + new = ["view" "docs" "auto_open"]; + } + {old = ["window" "completion" "border"];} + {old = ["window" "completion" "winhighlight"];} + {old = ["window" "completion" "zindex"];} + {old = ["window" "completion" "scrolloff"];} + { + old = ["window" "completion" "colOffset"]; + new = ["window" "completion" "col_offset"]; + } + { + old = ["window" "completion" "sidePadding"]; + new = ["window" "completion" "side_padding"]; + } + {old = ["window" "completion" "scrollbar"];} + {old = ["window" "documentation" "border"];} + {old = ["window" "documentation" "winhighlight"];} + {old = ["window" "documentation" "zindex"];} + { + old = ["window" "documentation" "maxWidth"]; + new = ["window" "documentation" "max_width"]; + } + { + old = ["window" "documentation" "maxHeight"]; + new = ["window" "documentation" "max_height"]; + } + {old = ["experimental"];} + ]; + + renameWarnings = + map + ( + rename: + mkRenamedOptionModule + (oldPluginBasePath ++ rename.old) + (settingsPath ++ (rename.new or rename.old)) + ) + renamedOptions; +in { + imports = + renameWarnings + ++ [ + ( + mkRemovedOptionModule + (oldPluginBasePath ++ ["preselect"]) + '' + Use `plugins.cmp.settings.preselect` option. But watch out, you now have to explicitly write `cmp.PreselectMode.`. + See the option documentation for more details. + '' + ) + ( + mkRemovedOptionModule + (oldPluginBasePath ++ ["mappingPresets"]) + "If you want to have a complex mapping logic, express it in raw lua within the `plugins.cmp.settings.mapping` option." + ) + ( + mkRemovedOptionModule + (oldPluginBasePath ++ ["snippet" "expand"]) + '' + Use `plugins.cmp.settings.snippet.expand` option. But watch out, you can no longer put only the name of the snippet engine. + If you use `luasnip` for instance, set: + ``` + plugins.cmp.settings.snippet.expand = "function(args) require('luasnip').lsp_expand(args.body) end"; + ``` + '' + ) + ( + mkRemovedOptionModule + (oldPluginBasePath ++ ["sorting" "comparators"]) + '' + Use `plugins.cmp.settings.sorting.comparators` option. But watch out, you can no longer put only the name of the comparators. + See the option documentation for more details. + '' + ) + ( + mkRemovedOptionModule + (oldPluginBasePath ++ ["sources"]) + '' + Use `plugins.cmp.settings.sources` option. But watch out, you can no longer provide a list of lists of sources. + For this type of use, directly write lua. + See the option documentation for more details. + '' + ) + ]; +} diff --git a/plugins/completion/cmp/options/default.nix b/plugins/completion/cmp/options/default.nix new file mode 100644 index 00000000..3b282173 --- /dev/null +++ b/plugins/completion/cmp/options/default.nix @@ -0,0 +1,82 @@ +{ + lib, + helpers, +}: +with lib; rec { + settingsOptions = import ./settings-options.nix {inherit lib helpers;}; + + settingsExample = { + snippet.expand = "luasnip"; + mapping.__raw = '' + cmp.mapping.preset.insert({ + [''] = cmp.mapping.scroll_docs(-4), + [''] = cmp.mapping.scroll_docs(4), + [''] = cmp.mapping.complete(), + [''] = cmp.mapping.abort(), + [''] = cmp.mapping.confirm({ select = true }), + }) + ''; + sources.__raw = '' + cmp.config.sources({ + { name = 'nvim_lsp' }, + { name = 'vsnip' }, + -- { name = 'luasnip' }, + -- { name = 'ultisnips' }, + -- { name = 'snippy' }, + }, { + { name = 'buffer' }, + }) + ''; + }; + + attrsOfOptions = with types; + attrsOf ( + submodule { + freeformType = attrsOf anything; + options = settingsOptions; + } + ); + + filetype = mkOption { + type = attrsOfOptions; + default = {}; + description = "Options for `cmp.filetype()`."; + example = { + python = { + sources = [ + {name = "nvim_lsp";} + ]; + }; + }; + }; + + cmdline = mkOption { + type = attrsOfOptions; + default = {}; + description = "Options for `cmp.cmdline()`."; + example = { + "/" = { + mapping.__raw = "cmp.mapping.preset.cmdline()"; + sources = [ + {name = "buffer";} + ]; + }; + ":" = { + mapping.__raw = "cmp.mapping.preset.cmdline()"; + sources = [ + [ + {name = "path";} + ] + [ + { + name = "cmdline"; + option = { + ignore_cmds = ["Man" "!"]; + }; + } + ] + ]; + }; + }; + }; +} diff --git a/plugins/completion/cmp/options/settings-options.nix b/plugins/completion/cmp/options/settings-options.nix new file mode 100644 index 00000000..61a437f6 --- /dev/null +++ b/plugins/completion/cmp/options/settings-options.nix @@ -0,0 +1,350 @@ +{ + lib, + helpers, +}: +with lib; { + performance = { + debounce = helpers.defaultNullOpts.mkUnsignedInt 60 '' + Sets debounce time. + This is the interval used to group up completions from different sources for filtering and + displaying. + ''; + + throttle = helpers.defaultNullOpts.mkUnsignedInt 30 '' + Sets throttle time. + This is used to delay filtering and displaying completions. + ''; + + fetching_timeout = helpers.defaultNullOpts.mkUnsignedInt 500 '' + Sets the timeout of candidate fetching process. + The nvim-cmp will wait to display the most prioritized source. + ''; + + confirm_resolve_timeout = helpers.defaultNullOpts.mkUnsignedInt 80 '' + Sets the timeout for resolving item before confirmation. + ''; + + async_budget = helpers.defaultNullOpts.mkUnsignedInt 1 '' + Maximum time (in ms) an async function is allowed to run during one step of the event loop. + ''; + + max_view_entries = helpers.defaultNullOpts.mkUnsignedInt 200 '' + Maximum number of items to show in the entries list. + ''; + }; + + preselect = helpers.defaultNullOpts.mkLua "cmp.PreselectMode.Item" '' + - "cmp.PreselectMode.Item": nvim-cmp will preselect the item that the source specified. + - "cmp.PreselectMode.None": nvim-cmp will not preselect any items. + ''; + + mapping = mkOption { + default = {}; + type = with helpers.nixvimTypes; + maybeRaw + (attrsOf strLua); + apply = v: + # Handle the raw case first + if helpers.nixvimTypes.isRawType v + then v + # When v is an attrs **but not {__raw = ...}** + else mapAttrs (_: helpers.mkRaw) v; + example = { + "" = "cmp.mapping.scroll_docs(-4)"; + "" = "cmp.mapping.scroll_docs(4)"; + "" = "cmp.mapping.complete()"; + "" = "cmp.mapping.close()"; + "" = "cmp.mapping(cmp.mapping.select_next_item(), {'i', 's'})"; + "" = "cmp.mapping(cmp.mapping.select_prev_item(), {'i', 's'})"; + "" = "cmp.mapping.confirm({ select = true })"; + }; + }; + + snippet = { + expand = mkOption { + type = with helpers.nixvimTypes; nullOr strLuaFn; + default = null; + description = '' + The snippet expansion function. That's how nvim-cmp interacts with a particular snippet + engine. + + + Common engines: + ```lua + function(args) + # vsnip + vim.fn["vsnip#anonymous"](args.body) + + # luasnip + require('luasnip').lsp_expand(args.body) + + # snippy + require('snippy').expand_snippet(args.body) + + # ultisnips + vim.fn["UltiSnips#Anon"](args.body) + end + ``` + + You can also provide a custom function: + ```lua + function(args) + vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users. + -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users. + -- require('snippy').expand_snippet(args.body) -- For `snippy` users. + -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users. + end + ``` + ''; + apply = helpers.mkRaw; + example = '' + function(args) + require('luasnip').lsp_expand(args.body) + end + ''; + }; + }; + + completion = { + keyword_length = helpers.defaultNullOpts.mkUnsignedInt 1 '' + The number of characters needed to trigger auto-completion. + ''; + + keyword_pattern = + helpers.defaultNullOpts.mkLua + ''[[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]]'' + "The default keyword pattern."; + + autocomplete = + helpers.defaultNullOpts.mkNullable + ( + with helpers.nixvimTypes; + either + (enum [false]) + (listOf strLua) + ) + ''["require('cmp.types').cmp.TriggerEvent.TextChanged"]'' + '' + The event to trigger autocompletion. + If set to `false`, then completion is only invoked manually (e.g. by calling `cmp.complete`). + ''; + + completeopt = helpers.defaultNullOpts.mkStr "menu,menuone,noselect" '' + Like vim's completeopt setting. + In general, you don't need to change this. + ''; + }; + + confirmation = { + get_commit_characters = + helpers.defaultNullOpts.mkLuaFn + '' + function(commit_characters) + return commit_characters + end + '' + '' + You can append or exclude `commitCharacters` via this configuration option function. + The `commitCharacters` are defined by the LSP spec. + ''; + }; + + formatting = { + expandable_indicator = helpers.defaultNullOpts.mkBool true '' + Boolean to show the `~` expandable indicator in cmp's floating window. + ''; + + fields = helpers.defaultNullOpts.mkListOf types.str ''["abbr" "kind" "menu"]'' '' + An array of completion fields to specify their order. + ''; + + format = + helpers.defaultNullOpts.mkLuaFn + '' + function(_, vim_item) + return vim_item + end + '' + '' + `fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem` + + The function used to customize the appearance of the completion menu. + See `|complete-items|`. + This value can also be used to modify the `dup` property. + + NOTE: The `vim.CompletedItem` can contain the special properties `abbr_hl_group`, + `kind_hl_group` and `menu_hl_group`. + ''; + }; + + matching = { + disallow_fuzzy_matching = helpers.defaultNullOpts.mkBool false '' + Whether to allow fuzzy matching. + ''; + + disallow_fullfuzzy_matching = helpers.defaultNullOpts.mkBool false '' + Whether to allow full-fuzzy matching. + ''; + + disallow_partial_fuzzy_matching = helpers.defaultNullOpts.mkBool true '' + Whether to allow fuzzy matching without prefix matching. + ''; + + disallow_partial_matching = helpers.defaultNullOpts.mkBool false '' + Whether to allow partial matching. + ''; + + disallow_prefix_unmatching = helpers.defaultNullOpts.mkBool false '' + Whether to allow prefix unmatching. + ''; + }; + + sorting = { + priority_weight = helpers.defaultNullOpts.mkUnsignedInt 2 '' + Each item's original priority (given by its corresponding source) will be increased by + `#sources - (source_index - 1)` and multiplied by `priority_weight`. + + That is, the final priority is calculated by the following formula: + `final_score = orig_score + ((#sources - (source_index - 1)) * sorting.priority_weight)` + ''; + + comparators = mkOption { + type = with helpers.nixvimTypes; nullOr (listOf strLuaFn); + apply = v: + helpers.ifNonNull' v ( + map + helpers.mkRaw + v + ); + default = null; + description = '' + The function to customize the sorting behavior. + You can use built-in comparators via `cmp.config.compare.*`. + + Signature: `(fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil)[]` + + Default: + ```nix + [ + "require('cmp.config.compare').offset" + "require('cmp.config.compare').exact" + "require('cmp.config.compare').score" + "require('cmp.config.compare').recently_used" + "require('cmp.config.compare').locality" + "require('cmp.config.compare').kind" + "require('cmp.config.compare').length" + "require('cmp.config.compare').order" + ] + ``` + ''; + }; + }; + + sources = import ./sources-option.nix {inherit lib helpers;}; + + view = { + entries = + helpers.defaultNullOpts.mkNullable + ( + with types; + either + str + (attrsOf anything) + ) + '' + { + name = "custom"; + selection_order = "top_down"; + } + '' + '' + The view class used to customize nvim-cmp's appearance. + ''; + + docs = { + auto_open = helpers.defaultNullOpts.mkBool true '' + Specify whether to show the docs_view when selecting an item. + ''; + }; + }; + + window = let + mkWinhighlightOption = default: + helpers.defaultNullOpts.mkStr + default + '' + Specify the window's winhighlight option. + See `|nvim_open_win|`. + ''; + + zindex = helpers.mkNullOrOption types.ints.unsigned '' + The window's zindex. + See `|nvim_open_win|`. + ''; + in { + completion = { + border = + helpers.defaultNullOpts.mkBorder + ''[ "" "" "" "" "" "" "" "" ]'' + "nvim-cmp completion popup menu" + ""; + + winhighlight = + mkWinhighlightOption + "Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None"; + + inherit zindex; + + scrolloff = helpers.defaultNullOpts.mkUnsignedInt 0 '' + Specify the window's scrolloff option. + See |'scrolloff'|. + ''; + + col_offset = helpers.defaultNullOpts.mkUnsignedInt 0 '' + Offsets the completion window relative to the cursor. + ''; + + side_padding = helpers.defaultNullOpts.mkUnsignedInt 1 '' + The amount of padding to add on the completion window's sides. + ''; + + scrollbar = helpers.defaultNullOpts.mkBool true '' + Whether the scrollbar should be enabled if there are more items that fit. + ''; + }; + + documentation = { + border = + helpers.defaultNullOpts.mkBorder + ''[ "" "" "" " " "" "" "" " " ]'' + "nvim-cmp documentation popup menu" + ""; + + winhighlight = mkWinhighlightOption "FloatBorder:NormalFloat"; + + inherit zindex; + + max_width = + helpers.mkNullOrStrLuaOr types.ints.unsigned + '' + The documentation window's max width. + + Default: "math.floor((40 * 2) * (vim.o.columns / (40 * 2 * 16 / 9)))" + ''; + + max_height = + helpers.mkNullOrStrLuaOr types.ints.unsigned + '' + The documentation window's max height. + + Default: "math.floor(40 * (40 / vim.o.lines))" + ''; + }; + }; + + # This can be kept as types.attrs since experimental features are often removed or completely + # changed after a while + experimental = helpers.mkNullOrOption (with types; attrsOf anything) '' + Experimental features. + ''; +} diff --git a/plugins/completion/cmp/options/sources-option.nix b/plugins/completion/cmp/options/sources-option.nix new file mode 100644 index 00000000..19a7a65a --- /dev/null +++ b/plugins/completion/cmp/options/sources-option.nix @@ -0,0 +1,107 @@ +{ + lib, + helpers, +}: +with lib; let + sourceType = types.submodule { + freeformType = with types; attrsOf anything; + options = { + name = mkOption { + type = types.str; + description = "The name of the source."; + example = "buffer"; + }; + + option = helpers.mkNullOrOption (with types; attrsOf anything) '' + Any specific options defined by the source itself. + + If direct lua code is needed use `helpers.mkRaw`. + ''; + + keyword_length = helpers.mkNullOrOption types.ints.unsigned '' + The source-specific keyword length to trigger auto completion. + ''; + + keyword_pattern = helpers.mkNullOrLua '' + The source-specific keyword pattern. + ''; + + trigger_characters = helpers.mkNullOrOption (with types; listOf str) '' + Trigger characters. + ''; + + priority = helpers.mkNullOrOption types.ints.unsigned '' + The source-specific priority value. + ''; + + group_index = helpers.mkNullOrOption types.ints.unsigned '' + The source group index. + + For instance, you can set the `buffer`'s source `group_index` to a larger number + if you don't want to see `buffer` source items while `nvim-lsp` source is available: + + ```nix + sources = [ + { + name = "nvim_lsp"; + group_index = 1; + } + { + name = "buffer"; + group_index = 2; + } + ]; + ``` + ''; + + entry_filter = helpers.mkNullOrLuaFn '' + A source-specific entry filter, with the following function signature: + + `function(entry: cmp.Entry, ctx: cmp.Context): boolean` + + Returning `true` will keep the entry, while returning `false` will remove it. + + This can be used to hide certain entries from a given source. For instance, you + could hide all entries with kind `Text` from the `nvim_lsp` filter using the + following source definition: + + ```nix + { + name = "nvim_lsp"; + entry_filter = \'\' + function(entry, ctx) + return require('cmp.types').lsp.CompletionItemKind[entry:get_kind()] ~= 'Text' + end + \'\'; + } + ``` + + Using the `ctx` parameter, you can further customize the behaviour of the source. + ''; + }; + }; +in + mkOption { + default = []; + type = with helpers.nixvimTypes; + maybeRaw (listOf sourceType); + description = '' + The sources to use. + Can either be a list of `sourceConfigs` which will be made directly to a Lua object. + Or it can be a raw lua string which might be necessary for more advanced use cases. + + WARNING: + If `plugins.cmp.autoEnableSources` Nixivm will automatically enable the corresponding source + plugins. This will work only when this option is set to a list. + If you use a raw lua string, you will need to explicitly enable the relevant source plugins in + your nixvim configuration. + + Default: `[]` + ''; + example = [ + {name = "nvim_lsp";} + {name = "luasnip";} + {name = "path";} + {name = "buffer";} + ]; + } diff --git a/plugins/completion/nvim-cmp/sources/cmp-tabby.nix b/plugins/completion/cmp/sources/cmp-tabby.nix similarity index 100% rename from plugins/completion/nvim-cmp/sources/cmp-tabby.nix rename to plugins/completion/cmp/sources/cmp-tabby.nix diff --git a/plugins/completion/nvim-cmp/sources/cmp-tabnine.nix b/plugins/completion/cmp/sources/cmp-tabnine.nix similarity index 100% rename from plugins/completion/nvim-cmp/sources/cmp-tabnine.nix rename to plugins/completion/cmp/sources/cmp-tabnine.nix diff --git a/plugins/completion/nvim-cmp/sources/codeium-nvim.nix b/plugins/completion/cmp/sources/codeium-nvim.nix similarity index 100% rename from plugins/completion/nvim-cmp/sources/codeium-nvim.nix rename to plugins/completion/cmp/sources/codeium-nvim.nix diff --git a/plugins/completion/nvim-cmp/sources/copilot-cmp.nix b/plugins/completion/cmp/sources/copilot-cmp.nix similarity index 100% rename from plugins/completion/nvim-cmp/sources/copilot-cmp.nix rename to plugins/completion/cmp/sources/copilot-cmp.nix diff --git a/plugins/completion/nvim-cmp/sources/crates-nvim.nix b/plugins/completion/cmp/sources/crates-nvim.nix similarity index 100% rename from plugins/completion/nvim-cmp/sources/crates-nvim.nix rename to plugins/completion/cmp/sources/crates-nvim.nix diff --git a/plugins/completion/nvim-cmp/sources/default.nix b/plugins/completion/cmp/sources/default.nix similarity index 75% rename from plugins/completion/nvim-cmp/sources/default.nix rename to plugins/completion/cmp/sources/default.nix index 54927b09..b999e40b 100644 --- a/plugins/completion/nvim-cmp/sources/default.nix +++ b/plugins/completion/cmp/sources/default.nix @@ -8,7 +8,13 @@ with lib; let cmpLib = import ../cmp-helpers.nix {inherit lib config helpers pkgs;}; cmpSourcesPluginNames = attrValues cmpLib.pluginAndSourceNames; - pluginModules = lists.map (name: cmpLib.mkCmpSourcePlugin {inherit name;}) cmpSourcesPluginNames; + pluginModules = + map + ( + name: + cmpLib.mkCmpSourcePlugin {inherit name;} + ) + cmpSourcesPluginNames; in { # For extra cmp plugins imports = diff --git a/plugins/completion/lspkind.nix b/plugins/completion/lspkind.nix index e5a416c2..2d4dbd80 100644 --- a/plugins/completion/lspkind.nix +++ b/plugins/completion/lspkind.nix @@ -49,7 +49,7 @@ in { }; config = let - doCmp = cfg.cmp.enable && config.plugins.nvim-cmp.enable; + doCmp = cfg.cmp.enable && config.plugins.cmp.enable; options = { inherit (cfg) mode preset; @@ -73,7 +73,7 @@ in { require('lspkind').init(${helpers.toLuaObject options}) ''; - plugins.nvim-cmp.formatting.format = + plugins.cmp.settings.formatting.format = if cfg.cmp.after != null then '' function(entry, vim_item) diff --git a/plugins/completion/nvim-cmp/default.nix b/plugins/completion/nvim-cmp/default.nix deleted file mode 100644 index 006a6ad8..00000000 --- a/plugins/completion/nvim-cmp/default.nix +++ /dev/null @@ -1,578 +0,0 @@ -{ - lib, - helpers, - pkgs, - config, - ... -} @ args: -with lib; let - cfg = config.plugins.nvim-cmp; - cmpLib = import ./cmp-helpers.nix args; -in { - options.plugins.nvim-cmp = - helpers.neovim-plugin.extraOptionsOptions - // { - enable = mkEnableOption "nvim-cmp"; - - package = helpers.mkPackageOption "nvim-cmp" pkgs.vimPlugins.nvim-cmp; - - performance = { - debounce = helpers.defaultNullOpts.mkInt 60 '' - Sets debounce time - This is the interval used to group up completions from different sources - for filtering and displaying. - ''; - - throttle = helpers.defaultNullOpts.mkInt 30 '' - Sets throttle time. - This is used to delay filtering and displaying completions. - ''; - - fetchingTimeout = helpers.defaultNullOpts.mkInt 500 '' - Sets the timeout of candidate fetching process. - The nvim-cmp will wait to display the most prioritized source. - ''; - - asyncBudget = helpers.defaultNullOpts.mkInt 1 '' - Maximum time (in ms) an async function is allowed to run during one step of the event loop. - ''; - - maxViewEntries = helpers.defaultNullOpts.mkInt 200 '' - Maximum number of items to show in the entries list. - ''; - }; - - preselect = mkOption { - type = with types; nullOr (enum ["Item" "None"]); - apply = v: - helpers.ifNonNull' v - (helpers.mkRaw "cmp.PreselectMode.${v}"); - default = null; - description = '' - - "Item": nvim-cmp will preselect the item that the source specified. - - "None": nvim-cmp will not preselect any items. - ''; - }; - - mapping = mkOption { - default = {}; - type = with types; - attrsOf - ( - either - str - ( - submodule { - options = { - action = mkOption { - type = nonEmptyStr; - description = "The function the mapping should call"; - example = ''"cmp.mapping.scroll_docs(-4)"''; - }; - modes = mkOption { - default = null; - type = nullOr (listOf str); - example = ''[ "i" "s" ]''; - }; - }; - } - ) - ); - example = { - "" = "cmp.mapping.scroll_docs(-4)"; - "" = "cmp.mapping.scroll_docs(4)"; - "" = "cmp.mapping.complete()"; - "" = "cmp.mapping.close()"; - "" = { - modes = ["i" "s"]; - action = "cmp.mapping.select_next_item()"; - }; - "" = { - modes = ["i" "s"]; - action = "cmp.mapping.select_prev_item()"; - }; - "" = "cmp.mapping.confirm({ select = true })"; - }; - }; - - mappingPresets = mkOption { - default = []; - type = with types; - listOf (enum ["insert" "cmdline"]); - description = '' - Mapping presets to use; cmp.mapping.preset. - \$\{mappingPreset} will be called with the configured mappings. - ''; - example = ''[ "insert" "cmdline" ]''; - }; - - snippet = { - expand = let - snippetEngines = { - "vsnip" = ''vim.fn["vsnip#anonymous"](args.body)''; - "luasnip" = ''require('luasnip').lsp_expand(args.body)''; - "snippy" = ''require('snippy').expand_snippet(args.body)''; - "ultisnips" = ''vim.fn["UltiSnips#Anon"](args.body)''; - }; - in - mkOption { - default = null; - type = with types; - nullOr - ( - either - helpers.nixvimTypes.rawLua - (enum (attrNames snippetEngines)) - ); - apply = v: - if isString v - then - helpers.mkRaw '' - function(args) - ${snippetEngines.${v}} - end - '' - else v; - description = '' - The snippet expansion function. That's how nvim-cmp interacts with a - particular snippet engine. - - You may directly provide one of those four supported engines: - - vsnip - - luasnip - - snippy - - ultisnips - - You can also provide a custom function: - ```nix - { - __raw = \'\' - function(args) - vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` users. - -- require('luasnip').lsp_expand(args.body) -- For `luasnip` users. - -- require('snippy').expand_snippet(args.body) -- For `snippy` users. - -- vim.fn["UltiSnips#Anon"](args.body) -- For `ultisnips` users. - end - \'\'; - }; - ``` - ''; - }; - }; - - completion = { - keywordLength = helpers.defaultNullOpts.mkInt 1 '' - The number of characters needed to trigger auto-completion. - ''; - - keywordPattern = mkOption { - type = with types; nullOr str; - default = null; - description = '' - The default keyword pattern. - - Note: the provided pattern will be embedded as such: `[[PATTERN]]`. - - Default: "\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)" - ''; - apply = v: - helpers.ifNonNull' - v - (helpers.mkRaw "[[${v}]]"); - }; - - autocomplete = mkOption { - type = with types; - nullOr - ( - either - (listOf str) - (types.enum [false]) - ); - default = null; - description = '' - The event to trigger autocompletion. - If set to `"false"`, then completion is only invoked manually - (e.g. by calling `cmp.complete`). - - Default: ["TextChanged"] - ''; - apply = v: - if isList v - then - map - (triggerEvent: - helpers.mkRaw "require('cmp.types').cmp.TriggerEvent.${triggerEvent}") - v - # either null or false - else v; - }; - - completeopt = helpers.defaultNullOpts.mkStr "menu,menuone,noselect" '' - Like vim's completeopt setting. In general, you don't need to change this. - ''; - }; - - confirmation = { - getCommitCharacters = - helpers.defaultNullOpts.mkLuaFn - '' - function(commit_characters) - return commit_characters - end - '' - '' - You can append or exclude commitCharacters via this configuration option - function. The commitCharacters are defined by the LSP spec. - ''; - }; - - formatting = { - expandableIndicator = helpers.defaultNullOpts.mkBool true '' - Boolean to show the `~` expandable indicator in cmp's floating window. - ''; - - fields = - helpers.defaultNullOpts.mkNullable - (with types; listOf str) - ''[ "kind" "abbr" "menu" ]'' - "An array of completion fields to specify their order."; - - format = - helpers.defaultNullOpts.mkLuaFn - '' - function(_, vim_item) - return vim_item - end - '' - '' - `fun(entry: cmp.Entry, vim_item: vim.CompletedItem): vim.CompletedItem` - The function used to customize the appearance of the completion menu. See - |complete-items|. This value can also be used to modify the `dup` property. - NOTE: The `vim.CompletedItem` can contain the special properties - `abbr_hl_group`, `kind_hl_group` and `menu_hl_group`. - ''; - }; - - matching = { - disallowFuzzyMatching = helpers.defaultNullOpts.mkBool false '' - Whether to allow fuzzy matching. - ''; - - disallowFullfuzzyMatching = helpers.defaultNullOpts.mkBool false '' - Whether to allow full-fuzzy matching. - ''; - - disallowPartialFuzzyMatching = helpers.defaultNullOpts.mkBool true '' - Whether to allow fuzzy matching without prefix matching. - ''; - - disallowPartialMatching = helpers.defaultNullOpts.mkBool false '' - Whether to allow partial matching. - ''; - - disallowPrefixUnmatching = helpers.defaultNullOpts.mkBool false '' - Whether to allow prefix unmatching. - ''; - }; - - sorting = { - priorityWeight = helpers.defaultNullOpts.mkInt 2 '' - Each item's original priority (given by its corresponding source) will be - increased by `#sources - (source_index - 1)` and multiplied by `priority_weight`. - That is, the final priority is calculated by the following formula: - - `final_score = orig_score + ((#sources - (source_index - 1)) * sorting.priority_weight)` - ''; - - comparators = mkOption { - type = with helpers.nixvimTypes; nullOr (listOf strLuaFn); - apply = v: - helpers.ifNonNull' v ( - map - ( - funcName: - helpers.mkRaw "require('cmp.config.compare').${funcName}" - ) - v - ); - default = null; - description = '' - The function to customize the sorting behavior. - You can use built-in comparators via `cmp.config.compare.*`. - - Signature: `(fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil)[]` - - Default: ["offset" "exact" "score" "recently_used" "locality" "kind" "length" "order"] - ''; - }; - }; - - autoEnableSources = mkOption { - type = types.bool; - default = true; - description = '' - Scans the sources array and installs the plugins if they are known to nixvim. - ''; - }; - - sources = let - source_config = types.submodule { - options = { - name = mkOption { - type = types.str; - description = "The name of the source."; - example = "buffer"; - }; - - option = helpers.mkNullOrOption types.attrs '' - Any specific options defined by the source itself. - - If direct lua code is needed use `helpers.mkRaw`. - ''; - - keywordLength = helpers.mkNullOrOption types.int '' - The source-specific keyword length to trigger auto completion. - ''; - - keywordPattern = helpers.mkNullOrOption types.str '' - The source-specific keyword pattern. - - Note: the provided pattern will be embedded as such: `[[PATTERN]]`. - ''; - - triggerCharacters = helpers.mkNullOrOption (with types; listOf str) '' - A source-specific keyword pattern. - ''; - - priority = helpers.mkNullOrOption types.int "The source-specific priority value."; - - groupIndex = helpers.mkNullOrOption types.int '' - The source group index. - - For instance, you can set the `buffer`'s source `groupIndex` to a larger number - if you don't want to see `buffer` source items while `nvim-lsp` source is available: - - ```nix - sources = [ - { - name = "nvim_lsp"; - groupIndex = 1; - } - { - name = "buffer"; - groupIndex = 2; - } - ]; - ``` - ''; - - entryFilter = helpers.mkNullOrOption types.str '' - A source-specific entry filter, with the following function signature: - - `function(entry: cmp.Entry, ctx: cmp.Context): boolean` - - Returning `true` will keep the entry, while returning `false` will remove it. - - This can be used to hide certain entries from a given source. For instance, you - could hide all entries with kind `Text` from the `nvim_lsp` filter using the - following source definition: - - ``` - { - name = 'nvim_lsp', - entry_filter = function(entry, ctx) - return require('cmp.types').lsp.CompletionItemKind[entry:get_kind()] ~= 'Text' - end - } - ``` - - Using the `ctx` parameter, you can further customize the behaviour of the source. - ''; - }; - }; - in - mkOption { - default = null; - type = with types; - nullOr - ( - listOf - ( - either - source_config - (listOf source_config) - ) - ); - description = '' - The sources to use. - Can either be a list of sourceConfigs which will be made directly to a Lua object. - Or it can be a list of lists, which will use the cmp built-in helper function - `cmp.config.sources`. - - Default: `[]` - ''; - example = '' - [ - { name = "nvim_lsp"; } - { name = "luasnip"; } #For luasnip users. - { name = "path"; } - { name = "buffer"; } - ] - ''; - }; - - view = { - entries = - helpers.defaultNullOpts.mkNullable (with types; either str attrs) - '' - { - name = "custom"; - selection_order = "top_down"; - } - '' - '' - The view class used to customize nvim-cmp's appearance. - ''; - - docs = { - autoOpen = helpers.defaultNullOpts.mkBool true '' - Specify whether to show the docs_view when selecting an item. - ''; - }; - }; - - window = let - # Reusable options - mkWinhighlightOption = default: - helpers.defaultNullOpts.mkStr - default - '' - Specify the window's winhighlight option. - See |nvim_open_win|. - ''; - - zindex = helpers.mkNullOrOption types.int '' - The completion window's zindex. - See |nvim_open_win|. - ''; - in { - completion = { - border = - helpers.defaultNullOpts.mkBorder - ''[ "" "" "" "" "" "" "" "" ]'' - "nvim-cmp window" - ""; - - winhighlight = - mkWinhighlightOption - "Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None"; - - inherit zindex; - - scrolloff = helpers.defaultNullOpts.mkInt 0 '' - Specify the window's scrolloff option. - See |'scrolloff'|. - ''; - - colOffset = helpers.defaultNullOpts.mkInt 0 '' - Offsets the completion window relative to the cursor. - ''; - - sidePadding = helpers.defaultNullOpts.mkInt 1 '' - The amount of padding to add on the completion window's sides. - ''; - - scrollbar = helpers.defaultNullOpts.mkBool true '' - Whether the scrollbar should be enabled if there are more items that fit - ''; - }; - - documentation = { - border = - helpers.defaultNullOpts.mkBorder - ''[ "" "" "" " " "" "" "" " " ]'' - "nvim-cmp documentation window" - ""; - - winhighlight = mkWinhighlightOption "FloatBorder:NormalFloat"; - - inherit zindex; - - maxWidth = - helpers.mkNullOrStrLuaOr types.ints.unsigned - '' - The documentation window's max width. - - Default: "math.floor((40 * 2) * (vim.o.columns / (40 * 2 * 16 / 9)))" - ''; - - maxHeight = - helpers.mkNullOrStrLuaOr types.ints.unsigned - '' - The documentation window's max height. - - Default: "math.floor(40 * (40 / vim.o.lines))" - ''; - }; - }; - - # This can be kept as types.attrs since experimental features are often removed or completely - # changed after a while - experimental = helpers.mkNullOrOption types.attrs "Experimental features"; - }; - - config = mkIf cfg.enable { - extraPlugins = [cfg.package]; - - extraConfigLua = let - setupOptions = import ./setup-options.nix { - inherit cfg lib helpers; - }; - in - helpers.wrapDo '' - local cmp = require('cmp') - cmp.setup(${helpers.toLuaObject setupOptions}) - ''; - - # If autoEnableSources is set to true, figure out which are provided by the user - # and enable the corresponding plugins. - plugins = let - sourcesFlattenedList = - if cfg.sources == null - then [] - else flatten cfg.sources; - - # Take only the names from the sources provided by the user - foundSources = - lists.unique - ( - map - (source: source.name) - sourcesFlattenedList - ); - - # A list of known source names - knownSourceNames = attrNames cmpLib.pluginAndSourceNames; - - attrsEnabled = listToAttrs (map - (name: { - # Name of the corresponding plugin to enable - name = cmpLib.pluginAndSourceNames.${name}; - - # Whether or not we enable it - value.enable = mkIf (elem name foundSources) true; - }) - knownSourceNames); - in - mkMerge [ - (mkIf cfg.autoEnableSources attrsEnabled) - (mkIf (elem "nvim_lsp" foundSources) - { - lsp.capabilities = '' - capabilities = vim.tbl_deep_extend("force", capabilities, require('cmp_nvim_lsp').default_capabilities()) - ''; - }) - ]; - }; -} diff --git a/plugins/completion/nvim-cmp/setup-options.nix b/plugins/completion/nvim-cmp/setup-options.nix deleted file mode 100644 index dc848af5..00000000 --- a/plugins/completion/nvim-cmp/setup-options.nix +++ /dev/null @@ -1,148 +0,0 @@ -{ - cfg, - lib, - helpers, -}: -with lib; let - # Not very readable sorry - # If null then null - # If an attribute is a string, just treat it as lua code for that mapping - # If an attribute is a module, create a mapping with cmp.mapping() using the action as the first input and the modes as the second. - mapping' = let - mappings = - mapAttrs - ( - key: action: - helpers.mkRaw ( - if isString action - then action - else let - inherit (action) modes; - modesString = - optionalString - ( - (modes != null) - && ((length modes) >= 1) - ) - ("," + (helpers.toLuaObject modes)); - in "cmp.mapping(${action.action}${modesString})" - ) - ) - cfg.mapping; - - luaMappings = helpers.toLuaObject mappings; - - wrapped = - lists.fold - ( - presetName: prevString: ''cmp.mapping.preset.${presetName}(${prevString})'' - ) - luaMappings - cfg.mappingPresets; - in - helpers.mkRaw wrapped; - - sources' = let - convertSourceAttrs = source: - with source; { - inherit - name - option - ; - keyword_length = keywordLength; - keywordPattern = - helpers.ifNonNull' keywordPattern (helpers.mkRaw "[[${keywordPattern}]]"); - trigger_characters = triggerCharacters; - inherit priority; - group_index = groupIndex; - entry_filter = entryFilter; - }; - in - if cfg.sources == null || cfg.sources == [] - then null - # List of lists of sources -> we use the `cmp.config.sources` helper - else if isList (head cfg.sources) - then let - sourcesListofLists = - map - (map convertSourceAttrs) - cfg.sources; - in - helpers.mkRaw "cmp.config.sources(${helpers.toLuaObject sourcesListofLists})" - # List of sources - else map convertSourceAttrs cfg.sources; -in - with cfg; - { - performance = with performance; { - inherit - debounce - throttle - ; - fetching_timeout = fetchingTimeout; - async_budget = asyncBudget; - max_view_entries = maxViewEntries; - }; - inherit preselect; - mapping = mapping'; - snippet = with snippet; { - inherit expand; - }; - completion = with completion; { - keyword_length = keywordLength; - keyword_pattern = keywordPattern; - inherit - autocomplete - completeopt - ; - }; - confirmation = with confirmation; { - get_commit_characters = getCommitCharacters; - }; - formatting = with formatting; { - expandable_indicator = expandableIndicator; - inherit fields format; - }; - matching = with matching; { - disallow_fuzzy_matching = disallowFuzzyMatching; - disallow_fullfuzzy_matching = disallowFullfuzzyMatching; - disallow_partial_fuzzy_matching = disallowPartialFuzzyMatching; - disallow_partial_matching = disallowPartialMatching; - disallow_prefix_unmatching = disallowPrefixUnmatching; - }; - sorting = with sorting; { - priority_weight = priorityWeight; - inherit comparators; - }; - sources = sources'; - view = with view; { - inherit entries; - docs = with docs; { - auto_open = autoOpen; - }; - }; - window = with window; { - completion = with completion; { - inherit - border - winhighlight - zindex - scrolloff - ; - col_offset = colOffset; - side_padding = sidePadding; - inherit scrollbar; - }; - documentation = with documentation; { - inherit - border - winhighlight - zindex - ; - max_width = maxWidth; - max_height = maxHeight; - }; - }; - inherit experimental; - } - // cfg.extraOptions diff --git a/plugins/default.nix b/plugins/default.nix index c4ea2eb8..21cc9ef6 100644 --- a/plugins/default.nix +++ b/plugins/default.nix @@ -27,8 +27,7 @@ ./completion/coq.nix ./completion/coq-thirdparty.nix ./completion/lspkind.nix - ./completion/nvim-cmp - ./completion/nvim-cmp/sources + ./completion/cmp ./filetrees/chadtree.nix ./filetrees/neo-tree.nix diff --git a/plugins/utils/obsidian.nix b/plugins/utils/obsidian.nix index bd5fefff..13b5a68f 100644 --- a/plugins/utils/obsidian.nix +++ b/plugins/utils/obsidian.nix @@ -122,7 +122,7 @@ with lib; let nvimCmp = helpers.mkNullOrOption types.bool '' Set to false to disable completion. - Default: `true` if `nvim-cmp` is enabled. + Default: `true` if `cmp` is enabled. ''; minChars = helpers.defaultNullOpts.mkUnsignedInt 2 '' @@ -500,10 +500,10 @@ in { assertion = let nvimCmpEnabled = isBool cfg.completion.nvimCmp && cfg.completion.nvimCmp; in - nvimCmpEnabled -> config.plugins.nvim-cmp.enable; + nvimCmpEnabled -> config.plugins.cmp.enable; message = '' - Nixvim (plugins.obsidian): You have enabled `completion.nvimCmp` but `plugins.nvim-cmp.enable` is `false`. - You need to enable `nvim-cmp` to use this setting. + Nixvim (plugins.obsidian): You have enabled `completion.nvimCmp` but `plugins.cmp.enable` is `false`. + You need to enable `cmp` to use this setting. ''; } ]; diff --git a/tests/test-sources/example-configurations/issues.nix b/tests/test-sources/example-configurations/issues.nix index 6d70f40c..b45ea3b0 100644 --- a/tests/test-sources/example-configurations/issues.nix +++ b/tests/test-sources/example-configurations/issues.nix @@ -100,37 +100,40 @@ enable = true; }; - nvim-cmp = { - formatting = { - format = '' - require("lspkind").cmp_format({ - mode="symbol", - maxwidth = 50, - ellipsis_char = "..." - }) - ''; - }; - - autoEnableSources = true; - snippet = { - expand.__raw = '' - function(args) - require("luasnip").lsp_expand(args.body) - end - ''; - }; + cmp = { enable = true; - sources = [ - {name = "nvim_lsp";} - { - name = "luasnip"; - option = { - show_autosnippets = true; - }; - } - {name = "path";} - {name = "buffer";} - ]; + autoEnableSources = true; + + settings = { + formatting = { + format = '' + require("lspkind").cmp_format({ + mode="symbol", + maxwidth = 50, + ellipsis_char = "..." + }) + ''; + }; + + snippet = { + expand = '' + function(args) + require("luasnip").lsp_expand(args.body) + end + ''; + }; + sources = [ + {name = "nvim_lsp";} + { + name = "luasnip"; + option = { + show_autosnippets = true; + }; + } + {name = "path";} + {name = "buffer";} + ]; + }; }; barbar.enable = true; }; diff --git a/tests/test-sources/plugins/completion/cmp-tabby.nix b/tests/test-sources/plugins/completion/cmp-tabby.nix index e2fb898b..2201c804 100644 --- a/tests/test-sources/plugins/completion/cmp-tabby.nix +++ b/tests/test-sources/plugins/completion/cmp-tabby.nix @@ -1,8 +1,8 @@ { empty = { - plugins.nvim-cmp = { + plugins.cmp = { enable = true; - sources = [ + settings.sources = [ {name = "cmp_tabby";} ]; }; @@ -10,9 +10,9 @@ defaults = { plugins = { - nvim-cmp = { + cmp = { enable = true; - sources = [ + settings.sources = [ {name = "cmp_tabby";} ]; }; diff --git a/tests/test-sources/plugins/completion/cmp.nix b/tests/test-sources/plugins/completion/cmp.nix new file mode 100644 index 00000000..7b7f505e --- /dev/null +++ b/tests/test-sources/plugins/completion/cmp.nix @@ -0,0 +1,272 @@ +{ + empty = { + plugins.cmp.enable = true; + }; + + snippet-engine = { + plugins.cmp = { + enable = true; + settings.snippet.expand = "luasnip"; + }; + }; + + # Issue #536 + mappings = { + plugins.cmp = { + enable = true; + settings.mapping = { + "" = "cmp.mapping.confirm({ select = false })"; + "" = '' + cmp.mapping( + function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif luasnip.expandable() then + luasnip.expand() + elseif luasnip.expand_or_jumpable() then + luasnip.expand_or_jump() + elseif check_backspace() then + fallback() + else + fallback() + end + end, + { "i", "s" } + ) + ''; + }; + }; + }; + + # All the upstream default options of nvim-cmp + defaults = { + plugins.cmp = { + enable = true; + + settings = { + performance = { + debounce = 60; + throttle = 30; + fetchingTimeout = 500; + asyncBudget = 1; + maxViewEntries = 200; + }; + preselect = "Item"; + snippet = { + expand = '' + function(_) + error('snippet engine is not configured.') + end + ''; + }; + completion = { + keywordLength = 1; + keywordPattern = ''[[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)]]''; + autocomplete = ["TextChanged"]; + completeopt = "menu,menuone,noselect"; + }; + confirmation = { + getCommitCharacters = '' + function(commit_characters) + return commit_characters + end + ''; + }; + formatting = { + expandableIndicator = true; + fields = ["abbr" "kind" "menu"]; + format = '' + function(_, vim_item) + return vim_item + end + ''; + }; + matching = { + disallowFuzzyMatching = false; + disallowFullfuzzyMatching = false; + disallowPartialFuzzyMatching = true; + disallowPartialMatching = false; + disallowPrefixUnmatching = false; + }; + sorting = { + priorityWeight = 2; + comparators = [ + "offset" + "exact" + "score" + "recently_used" + "locality" + "kind" + "length" + "order" + ]; + }; + sources = []; + experimental = { + ghost_text = false; + }; + view = { + entries = { + name = "custom"; + selection_order = "top_down"; + }; + docs = { + autoOpen = true; + }; + }; + window = { + completion = { + border = ["" "" "" "" "" "" "" ""]; + winhighlight = "Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None"; + scrolloff = 0; + colOffset = 0; + sidePadding = 1; + scrollbar = true; + }; + documentation = { + maxHeight = "math.floor(40 * (40 / vim.o.lines))"; + maxWidth = "math.floor((40 * 2) * (vim.o.columns / (40 * 2 * 16 / 9)))"; + border.__raw = "cmp.config.window.bordered()"; + winhighlight = "FloatBorder:NormalFloat"; + }; + }; + }; + }; + }; + + list-of-sources = { + plugins.cmp = { + enable = true; + + settings.sources = [ + {name = "path";} + {name = "nvim_lsp";} + {name = "luasnip";} + { + name = "buffer"; + option.get_bufnrs.__raw = "vim.api.nvim_list_bufs"; + } + {name = "neorg";} + ]; + }; + }; + + list-of-lists-of-sources = { + plugins.cmp = { + enable = true; + + # Here we are setting sources with raw lua. This cannot be parsed by Nixvim. + autoEnableSources = false; + + settings.sources.__raw = '' + cmp.config.sources({ + { + {name = "path"}, + {name = "nvim_lsp"} + }, + { + { + name = "buffer", + option = { + get_bufnrs = vim.api.nvim_list_bufs + } + } + }, + }) + ''; + }; + }; + + example = { + plugins.cmp = { + enable = true; + + settings = { + snippet.expand = "luasnip"; + window = { + completion.__raw = "cmp.config.window.bordered"; + documentation.__raw = "cmp.config.window.bordered"; + }; + + mapping = { + "" = "cmp.mapping.scroll_docs(-4)"; + "" = "cmp.mapping.scroll_docs(4)"; + "" = "cmp.mapping.complete()"; + "" = "cmp.mapping.abort()"; + "" = "cmp.mapping.confirm({ select = true })"; + }; + }; + }; + }; + + readme-example = { + plugins.cmp = { + enable = true; + + # Here we are setting sources with raw lua. This cannot be parsed by Nixvim. + autoEnableSources = false; + + settings = { + mapping.__raw = '' + cmp.mapping.preset.insert({ + [''] = cmp.mapping.scroll_docs(-4), + [''] = cmp.mapping.scroll_docs(4), + [''] = cmp.mapping.complete(), + [''] = cmp.mapping.abort(), + [''] = cmp.mapping.confirm({ select = true }), + }) + ''; + sources.__raw = '' + cmp.config.sources({ + { name = 'nvim_lsp' }, + { name = 'vsnip' }, + -- { name = 'luasnip' }, + -- { name = 'ultisnips' }, + -- { name = 'snippy' }, + }, { + { name = 'buffer' }, + }) + ''; + }; + + filetype.gitcommit.sources.__raw = '' + cmp.config.sources({ + { name = 'git' }, + }, { + { name = 'buffer' }, + }) + ''; + + cmdline = { + "/" = { + mapping.__raw = "cmp.mapping.preset.cmdline()"; + # Need to use a raw lua string as we only accept **flat** lists (not nested). + sources.__raw = '' + { + { name = 'buffer' } + } + ''; + }; + "?" = { + mapping.__raw = "cmp.mapping.preset.cmdline()"; + # Need to use a raw lua string as we only accept **flat** lists (not nested). + sources.__raw = '' + { + { name = 'buffer' } + } + ''; + }; + ":" = { + mapping.__raw = "cmp.mapping.preset.cmdline()"; + sources.__raw = '' + cmp.config.sources({ + { name = 'path' } + }, { + { name = 'cmdline' } + }) + ''; + }; + }; + }; + }; +} diff --git a/tests/test-sources/plugins/completion/codeium-nvim.nix b/tests/test-sources/plugins/completion/codeium-nvim.nix index e6990ee9..7894c5ce 100644 --- a/tests/test-sources/plugins/completion/codeium-nvim.nix +++ b/tests/test-sources/plugins/completion/codeium-nvim.nix @@ -4,10 +4,10 @@ }; enabled-by-cmp = { - plugins.nvim-cmp = { + plugins.cmp = { enable = true; - sources = [ + settings.sources = [ {name = "codeium";} ]; }; diff --git a/tests/test-sources/plugins/completion/copilot-lua.nix b/tests/test-sources/plugins/completion/copilot-lua.nix index 45b20d84..38ce10f4 100644 --- a/tests/test-sources/plugins/completion/copilot-lua.nix +++ b/tests/test-sources/plugins/completion/copilot-lua.nix @@ -17,9 +17,9 @@ fixPairs = true; }; - nvim-cmp = { + cmp = { enable = true; - sources = [ + settings.sources = [ {name = "copilot";} ]; }; diff --git a/tests/test-sources/plugins/completion/lspkind.nix b/tests/test-sources/plugins/completion/lspkind.nix index db7e7819..107aba22 100644 --- a/tests/test-sources/plugins/completion/lspkind.nix +++ b/tests/test-sources/plugins/completion/lspkind.nix @@ -10,7 +10,7 @@ enable = true; servers.clangd.enable = true; }; - nvim-cmp.enable = true; + cmp.enable = true; lspkind.enable = true; }; }; diff --git a/tests/test-sources/plugins/completion/nvim-cmp.nix b/tests/test-sources/plugins/completion/nvim-cmp.nix deleted file mode 100644 index 781513ad..00000000 --- a/tests/test-sources/plugins/completion/nvim-cmp.nix +++ /dev/null @@ -1,171 +0,0 @@ -{ - # Empty configuration - empty = { - plugins.nvim-cmp.enable = true; - }; - - snippetEngine = { - plugins.nvim-cmp = { - enable = true; - snippet.expand = "luasnip"; - }; - }; - - # Issue #536 - mappings = { - plugins.nvim-cmp = { - enable = true; - mapping = { - "" = "cmp.mapping.confirm({ select = false })"; - "" = { - modes = ["i" "s"]; - action = '' - function(fallback) - if cmp.visible() then - cmp.select_next_item() - elseif luasnip.expandable() then - luasnip.expand() - elseif luasnip.expand_or_jumpable() then - luasnip.expand_or_jump() - elseif check_backspace() then - fallback() - else - fallback() - end - end - ''; - }; - }; - }; - }; - - # All the upstream default options of nvim-cmp - defaults = { - plugins.nvim-cmp = { - enable = true; - - performance = { - debounce = 60; - throttle = 30; - fetchingTimeout = 500; - asyncBudget = 1; - maxViewEntries = 200; - }; - preselect = "Item"; - snippet = { - expand.__raw = '' - function(_) - error('snippet engine is not configured.') - end - ''; - }; - completion = { - keywordLength = 1; - keywordPattern = ''\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)''; - autocomplete = ["TextChanged"]; - completeopt = "menu,menuone,noselect"; - }; - confirmation = { - getCommitCharacters = '' - function(commit_characters) - return commit_characters - end - ''; - }; - formatting = { - expandableIndicator = true; - fields = ["abbr" "kind" "menu"]; - format = '' - function(_, vim_item) - return vim_item - end - ''; - }; - matching = { - disallowFuzzyMatching = false; - disallowFullfuzzyMatching = false; - disallowPartialFuzzyMatching = true; - disallowPartialMatching = false; - disallowPrefixUnmatching = false; - }; - sorting = { - priorityWeight = 2; - comparators = [ - "offset" - "exact" - "score" - "recently_used" - "locality" - "kind" - "length" - "order" - ]; - }; - sources = []; - experimental = { - ghost_text = false; - }; - view = { - entries = { - name = "custom"; - selection_order = "top_down"; - }; - docs = { - autoOpen = true; - }; - }; - window = { - completion = { - border = ["" "" "" "" "" "" "" ""]; - winhighlight = "Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None"; - scrolloff = 0; - colOffset = 0; - sidePadding = 1; - scrollbar = true; - }; - documentation = { - maxHeight = "math.floor(40 * (40 / vim.o.lines))"; - maxWidth = "math.floor((40 * 2) * (vim.o.columns / (40 * 2 * 16 / 9)))"; - border.__raw = "cmp.config.window.bordered()"; - winhighlight = "FloatBorder:NormalFloat"; - }; - }; - }; - }; - - list-of-sources = { - plugins.nvim-cmp = { - enable = true; - - sources = [ - {name = "path";} - {name = "nvim_lsp";} - {name = "luasnip";} - { - name = "buffer"; - option.get_bufnrs.__raw = "vim.api.nvim_list_bufs"; - } - {name = "neorg";} - ]; - }; - }; - - list-of-lists-of-sources = { - plugins.nvim-cmp = { - enable = true; - - sources = [ - [ - {name = "path";} - {name = "nvim_lsp";} - ] - [ - { - name = "buffer"; - option.get_bufnrs.__raw = "vim.api.nvim_list_bufs"; - } - ] - ]; - }; - }; -} diff --git a/tests/test-sources/plugins/utils/obsidian.nix b/tests/test-sources/plugins/utils/obsidian.nix index 3e905a5e..636eca66 100644 --- a/tests/test-sources/plugins/utils/obsidian.nix +++ b/tests/test-sources/plugins/utils/obsidian.nix @@ -6,7 +6,7 @@ example = { plugins = { - nvim-cmp.enable = true; + cmp.enable = true; obsidian = { enable = false;