Skip to content

Commit

Permalink
zsh: add initContents option for custom .zshrc content insertion
Browse files Browse the repository at this point in the history
- Users can add content anywhere by using `lib.mkOrder`, `lib.mkBefore`
and `lib.mkAfter` custom configurations.
- Add test cases to verify the insertion of content before and after
existing configurations in `.zshrc`.

Signed-off-by: Qiming Chu <[email protected]>
  • Loading branch information
Emin017 committed Feb 20, 2025
1 parent 315ce87 commit 472e79e
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 134 deletions.
267 changes: 133 additions & 134 deletions modules/programs/zsh.nix
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,12 @@ in
description = "Environment variables that will be set for zsh session.";
};

initContents = mkOption {
default = "";
type = types.lines;
description = "Content to be added to {file}`.zshrc`.";
};

initExtraBeforeCompInit = mkOption {
default = "";
type = types.lines;
Expand All @@ -489,15 +495,6 @@ in
description = "Commands that should be added to top of {file}`.zshrc`.";
};

initExtraLast = mkOption {
default = "";
type = types.lines;
description = ''
Shell script code called during interactive zsh shell
initialisation, this will be the last thing executed in zsh startup.
'';
};

envExtra = mkOption {
default = "";
type = types.lines;
Expand Down Expand Up @@ -625,160 +622,162 @@ in
++ optional cfg.enableCompletion pkgs.nix-zsh-completions
++ optional cfg.oh-my-zsh.enable cfg.oh-my-zsh.package;

home.file."${relToDotDir ".zshrc"}".text = concatStringsSep "\n" ([
programs.zsh.initContents = mkMerge [
# zprof must be loaded before everything else, since it
# benchmarks the shell initialization.
(optionalString cfg.zprof.enable ''
(mkOrder 500 (optionalString cfg.zprof.enable ''
zmodload zsh/zprof
'')
''))

cfg.initExtraFirst
"typeset -U path cdpath fpath manpath"
(mkOrder 550 cfg.initExtraFirst)
(mkOrder 600 "typeset -U path cdpath fpath manpath")

(optionalString (cfg.cdpath != []) ''
(mkOrder 650 (optionalString (cfg.cdpath != [ ]) ''
cdpath+=(${concatStringsSep " " cfg.cdpath})
'')
''))
(mkOrder 700 ''
for profile in ''${(z)NIX_PROFILES}; do
fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
done
''
for profile in ''${(z)NIX_PROFILES}; do
fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
done
HELPDIR="${cfg.package}/share/zsh/$ZSH_VERSION/help"
''
HELPDIR="${cfg.package}/share/zsh/$ZSH_VERSION/help"
'')

(optionalString (cfg.defaultKeymap != null) ''
(mkOrder 750 (optionalString (cfg.defaultKeymap != null) ''
# Use ${cfg.defaultKeymap} keymap as the default.
${getAttr cfg.defaultKeymap bindkeyCommands}
'')
localVarsStr
''))

cfg.initExtraBeforeCompInit
(mkOrder 800 localVarsStr)
(mkOrder 850 cfg.initExtraBeforeCompInit)

(concatStrings (map (plugin: ''
(mkOrder 900 (concatStrings (map (plugin: ''
path+="$HOME/${pluginsDir}/${plugin.name}"
fpath+="$HOME/${pluginsDir}/${plugin.name}"
'') cfg.plugins))
'') cfg.plugins)))

''
# Oh-My-Zsh/Prezto calls compinit during initialization,
# calling it twice causes slight start up slowdown
# as all $fpath entries will be traversed again.
${optionalString (cfg.enableCompletion && !cfg.oh-my-zsh.enable && !cfg.prezto.enable)
cfg.completionInit
}''
(mkOrder 950 ''
# Oh-My-Zsh/Prezto calls compinit during initialization,
# calling it twice causes slight start up slowdown
# as all $fpath entries will be traversed again.
${optionalString
(cfg.enableCompletion && !cfg.oh-my-zsh.enable && !cfg.prezto.enable)
cfg.completionInit}'')

(optionalString cfg.autosuggestion.enable ''
(mkOrder 1000 (optionalString cfg.autosuggestion.enable ''
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
${optionalString (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.oh-my-zsh.enable ''
# oh-my-zsh extra settings for plugins
${cfg.oh-my-zsh.extraConfig}
# oh-my-zsh configuration generated by NixOS
${optionalString (cfg.oh-my-zsh.plugins != [])
"plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})"
}
${optionalString (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}\""
}
source $ZSH/oh-my-zsh.sh
${optionalString (cfg.autosuggestion.strategy != [ ]) ''
ZSH_AUTOSUGGEST_STRATEGY=(${
concatStringsSep " " cfg.autosuggestion.strategy
})
''}
''))
(mkOrder 1050 (optionalString
(cfg.autosuggestion.enable && cfg.autosuggestion.highlight != null) ''
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.autosuggestion.highlight}"
''))

(mkOrder 1100 (optionalString cfg.oh-my-zsh.enable ''
# oh-my-zsh extra settings for plugins
${cfg.oh-my-zsh.extraConfig}
# oh-my-zsh configuration generated by NixOS
${optionalString (cfg.oh-my-zsh.plugins != [ ])
"plugins=(${concatStringsSep " " cfg.oh-my-zsh.plugins})"}
${optionalString (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}"''}
source $ZSH/oh-my-zsh.sh
''))
(mkOrder 1150 ''
${optionalString cfg.prezto.enable (builtins.readFile
"${cfg.prezto.package}/share/zsh-prezto/runcoms/zshrc")}
${concatStrings (map (plugin: ''
if [[ -f "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then
source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}"
fi
'') cfg.plugins)}
# History options should be set in .zshrc and after oh-my-zsh sourcing.
# See https://github.com/nix-community/home-manager/issues/177.
HISTSIZE="${toString cfg.history.size}"
SAVEHIST="${toString cfg.history.save}"
${optionalString (cfg.history.ignorePatterns != [ ])
"HISTORY_IGNORE=${
lib.escapeShellArg
"(${lib.concatStringsSep "|" cfg.history.ignorePatterns})"
}"}
${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
${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.saveNoDups then "setopt" else "unsetopt"} HIST_SAVE_NO_DUPS
${if cfg.history.findNoDups then "setopt" else "unsetopt"} HIST_FIND_NO_DUPS
${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 ""}
'')

''
${optionalString cfg.prezto.enable
(builtins.readFile "${cfg.prezto.package}/share/zsh-prezto/runcoms/zshrc")}
${concatStrings (map (plugin: ''
if [[ -f "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}" ]]; then
source "$HOME/${pluginsDir}/${plugin.name}/${plugin.file}"
fi
'') cfg.plugins)}
# History options should be set in .zshrc and after oh-my-zsh sourcing.
# See https://github.com/nix-community/home-manager/issues/177.
HISTSIZE="${toString cfg.history.size}"
SAVEHIST="${toString cfg.history.save}"
${optionalString (cfg.history.ignorePatterns != []) "HISTORY_IGNORE=${lib.escapeShellArg "(${lib.concatStringsSep "|" cfg.history.ignorePatterns})"}"}
${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
${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.saveNoDups then "setopt" else "unsetopt"} HIST_SAVE_NO_DUPS
${if cfg.history.findNoDups then "setopt" else "unsetopt"} HIST_FIND_NO_DUPS
${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}
(mkOrder 1200 cfg.initExtra)

# Aliases
${aliasesStr}
''
]
++ (mapAttrsToList (k: v: "alias -g -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}") cfg.shellGlobalAliases)
++ [ (''
# Named Directory Hashes
${dirHashesStr}
(mkOrder 1250 aliasesStr)
(mkOrder 1250 (concatStringsSep "\n" (mapAttrsToList
(k: v: "alias -g -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}")
cfg.shellGlobalAliases)))
(mkOrder 1300 ''
# Named Directory Hashes
${dirHashesStr}
'')

(optionalString cfg.syntaxHighlighting.enable
(mkOrder 1350 (optionalString cfg.syntaxHighlighting.enable
# Load zsh-syntax-highlighting after all custom widgets have been created
# https://github.com/zsh-users/zsh-syntax-highlighting#faq
''
source ${cfg.syntaxHighlighting.package}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
ZSH_HIGHLIGHT_HIGHLIGHTERS+=(${lib.concatStringsSep " " (map lib.escapeShellArg cfg.syntaxHighlighting.highlighters)})
${lib.concatStringsSep "\n" (
lib.mapAttrsToList
(name: value: "ZSH_HIGHLIGHT_STYLES+=(${lib.escapeShellArg name} ${lib.escapeShellArg value})")
cfg.syntaxHighlighting.styles
)}
${lib.concatStringsSep "\n" (
lib.mapAttrsToList
(name: value: "ZSH_HIGHLIGHT_PATTERNS+=(${lib.escapeShellArg name} ${lib.escapeShellArg value})")
cfg.syntaxHighlighting.patterns
)}
'')

(optionalString (cfg.historySubstringSearch.enable or false)
''
source ${cfg.syntaxHighlighting.package}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
ZSH_HIGHLIGHT_HIGHLIGHTERS+=(${
lib.concatStringsSep " "
(map lib.escapeShellArg cfg.syntaxHighlighting.highlighters)
})
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
"ZSH_HIGHLIGHT_STYLES+=(${lib.escapeShellArg name} ${
lib.escapeShellArg value
})") cfg.syntaxHighlighting.styles)}
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value:
"ZSH_HIGHLIGHT_PATTERNS+=(${lib.escapeShellArg name} ${
lib.escapeShellArg value
})") cfg.syntaxHighlighting.patterns)}
''))

(mkOrder 1400 (optionalString
(cfg.historySubstringSearch.enable or false)
# Load zsh-history-substring-search after zsh-syntax-highlighting
# 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
${lib.concatMapStringsSep "\n"
(upKey: "bindkey \"${upKey}\" history-substring-search-up")
(lib.toList cfg.historySubstringSearch.searchUpKey)
}
${lib.concatMapStringsSep "\n"
(downKey: "bindkey \"${downKey}\" history-substring-search-down")
(lib.toList cfg.historySubstringSearch.searchDownKey)
}
'')

(optionalString cfg.zprof.enable
''
''
source ${pkgs.zsh-history-substring-search}/share/zsh-history-substring-search/zsh-history-substring-search.zsh
${lib.concatMapStringsSep "\n"
(upKey: ''bindkey "${upKey}" history-substring-search-up'')
(lib.toList cfg.historySubstringSearch.searchUpKey)}
${lib.concatMapStringsSep "\n"
(downKey: ''bindkey "${downKey}" history-substring-search-down'')
(lib.toList cfg.historySubstringSearch.searchDownKey)}
''))

(mkOrder 1450 (optionalString cfg.zprof.enable ''
zprof
'')
''))
];

cfg.initExtraLast
]);
home.file."${relToDotDir ".zshrc"}".text = cfg.initContents;
}

(mkIf cfg.oh-my-zsh.enable {
Expand Down
2 changes: 2 additions & 0 deletions tests/modules/programs/zsh/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
zsh-prezto = ./prezto.nix;
zsh-syntax-highlighting = ./syntax-highlighting.nix;
zsh-abbr = ./zsh-abbr.nix;
zshrc-contents-insert-before = ./zshrc-contents-insert-before.nix;
zshrc-contents-insert-after = ./zshrc-contents-insert-after.nix;
}
14 changes: 14 additions & 0 deletions tests/modules/programs/zsh/zshrc-contents-insert-after.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{ lib, ... }: {
programs.zsh = {
enable = true;
initContents = lib.mkAfter ''
# Custom contents
echo "Custom contents"
'';
};

nmt.script = ''
assertFileExists home-files/.zshrc
assertFileRegex home-files/.zshrc $'# Custom contents\necho "Custom contents"$'
'';
}
14 changes: 14 additions & 0 deletions tests/modules/programs/zsh/zshrc-contents-insert-before.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{ lib, ... }: {
programs.zsh = {
enable = true;
initContents = lib.mkBefore ''
# Custom contents
echo "Custom contents"
'';
};

nmt.script = ''
assertFileExists home-files/.zshrc
assertFileRegex home-files/.zshrc $'^# Custom contents\necho "Custom contents"'
'';
}

0 comments on commit 472e79e

Please sign in to comment.