diff --git a/modules/misc/news.nix b/modules/misc/news.nix index f088f2909..a3fb800f0 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -2129,6 +2129,17 @@ in { Cohere, Groq). ''; } + + { + time = "2025-03-11T02:34:43+00:00"; + condition = config.programs.zsh.enable; + message = '' + A new module is available: 'programs.zsh.initContent'. + + initContent option allows you to set the content of the zshrc file, + you can use `lib.mkOrder` to specify the order of the content you want to insert. + ''; + } ]; }; } diff --git a/modules/programs/zsh.nix b/modules/programs/zsh.nix index e4399d2d7..67e95d4e0 100644 --- a/modules/programs/zsh.nix +++ b/modules/programs/zsh.nix @@ -471,6 +471,15 @@ in description = "Environment variables that will be set for zsh session."; }; + initContent = mkOption { + default = ""; + type = types.lines; + example = lib.mkOrder 1000 '' + echo "Hello, initContent!" + ''; + description = "Content to be added to {file}`.zshrc`. To specify the order, use `lib.mkOrder`."; + }; + initExtraBeforeCompInit = mkOption { default = ""; type = types.lines; @@ -616,158 +625,161 @@ 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.initContent = mkMerge [ # zprof must be loaded before everything else, since it # benchmarks the shell initialization. - (optionalString cfg.zprof.enable '' + (mkOrder 400 (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 + + HELPDIR="${cfg.package}/share/zsh/$ZSH_VERSION/help" '') - '' - 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" - '' - - (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.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.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.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" ( + '' + 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}") + (name: value: "ZSH_HIGHLIGHT_STYLES[${lib.escapeShellArg name}]=${lib.escapeShellArg value}") cfg.syntaxHighlighting.styles - )} - ${lib.concatStringsSep "\n" ( + )} + ${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) + (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) - } - '') + '' + 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 - '' + (mkOrder 1450 (optionalString cfg.zprof.enable '' zprof - '') - ]); + '')) + ]; + + home.file."${relToDotDir ".zshrc"}".text = cfg.initContent; } (mkIf cfg.oh-my-zsh.enable { diff --git a/tests/modules/programs/zsh/default.nix b/tests/modules/programs/zsh/default.nix index 25aa3b470..38e704aac 100644 --- a/tests/modules/programs/zsh/default.nix +++ b/tests/modules/programs/zsh/default.nix @@ -9,4 +9,5 @@ zsh-prezto = ./prezto.nix; zsh-syntax-highlighting = ./syntax-highlighting.nix; zsh-abbr = ./zsh-abbr.nix; + zshrc-contents-priorities = ./zshrc-content-priorities.nix; } diff --git a/tests/modules/programs/zsh/zshrc-content-priorities.nix b/tests/modules/programs/zsh/zshrc-content-priorities.nix new file mode 100644 index 000000000..5245bef58 --- /dev/null +++ b/tests/modules/programs/zsh/zshrc-content-priorities.nix @@ -0,0 +1,36 @@ +{ lib, ... }: { + programs.zsh = { + enable = true; + + initContent = lib.mkMerge [ + (lib.mkBefore '' + # High priority (mkBefore) + echo "High priority content" + '') + + (lib.mkAfter '' + # Low priority (mkAfter) + echo "Low priority content" + '') + + '' + # Default priority + echo "Default priority content" + '' + ]; + + zprof.enable = true; + }; + + nmt.script = '' + assertFileExists home-files/.zshrc + + assertFileContains home-files/.zshrc "zmodload zsh/zprof" + assertFileContains home-files/.zshrc "High priority content" + assertFileContains home-files/.zshrc "Default priority content" + assertFileContains home-files/.zshrc "Low priority content" + + assertFileRegex home-files/.zshrc '^zmodload zsh/zprof' + assertFileRegex home-files/.zshrc 'echo "Low priority content"$' + ''; +}