#!/bin/zsh ### ### Set global variables ### : ${XDG_CONFIG_HOME:=$HOME/.config} export XDG_CONFIG_HOME ### ### Zsh Configuration ### # Customize spelling correction prompt. SPROMPT='zsh: correct %F{red}%R%f to %F{green}%r%f [nyae]? ' # Characters considered part of a word by the line editor WORDCHARS="*?_-.~$" # Set the "main" keyboard so `bindkey` without `-M` will change the expected map bindkey -A viins main ### ### Path ### setopt nullglob path=( $HOME/bin $HOME/dev_local/bin $HOME/.local/bin $HOME/.local/*/bin $HOME/go/bin /opt/*/bin $path) setopt nonullglob fpath=($fpath /usr/share/zsh/site-functions) # Remove duplicates typeset -U path fpath ### ### Modules ### ### ASDF & direnv # if [[ -e "${XDG_CONFIG_HOME}/asdf-direnv/zshrc" ]]; then source "${XDG_CONFIG_HOME}/asdf-direnv/zshrc" else echo "asdf: direnv plugin not set!" # asdf plugin add direnv # asdf install direnv latest # asdf direnv setup --version latest # asdf global direnv latest fi ### b4b4r07/enhancd # # See https://github.com/b4b4r07/enhancd # # Changes from default behaviour: # - Use single-dot to show upper tree (default is sub-dir); # (use Alt-C (provided by fzf) to go into sub-dir). ENHANCD_DIR=$HOME/.zim ENHANCD_ARG_DOUBLE_DOT='.' ENHANCD_ENABLE_SINGLE_DOT=false ### fzf # # Scripts sourced by zvm_after_init() # # - https://github.com/junegunn/fzf#key-bindings-for-command-line # - https://github.com/junegunn/fzf#fuzzy-completion-for-bash-and-zsh # # Adapted from https://github.com/zimfw/fzf # export FZF_DEFAULT_OPTS='--height 40% --layout=reverse' if (( ${+commands[fd]} )); then export FZF_DEFAULT_COMMAND='command fd --color=always --hidden --no-ignore-vcs --exclude=.git --type=file --strip-cwd-prefix' export FZF_ALT_C_COMMAND='command fd --color=always --hidden --no-ignore-vcs --exclude=.git --type=directory --strip-cwd-prefix' # Trigger for completion: "**" # Commands using _dir: FZF_COMPLETION_DIR_COMMANDS _fzf_compgen_path() { command fd --color=always --hidden --no-ignore-vcs --exclude=.git --type=file . "${1}" } _fzf_compgen_dir() { command fd --color=always --hidden --no-ignore-vcs --exclude=.git --type=directory . "${1}" } export FZF_DEFAULT_OPTS="--ansi ${FZF_DEFAULT_OPTS}" elif (( ${+commands[rg]} )); then export FZF_DEFAULT_COMMAND="command rg -uu -g '!.git' --files" _fzf_compgen_path() { command rg -uu -g '!.git' --files "${1}" } fi if (( ${+commands[bat]} )); then export FZF_CTRL_T_OPTS="\ --preview='command bat --color=always --line-range :100 --style=plain {}'\ --preview-window=border-left\ ${FZF_CTRL_T_OPTS}" fi if (( ${+FZF_DEFAULT_COMMAND} )) export FZF_CTRL_T_COMMAND=${FZF_DEFAULT_COMMAND} # fzf: Debian Specific # FZF_CTRL_R_OPTS="--no-sort --exact" # fzf: Alternate history (Ctrl-Win-R) # if [[ -f $ALT_HISTORY_FILE ]]; then fzf-alt-history-widget() { local selected num setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null selected=( $(cat ${ALT_HISTORY_FILE} | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort,ctrl-z:ignore $FZF_CTRL_R_OPTS --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) ) local ret=$? BUFFER=$selected zle reset-prompt return $ret } zle -N fzf-alt-history-widget bindkey -M vicmd '^[[114;13u' fzf-alt-history-widget bindkey -M viins '^[[114;13u' fzf-alt-history-widget fi ### ZVM — Zsh VI mode # # https://github.com/jeffreytse/zsh-vi-mode function zvm_config() { ZVM_LINE_INIT_MODE=$ZVM_MODE_INSERT } # bindkey for other modules function zvm_after_init { # fzf source /usr/share/doc/fzf/examples/key-bindings.zsh source /usr/share/doc/fzf/examples/completion.zsh # Automatic expansion of global aliases bindkey " " globalias bindkey "^ " magic-space # control-space to bypass completion bindkey -M isearch " " magic-space # normal space during searches } ### ### Zim Framework ### # Use degit instead of git as the default tool to install and update modules. zstyle ':zim:zmodule' use 'degit' ZIM_HOME=${ZDOTDIR:-${HOME}}/.zim if [[ ! -e ${ZIM_HOME}/zimfw.zsh ]]; then # Download zimfw script if missing. if (( ${+commands[curl]} )); then curl -fsSL --create-dirs -o ${ZIM_HOME}/zimfw.zsh https://github.com/zimfw/zimfw/releases/latest/download/zimfw.zsh else mkdir -p ${ZIM_HOME} && wget -nv -O ${ZIM_HOME}/zimfw.zsh https://github.com/zimfw/zimfw/releases/latest/download/zimfw.zsh fi fi if [[ ! ${ZIM_HOME}/init.zsh -nt ${ZDOTDIR:-${HOME}}/.zimrc ]]; then # Install missing modules, and update ${ZIM_HOME}/init.zsh if missing or outdated. source ${ZIM_HOME}/zimfw.zsh init -q fi source ${ZIM_HOME}/init.zsh ### ### Aliases & Small Functions ### # # https://www.thorsten-hans.com/5-types-of-zsh-aliases # # https://unix.stackexchange.com/questions/250314/whats-the-intended-use-case-for-complete-aliases-in-zsh # See also the Completion section below alias cp='cp --interactive' alias mv='mv --interactive' alias rm='rm -I' mkcd() { command mkdir -p "$@" && builtin cd "$@" } autoload -U zmv alias mmv='noglob zmv -W' # https://github.com/lsd-rs/lsd # ~/.config/lsd/config.yaml alias ls='lsd' alias l='lsd --total-size -l' alias ll='lsd --total-size -lA' alias lt='lsd -ltr' alias llt='lsd -ltrA' alias lx='lsd -l --blocks=permission,user,group,context,size,date,name' alias llx='lx -A' alias t='lsd --long --tree --depth=2 --no-symlink' # Dev # alias make='colormake-short' compdef colormake=make compdef colormake-short=make # Git # alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles --work-tree=$HOME' lg() { export LAZYGIT_NEW_DIR_FILE="${XDG_CONFIG_HOME}/lazygit/cd-on-quit_$$" lazygit "$@" if [ -f $LAZYGIT_NEW_DIR_FILE ]; then cd "$(cat $LAZYGIT_NEW_DIR_FILE)" rm -f $LAZYGIT_NEW_DIR_FILE > /dev/null fi } # Kitty # hg () { kitten hyperlinked_grep "$@" } compdef hg=rg # Neovim — nvim # # To choose an existing instance for nvr, use: # `export NVIM=$(nvr --serverlist | fzf --exit-0 --select-1) && echo $NVIM` # nnn - n³ # local nnn_plug=( 'b:-!bat --paging=always "$nnn"*' 'd:fzcd' 'z:fzopen' 'm:mimelist' 'o:-!mimeopen "$nnn"*' 'O:-!mimeopen --ask "$nnn"*' 'U:umounttree' ';:-!clear*' # TODO Dump trash ) n() { if [ ${NNNLVL:-0} -gt 0 ]; then echo 'nnn is already running' return fi local -x LESS=${LESS/F} local -x PATH=$HOME/.config/nnn/plugins:$PATH # See https://github.com/jarun/nnn/wiki/Usage#configuration local -x GUI=1 # plugins: nuke, xdgdefault local -x NNN_ARCHIVE='\.(7z|bz2|cbz|cbr|gz|tar|tbz|tgz|xz|zip|zst)$' local -x NNN_BMS="d:~/Downloads;q:~/QubesIncoming" local -x NNN_OPENER="${XDG_CONFIG_HOME}/nnn/plugins/nuke" local -x NNN_OPTS='ABeGo' local -x NNN_ORDER="t:$HOME/Downloads" local -x NNN_PLUG=${(j:;:)nnn_plug} local -x NNN_TMPFILE="${XDG_CONFIG_HOME}/nnn/cd-on-quit" if (( ${+commands[fuse-archive]} )); then local -x NNN_ARCHMNT='fuse-archive' fi if (( ${+commands[trash-put]} )); then local -x NNN_TRASH=1 elif (( ${+commands[gio]} )); then local -x NNN_TRASH=2 fi command nnn "$@" if [ -f $NNN_TMPFILE ]; then # Tmp file contains a `cd` directive that will # behave like a `pushd`, ignoring duplicates. setopt autopushd pushdignoredups . $NNN_TMPFILE rm -f $NNN_TMPFILE > /dev/null dirs -v fi } compdef n=nnn nnn-file-widget() { # Extract "shell word" at cursor position (respecting quotes) local _cursor=$CURSOR zle .select-a-shell-word -N # Helper nnn-pick() { IFS=$'\n' local items=( $(command nnn -p- $1 < "$TTY") ) if (( #items > 0 )); then items=( ${(q-)items} ) print -n "${(j: :)items} " fi } # Adapt behaviour to buffer content if (($CURSOR < _cursor)); then # Nothing at cursor, restore state before calling nnn CURSOR=${_cursor} REGION_ACTIVE=0 zle -U "$(nnn-pick)" else # Provide content under cursor to nnn zle .kill-region local result="$(nnn-pick ${CUTBUFFER## })" if (( #result == 0 )); then # Nothing selected, restore content zle .yank else # Replace content with what nnn returned zle -U " $result" fi fi # TODO single-op undo # TODO Handle vicmd properly zle .redisplay } zle -N nnn-file-widget bindkey -M vicmd '\en' nnn-file-widget bindkey -M viins '\en' nnn-file-widget # Whiptail helper # # see https://stackoverflow.com/questions/1970180/whiptail-how-to-redirect-output-to-environment-variable whiptail_checklist() { autoload zmathfunc; zmathfunc # min, max, sum eval $(resize) # COLUMNS, LINES local res title="$1" text="$2" (( width=max(30, $#text) )) shift 2 # Make tags compatible with whiptail local -a tags for tag; do tags+=($tag $tag 0) (( width=max(width, $#tag) )) done # Compute "best" width & height (( list_height=min(20, ${#*}, LINES * .5) )) (( height=min(list_height+8, LINES) )) (( width=min(width + 9, COLUMNS * .75) )) tag=$(whiptail --title $title \ --checklist $text $height $width $list_height $tags \ --separate-output --notags 3>&1 1>&2 2>&3 3>&-) res=$? # Use res=${(f)"$(whiptail_checklist...)"} to ignore spaces echo $tag return $res } whiptail_menu() { autoload zmathfunc; zmathfunc # min, max, sum eval $(resize) # COLUMNS, LINES local res title="$1" text="$2" (( width=max(30, $#text) )) shift 2 # Make tags compatible with whiptail local -a tags for tag; do tags+=($tag $tag) (( width=max(width, $#tag) )) done # Compute "best" width & height (( menu_height=min(20, ${#*}, LINES * .5) )) (( height=min(menu_height+8, LINES) )) (( width=min(width + 5, COLUMNS * .75) )) tag=$(whiptail --title $title \ --menu $text $height $width $menu_height $tags \ --notags 3>&1 1>&2 2>&3 3>&-) res=$? echo $tag return $res } ### ### Aliases -- Global & automatic expansion ### alias -g D='$(date +%Y-%m-%d_%H-%M-%S)' alias -g E='2> /dev/null' alias -g G='|& grep -i' alias -g H='| head' alias -g HL='--help |& less -r' alias -g L='| less' alias -g N='&> /dev/null' alias -g P='| peco' alias -g S='| sort' alias -g T='| tail' # Expand aliases inline - see http://blog.patshead.com/2012/11/automatically-expaning-zsh-global-aliases---simplified.html globalias() { if [[ $LBUFFER =~ ' [A-Z0-9]+$' ]]; then zle _expand_alias # If you want the content of the alias to be also expanded: # zle expand-word # (otherwise, do the job...) fi zle self-insert } zle -N globalias ### ### Colors ### if [[ -r /etc/grc.zsh ]]; then # ! Incompatible with P10K Transient source /etc/grc.zsh # I want the default behaviour for: unset -f journalctl unset -f ls fi ### ### Exports ### export BROWSER=firefox export EDITOR=vi export LESS=-iFRS export SYSTEMD_LESS='iFRSXMK' export VISUAL=bat # KeePassXC SSH Agent [[ -v SSH_AUTH_SOCK ]] || export SSH_AUTH_SOCK=$(echo /tmp/ssh-*/agent.*(U)) ### ### History ### setopt hist_ignore_all_dups setopt hist_ignore_space setopt hist_no_functions setopt hist_reduce_blanks setopt hist_save_no_dups setopt hist_verify setopt inc_append_history HISTFILE=${ZDOTDIR:-${HOME}}/.zhistory HISTSIZE=4000 SAVEHIST=3000 HISTORY_IGNORE="(?|??|???|cd [a-zA-Z]*|* --help|#*|builtin *|run-help *)" ### ### Key Binding ### # `bindkey | grep -v self-insert` to print existing bindings # `showkey -a` to print keys # Undo (useful to cancel completion -- borrowed from emacs keymap) bindkey -M vicmd "^_" undo bindkey -M viins "^_" undo ### ### Zsh Options ### ## Changing Directories setopt no_auto_cd ## Expansion and Globbing setopt equals ## Input/Output setopt no_clobber setopt correct setopt interactive_comments setopt no_print_exit_value ## Job control setopt auto_continue setopt no_bg_nice setopt check_jobs setopt no_hup # Remove path duplicates typeset -U path fpath ### ### Completion Late Definitions ### compdef locate=glocate # Hide "parameters" (i.e. environment variables) when looking for a "command" zstyle ':completion:*:-command-:*' tag-order '!parameters'