From df428b562dec0c4a31f257962ab93bc6c998608d Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Fri, 9 May 2025 17:03:36 +0100 Subject: [PATCH 1/7] qt: use a static `autoEnable=true` on NixOS --- modules/qt/nixos.nix | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/qt/nixos.nix b/modules/qt/nixos.nix index 6d6f0ea4..4cfd293b 100644 --- a/modules/qt/nixos.nix +++ b/modules/qt/nixos.nix @@ -1,9 +1,4 @@ -{ - lib, - pkgs, - config, - ... -}: +{ lib, config, ... }: let @@ -16,7 +11,7 @@ let in { options.stylix.targets.qt = { - enable = config.lib.stylix.mkEnableTarget "QT" pkgs.stdenv.hostPlatform.isLinux; + enable = config.lib.stylix.mkEnableTarget "QT" true; platform = lib.mkOption { description = '' Selects the platform theme to use for Qt applications. From 93eb9c5401b0efe517d9fe782638012fec99ebef Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Tue, 20 May 2025 21:36:02 +0100 Subject: [PATCH 2/7] stylix: simplify `mkEnableTarget` & `mkEnableWallpaper` defaultText Drop the redundant "same as" prefix and instead use a `literalExpression`. Co-authored-by: NAHO <90870942+trueNAHO@users.noreply.github.com> --- stylix/target.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stylix/target.nix b/stylix/target.nix index 1074acb3..9ff4cd89 100644 --- a/stylix/target.nix +++ b/stylix/target.nix @@ -44,7 +44,7 @@ example = !autoEnable; } // lib.optionalAttrs autoEnable { - defaultText = lib.literalMD "same as `stylix.autoEnable`"; + defaultText = lib.literalExpression "stylix.autoEnable"; }; mkEnableWallpaper = humanName: autoEnable: @@ -55,7 +55,7 @@ type = lib.types.bool; } // lib.optionalAttrs autoEnable { - defaultText = lib.literalMD "`stylix.image != null`"; + defaultText = lib.literalExpression "stylix.image != null"; }; }; } From 92276a14a70b87578a31730974d18a243207d028 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Fri, 9 May 2025 13:31:54 +0100 Subject: [PATCH 3/7] stylix: add `mkEnableIf` Refactor `mkEnableTarget` and `mkEnableWallpaper` to use `mkEnableIf`. --- stylix/target.nix | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/stylix/target.nix b/stylix/target.nix index 9ff4cd89..ffdb1441 100644 --- a/stylix/target.nix +++ b/stylix/target.nix @@ -34,28 +34,44 @@ config.lib.stylix = let cfg = config.stylix; + self = config.lib.stylix; in { mkEnableTarget = humanName: autoEnable: - lib.mkEnableOption "theming for ${humanName}" - // { + self.mkEnableIf { + description = "Whether to enable theming for ${humanName}."; default = cfg.autoEnable && autoEnable; + ${if autoEnable then "defaultText" else null} = + lib.literalExpression "stylix.autoEnable"; example = !autoEnable; - } - // lib.optionalAttrs autoEnable { - defaultText = lib.literalExpression "stylix.autoEnable"; }; + mkEnableWallpaper = humanName: autoEnable: - lib.mkOption { - default = config.stylix.image != null && autoEnable; - example = config.stylix.image == null; + self.mkEnableIf { description = "Whether to set the wallpaper for ${humanName}."; + default = config.stylix.image != null && autoEnable; + defaultText = + if autoEnable then lib.literalExpression "stylix.image != null" else false; + example = config.stylix.image == null; + }; + + mkEnableIf = + { + description, + default, + defaultText ? null, + example ? if args ? defaultText then true else !default, + }@args: + lib.mkOption { type = lib.types.bool; - } - // lib.optionalAttrs autoEnable { - defaultText = lib.literalExpression "stylix.image != null"; + defaultText = if args ? defaultText then defaultText else default; + inherit + default + description + example + ; }; }; } From a4d92fea8771b608d1b4db27b9b13cb8f84e65f7 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Fri, 9 May 2025 13:31:54 +0100 Subject: [PATCH 4/7] stylix: split `mkEnableTarget` into `mkEnableTargetWith` --- stylix/target.nix | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/stylix/target.nix b/stylix/target.nix index ffdb1441..9f521efc 100644 --- a/stylix/target.nix +++ b/stylix/target.nix @@ -38,9 +38,16 @@ in { mkEnableTarget = - humanName: autoEnable: + name: autoEnable: + config.lib.stylix.mkEnableTargetWith { inherit name autoEnable; }; + + mkEnableTargetWith = + { + name, + autoEnable ? true, + }: self.mkEnableIf { - description = "Whether to enable theming for ${humanName}."; + description = "Whether to enable theming for ${name}."; default = cfg.autoEnable && autoEnable; ${if autoEnable then "defaultText" else null} = lib.literalExpression "stylix.autoEnable"; From 0449dae6d4b31e269a1c6ccfcd2c1af0916dc916 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Fri, 9 May 2025 13:31:54 +0100 Subject: [PATCH 5/7] stylix: allow specifying `autoEnableExpr` in `mkEnableTargetWith` This allows correctly documenting dynamic enable conditions. E.g: ```nix enable = mkEnableTargetWith { name = "QT"; autoEnable = pkgs.stdenv.hostPlatform.isLinux; autoEnableExpr = "pkgs.stdenv.hostPlatform.isLinux"; }; ``` `autoEnableExpr` will be wrapped in parentheses if it contains a nix operator with lower precedence than `&&`. Added a note in the docs mentioning `mkEnableTarget` should either have a static `autoEnable` or specify `autoEnableExpr`. --- doc/src/modules.md | 7 ++++++ stylix/target.nix | 59 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/doc/src/modules.md b/doc/src/modules.md index 0250dffc..d2b21d3a 100644 --- a/doc/src/modules.md +++ b/doc/src/modules.md @@ -106,6 +106,13 @@ the following applies: - There is no reliable way to detect whether the target is installed, *and* enabling it unconditionally would cause problems. +> [!CAUTION] +> The boolean value after `mkEnableTarget` should usually be a static `true` or +> `false` literal. +> +> Using a dynamic value requires you to document the dynamic expression using +> `mkEnableTargetWith`'s `autoEnableExpr` argument. + ### Overlays If your module is provided as an overlay it uses a special format, where config diff --git a/stylix/target.nix b/stylix/target.nix index 9f521efc..4ec78760 100644 --- a/stylix/target.nix +++ b/stylix/target.nix @@ -35,6 +35,42 @@ let cfg = config.stylix; self = config.lib.stylix; + + # Will wrap with (parentheses) if the expr contains operators with lower precedence than `&&` + wrapExprWith = + { + autoWrapExpr ? true, + trimExpr ? true, + indentMultilineExpr ? true, + }: + expr: + let + trimmed = if trimExpr then lib.trim expr else expr; + isWrapped = builtins.match ''[(].*[)]'' trimmed != null; + hasNewlines = lib.hasInfix "\n" trimmed; + needsWrapping = builtins.any (op: lib.hasInfix op trimmed) [ + # These operators have lower precedence than `&&` + # See https://nix.dev/manual/nix/2.28/language/operators + "||" + "->" + "|>" + "<|" + # These keywords would also need wrapping + "with " + "assert " + ]; + indented = + if indentMultilineExpr then + lib.pipe trimmed [ + (lib.strings.splitString "\n") + (map (line: if line == "" then "" else " " + line)) + (builtins.concatStringsSep "\n") + ] + else + trimmed; + wrapped = if hasNewlines then "(\n${indented}\n)" else "(${trimmed})"; + in + if autoWrapExpr && !isWrapped && needsWrapping then wrapped else trimmed; in { mkEnableTarget = @@ -45,13 +81,26 @@ { name, autoEnable ? true, - }: + autoEnableExpr ? null, + autoWrapExpr ? true, + example ? if args ? autoEnableExpr then true else !autoEnable, + }@args: + let + wrapExpr = wrapExprWith { + inherit autoWrapExpr; + }; + in self.mkEnableIf { - description = "Whether to enable theming for ${name}."; + description = "Whether to enable theming for ${name}"; default = cfg.autoEnable && autoEnable; - ${if autoEnable then "defaultText" else null} = - lib.literalExpression "stylix.autoEnable"; - example = !autoEnable; + defaultText = + if args ? autoEnableExpr then + lib.literalExpression "stylix.autoEnable && ${wrapExpr autoEnableExpr}" + else if autoEnable then + lib.literalExpression "stylix.autoEnable" + else + false; + inherit example; }; mkEnableWallpaper = From 67b4644e700747cb1b6e4748d9f04646c29f7295 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Thu, 22 May 2025 14:43:11 +0100 Subject: [PATCH 6/7] stylix: use `mkEnableTargetWith` in `mkTarget` --- stylix/mk-target.nix | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/stylix/mk-target.nix b/stylix/mk-target.nix index 96603173..5fd048f0 100644 --- a/stylix/mk-target.nix +++ b/stylix/mk-target.nix @@ -66,6 +66,27 @@ This should be disabled if manual setup is required or if auto-enabling causes issues. + The default (`true`) is inherited from `mkEnableTargetWith`. + + `autoEnableExpr` (String) + : A string representation of `autoEnable`, for use in documentation. + + Not required if `autoEnable` is a literal `true` or `false`, but **must** + be used when `autoEnable` is a dynamic expression. + + E.g. `"pkgs.stdenv.hostPlatform.isLinux"`. + + `autoWrapEnableExpr` (Boolean) + : Whether to automatically wrap `autoEnableExpr` with parenthesis, when it + contains a potentially problematic infix. + + The default (`true`) is inherited from `mkEnableTargetWith`. + + `enableExample` (Boolean or literal expression) + : An example to include on the enable option. The default is calculated + automatically by `mkEnableTargetWith` and depends on `autoEnable` and + whether an `autoEnableExpr` is used. + `extraOptions` (Attribute set) : Additional options to be added in the `stylix.targets.${name}` namespace along the `stylix.targets.${name}.enable` option. @@ -152,12 +173,15 @@ { name, humanName, - autoEnable ? true, + autoEnable ? null, + autoEnableExpr ? null, + autoWrapEnableExpr ? null, + enableExample ? null, extraOptions ? { }, configElements ? [ ], generalConfig ? null, imports ? [ ], -}: +}@args: let module = { config, lib, ... }: @@ -212,7 +236,19 @@ let inherit imports; options.stylix.targets.${name}.enable = - config.lib.stylix.mkEnableTarget humanName autoEnable; + let + enableArgs = + { + name = humanName; + } + // lib.optionalAttrs (args ? autoEnable) { inherit autoEnable; } + // lib.optionalAttrs (args ? autoEnableExpr) { inherit autoEnableExpr; } + // lib.optionalAttrs (args ? autoWrapEnableExpr) { + autoWrapExpr = autoWrapEnableExpr; + } + // lib.optionalAttrs (args ? enableExample) { example = enableExample; }; + in + config.lib.stylix.mkEnableTargetWith enableArgs; config = lib.mkIf (config.stylix.enable && cfg.enable) ( lib.mkMerge ( From 289356d03169542e5ef90f37fafa165e0fe8bae9 Mon Sep 17 00:00:00 2001 From: Matt Sturgeon Date: Fri, 9 May 2025 14:34:10 +0100 Subject: [PATCH 7/7] treewide: use `mkEnableTargetWith` for dynamic conditions Ensure enable options with dynamic `autoEnable` are documented correctly. --- modules/feh/hm.nix | 8 ++++++++ modules/feh/nixos.nix | 4 ++++ modules/gnome/hm.nix | 6 +++++- modules/hyprland/hm.nix | 8 +++++--- modules/qt/hm.nix | 8 +++++--- modules/swaylock/hm.nix | 22 +++++++++++++--------- 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/modules/feh/hm.nix b/modules/feh/hm.nix index 5c028991..d0187ebf 100644 --- a/modules/feh/hm.nix +++ b/modules/feh/hm.nix @@ -15,6 +15,14 @@ mkTarget { || i3.enable || spectrwm.enable || xmonad.enable; + autoEnableExpr = '' + with config.xsession.windowManager; + bspwm.enable + || herbstluftwm.enable + || i3.enable + || spectrwm.enable + || xmonad.enable + ''; configElements = { imageScalingMode, image }: diff --git a/modules/feh/nixos.nix b/modules/feh/nixos.nix index eefd8190..cc0ee85e 100644 --- a/modules/feh/nixos.nix +++ b/modules/feh/nixos.nix @@ -11,6 +11,10 @@ mkTarget { autoEnable = with config.services.xserver.windowManager; xmonad.enable || i3.enable; + autoEnableExpr = '' + with services.xserver.windowManager; + xmonad.enable || i3.enable + ''; configElements = { image, imageScalingMode }: diff --git a/modules/gnome/hm.nix b/modules/gnome/hm.nix index bc64a6cb..9b8e4561 100644 --- a/modules/gnome/hm.nix +++ b/modules/gnome/hm.nix @@ -44,7 +44,11 @@ let in { options.stylix.targets.gnome = { - enable = config.lib.stylix.mkEnableTarget "GNOME" pkgs.stdenv.hostPlatform.isLinux; + enable = config.lib.stylix.mkEnableTargetWith { + name = "GNOME"; + autoEnable = pkgs.stdenv.hostPlatform.isLinux; + autoEnableExpr = "pkgs.stdenv.hostPlatform.isLinux"; + }; useWallpaper = config.lib.stylix.mkEnableWallpaper "GNOME" true; }; diff --git a/modules/hyprland/hm.nix b/modules/hyprland/hm.nix index cb16ceb9..70ef1be2 100644 --- a/modules/hyprland/hm.nix +++ b/modules/hyprland/hm.nix @@ -7,9 +7,11 @@ mkTarget { name = "hyprland"; humanName = "Hyprland"; - extraOptions.hyprpaper.enable = config.lib.stylix.mkEnableTarget "Hyprpaper" ( - config.stylix.image != null - ); + extraOptions.hyprpaper.enable = config.lib.stylix.mkEnableTargetWith { + name = "Hyprpaper"; + autoEnable = config.stylix.image != null; + autoEnableExpr = "stylix.image != null"; + }; configElements = [ ( { colors }: diff --git a/modules/qt/hm.nix b/modules/qt/hm.nix index 562a4a4c..dd876237 100644 --- a/modules/qt/hm.nix +++ b/modules/qt/hm.nix @@ -13,9 +13,11 @@ # # [1]: https://github.com/nix-community/stylix/issues/933 # [2]: https://github.com/nix-community/home-manager/issues/6565 - enable = config.lib.stylix.mkEnableTarget "QT" ( - pkgs.stdenv.hostPlatform.isLinux && osConfig != null - ); + enable = config.lib.stylix.mkEnableTargetWith { + name = "QT"; + autoEnable = pkgs.stdenv.hostPlatform.isLinux && osConfig != null; + autoEnableExpr = "pkgs.stdenv.hostPlatform.isLinux && osConfig != null"; + }; platform = lib.mkOption { description = '' diff --git a/modules/swaylock/hm.nix b/modules/swaylock/hm.nix index a0440b23..20504c5c 100644 --- a/modules/swaylock/hm.nix +++ b/modules/swaylock/hm.nix @@ -26,15 +26,19 @@ in }) ]; options.stylix.targets.swaylock = { - enable = - config.lib.stylix.mkEnableTarget "Swaylock" - # When the state version is older than 23.05, Swaylock enables itself - # automatically if `settings != {}` [1]. Therefore, Swaylock theming - # shouldn't be enabled by default for such state versions, to avoid - # inadvertently installing Swaylock when it's not desired. - # - # [1]: https://github.com/nix-community/home-manager/blob/5cfbf5cc37a3bd1da07ae84eea1b828909c4456b/modules/programs/swaylock.nix#L12-L17 - (lib.versionAtLeast config.home.stateVersion "23.05"); + enable = config.lib.stylix.mkEnableTargetWith { + name = "Swaylock"; + # When the state version is older than 23.05, Swaylock enables itself + # automatically if `settings != {}` [1]. Therefore, Swaylock theming + # shouldn't be enabled by default for such state versions, to avoid + # inadvertently installing Swaylock when it's not desired. + # + # [1]: https://github.com/nix-community/home-manager/blob/5cfbf5cc37a3bd1da07ae84eea1b828909c4456b/modules/programs/swaylock.nix#L12-L17 + autoEnable = lib.versionAtLeast config.home.stateVersion "23.05"; + autoEnableExpr = '' + lib.versionAtLeast home.stateVersion "23.05" + ''; + }; useWallpaper = config.lib.stylix.mkEnableWallpaper "Swaylock" true; };