From c7da45855e889080f4a4531105bc85e72f5d5482 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Wed, 29 Jan 2025 18:54:50 +0200 Subject: [PATCH 1/6] nix/module: toHyprconf -> toHyprlang Updated generator that will end up living in Nixpkgs' `lib/generators`. --- nix/module.nix | 186 +++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 76 deletions(-) diff --git a/nix/module.nix b/nix/module.nix index 46191dfa82f..31c7868df66 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -7,70 +7,98 @@ inputs: { inherit (pkgs.stdenv.hostPlatform) system; cfg = config.programs.hyprland; - # basically 1:1 taken from https://github.com/nix-community/home-manager/blob/master/modules/services/window-managers/hyprland.nix - toHyprconf = { - attrs, - indentLevel ? 0, - importantPrefixes ? ["$"], - }: let + toHyprlang = { + topCommandsPrefixes ? ["$"], + bottomCommandsPrefixes ? [], + }: attrs: let + inherit (pkgs) lib; inherit - (lib) - all - concatMapStringsSep - concatStrings - concatStringsSep + (lib.generators) + mkKeyValueDefault + toKeyValue + ; + inherit + (lib.attrsets) filterAttrs - foldl - generators - hasPrefix isAttrs - isList mapAttrsToList - replicate + ; + inherit + (lib.lists) + foldl + isList + ; + inherit + (lib.strings) + concatMapStringsSep + concatStringsSep + hasPrefix + ; + inherit + (lib.trivial) + boolToString + isBool + ; + inherit + (builtins) + all + attrNames + partition ; - initialIndent = concatStrings (replicate indentLevel " "); - - toHyprconf' = indent: attrs: let - sections = - filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; + toHyprlang' = attrs: let + toStr = x: + if isBool x + then boolToString x + else toString x; - mkSection = n: attrs: + categories = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; + mkCategory = parent: attrs: if lib.isList attrs - then (concatMapStringsSep "\n" (a: mkSection n a) attrs) - else '' - ${indent}${n} { - ${toHyprconf' " ${indent}" attrs}${indent}} - ''; - - mkFields = generators.toKeyValue { + then concatMapStringsSep "\n" (a: mkCategory parent a) attrs + else + concatStringsSep "\n" ( + mapAttrsToList ( + k: v: + if isAttrs v + then mkCategory "${parent}:${k}" v + else if isList v + then concatMapStringsSep "\n" (item: "${parent}:${k} = ${toStr item}") v + else "${parent}:${k} = ${toStr v}" + ) + attrs + ); + + mkCommands = toKeyValue { + mkKeyValue = mkKeyValueDefault {} " = "; listsAsDuplicateKeys = true; - inherit indent; + indent = ""; }; - allFields = - filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) - attrs; + allCommands = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs; - isImportantField = n: _: - foldl (acc: prev: - if hasPrefix prev n - then true - else acc) - false - importantPrefixes; + filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; - importantFields = filterAttrs isImportantField allFields; + # Get topCommands attr names + result = partition (filterCommands topCommandsPrefixes) (attrNames allCommands); + # Filter top commands from all commands + topCommands = filterAttrs (n: _: (builtins.elem n result.right)) allCommands; + # Remaining commands = allcallCommands - topCommands + remainingCommands = removeAttrs allCommands result.right; - fields = - builtins.removeAttrs allFields - (mapAttrsToList (n: _: n) importantFields); + # Get bottomCommands attr names + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + # Filter bottom commands from remainingCommands + bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands; + # Regular commands = allCommands - topCommands - bottomCommands + regularCommands = removeAttrs remainingCommands result2.right; in - mkFields importantFields - + concatStringsSep "\n" (mapAttrsToList mkSection sections) - + mkFields fields; + mkCommands topCommands + + concatStringsSep "\n" (mapAttrsToList mkCategory categories) + + mkCommands regularCommands + + mkCommands bottomCommands; in - toHyprconf' initialIndent attrs; + toHyprlang' attrs; in { options = { programs.hyprland = { @@ -106,6 +134,9 @@ in { should be written as lists. Variables' and colors' names should be quoted. See for more examples. + Special categories (e.g `devices`) should be written as + `"devices[device-name]"`. + ::: {.note} Use the [](#programs.hyprland.plugins) option to declare plugins. @@ -151,20 +182,21 @@ in { ''; }; - sourceFirst = - lib.mkEnableOption '' - putting source entries at the top of the configuration - '' - // { - default = true; - }; + topPrefixes = lib.mkOption { + type = with lib.types; listOf str; + default = ["$" "bezier"]; + example = ["$" "bezier" "source"]; + description = '' + List of prefix of attributes to put at the top of the config. + ''; + }; - importantPrefixes = lib.mkOption { + bottomPrefixes = lib.mkOption { type = with lib.types; listOf str; - default = ["$" "bezier" "name"] ++ lib.optionals cfg.sourceFirst ["source"]; - example = ["$" "bezier"]; + default = []; + example = ["source"]; description = '' - List of prefix of attributes to source at the top of the config. + List of prefix of attributes to put at the bottom of the config. ''; }; }; @@ -182,29 +214,31 @@ in { environment.etc."xdg/hypr/hyprland.conf" = let shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != []; - pluginsToHyprconf = plugins: - toHyprconf { - attrs = { - plugin = let - mkEntry = entry: - if lib.types.package.check entry - then "${entry}/lib/lib${entry.pname}.so" - else entry; - in - map mkEntry cfg.plugins; - }; - inherit (cfg) importantPrefixes; + pluginsToHyprlang = plugins: + toHyprlang { + topCommandsPrefixes = cfg.topPrefixes; + bottomCommandsPrefixes = cfg.bottomPrefixes; + } + { + plugin = let + mkEntry = entry: + if lib.types.package.check entry + then "${entry}/lib/lib${entry.pname}.so" + else entry; + in + map mkEntry cfg.plugins; }; in lib.mkIf shouldGenerate { text = lib.optionalString (cfg.plugins != []) - (pluginsToHyprconf cfg.plugins) + (pluginsToHyprlang cfg.plugins) + lib.optionalString (cfg.settings != {}) - (toHyprconf { - attrs = cfg.settings; - inherit (cfg) importantPrefixes; - }) + (toHyprlang { + topCommandsPrefixes = cfg.topPrefixes; + bottomCommandsPrefixes = cfg.bottomPrefixes; + } + cfg.settings) + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; }; }) From 0d27fc1946485dddbe93c0aa6b910ccde4bddd86 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Wed, 29 Jan 2025 22:20:31 +0200 Subject: [PATCH 2/6] nix/module: use xdph package directly The downstream module already applies hyprland's finalPackage to the portalPackage. --- nix/module.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nix/module.nix b/nix/module.nix index 31c7868df66..4c07bd9f22a 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -205,9 +205,7 @@ in { { programs.hyprland = { package = lib.mkDefault inputs.self.packages.${system}.hyprland; - portalPackage = lib.mkDefault (inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override { - hyprland = cfg.finalPackage; - }); + portalPackage = lib.mkDefault inputs.self.packages.${system}.xdg-desktop-portal-hyprland; }; } (lib.mkIf cfg.enable { From 241dfd08c9c74cfe497528d527100a6b6a586a43 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Mon, 10 Feb 2025 20:10:40 +0200 Subject: [PATCH 3/6] new lib --- nix/lib.nix | 59 ++++++++++++++++++++++++++++++ nix/module.nix | 98 ++------------------------------------------------ 2 files changed, 62 insertions(+), 95 deletions(-) create mode 100644 nix/lib.nix diff --git a/nix/lib.nix b/nix/lib.nix new file mode 100644 index 00000000000..946bcc32a1a --- /dev/null +++ b/nix/lib.nix @@ -0,0 +1,59 @@ +lib: with lib; { + toHyprlang = { + topCommandsPrefixes ? ["$"], + bottomCommandsPrefixes ? [], + }: attrs: let + toHyprlang' = attrs: let + toStr = x: + if isBool x + then boolToString x + else toString x; + + categories = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; + mkCategory = parent: attrs: + if lib.isList attrs + then concatMapStringsSep "\n" (a: mkCategory parent a) attrs + else + concatStringsSep "\n" ( + mapAttrsToList ( + k: v: + if isAttrs v + then mkCategory "${parent}:${k}" v + else if isList v + then concatMapStringsSep "\n" (item: "${parent}:${k} = ${toStr item}") v + else "${parent}:${k} = ${toStr v}" + ) + attrs + ); + + mkCommands = toKeyValue { + mkKeyValue = mkKeyValueDefault {} " = "; + listsAsDuplicateKeys = true; + indent = ""; + }; + + allCommands = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs; + + filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; + + # Get topCommands attr names + result = partition (filterCommands topCommandsPrefixes) (attrNames allCommands); + # Filter top commands from all commands + topCommands = filterAttrs (n: _: (builtins.elem n result.right)) allCommands; + # Remaining commands = allcallCommands - topCommands + remainingCommands = removeAttrs allCommands result.right; + + # Get bottomCommands attr names + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + # Filter bottom commands from remainingCommands + bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands; + # Regular commands = allCommands - topCommands - bottomCommands + regularCommands = removeAttrs remainingCommands result2.right; + in + mkCommands topCommands + + concatStringsSep "\n" (mapAttrsToList mkCategory categories) + + mkCommands regularCommands + + mkCommands bottomCommands; + in + toHyprlang' attrs; +} diff --git a/nix/module.nix b/nix/module.nix index 4c07bd9f22a..0b8c4f420f4 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -5,100 +5,8 @@ inputs: { ... }: let inherit (pkgs.stdenv.hostPlatform) system; + selflib = import ./lib.nix lib; cfg = config.programs.hyprland; - - toHyprlang = { - topCommandsPrefixes ? ["$"], - bottomCommandsPrefixes ? [], - }: attrs: let - inherit (pkgs) lib; - inherit - (lib.generators) - mkKeyValueDefault - toKeyValue - ; - inherit - (lib.attrsets) - filterAttrs - isAttrs - mapAttrsToList - ; - inherit - (lib.lists) - foldl - isList - ; - inherit - (lib.strings) - concatMapStringsSep - concatStringsSep - hasPrefix - ; - inherit - (lib.trivial) - boolToString - isBool - ; - inherit - (builtins) - all - attrNames - partition - ; - - toHyprlang' = attrs: let - toStr = x: - if isBool x - then boolToString x - else toString x; - - categories = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; - mkCategory = parent: attrs: - if lib.isList attrs - then concatMapStringsSep "\n" (a: mkCategory parent a) attrs - else - concatStringsSep "\n" ( - mapAttrsToList ( - k: v: - if isAttrs v - then mkCategory "${parent}:${k}" v - else if isList v - then concatMapStringsSep "\n" (item: "${parent}:${k} = ${toStr item}") v - else "${parent}:${k} = ${toStr v}" - ) - attrs - ); - - mkCommands = toKeyValue { - mkKeyValue = mkKeyValueDefault {} " = "; - listsAsDuplicateKeys = true; - indent = ""; - }; - - allCommands = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs; - - filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; - - # Get topCommands attr names - result = partition (filterCommands topCommandsPrefixes) (attrNames allCommands); - # Filter top commands from all commands - topCommands = filterAttrs (n: _: (builtins.elem n result.right)) allCommands; - # Remaining commands = allcallCommands - topCommands - remainingCommands = removeAttrs allCommands result.right; - - # Get bottomCommands attr names - result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - # Filter bottom commands from remainingCommands - bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands; - # Regular commands = allCommands - topCommands - bottomCommands - regularCommands = removeAttrs remainingCommands result2.right; - in - mkCommands topCommands - + concatStringsSep "\n" (mapAttrsToList mkCategory categories) - + mkCommands regularCommands - + mkCommands bottomCommands; - in - toHyprlang' attrs; in { options = { programs.hyprland = { @@ -213,7 +121,7 @@ in { shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != []; pluginsToHyprlang = plugins: - toHyprlang { + selflib.toHyprlang { topCommandsPrefixes = cfg.topPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes; } @@ -232,7 +140,7 @@ in { lib.optionalString (cfg.plugins != []) (pluginsToHyprlang cfg.plugins) + lib.optionalString (cfg.settings != {}) - (toHyprlang { + (selflib.toHyprlang { topCommandsPrefixes = cfg.topPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes; } From d723956ac33feddf192a81bfb8301f282313a8a4 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Fri, 14 Feb 2025 22:48:46 +0200 Subject: [PATCH 4/6] lib: add flattenAttrs, remove category processing Flattening attributes means we no longer need to process categories separately. For all intents and purposes, they do not exist. Simplify the codebase once again, while introducing an easy to grasp recursive function. Add a bit of documentation for toHyprlang, though I doubt it's clear enough even now. Still needs proper NixDoc. --- nix/lib.nix | 137 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 37 deletions(-) diff --git a/nix/lib.nix b/nix/lib.nix index 946bcc32a1a..000d111f8b9 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,59 +1,122 @@ -lib: with lib; { +lib: with lib; let toHyprlang = { topCommandsPrefixes ? ["$"], bottomCommandsPrefixes ? [], }: attrs: let toHyprlang' = attrs: let - toStr = x: - if isBool x - then boolToString x - else toString x; - - categories = filterAttrs (n: v: isAttrs v || (isList v && all isAttrs v)) attrs; - mkCategory = parent: attrs: - if lib.isList attrs - then concatMapStringsSep "\n" (a: mkCategory parent a) attrs - else - concatStringsSep "\n" ( - mapAttrsToList ( - k: v: - if isAttrs v - then mkCategory "${parent}:${k}" v - else if isList v - then concatMapStringsSep "\n" (item: "${parent}:${k} = ${toStr item}") v - else "${parent}:${k} = ${toStr v}" - ) - attrs - ); - - mkCommands = toKeyValue { - mkKeyValue = mkKeyValueDefault {} " = "; + # Specially configured `toKeyValue` generator with support for duplicate + # keys and legible key-value separator. + mkCommands = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault {} " = "; listsAsDuplicateKeys = true; + # No indent, since we don't have nesting indent = ""; }; - allCommands = filterAttrs (n: v: !(isAttrs v || (isList v && all isAttrs v))) attrs; + # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. + # See `flattenAttrs` for more info. + commands = flattenAttrs (p: k: "${p}:${k}") attrs; + # General filtering command. Used for filtering top/bottom commands. + # Takes a list of prefixes and an element to check. + # Returns true if any of the prefixes matched, otherwise false. filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; - # Get topCommands attr names - result = partition (filterCommands topCommandsPrefixes) (attrNames allCommands); - # Filter top commands from all commands - topCommands = filterAttrs (n: _: (builtins.elem n result.right)) allCommands; + # FIXME(docs): improve explanations for the below amalgamation + + # Get topCommands attribute names + result = partition (filterCommands topCommandsPrefixes) (attrNames commands); + # Filter top commands from all commands, using the attribute names + # previously obtained + topCommands = filterAttrs (n: _: (builtins.elem n result.right)) commands; # Remaining commands = allcallCommands - topCommands - remainingCommands = removeAttrs allCommands result.right; + remainingCommands = removeAttrs commands result.right; - # Get bottomCommands attr names + # Get bottomCommands attr names from commands remaining (in result.wrong) result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - # Filter bottom commands from remainingCommands + # Filter bottom commands from remainingCommands using the attribute names + # previously obtained bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands; # Regular commands = allCommands - topCommands - bottomCommands regularCommands = removeAttrs remainingCommands result2.right; in - mkCommands topCommands - + concatStringsSep "\n" (mapAttrsToList mkCategory categories) - + mkCommands regularCommands - + mkCommands bottomCommands; + # Concatenate the strings resulting from mapping `mkCommands` over the + # list of commands. + concatMapStrings mkCommands [ + topCommands + regularCommands + bottomCommands + ]; in toHyprlang' attrs; + + + /** + Flatten a nested attribute set into a flat attribute set, joining keys with a user-defined function. + + This function takes a function `pred` that determines how nested keys should be joined, + and an attribute set `attrs` that should be flattened. + + ## Example + + ```nix + let + nested = { + a = "3"; + b = { c = "4"; d = "5"; }; + }; + + separator = (prefix: key: "${prefix}.${key}"); # Use dot notation + in flattenAttrs separator nested + ``` + + **Output:** + ```nix + { + "a" = "3"; + "b.c" = "4"; + "b.d" = "5"; + } + ``` + + ## Parameters + + - **pred** : function `(string -> string -> string)` + A function that takes a prefix and a key and returns the new flattened key. + + - **attrs** : attrset + The nested attribute set to be flattened. + + ## Returns + + - **attrset** : A flattened attribute set where keys are joined according to `pred`. + + ## Notes + + - This function works recursively for any level of nesting. + - It does not modify non-attribute values. + - If `pred` is `prefix: key: "${prefix}.${key}"`, it mimics dot notation. + */ + flattenAttrs = pred: attrs: let + flattenAttrs' = prefix: attrs: + builtins.foldl' ( + acc: key: let + value = attrs.${key}; + newKey = + if prefix == "" + then key + else pred prefix key; + in + acc + // ( + if builtins.isAttrs value + then flattenAttrs' newKey value + else {"${newKey}" = value;} + ) + ) {} (builtins.attrNames attrs); + in + flattenAttrs' "" attrs; +in +{ + inherit flattenAttrs toHyprlang; } From ee6a95df8390d57f0e799014b4fe0886c3a1ca80 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sun, 23 Feb 2025 17:53:13 +0200 Subject: [PATCH 5/6] lib: add proper NixDoc --- nix/lib.nix | 158 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 46 deletions(-) diff --git a/nix/lib.nix b/nix/lib.nix index 000d111f8b9..9207b843407 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,47 +1,105 @@ lib: with lib; let + /** + Convert a structured Nix attribute set into Hyprland's configuration format. + + This function takes a nested attribute set and converts it into Hyprland-compatible + configuration syntax, supporting top, bottom, and regular command sections. + + Commands are flattened using the `flattenAttrs` function, and attributes are formatted as + `key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format. + + Configuration: + + * `topCommandsPrefixes` - A list of prefixes to define **top** commands (default: `["$"]`). + * `bottomCommandsPrefixes` - A list of prefixes to define **bottom** commands (default: `[]`). + + Attention: + + - The function ensures top commands appear **first** and bottom commands **last**. + - The generated configuration is a **single string**, suitable for writing to a config file. + - Lists are converted into multiple entries, ensuring compatibility with Hyprland. + + # Inputs + + Structured function argument: + + : topCommandsPrefixes (optional, default: `["$"]`) + : A list of prefixes that define **top** commands. Any key starting with one of these + prefixes will be placed at the beginning of the configuration. + : bottomCommandsPrefixes (optional, default: `[]`) + : A list of prefixes that define **bottom** commands. Any key starting with one of these + prefixes will be placed at the end of the configuration. + + Value: + + : The attribute set to be converted to Hyprland configuration format. + + # Type + + ``` + toHyprlang :: AttrSet -> AttrSet -> String + ``` + + # Examples + :::{.example} + + ```nix + let + config = { + "$mod" = "SUPER"; + monitor = { + "HDMI-A-1" = "1920x1080@60,0x0,1"; + }; + exec = [ + "waybar" + "dunst" + ]; + }; + in lib.toHyprlang {} config + ``` + + **Output:** + ```nix + "$mod = SUPER" + "monitor:HDMI-A-1 = 1920x1080@60,0x0,1" + "exec = waybar" + "exec = dunst" + ``` + + ::: + */ toHyprlang = { topCommandsPrefixes ? ["$"], bottomCommandsPrefixes ? [], }: attrs: let toHyprlang' = attrs: let - # Specially configured `toKeyValue` generator with support for duplicate - # keys and legible key-value separator. + # Specially configured `toKeyValue` generator with support for duplicate keys + # and a legible key-value separator. mkCommands = generators.toKeyValue { mkKeyValue = generators.mkKeyValueDefault {} " = "; listsAsDuplicateKeys = true; - # No indent, since we don't have nesting - indent = ""; + indent = ""; # No indent, since we don't have nesting }; # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. - # See `flattenAttrs` for more info. + # Uses `flattenAttrs` with a colon separator. commands = flattenAttrs (p: k: "${p}:${k}") attrs; - # General filtering command. Used for filtering top/bottom commands. - # Takes a list of prefixes and an element to check. - # Returns true if any of the prefixes matched, otherwise false. - filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; - - # FIXME(docs): improve explanations for the below amalgamation + # General filtering function to check if a key starts with any prefix in a given list. + filterCommands = list: n: + foldl (acc: prefix: acc || hasPrefix prefix n) false list; - # Get topCommands attribute names + # Partition keys into top commands and the rest result = partition (filterCommands topCommandsPrefixes) (attrNames commands); - # Filter top commands from all commands, using the attribute names - # previously obtained - topCommands = filterAttrs (n: _: (builtins.elem n result.right)) commands; - # Remaining commands = allcallCommands - topCommands + topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; remainingCommands = removeAttrs commands result.right; - # Get bottomCommands attr names from commands remaining (in result.wrong) + # Partition remaining commands into bottom commands and regular commands result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - # Filter bottom commands from remainingCommands using the attribute names - # previously obtained - bottomCommands = filterAttrs (n: _: (builtins.elem n result2.right)) remainingCommands; - # Regular commands = allCommands - topCommands - bottomCommands + bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; regularCommands = removeAttrs remainingCommands result2.right; in - # Concatenate the strings resulting from mapping `mkCommands` over the - # list of commands. + # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. concatMapStrings mkCommands [ topCommands regularCommands @@ -50,14 +108,37 @@ lib: with lib; let in toHyprlang' attrs; - /** - Flatten a nested attribute set into a flat attribute set, joining keys with a user-defined function. + Flatten a nested attribute set into a flat attribute set, using a custom key separator function. + + This function recursively traverses a nested attribute set and produces a flat attribute set + where keys are joined using a user-defined function (`pred`). It allows transforming deeply + nested structures into a single-level attribute set while preserving key-value relationships. + + Configuration: + + * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. + + # Inputs + + Structured function argument: - This function takes a function `pred` that determines how nested keys should be joined, - and an attribute set `attrs` that should be flattened. + : pred (required) + : A function that determines how parent and child keys should be combined into a single key. + It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. + + Value: - ## Example + : The nested attribute set to be flattened. + + # Type + + ``` + flattenAttrs :: (String -> String -> String) -> AttrSet -> AttrSet + ``` + + # Examples + :::{.example} ```nix let @@ -67,7 +148,7 @@ lib: with lib; let }; separator = (prefix: key: "${prefix}.${key}"); # Use dot notation - in flattenAttrs separator nested + in lib.flattenAttrs separator nested ``` **Output:** @@ -79,24 +160,9 @@ lib: with lib; let } ``` - ## Parameters - - - **pred** : function `(string -> string -> string)` - A function that takes a prefix and a key and returns the new flattened key. - - - **attrs** : attrset - The nested attribute set to be flattened. - - ## Returns - - - **attrset** : A flattened attribute set where keys are joined according to `pred`. - - ## Notes + ::: - - This function works recursively for any level of nesting. - - It does not modify non-attribute values. - - If `pred` is `prefix: key: "${prefix}.${key}"`, it mimics dot notation. - */ + */ flattenAttrs = pred: attrs: let flattenAttrs' = prefix: attrs: builtins.foldl' ( From e9c9f931ad7965de1345e5ac66437c80653a4e28 Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Mon, 24 Feb 2025 17:05:49 +0200 Subject: [PATCH 6/6] nix/lib: inherit from lib --- nix/lib.nix | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nix/lib.nix b/nix/lib.nix index 9207b843407..cb5223b97a3 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,4 +1,17 @@ -lib: with lib; let +lib: let + inherit (lib) + attrNames + filterAttrs + foldl + generators + partition + ; + + inherit (lib.strings) + concatMapStrings + hasPrefix + ; + /** Convert a structured Nix attribute set into Hyprland's configuration format.