diff --git a/common/.config/zsh/.zshrc b/common/.config/zsh/.zshrc index ca0a88a0..9ca61fec 100644 --- a/common/.config/zsh/.zshrc +++ b/common/.config/zsh/.zshrc @@ -1,3 +1,9 @@ +command -v direnv &>/dev/null && emulate zsh -c "$(direnv export zsh)" + +if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then + source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" +fi + for f in \ "plugins" \ "functions" \ @@ -7,7 +13,10 @@ for f in \ "bindings" \ "completions" \ "widgets" \ - "host" + "host" \ + "prompt" do source "${ZDOTDIR}/config/${f}.zsh" done + +export GPG_TTY=$TTY diff --git a/common/.config/zsh/config/plugins.zsh b/common/.config/zsh/config/plugins.zsh index 748082a3..81a0675f 100644 --- a/common/.config/zsh/config/plugins.zsh +++ b/common/.config/zsh/config/plugins.zsh @@ -23,6 +23,9 @@ function zsnippet() { zinit snippet "${@}" } +# NOTE: prompt +zload romkatv/powerlevel10k + # NOTE: virtualenv wrapper turbo zsnippet https://raw.githubusercontent.com/python-virtualenvwrapper/virtualenvwrapper/refs/heads/main/virtualenvwrapper.sh diff --git a/common/.config/zsh/config/prompt.zsh b/common/.config/zsh/config/prompt.zsh new file mode 100644 index 00000000..2c603686 --- /dev/null +++ b/common/.config/zsh/config/prompt.zsh @@ -0,0 +1,210 @@ +local -a p10k_config_opts +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +setopt no_aliases no_sh_glob brace_expand + +() { + emulate -L zsh -o extended_glob + + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + context + dir + vcs + + newline + prompt_char + ) + + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + status + command_execution_time + background_jobs + direnv + virtualenv + taskwarrior + ) + + typeset -g POWERLEVEL9K_MODE=nerdfont-v3 + typeset -g POWERLEVEL9K_ICON_PADDING=none # moderate | none + + typeset -g POWERLEVEL9K_BACKGROUND= + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' + typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= + + + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true + + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true + + # NOTE: prompt_char + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=2 + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=1 + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + + # NOTE: dir + typeset -g POWERLEVEL9K_DIR_FOREGROUND=15 + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + typeset -g POWERLEVEL9K_DIR_CONTENT_EXPANSION='%B%F{magenta}[%F{white}%3~%F{magenta}]' + + # NOTE: vcs + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + local meta='%f' + local clean='%F{green}' + local modified='%B%F{yellow}' + local untracked='%B%F{blue}' + local conflicted='%F{red}' + else + local meta='%f' + local clean='%f' + local modified='%f' + local untracked='%f' + local conflicted='%f' + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + (( $#branch > 32 )) && branch[13,-13]="…" + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG && -z $VCS_STATUS_LOCAL_BRANCH ]]; then + local tag=${(V)VCS_STATUS_TAG} + (( $#tag > 32 )) && tag[13,-13]="…" + res+="${meta}#${clean}${tag//\%/%%}" + fi + + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + fi + + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}?${VCS_STATUS_NUM_UNTRACKED}" + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION="" + + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=2 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=2 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=3 + + # NOTE: status + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + typeset -g POWERLEVEL9K_STATUS_OK=false + + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=2 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION="" + + typeset -g POWERLEVEL9K_STATUS_ERROR=true + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=1 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION="" + + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=1 + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION="" + + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=1 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION="" + + # NOTE: command_execution_time + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=3 + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION="" + + # NOTE: background_jobs: presence of background jobs + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS=true + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=1 + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION="" + + # NOTE: direnv + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=3 + typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION="▼" + + # NOTE: taskwarrior + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=6 + typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='' + + + # NOTE: context + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%F{red}%n%F{cyan}@%F{blue}%m' + typeset -g POWERLEVEL9K_CONTEXT_REMOTE_TEMPLATE='%B%F{blue}%n%F{cyan}@%F{red}%m' + typeset -g POWERLEVEL9K_CONTEXT_REMOTE_SUDO_TEMPLATE='%B%F{red}%n%F{cyan}@%F{red}%m' + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%B%F{blue}%n%F{cyan}@%F{blue}%m' + typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION= + + # NOTE: virtualenv + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=3 + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='' + + # NOTE: other options + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose +} + +typeset -g POWERLEVEL9K_CONFIG_FILE="realpath $(readlink "${ZDOTDIR}/config/prompt.zsh" || echo "${ZDOTDIR}/config/prompt.zsh")" + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +unset p10k_config_opts diff --git a/common/.config/zsh/config/widgets.zsh b/common/.config/zsh/config/widgets.zsh index 8a2eea81..a5da5dda 100644 --- a/common/.config/zsh/config/widgets.zsh +++ b/common/.config/zsh/config/widgets.zsh @@ -2,9 +2,16 @@ autoload -Uz add-zsh-hook autoload -Uz colors && colors autoload -Uz edit-command-line && zle -N edit-command-line -function venv_prompt() { - [[ -n ${VIRTUAL_ENV} ]] || return - echo -e "%F{yellow}%f ${VIRTUAL_ENV:t:gs/%/%%}" +# function venv_prompt() { +# [[ -n ${VIRTUAL_ENV} ]] || return +# echo -e "%F{yellow}%f ${VIRTUAL_ENV:t:gs/%/%%}" +# } + +function reset_prompt() { + for f in chpwd "${chpwd_functions[@]}" precmd "${(@)precmd_functions:#-z4h-direnv-hook}"; do + [[ "${+functions[$f]}" == 0 ]] || "$f" &>/dev/null || true + done + zle reset-prompt } # Cursor Shape @@ -31,7 +38,7 @@ function reset_beam() { } add-zsh-hook preexec reset_beam -fancy-ctrl-z () { +function fancy-ctrl-z () { if [[ $#BUFFER -eq 0 ]]; then BUFFER="fg" zle accept-line -w @@ -42,7 +49,7 @@ fancy-ctrl-z () { } zle -N fancy-ctrl-z -sudo-command-line() { +function sudo-command-line() { [[ -z $BUFFER ]] && zle up-history if [[ $BUFFER == sudo\ * ]]; then LBUFFER="${LBUFFER#sudo }" @@ -73,7 +80,7 @@ function lf-wrap() { fi fi - zle reset-prompt + reset_prompt write_title_wd reset_beam } @@ -82,7 +89,7 @@ zle -N lf-wrap function lazygit-wrap() { [ ! -d "$(pwd)/.git" ] && [[ $(read -erk "?Not in a git repository. Create a new git repository? (y/n): ") =~ ^[Yy]$ ]] && git init [ -d "$(pwd)/.git" ] && lazygit -p "$(pwd)" - zle reset-prompt + reset_prompt } zle -N lazygit-wrap @@ -120,4 +127,4 @@ function workon_cwd() { } add-zsh-hook chpwd workon_cwd -eval "$(direnv hook zsh)" +command -v direnv &>/dev/null && emulate zsh -c "$(direnv hook zsh)"