Merge pull request #510 from horriblename/feat-blink
Some checks failed
Set up binary cache / cachix (default) (push) Has been cancelled
Set up binary cache / cachix (maximal) (push) Has been cancelled
Set up binary cache / cachix (nix) (push) Has been cancelled
Validate flake & check documentation / Validate Flake Documentation (push) Has been cancelled
Validate flake & check documentation / Validate hyperlinks in documentation sources (push) Has been cancelled
Validate flake & check formatting / Validate Flake (push) Has been cancelled
Validate flake & check formatting / Formatting via Alejandra (push) Has been cancelled
Build and deploy documentation / Check latest commit (push) Has been cancelled
Check for typos in the source tree / check-typos (push) Has been cancelled
Build and deploy documentation / publish (push) Has been cancelled

completion/blink.cmp: add
This commit is contained in:
raf 2025-02-09 08:27:19 +00:00 committed by GitHub
commit a78026438c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 494 additions and 120 deletions

View file

@ -47,6 +47,12 @@
- Add Haskell support under `vim.languages.haskell` using [haskell-tools.nvim]. - Add Haskell support under `vim.languages.haskell` using [haskell-tools.nvim].
[horriblename](https://github.com/horriblename):
[blink.cmp]: https://github.com/saghen/blink.cmp
- Add [blink.cmp] support
[diniamo](https://github.com/diniamo): [diniamo](https://github.com/diniamo):
- Add Odin support under `vim.languages.odin`. - Add Odin support under `vim.languages.odin`.

34
flake.lock generated
View file

@ -167,6 +167,38 @@
"type": "github" "type": "github"
} }
}, },
"plugin-blink-cmp": {
"flake": false,
"locked": {
"lastModified": 1736295934,
"narHash": "sha256-MfHI4efAdaoCU8si6YFdznZmSTprthDq3YKuF91z7ss=",
"owner": "saghen",
"repo": "blink.cmp",
"rev": "1cc3b1a908fbcfd15451c4772759549724f38524",
"type": "github"
},
"original": {
"owner": "saghen",
"repo": "blink.cmp",
"type": "github"
}
},
"plugin-blink-compat": {
"flake": false,
"locked": {
"lastModified": 1734896240,
"narHash": "sha256-Rrrh+O3FbBnaAnCHwPuQyfhH+XueSkQp6ipEkn6esGY=",
"owner": "saghen",
"repo": "blink.compat",
"rev": "74b251a1e9478c4fa6d7c6bc2921d7124e6f6cbb",
"type": "github"
},
"original": {
"owner": "saghen",
"repo": "blink.compat",
"type": "github"
}
},
"plugin-bufdelete-nvim": { "plugin-bufdelete-nvim": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -2788,6 +2820,8 @@
"plugin-aerial-nvim": "plugin-aerial-nvim", "plugin-aerial-nvim": "plugin-aerial-nvim",
"plugin-alpha-nvim": "plugin-alpha-nvim", "plugin-alpha-nvim": "plugin-alpha-nvim",
"plugin-base16": "plugin-base16", "plugin-base16": "plugin-base16",
"plugin-blink-cmp": "plugin-blink-cmp",
"plugin-blink-compat": "plugin-blink-compat",
"plugin-bufdelete-nvim": "plugin-bufdelete-nvim", "plugin-bufdelete-nvim": "plugin-bufdelete-nvim",
"plugin-catppuccin": "plugin-catppuccin", "plugin-catppuccin": "plugin-catppuccin",
"plugin-ccc": "plugin-ccc", "plugin-ccc": "plugin-ccc",

View file

@ -294,6 +294,16 @@
flake = false; flake = false;
}; };
plugin-blink-cmp = {
url = "github:saghen/blink.cmp";
flake = false;
};
plugin-blink-compat = {
url = "github:saghen/blink.compat";
flake = false;
};
plugin-nvim-cmp = { plugin-nvim-cmp = {
url = "github:hrsh7th/nvim-cmp"; url = "github:hrsh7th/nvim-cmp";
flake = false; flake = false;

View file

@ -9,10 +9,14 @@
overlays = [ overlays = [
inputs.self.overlays.default inputs.self.overlays.default
(_: _: { (final: _: {
# Build nil from source to get most recent # Build nil from source to get most recent
# features as they are added. # features as they are added.
nil = inputs'.nil.packages.default; nil = inputs'.nil.packages.default;
blink-cmp = final.callPackage ./legacyPackages/blink-cmp.nix {
src = inputs.plugin-blink-cmp;
version = inputs.plugin-blink-cmp.shortRev or inputs.plugin-blink-cmp.shortDirtyRev or "dirty";
};
}) })
]; ];
}; };

View file

@ -0,0 +1,38 @@
{
rustPlatform,
hostPlatform,
vimUtils,
git,
src,
version,
}: let
blink-fuzzy-lib = rustPlatform.buildRustPackage {
pname = "blink-fuzzy-lib";
inherit version src;
env = {
# TODO: remove this if plugin stops using nightly rust
RUSTC_BOOTSTRAP = true;
};
nativeBuildInputs = [git];
cargoLock = {
lockFile = "${src}/Cargo.lock";
allowBuiltinFetchGit = true;
};
};
libExt =
if hostPlatform.isDarwin
then "dylib"
else "so";
in
vimUtils.buildVimPlugin {
pname = "blink-cmp";
inherit version src;
# blink references a repro.lua which is placed outside the lua/ directory
doCheck = false;
preInstall = ''
mkdir -p target/release
ln -s ${blink-fuzzy-lib}/lib/libblink_cmp_fuzzy.${libExt} target/release/libblink_cmp_fuzzy.${libExt}
'';
}

View file

@ -10,10 +10,10 @@
nixConfig = import ../configuration.nix false; nixConfig = import ../configuration.nix false;
maximalConfig = import ../configuration.nix true; maximalConfig = import ../configuration.nix true;
in { in {
flake.overlays.default = _final: prev: { flake.overlays.default = final: _prev: {
inherit neovimConfiguration; inherit neovimConfiguration;
neovim-nix = buildPkg prev [nixConfig]; neovim-nix = buildPkg final [nixConfig];
neovim-maximal = buildPkg prev [maximalConfig]; neovim-maximal = buildPkg final [maximalConfig];
devPkg = buildPkg pkgs [nixConfig {config.vim.languages.html.enable = pkgs.lib.mkForce true;}]; devPkg = buildPkg pkgs [nixConfig {config.vim.languages.html.enable = pkgs.lib.mkForce true;}];
}; };
} }

View file

@ -0,0 +1,120 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption mkOption literalMD;
inherit (lib.types) listOf str either attrsOf submodule enum anything int nullOr;
inherit (lib.generators) mkLuaInline;
inherit (lib.nvim.types) mkPluginSetupOption luaInline;
inherit (lib.nvim.binds) mkMappingOption;
inherit (lib.nvim.config) mkBool;
keymapType = submodule {
freeformType = attrsOf (listOf (either str luaInline));
options = {
preset = mkOption {
type = enum ["default" "none" "super-tab" "enter"];
default = "none";
description = "keymap presets";
};
};
};
providerType = submodule {
freeformType = anything;
options = {
module = mkOption {
type = str;
description = "module of the provider";
};
};
};
in {
options.vim.autocomplete.blink-cmp = {
enable = mkEnableOption "blink.cmp";
setupOpts = mkPluginSetupOption "blink.cmp" {
sources = {
default = mkOption {
type = listOf str;
default = ["lsp" "path" "snippets" "buffer"];
description = "Default list of sources to enable for completion.";
};
cmdline = mkOption {
type = nullOr (listOf str);
default = [];
description = "List of sources to enable for cmdline. Null means use default source list.";
};
providers = mkOption {
type = attrsOf providerType;
default = {};
description = "Settings for completion providers";
};
transform_items = mkOption {
type = nullOr luaInline;
default = mkLuaInline "function(_, items) return items end";
defaultText = ''
Our default does nothing. If you want blink.cmp's default, which
lowers the score for snippets, set this option to null.
'';
description = ''
Function to use when transforming the items before they're returned
for all providers.
'';
};
};
completion = {
documentation = {
auto_show = mkBool true "Show documentation whenever an item is selected";
auto_show_delay_ms = mkOption {
type = int;
default = 200;
description = "Delay before auto show triggers";
};
};
};
keymap = mkOption {
type = keymapType;
default = {};
description = "blink.cmp keymap";
example = literalMD ''
```nix
vim.autocomplete.blink-cmp.setupOpts.keymap = {
preset = "none";
"<Up>" = ["select_prev" "fallback"];
"<C-n>" = [
(lib.generators.mkLuaInline ''''
function(cmp)
if some_condition then return end -- runs the next command
return true -- doesn't run the next command
end,
'''')
"select_next"
];
};
```
'';
};
fuzzy = {
prebuilt_binaries = {
download = mkBool false ''
Auto-downloads prebuilt binaries. Do not enable, it doesn't work on nix
'';
};
};
};
mappings = {
complete = mkMappingOption "Complete [blink.cmp]" "<C-Space>";
confirm = mkMappingOption "Confirm [blink.cmp]" "<CR>";
next = mkMappingOption "Next item [blink.cmp]" "<Tab>";
previous = mkMappingOption "Previous item [blink.cmp]" "<S-Tab>";
close = mkMappingOption "Close [blink.cmp]" "<C-e>";
scrollDocsUp = mkMappingOption "Scroll docs up [blink.cmp]" "<C-d>";
scrollDocsDown = mkMappingOption "Scroll docs down [blink.cmp]" "<C-f>";
};
};
}

View file

@ -0,0 +1,91 @@
{
lib,
config,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.strings) optionalString;
inherit (lib.generators) mkLuaInline;
inherit (lib.nvim.lua) toLuaObject;
inherit (builtins) concatStringsSep typeOf tryEval attrNames mapAttrs;
cfg = config.vim.autocomplete.blink-cmp;
cmpCfg = config.vim.autocomplete.nvim-cmp;
inherit (cfg) mappings;
getPluginName = plugin:
if typeOf plugin == "string"
then plugin
else if (plugin ? pname && (tryEval plugin.pname).success)
then plugin.pname
else plugin.name;
in {
vim = mkIf cfg.enable {
startPlugins = ["blink-compat"];
lazy.plugins = {
blink-cmp = {
package = "blink-cmp";
setupModule = "blink.cmp";
inherit (cfg) setupOpts;
# TODO: lazy disabled until lspconfig is lazy loaded
#
# event = ["InsertEnter" "CmdlineEnter"];
after = ''
${optionalString config.vim.lazy.enable
(concatStringsSep "\n" (map
(package: "require('lz.n').trigger_load(${toLuaObject (getPluginName package)})")
cmpCfg.sourcePlugins))}
'';
};
};
autocomplete = {
enableSharedCmpSources = true;
blink-cmp.setupOpts = {
sources = {
default = ["lsp" "path" "snippets" "buffer"] ++ (attrNames cmpCfg.sources);
providers =
mapAttrs (name: _: {
inherit name;
module = "blink.compat.source";
})
cmpCfg.sources;
};
snippets = mkIf config.vim.snippets.luasnip.enable {
preset = "luasnip";
};
keymap = {
${mappings.complete} = ["show" "fallback"];
${mappings.close} = ["hide" "fallback"];
${mappings.scrollDocsUp} = ["scroll_documentation_up" "fallback"];
${mappings.scrollDocsDown} = ["scroll_documentation_down" "fallback"];
${mappings.confirm} = ["accept" "fallback"];
${mappings.next} = [
"select_next"
"snippet_forward"
(mkLuaInline ''
function(cmp)
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
has_words_before = col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
if has_words_before then
return cmp.show()
end
end
'')
"fallback"
];
${mappings.previous} = [
"select_prev"
"snippet_backward"
"fallback"
];
};
};
};
};
}

View file

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

View file

@ -0,0 +1,34 @@
{
lib,
config,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.nvim.attrsets) mapListToAttrs;
inherit (builtins) typeOf tryEval;
cfg = config.vim.autocomplete;
getPluginName = plugin:
if typeOf plugin == "string"
then plugin
else if (plugin ? pname && (tryEval plugin.pname).success)
then plugin.pname
else plugin.name;
in {
config.vim = mkIf cfg.enableSharedCmpSources {
startPlugins = ["rtp-nvim"];
lazy.plugins =
mapListToAttrs (package: {
name = getPluginName package;
value = {
inherit package;
lazy = true;
after = ''
local path = vim.fn.globpath(vim.o.packpath, 'pack/*/opt/${getPluginName package}')
require("rtp_nvim").source_after_plugin_dir(path)
'';
};
})
cfg.nvim-cmp.sourcePlugins;
};
}

View file

@ -1,5 +1,9 @@
{ {
imports = [ imports = [
./module.nix
./config.nix
./nvim-cmp ./nvim-cmp
./blink-cmp
]; ];
} }

View file

@ -0,0 +1,7 @@
{lib, ...}: let
inherit (lib.options) mkEnableOption;
in {
options.vim.autocomplete = {
enableSharedCmpSources = mkEnableOption "sources shared by blink.cmp and nvim-cmp";
};
}

View file

@ -24,21 +24,7 @@
in { in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
vim = { vim = {
startPlugins = ["rtp-nvim"]; lazy.plugins = {
lazy.plugins = mkMerge [
(mapListToAttrs (package: {
name = getPluginName package;
value = {
inherit package;
lazy = true;
after = ''
local path = vim.fn.globpath(vim.o.packpath, 'pack/*/opt/${getPluginName package}')
require("rtp_nvim").source_after_plugin_dir(path)
'';
};
})
cfg.sourcePlugins)
{
nvim-cmp = { nvim-cmp = {
package = "nvim-cmp"; package = "nvim-cmp";
after = '' after = ''
@ -68,10 +54,12 @@ in {
event = ["InsertEnter" "CmdlineEnter"]; event = ["InsertEnter" "CmdlineEnter"];
}; };
} };
];
autocomplete.nvim-cmp = { autocomplete = {
enableSharedCmpSources = true;
nvim-cmp = {
sources = { sources = {
nvim-cmp = null; nvim-cmp = null;
buffer = "[Buffer]"; buffer = "[Buffer]";
@ -137,4 +125,5 @@ in {
}; };
}; };
}; };
};
} }

View file

@ -11,6 +11,7 @@
cfg = config.vim.lsp; cfg = config.vim.lsp;
usingNvimCmp = config.vim.autocomplete.nvim-cmp.enable; usingNvimCmp = config.vim.autocomplete.nvim-cmp.enable;
usingBlinkCmp = config.vim.autocomplete.blink-cmp.enable;
self = import ./module.nix {inherit config lib pkgs;}; self = import ./module.nix {inherit config lib pkgs;};
mappingDefinitions = self.options.vim.lsp.mappings; mappingDefinitions = self.options.vim.lsp.mappings;
@ -22,7 +23,7 @@
in { in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
vim = { vim = {
autocomplete.nvim-cmp = { autocomplete.nvim-cmp = mkIf usingNvimCmp {
sources = {nvim_lsp = "[LSP]";}; sources = {nvim_lsp = "[LSP]";};
sourcePlugins = ["cmp-nvim-lsp"]; sourcePlugins = ["cmp-nvim-lsp"];
}; };
@ -170,6 +171,10 @@ in {
}, },
} }
''} ''}
${optionalString usingBlinkCmp ''
capabilities = require('blink.cmp').get_lsp_capabilities()
''}
''; '';
}; };
}; };

View file

@ -10,6 +10,16 @@
cfg = config.vim.lsp; cfg = config.vim.lsp;
in { in {
config = mkIf (cfg.enable && cfg.lspSignature.enable) { config = mkIf (cfg.enable && cfg.lspSignature.enable) {
assertions = [
{
assertion = !config.vim.autocomplete.blink-cmp.enable;
message = ''
lsp-signature does not work with blink.cmp. Please use blink.cmp's builtin signature feature:
vim.autocomplete.blink-cmp.setupOpts.signature.enabled = true;
'';
}
];
vim = { vim = {
startPlugins = [ startPlugins = [
"lsp-signature" "lsp-signature"

View file

@ -8,16 +8,18 @@
inherit (lib.nvim.lua) toLuaObject; inherit (lib.nvim.lua) toLuaObject;
cfg = config.vim.lsp.lspkind; cfg = config.vim.lsp.lspkind;
usingCmp = config.vim.autocomplete.nvim-cmp.enable;
usingBlink = config.vim.autocomplete.blink-cmp.enable;
in { in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [ assertions = [
{ {
assertion = config.vim.autocomplete.nvim-cmp.enable; assertion = usingCmp || usingBlink;
message = '' message = ''
While lspkind supports Neovim's native lsp upstream, using that over While lspkind supports Neovim's native lsp upstream, using that over
nvim-cmp isn't recommended, nor supported by nvf. nvim-cmp/blink.cmp isn't recommended, nor supported by nvf.
Please migrate to nvim-cmp if you want to use lspkind. Please migrate to nvim-cmp/blink.cmp if you want to use lspkind.
''; '';
} }
]; ];
@ -26,9 +28,19 @@ in {
startPlugins = ["lspkind"]; startPlugins = ["lspkind"];
lsp.lspkind.setupOpts.before = config.vim.autocomplete.nvim-cmp.format; lsp.lspkind.setupOpts.before = config.vim.autocomplete.nvim-cmp.format;
autocomplete.nvim-cmp.setupOpts.formatting.format = mkForce (mkLuaInline '' autocomplete = {
nvim-cmp = mkIf usingCmp {
setupOpts.formatting.format = mkForce (mkLuaInline ''
require("lspkind").cmp_format(${toLuaObject cfg.setupOpts}) require("lspkind").cmp_format(${toLuaObject cfg.setupOpts})
''); '');
}; };
blink-cmp = mkIf usingBlink {
setupOpts.appearance.kind_icons = mkLuaInline ''
require("lspkind").symbol_map
'';
};
};
};
}; };
} }

View file

@ -7,6 +7,8 @@
inherit (lib.types) bool str nullOr; inherit (lib.types) bool str nullOr;
inherit (lib.modules) mkRenamedOptionModule; inherit (lib.modules) mkRenamedOptionModule;
inherit (lib.nvim.types) mkPluginSetupOption; inherit (lib.nvim.types) mkPluginSetupOption;
autocompleteCfg = config.vim.autocomplete;
in { in {
imports = let imports = let
renamedSetupOption = oldPath: newPath: renamedSetupOption = oldPath: newPath:
@ -42,7 +44,7 @@ in {
# If using nvim-cmp, otherwise set to false # If using nvim-cmp, otherwise set to false
type = bool; type = bool;
description = "If using nvim-cmp, otherwise set to false"; description = "If using nvim-cmp, otherwise set to false";
default = config.vim.autocomplete.nvim-cmp.enable; default = autocompleteCfg.nvim-cmp.enable || autocompleteCfg.blink-cmp.enable;
}; };
}; };
}; };

View file

@ -20,7 +20,7 @@ in {
after = cfg.loaders; after = cfg.loaders;
}; };
startPlugins = cfg.providers; startPlugins = cfg.providers;
autocomplete.nvim-cmp = { autocomplete.nvim-cmp = mkIf config.vim.autocomplete.nvim-cmp.enable {
sources = {luasnip = "[LuaSnip]";}; sources = {luasnip = "[LuaSnip]";};
sourcePlugins = ["cmp-luasnip"]; sourcePlugins = ["cmp-luasnip"];
}; };

View file

@ -20,7 +20,8 @@ in {
vim = { vim = {
startPlugins = ["nvim-treesitter"]; startPlugins = ["nvim-treesitter"];
autocomplete.nvim-cmp = { # cmp-treesitter doesn't work on blink.cmp
autocomplete.nvim-cmp = mkIf config.vim.autocomplete.nvim-cmp.enable {
sources = {treesitter = "[Treesitter]";}; sources = {treesitter = "[Treesitter]";};
sourcePlugins = ["cmp-treesitter"]; sourcePlugins = ["cmp-treesitter"];
}; };

View file

@ -61,6 +61,7 @@
"flutter-tools.dev_tools" "flutter-tools.dev_tools"
]; ];
}; };
inherit (pkgs) blink-cmp;
}; };
buildConfigPlugins = plugins: buildConfigPlugins = plugins: