275 lines
8.4 KiB
Nix
275 lines
8.4 KiB
Nix
{ config, pkgs, lib, extendedLib, ... }: let
|
|
cfg = config.local.shell;
|
|
|
|
shellAliasesFormat = let
|
|
formatLib = {
|
|
functors = let
|
|
inherit (extendedLib.termColors.front) blue;
|
|
in
|
|
{
|
|
"|" = {
|
|
apply = f: lib.concatStringsSep " | " f.alias;
|
|
stringify = f: lib.concatStringsSep (blue "\n| ") f.alias;
|
|
};
|
|
"&&" = {
|
|
apply = f: lib.concatStringsSep " && " f.alias;
|
|
stringify = f: lib.concatStringsSep (blue "\n&& ") f.alias;
|
|
};
|
|
";" = {
|
|
apply = f: lib.concatStringsSep "; " f.alias;
|
|
stringify = f: lib.concatStringsSep (blue ";\n ") f.alias;
|
|
};
|
|
" " = {
|
|
apply = f: lib.concatStringsSep " " f.alias;
|
|
stringify = f: lib.concatStringsSep " \\\n " f.alias;
|
|
};
|
|
};
|
|
|
|
isAlias = v: builtins.isAttrs v && v ? alias;
|
|
};
|
|
in {
|
|
lib = formatLib;
|
|
|
|
type = let
|
|
rawAliasType = lib.types.submodule {
|
|
options = {
|
|
type = lib.mkOption {
|
|
description = "If the alias is a list of commands, this is the kind of separator that will be used.";
|
|
type = lib.types.enum (lib.attrNames formatLib.functors);
|
|
default = ";";
|
|
example = "&&";
|
|
};
|
|
alias = lib.mkOption {
|
|
description = "List of commands that will be concatenated together.";
|
|
type = with lib.types; listOf str;
|
|
example = [
|
|
"ls"
|
|
"grep nix"
|
|
];
|
|
};
|
|
shells = lib.mkOption {
|
|
description = "List of shells for which the alias is valid.";
|
|
type = with lib.types; listOf (enum [
|
|
"bash"
|
|
"zsh"
|
|
"fish"
|
|
"nushell"
|
|
]);
|
|
default = [
|
|
"bash"
|
|
"zsh"
|
|
"fish"
|
|
"nushell"
|
|
];
|
|
example = [
|
|
"bash"
|
|
"zsh"
|
|
];
|
|
};
|
|
# TODO:
|
|
# subshell = lib.mkEnableOption "" // {
|
|
# description = "Whether to run the aliased command in a subshell";
|
|
# };
|
|
};
|
|
};
|
|
|
|
coercedAliasType = with lib.types; let
|
|
coerce = str: {
|
|
type = " ";
|
|
alias = [ str ];
|
|
};
|
|
in addCheck (coercedTo str coerce rawAliasType) (v: builtins.isString v || formatLib.isAlias v);
|
|
|
|
aliasTreeType = with lib.types; attrsOf (either coercedAliasType aliasTreeType) // {
|
|
description = "Alias tree";
|
|
};
|
|
in aliasTreeType;
|
|
|
|
# Alias Tree -> String -> { :: Alias }
|
|
generateAttrs = shell: let
|
|
generateAttrs' = attrset: lib.pipe attrset [
|
|
(attrset: {
|
|
right = lib.filterAttrs (_: v: formatLib.isAlias v) attrset;
|
|
wrong = lib.filterAttrs (_: v: !(formatLib.isAlias v)) attrset;
|
|
})
|
|
({ right, wrong }:
|
|
# Leaf nodes
|
|
(lib.pipe right [
|
|
(lib.filterAttrs (_: v: lib.elem shell v.shells))
|
|
(builtins.mapAttrs (_: v: formatLib.functors.${v.type}.apply v))
|
|
])
|
|
//
|
|
# Subsets
|
|
(lib.pipe wrong [
|
|
builtins.attrValues
|
|
(map generateAttrs')
|
|
(lib.foldr (a: b: a // b) { })
|
|
])
|
|
)
|
|
];
|
|
in generateAttrs';
|
|
|
|
# Alias Tree -> String
|
|
generateText = aliases: let
|
|
inherit (extendedLib.termColors.front) red green blue;
|
|
|
|
# String -> Alias -> String
|
|
stringifyAlias = aliasName: alias: let
|
|
# String -> String
|
|
removeNixLinks = text: let
|
|
maybeMatches = lib.match "(|.*[^)])(/nix/store/.*/bin/).*" text;
|
|
matches = lib.mapNullable (lib.remove "") maybeMatches;
|
|
in
|
|
if (maybeMatches == null)
|
|
then text
|
|
else lib.replaceStrings matches (lib.replicate (lib.length matches) "") text;
|
|
|
|
# Alias -> String
|
|
applyFunctor = attrset: let
|
|
applied = formatLib.functors.${attrset.type}.stringify attrset;
|
|
indent' = lib.strings.replicate (lib.stringLength "${aliasName} -> ") " ";
|
|
in
|
|
lib.replaceStrings ["\n"] [("\n" + indent')] applied;
|
|
in "${red aliasName} -> ${blue "\""}${removeNixLinks (applyFunctor alias)}${blue "\""}";
|
|
|
|
# String -> String
|
|
indent = x: lib.pipe x [
|
|
(x: "\n" + x)
|
|
(lib.replaceStrings ["\n"] [("\n" + (lib.strings.replicate 2 " "))])
|
|
(lib.removePrefix "\n")
|
|
];
|
|
|
|
# String -> { :: Alias | Category } -> String
|
|
stringifyCategory = categoryName: category: lib.pipe category [
|
|
(category: let
|
|
aliases = lib.filterAttrs (_: formatLib.isAlias) category;
|
|
in {
|
|
inherit aliases;
|
|
subcategories = lib.removeAttrs category (lib.attrNames aliases);
|
|
})
|
|
({ aliases, subcategories }: {
|
|
aliases = lib.mapAttrsToList stringifyAlias aliases;
|
|
subcategories = lib.mapAttrsToList stringifyCategory subcategories;
|
|
})
|
|
({ aliases, subcategories }:
|
|
lib.concatStringsSep "\n" (lib.filter (x: lib.trim x != "") [
|
|
"[${green categoryName}]"
|
|
(indent (lib.concatStringsSep "\n" aliases) + "\n")
|
|
(indent (lib.concatStringsSep "\n" subcategories) + "\n")
|
|
])
|
|
)
|
|
];
|
|
in (stringifyCategory "Aliases" aliases);
|
|
};
|
|
in {
|
|
options.local.shell = {
|
|
aliases = lib.mkOption {
|
|
# TODO: freeformType
|
|
type = shellAliasesFormat.type;
|
|
description = "A tree of aliases";
|
|
default = { };
|
|
example = {
|
|
"My first alias category" = {
|
|
cmd1 = ''echo "hello world"'';
|
|
cmd2 = {
|
|
type = "|";
|
|
alias = [
|
|
"ls -la"
|
|
"grep -i hello"
|
|
];
|
|
};
|
|
};
|
|
|
|
"My second alias category" = {
|
|
cmd1 = {
|
|
type = "&&";
|
|
alias = [
|
|
''echo "hello world"''
|
|
''echo "goodbye world"''
|
|
];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
variables = lib.mkOption {
|
|
type = with lib.types; attrsOf str;
|
|
description = "Environment variables";
|
|
default = { };
|
|
};
|
|
|
|
# TODO: I want a similar system for functions at some point.
|
|
# functions = {
|
|
|
|
# };
|
|
|
|
enablePackageManagerLecture = lib.mkEnableOption "distro reminder messages, aliased to common package manager commands";
|
|
|
|
enableAliasOverview = lib.mkEnableOption "`aliases` command that prints out a list of all aliases" // {
|
|
default = true;
|
|
example = false;
|
|
};
|
|
};
|
|
|
|
config = {
|
|
xdg.dataFile = {
|
|
"aliases".text = shellAliasesFormat.generateText cfg.aliases;
|
|
|
|
"packageManagerLecture" = lib.mkIf cfg.enablePackageManagerLecture {
|
|
target = "package-manager.lecture";
|
|
text = let
|
|
inherit (extendedLib.termColors.front) red blue;
|
|
in lib.concatStringsSep "\n" [
|
|
((red "This package manager is not installed on ") + (blue "NixOS") + (red "."))
|
|
((red "Either use ") + ("\"nix-env -i\"") + (red " or install it through a configuration file."))
|
|
""
|
|
];
|
|
};
|
|
};
|
|
|
|
local.shell.aliases."Package Managers" = lib.mkIf cfg.enablePackageManagerLecture (let
|
|
inherit (lib.attrsets) nameValuePair listToAttrs;
|
|
|
|
packageManagers = [
|
|
"apt"
|
|
"dpkg"
|
|
"flatpak"
|
|
"pacman"
|
|
"pamac"
|
|
"paru"
|
|
"rpm"
|
|
"snap"
|
|
"xbps"
|
|
"yay"
|
|
"yum"
|
|
];
|
|
|
|
command = "${pkgs.coreutils}/bin/cat $HOME/${config.xdg.dataFile.packageManagerLecture.target}";
|
|
nameValuePairs = map (pm: nameValuePair pm command) packageManagers;
|
|
in listToAttrs nameValuePairs);
|
|
|
|
local.shell.aliases.aliases = lib.mkIf cfg.enableAliasOverview
|
|
"${pkgs.coreutils}/bin/cat $HOME/${config.xdg.dataFile.aliases.target}";
|
|
|
|
programs = {
|
|
zsh = {
|
|
shellAliases = shellAliasesFormat.generateAttrs "zsh" cfg.aliases;
|
|
sessionVariables = cfg.variables;
|
|
};
|
|
bash = {
|
|
shellAliases = shellAliasesFormat.generateAttrs "bash" cfg.aliases;
|
|
sessionVariables = cfg.variables;
|
|
};
|
|
fish = {
|
|
shellAliases = shellAliasesFormat.generateAttrs "fish" cfg.aliases;
|
|
# TODO: fish does not support session variables?
|
|
# localVariables = cfg.variables;
|
|
};
|
|
nushell = {
|
|
shellAliases = shellAliasesFormat.generateAttrs "nushell" cfg.aliases;
|
|
environmentVariables = cfg.variables;
|
|
};
|
|
};
|
|
};
|
|
}
|