This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / tools / zsh / flake-module.nix
14 kB 377 lines
1{ self, inputs, ... }: 2{ 3 perSystem = 4 { pkgs, ... }: 5 let 6 zshrc = '' 7 autoload -U compinit && compinit 8 9 # Setup custom interactive shell init stuff. 10 # Bind gpg-agent to this TTY if gpg commands are used. 11 export GPG_TTY=$(tty) 12 13 # show how long a command takes 14 ZSH_COMMAND_TIME_MIN_SECONDS=3 15 ZSH_COMMAND_TIME_EXCLUDE=(vim) 16 ZSH_COMMAND_TIME_MSG="took: %s sec" 17 source ${ 18 pkgs.fetchFromGitHub { 19 owner = "popstas"; 20 repo = "zsh-command-time"; 21 rev = "803d26eef526bff1494d1a584e46a6e08d25d918"; 22 hash = "sha256-ndHVFcz+XmUW0zwFq7pBXygdRKyPLjDZNmTelhd5bv8="; 23 } 24 }/command-time.plugin.zsh 25 26 unsetopt nomatch # no matches found urls 27 setopt autocd extendedglob 28 bindkey -e 29 30 #C-x C-e open line in editor 31 autoload -z edit-command-line 32 zle -N edit-command-line 33 bindkey "^X^E" edit-command-line 34 35 #fzf inclusion 36 source ${pkgs.fzf}/share/fzf/completion.zsh 37 source ${pkgs.fzf}/share/fzf/key-bindings.zsh 38 39 # atuin distributed shell history 40 if type -p atuin >/dev/null; then 41 export ATUIN_CONFIG_DIR=${pkgs.writeTextDir "/config.toml" '' 42 auto_sync = true 43 update_check = false 44 sync_address = "http://green.r:8888" 45 sync_frequency = 0 46 style = "compact" 47 ''}; 48 export ATUIN_NOBIND="true" # disable all keybdinings of atuin 49 eval "$(${pkgs.atuin}/bin/atuin init zsh)" # TODO make this optional? 50 bindkey '^r' _atuin_search_widget # bind ctrl+r to atuin 51 # use zsh only session history 52 fc -p 53 fi 54 55 #completion magic 56 autoload -Uz compinit 57 compinit 58 zstyle ':completion:*' menu select 59 60 #enable automatic rehashing of $PATH 61 zstyle ':completion:*' rehash true 62 63 # Lazy load completions for commands based on their installation path 64 _lazy_completion_loader() { 65 local cmd="$words[1]" 66 local basename_cmd="$cmd" 67 local cmd_path="" 68 69 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: _lazy_completion_loader called for: $cmd" >&2 70 71 # Get command path and basename 72 if [[ "$cmd" == /* ]] && [[ -x "$cmd" ]]; then 73 cmd_path="$cmd" 74 basename_cmd="$(basename "$cmd")" 75 elif command -v "$cmd" >/dev/null 2>&1; then 76 cmd_path="$(which "$cmd" 2>/dev/null)" 77 basename_cmd="$(basename "$cmd_path")" 78 fi 79 80 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: cmd_path=$cmd_path, basename_cmd=$basename_cmd" >&2 81 82 # Find and load completion file 83 local current="$cmd_path" 84 while [[ -n "$current" ]]; do 85 local share_dir="$(dirname "$current")/../share" 86 for subdir in zsh/site-functions zsh/vendor-completions; do 87 local dir="$share_dir/$subdir" 88 if [[ -f "$dir/_$basename_cmd" ]]; then 89 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: found completion: $dir/_$basename_cmd" >&2 90 [[ ! " ''${fpath[@]} " =~ " $dir " ]] && fpath=("$dir" $fpath) 91 source "$dir/_$basename_cmd" 92 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: after sourcing, _comps[$basename_cmd]=''${_comps[$basename_cmd]:-not set}" >&2 93 break 2 94 fi 95 done 96 # Follow absolute symlinks only 97 [[ -L "$current" ]] || break 98 current="$(readlink "$current" 2>/dev/null)" 99 [[ "$current" == /* ]] || break 100 done 101 102 # Register completion for the original command if needed 103 if [[ -n "''${_comps[$basename_cmd]:-}" ]]; then 104 if [[ "$cmd" != "$basename_cmd" ]]; then 105 _comps[$cmd]="''${_comps[$basename_cmd]}" 106 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: registered ''${_comps[$basename_cmd]} for $cmd" >&2 107 fi 108 fi 109 110 # Remove lazy loader only if it's still set 111 if [[ "''${_comps[$cmd]:-}" == "_lazy_completion_loader" ]]; then 112 compdef -d "$cmd" 2>/dev/null 113 fi 114 115 # Run the appropriate completion 116 local comp="''${_comps[$cmd]:-}" 117 [[ -z "$comp" ]] && comp="''${_comps[$basename_cmd]:-_normal}" 118 [[ -n "$ZSH_COMP_DEBUG" ]] && echo "DEBUG: using completion: $comp" >&2 119 $comp "$@" 120 } 121 122 # Set as default completer for commands without completions 123 zstyle -e ':completion:*' completer ' 124 if [[ -z $_comps[$words[1]] ]]; then 125 reply=(_lazy_completion_loader _complete) 126 else 127 reply=(_complete) 128 fi 129 ' 130 131 # fancy mv which interactively gets the second argument if not given 132 function mv() { 133 if [[ "$#" -ne 1 ]] || [[ ! -e "$1" ]]; then 134 command mv -v "$@" 135 return 136 fi 137 138 newfilename="$1" 139 vared newfilename 140 command mv -v -- "$1" "$newfilename" 141 } 142 143 #emacs bindings 144 bindkey "[7~" beginning-of-line 145 bindkey "[8~" end-of-line 146 bindkey "Oc" emacs-forward-word 147 bindkey "Od" emacs-backward-word 148 149 # direnv integration 150 eval "$(${pkgs.direnv}/bin/direnv hook zsh)" 151 152 # Use nvim as MANPAGER if available and output is not piped 153 if command -v nvim >/dev/null 2>&1; then 154 export MANPAGER='sh -c "if [ -t 1 ]; then nvim +Man!; else cat; fi"' 155 fi 156 157 # This function is called whenever a command is not found. 158 command_not_found_handler() { 159 local p='${pkgs.nix-index}/etc/profile.d/command-not-found.sh' 160 if [ -x "$p" ] && [ -f '/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite' ]; then 161 # Run the helper program. 162 "$p" "$@" 163 164 # Retry the command if we just installed it. 165 if [ $? = 126 ]; then 166 "$@" 167 else 168 return 127 169 fi 170 else 171 # Indicate than there was an error so ZSH falls back to its default handler 172 echo "$1: command not found" >&2 173 return 127 174 fi 175 } 176 177 download_nixpkgs_cache_index () { 178 filename="index-$(uname -m | sed 's/^arm64$/aarch64/')-$(uname | tr A-Z a-z)" 179 mkdir -p ~/.cache/nix-index && cd ~/.cache/nix-index 180 # -N will only download a new version if there is an update. 181 , wget -q -N https://github.com/nix-community/nix-index-database/releases/latest/download/$filename 182 ln -f $filename files 183 } 184 185 # Setup aliases. 186 alias -- grep='grep --color=auto' 187 alias -- ip='ip -color=auto' 188 alias -- l='ls -alh' 189 alias -- la='ls -la' 190 alias -- ll='ls -l' 191 alias -- ls='ls --color' 192 alias -- ns='nix-shell --command zsh' 193 alias -- nb='nix build --no-link --print-out-paths -L' 194 alias -- ne='nix eval --json' 195 alias -- nloc='nix-locate --top-level --whole-name' 196 # a child process cannot change this shell's env, so eval its export line 197 alias -- ssh-agent-pick='eval "$(${ 198 self.packages.${pkgs.system}.ssh-agent-pick 199 }/bin/ssh-agent-pick)"' 200 201 # Setup prompt. 202 autoload -U promptinit 203 promptinit 204 205 p_error='%(?..%F{red}%?%f )' 206 t_error='%(?..%? )' 207 208 case $UID in 209 0) 210 p_username='%F{red}root%f' 211 t_username='root' 212 ;; 213 1337) 214 p_username="" 215 t_username="" 216 ;; 217 *) 218 p_username='%F{blue}%n%f' 219 t_username='%n' 220 ;; 221 esac 222 223 if test -n "$SSH_CLIENT"; then 224 p_hostname='@%F{magenta}%M%f ' 225 t_hostname='@%M ' 226 else 227 p_hostname="" 228 t_hostname="" 229 fi 230 231 #check if in nix shell 232 if test -n "$IN_NIX_SHELL"; then 233 p_nixshell='%F{green}[s]%f ' 234 t_nixshell='[s] ' 235 else 236 p_nixshell="" 237 t_nixshell="" 238 fi 239 240 PROMPT="$p_error$p_username$p_hostname$p_nixshell%~ " 241 TITLE="$t_error$t_username$t_hostname$t_nixshell%~" 242 case $TERM in 243 (*xterm* | *rxvt*) 244 function precmd { 245 PROMPT_EVALED=$(print -P "$TITLE") 246 echo -ne "\033]0;$$ $PROMPT_EVALED\007" 247 } 248 # This seems broken for some reason 249 # # This is seen while the shell waits for a command to complete. 250 # function preexec { 251 # PROMPT_EVALED=$(print -P "$TITLE") 252 # echo -ne "\033]0;$$ $PROMPT_EVALED $1\007" 253 # } 254 ;; 255 esac 256 257 258 # Auto-start tmux for new sessions 259 # Check if we're already in tmux (even through sudo) 260 # Only run in interactive shells — non-interactive SSH commands must not exec tmux 261 if [[ -o interactive && "$TERM" != "linux" && -z "$TMUX" && "$TERM" != "dumb" ]]; then 262 # Check if we're inside tmux by looking for tmux in the process tree 263 in_tmux=false 264 pid=$$ 265 while [[ $pid -ne 1 ]]; do 266 if ps -p $pid -o comm= 2>/dev/null | grep -q '^tmux'; then 267 in_tmux=true 268 break 269 fi 270 # Get parent PID 271 pid=$(ps -p $pid -o ppid= 2>/dev/null | tr -d ' ') || break 272 done 273 274 if [[ "$in_tmux" == "false" ]]; then 275 # Preserve SSH_AUTH_SOCK for tmux 276 if [[ -n "$SSH_AUTH_SOCK" ]]; then 277 tmux set-environment -g SSH_AUTH_SOCK "$SSH_AUTH_SOCK" 2>/dev/null 278 fi 279 280 # SSH session chooser: show existing sessions via fzf 281 # Delete key kills the highlighted session, then refreshes the list 282 if [[ -n "$SSH_CLIENT" ]]; then 283 while true; do 284 existing=$(tmux list-sessions -F '#{session_name}: #{session_windows} windows (created #{session_created_string})#{?session_attached, (attached),}' 2>/dev/null) 285 [[ -z "$existing" ]] && break 286 chosen=$(echo ">>> new session <<<\n$existing" \ 287 | fzf --height=80% --reverse \ 288 --header="Pick a tmux session (DEL to kill)" \ 289 --expect=delete \ 290 --preview='s={1}; s=''${s%:}; [ "$s" = ">>>" ] && echo "Create a new session" || tmux capture-pane -t "$s" -p -e 2>/dev/null' \ 291 --preview-window=right:60%) 292 key=$(echo "$chosen" | head -1) 293 selection=$(echo "$chosen" | tail -1) 294 chosen_name=$(echo "$selection" | cut -d: -f1) 295 if [[ "$key" == "delete" && -n "$chosen_name" && "$chosen_name" != ">>> new session <<<" ]]; then 296 tmux kill-session -t "$chosen_name" 2>/dev/null 297 continue 298 fi 299 if [[ -n "$chosen_name" && "$chosen_name" != ">>> new session <<<" ]]; then 300 exec tmux -u attach -t "$chosen_name" 301 fi 302 break 303 done 304 fi 305 306 # Generate a readable session name using name-generator 307 session_name=$(${ 308 self.packages.${pkgs.system}.name-generator 309 }/bin/name-generator 2>/dev/null || echo "session-$$") 310 # Prefix with workspace name in graphical sessions 311 if [[ -n "$WAYLAND_DISPLAY" ]]; then 312 _ws_name=$(${ 313 self.packages.${pkgs.system}.workspace-manager 314 }/bin/workspace-manager workspace 2>/dev/null) 315 [[ -n "$_ws_name" ]] && session_name="''${_ws_name}-''${session_name}" 316 fi 317 # Start tmux with the generated session name 318 exec tmux -u new-session -s "$session_name" 319 fi 320 fi 321 322 # Set tmux status bar color based on hostname for SSH sessions 323 if [[ -n "$TMUX" && "$__host__" != "$HOST" ]]; then 324 # Generate a color based on hostname hash 325 color=$(( $(echo -n "$HOST" | od -An -N4 -tu4) % 255 )) 326 tmux set -g status-bg "colour$color" 2>/dev/null || true 327 export __host__="$HOST" 328 fi 329 330 # Configure tmux for better mouse support 331 if [[ -n "$TMUX" ]]; then 332 # Enable mouse mode for scrolling 333 tmux set -g mouse on 2>/dev/null || true 334 fi 335 336 # Workspace manager integration: cd to workspace directory on new terminal. 337 # Only for shells that start in $HOME: a pane spawned with an explicit 338 # directory (herdr, tmux split in cwd) must keep it. 339 if [[ "$PWD" == "$HOME" ]]; then 340 _ws_dir=$(${self.packages.${pkgs.system}.workspace-manager}/bin/workspace-manager dir 2>/dev/null) 341 [[ -n "$_ws_dir" && -d "$_ws_dir" ]] && cd "$_ws_dir" 342 fi 343 344 # Disable some features to support TRAMP. 345 if [ "$TERM" = dumb ]; then 346 unsetopt zle prompt_cr prompt_subst 347 unset RPS1 RPROMPT 348 PS1='$ ' 349 PROMPT='$ ' 350 fi 351 ''; 352 in 353 { 354 packages.zsh = 355 (inputs.wrappers.lib.wrapPackage { 356 pkgs = pkgs; 357 package = pkgs.zsh; 358 runtimeInputs = with pkgs; [ 359 gnugrep 360 tmux 361 fzf 362 direnv 363 atuin 364 nix-index 365 self.packages.${pkgs.system}.name-generator 366 ]; 367 env = { 368 ZDOTDIR = pkgs.writeTextDir "/.zshrc" zshrc; 369 }; 370 }).overrideAttrs 371 (old: { 372 passthru = (old.passthru or { }) // { 373 inherit zshrc; 374 }; 375 }); 376 }; 377}