diff --git a/pure.zsh b/pure.zsh index 2e29028..5c60edf 100644 --- a/pure.zsh +++ b/pure.zsh @@ -121,6 +121,23 @@ prompt_pure_set_colors() { done } +prompt_pure_render_dimmed_path() { + setopt localoptions noshwordsplit + + # This runs from PROMPT_SUBST so directory changes followed by reset-prompt redraw correctly without precmd. + local current_path=${(%):-%~} + current_path=${current_path//\%/%%} + + local separator=$'%{\e[2m%}/%{\e[22m%}' + # Keep the leading / on absolute paths at full brightness. + local prefix= + if [[ $current_path == /* ]]; then + prefix=/ + current_path=${current_path:1} + fi + print -n -r -- "%F{${prompt_pure_colors[path]}}${prefix}${current_path//\//$separator}%f" +} + prompt_pure_preprompt_render() { setopt localoptions noshwordsplit @@ -925,7 +942,7 @@ prompt_pure_setup() { add-zle-hook-widget zle-keymap-select prompt_pure_update_vim_prompt_widget fi - # Initialize globals referenced by PROMPT via prompt subst. + # Initialize git globals referenced by PROMPT via prompt subst. typeset -gA prompt_pure_vcs_info typeset -g prompt_pure_git_branch_color=$prompt_pure_colors[git:branch] @@ -954,7 +971,12 @@ prompt_pure_setup() { # Preprompt line: each %(NV..) section only renders when its psvar is non-empty. PROMPT='%(12V.%F{$prompt_pure_colors[suspended_jobs]}%12v%f .)' PROMPT+='%(13V.%F{$prompt_pure_colors['"${prompt_pure_state[user_color]:-user}"']}%n%f%F{$prompt_pure_colors[host]}@%m%f .)' - PROMPT+='%F{${prompt_pure_colors[path]}}%~%f' + if zstyle -t ':prompt:pure:path:separator' dim; then + PROMPT+='$(prompt_pure_render_dimmed_path)' + else + # Keep the default hot path on native prompt expansion. Command substitution is only needed for dimmed separators. + PROMPT+='%F{$prompt_pure_colors[path]}%~%f' + fi PROMPT+='%(14V. %F{${prompt_pure_git_branch_color}}%14v%(15V.%F{$prompt_pure_colors[git:dirty]}%15v.)%f.)' PROMPT+='%(16V. %F{$prompt_pure_colors[git:action]}%16v%f.)' PROMPT+='%(17V. %F{$prompt_pure_colors[git:arrow]}%17v%f.)' diff --git a/readme.md b/readme.md index 9827303..b7b5612 100644 --- a/readme.md +++ b/readme.md @@ -121,6 +121,10 @@ Git integration is enabled by default, you can disable it with: `zstyle :prompt:pure:git show no` +Path separator dimming makes `/` characters in the path visually dimmer to help distinguish path components. It is not enabled by default. You can enable it with: + +`zstyle :prompt:pure:path:separator dim yes` + Automatic terminal title management can be disabled if you want to set your own tab or window titles: `zstyle :prompt:pure:title show no` diff --git a/tests/path-separator.zsh b/tests/path-separator.zsh new file mode 100644 index 0000000..c2af604 --- /dev/null +++ b/tests/path-separator.zsh @@ -0,0 +1,113 @@ +#!/usr/bin/env zsh + +source "${0:A:h}/test-helper.zsh" + +main() { + zmodload -F zsh/files b:zf_rm + + local base_directory=$PWD/.ai-temporary/pure-path-test-$$ + trap "zf_rm -rf -- ${(q)base_directory}" EXIT + + typeset -gA prompt_pure_colors=( + path blue + ) + + # Disabled by default: path should use plain %~ rendering. + local prompt_layout + prompt_layout=$(command zsh -fc 'source ./pure.zsh >/dev/null 2>&1; print -r -- $PROMPT') + if [[ $prompt_layout != *'%F{$prompt_pure_colors[path]}%~%f'* || $prompt_layout == *'$(prompt_pure_render_dimmed_path)'* ]]; then + print -u2 -- "Assertion failed: default PROMPT should render path without command substitution" + print -u2 -- "Actual: $prompt_layout" + return 1 + fi + + # Enabled via zstyle: separators should use ANSI dim attribute. + zstyle ':prompt:pure:path:separator' dim yes + local rendered_path + rendered_path=$(prompt_pure_render_dimmed_path) + local dim=$'\e[2m' + local nodim=$'\e[22m' + if [[ $rendered_path != *"%{${dim}%}/%{${nodim}%}"* ]]; then + print -u2 -- "Assertion failed: separators should use ANSI dim when enabled" + print -u2 -- "Actual: $rendered_path" + return 1 + fi + if [[ $rendered_path != "%F{blue}"* ]]; then + print -u2 -- "Assertion failed: path should start with path color" + print -u2 -- "Actual: $rendered_path" + return 1 + fi + + # Path without slashes (home directory itself). + local saved_pwd=$PWD + builtin cd -q ~ + rendered_path=$(prompt_pure_render_dimmed_path) + assert_equal "%F{blue}~%f" "$rendered_path" "home directory with no slashes should produce no dim separators" + builtin cd -q "$saved_pwd" + + # Absolute path outside home: leading / should not be dimmed. + builtin cd -q / + rendered_path=$(prompt_pure_render_dimmed_path) + if [[ $rendered_path != "%F{blue}/"* ]]; then + print -u2 -- "Assertion failed: leading slash on absolute path should not be dimmed" + print -u2 -- "Actual: $rendered_path" + builtin cd -q "$saved_pwd" + return 1 + fi + builtin cd -q "$saved_pwd" + + # Paths with percent characters are escaped. + mkdir -p -- "$base_directory/a%b" + builtin cd -q "$base_directory/a%b" + rendered_path=$(prompt_pure_render_dimmed_path) + if [[ $rendered_path != *"a%%b"* ]]; then + print -u2 -- "Assertion failed: percent in path should be escaped as %%" + print -u2 -- "Actual: $rendered_path" + builtin cd -q "$saved_pwd" + return 1 + fi + builtin cd -q "$saved_pwd" + + # Prompt redraws without precmd should still show the current directory. + mkdir -p -- "$base_directory/first/path" "$base_directory/second/path" + local first_prompt second_prompt + { + read -r first_prompt + read -r second_prompt + } < <( + command zsh -fc ' + typeset -g prompt_newline=" " + zstyle ":prompt:pure:path:separator" dim yes + source ./pure.zsh >/dev/null 2>&1 + typeset -gA prompt_pure_colors=( + path blue + prompt:success magenta + prompt:error red + ) + builtin cd -q "$1" + local first_prompt=${(S%%)PROMPT} + builtin cd -q "$2" + local second_prompt=${(S%%)PROMPT} + print -r -- "$first_prompt" + print -r -- "$second_prompt" + ' zsh "$base_directory/first/path" "$base_directory/second/path" + ) + if [[ $first_prompt != *"first"* || $second_prompt != *"second"* || $second_prompt == *"first"* ]]; then + print -u2 -- "Assertion failed: dimmed path should update on prompt redraw without precmd" + print -u2 -- "First prompt: $first_prompt" + print -u2 -- "Second prompt: $second_prompt" + return 1 + fi + + # PROMPT template renders the path dynamically. + prompt_layout=$(command zsh -fc 'zstyle ":prompt:pure:path:separator" dim yes; source ./pure.zsh >/dev/null 2>&1; print -r -- $PROMPT') + if [[ $prompt_layout != *'$(prompt_pure_render_dimmed_path)'* ]]; then + print -u2 -- "Assertion failed: PROMPT should render path dynamically" + print -u2 -- "Actual: $prompt_layout" + return 1 + fi + + print -- "path-separator tests passed" +} + +main "$@"