Introduce a zsh module for nicer shelling

This commit is contained in:
TEC 2023-06-27 20:33:25 +08:00
parent 8c2475e8e8
commit 5de9912cf7
Signed by: tec
SSH Key Fingerprint: SHA256:eobz41Mnm0/iYWBvWThftS0ElEs1ftBr6jamutnXc/A
5 changed files with 1905 additions and 0 deletions

View File

@ -11,6 +11,8 @@
};
};
nixConfig.sandbox = "relaxed";
outputs = inputs@{ self, nixpkgs, flake-utils-plus, agenix, ... }:
let
modules = flake-utils-plus.lib.exportModules (
@ -24,6 +26,7 @@
golgi.modules = with modules; [
common
admin
zsh
hardware-hetzner
hardened
agenix.nixosModule

31
modules/zsh/default.nix Normal file
View File

@ -0,0 +1,31 @@
{ config, lib, pkgs, ... }:
{
imports = [ ./z4h.nix ];
users.defaultUserShell = pkgs.zsh;
environment.shells = [ pkgs.zsh ];
environment.systemPackages =
[ pkgs.exa pkgs.bat pkgs.fd pkgs.ripgrep pkgs.htop ];
programs.zsh = {
enable = true;
shellAliases = {
l = "exa --long --all --binary --modified --classify --group-directories-first";
tree = "exa --tree --binary --modified --classify --group-directories-first";
};
z4h = {
enable = true;
plugins = [ "hlissner/zsh-autopair" ];
env = {
POWERLEVEL9K_CONFIG_FILE = "/etc/p10k.zsh";
};
envRc = {
LESS = "--quit-if-one-screen --ignore-case --status-column --LONG-PROMPT --RAW-CONTROL-CHARS --HILITE-UNREAD --tabs=4 --no-init --window=-4";
BAT_THEME = "ansi";
};
autoloads = [ "zmv" ];
multiLinePrompt = true;
};
};
environment.etc."p10k.zsh".source = ./p10k.zsh;
}

1623
modules/zsh/p10k.zsh Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
{ config, lib, pkgs, plugins, ... }:
with pkgs;
stdenv.mkDerivation rec {
# Disable the Nix build sandbox for this specific build.
# This means the build can freely talk to the Internet.
# Requires the sandbox to be set to false/"relaxed".
__noChroot = true;
pname = "zsh4humans";
version = "latest";
dontUnpack = true;
nativeBuildInputs = [ zsh curl cacert git ];
buildPhase = ''
runHook preBuild
export HOME=$(pwd)
export ZDOTDIR=$(pwd)
export XDG_CACHE_HOME="$(pwd)"/.cache
echo 'if [ -n "''${ZSH_VERSION-}" ]; then
: ''${ZDOTDIR:=~}
setopt no_global_rcs
[[ -o no_interactive && -z "''${Z4H_BOOTSTRAPPING-}" ]] && return
setopt no_rcs
unset Z4H_BOOTSTRAPPING
fi
Z4H_URL="https://raw.githubusercontent.com/romkatv/zsh4humans/v5"
: "''${Z4H:=''${XDG_CACHE_HOME:-''$HOME/.cache}/zsh4humans/v5}"
umask o-w
if [ ! -e "''$Z4H"/z4h.zsh ]; then
mkdir -p "''$Z4H"
curl -fsSL -- "''$Z4H_URL"/z4h.zsh >"''$Z4H"/z4h.zsh
fi
. "''$Z4H"/z4h.zsh || return
setopt rcs
' > "$ZDOTDIR"/.zshenv
echo "--zshenv--"
cat "$ZDOTDIR"/.zshenv
echo '
zstyle ':z4h:' auto-update 'no'
${(lib.strings.concatMapStringsSep "\n" (plg: "z4h install " + plg) plugins)}
z4h init
${(lib.strings.concatMapStringsSep "\n" (plg: "z4h load " + plg) plugins)}
' > "''$ZDOTDIR"/.zshrc
echo "--zshrc--"
cat "$ZDOTDIR"/.zshrc
echo "--running--"
zsh -i -l -c 'exit'
'';
installPhase = ''
runHook preInstall
mkdir -p $out/share
mv .cache/zsh4humans/v5 $out/share/z4h
runHook postInstall
'';
}

185
modules/zsh/z4h.nix Normal file
View File

@ -0,0 +1,185 @@
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.programs.zsh.z4h;
package = (pkgs.callPackage ./z4h-package.nix {
plugins = cfg.plugins;
});
in
{
options = {
programs.zsh.z4h = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Enable zsh4humans.";
};
plugins = mkOption {
type = types.listOf(types.str);
default = [];
description = lib.mdDoc "List of zsh plugins.";
};
path = mkOption {
type = types.str;
default = "/var/lib/zsh";
description = lib.mdDoc "Path to the zsh4humans state dir.";
};
histFile = mkOption {
type = types.str;
default = "${cfg.path}/history";
description = lib.mdDoc "Path of the history file to use.";
};
env = mkOption {
type = types.attrs;
default = { };
description = lib.mdDoc "Environment variables applied in `.zshenv`.";
};
envRc = mkOption {
type = types.attrs;
default = { };
description = lib.mdDoc "Environment variables applied in `.zshrc`.";
};
multiLinePrompt = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Indicate whether the shell prompt is multi-line.";
};
autoloads = mkOption {
type = types.listOf(types.str);
default = [];
description = lib.mdDoc "List of functions to autoload.";
};
zstyles = mkOption {
type = types.str;
default = ''
zstyle ':z4h:' auto-update 'no'
zstyle ':z4h:bindkey' keyboard 'pc'
zstyle ':z4h:' start-tmux 'no'
zstyle ':z4h:' term-shell-integration 'yes'
zstyle ':z4h:autosuggestions' forward-char 'accept'
zstyle ':z4h:fzf-complete' recurse-dirs 'yes'
zstyle ':z4h:*' fzf-flags --color=hl:13,hl+:13
'';
};
zstylesExtra = mkOption {
type = types.str;
default = "";
};
keybindings = mkOption {
type = types.str;
default = ''
z4h bindkey z4h-backward-kill-word Ctrl+Backspace Ctrl+H
z4h bindkey z4h-backward-kill-zword Ctrl+Alt+Backspace
z4h bindkey undo Ctrl+/ Shift+Tab # undo the last command line change
z4h bindkey redo Alt+/ # redo the last undone command line change
z4h bindkey z4h-cd-back Alt+Left # cd into the previous directory
z4h bindkey z4h-cd-forward Alt+Right # cd into the next directory
z4h bindkey z4h-cd-up Alt+Up # cd into the parent directory
z4h bindkey z4h-cd-down Alt+Down # cd into a child directory
# Make the transient prompt work consistently when closing an SSH connection.
z4h bindkey z4h-eof Ctrl+D
setopt ignore_eof
'';
};
keybindingsExtra = mkOption {
type = types.str;
default = "";
};
mdFunction = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Define `mkdir` + `cd` function `md`.";
};
extraInit = mkOption {
type = types.str;
default = "";
};
};
};
config = mkIf cfg.enable {
programs.zsh = {
# Interpolated config
histSize = 50000;
histFile = "/var/lib/zsh/history";
enableCompletion = false; # We take care of this manually.
# .zshenv body
shellInit = concatStringsSep "\n" [
''
if [ -n "''${ZSH_VERSION-}" ]; then
HISTFILE=${cfg.histFile}
''
(concatStringsSep "\n"
(lib.attrsets.mapAttrsToList
(k: v: " export ${k}=${v}")
cfg.env))
''
zsh-newuser-install() { source /etc/zshrc }
: ''${ZDOTDIR:=~}
setopt no_global_rcs
[[ -o no_interactive && -z "''${Z4H_BOOTSTRAPPING-}" ]] && return
setopt no_rcs
unset Z4H_BOOTSTRAPPING
fi
# Plain prompt for emacs tramp sessions, and no further setup.
[[ $TERM == "dumb" ]] && unsetopt zle && PS1='> ' && return
Z4H_URL="https://raw.githubusercontent.com/romkatv/zsh4humans/v5"
: ''${Z4H:="${cfg.path}"}
umask o-w
. "$Z4H"/z4h.zsh || return
setopt rcs
''
];
# .zshrc body
promptInit = concatStringsSep "\n" [
"[[ $TERM == \"dumb\" ]] && unsetopt zle && PS1='> ' && return"
cfg.zstyles
cfg.zstylesExtra
(lib.strings.concatMapStringsSep "\n"
(plg: "z4h install " + plg) cfg.plugins)
"z4h init || return"
(concatStringsSep "\n"
(lib.attrsets.mapAttrsToList
(k: v: "export ${k}=${v}")
cfg.env))
(lib.strings.concatMapStringsSep "\n"
(plg: "z4h load " + plg) cfg.plugins)
(lib.strings.concatMapStringsSep "\n"
(func: "autoload -Uz " + func) cfg.autoloads)
cfg.keybindings
cfg.keybindingsExtra
(if cfg.multiLinePrompt then
"POSTEDIT=$'\n\n'" else "")
(if cfg.mdFunction then
''
function md() { [[ $# == 1 ]] && mkdir -p -- "$1" && cd -- "$1" }
compdef _directories md
'' else "")
cfg.extraInit
"alias ls=\"\${aliases[ls]:-ls} -A\""
''
setopt glob_dots # no special treatment for file names with a leading dot
setopt no_auto_menu # require an extra TAB press to open the completion menu
''
];
};
systemd.tmpfiles.rules = [
"d ${cfg.path} 0755 root wheel"
"d ${cfg.path}/tmp 0777 root wheel"
"d ${cfg.path}/cache 0777 root wheel"
"d ${cfg.path}/stickycache 0777 root wheel"
"f ${cfg.histFile} 0666 root wheel"
] ++ (map
(stub: "L+ ${cfg.path}/${stub} - - - - ${package}/share/z4h/${stub}")
(["bin" "fn" "fzf" "powerlevel10k" "systemd"
"terminfo" "zsh4humans" "zsh-autosuggestions"
"zsh-completions" "zsh-history-substring-search"
"zsh-syntax-highlighting" "z4h.zsh" "z4h.zsh.zwc"]
++ (lists.unique (map head (map (splitString "/") cfg.plugins)))));
};
}