mirror of
https://gitlab.com/rensa-nix/devshell.git
synced 2025-12-12 22:30:08 +01:00
212 lines
5.4 KiB
Nix
212 lines
5.4 KiB
Nix
|
|
{
|
|||
|
|
pkgs,
|
|||
|
|
lib,
|
|||
|
|
config,
|
|||
|
|
...
|
|||
|
|
}: let
|
|||
|
|
inherit (lib) mkOption types optional mkIf concatMapStrings concatMapStringsSep toUpper splitString;
|
|||
|
|
cfg = config.meta;
|
|||
|
|
|
|||
|
|
printSubtext = subtext:
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
printf " $FG_GRAY ↳ $RESET''${BOLD}${subtext.text}$RESET: ${subtext.desc}\n"
|
|||
|
|
'';
|
|||
|
|
|
|||
|
|
printEntry = entry:
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
printf " $FG_${entry.color}› %-*s$RESET $FG_GRAY%s$RESET\n" "${maxWidth}" "${entry.text}" "${entry.desc}"
|
|||
|
|
${concatMapStrings printSubtext entry.subtexts}
|
|||
|
|
'';
|
|||
|
|
printGroup = group:
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
printf " $FG_WHITE$RESET$BG_WHITE$FG_BLACK %s $RESET$FG_WHITE$RESET\n" "${group.title}"
|
|||
|
|
${concatMapStrings printEntry group.entries}
|
|||
|
|
'';
|
|||
|
|
|
|||
|
|
calculateMaxWidth = groups: let
|
|||
|
|
lengths = builtins.concatLists (builtins.map (
|
|||
|
|
group:
|
|||
|
|
builtins.map (entry: builtins.stringLength entry.text) group.entries
|
|||
|
|
)
|
|||
|
|
groups);
|
|||
|
|
|
|||
|
|
max = a: b:
|
|||
|
|
if a > b
|
|||
|
|
then a
|
|||
|
|
else b;
|
|||
|
|
maxLength =
|
|||
|
|
if lengths == []
|
|||
|
|
then 0
|
|||
|
|
else builtins.foldl' max 0 lengths;
|
|||
|
|
in
|
|||
|
|
maxLength;
|
|||
|
|
maxWidth = builtins.toString (calculateMaxWidth result);
|
|||
|
|
|
|||
|
|
printHeaderDescription = text:
|
|||
|
|
concatMapStringsSep "\n" (line:
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
printf " $FG_BLUE│$RESET $FG_WHITE%s$RESET\n" "${line}"
|
|||
|
|
'') (splitString "\n" text);
|
|||
|
|
|
|||
|
|
menuPackage = pkgs.writeTextFile {
|
|||
|
|
name = "menu";
|
|||
|
|
destination = "/bin/menu";
|
|||
|
|
executable = true;
|
|||
|
|
text =
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
BOLD=$(tput bold)
|
|||
|
|
RESET=$(tput sgr0)
|
|||
|
|
FG_BLACK=$(tput setaf 0)
|
|||
|
|
FG_RED=$(tput setaf 1)
|
|||
|
|
FG_GREEN=$(tput setaf 2)
|
|||
|
|
FG_YELLOW=$(tput setaf 3)
|
|||
|
|
FG_BLUE=$(tput setaf 4)
|
|||
|
|
FG_MAGENTA=$(tput setaf 5)
|
|||
|
|
FG_CYAN=$(tput setaf 6)
|
|||
|
|
FG_WHITE=$(tput setaf 7)
|
|||
|
|
BG_WHITE=$(tput setab 7)
|
|||
|
|
FG_GRAY=$(tput setaf 8)
|
|||
|
|
|
|||
|
|
printf " $FG_BLUE╭─┄┄┈┈$RESET\n"
|
|||
|
|
printf " $FG_BLUE│$RESET $FG_YELLOW$BOLD%s$RESET\n" "${config.name}"
|
|||
|
|
${printHeaderDescription cfg.description}
|
|||
|
|
printf " $FG_BLUE╰─┄┄┈┈$RESET\n"
|
|||
|
|
printf "\n"
|
|||
|
|
|
|||
|
|
${concatMapStringsSep "printf '\n'\n" printGroup result}
|
|||
|
|
'';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
groupedEntries = builtins.foldl' (
|
|||
|
|
acc: entryName: let
|
|||
|
|
entry = cfg.entries.${entryName};
|
|||
|
|
sectionName = entry.section;
|
|||
|
|
section = cfg.sections.${sectionName} or {};
|
|||
|
|
|
|||
|
|
newEntry = {
|
|||
|
|
text = entry.name;
|
|||
|
|
desc = entry.description or "";
|
|||
|
|
color = section.color or "BLUE";
|
|||
|
|
subtexts = map (name: {
|
|||
|
|
text = name;
|
|||
|
|
desc = entry.subEntries.${name};
|
|||
|
|
}) (builtins.attrNames entry.subEntries);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
updatedSection =
|
|||
|
|
if acc ? ${sectionName}
|
|||
|
|
then acc.${sectionName} ++ [newEntry]
|
|||
|
|
else [newEntry];
|
|||
|
|
in
|
|||
|
|
acc // {${sectionName} = updatedSection;}
|
|||
|
|
) {} (builtins.attrNames cfg.entries);
|
|||
|
|
|
|||
|
|
result = builtins.map (sectionName: {
|
|||
|
|
title = sectionName;
|
|||
|
|
entries = groupedEntries.${sectionName} or [];
|
|||
|
|
}) (builtins.attrNames groupedEntries);
|
|||
|
|
in {
|
|||
|
|
options.meta = {
|
|||
|
|
enableMenu = mkOption {
|
|||
|
|
type = types.bool;
|
|||
|
|
default = true;
|
|||
|
|
description = ''
|
|||
|
|
Add a `menu` command which shows a nice overview/summary of the devshell.
|
|||
|
|
'';
|
|||
|
|
};
|
|||
|
|
showMenu = mkOption {
|
|||
|
|
type = types.enum ["always" "once" "never"];
|
|||
|
|
default = "once";
|
|||
|
|
description = ''
|
|||
|
|
When to show the devshell menu. `once` uses a state file in `$REN_STATE`
|
|||
|
|
to keep track of the first time.
|
|||
|
|
'';
|
|||
|
|
};
|
|||
|
|
description = mkOption {
|
|||
|
|
type = types.str;
|
|||
|
|
default = "Welcome to the devshell.";
|
|||
|
|
description = '''';
|
|||
|
|
};
|
|||
|
|
sections = mkOption {
|
|||
|
|
type = types.attrsOf (types.submodule ({name, ...}: {
|
|||
|
|
options = {
|
|||
|
|
name = mkOption {
|
|||
|
|
type = types.str;
|
|||
|
|
default = name;
|
|||
|
|
};
|
|||
|
|
color = mkOption {
|
|||
|
|
type = types.nullOr types.str;
|
|||
|
|
default = "blue";
|
|||
|
|
apply = toUpper;
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
}));
|
|||
|
|
default = {
|
|||
|
|
"general" = {};
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
entries = mkOption {
|
|||
|
|
type = types.attrsOf (types.submodule ({name, ...}: {
|
|||
|
|
options = {
|
|||
|
|
name = mkOption {
|
|||
|
|
type = types.str;
|
|||
|
|
default = name;
|
|||
|
|
};
|
|||
|
|
section = mkOption {
|
|||
|
|
type = types.str;
|
|||
|
|
default = "general";
|
|||
|
|
};
|
|||
|
|
description = mkOption {
|
|||
|
|
type = types.nullOr types.str;
|
|||
|
|
default = null;
|
|||
|
|
};
|
|||
|
|
subEntries = mkOption {
|
|||
|
|
type = types.attrs;
|
|||
|
|
default = {};
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
}));
|
|||
|
|
default = {};
|
|||
|
|
};
|
|||
|
|
finalMetadata = mkOption {
|
|||
|
|
type = types.attrs;
|
|||
|
|
internal = true;
|
|||
|
|
};
|
|||
|
|
finalJson = mkOption {
|
|||
|
|
type = types.package;
|
|||
|
|
internal = true;
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
config = {
|
|||
|
|
meta = rec {
|
|||
|
|
finalMetadata = {
|
|||
|
|
inherit (config) name;
|
|||
|
|
# inherit (cfg) sections entries;
|
|||
|
|
sections = result;
|
|||
|
|
};
|
|||
|
|
finalJson = builtins.toFile "meta.json" (builtins.toJSON finalMetadata);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
packages = optional cfg.enableMenu menuPackage;
|
|||
|
|
# shows the menu when entering the devshell
|
|||
|
|
enterShellCommands."menu".text = mkIf (cfg.enableMenu && cfg.showMenu != "never") (
|
|||
|
|
if cfg.showMenu == "always"
|
|||
|
|
then "menu"
|
|||
|
|
else
|
|||
|
|
# sh
|
|||
|
|
''
|
|||
|
|
if [[ ! -f "$REN_STATE/${config.name}_menu_shown" ]]; then
|
|||
|
|
touch "$REN_STATE/${config.name}_menu_shown"
|
|||
|
|
menu
|
|||
|
|
fi
|
|||
|
|
''
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
}
|