Search code examples
zshprompt

fish function prompt_pwd but in zsh?


I am currently using ZSH, but I have seen that the Fish Shell has a feature called prompt_pwd which is a function. It displays the prompt path similar as this:

// normally this would apper like this:
$ /my/computer/User/Local/Documents/Something

// prompt_pwd function on fish shell:
$ /m/c/U/L/Documents cd Something
$ /m/c/U/L/D/Something

According you navigates through your file system the full name of them changes to only the first letter of its name and the only full name will be displayed on the current directory...

  • Graphical example: enter image description here

Do you know how I can achieve this in ZSH?

Thanks!

This is the full code extracted from the fish_config wizard:

    # Defined in /opt/homebrew/Cellar/fish/3.5.1/share/fish/functions/prompt_pwd.fish @ line 1 function prompt_pwd --description 'short CWD for the prompt'
    set -l options h/help d/dir-length= D/full-length-dirs=
    argparse -n prompt_pwd $options -- $argv
    or return

  if set -q _flag_help
        __fish_print_help prompt_pwd
        return 0
    end

  set -q argv[1]
    or set argv $PWD

  set -ql _flag_d
    and set -l fish_prompt_pwd_dir_length $_flag_d

  set -q fish_prompt_pwd_dir_length
    or set -l fish_prompt_pwd_dir_length 1

  set -l fulldirs 0
    set -ql _flag_D
    and set fish_prompt_pwd_full_dirs $_flag_D

  set -q fish_prompt_pwd_full_dirs
    or set -l fish_prompt_pwd_full_dirs 1

  for path in $argv
        # Replace $HOME with "~"
    set -l realhome ~
        set -l tmp (string replace -r '^'"$realhome"'($|/)' '~$1' $path)

    if test "$fish_prompt_pwd_dir_length" -eq 0
            echo $tmp
        else
            # Shorten to at most $fish_prompt_pwd_dir_length characters per directory
      # with full-length-dirs components left at full length.
      set -l full
            if test $fish_prompt_pwd_full_dirs -gt 0
                set -l all (string split -m (math $fish_prompt_pwd_full_dirs - 1) -r / $tmp)
                set tmp $all[1]
                set full $all[2..]
            else if test $fish_prompt_pwd_full_dirs -eq 0
                # 0 means not even the last component is kept
        string replace -ar '(\.?[^/]{'"$fish_prompt_pwd_dir_length"'})[^/]*' '$1' $tmp
                continue
            end

      string join / (string replace -ar '(\.?[^/]{'"$fish_prompt_pwd_dir_length"'})[^/]*/' '$1/' $tmp) $full
        end
    end end

Solution

  • This should match your initial text example; add it to ~/.zshrc:

    setopt prompt_subst
    PROMPT='\$ /$(printf "%.1s/" ${(s./.)PWD:h})${PWD:t} '
    
    • (s./.) - splits a path at /.
    • printf "%.1s/" - prints the first character of each directory piece.
    • ${PWD:h} - the 'head' of the current directory, i.e. everything but the last element.
    • ${PWD:t} - the tail / last element in the directory path.

    Adding colors and other characters is a bit more involved. When building a complicated prompt, it's often easier to use a precmd; some of the many posts that discuss prompt colors and precmds are here, here, and here.


    Edited to add:
    As @rowboat noted, the code above will include an extra / in top-level directories like /tmp. This is due to printf always including at least one result even when there are zero parent directories. One fix is a substitution to convert double // to /:

    PROMPT='\$ ${${:-$(printf "/%.1s" ${(s./.)PWD:h})/${PWD:t}}/\/\///} '
    

    Similarly, another substitution can be added to escape % as %%, if you need to handle directory names that start with a percent sign:

    PROMPT='\$ ${${${:-$(printf "/%.1s" ${(s./.)PWD:h})/${PWD:t}}/\/\///}//\%/%%} '