-
-
Notifications
You must be signed in to change notification settings - Fork 269
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
plugins/lazy.nvim: switch to mkNeovimPlugin #2082
base: main
Are you sure you want to change the base?
Conversation
5a142a3
to
2205937
Compare
2ca79f3
to
8230165
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
85482f0
to
4473f5c
Compare
pluginType
freeform71f83e8
to
5a7b6c0
Compare
Seems there's two errors being picked up by CI currently, both part of the
And
|
Hmmm I did see that and I think I have some ideas on how to fix it. Do you know how to run just the test-17 group? This doesn't work |
You can do If you enter a devshell, you can also use our Further, if you don't want to use a devshell, you can still get at individual tests within a group by using the E.g. |
1e52f22
to
a128e90
Compare
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.
So I was able to simplify the
pluginToLua
function, I'll add that as a temporary commit to this PR, and if it's deemed as appropriate for this PR itself I can squash it but if not I'll move it into it's own separate PR
Looks much better. I think it makes sense as part of this PR.
I only really went over this part of the PR in this review, but I'll try and find time to fully review soon as everything seems to be coming together 😀
4170261
to
0bb1ce7
Compare
0bb1ce7
to
d58c919
Compare
d58c919
to
bb8135f
Compare
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.
I'm sure @MattSturgeon will have better recommendations for wording of descriptions
plugins/pluginmanagers/lazy.nix
Outdated
''; | ||
|
||
enabled = defaultNullOpts.mkStrLuaFnOr bool "`true`" '' | ||
When false then this plugin will not be included in the spec. (accepts fun():boolean) |
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.
When false then this plugin will not be included in the spec. (accepts fun():boolean) | |
When false then this plugin will not be included in the spec. |
The type will explain this
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.
The type won't fully explain this; it won't show the expected function signature (i.e. no parameters -> returns a bool)
plugins/pluginmanagers/lazy.nix
Outdated
mkNullOrOption (maybeRaw ( | ||
either str (listOf str) | ||
)) "Lazy-load on event. Events can be specified as BufEnter or with a pattern like BufEnter *.lua"; | ||
|
||
cmd = mkNullOrOption (maybeRaw (either str (listOf str))) "Lazy-load on command"; | ||
|
||
ft = mkNullOrOption (maybeRaw (either str (listOf str))) "Lazy-load on filetype"; | ||
|
||
keys = mkNullOrOption (maybeRaw (either str (listOf str))) "Lazy-load on key mapping"; |
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.
These could all do with examples, probably..
plugins/pluginmanagers/lazy.nix
Outdated
|
||
mkNullOrOption (maybeRaw (attrsOf anything)) '' | ||
opts should be a table (will be merged with parent specs), | ||
return a table (replaces parent specs) or should change a table. | ||
The table will be passed to the Plugin.config() function. | ||
Setting this value will imply Plugin.config() | ||
''; |
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.
mkNullOrOption (maybeRaw (attrsOf anything)) '' | |
opts should be a table (will be merged with parent specs), | |
return a table (replaces parent specs) or should change a table. | |
The table will be passed to the Plugin.config() function. | |
Setting this value will imply Plugin.config() | |
''; | |
mkNullOrOption (maybeRaw (attrsOf anything)) '' | |
The opts value can be one of the following: | |
- A table: This table will be merged with any existing configuration settings from parent specifications. | |
- A function that returns a table: The returned table will completely replace any existing configuration settings from parent specifications. | |
- A function that modifies a table: This function will receive the existing configuration table as an argument and can modify it directly. | |
In all cases, the resulting configuration table will be passed to the Plugin.config() function. | |
Setting the opts value automatically implies that Plugin.config() will be called. | |
''; |
I think this might make it more clear what is occurring? Maybe we should link to their docs, too?
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 that's a lot easier to understand compared to the offical docs. I will add that on an unrelated note how are you wrapping your comments, just gw
(gq
doesn't work for me) in a blank file? When I do gw
for the comment I think it's taking the indentation into account so each line becomes shorter so instead of:
Optional specs are only included in the final configuration if the
corresponding plugin is also specified as a required (non-optional) plugin
elsewhere. This feature is particularly helpful for Neovim distributions,
allowing them to pre-configure settings for plugins that users may or may not
have installed.
I get:
Optional specs are only included in the final configuration
if the corresponding plugin is also specified as a required
(non-optional) plugin elsewhere. This feature is
particularly helpful for Neovim distributions, allowing
them to pre-configure settings for plugins that users may
or may not have installed.
Or is what i'm getting what it's supposed to be?
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.
That sounds like lazy.nvim is telling us this option can be used to configure plugins that may or may not be loaded.
It is saying that a different part of lazy's configuration is responsible for saying what/when plugins are loaded.
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.
Re: gw
and gq
, I generally wrap manually. I usually try to wrap semantically; preferring line breaks at punctuation or the end of a phrase if possible. Unless it looks too silly and unbalanced.
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.
That sounds like lazy.nvim is telling us this option can be used to configure plugins that may or may not be loaded.
It is saying that a different part of lazy's configuration is responsible for saying what/when plugins are loaded.
Yup essentially. So if the plugin isn't installed lazy.nvim
won't fetch it, and if it is it won't load unless another spec tags it as not optional and defines the conditions for it to be loaded (or the other spec has enabled = true
)
Thanks for taking this module on, it looks like a beast of one! |
How's this going functionally? Are you still running into issues getting the correct store paths assigned to plugin |
Functionally everything works and I'm not running into those issues anymore (#2082 (comment) the fix was this #2082 (comment)) , and we don't need to update/overlay nixpkgs to get this PR to work (I still think it's worthwhile doing regardless). I was going to add an example of how to set up LazyVim in I guess right now the only things I'd need help with is the review, and any rewording of the docs :) I'll reference any lines or blocks that I'd want input on as well. I’ll also explicitly be adding some settings options that I think will help for visibility |
plugins/pluginmanagers/lazy.nix
Outdated
# it shouldn't do any harm but it does take up unecessary space in the | ||
# init.lua file. Since we're done using it we will strip it from the | ||
# final list of specs. | ||
removePkgAttrFromPlugin = |
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.
So I tried to have the pkg
attribute be stripped around here
nixvim/plugins/pluginmanagers/lazy.nix
Line 162 in bb8135f
config.name = lib.mkIf (cfg.pkg != null) (lib.mkDefault "${lib.getName cfg.pkg}"); |
config.pkg = lib.mkIf (config.pkg != null) lib.mkDefault null
but that leads to infinite recursion. Is there a better way to strip the pkg attribute after it's done being used or is this approach fine?
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.
Have you double checked nixvim-print-init
? By default toLuaObject
filters out any null or empty items.
Yes, this is a horrible hack, but it's currently the only reasonable way we have to filter out null sub-options from freeform submodules...
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.
So pkg
can be not null because a user can specify a package using the pkg
attribute. lazy.nvim
doesn't have a pkg
attribute for plugin specs, it's just something we have for convenience to map nixpkgs to the lazy.nvim
s dir
property. So all pkg
attributes should be set to null after they've been mapped to the dir
attribute. Currently I'm doing that at the end via removePkgAttrFromPlugins
at the end in extraConfig
but was just wondering if there's a better way to do this
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.
I'll have a look at the actual code a bit later, but it sounds like we need a builtins.removeAttrs plugin [ "pkg" ]
somewhere when we convert cfg.plugins
-> settings.spec
.
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.
Yup pretty much. I tried doing the conversion in cfg.plugins
after the dir
attribute was set but that didn't work and lead to infinite recursion
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.
My view is (unlike settings
) plugins
isn't aiming to be a 1-to-1 mapping of the upstream config format.
Instead it is an alternative (nicer) way to define settings.spec
, whatever tranformations need to be done to it should be done when defining plugins.lazy.settings.spec
.
This means users will have a predictable value at config.plugins.lazy.plugins
if they want to read that in their own modules for any reason.
Dumb exercise in what's possible
To be clear, I'm not recommend you use apply
here.
But for the sake argument, if you did want to transform the value at config.plugins.plugins
to be different in some way from what users define, this could be done with either a custom option-type's merge
function (this is how coercedTo
works) or defining an apply
function when calling mkOption
.
apply
acts as a final transformation on the fully merged option value, so if I wanted to I could do something really stupid like:
{ config, lib, ... }:
{
# Declare foo as a boolean option, that converts its final value to a stupid string...
options.foo = lib.mkOption {
type = types.bool;
apply = v: "was defined as ${lib.boolToString v}!";
};
config = {
# Define foo to be true
foo = true;
assertions = [
{
# NOTE: while `foo` is defined as a bool, its value is a string!
# DO NOT DO THIS lol
assertion = config.foo == "was defined as true!";
message = "apply failed";
}
];
};
}
Note that this could be used as a sledgehammer to work around infinite recursion, but is probably an anti-pattern 9 times out of 10.
I updated the first comment with an example standalone flake that sets up |
bb8135f
to
f4df927
Compare
]; | ||
''; | ||
|
||
keys = mkNullOrOption (maybeRaw (either str (listOf str))) "Lazy-load on key mapping"; |
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.
I had issues lazy-loading on keys when I was writing an example for the docs on how to use this trigger. The type for this can be made better to, I'll try to come up with something better as it's a common use case to lazy load on keybindings
6e8e540
to
c3fdd41
Compare
plugins/pluginmanagers/lazy.nix
Outdated
config.name = lib.mkIf (cfg.pkg != null) (lib.mkDefault "${lib.getName cfg.pkg}"); | ||
config.dir = lib.mkIf (cfg.pkg != null) (lib.mkDefault "${cfg.pkg}"); |
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.
config.name = lib.mkIf (cfg.pkg != null) (lib.mkDefault "${lib.getName cfg.pkg}"); | |
config.dir = lib.mkIf (cfg.pkg != null) (lib.mkDefault "${cfg.pkg}"); | |
config = lib.mkIf (cfg.pkg != null) { | |
name = lib.mkDefault "${lib.getName cfg.pkg}"; | |
dir = lib.mkDefault "${cfg.pkg}"; | |
}; |
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.
TBH I don't think we even need separate pkg
and dir
options...
Couldn't we have a source
option that is either path package
?
We could still have a default for name, e.g:
config.name = mkOptionDefault (
if isDerivation config.source then
getName config.source
else
baseNameOf config.source
);
I'm also debating mkDefault
vs mkOptionDefault
here. The latter is lower priority (higher priority number); its the same as a default passed directly to mkOption
.
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.
We could have a source
option that is either path package
but it would have to be resolved to dir
in the end for lazy.nvim
to pick it up. Since the pluginType is also freeform, the user can still specify dir
if they want to so we'd have to deal with potential collisions when the user specifies both source
and dir
, but I mean for that case we could just do config.dir = (cfg.dir == nulll && cfg.source !=null) lib.mkDefault "${cfg.source}"
. But at that point wouldn't source just be another abstraction for pkg
?
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.
So the thing I've found out is if source
is restricted to just be of type path
url
short git url
or package
, the case that gets missed is if a user is trying to disable a plugin via a custom name.
Being able to disable a plugin by a custom name is important. One such instance of why this is important is because there are several vim plugins that get bundled together in one nix package (mini-nvim
for example that bundles in all mini modules such as mini-ai
, mini-trailspace
etc), and you won't be able to disable an individual plugin without affecting other bundled plugins if you can't do something like:
{
source = "mini.ai";
enabled = false;
}
One workaround would be to use the name
option but I feel like the intention of the name
option would then differ from upstream. Allowing source
to be just a string would mean less type validation though. I guess we can take a garbage in garbage out approach and let all the validation be handled by upstream lazy.nvim
but I'm not really sure what would be ideal here. Any thoughts?
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.
That sounds like a valid reason to have it accept any string. Makes me wonder if "source" is an appropriate name though, if it can also be a "plugin name"?
If we like the idea of a type-checked option named source, we could also have a "name" option. Bearing in mind it doesn't necessarily have to correspond 1-to-1 with upstream's plugin spec, since we can do conversion when assigning to settings.spec
.
No strong preferences here, just exploring possible options 🙂
plugins/pluginmanagers/lazy.nix
Outdated
# WARNING: Be very careful if changing the type of | ||
# `dependencies`. Choosing the wrong type may cause a stack | ||
# overflow due to infinite recursion, and it's very possible | ||
# that the test cases won't catch this problem. To be safe, | ||
# perform thorough manual testing if you do change the type of | ||
# `dependencies`. Also use `types.eitherRecursive` instead of | ||
# `types.either` here, as using just `types.either` also leads | ||
# to stack overflow. | ||
dependencies = mkNullOrOption (eitherRecursive lazyPluginType lazyPluginsListType) '' |
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 is concerning... In particular:
Also use
types.eitherRecursive
instead oftypes.either
here, as using justtypes.either
also leads to stack overflow.
eitherRecursive
and either
are usually the same thing; the former only exists for documentation purposes (and should probably be deprecated in favour of checking config.isDocs
).
Lines 98 to 99 in fc7e9b2
# Overridden when building the documentation | |
eitherRecursive = types.either; |
If eitherRecursive
has a different behaviour to either
outside of our docs, then something is very broken.
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.
I would personally prefer to implement this something like:
dependenciesType =
if config.isDocs then
# Use a stub type for documentation purposes
# We don't need to repeat all the plugin-type sub-options again in the docs
# It'd also be infinitely recursive
lib.mkOptionType {
description = "plugin submodule";
descriptionClass = "noun";
check = throw "should not be used";
merge = throw "should not be used";
}
else
lazyPluginType;
in
dependencies = mkOption {
# NOTE: use attrsOf for the LHS, because coercedTo only supports submodules on the RHS
type = types.coercedTo (attrsOf anything) lib.toList (listOf dependenciesType);
};
Note: I'm fine with non-nullable lists/attrs here and using coercedTo, because this is our option, not a direct representation of upstream's settings.
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.
Hmmm so i'm getting errors saying that isDocs
is missing. Doing ((config.isDocs or null) != null)
while it works (at least for tests) shouldn't need to be done right?
[devshell]$ tests plugins-pluginmanagers-lazy
Running 1 tests: plugins-pluginmanagers-lazy
error:
… while calling the 'derivationStrict' builtin
at /builtin/derivation.nix:9:12: (source not available)
… while evaluating derivation 'plugins-pluginmanagers-lazy'
whose name attribute is located at /nix/store/887hpp8a2i99n9jjwcvz6qkhhhqsvzkg-source/pkgs/stdenv/generic/make-derivation.nix:336:7
… while evaluating attribute 'buildCommand' of derivation 'plugins-pluginmanagers-lazy'
at /nix/store/887hpp8a2i99n9jjwcvz6qkhhhqsvzkg-source/pkgs/build-support/trivial-builders/default.nix:59:16:
58| enableParallelBuilding = true;
59| inherit buildCommand name;
| ^
60| passAsFile = [ "buildCommand" ]
(stack trace truncated; use '--show-trace' to show the full trace)
error: attribute 'isDocs' missing
at /nix/store/94m1msjw2jq997gqb38zgb2l9669xp3q-source/plugins/pluginmanagers/lazy.nix:142:26:
141| dependenciesType =
142| if config.isDocs then
| ^
143| # Use a stub type for documentation purposes
Test failure
This is currently what I temporarily have inside the submodule:
dependencies =
let
dependenciesType =
if config.isDocs then
# Use a stub type for documentation purposes
# We don't need to repeat all the plugin-type sub-options again in the docs
# It'd also be infinitely recursive
lib.mkOptionType {
description = "plugin submodule";
descriptionClass = "noun";
check = throw "should not be used";
merge = throw "should not be used";
}
else
lazyPluginType;
in
# NOTE: use attrsOf for the LHS, because coercedTo only supports submodules on the RHS
mkNullOrOption (types.coercedTo (attrsOf anything) lib.toList (listOf dependenciesType)) ''
A list of plugin names or plugin specs that should be
loaded when the plugin loads. Dependencies are always
lazy-loaded unless specified otherwise. When specifying a
name, make sure the plugin spec has been defined somewhere
else. This can also be a single string such as for a short
plugin url (See: https://lazy.folke.io/spec).
'';
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.
Looks like you're using the submodule's config
which won't have isDocs
.
The isDocs
option is declared in the programs.nixvim
submodule, so it won't exist in your submodule.
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.
Ooohh thanks! yup that fixed things! I'll test if this causes infinite recursion on my home-manager setup and I'll get the changes we talked about minus some updates to the docstrings (will get around to updating them once the implementation itself is finalized) soon ish. Feel free to still make suggestions on the docstrings though (I will get around to them 🙂)
my7h3le | ||
]; | ||
|
||
settingsOptions = with types; { |
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.
Just a reminder: settings
is a freeform option. This means we don't need to declare sub-options for everything that can be configured.
Any config definitions that don't match the options we've declared will use the freeformType attrsOf anything
.
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 these were only added purely for documentation purposes, as some of the options in the plugin type reference these settings. I can totally remove them though if it's deemed better to avoid having them 🙂
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.
I can totally remove them though if it's deemed better to avoid having them 🙂
It's always a difficult judgement call. Let's revisit this towards the end of the review process, unless anyone already has a strong opinion.
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.
I'll stash and remove them for now then, @khaneliman do you have any thoughts on this?
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.
After going over everything again, I don't think there were as many settings options as I thought. With this dropped, there's currently zero.
I'm leaning towards including at least some of them 🫡
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.
These are all the settings options lazy.nvim
has https://lazy.folke.io/configuration but only dev.fallback
, install.missing
and git.url_format
is worth including. git.url_format
may not even be worth including as it's more purely just for documentation purposes. dev.fallback
and install.missing
would be worth it as we'd be using different defaults compared to upstream, because by default we shouldn't fetch packages out out of tree but prefer to use nix packages.
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.
I always prefer having a decent amount of settings included to make it obvious what functionality can be customized without going to upstream docs. But, some plugins have too many that it becomes just pure tech debt to support all the settingsOptions.
c3fdd41
to
f3d67a5
Compare
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.
Apologies if I seem a bit confused and/or inconsistent in this review. This is quite a complex plugin (and refactor) to wrap my head around!
plugins/pluginmanagers/lazy.nix
Outdated
|
||
let | ||
cfg = config; | ||
in |
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.
let | |
cfg = config; | |
in |
For the sake of 3 chars, let's not alias this argument. Sometimes abstractions and indirection just add unnecessary complexity 😉
plugins/pluginmanagers/lazy.nix
Outdated
gitPackage = lib.mkPackageOption pkgs "git" { | ||
nullable = true; | ||
}; | ||
lazyPluginCoercibleType = with types; either str package; |
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.
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.
So it can be just a name as well because [1]
usptream can be a name or url. If it is a name the spec has to be defined somewhere else in the list. The current implementation each element in the plugins list has the same type as [1]
upstream, so each element can be a (name | url | short git url | dir). We either make each element in the list be either package | path | url
or explicitly package | path
and if a user wants a url they have to specify it in an attribute set but personally I would prefer the former (package | path | url)
. Removing it being a name does make sense though as having a name isn't entirely useful as other properties for the plugin would have to be defined later in the list with an attribute set
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.
Removing it being a name does make sense though as having a name isn't entirely useful as other properties for the plugin would have to be defined later in the list with an attribute set
Good observation. I agree it makes no sense to coerce a name string to an attrset if a name definition without any other config is effectively useless. If a user really wanted to, they could still do it manually with settings.spec
or plugins.*.name
(if we have/keep that).
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.
Removing it being a name does make sense though as having a name isn't entirely useful as other properties for the plugin would have to be defined later in the list with an attribute set
Good observation. I agree it makes no sense to coerce a name string to an attrset if a name definition without any other config is effectively useless. If a user really wanted to, they could still do it manually with
settings.spec
orplugins.*.name
(if we have/keep that).
I think keeping plugins.*.name
makes sense in case the user wants to use a very custom name that is arbitrary, but the plugins.*.source
option will just set a default value for name.
Edit: actually it might not make sense to have plugins.*.name
reading the spec again https://lazy.folke.io/spec
plugins.*.name
is just a display name and or the directory name (when lazy.nvim fetches it from git) it doesn't actually change what name you pass to require(<plugin-name>)
. So I think it does make sense to remove plugins.*.name as well
cfg = config; | ||
in | ||
{ | ||
freeformType = attrsOf anything; |
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.
freeformType = attrsOf anything; |
Freeform types are great for dealing with arbitrary config, but I'm skeptical we should have one in a custom option where we're re-defining the interface to be more nix-friendly?
Not a strong opinion atm, more a gut feeling.
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.
So there are a fair amount of plugin spec options we don't explicitly implement like import
specs
version
branch
etc. https://lazy.folke.io/spec Removing the freeformType would mean we'd have to explicitly implement at least some of those options (the import
plugin spec option is important). Also implementing specs
(each plugin can have a specs option) ourselves might be annoying
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.
What feels weird to me is that this is simultaneously a freeform type, where users expect their definitions to be used verbatim, and also a wrapper abstraction on top of settings.spec
.
The two seem contradictory to me.
We could go "old-school" and have an extraOptions
option, but that feels like a bad solution...
It'll probably be fine if we just document the transformation clearly, but it still feels off.
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.
So can the plugin type itself have extraOptions
so would it look something like this?
plugins.lazy.plugins = with pkgs.vimPlugins; [
{
source = vim-closer;
# Extra options used when user wants to pass additional definitions verbatim to the plugin spec?
extraOptions = { import "vim-closer"; };
}
]
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.
Yes, if we declare options.extraOptions = mkOption { type = attrsOf anything; }
instead of a freeformType.
As I said, it's a bit old school...
plugins/pluginmanagers/lazy.nix
Outdated
else if lib.isString plugin then | ||
{ __unkeyed = lib.mkDefault plugin; } |
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.
Oh, I see. You were using str
to mean the plugin name? I'm not sure I'm a fan of this. Because derivations are string-like types this could get confusing fast.
I think it best to either do no coercion at all, and always expect a submodule definition, or to only coerce one type (e.g. types.path
).
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.
So at the time I didn't know I could have a url type, so I just used str as a catch all for either (name | dir | url | short git plugin-url ) 😅
plugins/pluginmanagers/lazy.nix
Outdated
__unkeyed = mkNullOrOption str '' | ||
The "__unkeyed" attribute can either be one of: | ||
|
||
- a local plugin directory path. | ||
- a full plugin url. | ||
- a short plugin url. | ||
- a custom name for a plugin. |
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.
If this is a custom submodule in a custom option, there's no need to be making users define unkeyed options.
Let's give this a useful name and then convert it later when assigning this option's value to settings.spec
.
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.
IMO if the plugins
option simply matches the schema of settings.spec
, without offering any abstractions, then it is redundant and should be replaced with a rename alias pointing to settings.spec
.
plugins/pluginmanagers/lazy.nix
Outdated
dir = mkNullOrOption str '' | ||
A directory pointing to a local plugin path e.g. "~/plugins/trouble.nvim". | ||
''; |
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.
I'm confused. Both dir
and __unkeyed
say they can be the path to a directory where the plugin is found... Are they the same thing?
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.
Ah I see, you had url
pkg
and dir
all mapping internally to __unkeyed
if defined?
In this case I think we just remove the __unkeyed
option from this submodule and do that kinda conversion later, when defining settings.spec
.
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 it's weird upstream [1]
or __unkeyed
in our case is a catch all almost as it can be a name, url, short git url, or a directory https://lazy.folke.io/spec. I don't internally map url, dir, pkg to __unkeyed, but rather lazy.nvim
handles all the different cases for __unkeyed
or [1]
, and if a dir
or url
is given those get passed directly to lazy.nvim
as well and lazy.nvim
decides what too pick (hopefully I explained that somewhat well)
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.
But speaking of if we were to remove __unkeyed
and internally map dir
, name
, url
to __unkeyed
at the end when the lua conversion is about to occur that could be cleaner. That would also mean we can have a source
option and omit the dir
, pkg
and url
options (it would differ from upstream but I'm assuming that is fine). source
at the end before the conversion would get mapped to __unkeyed
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.
EDIT for clarity: this was written before seeing the above comment.
Gotcha. So these options all map to upstream options, but there's some redundancy?
I think we need to better understand how upstream handles these before finalizing a design here.
As for __unkeyed
I feel like it can be named something better if this is an option we're intentionally doing transformations to, since our usual rules of "pass exactly what the user configured in settings
to setup
" don't really apply here. 🤔
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 they're all upstream options, lazy.nvim
. But instead of redundancy I think they did it more for convenience. It's convenient in lua to just pass a (url | short git url | dir | name) as just one argument to [1]
, and I think they added explicit dir
url
options for mild performance gains as in if a user uses dir
for example, then don't bother parsing and determining if it's a directory/url/name
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.
I think getting rid of unkeyed and making it so each element in the plugins list is either path | url | package
or lazyPluginType
where lazyPluginType is the submodule type
plugins/pluginmanagers/lazy.nix
Outdated
return a table (replaces parent specs) or should change a table. | ||
The table will be passed to the Plugin.config() function. | ||
Setting this value will imply Plugin.config() | ||
pkg = mkNullOrOption package "Vim plugin to install"; |
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.
I think we can combine the various "source" options into a single one. Or at the very least fewer.
First let's double-check if types.path
is in fact a superset of types.package
, if so that simplifies things. Especially if the un-stringified derivation attrs survive the type merging process.
We can also use a "url" type if needed, types.strMatching "https?://.*"
for example.
I'm imaging something like:
source = mkOption {
type = with types; oneOf [
path
package
(strMatching "https?://.*")
];
}
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.
I'm open to discussion on this. Is there benefit to having multiple? Or do they map to several options in the upstream plugin spec?
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.
So types.package
seems to be distinct and not a subest of types.path
https://github.com/hsjobeki/nixpkgs/blob/4c181b62b7615af4cd0d139d5b82019fe10015d4/lib/types.nix#L532
https://github.com/hsjobeki/nixpkgs/blob/4c181b62b7615af4cd0d139d5b82019fe10015d4/lib/types.nix#L501
path = mkOptionType {
name = "path";
descriptionClass = "noun";
check = x: isStringLike x && builtins.substring 0 1 (toString x) == "/";
merge = mergeEqualOption;
};
pathInStore = mkOptionType {
name = "pathInStore";
description = "path in the Nix store";
descriptionClass = "noun";
check = x: isStringLike x && builtins.match "${builtins.storeDir}/[^.].*" (toString x) != null;
merge = mergeEqualOption;
};
# A package is a top-level store path (/nix/store/hash-name). This includes:
# - derivations
# - more generally, attribute sets with an `outPath` or `__toString` attribute
# pointing to a store path, e.g. flake inputs
# - strings with context, e.g. "${pkgs.foo}" or (toString pkgs.foo)
# - hardcoded store path literals (/nix/store/hash-foo) or strings without context
# ("/nix/store/hash-foo"). These get a context added to them using builtins.storePath.
# If you don't need a *top-level* store path, consider using pathInStore instead.
package = mkOptionType {
name = "package";
descriptionClass = "noun";
check = x: isDerivation x || isStorePath x;
merge = loc: defs:
let res = mergeOneOption loc defs;
in if builtins.isPath res || (builtins.isString res && ! builtins.hasContext res)
then toDerivation res
else res;
};
There's also a pathInStore
option type as well.
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.
I actually like have just a single source
option and then just mapping it to __unkeyed
at the end when the conversion is about to occur. Despite it being different than upstream it would be cleaner, and simplify some test cases too
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.
package is a top-level store path (/nix/store/hash-name)
i.e. its a subset of pathInStore:
path in the Nix store
which itself is a subset of path:
isStringLike && firstChar == "/"
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.
I'm open to discussion on this. Is there benefit to having multiple? Or do they map to several options in the upstream plugin spec?
I don't think it makes sense for us to have multiple attributes to define a plugin source. I mean we can but I think having a source option is cleaner if we can check if the given option value is either a url | package | path and then conditionally map them to dir
url
upstream options at the end before the conversion.
So being able to do this with final implementation I think would be nice:
plugins.lazy.plugins = with pkgs.vimPlugins; [
vim-closer
"https://github.com/echasnovski/mini.ai"
"echasnovski/mini.trailspace"
{
source = dial-nvim;
.
.
}
];
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.
package is a top-level store path (/nix/store/hash-name)
i.e. its a subset of pathInStore:
path in the Nix store
which itself is a subset of path:
isStringLike && firstChar == "/"
Yup my bad I thought you meant if the types themselves literally extended types.path
but just added some additional attributes/functionality
|
||
lazyPluginsListType = types.listOf lazyPluginType; |
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.
lazyPluginsListType = types.listOf lazyPluginType; |
This can just be defined inline as e.g. type = listOf lazyPluginType
plugins = lib.mkOption { | ||
type = lazyPluginsListType; | ||
default = [ ]; | ||
description = "List of plugins"; | ||
}; | ||
}; |
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 option would benefit greatly from an example
and also a fleshed out description
.
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.
Yup I totally agree, I will get around to adding that, probably towards the final stages of the PR as it seems like plugins
itself is going to change in definition
plugins/pluginmanagers/lazy.nix
Outdated
cfg.gitPackage | ||
cfg.luarocksPackage | ||
]; | ||
plugins.lazy.settings.spec = removePkgAttrFromPlugins cfg.plugins; |
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.lazy.settings.spec = removePkgAttrFromPlugins cfg.plugins; | |
plugins.lazy.settings.spec = map toPluginSpec cfg.plugins; |
Let's inline removePkgAttrFromPlugins
here and rename removePkgAttrFromPlugin
to toPluginSpec
.
f3d67a5
to
d26c588
Compare
No worries at all! I was definitely pretty confused as well (still am to a certain extent 😂), and your comments helped a bunch! Besides I'm not in any rush to get this out, and I'd rather have it thoroughly review 🙂 |
389cfcb
to
308d683
Compare
308d683
to
64eff65
Compare
This PR came about as a result of the comments made in this PR #1904. It refactors the existing lazy plugin manager to use
mkNeovimPlugin
and makes the plugin type freeform.Closes #2174 #1904
Here's an example of how to set up
LazyVim
using the changes to this plugin (Note: this is a standalone flake, saving it to any directory asflake.nix
and runningnix build
should be enough, to launch neovim run./result/bin/nvim
)