summary refs log tree commit diff
path: root/nixos/modules/programs/zsh
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/programs/zsh')
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.nix146
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.xml155
-rw-r--r--nixos/modules/programs/zsh/zinputrc42
-rw-r--r--nixos/modules/programs/zsh/zsh-autoenv.nix28
-rw-r--r--nixos/modules/programs/zsh/zsh-autosuggestions.nix73
-rw-r--r--nixos/modules/programs/zsh/zsh-syntax-highlighting.nix107
-rw-r--r--nixos/modules/programs/zsh/zsh.nix303
7 files changed, 854 insertions, 0 deletions
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.nix b/nixos/modules/programs/zsh/oh-my-zsh.nix
new file mode 100644
index 00000000000..9d7622bd328
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.nix
@@ -0,0 +1,146 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.zsh.ohMyZsh;
+
+  mkLinkFarmEntry = name: dir:
+    let
+      env = pkgs.buildEnv {
+        name = "zsh-${name}-env";
+        paths = cfg.customPkgs;
+        pathsToLink = "/share/zsh/${dir}";
+      };
+    in
+      { inherit name; path = "${env}/share/zsh/${dir}"; };
+
+  mkLinkFarmEntry' = name: mkLinkFarmEntry name name;
+
+  custom =
+    if cfg.custom != null then cfg.custom
+    else if length cfg.customPkgs == 0 then null
+    else pkgs.linkFarm "oh-my-zsh-custom" [
+      (mkLinkFarmEntry' "themes")
+      (mkLinkFarmEntry "completions" "site-functions")
+      (mkLinkFarmEntry' "plugins")
+    ];
+
+in
+  {
+    imports = [
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "enable" ] [ "programs" "zsh" "ohMyZsh" "enable" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ])
+      (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ])
+    ];
+
+    options = {
+      programs.zsh.ohMyZsh = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable oh-my-zsh.
+          '';
+        };
+
+        package = mkOption {
+          default = pkgs.oh-my-zsh;
+          defaultText = literalExpression "pkgs.oh-my-zsh";
+          description = ''
+            Package to install for `oh-my-zsh` usage.
+          '';
+
+          type = types.package;
+        };
+
+        plugins = mkOption {
+          default = [];
+          type = types.listOf(types.str);
+          description = ''
+            List of oh-my-zsh plugins
+          '';
+        };
+
+        custom = mkOption {
+          default = null;
+          type = with types; nullOr str;
+          description = ''
+            Path to a custom oh-my-zsh package to override config of oh-my-zsh.
+            (Can't be used along with `customPkgs`).
+          '';
+        };
+
+        customPkgs = mkOption {
+          default = [];
+          type = types.listOf types.package;
+          description = ''
+            List of custom packages that should be loaded into `oh-my-zsh`.
+          '';
+        };
+
+        theme = mkOption {
+          default = "";
+          type = types.str;
+          description = ''
+            Name of the theme to be used by oh-my-zsh.
+          '';
+        };
+
+        cacheDir = mkOption {
+          default = "$HOME/.cache/oh-my-zsh";
+          type = types.str;
+          description = ''
+            Cache directory to be used by `oh-my-zsh`.
+            Without this option it would default to the read-only nix store.
+          '';
+        };
+      };
+    };
+
+    config = mkIf cfg.enable {
+
+      # Prevent zsh from overwriting oh-my-zsh's prompt
+      programs.zsh.promptInit = mkDefault "";
+
+      environment.systemPackages = [ cfg.package ];
+
+      programs.zsh.interactiveShellInit = ''
+        # oh-my-zsh configuration generated by NixOS
+        export ZSH=${cfg.package}/share/oh-my-zsh
+
+        ${optionalString (length(cfg.plugins) > 0)
+          "plugins=(${concatStringsSep " " cfg.plugins})"
+        }
+
+        ${optionalString (custom != null)
+          "ZSH_CUSTOM=\"${custom}\""
+        }
+
+        ${optionalString (stringLength(cfg.theme) > 0)
+          "ZSH_THEME=\"${cfg.theme}\""
+        }
+
+        ${optionalString (cfg.cacheDir != null) ''
+          if [[ ! -d "${cfg.cacheDir}" ]]; then
+            mkdir -p "${cfg.cacheDir}"
+          fi
+          ZSH_CACHE_DIR=${cfg.cacheDir}
+        ''}
+
+        source $ZSH/oh-my-zsh.sh
+      '';
+
+      assertions = [
+        {
+          assertion = cfg.custom != null -> cfg.customPkgs == [];
+          message = "If `cfg.custom` is set for `ZSH_CUSTOM`, `customPkgs` can't be used!";
+        }
+      ];
+
+    };
+
+    meta.doc = ./oh-my-zsh.xml;
+  }
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.xml b/nixos/modules/programs/zsh/oh-my-zsh.xml
new file mode 100644
index 00000000000..14a7228ad9b
--- /dev/null
+++ b/nixos/modules/programs/zsh/oh-my-zsh.xml
@@ -0,0 +1,155 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-programs-zsh-ohmyzsh">
+ <title>Oh my ZSH</title>
+ <para>
+  <literal><link xlink:href="https://ohmyz.sh/">oh-my-zsh</link></literal> is a
+  framework to manage your <link xlink:href="https://www.zsh.org/">ZSH</link>
+  configuration including completion scripts for several CLI tools or custom
+  prompt themes.
+ </para>
+ <section xml:id="module-programs-oh-my-zsh-usage">
+  <title>Basic usage</title>
+
+  <para>
+   The module uses the <literal>oh-my-zsh</literal> package with all available
+   features. The initial setup using Nix expressions is fairly similar to the
+   configuration format of <literal>oh-my-zsh</literal>.
+<programlisting>
+{
+  programs.zsh.ohMyZsh = {
+    enable = true;
+    plugins = [ "git" "python" "man" ];
+    theme = "agnoster";
+  };
+}
+</programlisting>
+   For a detailed explanation of these arguments please refer to the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal>
+   docs</link>.
+  </para>
+
+  <para>
+   The expression generates the needed configuration and writes it into your
+   <literal>/etc/zshrc</literal>.
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-additions">
+  <title>Custom additions</title>
+
+  <para>
+   Sometimes third-party or custom scripts such as a modified theme may be
+   needed. <literal>oh-my-zsh</literal> provides the
+   <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link>
+   environment variable for this which points to a directory with additional
+   scripts.
+  </para>
+
+  <para>
+   The module can do this as well:
+<programlisting>
+{
+  programs.zsh.ohMyZsh.custom = "~/path/to/custom/scripts";
+}
+</programlisting>
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-environments">
+  <title>Custom environments</title>
+
+  <para>
+   There are several extensions for <literal>oh-my-zsh</literal> packaged in
+   <literal>nixpkgs</literal>. One of them is
+   <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link>
+   which bundles completion scripts and a plugin for
+   <literal>oh-my-zsh</literal>.
+  </para>
+
+  <para>
+   Rather than using a single mutable path for <literal>ZSH_CUSTOM</literal>,
+   it's also possible to generate this path from a list of Nix packages:
+<programlisting>
+{ pkgs, ... }:
+{
+  programs.zsh.ohMyZsh.customPkgs = [
+    pkgs.nix-zsh-completions
+    # and even more...
+  ];
+}
+</programlisting>
+   Internally a single store path will be created using
+   <literal>buildEnv</literal>. Please refer to the docs of
+   <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link>
+   for further reference.
+  </para>
+
+  <para>
+   <emphasis>Please keep in mind that this is not compatible with
+   <literal>programs.zsh.ohMyZsh.custom</literal> as it requires an immutable
+   store path while <literal>custom</literal> shall remain mutable! An
+   evaluation failure will be thrown if both <literal>custom</literal> and
+   <literal>customPkgs</literal> are set.</emphasis>
+  </para>
+ </section>
+ <section xml:id="module-programs-oh-my-zsh-packaging-customizations">
+  <title>Package your own customizations</title>
+
+  <para>
+   If third-party customizations (e.g. new themes) are supposed to be added to
+   <literal>oh-my-zsh</literal> there are several pitfalls to keep in mind:
+  </para>
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     To comply with the default structure of <literal>ZSH</literal> the entire
+     output needs to be written to <literal>$out/share/zsh.</literal>
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Completion scripts are supposed to be stored at
+     <literal>$out/share/zsh/site-functions</literal>. This directory is part
+     of the
+     <literal><link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html">fpath</link></literal>
+     and the package should be compatible with pure <literal>ZSH</literal>
+     setups. The module will automatically link the contents of
+     <literal>site-functions</literal> to completions directory in the proper
+     store path.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     The <literal>plugins</literal> directory needs the structure
+     <literal>pluginname/pluginname.plugin.zsh</literal> as structured in the
+     <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream
+     repo.</link>
+    </para>
+   </listitem>
+  </itemizedlist>
+
+  <para>
+   A derivation for <literal>oh-my-zsh</literal> may look like this:
+<programlisting>
+{ stdenv, fetchFromGitHub }:
+
+stdenv.mkDerivation rec {
+  name = "exemplary-zsh-customization-${version}";
+  version = "1.0.0";
+  src = fetchFromGitHub {
+    # path to the upstream repository
+  };
+
+  dontBuild = true;
+  installPhase = ''
+    mkdir -p $out/share/zsh/site-functions
+    cp {themes,plugins} $out/share/zsh
+    cp completions $out/share/zsh/site-functions
+  '';
+}
+</programlisting>
+  </para>
+ </section>
+</chapter>
diff --git a/nixos/modules/programs/zsh/zinputrc b/nixos/modules/programs/zsh/zinputrc
new file mode 100644
index 00000000000..6121f3e21f1
--- /dev/null
+++ b/nixos/modules/programs/zsh/zinputrc
@@ -0,0 +1,42 @@
+# Stolen from ArchWiki
+
+# create a zkbd compatible hash;
+# to add other keys to this hash, see: man 5 terminfo
+typeset -A key
+
+key[Home]=${terminfo[khome]}
+
+key[End]=${terminfo[kend]}
+key[Insert]=${terminfo[kich1]}
+key[Delete]=${terminfo[kdch1]}
+key[Up]=${terminfo[kcuu1]}
+key[Down]=${terminfo[kcud1]}
+key[Left]=${terminfo[kcub1]}
+key[Right]=${terminfo[kcuf1]}
+key[PageUp]=${terminfo[kpp]}
+key[PageDown]=${terminfo[knp]}
+
+# setup key accordingly
+[[ -n "${key[Home]}"     ]]  && bindkey  "${key[Home]}"     beginning-of-line
+[[ -n "${key[End]}"      ]]  && bindkey  "${key[End]}"      end-of-line
+[[ -n "${key[Insert]}"   ]]  && bindkey  "${key[Insert]}"   overwrite-mode
+[[ -n "${key[Delete]}"   ]]  && bindkey  "${key[Delete]}"   delete-char
+[[ -n "${key[Up]}"       ]]  && bindkey  "${key[Up]}"       up-line-or-history
+[[ -n "${key[Down]}"     ]]  && bindkey  "${key[Down]}"     down-line-or-history
+[[ -n "${key[Left]}"     ]]  && bindkey  "${key[Left]}"     backward-char
+[[ -n "${key[Right]}"    ]]  && bindkey  "${key[Right]}"    forward-char
+[[ -n "${key[PageUp]}"   ]]  && bindkey  "${key[PageUp]}"   beginning-of-buffer-or-history
+[[ -n "${key[PageDown]}" ]]  && bindkey  "${key[PageDown]}" end-of-buffer-or-history
+
+# Finally, make sure the terminal is in application mode, when zle is
+# active. Only then are the values from $terminfo valid.
+if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
+    function zle-line-init () {
+        printf '%s' "${terminfo[smkx]}"
+    }
+    function zle-line-finish () {
+        printf '%s' "${terminfo[rmkx]}"
+    }
+    zle -N zle-line-init
+    zle -N zle-line-finish
+fi
diff --git a/nixos/modules/programs/zsh/zsh-autoenv.nix b/nixos/modules/programs/zsh/zsh-autoenv.nix
new file mode 100644
index 00000000000..62f497a45dd
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autoenv.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.zsh-autoenv;
+in {
+  options = {
+    programs.zsh.zsh-autoenv = {
+      enable = mkEnableOption "zsh-autoenv";
+      package = mkOption {
+        default = pkgs.zsh-autoenv;
+        defaultText = literalExpression "pkgs.zsh-autoenv";
+        description = ''
+          Package to install for `zsh-autoenv` usage.
+        '';
+
+        type = types.package;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    programs.zsh.interactiveShellInit = ''
+      source ${cfg.package}/share/zsh-autoenv/autoenv.zsh
+    '';
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh-autosuggestions.nix b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
new file mode 100644
index 00000000000..2e53e907d54
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-autosuggestions.nix
@@ -0,0 +1,73 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.autosuggestions;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "programs" "zsh" "enableAutosuggestions" ] [ "programs" "zsh" "autosuggestions" "enable" ])
+  ];
+
+  options.programs.zsh.autosuggestions = {
+
+    enable = mkEnableOption "zsh-autosuggestions";
+
+    highlightStyle = mkOption {
+      type = types.str;
+      default = "fg=8"; # https://github.com/zsh-users/zsh-autosuggestions/tree/v0.4.3#suggestion-highlight-style
+      description = "Highlight style for suggestions ({fore,back}ground color)";
+      example = "fg=cyan";
+    };
+
+    strategy = mkOption {
+      type = types.listOf (types.enum [ "history" "completion" "match_prev_cmd" ]);
+      default = [ "history" ];
+      description = ''
+        `ZSH_AUTOSUGGEST_STRATEGY` is an array that specifies how suggestions should be generated.
+        The strategies in the array are tried successively until a suggestion is found.
+        There are currently three built-in strategies to choose from:
+
+        - `history`: Chooses the most recent match from history.
+        - `completion`: Chooses a suggestion based on what tab-completion would suggest. (requires `zpty` module)
+        - `match_prev_cmd`: Like `history`, but chooses the most recent match whose preceding history item matches
+            the most recently executed command. Note that this strategy won't work as expected with ZSH options that
+            don't preserve the history order such as `HIST_IGNORE_ALL_DUPS` or `HIST_EXPIRE_DUPS_FIRST`.
+      '';
+    };
+
+    async = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to fetch suggestions asynchronously";
+      example = false;
+    };
+
+    extraConfig = mkOption {
+      type = with types; attrsOf str;
+      default = {};
+      description = "Attribute set with additional configuration values";
+      example = literalExpression ''
+        {
+          "ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE" = "20";
+        }
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.zsh.interactiveShellInit = ''
+      source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
+
+      export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="${cfg.highlightStyle}"
+      export ZSH_AUTOSUGGEST_STRATEGY=(${concatStringsSep " " cfg.strategy})
+      ${optionalString (!cfg.async) "unset ZSH_AUTOSUGGEST_USE_ASYNC"}
+
+      ${concatStringsSep "\n" (mapAttrsToList (key: value: ''export ${key}="${value}"'') cfg.extraConfig)}
+    '';
+
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
new file mode 100644
index 00000000000..1eb53ccae52
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh-syntax-highlighting.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.zsh.syntaxHighlighting;
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "enable" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "highlighters" ] [ "programs" "zsh" "syntaxHighlighting" "highlighters" ])
+    (mkRenamedOptionModule [ "programs" "zsh" "syntax-highlighting" "patterns" ] [ "programs" "zsh" "syntaxHighlighting" "patterns" ])
+  ];
+
+  options = {
+    programs.zsh.syntaxHighlighting = {
+      enable = mkEnableOption "zsh-syntax-highlighting";
+
+      highlighters = mkOption {
+        default = [ "main" ];
+
+        # https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+        type = types.listOf(types.enum([
+          "main"
+          "brackets"
+          "pattern"
+          "cursor"
+          "root"
+          "line"
+        ]));
+
+        description = ''
+          Specifies the highlighters to be used by zsh-syntax-highlighting.
+
+          The following defined options can be found here:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
+        '';
+      };
+
+      patterns = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+
+        example = literalExpression ''
+          {
+            "rm -rf *" = "fg=white,bold,bg=red";
+          }
+        '';
+
+        description = ''
+          Specifies custom patterns to be highlighted by zsh-syntax-highlighting.
+
+          Please refer to the docs for more information about the usage:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/pattern.md
+        '';
+      };
+      styles = mkOption {
+        default = {};
+        type = types.attrsOf types.str;
+
+        example = literalExpression ''
+          {
+            "alias" = "fg=magenta,bold";
+          }
+        '';
+
+        description = ''
+          Specifies custom styles to be highlighted by zsh-syntax-highlighting.
+
+          Please refer to the docs for more information about the usage:
+          https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/main.md
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ zsh-syntax-highlighting ];
+
+    assertions = [
+      {
+        assertion = length(attrNames cfg.patterns) > 0 -> elem "pattern" cfg.highlighters;
+        message = ''
+          When highlighting patterns, "pattern" needs to be included in the list of highlighters.
+        '';
+      }
+    ];
+
+    programs.zsh.interactiveShellInit = with pkgs;
+      lib.mkAfter (lib.concatStringsSep "\n" ([
+        "source ${zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
+      ] ++ optional (length(cfg.highlighters) > 0)
+        "ZSH_HIGHLIGHT_HIGHLIGHTERS=(${concatStringsSep " " cfg.highlighters})"
+        ++ optionals (length(attrNames cfg.patterns) > 0)
+          (mapAttrsToList (
+            pattern: design:
+            "ZSH_HIGHLIGHT_PATTERNS+=('${pattern}' '${design}')"
+          ) cfg.patterns)
+        ++ optionals (length(attrNames cfg.styles) > 0)
+          (mapAttrsToList (
+            styles: design:
+            "ZSH_HIGHLIGHT_STYLES[${styles}]='${design}'"
+          ) cfg.styles)
+      ));
+  };
+}
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
new file mode 100644
index 00000000000..5fe98b6801b
--- /dev/null
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -0,0 +1,303 @@
+# This module defines global configuration for the zshell.
+
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfge = config.environment;
+
+  cfg = config.programs.zsh;
+  opt = options.programs.zsh;
+
+  zshAliases = concatStringsSep "\n" (
+    mapAttrsFlatten (k: v: "alias ${k}=${escapeShellArg v}")
+      (filterAttrs (k: v: v != null) cfg.shellAliases)
+  );
+
+  zshStartupNotes = ''
+    # Note that generated /etc/zprofile and /etc/zshrc files do a lot of
+    # non-standard setup to make zsh usable with no configuration by default.
+    #
+    # Which means that unless you explicitly meticulously override everything
+    # generated, interactions between your ~/.zshrc and these files are likely
+    # to be rather surprising.
+    #
+    # Note however, that you can disable loading of the generated /etc/zprofile
+    # and /etc/zshrc (you can't disable loading of /etc/zshenv, but it is
+    # designed to not set anything surprising) by setting `no_global_rcs` option
+    # in ~/.zshenv:
+    #
+    #   echo setopt no_global_rcs >> ~/.zshenv
+    #
+    # See "STARTUP/SHUTDOWN FILES" section of zsh(1) for more info.
+  '';
+
+in
+
+{
+
+  options = {
+
+    programs.zsh = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to configure zsh as an interactive shell. To enable zsh for
+          a particular user, use the <option>users.users.&lt;name?&gt;.shell</option>
+          option for that user. To enable zsh system-wide use the
+          <option>users.defaultUserShell</option> option.
+        '';
+        type = types.bool;
+      };
+
+      shellAliases = mkOption {
+        default = { };
+        description = ''
+          Set of aliases for zsh shell, which overrides <option>environment.shellAliases</option>.
+          See <option>environment.shellAliases</option> for an option format description.
+        '';
+        type = with types; attrsOf (nullOr (either str path));
+      };
+
+      shellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      loginShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during zsh login shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      interactiveShellInit = mkOption {
+        default = "";
+        description = ''
+          Shell script code called during interactive zsh shell initialisation.
+        '';
+        type = types.lines;
+      };
+
+      promptInit = mkOption {
+        default = ''
+          # Note that to manually override this in ~/.zshrc you should run `prompt off`
+          # before setting your PS1 and etc. Otherwise this will likely to interact with
+          # your ~/.zshrc configuration in unexpected ways as the default prompt sets
+          # a lot of different prompt variables.
+          autoload -U promptinit && promptinit && prompt suse && setopt prompt_sp
+        '';
+        description = ''
+          Shell script code used to initialise the zsh prompt.
+        '';
+        type = types.lines;
+      };
+
+      histSize = mkOption {
+        default = 2000;
+        description = ''
+          Change history size.
+        '';
+        type = types.int;
+      };
+
+      histFile = mkOption {
+        default = "$HOME/.zsh_history";
+        description = ''
+          Change history file.
+        '';
+        type = types.str;
+      };
+
+      setOptions = mkOption {
+        type = types.listOf types.str;
+        default = [
+          "HIST_IGNORE_DUPS"
+          "SHARE_HISTORY"
+          "HIST_FCNTL_LOCK"
+        ];
+        example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
+        description = ''
+          Configure zsh options. See
+          <citerefentry><refentrytitle>zshoptions</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+        '';
+      };
+
+      enableCompletion = mkOption {
+        default = true;
+        description = ''
+          Enable zsh completion for all interactive zsh shells.
+        '';
+        type = types.bool;
+      };
+
+      enableBashCompletion = mkOption {
+        default = false;
+        description = ''
+          Enable compatibility with bash's programmable completion system.
+        '';
+        type = types.bool;
+      };
+
+      enableGlobalCompInit = mkOption {
+        default = cfg.enableCompletion;
+        defaultText = literalExpression "config.${opt.enableCompletion}";
+        description = ''
+          Enable execution of compinit call for all interactive zsh shells.
+
+          This option can be disabled if the user wants to extend its
+          <literal>fpath</literal> and a custom <literal>compinit</literal>
+          call in the local config is required.
+        '';
+        type = types.bool;
+      };
+
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    programs.zsh.shellAliases = mapAttrs (name: mkDefault) cfge.shellAliases;
+
+    environment.etc.zshenv.text =
+      ''
+        # /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for all shells.
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHENV_SOURCED" ]; then return; fi
+        __ETC_ZSHENV_SOURCED=1
+
+        if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
+            . ${config.system.build.setEnvironment}
+        fi
+
+        HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
+
+        # Tell zsh how to find installed completions.
+        for p in ''${(z)NIX_PROFILES}; do
+            fpath+=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions)
+        done
+
+        # Setup custom shell init stuff.
+        ${cfge.shellInit}
+
+        ${cfg.shellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zshenv.local; then
+            . /etc/zshenv.local
+        fi
+      '';
+
+    environment.etc.zprofile.text =
+      ''
+        # /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for login shells.
+        #
+        ${zshStartupNotes}
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZPROFILE_SOURCED" ]; then return; fi
+        __ETC_ZPROFILE_SOURCED=1
+
+        # Setup custom login shell init stuff.
+        ${cfge.loginShellInit}
+
+        ${cfg.loginShellInit}
+
+        # Read system-wide modifications.
+        if test -f /etc/zprofile.local; then
+            . /etc/zprofile.local
+        fi
+      '';
+
+    environment.etc.zshrc.text =
+      ''
+        # /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
+        # This file is read for interactive shells.
+        #
+        ${zshStartupNotes}
+
+        # Only execute this file once per shell.
+        if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
+        __ETC_ZSHRC_SOURCED=1
+
+        ${optionalString (cfg.setOptions != []) ''
+          # Set zsh options.
+          setopt ${concatStringsSep " " cfg.setOptions}
+        ''}
+
+        # Setup command line history.
+        # Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
+        SAVEHIST=${toString cfg.histSize}
+        HISTSIZE=${toString cfg.histSize}
+        HISTFILE=${cfg.histFile}
+
+        # Configure sane keyboard defaults.
+        . /etc/zinputrc
+
+        ${optionalString cfg.enableGlobalCompInit ''
+          # Enable autocompletion.
+          autoload -U compinit && compinit
+        ''}
+
+        ${optionalString cfg.enableBashCompletion ''
+          # Enable compatibility with bash's completion system.
+          autoload -U bashcompinit && bashcompinit
+        ''}
+
+        # Setup custom interactive shell init stuff.
+        ${cfge.interactiveShellInit}
+
+        ${cfg.interactiveShellInit}
+
+        # Setup aliases.
+        ${zshAliases}
+
+        # Setup prompt.
+        ${cfg.promptInit}
+
+        # Disable some features to support TRAMP.
+        if [ "$TERM" = dumb ]; then
+            unsetopt zle prompt_cr prompt_subst
+            unset RPS1 RPROMPT
+            PS1='$ '
+            PROMPT='$ '
+        fi
+
+        # Read system-wide modifications.
+        if test -f /etc/zshrc.local; then
+            . /etc/zshrc.local
+        fi
+      '';
+
+    # Bug in nix flakes:
+    # If we use `.source` here the path is garbage collected also we point to it with a symlink
+    # see https://github.com/NixOS/nixpkgs/issues/132732
+    environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
+
+    environment.systemPackages = [ pkgs.zsh ]
+      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+
+    environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
+
+    #users.defaultUserShell = mkDefault "/run/current-system/sw/bin/zsh";
+
+    environment.shells =
+      [
+        "/run/current-system/sw/bin/zsh"
+        "${pkgs.zsh}/bin/zsh"
+      ];
+
+  };
+
+}