-
-
Notifications
You must be signed in to change notification settings - Fork 326
lib/modules: init mkBlinkPluginModule
#2981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e354aeb
a9b720f
75900cc
2b22033
e1348ae
3c266ed
999d81c
2b3e511
802e289
58cead7
003c96f
8403bff
b4bdcc8
0b5e153
513e154
29c208e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ let | |
applyExtraConfig = "It has been moved to `lib.plugins.utils`"; | ||
mkConfigAt = "It has been moved to `lib.plugins.utils`"; | ||
}; | ||
internal = lib.mkOption { internal = true; }; | ||
in | ||
{ | ||
# Evaluate nixvim modules, checking warnings and assertions | ||
|
@@ -52,6 +53,282 @@ in | |
helpers = self; | ||
} // extraSpecialArgs; | ||
}; | ||
|
||
# Create a module configuring a plugin's integration with blink.cmp | ||
mkBlinkPluginModule = | ||
{ | ||
# The plugin's option location-path | ||
loc ? [ | ||
"plugins" | ||
pluginName | ||
], | ||
# Name of the plugin, used in documentation | ||
pluginName, | ||
# Name of the module blink should import | ||
# i.e. `sources.providers.<name>.module` | ||
module ? pluginName, | ||
# The default for `blink.settings.name` | ||
# i.e. `sources.providers.<name>.name` | ||
# TODO: consider doing some pre-processing to the default source name, | ||
# e.g. removing `-cmp` or `blink-` prefix/suffix? | ||
sourceName, | ||
# The default for `blink.key` | ||
# i.e. the attr name for `sources.providers.<name>` | ||
key ? lib.strings.toLower sourceName, | ||
# Whether to enable the blink completion provider by default | ||
enableProvider ? true, | ||
# Defaults for the corresponding source options | ||
enableDefault ? true, | ||
enableCmdline ? false, | ||
enabledFiletypes ? { }, | ||
# Whether the plugin's settings should be used as the provider's `opts` | ||
usePluginSettings ? true, | ||
settingsExample ? { | ||
score_offset = -7; | ||
fallbacks = [ ]; | ||
}, | ||
}: | ||
{ config, options, ... }: | ||
let | ||
pluginCfg = lib.getAttrFromPath loc config; | ||
cfg = pluginCfg.blink; | ||
pluginOpts = lib.getAttrFromPath loc options; | ||
opt = pluginOpts.blink; | ||
in | ||
{ | ||
options = lib.setAttrByPath loc { | ||
blink = { | ||
enable = lib.mkOption { | ||
type = lib.types.bool; | ||
default = enableProvider; | ||
example = !enableProvider; | ||
description = '' | ||
Whether to integrate this plugin with blink.cmp. | ||
''; | ||
}; | ||
key = lib.mkOption { | ||
type = lib.types.str; | ||
default = key; | ||
description = '' | ||
The key to use for ${pluginName}'s blink.cmp provider. | ||
This is the id you should use when including this provider in completion source lists. | ||
Must be unique. | ||
''; | ||
}; | ||
default = lib.mkOption { | ||
type = lib.types.bool; | ||
default = enableDefault; | ||
example = !enableDefault; | ||
description = '' | ||
Whether to include this plugin in the `default` completion source list. | ||
''; | ||
}; | ||
cmdline = lib.mkOption { | ||
type = lib.types.bool; | ||
default = enableCmdline; | ||
example = !enableCmdline; | ||
description = '' | ||
Whether to include this plugin in the `cmdline` completion source list. | ||
''; | ||
}; | ||
filetypes = lib.mkOption { | ||
type = lib.types.attrsOf lib.types.bool; | ||
# Only include `true` attrs in the final value | ||
apply = lib.filterAttrs (_: lib.id); | ||
default = enabledFiletypes; | ||
# TODO: example | ||
description = '' | ||
Whether to include this plugin in the specific `per_filetype` completion source lists. | ||
''; | ||
}; | ||
settings = lib.mkOption { | ||
default = { }; | ||
description = '' | ||
Settings for the blink.cmp completion provider. | ||
''; | ||
example = settingsExample; | ||
type = lib.types.submodule [ | ||
{ | ||
options.enabled = internal; | ||
options.module = internal; | ||
} | ||
../plugins/by-name/blink-cmp/provider-config.nix | ||
]; | ||
}; | ||
}; | ||
}; | ||
config = lib.mkMerge [ | ||
(lib.setAttrByPath loc { | ||
# NOTE: this could be defined within the `blink.settings` submodule, | ||
# but that would not populate the option's `definitions` list. | ||
# Meaning we wouldn't be able to propagate the definitions further using `mkAliasDefinitions`. | ||
blink.settings = { | ||
name = lib.mkDefault sourceName; | ||
inherit module; | ||
opts = lib.mkIf usePluginSettings (lib.modules.mkAliasDefinitions pluginOpts.settings); | ||
}; | ||
}) | ||
(lib.mkIf (pluginCfg.enable && cfg.enable) { | ||
plugins.blink-cmp.settings.sources = { | ||
# Use mkAliasDefinitions to preserve override priorities | ||
providers.${cfg.key} = lib.modules.mkAliasDefinitions opt.settings; | ||
default = lib.mkIf cfg.default [ cfg.key ]; | ||
# FIXME: the reference shows `cmdline` should/could be defined as a function | ||
# https://cmp.saghen.dev/configuration/reference.html#sources | ||
cmdline = lib.mkIf cfg.cmdline [ cfg.key ]; | ||
per_filetype = lib.mkIf (cfg.filetypes != { }) ( | ||
builtins.mapAttrs (_: _: [ cfg.key ]) cfg.filetypes | ||
); | ||
}; | ||
warnings = lib.nixvim.mkWarnings (lib.showOption loc) { | ||
when = !config.plugins.blink-cmp.enable && options.plugins.blink-cmp.enable.highestPrio == 1500; | ||
message = '' | ||
You have enabled the blink.cmp provider, but `plugins.blink-cmp` is not enabled. | ||
You can suppress this warning by explicitly setting `plugins.blink-cmp.enable = false`. | ||
''; | ||
}; | ||
}) | ||
]; | ||
}; | ||
|
||
# Create a module configuring a plugin's integration with nvim-cmp and blink.cmp | ||
mkCmpPluginModule = | ||
{ | ||
# The plugin's option location-path | ||
loc ? [ | ||
"plugins" | ||
pluginName | ||
], | ||
# Name of the plugin, used in documentation | ||
pluginName, | ||
# The nvim-cmp source name | ||
# TODO: can we compute a sane default for sourceName? | ||
sourceName, | ||
# Defaults for the corresponding cmp options | ||
enableDefault ? true, | ||
enableCmdline ? { }, | ||
enabledFiletypes ? { }, | ||
# Whether to include a `blink` option at all | ||
offerBlinkCompatibility ? true, | ||
# Defaults for the blink compatibility option | ||
enableBlinkProvider ? false, | ||
enableBlinkDefault ? false, | ||
enableBlinkCmdline ? false, | ||
enabledBlinkFiletypes ? { }, | ||
Comment on lines
+215
to
+217
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just use the corresponding arguments above ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you're asking. These are probably badly named, but they're intended to be the default values for the corresponding module options. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have enableDefault, enabldeCmdline, and enableFiletypes already in this argument list. I'm asking why they are special blink versions for the same name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah. Because it felt unlikely that enabling a plugin as a nvim-cmp source by default should also enable it as a blink source by default. I was thinking that a plugin maintainer should manually make that decision. We could have these defaults to the nvim-cmp equivalent, but I think it should still be possible to set these separately? |
||
# Whether the plugin's settings should be used as the blink provider's `opts` | ||
usePluginSettingsForBlink ? false, | ||
# The key to use with blink, | ||
# i.e. the attr name for `sources.providers.<name>` | ||
# TODO: should this default to pluginName or sourceName? | ||
blinkProviderKey ? lib.strings.toLower pluginName, | ||
}: | ||
{ config, options, ... }: | ||
let | ||
pluginOpt = lib.getAttrFromPath loc options; | ||
pluginCfg = lib.getAttrFromPath loc config; | ||
blinkOpt = pluginOpt.blink; | ||
blinkCfg = pluginCfg.blink; | ||
cfg = pluginCfg.cmp; | ||
toSourceDef = v: lib.optionalAttrs (builtins.isAttrs v) v // { inherit (cfg) name; }; | ||
toSources = v: { sources = [ (toSourceDef v) ]; }; | ||
in | ||
{ | ||
imports = lib.optionals offerBlinkCompatibility [ | ||
(lib.nixvim.modules.mkBlinkPluginModule { | ||
inherit loc pluginName sourceName; | ||
key = blinkProviderKey; | ||
module = "blink.compat.source"; | ||
enableProvider = enableBlinkProvider; | ||
enableDefault = enableBlinkDefault; | ||
enableCmdline = enableBlinkCmdline; | ||
enabledFiletypes = enabledBlinkFiletypes; | ||
usePluginSettings = usePluginSettingsForBlink; | ||
}) | ||
{ | ||
options = lib.setAttrByPath loc { | ||
blink.settings.name = internal; | ||
}; | ||
config = lib.mkIf (pluginCfg.enable && blinkCfg.enable) { | ||
# Enable blink-compat if the plugin has `blink.enable = true` | ||
plugins.blink-compat.enable = true; | ||
# This warning will show if someone overrides `plugins.blink-compat.enable = mkForce false` | ||
warnings = lib.nixvim.mkWarnings (lib.showOption loc) { | ||
when = !config.plugins.blink-compat.enable; | ||
message = '' | ||
`${blinkOpt.enable}` is enabled, but `${options.plugins.blink-compat.enable}` is not. | ||
This plugin is a nvim-cmp source, so it requires blink.compat when used with blink.cmp. | ||
''; | ||
}; | ||
}; | ||
} | ||
]; | ||
options = lib.setAttrByPath loc { | ||
cmp = { | ||
enable = lib.mkOption { | ||
type = lib.types.bool; | ||
default = true; | ||
example = false; | ||
description = '' | ||
Whether to integrate this plugin with nvim-cmp. | ||
''; | ||
}; | ||
name = lib.mkOption { | ||
type = lib.types.str; | ||
default = sourceName; | ||
description = "${pluginName}'s nvim-cmp source name."; | ||
internal = true; | ||
}; | ||
default = lib.mkOption { | ||
type = with lib.types; either bool (attrsOf anything); | ||
default = enableDefault; | ||
example = !enableDefault; | ||
description = '' | ||
Whether to include this plugin in the `default` completion source list. | ||
|
||
Can be defined as attrs to pass additional config to the source. | ||
''; | ||
}; | ||
cmdline = lib.mkOption { | ||
type = with lib.types; attrsOf (either bool (attrsOf anything)); | ||
# Remove false attrs in the final value | ||
apply = lib.filterAttrs (_: v: v != false); | ||
default = enableCmdline; | ||
# TODO: example | ||
description = '' | ||
Whether to include this plugin in the specific `cmdline` completion source lists. | ||
|
||
Elements can be defined as attrs to pass additional config to the source. | ||
''; | ||
}; | ||
filetypes = lib.mkOption { | ||
type = with lib.types; attrsOf (either bool (attrsOf anything)); | ||
# Remove false attrs in the final value | ||
apply = lib.filterAttrs (_: v: v != false); | ||
default = enabledFiletypes; | ||
# TODO: example | ||
description = '' | ||
Whether to include this plugin in the specific `per_filetype` completion source lists. | ||
|
||
Elements can be defined as attrs to pass additional config to the source. | ||
''; | ||
}; | ||
}; | ||
}; | ||
config = lib.mkIf (pluginCfg.enable && cfg.enable) { | ||
plugins.cmp = { | ||
settings = lib.mkIf (cfg.default != false) (toSources cfg.default); | ||
cmdline = lib.mkIf (cfg.cmdline != { }) (builtins.mapAttrs (_: toSources) cfg.cmdline); | ||
filetype = lib.mkIf (cfg.filetypes != { }) (builtins.mapAttrs (_: toSources) cfg.filetypes); | ||
}; | ||
warnings = lib.nixvim.mkWarnings (lib.showOption loc) { | ||
when = !config.plugins.cmp.enable && options.plugins.cmp.enable.highestPrio == 1500; | ||
message = '' | ||
You have enabled the nvim-cmp source, but `plugins.cmp` is not enabled. | ||
You can suppress this warning by explicitly setting `plugins.cmp.enable = false`. | ||
''; | ||
}; | ||
}; | ||
}; | ||
} | ||
// lib.mapAttrs ( | ||
name: msg: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,30 @@ | ||
{ config, lib, ... }: | ||
lib.nixvim.plugins.mkNeovimPlugin { | ||
let | ||
name = "blink-cmp-copilot"; | ||
package = "blink-cmp-copilot"; | ||
in | ||
lib.nixvim.plugins.mkNeovimPlugin { | ||
inherit name; | ||
|
||
maintainers = [ lib.maintainers.HeitorAugustoLN ]; | ||
|
||
description = '' | ||
This plugin should be configured through blink-cmp's source settings. | ||
|
||
For example: | ||
callSetup = false; | ||
hasLuaConfig = false; | ||
|
||
```nix | ||
plugins.blink-cmp = { | ||
enable = true; | ||
settings.sources = { | ||
copilot = { | ||
async = true; | ||
module = "blink-cmp-copilot"; | ||
name = "copilot"; | ||
score_offset = 100; | ||
}; | ||
imports = [ | ||
(lib.nixvim.modules.mkBlinkPluginModule { | ||
pluginName = name; | ||
# TODO: compute a sane-default source name | ||
sourceName = "copilot"; | ||
settingsExample = { | ||
async = true; | ||
score_offset = 100; | ||
Comment on lines
+13
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we could streamline this a little, by having a When present, mkNeovimPlugin could pass the argument to mkBlinkPluginModule, appending some basics like |
||
}; | ||
}; | ||
``` | ||
|
||
And then you can add it as a source for blink-cmp: | ||
}) | ||
]; | ||
|
||
```nix | ||
plugins.blink-cmp = { | ||
enable = true; | ||
settings.sources.default = [ | ||
"lsp" | ||
"path" | ||
"luasnip" | ||
"buffer" | ||
"copilot" | ||
]; | ||
}; | ||
``` | ||
''; | ||
|
||
callSetup = false; | ||
hasLuaConfig = false; | ||
hasSettings = false; | ||
settingsExample = { | ||
max_completions = 3; | ||
}; | ||
|
||
extraConfig = { | ||
warnings = | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't work the way we need it to to support blink's configuration.
Generates an invalid config:
I think we'd want to change it to support settings going to the top level and needing to pass
opts
in thesettings
attribute set.https://cmp.saghen.dev/configuration/sources.html#provider-options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... so this looks like we should inherit
pluginOpts.settings
for the known top level options and then opts inherits frompluginOpts.settings.opts
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plugins.*.settings
has always represented thesetup
function'sopts
, so I've attempted to be consistent with that here.The only difference is that blink invokes the
setup
function for us. But that's an implementation detail that is encapsulated from users in both normal and blink plugins...Provider settings can still be configured directly, via
plugins.*.blink.settings
.It was intentional to keep these separate, but I can see how it could be confusing if you take a different perspective to me.
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't make this feel like a gain UX wise if they need to split their provider configuration between 2 modules, though. Personally, I'd just keep my configuration through
blink-cmp
so I wouldn't have to jump back and forth between 2 attribute sets to see what i'm doing. It is more niche to actually passopts
to the provider than to declare the top level provider options.I'd perfer providing the top level options to each blink plugin if we want to keep
settings
forwarded specifically toopts
. So someone can choose to configure their provider from each source plulgin module itself.