Moving to zsh

Though I extensively use bash and ksh at work, I’ve decided to move to zsh :)
Added by Rémy CLOUARD almost 2 years ago

I won’t debate over the advantages of zsh over the other shells, this has always been said multiple times

What I wanted to show is my first steps with this shell.
To begin with, I wanted to customize my prompt and make it VCS aware :)

(You can see an screenshot here)

I’ve found several documentations about zsh prompts and vcs implementations

and I wanted to build my own prompt, and to understand some of what I call zshism, though the intro doc already gives a good overview.

My whole configuration is available on this repository, which turns out to be not only my awesome configs but also my public dotfiles :)

so let’s explain what I did (not that clean, but its usable):

setopt prompt_subst
autoload colors
colors

This is mandatory for the prompt to work. The first line allows variable to be interpreted and the other two allow the use of colours
# define some colour variables to avoid cluttering the command prompt
for color in red green yellow blue magenta cyan white
do
    eval C_$color="%{$fg[$color]%}" 
    eval CB_$color="%{$terminfo[bold]$fg[$color]%}" 
done
C_reset="%{$terminfo[sgr0]%}" 
# define a reset var
fSep="$CB_blue:$C_reset" 

The syntax here is a bit different from bash, but I like it better, because you can have access to the escape sequences through an associative array which is just way better than putting directly your escape sequences or using tput.
# define brackets
oBr="$CB_cyan—$C_reset$C_magenta($C_reset" 
cBr="$C_magenta)$CB_cyan—$C_reset" 


This is just decorative, but I like it
autoload -Uz vcs_info
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:*' unstagedstr   "$C_red"    # red color indicates unstaged changes
zstyle ':vcs_info:*' stagedstr     "$C_green"  # green color indicates staged changes
zstyle ':vcs_info:*' formats       \
        '%u%c' '%s' '%b' '%S'

This is the interesting part: zsh has built-in support for many vcses, see the online doc for more information

The first command just loads the module

The next three allows to gather information about your tree, beware, it can be slow, disable it if you work on large repositories (the doc explains how to achieve this)

The last part takes a list of formats and returns it in ${vcs_info_msg_N_} (see below)

precmd () {

    # declare local variables
    typeset fsScm="" 

    # revrieve info from vcs
    vcs_info

    # get status
    cSt="${vcs_info_msg_0_}" 


This part starts the precmd function and you see an example of the use of the variables you can get. This part just returns the status of your tree (dirty, staged changes). You’ll see below why I need this in my prompt
    # set a prompt character according to the used scm
    fsScm="${vcs_info_msg_1_}" 
    case $fsScm in

Here I get the vcs in use in the directory where I am.
        "git")
            vBr="${vcs_info_msg_2_}" 
            pCh="$cSt±$C_reset" 
            ;;
        "hg")
            vBr="${vcs_info_msg_2_}" 
            pCh="$cSt∂$C_reset" 
            ;;
        "svn")
            vBr="${vcs_info_msg_2_}" 
            pCh="$cSt∫$C_reset" 
            ;;

as you can see, I set a different char that reminds the vcs logo.
This character is colorized differently according to its state (works great for git, not for mercurial :/)
        *)
            vBr="" 
            pCh="$C_reset%%" 
            ;;
    esac

and this is to set up the standard % if I’m not in any of the previous vcs.
    # set different colours according to user/host
    case $USER in
        "root")
            aUsr="$C_red%n" 
            ;;
        "shikamaru")
            aUsr="$C_green%n" 
            ;;
        *)
            aUser="$C_yellow%n" 
            ;;
    esac
    case $HOSTNAME in
        "nibi"*|"sanbi"*)
            aHst="$CB_yellow@$C_red%m" 
            ;;
        *)
            aHst="$CB_yellow@$C_green%m" 
            ;;
    esac

This part is a bit less interesting, I just like to have different colours for my different user/machines :)
    vcsPWD=${vcs_info_msg_3_:-.}
}

And finally I get the subdirectory part if I’m in a repo. If for example I’m in /home/shikamaru/repo/awesome/config/ it returns awesome/config, see below why.

Let’s set up the prompt !

# PROMPT
# (retval:Branch:PromptChar)
PROMPT='$oBr%(?..$C_red%?$fSep)$C_cyan$vBr$fSep$pCh$cBr '

The %(?..$C_red%?$fSep) returns the exit code of the last command if it’s not 0 ($?). I find it useful as I often used to use echo $? to know it :)
$vBr is the branch on which I’m working on, we already had that in Mandriva for bash, but it used some bash-completion functions which can make your shell painfully slow when for instance you run urpmi a-local-package.rpm because it tries first to complete it to the available rpms in the repositories :/

And finally, we display our pretty prompt character. You shouldn’t have problems displaying it if you’re using an utf-8 system, which is the case for most linuxes nowadays, and I chose symbols from the mathematical symbols, which are normally available for you.

pwdMax=$(($COLUMNS-80))
RPROMPT='$oBr$aUsr$aHst$fSep%$pwdMax<…<${PWD/$vcsPWD/$C_yellow$vcsPWD}%<<$cBr'

Now, we display the right part of the prompt. It’s nice to use it because it disappears when you type a longer command than the available place you have, using the left prompt exclusively would make it too long I guess.

We first display the user and hostname and then we’re going on more crazy stuff :)
%$pwdMax<…<${PWD/$vcsPWD/$C_yellow$vcsPWD}%<<

This command just displays the path, but truncates it to leave at least 80 columns for the left prompt+commands, which was calculated in the variable $pwdMax. On top of that we colorize the vcs part of that path.

More on this:
The construction number<replace<string<< truncates the string to number characters and adds a replace string for the truncated part (here an ellipsis). This is just insane, AFAIK that’s not something you can find in bash, but it’s very useful in that case :)
${PWD/$vcsPWD/$C_yellow$vcsPWD} is another construction that allows substitutions. basically it substitutes the 2nd part of the variable with the third part.

The actual solution is not optimal though, I should also add another substitution to replace the $HOME part of the current directory with ~/, this won’t require much work though :)

I also need to fix a little issue that occurs if my terminal is smaller than 80 columns


Comments

Added by Rémy CLOUARD 11 months ago

Comments are now disabled for anonymous due to spam