mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
hyprland: add extraLuaFiles option
Allow Lua configs to be split across managed files under XDG_CONFIG_HOME/hypr.
Treat extraLuaFiles attribute names as Lua module names, so dotted names such as lib.helpers write lib/helpers.lua while autoloading with require("lib.helpers").
Add assertions for invalid configType usage, generated hyprland.lua collisions, and duplicate resolved Lua file targets.
This commit is contained in:
15
modules/misc/news/2026/05/2026-05-14_00-00-00.nix
Normal file
15
modules/misc/news/2026/05/2026-05-14_00-00-00.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
time = "2026-05-14T00:00:00+00:00";
|
||||
condition = config.wayland.windowManager.hyprland.enable;
|
||||
message = ''
|
||||
A new option `wayland.windowManager.hyprland.extraLuaFiles` is available for
|
||||
managing additional Lua files under `$XDG_CONFIG_HOME/hypr` when using
|
||||
`wayland.windowManager.hyprland.configType = "lua"`.
|
||||
|
||||
Files can be provided as paths or strings. Attribute names are treated as
|
||||
Lua module names, so `lib.helpers` writes `lib/helpers.lua`, and can be
|
||||
automatically loaded from the generated `hyprland.lua` with `require(...)`.
|
||||
'';
|
||||
}
|
||||
@@ -31,6 +31,10 @@ let
|
||||
pluginPath =
|
||||
entry: if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry;
|
||||
|
||||
luaModuleName = name: lib.replaceStrings [ "/" ] [ "." ] (lib.removeSuffix ".lua" name);
|
||||
|
||||
luaFileName = name: "${lib.replaceStrings [ "." ] [ "/" ] (luaModuleName name)}.lua";
|
||||
|
||||
reloadConfig = ''
|
||||
(
|
||||
XDG_RUNTIME_DIR=''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
|
||||
@@ -393,6 +397,75 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
extraLuaFiles = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
attrsOf (
|
||||
coercedTo (either path lines)
|
||||
(content: {
|
||||
inherit content;
|
||||
autoLoad = true;
|
||||
})
|
||||
(submodule {
|
||||
options = {
|
||||
content = lib.mkOption {
|
||||
type = either path lines;
|
||||
description = ''
|
||||
Lua file content, set either by specifying a path to a Lua
|
||||
file or by providing a multi-line Lua string.
|
||||
'';
|
||||
};
|
||||
|
||||
autoLoad = lib.mkOption {
|
||||
type = bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to generate a `require(...)` call for this file in
|
||||
{file}`$XDG_CONFIG_HOME/hypr/hyprland.lua`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
})
|
||||
);
|
||||
default = { };
|
||||
description = ''
|
||||
Extra Lua files written under {file}`$XDG_CONFIG_HOME/hypr`.
|
||||
|
||||
Attribute names are used as Lua module names and converted to file
|
||||
names with a {file}`.lua` suffix added when missing. For example,
|
||||
`bindings` writes
|
||||
{file}`$XDG_CONFIG_HOME/hypr/bindings.lua`, while
|
||||
`lib.helpers` writes {file}`$XDG_CONFIG_HOME/hypr/lib/helpers.lua`.
|
||||
|
||||
Files with {option}`autoLoad` enabled generate `require(...)` calls in
|
||||
{file}`$XDG_CONFIG_HOME/hypr/hyprland.lua` after adding the Hypr config
|
||||
directory to Lua's `package.path`. Use {option}`autoLoad = false` for
|
||||
helper modules that are imported by other Lua files.
|
||||
|
||||
This option only affects generated files when
|
||||
{option}`wayland.windowManager.hyprland.configType` is `"lua"`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
"00-vars" = '\'
|
||||
local M = {}
|
||||
M.mainMod = "SUPER"
|
||||
return M
|
||||
'\';
|
||||
|
||||
"bindings" = {
|
||||
content = ./bindings.lua;
|
||||
autoLoad = true;
|
||||
};
|
||||
|
||||
"lib.helpers" = {
|
||||
content = ./helpers.lua;
|
||||
autoLoad = false;
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
sourceFirst =
|
||||
lib.mkEnableOption ''
|
||||
putting source entries at the top of the configuration
|
||||
@@ -427,6 +500,18 @@ in
|
||||
assertion = !builtins.hasAttr "reset" cfg.submaps;
|
||||
message = "Submaps can't be named 'reset'. The name 'reset' is reserved in order to have a way to switch to the default submap; as if 'reset' was its name.";
|
||||
}
|
||||
{
|
||||
assertion = !builtins.elem "hyprland.lua" (map luaFileName (lib.attrNames cfg.extraLuaFiles));
|
||||
message = "wayland.windowManager.hyprland.extraLuaFiles cannot define hyprland.lua because it is generated by the Hyprland module.";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
let
|
||||
targets = map luaFileName (lib.attrNames cfg.extraLuaFiles);
|
||||
in
|
||||
lib.length targets == lib.length (lib.unique targets);
|
||||
message = "wayland.windowManager.hyprland.extraLuaFiles contains entries that resolve to the same Lua file path.";
|
||||
}
|
||||
];
|
||||
|
||||
warnings =
|
||||
@@ -434,9 +519,10 @@ in
|
||||
inconsistent =
|
||||
(cfg.systemd.enable || cfg.plugins != [ ])
|
||||
&& cfg.extraConfig == ""
|
||||
&& cfg.extraLuaFiles == { }
|
||||
&& cfg.settings == { }
|
||||
&& cfg.submaps == { };
|
||||
warning = "You have enabled hyprland.systemd.enable or listed plugins in hyprland.plugins but do not have any configuration in hyprland.settings, hyprland.extraConfig or hyprland.submaps. This is almost certainly a mistake.";
|
||||
warning = "You have enabled hyprland.systemd.enable or listed plugins in hyprland.plugins but do not have any configuration in hyprland.settings, hyprland.extraConfig, hyprland.extraLuaFiles or hyprland.submaps. This is almost certainly a mistake.";
|
||||
|
||||
filterNonBinds =
|
||||
attrs: builtins.filter (n: builtins.match "bind[[:lower:]]*" n == null) (builtins.attrNames attrs);
|
||||
@@ -589,6 +675,22 @@ in
|
||||
${lib.concatMapStrings (command: " hl.exec_cmd(${toLua command})\n") startupCommands}end)
|
||||
'';
|
||||
|
||||
renderLuaFiles =
|
||||
let
|
||||
autoloadFiles = lib.filterAttrs (_: file: file.autoLoad) cfg.extraLuaFiles;
|
||||
names = lib.sort lib.lessThan (lib.attrNames autoloadFiles);
|
||||
in
|
||||
if names == [ ] then
|
||||
""
|
||||
else
|
||||
renderSection "extraLuaFiles" (
|
||||
''
|
||||
local hm_xdg_config_home = os.getenv("XDG_CONFIG_HOME") or ${toLua config.xdg.configHome}
|
||||
package.path = hm_xdg_config_home .. "/hypr/?.lua;" .. hm_xdg_config_home .. "/hypr/?/init.lua;" .. package.path
|
||||
''
|
||||
+ lib.concatMapStringsSep "\n" (name: "require(${toLua (luaModuleName name)})") names
|
||||
);
|
||||
|
||||
renderSubmaps =
|
||||
let
|
||||
renderLuaArg = value: lib.replaceStrings [ "\n" ] [ "\n " ] (renderArgs value);
|
||||
@@ -623,6 +725,7 @@ in
|
||||
shouldGenerate =
|
||||
cfg.systemd.enable
|
||||
|| cfg.extraConfig != ""
|
||||
|| cfg.extraLuaFiles != { }
|
||||
|| cfg.settings != { }
|
||||
|| cfg.plugins != [ ]
|
||||
|| hasLuaSubmaps;
|
||||
@@ -633,6 +736,7 @@ in
|
||||
-- See https://wiki.hypr.land/Configuring/Start/
|
||||
|
||||
''
|
||||
+ renderLuaFiles
|
||||
+ renderSettings
|
||||
+ renderSubmaps
|
||||
+ renderStartHook
|
||||
@@ -642,6 +746,17 @@ in
|
||||
}
|
||||
);
|
||||
}
|
||||
(lib.mkIf (cfg.configType == "lua") (
|
||||
lib.mapAttrs' (
|
||||
name: file:
|
||||
lib.nameValuePair "hypr/${luaFileName name}" (
|
||||
if builtins.isPath file.content || lib.isStorePath file.content then
|
||||
{ source = file.content; }
|
||||
else
|
||||
{ text = file.content; }
|
||||
)
|
||||
) cfg.extraLuaFiles
|
||||
))
|
||||
];
|
||||
|
||||
xdg.portal = {
|
||||
|
||||
@@ -15,4 +15,6 @@ lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||
hyprland-submaps-config = ./submaps-config.nix;
|
||||
hyprland-submaps-on-dispatch = ./submaps-on-dispatch.nix;
|
||||
hyprland-lua-config = ./lua-config.nix;
|
||||
hyprland-lua-files-assertions = ./lua-files-assertions.nix;
|
||||
hyprland-lua-files-config = ./lua-files-config.nix;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
};
|
||||
|
||||
test.asserts.warnings.expected = [
|
||||
"You have enabled hyprland.systemd.enable or listed plugins in hyprland.plugins but do not have any configuration in hyprland.settings, hyprland.extraConfig or hyprland.submaps. This is almost certainly a mistake."
|
||||
"You have enabled hyprland.systemd.enable or listed plugins in hyprland.plugins but do not have any configuration in hyprland.settings, hyprland.extraConfig, hyprland.extraLuaFiles or hyprland.submaps. This is almost certainly a mistake."
|
||||
];
|
||||
test.asserts.warnings.enable = true;
|
||||
|
||||
|
||||
3
tests/modules/services/hyprland/lua-file-from-path.lua
Normal file
3
tests/modules/services/hyprland/lua-file-from-path.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
hl.on("hyprland.start", function()
|
||||
hl.exec_cmd("waybar")
|
||||
end)
|
||||
3
tests/modules/services/hyprland/lua-files-00-vars.lua
Normal file
3
tests/modules/services/hyprland/lua-files-00-vars.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
local M = {}
|
||||
M.mainMod = "SUPER"
|
||||
return M
|
||||
21
tests/modules/services/hyprland/lua-files-assertions.nix
Normal file
21
tests/modules/services/hyprland/lua-files-assertions.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
configType = "hyprlang";
|
||||
package = null;
|
||||
portalPackage = null;
|
||||
|
||||
extraLuaFiles = {
|
||||
foo = "";
|
||||
"foo.lua" = "";
|
||||
hyprland = "";
|
||||
};
|
||||
};
|
||||
|
||||
test.asserts.assertions.expected = [
|
||||
"wayland.windowManager.hyprland.extraLuaFiles cannot define hyprland.lua because it is generated by the Hyprland module."
|
||||
"wayland.windowManager.hyprland.extraLuaFiles contains entries that resolve to the same Lua file path."
|
||||
];
|
||||
|
||||
test.asserts.warnings.enable = false;
|
||||
}
|
||||
2
tests/modules/services/hyprland/lua-files-bindings.lua
Normal file
2
tests/modules/services/hyprland/lua-files-bindings.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
local vars = require("00-vars")
|
||||
hl.bind(vars.mainMod .. " + RETURN", hl.dsp.exec_cmd("kitty"))
|
||||
9
tests/modules/services/hyprland/lua-files-config.lua
Normal file
9
tests/modules/services/hyprland/lua-files-config.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Generated by Home Manager.
|
||||
-- See https://wiki.hypr.land/Configuring/Start/
|
||||
|
||||
-- extraLuaFiles
|
||||
local hm_xdg_config_home = os.getenv("XDG_CONFIG_HOME") or "/home/hm-user/.config"
|
||||
package.path = hm_xdg_config_home .. "/hypr/?.lua;" .. hm_xdg_config_home .. "/hypr/?/init.lua;" .. package.path
|
||||
require("00-vars")
|
||||
require("from-path")
|
||||
require("ui.bindings")
|
||||
59
tests/modules/services/hyprland/lua-files-config.nix
Normal file
59
tests/modules/services/hyprland/lua-files-config.nix
Normal file
@@ -0,0 +1,59 @@
|
||||
_:
|
||||
|
||||
{
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
configType = "lua";
|
||||
package = null;
|
||||
portalPackage = null;
|
||||
|
||||
systemd.enable = false;
|
||||
|
||||
extraLuaFiles = {
|
||||
"00-vars" = ''
|
||||
local M = {}
|
||||
M.mainMod = "SUPER"
|
||||
return M
|
||||
'';
|
||||
|
||||
"ui.bindings" = {
|
||||
content = ''
|
||||
local vars = require("00-vars")
|
||||
hl.bind(vars.mainMod .. " + RETURN", hl.dsp.exec_cmd("kitty"))
|
||||
'';
|
||||
};
|
||||
|
||||
"lib.helpers" = {
|
||||
content = ''
|
||||
local M = {}
|
||||
M.terminal = "kitty"
|
||||
return M
|
||||
'';
|
||||
autoLoad = false;
|
||||
};
|
||||
|
||||
"from-path.lua" = ./lua-file-from-path.lua;
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
config=home-files/.config/hypr/hyprland.lua
|
||||
assertFileExists "$config"
|
||||
assertPathNotExists home-files/.config/hypr/hyprland.conf
|
||||
assertPathNotExists home-files/.config/hypr/.luarc.json
|
||||
|
||||
assertFileContent "$config" ${./lua-files-config.lua}
|
||||
|
||||
assertFileContent home-files/.config/hypr/00-vars.lua \
|
||||
${./lua-files-00-vars.lua}
|
||||
|
||||
assertFileContent home-files/.config/hypr/ui/bindings.lua \
|
||||
${./lua-files-bindings.lua}
|
||||
|
||||
assertFileContent home-files/.config/hypr/lib/helpers.lua \
|
||||
${./lua-files-helpers.lua}
|
||||
|
||||
assertFileContent home-files/.config/hypr/from-path.lua \
|
||||
${./lua-file-from-path.lua}
|
||||
'';
|
||||
}
|
||||
3
tests/modules/services/hyprland/lua-files-helpers.lua
Normal file
3
tests/modules/services/hyprland/lua-files-helpers.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
local M = {}
|
||||
M.terminal = "kitty"
|
||||
return M
|
||||
Reference in New Issue
Block a user