From bfbe737aa3f191b551890c2a3d54104c71f27b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=A9tan=20Lepage?= <33058747+GaetanLepage@users.noreply.github.com> Date: Sun, 12 Mar 2023 18:52:02 +0100 Subject: [PATCH] plugins/nvim-cmp: refactor + tests (#237) --- plugins/completion/nvim-cmp/default.nix | 761 +++++++++++++++--------- tests/plugins/nvim-cmp.nix | 106 ++++ 2 files changed, 580 insertions(+), 287 deletions(-) create mode 100644 tests/plugins/nvim-cmp.nix diff --git a/plugins/completion/nvim-cmp/default.nix b/plugins/completion/nvim-cmp/default.nix index 93f74add..22b2defe 100644 --- a/plugins/completion/nvim-cmp/default.nix +++ b/plugins/completion/nvim-cmp/default.nix @@ -7,89 +7,53 @@ with lib; let cfg = config.plugins.nvim-cmp; helpers = import ../../helpers.nix {inherit lib;}; - mkNullOrOption = helpers.mkNullOrOption; cmpLib = import ./cmp-helpers.nix args; - # functionName should be a string - # parameters should be a list of strings - wrapWithFunction = functionName: parameters: let - parameterString = strings.concatStringsSep "," parameters; - in ''${functionName}(${parameterString})''; in { options.plugins.nvim-cmp = { enable = mkEnableOption "nvim-cmp"; package = helpers.mkPackageOption "nvim-cmp" pkgs.vimPlugins.nvim-cmp; - performance = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - debounce = mkOption { - type = types.nullOr types.int; - default = null; - }; - throttle = mkOption { - type = types.nullOr types.int; - default = null; - }; - }; - })); - }; + performance = helpers.mkCompositeOption "Performance options" { + debounce = helpers.defaultNullOpts.mkInt 60 '' + Sets debounce time + This is the interval used to group up completions from different sources + for filtering and displaying. + ''; - preselect = mkOption { - type = types.nullOr (types.enum ["Item" "None"]); - default = null; - example = ''"Item"''; - }; + throttle = helpers.defaultNullOpts.mkInt 30 '' + Sets throttle time. + This is used to delay filtering and displaying completions. + ''; - snippet = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - expand = mkOption { - type = types.nullOr types.str; - example = '' - 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 - ''; - }; - }; - })); - }; - - mappingPresets = mkOption { - default = []; - type = types.listOf (types.enum [ - "insert" - "cmdline" - # Not sure if there are more or if this should just be str - ]); - description = "Mapping presets to use; cmp.mapping.preset.\${mappingPreset} will be called with the configured mappings"; - example = '' - [ "insert" "cmdline" ] + fetchingTimeout = helpers.defaultNullOpts.mkInt 500 '' + Sets the timeout of candidate fetching process. + The nvim-cmp will wait to display the most prioritized source. ''; }; + preselect = helpers.defaultNullOpts.mkEnumFirstDefault ["Item" "None"] '' + - "Item": nvim-cmp will preselect the item that the source specified. + - "None": nvim-cmp will not preselect any items. + ''; + mapping = mkOption { default = null; - type = types.nullOr (types.attrsOf (types.either types.str (types.submodule ({...}: { - options = { - action = mkOption { - type = types.nonEmptyStr; - description = "The function the mapping should call"; - example = ''"cmp.mapping.scroll_docs(-4)"''; + type = with types; + nullOr (attrsOf (either str (types.submodule ({...}: { + options = { + action = mkOption { + type = types.nonEmptyStr; + description = "The function the mapping should call"; + example = ''"cmp.mapping.scroll_docs(-4)"''; + }; + modes = mkOption { + default = null; + type = types.nullOr (types.listOf types.str); + example = ''[ "i" "s" ]''; + }; }; - modes = mkOption { - default = null; - type = types.nullOr (types.listOf types.str); - example = ''[ "i" "s" ]''; - }; - }; - })))); + })))); example = '' { "" = "cmp.mapping.confirm({ select = true })"; @@ -115,103 +79,161 @@ in { ''; }; - completion = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - keyword_length = mkOption { - default = null; - type = types.nullOr types.int; - }; - - keyword_pattern = mkOption { - default = null; - type = types.nullOr types.str; - }; - - autocomplete = mkOption { - default = null; - type = types.nullOr types.str; - description = "Lua code for the event."; - example = ''"false"''; - }; - - completeopt = mkOption { - default = null; - type = types.nullOr types.str; - }; - }; - })); + mappingPresets = mkOption { + default = []; + type = types.listOf (types.enum [ + "insert" + "cmdline" + ]); + description = '' + Mapping presets to use; cmp.mapping.preset. + \$\{mappingPreset} will be called with the configured mappings. + ''; + example = ''[ "insert" "cmdline" ]''; }; - confirmation = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - get_commit_characters = mkOption { - default = null; - type = types.nullOr types.str; - description = "Direct lua code as a string"; - }; - }; - })); + snippet = helpers.mkCompositeOption "Snippet options" { + expand = + helpers.defaultNullOpts.mkStr + '' + function(_) + error('snippet engine is not configured.') + end + '' + '' + The snippet expansion function. That's how nvim-cmp interacts with a + particular snippet engine. + + Example: + ``` + 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 + ``` + ''; }; - formatting = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - fields = mkOption { - default = null; - type = types.nullOr (types.listOf types.str); - example = ''[ "kind" "abbr" "menu" ]''; - }; - format = mkOption { - default = null; - type = types.nullOr types.str; - description = "A lua function as a string"; - }; - }; - })); + completion = helpers.mkCompositeOption "Completion options" { + keywordLength = helpers.defaultNullOpts.mkInt 1 '' + The number of characters needed to trigger auto-completion. + ''; + + keywordPattern = + helpers.defaultNullOpts.mkStr + ''\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%(-\w*\)*\)'' + '' + The default keyword pattern. + + Note: the provided pattern will be embedded as such: `[[PATTERN]]`. + ''; + + autocomplete = + helpers.defaultNullOpts.mkNullable + ( + with types; + either + (listOf str) + (types.enum [false]) + ) + ''[ "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. + ''; }; - matching = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - disallow_fuzzy_matching = mkOption { - default = null; - type = types.nullOr types.bool; - }; - disallow_partial_matching = mkOption { - default = null; - type = types.nullOr types.bool; - }; - disallow_prefix_unmatching = mkOption { - default = null; - type = types.nullOr types.bool; - }; - }; - })); + confirmation = helpers.mkCompositeOption "Confirmation options" { + getCommitCharacters = + helpers.defaultNullOpts.mkStr + '' + 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. + ''; }; - sorting = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - priority_weight = mkOption { - default = null; - type = types.nullOr types.int; - }; - comparators = mkOption { - default = null; - type = types.nullOr types.str; - }; - }; - })); + formatting = helpers.mkCompositeOption "Formatting options" { + expandableIndicator = helpers.defaultNullOpts.mkBool true '' + Boolean to show the `~` expandable indicator in cmp's floating window. + ''; + + fields = + helpers.defaultNullOpts.mkNullable + (types.listOf types.str) + ''[ "kind" "abbr" "menu" ]'' + "An array of completion fields to specify their order."; + + format = + helpers.defaultNullOpts.mkStr + '' + 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`. + ''; }; - auto_enable_sources = mkOption { + matching = helpers.mkCompositeOption "Matching options" { + 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 = helpers.mkCompositeOption "Sorting options" { + 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 = + helpers.defaultNullOpts.mkNullable (types.listOf types.str) + ''[ "offset" "exact" "score" "recently_used" "locality" "kind" "length" "order" ]'' + '' + 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)[]` + ''; + }; + + autoEnableSources = mkOption { type = types.bool; default = true; description = '' @@ -228,51 +250,97 @@ in { example = ''"buffer"''; }; - option = mkOption { - default = null; - type = with types; nullOr (attrsOf anything); - description = "If direct lua code is needed use helpers.mkRaw"; - }; + option = helpers.mkNullOrOption (types.attrs) '' + Any specific options defined by the source itself. - keyword_length = mkOption { - default = null; - type = types.nullOr types.int; - }; + If direct lua code is needed use `helpers.mkRaw`. + ''; - keyword_pattern = mkOption { - default = null; - type = types.nullOr types.int; - }; + keywordLength = helpers.mkNullOrOption types.int '' + The source-specific keyword length to trigger auto completion. + ''; - trigger_characters = mkOption { - default = null; - type = with types; nullOr (listOf str); - }; + keywordPattern = helpers.mkNullOrOption types.str '' + The source-specific keyword pattern. - priority = mkOption { - default = null; - type = types.nullOr types.int; - }; + Note: the provided pattern will be embedded as such: `[[PATTERN]]`. + ''; - max_item_count = mkOption { - default = null; - type = types.nullOr types.int; - }; + triggerCharacters = helpers.mkNullOrOption (types.listOf types.str) '' + A source-specific keyword pattern. + ''; - group_index = mkOption { - default = null; - type = types.nullOr types.int; - }; + priority = helpers.mkNullOrOption types.int "The source-specific priority value."; + + maxItemCount = helpers.mkNullOrOption types.int "The source-specific item count."; + + groupIndex = helpers.mkNullOrOption types.int '' + 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: + + ``` + cmp.setup { + sources = { + { name = 'nvim_lsp', group_index = 1 }, + { name = 'buffer', group_index = 2 }, + } + } + ``` + + You can also achieve this by using the built-in configuration helper like this: + ``` + cmp.setup { + sources = cmp.config.sources({ + { name = 'nvim_lsp' }, + }, { + { name = 'buffer' }, + }) + } + ``` + ''; + + 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 (either (listOf source_config) (listOf (listOf source_config))); + type = with types; + nullOr ( + either + (listOf source_config) + (listOf (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`. + Or it can be a list of lists, which will use the cmp built-in helper function + `cmp.config.sources`. + + Default: [ ] ''; example = '' [ @@ -284,55 +352,96 @@ in { ''; }; - view = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - entries = mkOption { - default = null; - type = with types; nullOr (either str attrs); - }; - }; - })); + view = helpers.mkCompositeOption "View options" { + 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. + ''; }; window = let # Reusable options - border = with types; mkNullOrOption (either str (listOf str)) null; - winhighlight = mkNullOrOption types.str null; - zindex = mkNullOrOption types.int null; - in - mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - completion = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - inherit border winhighlight zindex; - col_offset = mkNullOrOption types.int "Offsets the completion window relative to the cursor"; - side_padding = mkNullOrOption types.int "The amount of padding to add on the completion window's sides"; - }; - })); - }; + mkBorderOption = default: + helpers.defaultNullOpts.mkNullable + (with types; either str (listOf str)) + default + '' + Border characters used for the completion popup menu when |experimental.native_menu| is disabled. + See |nvim_open_win|. + ''; - documentation = mkOption { - default = null; - type = types.nullOr (types.submodule ({...}: { - options = { - inherit border winhighlight zindex; - max_width = mkNullOrOption types.int "Window's max width"; - max_height = mkNullOrOption types.int "Window's max height"; - }; - })); - }; - }; - })); + 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 + helpers.mkCompositeOption "Windows options" { + completion = helpers.mkCompositeOption "Completion window options" { + border = mkBorderOption ''[ "" "" "" "" "" "" "" "" ]''; + + 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 = helpers.mkCompositeOption "Documentation window options" { + border = mkBorderOption ''[ "" "" "" " " "" "" "" " " ]''; + + winhighlight = mkWinhighlightOption "FloatBorder:NormalFloat"; + + inherit zindex; + + maxWidth = + helpers.defaultNullOpts.mkNullable + (types.either types.int types.str) + "math.floor((40 * 2) * (vim.o.columns / (40 * 2 * 16 / 9)))" + "The documentation window's max width."; + + maxHeight = + helpers.defaultNullOpts.mkNullable + (types.either types.int types.str) + "math.floor(40 * (40 / vim.o.lines))" + "The documentation window's max height."; + }; }; - # This can be kept as types.attrs since experimental features are often removed or completely changed after a while - experimental = mkNullOrOption types.attrs "Experimental features"; + # 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 = let @@ -340,9 +449,8 @@ in { enabled = cfg.enable; performance = cfg.performance; preselect = - if (isNull cfg.preselect) - then null - else helpers.mkRaw "cmp.PreselectMode.${cfg.preselect}"; + helpers.ifNonNull' cfg.preselect + (helpers.mkRaw "cmp.PreselectMode.${cfg.preselect}"); # Not very readable sorry # If null then null @@ -350,79 +458,158 @@ in { # 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 = - if (isNull cfg.mapping) - then null - else - mapAttrs + helpers.ifNonNull' cfg.mapping + (mapAttrs (bind: mapping: helpers.mkRaw ( if isString mapping then mapping - else "cmp.mapping(${mapping.action}${optionalString (mapping.modes != null && length mapping.modes >= 1) ("," + (helpers.toLuaObject mapping.modes))})" + else let + modes = mapping.modes; + modesString = + optionalString (!isNull modes && ((length modes) >= 1)) + ("," + (helpers.toLuaObject mapping.modes)); + in "cmp.mapping(${mapping.action}${modesString})" )) - cfg.mapping; + cfg.mapping); + luaMappings = helpers.toLuaObject mappings; - wrapped = lists.fold (presetName: prevString: ''cmp.mapping.preset.${presetName}(${prevString})'') luaMappings cfg.mappingPresets; + + wrapped = + lists.fold + ( + presetName: prevString: ''cmp.mapping.preset.${presetName}(${prevString})'' + ) + luaMappings + cfg.mappingPresets; in helpers.mkRaw wrapped; - snippet = { + snippet = helpers.ifNonNull' cfg.snippet { expand = - if (isNull cfg.snippet || isNull cfg.snippet.expand) - then null - else helpers.mkRaw cfg.snippet.expand; + helpers.ifNonNull' + cfg.snippet.expand + (helpers.mkRaw cfg.snippet.expand); }; - completion = - if (isNull cfg.completion) - then null - else { - keyword_length = cfg.completion.keyword_length; - keyword_pattern = cfg.completion.keyword_pattern; - autocomplete = - if (isNull cfg.completion.autocomplete) - then null - else helpers.mkRaw cfg.completion.autocomplete; - completeopt = cfg.completion.completeopt; - }; + completion = helpers.ifNonNull' cfg.completion { + keyword_length = cfg.completion.keywordLength; + keyword_pattern = let + keywordPattern = cfg.completion.keywordPattern; + in + helpers.ifNonNull' keywordPattern + (helpers.mkRaw "[[${keywordPattern}]]"); + autocomplete = let + autocomplete = cfg.completion.autocomplete; + in + if isList autocomplete + then + map + (triggerEvent: + helpers.mkRaw "require('cmp.types').cmp.TriggerEvent.${triggerEvent}") + autocomplete + # either null or false + else autocomplete; + completeopt = cfg.completion.completeopt; + }; - confirmation = - if (isNull cfg.confirmation) - then null - else { - get_commit_characters = - if (isString cfg.confirmation.get_commit_characters) - then helpers.mkRaw cfg.confirmation.get_commit_characters - else cfg.confirmation.get_commit_characters; - }; + confirmation = helpers.ifNonNull' cfg.confirmation { + get_commit_characters = + if (isString cfg.confirmation.getCommitCharacters) + then helpers.mkRaw cfg.confirmation.getCommitCharacters + else cfg.confirmation.getCommitCharacters; + }; - formatting = - if (isNull cfg.formatting) - then null - else { - fields = cfg.formatting.fields; - format = - if (isNull cfg.formatting.format) - then null - else helpers.mkRaw cfg.formatting.format; - }; + formatting = helpers.ifNonNull' cfg.formatting { + expandable_indicator = cfg.formatting.expandableIndicator; + fields = cfg.formatting.fields; + format = + helpers.ifNonNull' cfg.formatting.format + (helpers.mkRaw cfg.formatting.format); + }; - matching = cfg.matching; + matching = helpers.ifNonNull' cfg.matching { + disallow_fuzzy_matching = cfg.matching.disallowFuzzyMatching; + disallow_fullfuzzy_matching = cfg.matching.disallowFullfuzzyMatching; + disallow_partial_fuzzy_matching = cfg.matching.disallowPartialFuzzyMatching; + disallow_partial_matching = cfg.matching.disallowPartialMatching; + disallow_prefix_unmatching = cfg.matching.disallowPrefixUnmatching; + }; - sorting = - if (isNull cfg.sorting) - then null - else { - priority_weight = cfg.sorting.priority_weight; - comparators = - if (isNull cfg.sorting.comparators) - then null - else helpers.mkRaw cfg.sorting.comparators; - }; + sorting = helpers.ifNonNull' cfg.sorting { + priority_weight = cfg.sorting.priorityWeight; + comparators = let + comparators = cfg.sorting.comparators; + in + helpers.ifNonNull' comparators + ( + map + ( + funcName: + helpers.mkRaw "require('cmp.config.compare').${funcName}" + ) + comparators + ); + }; + + sources = helpers.ifNonNull' cfg.sources ( + map + (source: { + inherit (source) name option; + keyword_length = source.keywordLength; + + keywordPattern = + helpers.ifNonNull' source.keywordPattern + (helpers.mkRaw "[[${source.keywordPattern}]]"); + + trigger_characters = source.triggerCharacters; + + inherit (source) priority; + + max_item_count = source.maxItemCount; + + group_index = source.groupIndex; + + entry_filter = source.entryFilter; + }) + cfg.sources + ); - sources = cfg.sources; view = cfg.view; - window = cfg.window; + window = helpers.ifNonNull' cfg.window { + completion = helpers.ifNonNull' cfg.window.completion { + inherit + (cfg.window.completion) + border + winhighlight + zindex + scrolloff + scrollbar + ; + col_offset = cfg.window.completion.colOffset; + side_padding = cfg.window.completion.sidePadding; + }; + documentation = helpers.ifNonNull' cfg.window.completion { + inherit + (cfg.window.completion) + border + winhighlight + zindex + ; + max_width = let + maxWidth = cfg.window.documentation.maxWidth; + in + if isInt maxWidth + then maxWidth + else helpers.ifNonNull' maxWidth (helpers.mkRaw maxWidth); + max_height = let + maxHeight = cfg.window.documentation.maxHeight; + in + if isInt maxHeight + then maxHeight + else helpers.ifNonNull' maxHeight (helpers.mkRaw maxHeight); + }; + }; experimental = cfg.experimental; }; in @@ -454,7 +641,7 @@ in { known_source_names); in mkMerge [ - (mkIf cfg.auto_enable_sources attrs_enabled) + (mkIf cfg.autoEnableSources attrs_enabled) (mkIf (elem "nvim_lsp" found_sources) { lsp.capabilities = '' diff --git a/tests/plugins/nvim-cmp.nix b/tests/plugins/nvim-cmp.nix new file mode 100644 index 00000000..50474830 --- /dev/null +++ b/tests/plugins/nvim-cmp.nix @@ -0,0 +1,106 @@ +{ + # Empty configuration + empty = { + plugins.nvim-cmp.enable = true; + }; + + # All the upstream default options of nvim-cmp + defaults = { + plugins.nvim-cmp = { + enable = true; + + performance = { + debounce = 60; + throttle = 30; + fetchingTimeout = 500; + }; + + 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"; + }; + }; + + 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 = ["" "" "" " " "" "" "" " "]; + winhighlight = "FloatBorder:NormalFloat"; + }; + }; + }; + }; +}