zsh: improve dotDir handling
Previously, `config.programs.zsh.dotDir` prepended strings with `$HOME`. This caused issues like nix-community#5100, where `$HOME` is inconsistently resolved in time for the evaluation of the option. The handling of this variable is also inconsistent with how paths are handled elsewhere, including within the same module, where `config.programs.zsh.history.path` does not mutate the supplied string. To preserve backwards compatibility, this change prepends `config.home.homeDirectory` to relative paths, while assigning absolute paths unchanged. Tests for both cases are added. Signed-off-by: Austin Horstman <khaneliman12@gmail.com>
This commit is contained in:
parent
ef8a9767fc
commit
21399deff2
11 changed files with 173 additions and 82 deletions
|
|
@ -18,13 +18,13 @@ let
|
|||
|
||||
cfg = config.programs.zsh;
|
||||
|
||||
relToDotDir = file: (optionalString (cfg.dotDir != null) (cfg.dotDir + "/")) + file;
|
||||
|
||||
bindkeyCommands = {
|
||||
emacs = "bindkey -e";
|
||||
viins = "bindkey -v";
|
||||
vicmd = "bindkey -a";
|
||||
};
|
||||
|
||||
inherit (import ./lib.nix { inherit config lib; }) homeDir dotDirAbs dotDirRel;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
|
@ -100,8 +100,9 @@ in
|
|||
};
|
||||
|
||||
dotDir = mkOption {
|
||||
default = null;
|
||||
example = ".config/zsh";
|
||||
default = homeDir;
|
||||
defaultText = "`config.home.homeDirectory`";
|
||||
example = "`\${config.xdg.configHome}/zsh`";
|
||||
description = ''
|
||||
Directory where the zsh configuration and more should be located,
|
||||
relative to the users home directory. The default is the home
|
||||
|
|
@ -144,9 +145,9 @@ in
|
|||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
docs = "$HOME/Documents";
|
||||
vids = "$HOME/Videos";
|
||||
dl = "$HOME/Downloads";
|
||||
docs = "$\{config.home.homeDirectory}/Documents";
|
||||
vids = "$\{config.home.homeDirectory}/Videos";
|
||||
dl = "$\{config.home.homeDirectory}/Downloads";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
|
|
@ -339,30 +340,28 @@ in
|
|||
dirHashesStr = concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: ''hash -d ${k}="${v}"'') cfg.dirHashes
|
||||
);
|
||||
|
||||
zdotdir = "$HOME/" + lib.escapeShellArg cfg.dotDir;
|
||||
in
|
||||
mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
(mkIf (cfg.envExtra != "") {
|
||||
home.file."${relToDotDir ".zshenv"}".text = cfg.envExtra;
|
||||
home.file."${dotDirRel}/.zshenv".text = cfg.envExtra;
|
||||
})
|
||||
|
||||
(mkIf (cfg.profileExtra != "") {
|
||||
home.file."${relToDotDir ".zprofile"}".text = cfg.profileExtra;
|
||||
home.file."${dotDirRel}/.zprofile".text = cfg.profileExtra;
|
||||
})
|
||||
|
||||
(mkIf (cfg.loginExtra != "") {
|
||||
home.file."${relToDotDir ".zlogin"}".text = cfg.loginExtra;
|
||||
home.file."${dotDirRel}/.zlogin".text = cfg.loginExtra;
|
||||
})
|
||||
|
||||
(mkIf (cfg.logoutExtra != "") {
|
||||
home.file."${relToDotDir ".zlogout"}".text = cfg.logoutExtra;
|
||||
home.file."${dotDirRel}/.zlogout".text = cfg.logoutExtra;
|
||||
})
|
||||
|
||||
(mkIf (cfg.dotDir != null) {
|
||||
home.file."${relToDotDir ".zshenv"}".text = ''
|
||||
export ZDOTDIR=${zdotdir}
|
||||
(mkIf (dotDirAbs != homeDir) {
|
||||
home.file."${dotDirRel}/.zshenv".text = ''
|
||||
export ZDOTDIR=${dotDirAbs}
|
||||
'';
|
||||
|
||||
# When dotDir is set, only use ~/.zshenv to source ZDOTDIR/.zshenv,
|
||||
|
|
@ -370,12 +369,12 @@ in
|
|||
# already set correctly (by e.g. spawning a zsh inside a zsh), all env
|
||||
# vars still get exported
|
||||
home.file.".zshenv".text = ''
|
||||
source ${zdotdir}/.zshenv
|
||||
source ${dotDirAbs}/.zshenv
|
||||
'';
|
||||
})
|
||||
|
||||
{
|
||||
home.file."${relToDotDir ".zshenv"}".text = ''
|
||||
home.file."${dotDirRel}/.zshenv".text = ''
|
||||
# Environment variables
|
||||
. "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh"
|
||||
|
||||
|
|
@ -486,7 +485,7 @@ in
|
|||
))
|
||||
];
|
||||
|
||||
home.file."${relToDotDir ".zshrc"}".text = cfg.initContent;
|
||||
home.file."${dotDirRel}/.zshrc".text = cfg.initContent;
|
||||
}
|
||||
]
|
||||
);
|
||||
|
|
|
|||
42
modules/programs/zsh/lib.nix
Normal file
42
modules/programs/zsh/lib.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.programs.zsh;
|
||||
in
|
||||
rec {
|
||||
homeDir = config.home.homeDirectory;
|
||||
|
||||
# escapes for shell and cleans trailing slashes that can mess with test regex
|
||||
cleanPathStr = pathStr: lib.escapeShellArg (lib.removeSuffix "/" pathStr);
|
||||
|
||||
# strips home directory prefix from absolute path.
|
||||
mkRelPathStr =
|
||||
pathStr:
|
||||
# is already a relative path
|
||||
if (!lib.hasPrefix "/" pathStr) then
|
||||
cleanPathStr pathStr
|
||||
# is an absolute path within home dir
|
||||
else if (lib.hasPrefix homeDir pathStr) then
|
||||
cleanPathStr (lib.removePrefix "${homeDir}/" pathStr)
|
||||
# is an absolute path not in home dir
|
||||
else
|
||||
throw ''
|
||||
Attempted to convert an absolute path not within home directory to a
|
||||
home-relative path.
|
||||
Conversion attempted on:
|
||||
${pathStr}
|
||||
...which does not start with:
|
||||
${homeDir}
|
||||
'';
|
||||
|
||||
# given a relative (or unknown) path, returns absolute by prepending home dir
|
||||
# if path doesn't begin with "/"
|
||||
mkAbsPathStr =
|
||||
pathStr: cleanPathStr ((lib.optionalString (!lib.hasPrefix "/" pathStr) "${homeDir}/") + pathStr);
|
||||
|
||||
dotDirAbs = mkAbsPathStr cfg.dotDir;
|
||||
dotDirRel = mkRelPathStr cfg.dotDir;
|
||||
|
||||
# If dotDir is default (i.e., the user's home dir) plugins are stored in
|
||||
# ~/.zsh/plugins -- otherwise, in `programs.zsh.dotDir`/plugins
|
||||
pluginsDir = dotDirAbs + (lib.optionalString (homeDir == dotDirAbs) "/.zsh") + "/plugins";
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ let
|
|||
|
||||
cfg = config.programs.zsh;
|
||||
|
||||
relToDotDir = file: (lib.optionalString (cfg.dotDir != null) (cfg.dotDir + "/")) + file;
|
||||
inherit (import ../lib.nix { inherit config lib; }) pluginsDir;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
|
@ -88,49 +88,45 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
pluginsDir = if cfg.dotDir != null then relToDotDir "plugins" else ".zsh/plugins";
|
||||
in
|
||||
lib.mkIf (cfg.plugins != [ ]) {
|
||||
home.file = lib.foldl' (a: b: a // b) { } (
|
||||
map (plugin: { "${pluginsDir}/${plugin.name}".source = plugin.src; }) cfg.plugins
|
||||
);
|
||||
config = lib.mkIf (cfg.plugins != [ ]) {
|
||||
home.file = lib.foldl' (a: b: a // b) { } (
|
||||
map (plugin: { "${pluginsDir}/${plugin.name}".source = plugin.src; }) cfg.plugins
|
||||
);
|
||||
|
||||
programs.zsh = {
|
||||
# Many plugins require compinit to be called
|
||||
# but allow the user to opt out.
|
||||
enableCompletion = lib.mkDefault true;
|
||||
programs.zsh = {
|
||||
# Many plugins require compinit to be called
|
||||
# but allow the user to opt out.
|
||||
enableCompletion = lib.mkDefault true;
|
||||
|
||||
initContent = lib.mkMerge [
|
||||
(lib.mkOrder 560 (
|
||||
lib.concatStrings (
|
||||
map (plugin: ''
|
||||
path+="$HOME/${pluginsDir}/${plugin.name}"
|
||||
fpath+="$HOME/${pluginsDir}/${plugin.name}"
|
||||
${
|
||||
(lib.optionalString (plugin.completions != [ ]) ''
|
||||
fpath+=(${
|
||||
lib.concatMapStringsSep " " (
|
||||
completion: "\"$HOME/${pluginsDir}/${plugin.name}/${completion}\""
|
||||
) plugin.completions
|
||||
})
|
||||
'')
|
||||
}
|
||||
'') cfg.plugins
|
||||
)
|
||||
))
|
||||
initContent = lib.mkMerge [
|
||||
(lib.mkOrder 560 (
|
||||
lib.concatStrings (
|
||||
map (plugin: ''
|
||||
path+="${pluginsDir}/${plugin.name}"
|
||||
fpath+="${pluginsDir}/${plugin.name}"
|
||||
${
|
||||
(lib.optionalString (plugin.completions != [ ]) ''
|
||||
fpath+=(${
|
||||
lib.concatMapStringsSep " " (
|
||||
completion: "\"${pluginsDir}/${plugin.name}/${completion}\""
|
||||
) plugin.completions
|
||||
})
|
||||
'')
|
||||
}
|
||||
'') cfg.plugins
|
||||
)
|
||||
))
|
||||
|
||||
(lib.mkOrder 900 (
|
||||
lib.concatStrings (
|
||||
map (plugin: ''
|
||||
if [[ -f "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then
|
||||
source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}"
|
||||
fi
|
||||
'') cfg.plugins
|
||||
)
|
||||
))
|
||||
];
|
||||
};
|
||||
(lib.mkOrder 900 (
|
||||
lib.concatStrings (
|
||||
map (plugin: ''
|
||||
if [[ -f "${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then
|
||||
source "${pluginsDir}/${plugin.name}/${plugin.file}"
|
||||
fi
|
||||
'') cfg.plugins
|
||||
)
|
||||
))
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@
|
|||
let
|
||||
inherit (lib) mkOption optionalString types;
|
||||
|
||||
relToDotDir =
|
||||
file:
|
||||
(lib.optionalString (config.programs.zsh.dotDir != null) (config.programs.zsh.dotDir + "/")) + file;
|
||||
inherit (import ../lib.nix { inherit config lib; }) dotDirRel;
|
||||
|
||||
cfg = config.programs.zsh;
|
||||
|
||||
|
|
@ -34,7 +32,7 @@ let
|
|||
custom = mkOption {
|
||||
default = "";
|
||||
type = types.str;
|
||||
example = "$HOME/my_customizations";
|
||||
example = "\${config.home.homeDirectory}/my_customizations";
|
||||
description = ''
|
||||
Path to a custom oh-my-zsh package to override config of
|
||||
oh-my-zsh. See <https://github.com/robbyrussell/oh-my-zsh/wiki/Customization>
|
||||
|
|
@ -76,7 +74,7 @@ in
|
|||
packages = [ cfg.oh-my-zsh.package ];
|
||||
|
||||
file = {
|
||||
"${relToDotDir ".zshenv"}".text = ''
|
||||
"${dotDirRel}/.zshenv".text = ''
|
||||
ZSH="${cfg.oh-my-zsh.package}/share/oh-my-zsh";
|
||||
ZSH_CACHE_DIR="${config.xdg.cacheHome}/oh-my-zsh";
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -14,9 +14,7 @@ let
|
|||
|
||||
cfg = config.programs.zsh.prezto;
|
||||
|
||||
relToDotDir =
|
||||
file:
|
||||
(optionalString (config.programs.zsh.dotDir != null) (config.programs.zsh.dotDir + "/")) + file;
|
||||
inherit (import ../lib.nix { inherit config lib; }) dotDirRel;
|
||||
|
||||
preztoModule = types.submodule {
|
||||
options = {
|
||||
|
|
@ -425,25 +423,25 @@ in
|
|||
{
|
||||
home.packages = [ cfg.package ];
|
||||
|
||||
home.file."${relToDotDir ".zprofile"}".text = ''
|
||||
home.file."${dotDirRel}/.zprofile".text = ''
|
||||
# Generated by Nix
|
||||
source ${cfg.package}/share/zsh-prezto/runcoms/zprofile
|
||||
'';
|
||||
home.file."${relToDotDir ".zlogin"}".text = ''
|
||||
home.file."${dotDirRel}/.zlogin".text = ''
|
||||
# Generated by Nix
|
||||
source ${cfg.package}/share/zsh-prezto/runcoms/zlogin
|
||||
'';
|
||||
home.file."${relToDotDir ".zlogout"}".text = ''
|
||||
home.file."${dotDirRel}/.zlogout".text = ''
|
||||
# Generated by Nix
|
||||
source ${cfg.package}/share/zsh-prezto/runcoms/zlogout
|
||||
'';
|
||||
# Using mkAfter to make sure we load Home-Manager's environment
|
||||
# variables first (see modules/prgrams/zsh.nix)
|
||||
home.file."${relToDotDir ".zshenv"}".text = lib.mkAfter ''
|
||||
home.file."${dotDirRel}/.zshenv".text = lib.mkAfter ''
|
||||
# Generated by Nix
|
||||
source ${cfg.package}/share/zsh-prezto/runcoms/zshenv
|
||||
'';
|
||||
home.file."${relToDotDir ".zpreztorc"}".text = ''
|
||||
home.file."${dotDirRel}/.zpreztorc".text = ''
|
||||
# Generated by Nix
|
||||
${optionalString (cfg.caseSensitive != null) ''
|
||||
zstyle ':prezto:*:*' case-sensitive '${lib.hm.booleans.yesNo cfg.caseSensitive}'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue