Preventing zsh from using aliases in CWD (prompt)

oh-my-zshzsh

I have the following personalized theme activated with oh-my-zsh (latest version of zsh and oh-my-zsh):

local return_code="%(?..%{$fg[red]%}%? %{$reset_color%})"

local user_host='%{$terminfo[bold]$fg[green]%}%n @ %m%{$reset_color%}'
local current_dir='%{$terminfo[bold]$fg[cyan]%} %~%{$reset_color%}'
local rvm_ruby=''
local git_branch='$(git_prompt_info)%{$reset_color%}'

PROMPT="${user_host} %D{[%a, %b %d %I:%M:%S]} ${current_dir} ${rvm_ruby} ${git_branch}
%B$%b "
RPS1="${return_code}"

ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[yellow]%}‹"
ZSH_THEME_GIT_PROMPT_SUFFIX="› %{$reset_color%}"

I have noticed that in the prompt, whenever I have an alias to a directory, it displays the name of the alias as my current directory as opposed to the actual path. While this is an interesting feature, I would like to disable that.

I am relatively new to oh-my-zsh, and I am not sure if this is an oh-my-zsh or zsh feature, but how can I disable it?

Best Answer

The prompt escape sequence %~ (included in $current_dir) expands to the current directory, taking abbreviations into account. The abbreviations are:

  • ~ for your home directory;
  • ~joe for the home directory of user joe;
  • ~foo for a named directory: the directory aliased to foo with hash -d foo=…;
  • ~[bar] for a dynamic named directory.

You can use %/ instead of %~. This never uses any directory abbreviation.

If you want to be fancier, you can execute your own code to determine how the current directory is displayed. One approach is to use a parameter substitution inside the prompt string. This requires the prompt_subst option to be set, which oh-my-zsh does (otherwise: setopt prompt_subst). The current directory is always available in the parameter PWD. Here's a simple version that only shortens your home directory to ~:

local current_dir='%{$terminfo[bold]$fg[cyan]%} ${${PWD/#%$HOME/~}/#$HOME\//~/}%{$reset_color%}'

${${PWD/#%$HOME/\~}/#$HOME\//\~/} means: if $PWD is exactly the same as $HOME, then set the result to ~, otherwise set the result to $PWD; then, if the current result begins with $HOME/, then replace this prefix by ~/, otherwise leave the result unchanged.

A clearer approach is to maintain a parameter containing a pretty-printed version of the current directory. Update this parameter in the chpwd hook function which is executed on every current directory change. Also initialize that parameter in your .zshrc.

There's only one chpwd function, so don't override oh-my-zsh's. Oh-my-zsh's chpwd calls the function in the array chpwd_functions, so add yours to the array.

function my_update_pretty_PWD {
  case $PWD in
    $HOME(/*)#) pretty_PWD=\~${PWD#$HOME};;
    *) pretty_PWD=$PWD;;
  esac
}
chpwd_functions+=(my_update_pretty_PWD)
my_update_pretty_PWD
local current_dir='%{$terminfo[bold]$fg[cyan]%} ${pretty_PWD}%{$reset_color%}'

If you want to abbreviate users' home directories but not named directories, you can clear the home directories in a subshell and use the % parameter expansion flag to perform the automatic abbreviations in the subshell.

function my_update_pretty_PWD {
  pretty_PWD=$(hash -rd; print -lr -- ${(%)PWD})
}

Or if you prefer the inline approach:

local current_dir='%{$terminfo[bold]$fg[cyan]%} $(hash -rd; print -lr -- ${(%)PWD})%{$reset_color%}'
Related Question