treewide: rewrite autocompletion module and related stuff (#404)

* modules/completion: rewrite

* treewide: remove vsnip, add luasnip

* nvim-cmp: add default sorting

* nvim-cmp: load after luasnip

* lib: fix docs for mergelessListOf

* docs: add changelog entires for rewrite

* deprecations: add rewrite deprecations

* nvim-cmp: clarify in format description

* docs: fix option reference in release notes

* treewide: remove reduant `// {default = false;}`s

* luasnip: add missing `{option}` for option reference

* deprecations: add entry for vsnip

* nvim-autopairs: use multiline string

* nvim-dap: use outer attribute
This commit is contained in:
diniamo 2024-10-09 19:50:34 +02:00 committed by GitHub
parent ef413736e9
commit 7dbd1cd8d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 512 additions and 483 deletions

View file

@ -105,12 +105,10 @@ isMaximal: {
transparent = false;
};
autopairs.enable = true;
autopairs.nvim-autopairs.enable = true;
autocomplete = {
enable = true;
type = "nvim-cmp";
};
autocomplete.nvim-cmp.enable = true;
snippets.luasnip.enable = true;
filetree = {
nvimTree = {

View file

@ -28,10 +28,11 @@ configuration formats.
### `vim.maps` rewrite {#sec-vim-maps-rewrite}
Instead of specifying map modes using submodules (eg.: `vim.maps.normal`), a new `vim.keymaps`
submodule with support for a `mode` option has been introduced. It can be either a string, or a
list of strings, where a string represents the short-name of the map mode(s), that the mapping
should be set for. See `:help map-modes` for more information.
Instead of specifying map modes using submodules (eg.: `vim.maps.normal`), a new
`vim.keymaps` submodule with support for a `mode` option has been introduced. It
can be either a string, or a list of strings, where a string represents the
short-name of the map mode(s), that the mapping should be set for. See
`:help map-modes` for more information.
For example:
@ -62,7 +63,6 @@ Note that we are looking to add more alternatives in the future like
dressing.nvim and actions-preview.nvim, in case fastaction doesn't work for
everyone.
## Changelog {#sec-release-0.7-changelog}
[ItsSorae](https://github.com/ItsSorae):
@ -150,12 +150,29 @@ everyone.
- Replace `vim.lsp.nvimCodeActionMenu` with `vim.ui.fastaction`, see the
breaking changes section above for more details
- Add a `setupOpts` option to nvim-surround, which allows modifying options that aren't defined in nvf. Move the alternate nvim-surround keybinds to use `setupOpts`.
- Add a `setupOpts` option to nvim-surround, which allows modifying options that
aren't defined in nvf. Move the alternate nvim-surround keybinds to use
`setupOpts`.
- Remove `autopairs.type`, and rename `autopairs.enable` to
`autopairs.nvim-autopairs.enable`. The new
[](#opt-vim.autopairs.nvim-autopairs.enable) supports `setupOpts` format by
default.
- Refactor of `nvim-cmp` and completion related modules
- Remove `autocomplete.type` in favor of per-plugin enable options such as
[](#opt-vim.autocomplete.nvim-cmp.enable).
- Deprecate legacy Vimsnip in favor of Luasnip, and integrate
friendly-snippets for bundled snippets. [](#opt-vim.snippets.luasnip.enable)
can be used to toggle Luasnip.
- Add sorting function options for completion sources under
[](#opt-vim.autocomplete.nvim-cmp.setupOpts.sorting.comparators)
[Neovim documentation on `vim.cmd`]: https://neovim.io/doc/user/lua.html#vim.cmd()
- Make Neovim's configuration file entirely Lua based. This comes with a few
breaking changes:
- `vim.configRC` has been removed. You will need to migrate your entries to
Neovim-compliant Lua code, and add them to `vim.luaConfigRC` instead.
Existing vimscript configurations may be preserved in `vim.cmd` functions.
@ -248,4 +265,3 @@ everyone.
- Add support for [base16 theming](https://github.com/RRethy/base16-nvim) under
`vim.theme`
- Fix internal breakage in `elixir-tools` setup.

View file

@ -316,6 +316,22 @@
"type": "github"
}
},
"plugin-cmp-luasnip": {
"flake": false,
"locked": {
"lastModified": 1696878902,
"narHash": "sha256-nUJJl2zyK/oSwz5RzI9j3gf9zpDfCImCYbPbVsyXgz8=",
"owner": "saadparwaiz1",
"repo": "cmp_luasnip",
"rev": "05a9ab28b53f71d1aece421ef32fee2cb857a843",
"type": "github"
},
"original": {
"owner": "saadparwaiz1",
"repo": "cmp_luasnip",
"type": "github"
}
},
"plugin-cmp-nvim-lsp": {
"flake": false,
"locked": {
@ -364,22 +380,6 @@
"type": "github"
}
},
"plugin-cmp-vsnip": {
"flake": false,
"locked": {
"lastModified": 1669100283,
"narHash": "sha256-2mkN03noOr5vBvRbSb35xZKorSH+8savQNZtgM9+QcM=",
"owner": "hrsh7th",
"repo": "cmp-vsnip",
"rev": "989a8a73c44e926199bfd05fa7a516d51f2d2752",
"type": "github"
},
"original": {
"owner": "hrsh7th",
"repo": "cmp-vsnip",
"type": "github"
}
},
"plugin-codewindow-nvim": {
"flake": false,
"locked": {
@ -588,6 +588,22 @@
"type": "github"
}
},
"plugin-friendly-snippets": {
"flake": false,
"locked": {
"lastModified": 1727061933,
"narHash": "sha256-yTsuV5unoujY0mhLINssYYBWCeefe+nJaxQHJKm7hlk=",
"owner": "rafamadriz",
"repo": "friendly-snippets",
"rev": "00ba9dd3df89509f95437b8d595553707c46d5ea",
"type": "github"
},
"original": {
"owner": "rafamadriz",
"repo": "friendly-snippets",
"type": "github"
}
},
"plugin-gesture-nvim": {
"flake": false,
"locked": {
@ -828,6 +844,22 @@
"type": "github"
}
},
"plugin-luasnip": {
"flake": false,
"locked": {
"lastModified": 1726165831,
"narHash": "sha256-nkaa1NGOI28Et2QitQB+Spv+J42QVdHE1oywteLcJJw=",
"owner": "L3MON4D3",
"repo": "LuaSnip",
"rev": "e808bee352d1a6fcf902ca1a71cee76e60e24071",
"type": "github"
},
"original": {
"owner": "L3MON4D3",
"repo": "LuaSnip",
"type": "github"
}
},
"plugin-mind-nvim": {
"flake": false,
"locked": {
@ -1757,22 +1789,6 @@
"type": "github"
}
},
"plugin-vim-vsnip": {
"flake": false,
"locked": {
"lastModified": 1704937299,
"narHash": "sha256-gvm6z4pgSULBVPukewRyjwxZ0vZgreQWbG/0kOB1QBo=",
"owner": "hrsh7th",
"repo": "vim-vsnip",
"rev": "02a8e79295c9733434aab4e0e2b8c4b7cea9f3a9",
"type": "github"
},
"original": {
"owner": "hrsh7th",
"repo": "vim-vsnip",
"type": "github"
}
},
"plugin-which-key": {
"flake": false,
"locked": {
@ -1827,10 +1843,10 @@
"plugin-cheatsheet-nvim": "plugin-cheatsheet-nvim",
"plugin-cinnamon-nvim": "plugin-cinnamon-nvim",
"plugin-cmp-buffer": "plugin-cmp-buffer",
"plugin-cmp-luasnip": "plugin-cmp-luasnip",
"plugin-cmp-nvim-lsp": "plugin-cmp-nvim-lsp",
"plugin-cmp-path": "plugin-cmp-path",
"plugin-cmp-treesitter": "plugin-cmp-treesitter",
"plugin-cmp-vsnip": "plugin-cmp-vsnip",
"plugin-codewindow-nvim": "plugin-codewindow-nvim",
"plugin-comment-nvim": "plugin-comment-nvim",
"plugin-copilot-cmp": "plugin-copilot-cmp",
@ -1844,6 +1860,7 @@
"plugin-fastaction-nvim": "plugin-fastaction-nvim",
"plugin-fidget-nvim": "plugin-fidget-nvim",
"plugin-flutter-tools": "plugin-flutter-tools",
"plugin-friendly-snippets": "plugin-friendly-snippets",
"plugin-gesture-nvim": "plugin-gesture-nvim",
"plugin-gitsigns-nvim": "plugin-gitsigns-nvim",
"plugin-glow-nvim": "plugin-glow-nvim",
@ -1859,6 +1876,7 @@
"plugin-lspkind": "plugin-lspkind",
"plugin-lspsaga": "plugin-lspsaga",
"plugin-lualine": "plugin-lualine",
"plugin-luasnip": "plugin-luasnip",
"plugin-mind-nvim": "plugin-mind-nvim",
"plugin-minimap-vim": "plugin-minimap-vim",
"plugin-modes-nvim": "plugin-modes-nvim",
@ -1917,7 +1935,6 @@
"plugin-vim-markdown": "plugin-vim-markdown",
"plugin-vim-repeat": "plugin-vim-repeat",
"plugin-vim-startify": "plugin-vim-startify",
"plugin-vim-vsnip": "plugin-vim-vsnip",
"plugin-which-key": "plugin-which-key",
"rnix-lsp": "rnix-lsp",
"systems": "systems_2"

View file

@ -277,11 +277,6 @@
flake = false;
};
plugin-cmp-vsnip = {
url = "github:hrsh7th/cmp-vsnip";
flake = false;
};
plugin-cmp-path = {
url = "github:hrsh7th/cmp-path";
flake = false;
@ -292,9 +287,19 @@
flake = false;
};
plugin-cmp-luasnip = {
url = "github:saadparwaiz1/cmp_luasnip";
flake = false;
};
# snippets
plugin-vim-vsnip = {
url = "github:hrsh7th/vim-vsnip";
plugin-luasnip = {
url = "github:L3MON4D3/LuaSnip";
flake = false;
};
plugin-friendly-snippets = {
url = "github:rafamadriz/friendly-snippets";
flake = false;
};

5
lib/attrsets.nix Normal file
View file

@ -0,0 +1,5 @@
{lib}: let
inherit (builtins) listToAttrs;
in {
mapListToAttrs = f: list: listToAttrs (map f list);
}

View file

@ -10,6 +10,7 @@
dag = import ./dag.nix {inherit lib;};
languages = import ./languages.nix {inherit lib;};
lists = import ./lists.nix {inherit lib;};
attrsets = import ./attrsets.nix {inherit lib;};
lua = import ./lua.nix {inherit lib;};
neovimConfiguration = import ../modules {inherit inputs lib;};
}

View file

@ -2,8 +2,8 @@
{lib}: let
inherit (builtins) isString getAttr;
inherit (lib.options) mkOption;
inherit (lib.attrsets) listToAttrs;
inherit (lib.types) bool;
inherit (lib.nvim.attrsets) mapListToAttrs;
in {
# Converts a boolean to a yes/no string. This is used in lots of
# configuration formats.
@ -12,21 +12,21 @@ in {
config,
diagnosticsProviders,
}:
listToAttrs
(map (v: let
type =
if isString v
then v
else getAttr v.type;
package =
if isString v
then diagnosticsProviders.${type}.package
else v.package;
in {
name = "${lang}-diagnostics-${type}";
value = diagnosticsProviders.${type}.nullConfig package;
})
config);
mapListToAttrs
(v: let
type =
if isString v
then v
else getAttr v.type;
package =
if isString v
then diagnosticsProviders.${type}.package
else v.package;
in {
name = "${lang}-diagnostics-${type}";
value = diagnosticsProviders.${type}.nullConfig package;
})
config;
mkEnable = desc:
mkOption {

View file

@ -1,7 +1,7 @@
{lib}: let
inherit (lib) isStringLike showOption showFiles getFiles mergeOneOption mergeEqualOption mkOptionType;
inherit (lib.strings) isString;
inherit (lib.types) anything attrsOf;
inherit (lib.options) showOption showFiles getFiles mergeOneOption mergeEqualOption;
inherit (lib.strings) isString isStringLike;
inherit (lib.types) anything attrsOf listOf mkOptionType;
inherit (lib.nvim.types) anythingConcatLists;
inherit (builtins) typeOf isAttrs any head concatLists stringLength match;
in {
@ -52,6 +52,16 @@ in {
(mergeFunctions.${commonType} or mergeEqualOption) loc defs;
};
mergelessListOf = elemType: let
super = listOf elemType;
in
super
// {
name = "mergelessListOf";
description = "mergeless ${super.description}";
merge = mergeEqualOption;
};
char = mkOptionType {
name = "char";
description = "character";

View file

@ -6,10 +6,10 @@
typesDag = import ./dag.nix {inherit lib;};
typesPlugin = import ./plugins.nix {inherit inputs lib;};
typesLanguage = import ./languages.nix {inherit lib;};
typesTypes = import ./types.nix {inherit lib;};
customTypes = import ./custom.nix {inherit lib;};
in {
inherit (typesDag) dagOf;
inherit (typesPlugin) pluginsOpt extraPluginType mkPluginSetupOption luaInline pluginType borderType;
inherit (typesLanguage) diagnostics mkGrammarOption;
inherit (typesTypes) anythingConcatLists char hexColor;
inherit (customTypes) anythingConcatLists char hexColor mergelessListOf;
}

View file

@ -1,5 +1,5 @@
{lib, ...}: let
inherit (lib.modules) mkRemovedOptionModule;
inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule;
in {
imports = [
# 2024-06-06
@ -14,5 +14,26 @@ in {
available under `vim.ui.fastaction` as a replacement. Simply remove everything under
`vim.lsp.nvimCodeActionMenu`, and set `vim.ui.fastaction.enable` to `true`.
'')
(mkRemovedOptionModule ["vim" "autopairs" "enable"] ''
vim.autopairs.enable has been removed in favor of per-plugin modules.
You can enable nvim-autopairs with vim.autopairs.nvim-autopairs.enable instead.
'')
(mkRemovedOptionModule ["vim" "autopairs" "type"] ''
vim.autopairs.type has been removed in favor of per-plugin modules.
You can enable nvim-autopairs with vim.autopairs.nvim-autopairs.enable instead.
'')
(mkRemovedOptionModule ["vim" "autocomplete" "enable"] ''
vim.autocomplete.enable has been removed in favor of per-plugin modules.
You can enable nvim-cmp with vim.autocomplete.nvim-cmp.enable instead.
'')
(mkRemovedOptionModule ["vim" "autocomplete" "type"] ''
vim.autocomplete.type has been removed in favor of per-plugin modules.
You can enable nvim-cmp with vim.autocomplete.nvim-cmp.enable instead.
'')
(mkRemovedOptionModule ["vim" "snippets" "vsnip" "enable"] ''
vim.snippets.vsnip.enable has been removed in favor of the more modern luasnip.
'')
(mkRenamedOptionModule ["vim" "lsp" "lspkind" "mode"] ["vim" "lsp" "lspkind" "setupOpts" "mode"])
];
}

View file

@ -34,6 +34,8 @@ in {
"copilot-cmp"
];
vim.autocomplete.nvim-cmp.sources = {copilot = "[Copilot]";};
vim.pluginRC.copilot = entryAnywhere ''
require("copilot").setup(${toLuaObject cfg.setupOpts})

View file

@ -4,16 +4,17 @@
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.trivial) boolToString;
inherit (lib.nvim.dag) entryAnywhere;
inherit (lib.nvim.lua) toLuaObject;
cfg = config.vim.autopairs;
cfg = config.vim.autopairs.nvim-autopairs;
in {
config = mkIf cfg.enable {
vim.startPlugins = ["nvim-autopairs"];
vim.pluginRC.autopairs = entryAnywhere ''
require("nvim-autopairs").setup({ map_cr = ${boolToString (!config.vim.autocomplete.enable)} })
'';
vim = {
startPlugins = ["nvim-autopairs"];
pluginRC.autopairs = entryAnywhere ''
require('nvim-autopairs').setup(${toLuaObject cfg.setupOpts})
'';
};
};
}

View file

@ -1,21 +1,14 @@
{lib, ...}: let
inherit (lib) mkRemovedOptionModule;
inherit (lib.options) mkEnableOption mkOption;
inherit (lib.types) enum;
inherit (lib.options) mkEnableOption;
inherit (lib.nvim.types) mkPluginSetupOption;
in {
imports = [
(mkRemovedOptionModule ["vim" "autopairs" "nvim-compe"] "nvim-compe is deprecated and no longer suported.")
];
options.vim = {
autopairs = {
enable = mkEnableOption "autopairs" // {default = false;};
type = mkOption {
type = enum ["nvim-autopairs"];
default = "nvim-autopairs";
description = "Set the autopairs type. Options: nvim-autopairs [nvim-autopairs]";
};
};
options.vim.autopairs.nvim-autopairs = {
enable = mkEnableOption "autopairs";
setupOpts = mkPluginSetupOption "nvim-autopairs" {};
};
}

View file

@ -3,246 +3,115 @@
config,
...
}: let
inherit (builtins) toJSON;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.attrsets) attrNames mapAttrsToList;
inherit (lib.strings) concatMapStringsSep concatStringsSep optionalString;
inherit (lib.nvim.binds) addDescriptionsToMappings mkSetLuaBinding;
inherit (lib.nvim.dag) entryAnywhere entryAfter;
inherit (lib.modules) mkIf;
inherit (lib.strings) optionalString;
inherit (lib.generators) mkLuaInline;
inherit (lib.nvim.binds) addDescriptionsToMappings;
inherit (lib.nvim.dag) entryAfter;
inherit (lib.nvim.lua) toLuaObject;
inherit (builtins) attrNames;
cfg = config.vim.autocomplete;
lspkindEnabled = config.vim.lsp.enable && config.vim.lsp.lspkind.enable;
self = import ./nvim-cmp.nix {inherit lib;};
mappingDefinitions = self.options.vim.autocomplete.mappings;
cfg = config.vim.autocomplete.nvim-cmp;
luasnipEnable = config.vim.snippets.luasnip.enable;
self = import ./nvim-cmp.nix {inherit lib config;};
mappingDefinitions = self.options.vim.autocomplete.nvim-cmp.mappings;
mappings = addDescriptionsToMappings cfg.mappings mappingDefinitions;
builtSources =
concatMapStringsSep
"\n"
(n: "{ name = '${n}'},")
(attrNames cfg.sources);
builtMaps =
concatStringsSep
"\n"
(mapAttrsToList
(n: v:
if v == null
then ""
else "${n} = '${v}',")
cfg.sources);
dagPlacement =
if lspkindEnabled
then entryAfter ["lspkind"]
else entryAnywhere;
in {
config = mkIf cfg.enable {
vim.startPlugins = [
"nvim-cmp"
"cmp-buffer"
"cmp-vsnip"
"cmp-path"
"vim-vsnip"
];
vim = {
startPlugins = [
"nvim-cmp"
"cmp-buffer"
"cmp-path"
];
vim.autocomplete.sources = {
"nvim-cmp" = null;
"vsnip" = "[VSnip]";
"buffer" = "[Buffer]";
"crates" = "[Crates]";
"path" = "[Path]";
"copilot" = "[Copilot]";
};
autocomplete.nvim-cmp.sources = {
nvim-cmp = null;
buffer = "[Buffer]";
path = "[Path]";
};
vim.maps.insert = mkMerge [
(mkSetLuaBinding mappings.complete ''
require('cmp').complete
'')
(let
defaultKeys =
if config.vim.autopairs.enable
then "require('nvim-autopairs').autopairs_cr()"
else "vim.api.nvim_replace_termcodes(${toJSON mappings.confirm.value}, true, false, true)";
in
mkSetLuaBinding mappings.confirm ''
function()
if not require('cmp').confirm({ select = true }) then
vim.fn.feedkeys(${defaultKeys}, 'n')
autocomplete.nvim-cmp.setupOpts = {
sources = map (s: {name = s;}) (attrNames cfg.sources);
# TODO: try to get nvim-cmp to follow global border style
window = mkIf config.vim.ui.borders.enable {
completion = mkLuaInline "cmp.config.window.bordered()";
documentation = mkLuaInline "cmp.config.window.bordered()";
};
formatting.format = cfg.format;
};
pluginRC.nvim-cmp = mkIf cfg.enable (entryAfter ["autopairs" "luasnip"] ''
local luasnip = require("luasnip")
local cmp = require("cmp")
cmp.setup(${toLuaObject cfg.setupOpts})
'');
# `cmp` and `luasnip` are defined above, in the `nvim-cmp` section
autocomplete.nvim-cmp.setupOpts.mapping = {
${mappings.complete.value} = mkLuaInline "cmp.mapping.complete()";
${mappings.close.value} = mkLuaInline "cmp.mapping.abort()";
${mappings.scrollDocsUp.value} = mkLuaInline "cmp.mapping.scroll_docs(-4)";
${mappings.scrollDocsDown.value} = mkLuaInline "cmp.mapping.scroll_docs(4)";
${mappings.confirm.value} = mkLuaInline ''
cmp.mapping(function(fallback)
if cmp.visible() then
${
if luasnipEnable
then ''
if luasnip.expandable() then
luasnip.expand()
else
cmp.confirm({ select = true })
end
''
else "cmp.confirm({ select = true })"
}
else
fallback()
end
end
'')
(mkSetLuaBinding mappings.next ''
function()
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
end)
'';
local cmp = require('cmp')
${mappings.next.value} = mkLuaInline ''
cmp.mapping(function(fallback)
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
local feedkey = function(key, mode)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
end
if cmp.visible() then
cmp.select_next_item()
${optionalString luasnipEnable ''
elseif luasnip.locally_jumpable(1) then
luasnip.jump(1)
''}
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end)
'';
if cmp.visible() then
cmp.select_next_item()
elseif vim.fn['vsnip#available'](1) == 1 then
feedkey("<Plug>(vsnip-expand-or-jump)", "")
elseif has_words_before() then
cmp.complete()
else
local termcode = vim.api.nvim_replace_termcodes(${toJSON mappings.next.value}, true, false, true)
vim.fn.feedkeys(termcode, 'n')
end
end
'')
(mkSetLuaBinding mappings.previous ''
function()
local cmp = require('cmp')
local feedkey = function(key, mode)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
end
if cmp.visible() then
cmp.select_prev_item()
elseif vim.fn['vsnip#available'](-1) == 1 then
feedkeys("<Plug>(vsnip-jump-prev)", "")
end
end
'')
(mkSetLuaBinding mappings.close ''
require('cmp').mapping.abort()
'')
(mkSetLuaBinding mappings.scrollDocsUp ''
require('cmp').mapping.scroll_docs(-4)
'')
(mkSetLuaBinding mappings.scrollDocsDown ''
require('cmp').mapping.scroll_docs(4)
'')
];
vim.maps.command = mkMerge [
(mkSetLuaBinding mappings.complete ''
require('cmp').complete
'')
(mkSetLuaBinding mappings.close ''
require('cmp').mapping.close()
'')
(mkSetLuaBinding mappings.scrollDocsUp ''
require('cmp').mapping.scroll_docs(-4)
'')
(mkSetLuaBinding mappings.scrollDocsDown ''
require('cmp').mapping.scroll_docs(4)
'')
];
vim.maps.select = mkMerge [
(mkSetLuaBinding mappings.next ''
function()
local cmp = require('cmp')
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
local feedkey = function(key, mode)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
end
if cmp.visible() then
cmp.select_next_item()
elseif vim.fn['vsnip#available'](1) == 1 then
feedkey("<Plug>(vsnip-expand-or-jump)", "")
elseif has_words_before() then
cmp.complete()
else
local termcode = vim.api.nvim_replace_termcodes(${toJSON mappings.next.value}, true, false, true)
vim.fn.feedkeys(termcode, 'n')
end
end
'')
(mkSetLuaBinding mappings.previous ''
function()
local cmp = require('cmp')
local feedkey = function(key, mode)
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
end
if cmp.visible() then
cmp.select_prev_item()
elseif vim.fn['vsnip#available'](-1) == 1 then
feedkeys("<Plug>(vsnip-jump-prev)", "")
end
end
'')
];
# TODO: alternative snippet engines to vsnip
# https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt#L82
vim.pluginRC.completion = mkIf (cfg.type == "nvim-cmp") (dagPlacement ''
local nvim_cmp_menu_map = function(entry, vim_item)
-- name for each source
vim_item.menu = ({
${builtMaps}
})[entry.source.name]
return vim_item
end
${optionalString lspkindEnabled ''
lspkind_opts.before = ${cfg.formatting.format}
''}
local cmp = require'cmp'
cmp.setup({
${optionalString config.vim.ui.borders.enable ''
-- explicitly enabled by setting ui.borders.enable = true
-- TODO: try to get nvim-cmp to follow global border style
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
''}
snippet = {
expand = function(args)
vim.fn["vsnip#anonymous"](args.body)
end,
},
sources = {
${builtSources}
},
completion = {
completeopt = 'menu,menuone,noinsert',
${optionalString (!cfg.alwaysComplete) "autocomplete = false"}
},
formatting = {
format =
${
if lspkindEnabled
then "lspkind.cmp_format(lspkind_opts)"
else cfg.formatting.format
},
}
})
${optionalString (config.vim.autopairs.enable && config.vim.autopairs.type == "nvim-autopairs") ''
local cmp_autopairs = require('nvim-autopairs.completion.cmp')
cmp.event:on('confirm_done', cmp_autopairs.on_confirm_done({ map_char = { text = ""} }))
''}
'');
vim.snippets.vsnip.enable =
if (cfg.type == "nvim-cmp")
then true
else config.vim.snippets.vsnip.enable;
${mappings.previous.value} = mkLuaInline ''
cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
${optionalString luasnipEnable ''
elseif luasnip.locally_jumpable(-1) then
luasnip.jump(-1)
''}
else
fallback()
end
end)
'';
};
};
};
}

View file

@ -1,4 +1,4 @@
_: {
{
imports = [
./config.nix
./nvim-cmp.nix

View file

@ -1,72 +1,103 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption mkOption literalMD;
{
lib,
config,
...
}: let
inherit (lib.options) mkEnableOption mkOption literalExpression literalMD;
inherit (lib.types) str attrsOf nullOr either;
inherit (lib.generators) mkLuaInline;
inherit (lib.nvim.binds) mkMappingOption;
inherit (lib.types) enum attrsOf nullOr str bool;
inherit (lib.nvim.types) mkPluginSetupOption luaInline mergelessListOf;
inherit (lib.nvim.lua) toLuaObject;
inherit (builtins) isString;
cfg = config.vim.autocomplete.nvim-cmp;
in {
options.vim = {
autocomplete = {
enable = mkEnableOption "autocomplete" // {default = false;};
alwaysComplete = mkOption {
type = bool;
description = "Automatically show completion.";
default = true;
};
mappings = {
complete = mkMappingOption "Complete [nvim-cmp]" "<C-Space>";
confirm = mkMappingOption "Confirm [nvim-cmp]" "<CR>";
next = mkMappingOption "Next item [nvim-cmp]" "<Tab>";
previous = mkMappingOption "Previous item [nvim-cmp]" "<S-Tab>";
close = mkMappingOption "Close [nvim-cmp]" "<C-e>";
scrollDocsUp = mkMappingOption "Scroll docs up [nvim-cmp]" "<C-d>";
scrollDocsDown = mkMappingOption "Scroll docs down [nvim-cmp]" "<C-f>";
};
type = mkOption {
type = enum ["nvim-cmp"];
default = "nvim-cmp";
description = "Set the autocomplete plugin. Options: [nvim-cmp]";
};
sources = mkOption {
options.vim.autocomplete.nvim-cmp = {
enable = mkEnableOption "nvim-cmp";
setupOpts = mkPluginSetupOption "the autocomplete plugin" {
completion.completeopt = mkOption {
type = str;
default = "menu,menuone,noinsert";
description = ''
Attribute set of source names for nvim-cmp.
A comma-separated list of options for completion.
If an attribute set is provided, then the menu value of
`vim_item` in the format will be set to the value (if
utilizing the `nvim_cmp_menu_map` function).
Note: only use a single attribute name per attribute set
'';
type = attrsOf (nullOr str);
default = {};
example = ''
{nvim-cmp = null; buffer = "[Buffer]";}
See `:help completeopt` for the complete list.
'';
};
formatting = {
format = mkOption {
description = ''
The function used to customize the appearance of the completion menu.
sorting.comparators = mkOption {
type = mergelessListOf (either str luaInline);
default = [
"offset"
"exact"
"score"
"kind"
"length"
"sort_text"
];
description = ''
The comparator functions used for sorting completions.
If [](#opt-vim.lsp.lspkind.enable) is true, then the function
will be called before modifications from lspkind.
Default is to call the menu mapping function.
'';
type = str;
default = "nvim_cmp_menu_map";
example = literalMD ''
```lua
function(entry, vim_item)
return vim_item
end
```
'';
};
You can either pass a valid inline lua function
(see `:help cmp-config.sorting.comparators`),
or a string, in which case the builtin comparator with that name will
be used.
'';
apply = map (
c:
if isString c
then mkLuaInline ("cmp.config.compare." + c)
else c
);
};
};
mappings = {
complete = mkMappingOption "Complete [nvim-cmp]" "<C-Space>";
confirm = mkMappingOption "Confirm [nvim-cmp]" "<CR>";
next = mkMappingOption "Next item [nvim-cmp]" "<Tab>";
previous = mkMappingOption "Previous item [nvim-cmp]" "<S-Tab>";
close = mkMappingOption "Close [nvim-cmp]" "<C-e>";
scrollDocsUp = mkMappingOption "Scroll docs up [nvim-cmp]" "<C-d>";
scrollDocsDown = mkMappingOption "Scroll docs down [nvim-cmp]" "<C-f>";
};
format = mkOption {
type = luaInline;
default = mkLuaInline ''
function(entry, vim_item)
vim_item.menu = (${toLuaObject cfg.sources})[entry.source.name]
return vim_item
end
'';
defaultText = literalMD ''
```lua
function(entry, vim_item)
vim_item.menu = (''${toLuaObject config.vim.autocomplete.nvim-cmp.sources})[entry.source.name]
return vim_item
end
```
'';
description = ''
The function used to customize the completion menu entires. This is
outside of `setupOpts` to allow for an easier integration with
lspkind.nvim.
See `:help cmp-config.formatting.format`.
'';
};
sources = mkOption {
type = attrsOf (nullOr str);
default = {};
description = "The list of sources used by nvim-cmp";
example = literalExpression ''
{
nvim-cmp = null;
buffer = "[Buffer]";
}
'';
};
};
}

View file

@ -16,57 +16,61 @@
in {
config = mkMerge [
(mkIf cfg.enable {
vim.startPlugins = ["nvim-dap"];
vim = {
startPlugins = ["nvim-dap"];
vim.pluginRC =
{
# TODO customizable keymaps
nvim-dap = entryAnywhere ''
local dap = require("dap")
vim.fn.sign_define("DapBreakpoint", { text = "🛑", texthl = "ErrorMsg", linehl = "", numhl = "" })
'';
}
// mapAttrs (_: v: (entryAfter ["nvim-dap"] v)) cfg.sources;
pluginRC =
{
# TODO customizable keymaps
nvim-dap = entryAnywhere ''
local dap = require("dap")
vim.fn.sign_define("DapBreakpoint", { text = "🛑", texthl = "ErrorMsg", linehl = "", numhl = "" })
'';
}
// mapAttrs (_: v: (entryAfter ["nvim-dap"] v)) cfg.sources;
vim.maps.normal = mkMerge [
(mkSetLuaBinding mappings.continue "require('dap').continue")
(mkSetLuaBinding mappings.restart "require('dap').restart")
(mkSetLuaBinding mappings.terminate "require('dap').terminate")
(mkSetLuaBinding mappings.runLast "require('dap').run_last")
maps.normal = mkMerge [
(mkSetLuaBinding mappings.continue "require('dap').continue")
(mkSetLuaBinding mappings.restart "require('dap').restart")
(mkSetLuaBinding mappings.terminate "require('dap').terminate")
(mkSetLuaBinding mappings.runLast "require('dap').run_last")
(mkSetLuaBinding mappings.toggleRepl "require('dap').repl.toggle")
(mkSetLuaBinding mappings.hover "require('dap.ui.widgets').hover")
(mkSetLuaBinding mappings.toggleBreakpoint "require('dap').toggle_breakpoint")
(mkSetLuaBinding mappings.toggleRepl "require('dap').repl.toggle")
(mkSetLuaBinding mappings.hover "require('dap.ui.widgets').hover")
(mkSetLuaBinding mappings.toggleBreakpoint "require('dap').toggle_breakpoint")
(mkSetLuaBinding mappings.runToCursor "require('dap').run_to_cursor")
(mkSetLuaBinding mappings.stepInto "require('dap').step_into")
(mkSetLuaBinding mappings.stepOut "require('dap').step_out")
(mkSetLuaBinding mappings.stepOver "require('dap').step_over")
(mkSetLuaBinding mappings.stepBack "require('dap').step_back")
(mkSetLuaBinding mappings.runToCursor "require('dap').run_to_cursor")
(mkSetLuaBinding mappings.stepInto "require('dap').step_into")
(mkSetLuaBinding mappings.stepOut "require('dap').step_out")
(mkSetLuaBinding mappings.stepOver "require('dap').step_over")
(mkSetLuaBinding mappings.stepBack "require('dap').step_back")
(mkSetLuaBinding mappings.goUp "require('dap').up")
(mkSetLuaBinding mappings.goDown "require('dap').down")
];
(mkSetLuaBinding mappings.goUp "require('dap').up")
(mkSetLuaBinding mappings.goDown "require('dap').down")
];
};
})
(mkIf (cfg.enable && cfg.ui.enable) {
vim.startPlugins = ["nvim-dap-ui" "nvim-nio"];
vim = {
startPlugins = ["nvim-dap-ui" "nvim-nio"];
vim.pluginRC.nvim-dap-ui = entryAfter ["nvim-dap"] (''
local dapui = require("dapui")
dapui.setup()
''
+ optionalString cfg.ui.autoStart ''
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
'');
vim.maps.normal = mkSetLuaBinding mappings.toggleDapUI "require('dapui').toggle";
pluginRC.nvim-dap-ui = entryAfter ["nvim-dap"] (''
local dapui = require("dapui")
dapui.setup()
''
+ optionalString cfg.ui.autoStart ''
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
'');
maps.normal = mkSetLuaBinding mappings.toggleDapUI "require('dapui').toggle";
};
})
];
}

View file

@ -9,7 +9,7 @@
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.strings) optionalString;
inherit (lib.trivial) boolToString;
inherit (lib.lists) isList optionals;
inherit (lib.lists) isList;
inherit (lib.types) bool package str listOf either enum;
inherit (lib.nvim.types) mkGrammarOption;
inherit (lib.nvim.lua) expToLua;
@ -101,7 +101,7 @@ in {
vim = {
startPlugins = ["crates-nvim"];
lsp.null-ls.enable = mkIf cfg.crates.codeActions true;
autocomplete.sources = {"crates" = "[Crates]";};
autocomplete.nvim-cmp.sources = {crates = "[Crates]";};
pluginRC.rust-crates = entryAnywhere ''
require('crates').setup {
null_ls = {

View file

@ -11,7 +11,7 @@
inherit (lib.nvim.binds) addDescriptionsToMappings;
cfg = config.vim.lsp;
usingNvimCmp = config.vim.autocomplete.enable && config.vim.autocomplete.type == "nvim-cmp";
usingNvimCmp = config.vim.autocomplete.nvim-cmp.enable;
self = import ./module.nix {inherit config lib pkgs;};
mappingDefinitions = self.options.vim.lsp.mappings;
@ -25,7 +25,7 @@ in {
vim = {
startPlugins = optional usingNvimCmp "cmp-nvim-lsp";
autocomplete.sources = {"nvim_lsp" = "[LSP]";};
autocomplete.nvim-cmp.sources = {nvim_lsp = "[LSP]";};
pluginRC.lsp-setup = ''
vim.g.formatsave = ${boolToString cfg.formatOnSave};

View file

@ -3,18 +3,32 @@
lib,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.nvim.dag) entryAnywhere;
inherit (lib.modules) mkIf mkForce;
inherit (lib.generators) mkLuaInline;
inherit (lib.nvim.lua) toLuaObject;
cfg = config.vim.lsp;
cfg = config.vim.lsp.lspkind;
in {
config = mkIf (cfg.enable && cfg.lspkind.enable) {
vim.startPlugins = ["lspkind"];
vim.pluginRC.lspkind = entryAnywhere ''
local lspkind = require'lspkind'
local lspkind_opts = {
mode = '${cfg.lspkind.mode}'
config = mkIf cfg.enable {
assertions = [
{
assertion = config.vim.autocomplete.nvim-cmp.enable;
message = ''
While lspkind supports Neovim's native lsp upstream, using that over
nvim-cmp isn't recommended, nor supported by nvf.
Please migrate to nvim-cmp if you want to use lspkind.
'';
}
'';
];
vim = {
startPlugins = ["lspkind"];
lsp.lspkind.setupOpts.before = config.vim.autocomplete.nvim-cmp.format;
autocomplete.nvim-cmp.setupOpts.formatting.format = mkForce (mkLuaInline ''
require("lspkind").cmp_format(${toLuaObject cfg.setupOpts})
'');
};
};
}

View file

@ -1,16 +1,22 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption mkOption;
inherit (lib.types) enum;
inherit (lib.types) enum nullOr;
inherit (lib.nvim.types) mkPluginSetupOption luaInline;
in {
options.vim.lsp = {
lspkind = {
enable = mkEnableOption "vscode-like pictograms for lsp [lspkind]";
options.vim.lsp.lspkind = {
enable = mkEnableOption "vscode-like pictograms for lsp [lspkind]";
setupOpts = mkPluginSetupOption "lspkind.nvim" {
mode = mkOption {
description = "Defines how annotations are shown";
type = enum ["text" "text_symbol" "symbol_text" "symbol"];
default = "symbol_text";
};
before = mkOption {
description = "The function that will be called before lspkind's modifications are applied";
type = nullOr luaInline;
default = null;
};
};
};
}

View file

@ -45,10 +45,10 @@ in {
completion = {
nvim_cmp = mkOption {
# if using nvim-cmp, otherwise set to false
# If using nvim-cmp, otherwise set to false
type = bool;
description = "If using nvim-cmp, otherwise set to false";
default = config.vim.autocomplete.type == "nvim-cmp";
default = config.vim.autocomplete.nvim-cmp.enable;
};
};
};

View file

@ -1,5 +1,5 @@
{
imports = [
./vsnip
./luasnip
];
}

View file

@ -0,0 +1,17 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf;
cfg = config.vim.snippets.luasnip;
in {
config = mkIf cfg.enable {
vim = {
startPlugins = ["luasnip" "cmp-luasnip"] ++ cfg.providers;
autocomplete.nvim-cmp.sources = {luasnip = "[LuaSnip]";};
pluginRC.luasnip = cfg.loaders;
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./luasnip.nix
./config.nix
];
}

View file

@ -0,0 +1,36 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption mkOption literalExpression literalMD;
inherit (lib.types) listOf lines;
inherit (lib.nvim.types) pluginType;
in {
options.vim.snippets.luasnip = {
enable = mkEnableOption "luasnip";
providers = mkOption {
type = listOf pluginType;
default = ["friendly-snippets"];
description = ''
The snippet provider packages.
::: {.note}
These are simply appended to {option} `vim.startPlugins`.
:::
'';
example = literalExpression "[\"vimPlugins.vim-snippets\"]";
};
loaders = mkOption {
type = lines;
default = "require('luasnip.loaders.from_vscode').lazy_load()";
defaultText = literalMD ''
```lua
require('luasnip.loaders.from_vscode').lazy_load()
```
'';
description = "Lua code used to load snippet providers.";
example = literalMD ''
```lua
require("luasnip.loaders.from_snipmate").lazy_load()
```
'';
};
};
}

View file

@ -1,13 +0,0 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf;
cfg = config.vim.snippets.vsnip;
in {
config = mkIf cfg.enable {
vim.startPlugins = ["vim-vsnip"];
};
}

View file

@ -1,5 +0,0 @@
{
imports = [
./vsnip.nix
];
}

View file

@ -1,7 +0,0 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption;
in {
options.vim.snippets.vsnip = {
enable = mkEnableOption "vim-vsnip: snippet LSP/VSCode's format";
};
}

View file

@ -4,10 +4,11 @@
...
}: let
inherit (lib.options) mkOption;
inherit (lib.attrsets) attrNames listToAttrs;
inherit (lib.attrsets) attrNames;
inherit (lib.strings) hasPrefix;
inherit (lib.types) bool lines enum;
inherit (lib.modules) mkIf;
inherit (lib.nvim.attrsets) mapListToAttrs;
inherit (lib.nvim.dag) entryBefore;
inherit (lib.nvim.types) hexColor;
@ -17,7 +18,8 @@
};
numbers = ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F"];
base16Options = listToAttrs (map (n: {
base16Options =
mapListToAttrs (n: {
name = "base0${n}";
value = mkOption {
description = "The base0${n} color to use";
@ -28,7 +30,7 @@
else "#${v}";
};
})
numbers);
numbers;
in {
options.vim.theme = {
enable = mkOption {

View file

@ -11,7 +11,7 @@
inherit (lib.nvim.dag) entryBefore entryAfter;
cfg = config.vim.treesitter;
usingNvimCmp = config.vim.autocomplete.enable && config.vim.autocomplete.type == "nvim-cmp";
usingNvimCmp = config.vim.autocomplete.nvim-cmp.enable;
self = import ./treesitter.nix {inherit pkgs lib;};
mappingDefinitions = self.options.vim.treesitter.mappings;
@ -21,7 +21,7 @@ in {
vim = {
startPlugins = ["nvim-treesitter"] ++ optional usingNvimCmp "cmp-treesitter";
autocomplete.sources = {"treesitter" = "[Treesitter]";};
autocomplete.nvim-cmp.sources = {treesitter = "[Treesitter]";};
treesitter.grammars = optionals cfg.addDefaultGrammars cfg.defaultGrammars;
maps = {

View file

@ -21,7 +21,7 @@ in {
mkBool true "override the lsp markdown formatter with Noice";
"cmp.entry.get_documentation" =
mkBool (config.vim.autocomplete.type == "nvim-cmp") "override cmp documentation with Noice";
mkBool config.vim.autocomplete.nvim-cmp.enable "override cmp documentation with Noice";
};
signature = {

View file

@ -4,7 +4,7 @@
...
}: let
inherit (builtins) map mapAttrs filter;
inherit (lib.attrsets) mapAttrsToList filterAttrs;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) concatLines concatMapStringsSep;
inherit (lib.trivial) showWarnings;
inherit (lib.generators) mkLuaInline;