1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2024-12-14 11:57:55 +00:00

treewide: remove formatting excludes

This removes the last three formatting excludes so that the entire
project is formatted by nixfmt. This is the first step towards moving
to the RFC style format.
This commit is contained in:
Robert Helgesson 2024-12-13 13:20:31 +01:00
parent 3066cc58f5
commit a17d730ab5
No known key found for this signature in database
GPG key ID: 96E745BD17AA17ED
4 changed files with 540 additions and 574 deletions

14
format
View file

@ -23,23 +23,9 @@ for arg do
esac esac
done done
# The excludes are for files touched by open pull requests and we want
# to avoid merge conflicts.
excludes=(
modules/files.nix
modules/home-environment.nix
modules/programs/zsh.nix
)
exclude_args=()
for e in "${excludes[@]}"; do
exclude_args+=(-e "$e")
done
git_root=$(git rev-parse --show-toplevel) git_root=$(git rev-parse --show-toplevel)
git ls-files -z --cached --others --full-name -- "${files[@]}" | git ls-files -z --cached --others --full-name -- "${files[@]}" |
grep -z '\.nix$' | grep -z '\.nix$' |
grep -z -v "${exclude_args[@]}" |
sed -z "s|^|$git_root/|" | sed -z "s|^|$git_root/|" |
xargs -0 nixfmt "${nixfmt_args[@]}" xargs -0 nixfmt "${nixfmt_args[@]}"

View file

@ -8,26 +8,26 @@ let
homeDirectory = config.home.homeDirectory; homeDirectory = config.home.homeDirectory;
fileType = (import lib/file-type.nix { fileType =
inherit homeDirectory lib pkgs; (import lib/file-type.nix { inherit homeDirectory lib pkgs; }).fileType;
}).fileType;
sourceStorePath = file: sourceStorePath = file:
let let
sourcePath = toString file.source; sourcePath = toString file.source;
sourceName = config.lib.strings.storeFileName (baseNameOf sourcePath); sourceName = config.lib.strings.storeFileName (baseNameOf sourcePath);
in in if builtins.hasContext sourcePath then
if builtins.hasContext sourcePath file.source
then file.source else
else builtins.path { path = file.source; name = sourceName; }; builtins.path {
path = file.source;
name = sourceName;
};
in in {
{
options = { options = {
home.file = mkOption { home.file = mkOption {
description = "Attribute set of files to link into the user home."; description = "Attribute set of files to link into the user home.";
default = {}; default = { };
type = fileType "home.file" "{env}`HOME`" homeDirectory; type = fileType "home.file" "{env}`HOME`" homeDirectory;
}; };
@ -39,16 +39,14 @@ in
}; };
config = { config = {
assertions = [( assertions = [
let (let
dups = dups = attrNames (filterAttrs (n: v: v > 1)
attrNames (foldAttrs (acc: v: acc + v) 0
(filterAttrs (n: v: v > 1)
(foldAttrs (acc: v: acc + v) 0
(mapAttrsToList (n: v: { ${v.target} = 1; }) cfg))); (mapAttrsToList (n: v: { ${v.target} = 1; }) cfg)));
dupsStr = concatStringsSep ", " dups; dupsStr = concatStringsSep ", " dups;
in { in {
assertion = dups == []; assertion = dups == [ ];
message = '' message = ''
Conflicting managed target files: ${dupsStr} Conflicting managed target files: ${dupsStr}
@ -65,19 +63,17 @@ in
let let
pathStr = toString path; pathStr = toString path;
name = hm.strings.storeFileName (baseNameOf pathStr); name = hm.strings.storeFileName (baseNameOf pathStr);
in in pkgs.runCommandLocal name { } "ln -s ${escapeShellArg pathStr} $out";
pkgs.runCommandLocal name {} ''ln -s ${escapeShellArg pathStr} $out'';
# This verifies that the links we are about to create will not # This verifies that the links we are about to create will not
# overwrite an existing file. # overwrite an existing file.
home.activation.checkLinkTargets = hm.dag.entryBefore ["writeBoundary"] ( home.activation.checkLinkTargets = hm.dag.entryBefore [ "writeBoundary" ]
let (let
# Paths that should be forcibly overwritten by Home Manager. # Paths that should be forcibly overwritten by Home Manager.
# Caveat emptor! # Caveat emptor!
forcedPaths = forcedPaths =
concatMapStringsSep " " (p: ''"$HOME"/${escapeShellArg p}'') concatMapStringsSep " " (p: ''"$HOME"/${escapeShellArg p}'')
(mapAttrsToList (n: v: v.target) (mapAttrsToList (n: v: v.target) (filterAttrs (n: v: v.force) cfg));
(filterAttrs (n: v: v.force) cfg));
storeDir = escapeShellArg builtins.storeDir; storeDir = escapeShellArg builtins.storeDir;
@ -87,8 +83,7 @@ in
inherit (config.lib.bash) initHomeManagerLib; inherit (config.lib.bash) initHomeManagerLib;
inherit forcedPaths storeDir; inherit forcedPaths storeDir;
}; };
in in ''
''
function checkNewGenCollision() { function checkNewGenCollision() {
local newGenFiles local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")" newGenFiles="$(readlink -e "$newGenPath/home-files")"
@ -97,8 +92,7 @@ in
} }
checkNewGenCollision || exit 1 checkNewGenCollision || exit 1
'' '');
);
# This activation script will # This activation script will
# #
@ -121,129 +115,127 @@ in
# and a failure during the intermediate state FA ∩ FB will not # and a failure during the intermediate state FA ∩ FB will not
# result in lost links because this set of links are in both the # result in lost links because this set of links are in both the
# source and target generation. # source and target generation.
home.activation.linkGeneration = hm.dag.entryAfter ["writeBoundary"] ( home.activation.linkGeneration = hm.dag.entryAfter [ "writeBoundary" ] (let
let link = pkgs.writeShellScript "link" ''
link = pkgs.writeShellScript "link" '' ${config.lib.bash.initHomeManagerLib}
${config.lib.bash.initHomeManagerLib}
newGenFiles="$1" newGenFiles="$1"
shift shift
for sourcePath in "$@" ; do for sourcePath in "$@" ; do
relativePath="''${sourcePath#$newGenFiles/}" relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath" targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" && ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then if [[ -e "$targetPath" && ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
# The target exists, back it up # The target exists, back it up
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT" backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
run mv $VERBOSE_ARG "$targetPath" "$backup" || errorEcho "Moving '$targetPath' failed!" run mv $VERBOSE_ARG "$targetPath" "$backup" || errorEcho "Moving '$targetPath' failed!"
fi
if [[ -e "$targetPath" && ! -L "$targetPath" ]] && cmp -s "$sourcePath" "$targetPath" ; then
# The target exists but is identical don't do anything.
verboseEcho "Skipping '$targetPath' as it is identical to '$sourcePath'"
else
# Place that symlink, --force
# This can still fail if the target is a directory, in which case we bail out.
run mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
run ln -Tsf $VERBOSE_ARG "$sourcePath" "$targetPath" || exit 1
fi
done
'';
cleanup = pkgs.writeShellScript "cleanup" ''
${config.lib.bash.initHomeManagerLib}
# A symbolic link whose target path matches this pattern will be
# considered part of a Home Manager generation.
homeFilePattern="$(readlink -e ${escapeShellArg builtins.storeDir})/*-home-manager-files/*"
newGenFiles="$1"
shift 1
for relativePath in "$@" ; do
targetPath="$HOME/$relativePath"
if [[ -e "$newGenFiles/$relativePath" ]] ; then
verboseEcho "Checking $targetPath: exists"
elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
warnEcho "Path '$targetPath' does not link into a Home Manager generation. Skipping delete."
else
verboseEcho "Checking $targetPath: gone (deleting)"
run rm $VERBOSE_ARG "$targetPath"
# Recursively delete empty parent directories.
targetDir="$(dirname "$relativePath")"
if [[ "$targetDir" != "." ]] ; then
pushd "$HOME" > /dev/null
# Call rmdir with a relative path excluding $HOME.
# Otherwise, it might try to delete $HOME and exit
# with a permission error.
run rmdir $VERBOSE_ARG \
-p --ignore-fail-on-non-empty \
"$targetDir"
popd > /dev/null
fi
fi
done
'';
in
''
function linkNewGen() {
_i "Creating home file links in %s" "$HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) \
-exec bash ${link} "$newGenFiles" {} +
}
function cleanOldGen() {
if [[ ! -v oldGenPath || ! -e "$oldGenPath/home-files" ]] ; then
return
fi
_i "Cleaning up orphan links from %s" "$HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
# Apply the cleanup script on each leaf in the old
# generation. The find command below will print the
# relative path of the entry.
find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \
| xargs -0 bash ${cleanup} "$newGenFiles"
}
cleanOldGen
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
_i "Creating profile generation %s" $newGenNum
if [[ -e "$genProfilePath"/manifest.json ]] ; then
# Remove all packages from "$genProfilePath"
# `nix profile remove '.*' --profile "$genProfilePath"` was not working, so here is a workaround:
nix profile list --profile "$genProfilePath" \
| cut -d ' ' -f 4 \
| xargs -rt $DRY_RUN_CMD nix profile remove $VERBOSE_ARG --profile "$genProfilePath"
run nix profile install $VERBOSE_ARG --profile "$genProfilePath" "$newGenPath"
else
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
fi
run --quiet nix-store --realise "$newGenPath" --add-root "$newGenGcPath" --indirect
if [[ -e "$legacyGenGcPath" ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
else
_i "No change so reusing latest profile generation %s" "$oldGenNum"
fi fi
linkNewGen if [[ -e "$targetPath" && ! -L "$targetPath" ]] && cmp -s "$sourcePath" "$targetPath" ; then
'' # The target exists but is identical don't do anything.
); verboseEcho "Skipping '$targetPath' as it is identical to '$sourcePath'"
else
# Place that symlink, --force
# This can still fail if the target is a directory, in which case we bail out.
run mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
run ln -Tsf $VERBOSE_ARG "$sourcePath" "$targetPath" || exit 1
fi
done
'';
home.activation.checkFilesChanged = hm.dag.entryBefore ["linkGeneration"] ( cleanup = pkgs.writeShellScript "cleanup" ''
let ${config.lib.bash.initHomeManagerLib}
homeDirArg = escapeShellArg homeDirectory;
# A symbolic link whose target path matches this pattern will be
# considered part of a Home Manager generation.
homeFilePattern="$(readlink -e ${
escapeShellArg builtins.storeDir
})/*-home-manager-files/*"
newGenFiles="$1"
shift 1
for relativePath in "$@" ; do
targetPath="$HOME/$relativePath"
if [[ -e "$newGenFiles/$relativePath" ]] ; then
verboseEcho "Checking $targetPath: exists"
elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
warnEcho "Path '$targetPath' does not link into a Home Manager generation. Skipping delete."
else
verboseEcho "Checking $targetPath: gone (deleting)"
run rm $VERBOSE_ARG "$targetPath"
# Recursively delete empty parent directories.
targetDir="$(dirname "$relativePath")"
if [[ "$targetDir" != "." ]] ; then
pushd "$HOME" > /dev/null
# Call rmdir with a relative path excluding $HOME.
# Otherwise, it might try to delete $HOME and exit
# with a permission error.
run rmdir $VERBOSE_ARG \
-p --ignore-fail-on-non-empty \
"$targetDir"
popd > /dev/null
fi
fi
done
'';
in ''
function linkNewGen() {
_i "Creating home file links in %s" "$HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) \
-exec bash ${link} "$newGenFiles" {} +
}
function cleanOldGen() {
if [[ ! -v oldGenPath || ! -e "$oldGenPath/home-files" ]] ; then
return
fi
_i "Cleaning up orphan links from %s" "$HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
# Apply the cleanup script on each leaf in the old
# generation. The find command below will print the
# relative path of the entry.
find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \
| xargs -0 bash ${cleanup} "$newGenFiles"
}
cleanOldGen
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
_i "Creating profile generation %s" $newGenNum
if [[ -e "$genProfilePath"/manifest.json ]] ; then
# Remove all packages from "$genProfilePath"
# `nix profile remove '.*' --profile "$genProfilePath"` was not working, so here is a workaround:
nix profile list --profile "$genProfilePath" \
| cut -d ' ' -f 4 \
| xargs -rt $DRY_RUN_CMD nix profile remove $VERBOSE_ARG --profile "$genProfilePath"
run nix profile install $VERBOSE_ARG --profile "$genProfilePath" "$newGenPath"
else
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
fi
run --quiet nix-store --realise "$newGenPath" --add-root "$newGenGcPath" --indirect
if [[ -e "$legacyGenGcPath" ]]; then
run rm $VERBOSE_ARG "$legacyGenGcPath"
fi
else
_i "No change so reusing latest profile generation %s" "$oldGenNum"
fi
linkNewGen
'');
home.activation.checkFilesChanged = hm.dag.entryBefore [ "linkGeneration" ]
(let homeDirArg = escapeShellArg homeDirectory;
in '' in ''
function _cmp() { function _cmp() {
if [[ -d $1 && -d $2 ]]; then if [[ -d $1 && -d $2 ]]; then
@ -261,14 +253,12 @@ in
_cmp ${sourceArg} ${homeDirArg}/${targetArg} \ _cmp ${sourceArg} ${homeDirArg}/${targetArg} \
&& changedFiles[${targetArg}]=0 \ && changedFiles[${targetArg}]=0 \
|| changedFiles[${targetArg}]=1 || changedFiles[${targetArg}]=1
'') (filter (v: v.onChange != "") (attrValues cfg)) '') (filter (v: v.onChange != "") (attrValues cfg)) + ''
+ '' unset -f _cmp
unset -f _cmp '');
''
);
home.activation.onFilesChange = hm.dag.entryAfter ["linkGeneration"] ( home.activation.onFilesChange = hm.dag.entryAfter [ "linkGeneration" ]
concatMapStrings (v: '' (concatMapStrings (v: ''
if (( ''${changedFiles[${escapeShellArg v.target}]} == 1 )); then if (( ''${changedFiles[${escapeShellArg v.target}]} == 1 )); then
if [[ -v DRY_RUN || -v VERBOSE ]]; then if [[ -v DRY_RUN || -v VERBOSE ]]; then
echo "Running onChange hook for" ${escapeShellArg v.target} echo "Running onChange hook for" ${escapeShellArg v.target}
@ -277,90 +267,83 @@ in
${v.onChange} ${v.onChange}
fi fi
fi fi
'') (filter (v: v.onChange != "") (attrValues cfg)) '') (filter (v: v.onChange != "") (attrValues cfg)));
);
# Symlink directories and files that have the right execute bit. # Symlink directories and files that have the right execute bit.
# Copy files that need their execute bit changed. # Copy files that need their execute bit changed.
home-files = pkgs.runCommandLocal home-files = pkgs.runCommandLocal "home-manager-files" {
"home-manager-files" nativeBuildInputs = [ pkgs.xorg.lndir ];
{ } (''
nativeBuildInputs = [ pkgs.xorg.lndir ]; mkdir -p $out
}
(''
mkdir -p $out
# Needed in case /nix is a symbolic link. # Needed in case /nix is a symbolic link.
realOut="$(realpath -m "$out")" realOut="$(realpath -m "$out")"
function insertFile() { function insertFile() {
local source="$1" local source="$1"
local relTarget="$2" local relTarget="$2"
local executable="$3" local executable="$3"
local recursive="$4" local recursive="$4"
# If the target already exists then we have a collision. Note, this # If the target already exists then we have a collision. Note, this
# should not happen due to the assertion found in the 'files' module. # should not happen due to the assertion found in the 'files' module.
# We therefore simply log the conflict and otherwise ignore it, mainly # We therefore simply log the conflict and otherwise ignore it, mainly
# to make the `files-target-config` test work as expected. # to make the `files-target-config` test work as expected.
if [[ -e "$realOut/$relTarget" ]]; then if [[ -e "$realOut/$relTarget" ]]; then
echo "File conflict for file '$relTarget'" >&2 echo "File conflict for file '$relTarget'" >&2
return return
fi fi
# Figure out the real absolute path to the target. # Figure out the real absolute path to the target.
local target local target
target="$(realpath -m "$realOut/$relTarget")" target="$(realpath -m "$realOut/$relTarget")"
# Target path must be within $HOME. # Target path must be within $HOME.
if [[ ! $target == $realOut* ]] ; then if [[ ! $target == $realOut* ]] ; then
echo "Error installing file '$relTarget' outside \$HOME" >&2 echo "Error installing file '$relTarget' outside \$HOME" >&2
exit 1 exit 1
fi fi
mkdir -p "$(dirname "$target")" mkdir -p "$(dirname "$target")"
if [[ -d $source ]]; then if [[ -d $source ]]; then
if [[ $recursive ]]; then if [[ $recursive ]]; then
mkdir -p "$target" mkdir -p "$target"
lndir -silent "$source" "$target" lndir -silent "$source" "$target"
else
ln -s "$source" "$target"
fi
else else
[[ -x $source ]] && isExecutable=1 || isExecutable="" ln -s "$source" "$target"
fi
else
[[ -x $source ]] && isExecutable=1 || isExecutable=""
# Link the file into the home file directory if possible, # Link the file into the home file directory if possible,
# i.e., if the executable bit of the source is the same we # i.e., if the executable bit of the source is the same we
# expect for the target. Otherwise, we copy the file and # expect for the target. Otherwise, we copy the file and
# set the executable bit to the expected value. # set the executable bit to the expected value.
if [[ $executable == inherit || $isExecutable == $executable ]]; then if [[ $executable == inherit || $isExecutable == $executable ]]; then
ln -s "$source" "$target" ln -s "$source" "$target"
else
cp "$source" "$target"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else else
cp "$source" "$target" chmod -x "$target"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else
chmod -x "$target"
fi
fi fi
fi fi
} fi
'' + concatStrings ( }
mapAttrsToList (n: v: '' '' + concatStrings (mapAttrsToList (n: v: ''
insertFile ${ insertFile ${
escapeShellArgs [ escapeShellArgs [
(sourceStorePath v) (sourceStorePath v)
v.target v.target
(if v.executable == null (if v.executable == null then "inherit" else toString v.executable)
then "inherit" (toString v.recursive)
else toString v.executable) ]
(toString v.recursive) }
]} '') cfg));
'') cfg
));
}; };
} }

View file

@ -113,10 +113,10 @@ let
options = { options = {
layout = mkOption { layout = mkOption {
type = with types; nullOr str; type = with types; nullOr str;
default = default = if versionAtLeast config.home.stateVersion "19.09" then
if versionAtLeast config.home.stateVersion "19.09" null
then null else
else "us"; "us";
defaultText = literalExpression "null"; defaultText = literalExpression "null";
description = '' description = ''
Keyboard layout. If `null`, then the system Keyboard layout. If `null`, then the system
@ -138,8 +138,8 @@ let
options = mkOption { options = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
example = ["grp:caps_toggle" "grp_led:scroll"]; example = [ "grp:caps_toggle" "grp_led:scroll" ];
description = '' description = ''
X keyboard options; layout switching goes here. X keyboard options; layout switching goes here.
''; '';
@ -148,9 +148,7 @@ let
variant = mkOption { variant = mkOption {
type = with types; nullOr str; type = with types; nullOr str;
default = default =
if versionAtLeast config.home.stateVersion "19.09" if versionAtLeast config.home.stateVersion "19.09" then null else "";
then null
else "";
defaultText = literalExpression "null"; defaultText = literalExpression "null";
example = "colemak"; example = "colemak";
description = '' description = ''
@ -164,9 +162,7 @@ let
}; };
}; };
in in {
{
meta.maintainers = [ maintainers.rycee ]; meta.maintainers = [ maintainers.rycee ];
imports = [ imports = [
@ -217,7 +213,7 @@ in
home.language = mkOption { home.language = mkOption {
type = languageSubModule; type = languageSubModule;
default = {}; default = { };
description = "Language configuration."; description = "Language configuration.";
}; };
@ -255,9 +251,12 @@ in
}; };
home.sessionVariables = mkOption { home.sessionVariables = mkOption {
default = {}; default = { };
type = with types; lazyAttrsOf (oneOf [ str path int float ]); type = with types; lazyAttrsOf (oneOf [ str path int float ]);
example = { EDITOR = "emacs"; GS_OPTIONS = "-sPAPERSIZE=a4"; }; example = {
EDITOR = "emacs";
GS_OPTIONS = "-sPAPERSIZE=a4";
};
description = '' description = ''
Environment variables to always set at login. Environment variables to always set at login.
@ -332,13 +331,13 @@ in
home.packages = mkOption { home.packages = mkOption {
type = types.listOf types.package; type = types.listOf types.package;
default = []; default = [ ];
description = "The set of packages to appear in the user environment."; description = "The set of packages to appear in the user environment.";
}; };
home.extraOutputsToInstall = mkOption { home.extraOutputsToInstall = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
example = [ "doc" "info" "devdoc" ]; example = [ "doc" "info" "devdoc" ];
description = '' description = ''
List of additional package outputs of the packages List of additional package outputs of the packages
@ -371,7 +370,7 @@ in
home.activation = mkOption { home.activation = mkOption {
type = hm.types.dagOf types.str; type = hm.types.dagOf types.str;
default = {}; default = { };
example = literalExpression '' example = literalExpression ''
{ {
myActivationAction = lib.hm.dag.entryAfter ["writeBoundary"] ''' myActivationAction = lib.hm.dag.entryAfter ["writeBoundary"] '''
@ -494,77 +493,60 @@ in
} }
]; ];
warnings = warnings = let
let hmRelease = config.home.version.release;
hmRelease = config.home.version.release; nixpkgsRelease = lib.trivial.release;
nixpkgsRelease = lib.trivial.release; releaseMismatch = config.home.enableNixpkgsReleaseCheck && hmRelease
releaseMismatch = != nixpkgsRelease;
config.home.enableNixpkgsReleaseCheck in optional releaseMismatch ''
&& hmRelease != nixpkgsRelease; You are using
in
optional releaseMismatch ''
You are using
Home Manager version ${hmRelease} and Home Manager version ${hmRelease} and
Nixpkgs version ${nixpkgsRelease}. Nixpkgs version ${nixpkgsRelease}.
Using mismatched versions is likely to cause errors and unexpected Using mismatched versions is likely to cause errors and unexpected
behavior. It is therefore highly recommended to use a release of Home behavior. It is therefore highly recommended to use a release of Home
Manager that corresponds with your chosen release of Nixpkgs. Manager that corresponds with your chosen release of Nixpkgs.
If you insist then you can disable this warning by adding If you insist then you can disable this warning by adding
home.enableNixpkgsReleaseCheck = false; home.enableNixpkgsReleaseCheck = false;
to your configuration. to your configuration.
''; '';
home.username = home.username = mkIf (versionOlder config.home.stateVersion "20.09")
mkIf (versionOlder config.home.stateVersion "20.09") (mkDefault (builtins.getEnv "USER"));
(mkDefault (builtins.getEnv "USER")); home.homeDirectory = mkIf (versionOlder config.home.stateVersion "20.09")
home.homeDirectory = (mkDefault (builtins.getEnv "HOME"));
mkIf (versionOlder config.home.stateVersion "20.09")
(mkDefault (builtins.getEnv "HOME"));
home.profileDirectory = home.profileDirectory = if config.submoduleSupport.enable
if config.submoduleSupport.enable && config.submoduleSupport.externalPackageInstall then
&& config.submoduleSupport.externalPackageInstall "/etc/profiles/per-user/${cfg.username}"
then "/etc/profiles/per-user/${cfg.username}" else if config.nix.enable
else if config.nix.enable && (config.nix.settings.use-xdg-base-directories or false) && (config.nix.settings.use-xdg-base-directories or false) then
then "${config.xdg.stateHome}/nix/profile" "${config.xdg.stateHome}/nix/profile"
else cfg.homeDirectory + "/.nix-profile"; else
cfg.homeDirectory + "/.nix-profile";
programs.bash.shellAliases = cfg.shellAliases; programs.bash.shellAliases = cfg.shellAliases;
programs.zsh.shellAliases = cfg.shellAliases; programs.zsh.shellAliases = cfg.shellAliases;
programs.fish.shellAliases = cfg.shellAliases; programs.fish.shellAliases = cfg.shellAliases;
home.sessionVariables = home.sessionVariables =
let let maybeSet = n: v: optionalAttrs (v != null) { ${n} = v; };
maybeSet = n: v: optionalAttrs (v != null) { ${n} = v; }; in (maybeSet "LANG" cfg.language.base)
in // (maybeSet "LC_CTYPE" cfg.language.ctype)
(maybeSet "LANG" cfg.language.base) // (maybeSet "LC_NUMERIC" cfg.language.numeric)
// // (maybeSet "LC_TIME" cfg.language.time)
(maybeSet "LC_CTYPE" cfg.language.ctype) // (maybeSet "LC_COLLATE" cfg.language.collate)
// // (maybeSet "LC_MONETARY" cfg.language.monetary)
(maybeSet "LC_NUMERIC" cfg.language.numeric) // (maybeSet "LC_MESSAGES" cfg.language.messages)
// // (maybeSet "LC_PAPER" cfg.language.paper)
(maybeSet "LC_TIME" cfg.language.time) // (maybeSet "LC_NAME" cfg.language.name)
// // (maybeSet "LC_ADDRESS" cfg.language.address)
(maybeSet "LC_COLLATE" cfg.language.collate) // (maybeSet "LC_TELEPHONE" cfg.language.telephone)
// // (maybeSet "LC_MEASUREMENT" cfg.language.measurement);
(maybeSet "LC_MONETARY" cfg.language.monetary)
//
(maybeSet "LC_MESSAGES" cfg.language.messages)
//
(maybeSet "LC_PAPER" cfg.language.paper)
//
(maybeSet "LC_NAME" cfg.language.name)
//
(maybeSet "LC_ADDRESS" cfg.language.address)
//
(maybeSet "LC_TELEPHONE" cfg.language.telephone)
//
(maybeSet "LC_MEASUREMENT" cfg.language.measurement);
# Provide a file holding all session variables. # Provide a file holding all session variables.
home.sessionVariablesPackage = pkgs.writeTextFile { home.sessionVariablesPackage = pkgs.writeTextFile {
@ -602,148 +584,129 @@ in
# In case the user has moved from a user-install of Home Manager # In case the user has moved from a user-install of Home Manager
# to a submodule managed one we attempt to uninstall the # to a submodule managed one we attempt to uninstall the
# `home-manager-path` package if it is installed. # `home-manager-path` package if it is installed.
home.activation.installPackages = hm.dag.entryAfter ["writeBoundary"] ( home.activation.installPackages = hm.dag.entryAfter [ "writeBoundary" ]
if config.submoduleSupport.externalPackageInstall (if config.submoduleSupport.externalPackageInstall then ''
then nixProfileRemove home-manager-path
'' '' else ''
nixProfileRemove home-manager-path function nixReplaceProfile() {
'' local oldNix="$(command -v nix)"
else
''
function nixReplaceProfile() {
local oldNix="$(command -v nix)"
nixProfileRemove 'home-manager-path' nixProfileRemove 'home-manager-path'
run $oldNix profile install $1 run $oldNix profile install $1
} }
if [[ -e ${cfg.profileDirectory}/manifest.json ]] ; then if [[ -e ${cfg.profileDirectory}/manifest.json ]] ; then
INSTALL_CMD="nix profile install" INSTALL_CMD="nix profile install"
INSTALL_CMD_ACTUAL="nixReplaceProfile" INSTALL_CMD_ACTUAL="nixReplaceProfile"
LIST_CMD="nix profile list" LIST_CMD="nix profile list"
REMOVE_CMD_SYNTAX='nix profile remove {number | store path}' REMOVE_CMD_SYNTAX='nix profile remove {number | store path}'
else else
INSTALL_CMD="nix-env -i" INSTALL_CMD="nix-env -i"
INSTALL_CMD_ACTUAL="run nix-env -i" INSTALL_CMD_ACTUAL="run nix-env -i"
LIST_CMD="nix-env -q" LIST_CMD="nix-env -q"
REMOVE_CMD_SYNTAX='nix-env -e {package name}' REMOVE_CMD_SYNTAX='nix-env -e {package name}'
fi fi
if ! $INSTALL_CMD_ACTUAL ${cfg.path} ; then if ! $INSTALL_CMD_ACTUAL ${cfg.path} ; then
echo echo
_iError $'Oops, Nix failed to install your new Home Manager profile!\n\nPerhaps there is a conflict with a package that was installed using\n"%s"? Try running\n\n %s\n\nand if there is a conflicting package you can remove it with\n\n %s\n\nThen try activating your Home Manager configuration again.' "$INSTALL_CMD" "$LIST_CMD" "$REMOVE_CMD_SYNTAX" _iError $'Oops, Nix failed to install your new Home Manager profile!\n\nPerhaps there is a conflict with a package that was installed using\n"%s"? Try running\n\n %s\n\nand if there is a conflicting package you can remove it with\n\n %s\n\nThen try activating your Home Manager configuration again.' "$INSTALL_CMD" "$LIST_CMD" "$REMOVE_CMD_SYNTAX"
exit 1 exit 1
fi fi
unset -f nixReplaceProfile unset -f nixReplaceProfile
unset INSTALL_CMD INSTALL_CMD_ACTUAL LIST_CMD REMOVE_CMD_SYNTAX unset INSTALL_CMD INSTALL_CMD_ACTUAL LIST_CMD REMOVE_CMD_SYNTAX
'' '');
);
# Text containing Bash commands that will initialize the Home Manager Bash # Text containing Bash commands that will initialize the Home Manager Bash
# library. Most importantly, this will prepare for using translated strings # library. Most importantly, this will prepare for using translated strings
# in the `hm-modules` text domain. # in the `hm-modules` text domain.
lib.bash.initHomeManagerLib = lib.bash.initHomeManagerLib = let
let domainDir = pkgs.runCommand "hm-modules-messages" {
domainDir = pkgs.runCommand "hm-modules-messages" { nativeBuildInputs = [ pkgs.buildPackages.gettext ];
nativeBuildInputs = [ pkgs.buildPackages.gettext ]; } ''
} '' for path in ${./po}/*.po; do
for path in ${./po}/*.po; do lang="''${path##*/}"
lang="''${path##*/}" lang="''${lang%%.*}"
lang="''${lang%%.*}" mkdir -p "$out/$lang/LC_MESSAGES"
mkdir -p "$out/$lang/LC_MESSAGES" msgfmt -o "$out/$lang/LC_MESSAGES/hm-modules.mo" "$path"
msgfmt -o "$out/$lang/LC_MESSAGES/hm-modules.mo" "$path" done
done '';
''; in ''
in export TEXTDOMAIN=hm-modules
'' export TEXTDOMAINDIR=${domainDir}
export TEXTDOMAIN=hm-modules source ${../lib/bash/home-manager.sh}
export TEXTDOMAINDIR=${domainDir} '';
source ${../lib/bash/home-manager.sh}
'';
home.activationPackage = home.activationPackage = let
let mkCmd = res: ''
mkCmd = res: '' _iNote "Activating %s" "${res.name}"
_iNote "Activating %s" "${res.name}" ${res.data}
${res.data} '';
''; sortedCommands = hm.dag.topoSort cfg.activation;
sortedCommands = hm.dag.topoSort cfg.activation; activationCmds = if sortedCommands ? result then
activationCmds = concatStringsSep "\n" (map mkCmd sortedCommands.result)
if sortedCommands ? result then else
concatStringsSep "\n" (map mkCmd sortedCommands.result) abort ("Dependency cycle in activation script: "
else + builtins.toJSON sortedCommands);
abort ("Dependency cycle in activation script: "
+ builtins.toJSON sortedCommands);
# Programs that always should be available on the activation # Programs that always should be available on the activation
# script's PATH. # script's PATH.
activationBinPaths = lib.makeBinPath ( activationBinPaths = lib.makeBinPath (with pkgs;
with pkgs; [ [
bash bash
coreutils coreutils
diffutils # For `cmp` and `diff`. diffutils # For `cmp` and `diff`.
findutils findutils
gettext gettext
gnugrep gnugrep
gnused gnused
jq jq
ncurses # For `tput`. ncurses # For `tput`.
] ] ++ config.home.extraActivationPath) + (
++ config.home.extraActivationPath
)
+ (
# Add path of the Nix binaries, if a Nix package is configured, then # Add path of the Nix binaries, if a Nix package is configured, then
# use that one, otherwise grab the path of the nix-env tool. # use that one, otherwise grab the path of the nix-env tool.
if config.nix.enable && config.nix.package != null then if config.nix.enable && config.nix.package != null then
":${config.nix.package}/bin" ":${config.nix.package}/bin"
else else
":$(${pkgs.coreutils}/bin/dirname $(${pkgs.coreutils}/bin/readlink -m $(type -p nix-env)))" ":$(${pkgs.coreutils}/bin/dirname $(${pkgs.coreutils}/bin/readlink -m $(type -p nix-env)))")
)
+ optionalString (!cfg.emptyActivationPath) "\${PATH:+:}$PATH"; + optionalString (!cfg.emptyActivationPath) "\${PATH:+:}$PATH";
activationScript = pkgs.writeShellScript "activation-script" '' activationScript = pkgs.writeShellScript "activation-script" ''
set -eu set -eu
set -o pipefail set -o pipefail
cd $HOME cd $HOME
export PATH="${activationBinPaths}" export PATH="${activationBinPaths}"
${config.lib.bash.initHomeManagerLib} ${config.lib.bash.initHomeManagerLib}
${builtins.readFile ./lib-bash/activation-init.sh} ${builtins.readFile ./lib-bash/activation-init.sh}
if [[ ! -v SKIP_SANITY_CHECKS ]]; then if [[ ! -v SKIP_SANITY_CHECKS ]]; then
checkUsername ${escapeShellArg config.home.username} checkUsername ${escapeShellArg config.home.username}
checkHomeDirectory ${escapeShellArg config.home.homeDirectory} checkHomeDirectory ${escapeShellArg config.home.homeDirectory}
fi fi
${activationCmds} ${activationCmds}
''; '';
in in pkgs.runCommand "home-manager-generation" { preferLocalBuild = true; } ''
pkgs.runCommand mkdir -p $out
"home-manager-generation"
{
preferLocalBuild = true;
}
''
mkdir -p $out
echo "${config.home.version.full}" > $out/hm-version echo "${config.home.version.full}" > $out/hm-version
cp ${activationScript} $out/activate cp ${activationScript} $out/activate
mkdir $out/bin mkdir $out/bin
ln -s $out/activate $out/bin/home-manager-generation ln -s $out/activate $out/bin/home-manager-generation
substituteInPlace $out/activate \ substituteInPlace $out/activate \
--subst-var-by GENERATION_DIR $out --subst-var-by GENERATION_DIR $out
ln -s ${config.home-files} $out/home-files ln -s ${config.home-files} $out/home-files
ln -s ${cfg.path} $out/home-path ln -s ${cfg.path} $out/home-path
${cfg.extraBuilderCommands} ${cfg.extraBuilderCommands}
''; '';
home.path = pkgs.buildEnv { home.path = pkgs.buildEnv {
name = "home-manager-path"; name = "home-manager-path";

View file

@ -6,21 +6,21 @@ let
cfg = config.programs.zsh; cfg = config.programs.zsh;
relToDotDir = file: (optionalString (cfg.dotDir != null) (cfg.dotDir + "/")) + file; relToDotDir = file:
(optionalString (cfg.dotDir != null) (cfg.dotDir + "/")) + file;
pluginsDir = if cfg.dotDir != null then pluginsDir =
relToDotDir "plugins" else ".zsh/plugins"; if cfg.dotDir != null then relToDotDir "plugins" else ".zsh/plugins";
envVarsStr = config.lib.zsh.exportAll cfg.sessionVariables; envVarsStr = config.lib.zsh.exportAll cfg.sessionVariables;
localVarsStr = config.lib.zsh.defineAll cfg.localVariables; localVarsStr = config.lib.zsh.defineAll cfg.localVariables;
aliasesStr = concatStringsSep "\n" ( aliasesStr = concatStringsSep "\n" (mapAttrsToList
mapAttrsToList (k: v: "alias -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}") cfg.shellAliases (k: v: "alias -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}")
); cfg.shellAliases);
dirHashesStr = concatStringsSep "\n" ( dirHashesStr = concatStringsSep "\n"
mapAttrsToList (k: v: ''hash -d ${k}="${v}"'') cfg.dirHashes (mapAttrsToList (k: v: ''hash -d ${k}="${v}"'') cfg.dirHashes);
);
zdotdir = "$HOME/" + lib.escapeShellArg cfg.dotDir; zdotdir = "$HOME/" + lib.escapeShellArg cfg.dotDir;
@ -64,20 +64,22 @@ let
path = mkOption { path = mkOption {
type = types.str; type = types.str;
default = if versionAtLeast stateVersion "20.03" default = if versionAtLeast stateVersion "20.03" then
then "$HOME/.zsh_history" "$HOME/.zsh_history"
else relToDotDir ".zsh_history"; else
relToDotDir ".zsh_history";
defaultText = literalExpression '' defaultText = literalExpression ''
"$HOME/.zsh_history" if state version 20.03, "$HOME/.zsh_history" if state version 20.03,
"$ZDOTDIR/.zsh_history" otherwise "$ZDOTDIR/.zsh_history" otherwise
''; '';
example = literalExpression ''"''${config.xdg.dataHome}/zsh/zsh_history"''; example =
literalExpression ''"''${config.xdg.dataHome}/zsh/zsh_history"'';
description = "History file location"; description = "History file location";
}; };
ignorePatterns = mkOption { ignorePatterns = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
example = literalExpression ''[ "rm *" "pkill *" ]''; example = literalExpression ''[ "rm *" "pkill *" ]'';
description = '' description = ''
Do not enter command lines into the history list Do not enter command lines into the history list
@ -170,7 +172,7 @@ let
package = mkPackageOption pkgs "oh-my-zsh" { }; package = mkPackageOption pkgs "oh-my-zsh" { };
plugins = mkOption { plugins = mkOption {
default = []; default = [ ];
example = [ "git" "sudo" ]; example = [ "git" "sudo" ];
type = types.listOf types.str; type = types.listOf types.str;
description = '' description = ''
@ -215,7 +217,7 @@ let
options = { options = {
enable = mkEnableOption "history substring search"; enable = mkEnableOption "history substring search";
searchUpKey = mkOption { searchUpKey = mkOption {
type = with types; either (listOf str) str ; type = with types; either (listOf str) str;
default = [ "^[[A" ]; default = [ "^[[A" ];
description = '' description = ''
The key codes to be used when searching up. The key codes to be used when searching up.
@ -224,7 +226,7 @@ let
''; '';
}; };
searchDownKey = mkOption { searchDownKey = mkOption {
type = with types; either (listOf str) str ; type = with types; either (listOf str) str;
default = [ "^[[B" ]; default = [ "^[[B" ];
description = '' description = ''
The key codes to be used when searching down. The key codes to be used when searching down.
@ -253,7 +255,7 @@ let
patterns = mkOption { patterns = mkOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
default = {}; default = { };
example = { "rm -rf *" = "fg=white,bold,bg=red"; }; example = { "rm -rf *" = "fg=white,bold,bg=red"; };
description = '' description = ''
Custom syntax highlighting for user-defined patterns. Custom syntax highlighting for user-defined patterns.
@ -263,7 +265,7 @@ let
styles = mkOption { styles = mkOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
default = {}; default = { };
example = { comment = "fg=black,bold"; }; example = { comment = "fg=black,bold"; };
description = '' description = ''
Custom styles for syntax highlighting. Custom styles for syntax highlighting.
@ -273,13 +275,25 @@ let
}; };
}; };
in in {
{
imports = [ imports = [
(mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestion" "enable" ]) (mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [
(mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ]) "programs"
(mkRenamedOptionModule [ "programs" "zsh" "zproof" ] [ "programs" "zsh" "zprof" ]) "zsh"
"autosuggestion"
"enable"
])
(mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [
"programs"
"zsh"
"syntaxHighlighting"
"enable"
])
(mkRenamedOptionModule [ "programs" "zsh" "zproof" ] [
"programs"
"zsh"
"zprof"
])
]; ];
options = { options = {
@ -297,7 +311,7 @@ in
}; };
cdpath = mkOption { cdpath = mkOption {
default = []; default = [ ];
description = '' description = ''
List of paths to autocomplete calls to {command}`cd`. List of paths to autocomplete calls to {command}`cd`.
''; '';
@ -316,7 +330,7 @@ in
}; };
shellAliases = mkOption { shellAliases = mkOption {
default = {}; default = { };
example = literalExpression '' example = literalExpression ''
{ {
ll = "ls -l"; ll = "ls -l";
@ -331,7 +345,7 @@ in
}; };
shellGlobalAliases = mkOption { shellGlobalAliases = mkOption {
default = {}; default = { };
example = literalExpression '' example = literalExpression ''
{ {
UUID = "$(uuidgen | tr -d \\n)"; UUID = "$(uuidgen | tr -d \\n)";
@ -346,7 +360,7 @@ in
}; };
dirHashes = mkOption { dirHashes = mkOption {
default = {}; default = { };
example = literalExpression '' example = literalExpression ''
{ {
docs = "$HOME/Documents"; docs = "$HOME/Documents";
@ -374,7 +388,8 @@ in
completionInit = mkOption { completionInit = mkOption {
default = "autoload -U compinit && compinit"; default = "autoload -U compinit && compinit";
description = "Initialization commands to run when completion is enabled."; description =
"Initialization commands to run when completion is enabled.";
type = types.lines; type = types.lines;
}; };
@ -387,13 +402,13 @@ in
syntaxHighlighting = mkOption { syntaxHighlighting = mkOption {
type = syntaxHighlightingModule; type = syntaxHighlightingModule;
default = {}; default = { };
description = "Options related to zsh-syntax-highlighting."; description = "Options related to zsh-syntax-highlighting.";
}; };
historySubstringSearch = mkOption { historySubstringSearch = mkOption {
type = historySubstringSearchModule; type = historySubstringSearchModule;
default = {}; default = { };
description = "Options related to zsh-history-substring-search."; description = "Options related to zsh-history-substring-search.";
}; };
@ -415,7 +430,8 @@ in
}; };
strategy = mkOption { strategy = mkOption {
type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]); type = types.listOf
(types.enum [ "history" "completion" "match_prev_cmd" ]);
default = [ "history" ]; default = [ "history" ];
description = '' description = ''
`ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated. `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated.
@ -436,7 +452,7 @@ in
history = mkOption { history = mkOption {
type = historyModule; type = historyModule;
default = {}; default = { };
description = "Options related to commands history configuration."; description = "Options related to commands history configuration.";
}; };
@ -448,7 +464,7 @@ in
}; };
sessionVariables = mkOption { sessionVariables = mkOption {
default = {}; default = { };
type = types.attrs; type = types.attrs;
example = { MAILCHECK = 30; }; example = { MAILCHECK = 30; };
description = "Environment variables that will be set for zsh session."; description = "Environment variables that will be set for zsh session.";
@ -457,7 +473,8 @@ in
initExtraBeforeCompInit = mkOption { initExtraBeforeCompInit = mkOption {
default = ""; default = "";
type = types.lines; type = types.lines;
description = "Extra commands that should be added to {file}`.zshrc` before compinit."; description =
"Extra commands that should be added to {file}`.zshrc` before compinit.";
}; };
initExtra = mkOption { initExtra = mkOption {
@ -481,7 +498,8 @@ in
profileExtra = mkOption { profileExtra = mkOption {
default = ""; default = "";
type = types.lines; type = types.lines;
description = "Extra commands that should be added to {file}`.zprofile`."; description =
"Extra commands that should be added to {file}`.zprofile`.";
}; };
loginExtra = mkOption { loginExtra = mkOption {
@ -493,12 +511,13 @@ in
logoutExtra = mkOption { logoutExtra = mkOption {
default = ""; default = "";
type = types.lines; type = types.lines;
description = "Extra commands that should be added to {file}`.zlogout`."; description =
"Extra commands that should be added to {file}`.zlogout`.";
}; };
plugins = mkOption { plugins = mkOption {
type = types.listOf pluginModule; type = types.listOf pluginModule;
default = []; default = [ ];
example = literalExpression '' example = literalExpression ''
[ [
{ {
@ -528,14 +547,14 @@ in
oh-my-zsh = mkOption { oh-my-zsh = mkOption {
type = ohMyZshModule; type = ohMyZshModule;
default = {}; default = { };
description = "Options to configure oh-my-zsh."; description = "Options to configure oh-my-zsh.";
}; };
localVariables = mkOption { localVariables = mkOption {
type = types.attrs; type = types.attrs;
default = {}; default = { };
example = { POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=["dir" "vcs"]; }; example = { POWERLEVEL9K_LEFT_PROMPT_ELEMENTS = [ "dir" "vcs" ]; };
description = '' description = ''
Extra local variables defined at the top of {file}`.zshrc`. Extra local variables defined at the top of {file}`.zshrc`.
''; '';
@ -609,16 +628,16 @@ in
cfg.initExtraFirst cfg.initExtraFirst
"typeset -U path cdpath fpath manpath" "typeset -U path cdpath fpath manpath"
(optionalString (cfg.cdpath != []) '' (optionalString (cfg.cdpath != [ ]) ''
cdpath+=(${concatStringsSep " " cfg.cdpath}) cdpath+=(${concatStringsSep " " cfg.cdpath})
'') '')
'' ''
for profile in ''${(z)NIX_PROFILES}; do for profile in ''${(z)NIX_PROFILES}; do
fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions) fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
done done
HELPDIR="${cfg.package}/share/zsh/$ZSH_VERSION/help" HELPDIR="${cfg.package}/share/zsh/$ZSH_VERSION/help"
'' ''
(optionalString (cfg.defaultKeymap != null) '' (optionalString (cfg.defaultKeymap != null) ''
@ -635,120 +654,136 @@ in
'') cfg.plugins)) '') cfg.plugins))
'' ''
# Oh-My-Zsh/Prezto calls compinit during initialization, # Oh-My-Zsh/Prezto calls compinit during initialization,
# calling it twice causes slight start up slowdown # calling it twice causes slight start up slowdown
# as all $fpath entries will be traversed again. # as all $fpath entries will be traversed again.
${optionalString (cfg.enableCompletion && !cfg.oh-my-zsh.enable && !cfg.prezto.enable) ${optionalString
cfg.completionInit (cfg.enableCompletion && !cfg.oh-my-zsh.enable && !cfg.prezto.enable)
}'' cfg.completionInit}''
(optionalString cfg.autosuggestion.enable '' (optionalString cfg.autosuggestion.enable ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
${optionalString (cfg.autosuggestion.strategy != []) '' ${optionalString (cfg.autosuggestion.strategy != [ ]) ''
ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.autosuggestion.strategy}) ZSH_AUTOSUGGEST_STRATEGY=(${
'' concatStringsSep " " cfg.autosuggestion.strategy
} })
'') ''}
(optionalString (cfg.autosuggestion.enable && cfg.autosuggestion.highlight != null) ''
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.autosuggestion.highlight}"
'') '')
(optionalString
(cfg.autosuggestion.enable && cfg.autosuggestion.highlight != null) ''
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.autosuggestion.highlight}"
'')
(optionalString cfg.oh-my-zsh.enable '' (optionalString cfg.oh-my-zsh.enable ''
# oh-my-zsh extra settings for plugins # oh-my-zsh extra settings for plugins
${cfg.oh-my-zsh.extraConfig} ${cfg.oh-my-zsh.extraConfig}
# oh-my-zsh configuration generated by NixOS # oh-my-zsh configuration generated by NixOS
${optionalString (cfg.oh-my-zsh.plugins != []) ${optionalString (cfg.oh-my-zsh.plugins != [ ])
"plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})" "plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})"}
} ${optionalString (cfg.oh-my-zsh.custom != "")
${optionalString (cfg.oh-my-zsh.custom != "") ''ZSH_CUSTOM="${cfg.oh-my-zsh.custom}"''}
"ZSH_CUSTOM=\"${cfg.oh-my-zsh.custom}\"" ${optionalString (cfg.oh-my-zsh.theme != "")
} ''ZSH_THEME="${cfg.oh-my-zsh.theme}"''}
${optionalString (cfg.oh-my-zsh.theme != "") source $ZSH/oh-my-zsh.sh
"ZSH_THEME=\"${cfg.oh-my-zsh.theme}\""
}
source $ZSH/oh-my-zsh.sh
'') '')
'' ''
${optionalString cfg.prezto.enable ${optionalString cfg.prezto.enable (builtins.readFile
(builtins.readFile "${pkgs.zsh-prezto}/share/zsh-prezto/runcoms/zshrc")} "${pkgs.zsh-prezto}/share/zsh-prezto/runcoms/zshrc")}
${concatStrings (map (plugin: '' ${concatStrings (map (plugin: ''
if [[ -f "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then if [[ -f "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then
source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}"
fi fi
'') cfg.plugins)} '') cfg.plugins)}
# History options should be set in .zshrc and after oh-my-zsh sourcing. # History options should be set in .zshrc and after oh-my-zsh sourcing.
# See https://github.com/nix-community/home-manager/issues/177. # See https://github.com/nix-community/home-manager/issues/177.
HISTSIZE="${toString cfg.history.size}" HISTSIZE="${toString cfg.history.size}"
SAVEHIST="${toString cfg.history.save}" SAVEHIST="${toString cfg.history.save}"
${optionalString (cfg.history.ignorePatterns != []) "HISTORY_IGNORE=${lib.escapeShellArg "(${lib.concatStringsSep "|" cfg.history.ignorePatterns})"}"} ${optionalString (cfg.history.ignorePatterns != [ ])
${if versionAtLeast config.home.stateVersion "20.03" "HISTORY_IGNORE=${
then ''HISTFILE="${cfg.history.path}"'' lib.escapeShellArg
else ''HISTFILE="$HOME/${cfg.history.path}"''} "(${lib.concatStringsSep "|" cfg.history.ignorePatterns})"
mkdir -p "$(dirname "$HISTFILE")" }"}
${if versionAtLeast config.home.stateVersion "20.03" then
''HISTFILE="${cfg.history.path}"''
else
''HISTFILE="$HOME/${cfg.history.path}"''}
mkdir -p "$(dirname "$HISTFILE")"
setopt HIST_FCNTL_LOCK setopt HIST_FCNTL_LOCK
${if cfg.history.append then "setopt" else "unsetopt"} APPEND_HISTORY ${if cfg.history.append then "setopt" else "unsetopt"} APPEND_HISTORY
${if cfg.history.ignoreDups then "setopt" else "unsetopt"} HIST_IGNORE_DUPS ${
${if cfg.history.ignoreAllDups then "setopt" else "unsetopt"} HIST_IGNORE_ALL_DUPS if cfg.history.ignoreDups then "setopt" else "unsetopt"
${if cfg.history.ignoreSpace then "setopt" else "unsetopt"} HIST_IGNORE_SPACE } HIST_IGNORE_DUPS
${if cfg.history.expireDuplicatesFirst then "setopt" else "unsetopt"} HIST_EXPIRE_DUPS_FIRST ${
${if cfg.history.share then "setopt" else "unsetopt"} SHARE_HISTORY if cfg.history.ignoreAllDups then "setopt" else "unsetopt"
${if cfg.history.extended then "setopt" else "unsetopt"} EXTENDED_HISTORY } HIST_IGNORE_ALL_DUPS
${if cfg.autocd != null then "${if cfg.autocd then "setopt" else "unsetopt"} autocd" else ""} ${
if cfg.history.ignoreSpace then "setopt" else "unsetopt"
} HIST_IGNORE_SPACE
${
if cfg.history.expireDuplicatesFirst then "setopt" else "unsetopt"
} HIST_EXPIRE_DUPS_FIRST
${if cfg.history.share then "setopt" else "unsetopt"} SHARE_HISTORY
${
if cfg.history.extended then "setopt" else "unsetopt"
} EXTENDED_HISTORY
${if cfg.autocd != null then
"${if cfg.autocd then "setopt" else "unsetopt"} autocd"
else
""}
${cfg.initExtra} ${cfg.initExtra}
# Aliases # Aliases
${aliasesStr} ${aliasesStr}
'' ''
] ] ++ (mapAttrsToList
++ (mapAttrsToList (k: v: "alias -g -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}") cfg.shellGlobalAliases) (k: v: "alias -g -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}")
++ [ ('' cfg.shellGlobalAliases) ++ [
# Named Directory Hashes (''
${dirHashesStr} # Named Directory Hashes
'') ${dirHashesStr}
'')
(optionalString cfg.syntaxHighlighting.enable (optionalString cfg.syntaxHighlighting.enable
# Load zsh-syntax-highlighting after all custom widgets have been created # Load zsh-syntax-highlighting after all custom widgets have been created
# https://github.com/zsh-users/zsh-syntax-highlighting#faq # https://github.com/zsh-users/zsh-syntax-highlighting#faq
'' ''
source ${cfg.syntaxHighlighting.package}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh source ${cfg.syntaxHighlighting.package}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
ZSH_HIGHLIGHT_HIGHLIGHTERS+=(${lib.concatStringsSep " " (map lib.escapeShellArg cfg.syntaxHighlighting.highlighters)}) ZSH_HIGHLIGHT_HIGHLIGHTERS+=(${
${lib.concatStringsSep "\n" ( lib.concatStringsSep " "
lib.mapAttrsToList (map lib.escapeShellArg cfg.syntaxHighlighting.highlighters)
(name: value: "ZSH_HIGHLIGHT_STYLES+=(${lib.escapeShellArg name} ${lib.escapeShellArg value})") })
cfg.syntaxHighlighting.styles ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
)} "ZSH_HIGHLIGHT_STYLES+=(${lib.escapeShellArg name} ${
${lib.concatStringsSep "\n" ( lib.escapeShellArg value
lib.mapAttrsToList })") cfg.syntaxHighlighting.styles)}
(name: value: "ZSH_HIGHLIGHT_PATTERNS+=(${lib.escapeShellArg name} ${lib.escapeShellArg value})") ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
cfg.syntaxHighlighting.patterns "ZSH_HIGHLIGHT_PATTERNS+=(${lib.escapeShellArg name} ${
)} lib.escapeShellArg value
'') })") cfg.syntaxHighlighting.patterns)}
'')
(optionalString (cfg.historySubstringSearch.enable or false) (optionalString (cfg.historySubstringSearch.enable or false)
# Load zsh-history-substring-search after zsh-syntax-highlighting # Load zsh-history-substring-search after zsh-syntax-highlighting
# https://github.com/zsh-users/zsh-history-substring-search#usage # https://github.com/zsh-users/zsh-history-substring-search#usage
'' ''
source ${pkgs.zsh-history-substring-search}/share/zsh-history-substring-search/zsh-history-substring-search.zsh source ${pkgs.zsh-history-substring-search}/share/zsh-history-substring-search/zsh-history-substring-search.zsh
${lib.concatMapStringsSep "\n" ${lib.concatMapStringsSep "\n"
(upKey: "bindkey \"${upKey}\" history-substring-search-up") (upKey: ''bindkey "${upKey}" history-substring-search-up'')
(lib.toList cfg.historySubstringSearch.searchUpKey) (lib.toList cfg.historySubstringSearch.searchUpKey)}
} ${lib.concatMapStringsSep "\n"
${lib.concatMapStringsSep "\n" (downKey: ''bindkey "${downKey}" history-substring-search-down'')
(downKey: "bindkey \"${downKey}\" history-substring-search-down") (lib.toList cfg.historySubstringSearch.searchDownKey)}
(lib.toList cfg.historySubstringSearch.searchDownKey) '')
}
'')
(optionalString cfg.zprof.enable (optionalString cfg.zprof.enable ''
'' zprof
zprof '')
'') ]);
]);
} }
(mkIf cfg.oh-my-zsh.enable { (mkIf cfg.oh-my-zsh.enable {
@ -757,15 +792,14 @@ in
home.file."${config.xdg.cacheHome}/oh-my-zsh/.keep".text = ""; home.file."${config.xdg.cacheHome}/oh-my-zsh/.keep".text = "";
}) })
(mkIf (cfg.plugins != []) { (mkIf (cfg.plugins != [ ]) {
# Many plugins require compinit to be called # Many plugins require compinit to be called
# but allow the user to opt out. # but allow the user to opt out.
programs.zsh.enableCompletion = mkDefault true; programs.zsh.enableCompletion = mkDefault true;
home.file = home.file = foldl' (a: b: a // b) { }
foldl' (a: b: a // b) {}
(map (plugin: { "${pluginsDir}/${plugin.name}".source = plugin.src; }) (map (plugin: { "${pluginsDir}/${plugin.name}".source = plugin.src; })
cfg.plugins); cfg.plugins);
}) })
]); ]);
} }