463 lines
14 KiB
Nix
463 lines
14 KiB
Nix
# Additional theming primitives not provided by stylix
|
|
{ pkgs, lib, config, ... }:
|
|
let
|
|
inherit (lib) types;
|
|
|
|
cfg = config.satellite.neovim;
|
|
|
|
# {{{ Custom types
|
|
myTypes = {
|
|
zeroOrMore = t: types.nullOr (types.either t (types.listOf t));
|
|
|
|
luaCode = types.nullOr (types.oneOf [
|
|
types.str
|
|
types.path
|
|
myTypes.luaLiteral
|
|
]);
|
|
|
|
luaLiteral = types.submodule (_: {
|
|
options.__luaEncoderTag = lib.mkOption {
|
|
type = types.enum [ "lua" ];
|
|
};
|
|
options.value = lib.mkOption {
|
|
type = types.str;
|
|
};
|
|
});
|
|
|
|
luaValue = types.nullOr (types.oneOf [
|
|
types.str
|
|
types.number
|
|
types.bool
|
|
(types.attrsOf myTypes.luaValue)
|
|
(types.listOf myTypes.luaValue)
|
|
]);
|
|
|
|
# {{{ Key type
|
|
lazyKey = types.oneOf [
|
|
types.str
|
|
(types.submodule
|
|
(_: {
|
|
options.mapping = lib.mkOption {
|
|
type = types.str;
|
|
description = "The lhs of the neovim mapping";
|
|
};
|
|
|
|
options.action = lib.mkOption {
|
|
type = types.nullOr (types.oneOf [
|
|
types.str
|
|
myTypes.luaLiteral
|
|
]);
|
|
description = "The rhs of the neovim mapping";
|
|
};
|
|
|
|
options.ft = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.zeroOrMore types.str;
|
|
description = "Filetypes on which this keybind should take effect";
|
|
};
|
|
|
|
options.mode = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "The vim modes the mapping should take effect in";
|
|
};
|
|
|
|
options.desc = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "Description for the current keymapping";
|
|
};
|
|
}))
|
|
];
|
|
# }}}
|
|
# {{{ Lazy module type
|
|
lazyModule = lib.fix (lazyModule: types.submodule (_: {
|
|
options = {
|
|
package = lib.mkOption {
|
|
type = types.oneOf [
|
|
types.package
|
|
types.str
|
|
];
|
|
description = "Package to configure the module around";
|
|
example = "nvim-telescope/telescope.nvim";
|
|
};
|
|
|
|
name = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "Custom name to use for the module";
|
|
example = "lualine";
|
|
};
|
|
|
|
main = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "The name of the lua entrypoint for the plugin (usually auto-detected)";
|
|
example = "lualine";
|
|
};
|
|
|
|
version = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "Pin the package to a certain version (useful for non-nix managed packages)";
|
|
};
|
|
|
|
tag = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.str;
|
|
description = "Pin the package to a certain git tag (useful for non-nix managed packages)";
|
|
};
|
|
|
|
lazy = lib.mkOption {
|
|
default = null;
|
|
type = types.nullOr types.bool;
|
|
description = "Specifies whether this module should be lazy-loaded";
|
|
};
|
|
|
|
dependencies.lua = lib.mkOption {
|
|
default = [ ];
|
|
type = types.listOf lazyModule;
|
|
description = "Lazy.nvim module dependencies";
|
|
};
|
|
|
|
dependencies.nix = lib.mkOption {
|
|
default = [ ];
|
|
type = types.listOf types.package;
|
|
description = "Nix packages to give nvim access to";
|
|
};
|
|
|
|
cond = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.luaCode;
|
|
description = "Condition based on which to enable/disbale loading the package";
|
|
};
|
|
|
|
setup = lib.mkOption {
|
|
default = null;
|
|
type = types.oneOf [ myTypes.luaCode types.bool ];
|
|
description = ''
|
|
Lua function (or module) to use for configuring the package.
|
|
Used instead of the canonically named `config` because said property has a special name in nix'';
|
|
};
|
|
|
|
event = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.zeroOrMore types.str;
|
|
description = "Event on which the module should be lazy loaded";
|
|
};
|
|
|
|
ft = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.zeroOrMore types.str;
|
|
description = "Filetypes on which the module should be lazy loaded";
|
|
};
|
|
|
|
cmd = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.zeroOrMore types.str;
|
|
description = "Comands on which to load this plugin";
|
|
};
|
|
|
|
init = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.luaCode;
|
|
description = "Lua function (or module) to run right away (even if the package is not yet loaded)";
|
|
};
|
|
|
|
passthrough = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.luaCode;
|
|
description = "Attach additional things to the lazy module";
|
|
};
|
|
|
|
opts = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.luaValue;
|
|
description = "Custom data to pass to the plugin .setup function";
|
|
};
|
|
|
|
keys = lib.mkOption {
|
|
default = null;
|
|
type = myTypes.zeroOrMore myTypes.lazyKey;
|
|
description = "Keybinds to lazy-load the module on";
|
|
};
|
|
};
|
|
}));
|
|
# }}}
|
|
};
|
|
# }}}
|
|
# {{{ Lua encoders
|
|
mkRawLuaObject = chunks:
|
|
''
|
|
{
|
|
${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
|
|
}
|
|
'';
|
|
|
|
# An encoder is a function from some nix value to a string containing lua code.
|
|
# This object provides combinators for writing such encoders.
|
|
luaEncoders = {
|
|
# {{{ General helpers
|
|
identity = given: given;
|
|
bind = encoder: given: encoder given given;
|
|
conditional = predicate: caseTrue: caseFalse:
|
|
luaEncoders.bind (given: if predicate given then caseTrue else caseFalse);
|
|
map = f: encoder: given: encoder (f given);
|
|
trace = message: luaEncoders.map (f: lib.traceSeq message (lib.traceVal f));
|
|
fail = mkMessage: v: builtins.throw (mkMessage v);
|
|
const = code: _: code;
|
|
# }}}
|
|
# {{{ Base types
|
|
# TODO: figure out escaping and whatnot
|
|
string = string: ''"${string}"'';
|
|
bool = bool: if bool then "true" else "false";
|
|
number = toString;
|
|
nil = _: "nil";
|
|
stringOr = luaEncoders.conditional lib.isString luaEncoders.string;
|
|
boolOr = luaEncoders.conditional lib.isBool luaEncoders.bool;
|
|
numberOr = luaEncoders.conditional (e: lib.isFloat e || lib.isInt e) luaEncoders.number;
|
|
nullOr = luaEncoders.conditional (e: e == null) luaEncoders.nil;
|
|
anything = lib.pipe (luaEncoders.fail (v: "Cannot figure out how to encode value ${builtins.toJSON v}")) [
|
|
(luaEncoders.attrsetOfOr luaEncoders.anything)
|
|
(luaEncoders.listOfOr luaEncoders.anything)
|
|
luaEncoders.nullOr
|
|
luaEncoders.boolOr
|
|
luaEncoders.numberOr
|
|
luaEncoders.stringOr
|
|
luaEncoders.luaCodeOr # Lua code expressions have priority over attrsets
|
|
];
|
|
# }}}
|
|
# {{{ Lua code
|
|
luaCode = tag:
|
|
luaEncoders.luaCodeOr
|
|
(luaEncoders.conditional lib.isPath
|
|
(path: "dofile(${luaEncoders.string path}).${tag}")
|
|
luaEncoders.identity);
|
|
luaCodeOr =
|
|
luaEncoders.conditional (e: lib.isAttrs e && (e.__luaEncoderTag or null) == "lua")
|
|
(obj: obj.value);
|
|
# }}}
|
|
# {{{ Lists
|
|
listOf = encoder: list:
|
|
mkRawLuaObject (lib.lists.map encoder list);
|
|
tryNonemptyList = encoder: luaEncoders.conditional
|
|
(l: l == [ ])
|
|
luaEncoders.nil
|
|
(luaEncoders.listOf encoder);
|
|
listOfOr = encoder:
|
|
luaEncoders.conditional
|
|
lib.isList
|
|
(luaEncoders.listOf encoder);
|
|
oneOrMany = encoder: luaEncoders.listOfOr encoder encoder;
|
|
zeroOrMany = encoder: luaEncoders.nullOr (luaEncoders.oneOrMany encoder);
|
|
oneOrManyAsList = encoder: luaEncoders.map
|
|
(given: if lib.isList given then given else [ given ])
|
|
(luaEncoders.listOf encoder);
|
|
listAsOneOrMany = encoder:
|
|
luaEncoders.map
|
|
(l: if lib.length l == 1 then lib.head l else l)
|
|
(luaEncoders.oneOrMany encoder);
|
|
# }}}
|
|
# {{{ Attrsets
|
|
attrsetOf = encoder: object:
|
|
mkRawLuaObject (lib.mapAttrsToList
|
|
(name: value:
|
|
let result = encoder value;
|
|
in
|
|
lib.optionalString (result != "nil")
|
|
"${name} = ${result}"
|
|
)
|
|
object
|
|
);
|
|
attrsetOfOr = of: luaEncoders.conditional lib.isAttrs (luaEncoders.attrsetOf of);
|
|
attrset = noNils: listOrder: listSpec: spec: attrset:
|
|
let
|
|
shouldKeep = given:
|
|
if noNils then
|
|
given != "nil"
|
|
else
|
|
true;
|
|
|
|
listChunks = lib.lists.map
|
|
(attr:
|
|
let result = listSpec.${attr} (attrset.${attr} or null);
|
|
in
|
|
lib.optionalString (shouldKeep result) result
|
|
)
|
|
listOrder;
|
|
objectChunks = lib.mapAttrsToList
|
|
(attr: encoder:
|
|
let result = encoder (attrset.${attr} or null);
|
|
in
|
|
lib.optionalString (shouldKeep result)
|
|
"${attr} = ${result}"
|
|
)
|
|
spec;
|
|
in
|
|
mkRawLuaObject (listChunks ++ objectChunks);
|
|
# }}}
|
|
};
|
|
|
|
e = luaEncoders;
|
|
# }}}
|
|
# Format and write a lua file to disk
|
|
writeLuaFile = path: name: text:
|
|
let
|
|
directory = "lua/${path}";
|
|
destination = "${directory}/${name}.lua";
|
|
unformatted = pkgs.writeText "raw-lua-${name}" text;
|
|
in
|
|
pkgs.runCommand "formatted-lua-${name}" { } ''
|
|
mkdir -p $out/${directory}
|
|
cp --no-preserve=mode ${unformatted} $out/${destination}
|
|
${lib.getExe pkgs.stylua} --config-path ${cfg.styluaConfig} $out/${destination}
|
|
'';
|
|
in
|
|
{
|
|
options.satellite.neovim = {
|
|
lazy = lib.mkOption {
|
|
default = { };
|
|
description = "Record of persistent locations (eg: /persist)";
|
|
type = types.attrsOf myTypes.lazyModule;
|
|
};
|
|
|
|
generated = {
|
|
lazy = lib.mkOption {
|
|
type = types.attrsOf (types.submodule (_: {
|
|
options = {
|
|
raw = lib.mkOption {
|
|
type = types.lines;
|
|
description = "The lua script generated using the other options";
|
|
};
|
|
|
|
module = lib.mkOption {
|
|
type = types.package;
|
|
description = "The lua script generated using the other options";
|
|
};
|
|
};
|
|
}));
|
|
description = "Attrset containing every module generated from the lazy configuration";
|
|
};
|
|
|
|
all = lib.mkOption {
|
|
default = { };
|
|
type = types.package;
|
|
description = "Derivation building all the given nix modules";
|
|
};
|
|
|
|
dependencies = lib.mkOption {
|
|
default = [ ];
|
|
type = types.listOf types.package;
|
|
description = "List of packages to give neovim access to";
|
|
};
|
|
};
|
|
|
|
lib = {
|
|
lua = lib.mkOption {
|
|
default = value: { inherit value; __luaEncoderTag = "lua"; };
|
|
type = types.functionTo myTypes.luaLiteral;
|
|
description = "include some raw lua code inside module configuration";
|
|
};
|
|
|
|
import = lib.mkOption {
|
|
default = path: tag: cfg.lib.lua "dofile(${e.string path}).${tag}";
|
|
type = types.functionTo (types.functionTo myTypes.luaLiteral);
|
|
description = "import some identifier from some module";
|
|
};
|
|
|
|
blacklistEnv = lib.mkOption {
|
|
default = given: cfg.lib.lua ''
|
|
require(${e.string cfg.env.module}).blacklist(${e.listOf e.string given})
|
|
'';
|
|
type = types.functionTo myTypes.luaLiteral;
|
|
description = "Generate a lazy.cond predicate which disables a module if one of the given envs is active";
|
|
};
|
|
|
|
thunk = lib.mkOption {
|
|
default = given: cfg.lib.lua ''
|
|
function() return ${given} end
|
|
'';
|
|
type = types.functionTo myTypes.luaLiteral;
|
|
description = "Wrap a lua expression into a lua function";
|
|
};
|
|
};
|
|
|
|
env = {
|
|
module = lib.mkOption {
|
|
type = types.str;
|
|
example = "my.helpers.env";
|
|
description = "Module where to import env flags from";
|
|
};
|
|
};
|
|
|
|
styluaConfig = lib.mkOption {
|
|
type = types.path;
|
|
description = "Config to use for formatting lua modules";
|
|
};
|
|
};
|
|
|
|
config.satellite.neovim.generated.lazy =
|
|
let
|
|
lazyKeyEncoder =
|
|
e.stringOr (e.attrset true [ "mapping" "action" ]
|
|
{
|
|
mapping = e.string;
|
|
action = e.nullOr (e.luaCodeOr e.string);
|
|
}
|
|
{
|
|
mode = e.nullOr
|
|
(e.map
|
|
lib.strings.stringToCharacters
|
|
(e.listAsOneOrMany e.string));
|
|
desc = e.nullOr e.string;
|
|
ft = e.zeroOrMany e.string;
|
|
});
|
|
|
|
lazyObjectEncoder = e.bind
|
|
(opts: e.attrset true [ "package" ]
|
|
{ package = e.string; }
|
|
{
|
|
name = e.nullOr e.string;
|
|
main = e.nullOr e.string;
|
|
tag = e.nullOr e.string;
|
|
version = e.nullOr e.string;
|
|
dependencies = e.map (d: d.lua) (e.tryNonemptyList lazyObjectEncoder);
|
|
lazy = e.nullOr e.bool;
|
|
cond = e.nullOr (e.luaCode "cond");
|
|
config = e.const (e.nullOr (e.boolOr (e.luaCode "config")) opts.setup);
|
|
init = e.nullOr (e.luaCode "init");
|
|
event = e.zeroOrMany e.string;
|
|
cmd = e.zeroOrMany e.string;
|
|
ft = e.zeroOrMany e.string;
|
|
keys = e.nullOr (e.oneOrManyAsList lazyKeyEncoder);
|
|
passthrough = e.anything;
|
|
opts = e.anything;
|
|
});
|
|
|
|
makeLazyScript = opts: ''
|
|
-- This file was generated by nix
|
|
return ${lazyObjectEncoder opts}
|
|
'';
|
|
in
|
|
lib.attrsets.mapAttrs
|
|
(name: opts: rec {
|
|
raw = makeLazyScript opts;
|
|
module = writeLuaFile "nix/plugins" name raw;
|
|
})
|
|
cfg.lazy;
|
|
|
|
config.satellite.neovim.generated.all =
|
|
pkgs.symlinkJoin {
|
|
name = "lazy-nvim-modules";
|
|
paths = lib.attrsets.mapAttrsToList (_: m: m.module) cfg.generated.lazy;
|
|
};
|
|
|
|
config.satellite.neovim.generated.dependencies =
|
|
lib.pipe cfg.lazy
|
|
[
|
|
(lib.attrsets.mapAttrsToList (_: m: m.dependencies.nix))
|
|
lib.lists.flatten
|
|
]
|
|
;
|
|
}
|