plugins/completion: move to by-name

- Move nvim-cmp to plugins/cmp
- Move other completion plugins to plugins/by-name
This commit is contained in:
Matt Sturgeon 2024-09-05 02:36:41 +01:00
parent 3211a63306
commit ad85cd760e
No known key found for this signature in database
GPG key ID: 4F91844CED1A8299
36 changed files with 2 additions and 9 deletions

View file

@ -0,0 +1,90 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.cmp;
extractSources = { sources, ... }: if isList sources then sources else [ ];
# Collect the names of the sources provided by the user
# ["foo" "bar"]
enabledSources =
pipe
[
# cfg.setup.sources
(extractSources cfg.settings)
# cfg.filetype.<name>.sources
(mapAttrsToList (_: extractSources) cfg.filetype)
# cfg.cmdline.<name>.sources
(mapAttrsToList (_: extractSources) cfg.cmdline)
]
[
flatten
(map (getAttr "name"))
unique
];
in
{
options = {
# Note: this option must be outside of `plugins` to avoid infinite recursion
cmpSourcePlugins = mkOption {
type = with types; attrsOf str;
default = { };
description = ''
Internal option used to associate nvim-cmp source names with nixvim plugin module names.
Maps `<source-name> = <plugin-name>` where _plugin-name_ is the module name: `plugins.<plugin-name>.enable`.
'';
example = {
foo = "cmp-foo";
bar = "cmp-bar";
};
internal = true;
visible = false;
};
plugins.cmp.autoEnableSources = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Scans the sources array and enable the corresponding plugins if they are known to nixvim.
'';
};
};
config = mkIf (cfg.enable && cfg.autoEnableSources) (mkMerge [
{
warnings =
# TODO: expand this warning to ft & cmd sources lists and `showDefs` the offending definitions
optional (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`;
'';
# If the user has enabled the `foo` and `bar` sources, then `plugins` will look like:
# {
# cmp-foo.enable = true;
# cmp-bar.enable = true;
# }
plugins = mapAttrs' (source: name: {
inherit name;
value.enable = mkIf (elem source enabledSources) true;
}) config.cmpSourcePlugins;
}
{
plugins.lsp.capabilities = mkIf (elem "nvim_lsp" enabledSources) ''
capabilities = vim.tbl_deep_extend("force", capabilities, require('cmp_nvim_lsp').default_capabilities())
'';
}
]);
}

90
plugins/cmp/default.nix Normal file
View file

@ -0,0 +1,90 @@
{
lib,
helpers,
...
}:
let
cmpOptions = import ./options { inherit lib helpers; };
in
with lib;
helpers.neovim-plugin.mkNeovimPlugin {
name = "cmp";
originalName = "nvim-cmp";
package = "nvim-cmp";
maintainers = [ maintainers.GaetanLepage ];
description = ''
### Completion Source Installation
If `plugins.cmp.autoEnableSources` is `true` Nixivm will automatically enable the corresponding source
plugins. This is the default behavior, but will work only when this option is set to a list.
If you use a raw lua string or set `plugins.cmp.autoEnableSources` to `false`, you will need to explicitly enable the relevant source plugins in
your nixvim configuration.
#### With auto-enabled sources
```nix
plugins.cmp = {
autoEnableSources = true;
settings.sources = [
{ name = "nvim_lsp"; }
{ name = "path"; }
{ name = "buffer"; }
];
};
```
#### Without auto-enabled sources
```nix
plugins = {
cmp = {
autoEnableSources = false;
settings.sources = [
{ name = "nvim_lsp"; }
{ name = "path"; }
{ name = "buffer"; }
];
};
cmp-nvim-lsp.enable = true;
cmp-path.enable = true;
cmp-buffer.enable = true;
};
```
'';
imports = [
# Introduced on 2024 February 21
# TODO: remove ~June 2024
./deprecations.nix
./auto-enable.nix
./sources
];
deprecateExtraOptions = true;
inherit (cmpOptions) settingsOptions settingsExample;
extraOptions = {
inherit (cmpOptions) filetype cmdline;
};
callSetup = false;
extraConfig = cfg: {
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
));
};
}

View file

@ -0,0 +1,360 @@
{ 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 ++ [
(mkRenamedOptionModule (oldPluginBasePath ++ [ "enable" ]) (newPluginBasePath ++ [ "enable" ]))
(mkRenamedOptionModule (oldPluginBasePath ++ [ "autoEnableSources" ]) (
newPluginBasePath ++ [ "autoEnableSources" ]
))
(mkRemovedOptionModule (oldPluginBasePath ++ [ "preselect" ]) ''
Use `plugins.cmp.settings.preselect` option. But watch out, you now have to explicitly write `cmp.PreselectMode.<mode>`.
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.
'')
];
}

View file

@ -0,0 +1,74 @@
{ lib, helpers }:
with lib;
rec {
settingsOptions = import ./settings-options.nix { inherit lib helpers; };
settingsExample = {
snippet.expand = "function(args) require('luasnip').lsp_expand(args.body) end";
mapping.__raw = ''
cmp.mapping.preset.insert({
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = 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 provided to the `require('cmp').setup.filetype` function.";
example = {
python = {
sources = [ { name = "nvim_lsp"; } ];
};
};
};
cmdline = mkOption {
type = attrsOfOptions;
default = { };
description = "Options provided to the `require('cmp').setup.cmdline` function.";
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"
"!"
];
};
}
];
};
};
};
}

View file

@ -0,0 +1,327 @@
{ 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);
description = ''
cmp mappings declaration.
See `:h cmp-mapping` for more information.
'';
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 = {
"<C-d>" = "cmp.mapping.scroll_docs(-4)";
"<C-f>" = "cmp.mapping.scroll_docs(4)";
"<C-Space>" = "cmp.mapping.complete()";
"<C-e>" = "cmp.mapping.close()";
"<Tab>" = "cmp.mapping(cmp.mapping.select_next_item(), {'i', 's'})";
"<S-Tab>" = "cmp.mapping(cmp.mapping.select_prev_item(), {'i', 's'})";
"<CR>" = "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 (genList (_: "") 8) "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.mkInt 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 (genList (
_: ""
) 8) "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.
'';
}

View file

@ -0,0 +1,102 @@
{ 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.
'';
example = [
{ name = "nvim_lsp"; }
{ name = "luasnip"; }
{ name = "path"; }
{ name = "buffer"; }
];
}

View file

@ -0,0 +1,32 @@
{
lib,
helpers,
pkgs,
...
}:
{
pluginName,
sourceName,
package ? lib.mkPackageOption pkgs [
"vimPlugins"
pluginName
] { },
maintainers ? [ lib.maintainers.GaetanLepage ],
imports ? [ ],
...
}@args:
helpers.vim-plugin.mkVimPlugin (
builtins.removeAttrs args [
"pluginName"
"sourceName"
]
// {
inherit package maintainers;
name = pluginName;
imports = imports ++ [
# Register the source -> plugin name association
{ cmpSourcePlugins.${sourceName} = pluginName; }
];
}
)

View file

@ -0,0 +1,95 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.cmp-ai;
in
{
meta.maintainers = [ maintainers.GaetanLepage ];
options.plugins.cmp-ai = {
settings = helpers.mkSettingsOption {
description = "Options provided to the `require('cmp_ai.config'):setup` function.";
options = {
max_lines = helpers.defaultNullOpts.mkUnsignedInt 50 ''
How many lines of buffer context to use.
'';
run_on_every_keystroke = helpers.defaultNullOpts.mkBool true ''
Generate new completion items on every keystroke.
'';
provider = helpers.defaultNullOpts.mkStr "HF" ''
Which AI provider to use.
Check the [README](https://github.com/tzachar/cmp-ai/blob/main/README.md) to learn about
available options.
'';
provider_options = helpers.defaultNullOpts.mkAttrsOf types.anything { } ''
Options to forward to the provider.
'';
notify = helpers.defaultNullOpts.mkBool true ''
As some completion sources can be quit slow, setting this to `true` will trigger a
notification when a completion starts and ends using `vim.notify`.
'';
notify_callback = helpers.defaultNullOpts.mkLuaFn' {
description = ''
The default notify function uses `vim.notify`, but an override can be configured.
'';
pluginDefault = ''
function(msg)
vim.notify(msg)
end
'';
example = ''
function(msg)
require('notify').notify(msg, vim.log.levels.INFO, {
title = 'OpenAI',
render = 'compact',
})
end
'';
};
ignored_file_types = helpers.defaultNullOpts.mkAttrsOf' {
type = types.bool;
description = "Which filetypes to ignore.";
pluginDefault = { };
example = {
lua = true;
html = true;
};
};
};
example = {
max_lines = 1000;
provider = "HF";
notify = true;
notify_callback = ''
function(msg)
vim.notify(msg)
end
'';
run_on_every_keystroke = true;
ignored_file_types = {
lua = true;
};
};
};
};
config = mkIf cfg.enable {
extraConfigLua = ''
require('cmp_ai.config'):setup(${helpers.toLuaObject cfg.settings})
'';
};
}

View file

@ -0,0 +1,22 @@
{
lib,
config,
pkgs,
helpers,
...
}:
with lib;
let
cfg = config.plugins.cmp-fish;
in
{
meta.maintainers = [ maintainers.GaetanLepage ];
options.plugins.cmp-fish = {
fishPackage = lib.mkPackageOption pkgs "fish" {
nullable = true;
};
};
config = mkIf cfg.enable { extraPackages = [ cfg.fishPackage ]; };
}

View file

@ -0,0 +1,298 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.cmp-git;
mkAction =
action: target:
helpers.defaultNullOpts.mkLuaFn "require('cmp_git.${action}').git.${target}" ''
Function used to ${action} the ${replaceStrings [ "_" ] [ " " ] target}.
'';
in
{
options.plugins.cmp-git.settings = helpers.mkSettingsOption {
description = "Options provided to the `require('cmp_git').setup` function.";
options = {
filetypes = helpers.defaultNullOpts.mkListOf types.str [
"gitcommit"
"octo"
] "Filetypes for which to trigger.";
remotes = helpers.defaultNullOpts.mkListOf types.str [
"upstream"
"origin"
] "List of git remotes.";
enableRemoteUrlRewrites = helpers.defaultNullOpts.mkBool false ''
Whether to enable remote URL rewrites.
'';
git = {
commits = {
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of git commits to fetch.
'';
sort_by = mkAction "sort" "commits";
format = mkAction "format" "commits";
};
};
github = {
hosts = helpers.defaultNullOpts.mkListOf types.str [ ] ''
List of private instances of github.
'';
issues = {
fields = helpers.defaultNullOpts.mkListOf types.str [
"title"
"number"
"body"
"updatedAt"
"state"
] "The fields used for issues.";
filter = helpers.defaultNullOpts.mkStr "all" ''
The filter to use when fetching issues.
'';
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of issues to fetch.
'';
state = helpers.defaultNullOpts.mkStr "open" ''
Which issues to fetch (`"open"`, `"closed"` or `"all"`).
'';
sort_by = mkAction "sort" "issues";
format = mkAction "format" "issues";
};
mentions = {
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of mentions to fetch.
'';
sort_by = mkAction "sort" "mentions";
format = mkAction "format" "mentions";
};
pull_requests = {
fields = helpers.defaultNullOpts.mkListOf types.str [
"title"
"number"
"body"
"updatedAt"
"state"
] "The fields used for pull requests.";
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of pull requests to fetch.
'';
state = helpers.defaultNullOpts.mkStr "open" ''
Which issues to fetch (`"open"`, `"closed"`, `"merged"` or `"all"`).
'';
sort_by = mkAction "sort" "pull_requests";
format = mkAction "format" "pull_requests";
};
};
gitlab = {
hosts = helpers.defaultNullOpts.mkListOf types.str [ ] ''
List of private instances of gitlab.
'';
issues = {
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of issues to fetch.
'';
state = helpers.defaultNullOpts.mkStr "open" ''
Which issues to fetch (`"open"`, `"closed"` or `"all"`).
'';
sort_by = mkAction "sort" "issues";
format = mkAction "format" "issues";
};
mentions = {
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of mentions to fetch.
'';
sort_by = mkAction "sort" "mentions";
format = mkAction "format" "mentions";
};
merge_requests = {
limit = helpers.defaultNullOpts.mkUnsignedInt 100 ''
Max number of merge requests to fetch.
'';
state = helpers.defaultNullOpts.mkStr "open" ''
Which issues to fetch (`"open"`, `"closed"`, `"locked"` or `"merged"`).
'';
sort_by = mkAction "sort" "merge_requests";
format = mkAction "format" "merge_requests";
};
};
trigger_actions =
helpers.defaultNullOpts.mkListOf
(types.submodule {
options = {
debug_name = helpers.mkNullOrStr "Debug name.";
trigger_character = mkOption {
type = types.str;
example = ":";
description = ''
The trigger character.
Has to be a single character
'';
};
action = mkOption {
type = helpers.nixvimTypes.strLuaFn;
apply = helpers.mkRaw;
description = ''
The parameters to the action function are the different sources (currently `git`,
`gitlab` and `github`), the completion callback, the trigger character, the
parameters passed to complete from nvim-cmp, and the current git info.
'';
example = ''
function(sources, trigger_char, callback, params, git_info)
return sources.git:get_commits(callback, params, trigger_char)
end
'';
};
};
})
[
{
debug_name = "git_commits";
trigger_character = ":";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.git:get_commits(callback, params, trigger_char)
end
'';
}
{
debug_name = "gitlab_issues";
trigger_character = "#";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.gitlab:get_issues(callback, git_info, trigger_char)
end
'';
}
{
debug_name = "gitlab_mentions";
trigger_character = "@";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.gitlab:get_mentions(callback, git_info, trigger_char)
end
'';
}
{
debug_name = "gitlab_mrs";
trigger_character = "!";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.gitlab:get_merge_requests(callback, git_info, trigger_char)
end
'';
}
{
debug_name = "github_issues_and_pr";
trigger_character = "#";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.github:get_issues_and_prs(callback, git_info, trigger_char)
end
'';
}
{
debug_name = "github_mentions";
trigger_character = "@";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.github:get_mentions(callback, git_info, trigger_char)
end
'';
}
]
''
If you want specific behaviour for a trigger or new behaviour for a trigger, you need to
add an entry in the `trigger_actions` list of the config.
The two necessary fields are the `trigger_character` and the `action`.
'';
};
example = {
remotes = [
"upstream"
"origin"
"foo"
];
github.issues = {
filter = "all";
limit = 250;
state = "all";
format = ''
function(_, issue)
local icon = ({
open = '',
closed = '',
})[string.lower(issue.state)]
return string.format('%s #%d: %s', icon, issue.number, issue.title)
end
'';
sort_by = ''
function(issue)
local kind_rank = issue.pull_request and 1 or 0
local state_rank = issue.state == 'open' and 0 or 1
local age = os.difftime(os.time(), require('cmp_git.utils').parse_github_date(issue.updatedAt))
return string.format('%d%d%010d', kind_rank, state_rank, age)
end
'';
};
trigger_actions = [
{
debug_name = "git_commits";
trigger_character = ":";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.git:get_commits(callback, params, trigger_char)
end
'';
}
{
debug_name = "github_issues";
trigger_character = "#";
action = ''
function(sources, trigger_char, callback, params, git_info)
return sources.github:get_issues(callback, git_info, trigger_char)
end
'';
}
];
};
};
config = mkIf cfg.enable {
extraConfigLua = ''
require('cmp_git').setup(${helpers.toLuaObject cfg.settings})
'';
};
}

View file

@ -0,0 +1,69 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.cmp-tabby;
in
{
meta.maintainers = [ maintainers.GaetanLepage ];
# TODO: introduced 24-06-18, remove after 24.11
imports =
let
basePluginPath = [
"plugins"
"cmp-tabby"
];
settingsPath = basePluginPath ++ [ "settings" ];
in
[
(mkRenamedOptionModule (basePluginPath ++ [ "extraOptions" ]) settingsPath)
(mkRenamedOptionModule (basePluginPath ++ [ "host" ]) (settingsPath ++ [ "host" ]))
(mkRenamedOptionModule (basePluginPath ++ [ "maxLines" ]) (settingsPath ++ [ "max_lines" ]))
(mkRenamedOptionModule (basePluginPath ++ [ "runOnEveryKeyStroke" ]) (
settingsPath ++ [ "run_on_every_keystroke" ]
))
(mkRenamedOptionModule (basePluginPath ++ [ "stop" ]) (settingsPath ++ [ "stop" ]))
];
options.plugins.cmp-tabby = {
settings = helpers.mkSettingsOption {
description = "Options provided to the `require('cmp_ai.config'):setup` function.";
options = {
host = helpers.defaultNullOpts.mkStr "http://localhost:5000" ''
The address of the tabby host server.
'';
max_lines = helpers.defaultNullOpts.mkUnsignedInt 100 ''
The max number of lines to complete.
'';
run_on_every_keystroke = helpers.defaultNullOpts.mkBool true ''
Whether to run the completion on every keystroke.
'';
stop = helpers.defaultNullOpts.mkListOf types.str [ "\n" ] ''
Stop character.
'';
};
example = {
host = "http://localhost:5000";
max_lines = 100;
run_on_every_keystroke = true;
stop = [ "\n" ];
};
};
};
config = mkIf cfg.enable {
extraConfigLua = ''
require('cmp_tabby.config'):setup(${helpers.toLuaObject cfg.settings})
'';
};
}

View file

@ -0,0 +1,19 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.cmp-tabnine;
in
{
options.plugins.cmp-tabnine = helpers.neovim-plugin.extraOptionsOptions;
config = mkIf cfg.enable {
extraConfigLua = ''
require('cmp_tabnine.config'):setup(${helpers.toLuaObject cfg.extraOptions})
'';
};
}

View file

@ -0,0 +1,65 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
copilot-lua-cfg = config.plugins.copilot-lua;
cfg = config.plugins.copilot-cmp;
in
{
options.plugins.copilot-cmp = helpers.neovim-plugin.extraOptionsOptions // {
event =
helpers.defaultNullOpts.mkListOf types.str
[
"InsertEnter"
"LspAttach"
]
''
Configures when the source is registered.
Unless you have a unique problem for your particular configuration you probably don't want
to touch this.
'';
fixPairs = helpers.defaultNullOpts.mkBool true ''
Suppose you have the following code: `print('h')`.
Copilot might try to account for the `'` and `)` and complete it with this: `print('hello`.
This is not good behavior for consistency reasons and will just end up deleting the two ending
characters.
This option fixes that.
Don't turn this off unless you are having problems with pairs and believe this might be
causing them.
'';
};
config = mkIf cfg.enable {
warnings =
optional ((!isBool copilot-lua-cfg.suggestion.enabled) || copilot-lua-cfg.suggestion.enabled) ''
It is recommended to disable copilot's `suggestion` module, as it can interfere with
completions properly appearing in copilot-cmp.
''
++ optional ((!isBool copilot-lua-cfg.panel.enabled) || copilot-lua-cfg.panel.enabled) ''
It is recommended to disable copilot's `panel` module, as it can interfere with completions
properly appearing in copilot-cmp.
'';
plugins.copilot-lua.enable = true;
extraConfigLua =
let
setupOptions =
with cfg;
{
inherit event;
fix_pairs = fixPairs;
}
// cfg.extraOptions;
in
''
require('copilot_cmp').setup(${helpers.toLuaObject setupOptions})
'';
};
}

View file

@ -0,0 +1,19 @@
{
lib,
helpers,
config,
...
}:
with lib;
let
cfg = config.plugins.crates-nvim;
in
{
options.plugins.crates-nvim = helpers.neovim-plugin.extraOptionsOptions;
config = mkIf cfg.enable {
extraConfigLua = ''
require('crates').setup(${helpers.toLuaObject cfg.extraOptions})
'';
};
}

View file

@ -0,0 +1,204 @@
{
lib,
helpers,
pkgs,
...
}@args:
let
# A list of most cmp source plugins, passed to mkCmpSourcePlugin.
# More complex cmp sources can instead be defined as their own plugin
# and register their source-name association using the `cmpSourcePlugins` option.
sources = [
{
pluginName = "cmp-async-path";
sourceName = "async_path";
}
{
pluginName = "cmp-buffer";
sourceName = "buffer";
}
{
pluginName = "cmp-calc";
sourceName = "calc";
}
{
pluginName = "cmp-dap";
sourceName = "dap";
}
{
pluginName = "cmp-cmdline";
sourceName = "cmdline";
}
{
pluginName = "cmp-ai";
sourceName = "cmp_ai";
}
{
pluginName = "cmp-clippy";
sourceName = "cmp-clippy";
}
{
pluginName = "cmp-cmdline-history";
sourceName = "cmp-cmdline-history";
}
{
pluginName = "cmp-pandoc-nvim";
sourceName = "cmp_pandoc";
}
{
pluginName = "cmp-tabby";
sourceName = "cmp_tabby";
}
{
pluginName = "cmp-tabnine";
sourceName = "cmp_tabnine";
}
{
pluginName = "cmp-conventionalcommits";
sourceName = "conventionalcommits";
}
{
pluginName = "copilot-cmp";
sourceName = "copilot";
}
{
pluginName = "crates-nvim";
sourceName = "crates";
}
{
pluginName = "cmp-dictionary";
sourceName = "dictionary";
}
{
pluginName = "cmp-digraphs";
sourceName = "digraphs";
}
{
pluginName = "cmp-emoji";
sourceName = "emoji";
}
{
pluginName = "cmp-fish";
sourceName = "fish";
}
{
pluginName = "cmp-fuzzy-buffer";
sourceName = "fuzzy_buffer";
}
{
pluginName = "cmp-fuzzy-path";
sourceName = "fuzzy_path";
}
{
pluginName = "cmp-git";
sourceName = "git";
}
{
pluginName = "cmp-greek";
sourceName = "greek";
}
{
pluginName = "cmp-latex-symbols";
sourceName = "latex_symbols";
}
{
pluginName = "cmp-look";
sourceName = "look";
}
{
pluginName = "cmp_luasnip";
sourceName = "luasnip";
}
{
pluginName = "cmp-nvim-lsp";
sourceName = "nvim_lsp";
}
{
pluginName = "cmp-nvim-lsp-document-symbol";
sourceName = "nvim_lsp_document_symbol";
}
{
pluginName = "cmp-nvim-lsp-signature-help";
sourceName = "nvim_lsp_signature_help";
}
{
pluginName = "cmp-nvim-lua";
sourceName = "nvim_lua";
}
{
pluginName = "cmp-npm";
sourceName = "npm";
}
{
pluginName = "cmp-omni";
sourceName = "omni";
}
{
pluginName = "cmp-pandoc-references";
sourceName = "pandoc_references";
}
{
pluginName = "cmp-path";
sourceName = "path";
}
{
pluginName = "cmp-rg";
sourceName = "rg";
}
{
pluginName = "cmp-snippy";
sourceName = "snippy";
}
{
pluginName = "cmp-spell";
sourceName = "spell";
}
{
pluginName = "cmp-tmux";
sourceName = "tmux";
}
{
pluginName = "cmp-treesitter";
sourceName = "treesitter";
}
{
pluginName = "cmp-nvim-ultisnips";
sourceName = "ultisnips";
}
{
pluginName = "cmp-vim-lsp";
sourceName = "vim_lsp";
}
{
pluginName = "cmp-vimwiki-tags";
sourceName = "vimwiki-tags";
}
{
pluginName = "cmp-vsnip";
sourceName = "vsnip";
}
{
pluginName = "cmp_yanky";
sourceName = "yanky";
}
{
pluginName = "cmp-zsh";
sourceName = "zsh";
}
];
mkCmpSourcePlugin = import ./_mk-cmp-plugin.nix args;
pluginModules = builtins.map mkCmpSourcePlugin sources;
in
{
# For extra cmp plugins
imports = [
./copilot-cmp.nix
./cmp-ai.nix
./cmp-fish.nix
./cmp-git.nix
./cmp-tabby.nix
./cmp-tabnine.nix
./crates-nvim.nix
] ++ pluginModules;
}