2

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
faho
  • 14,470
  • 2
  • 37
  • 47
mabvmex
  • 61
  • 8

3 Answers3

2
prompt_pwd ()
  psvar[1]="${(@j[/]M)${(@s[/]M)PWD##*/}#?}$PWD:t"

precmd_functions+=( prompt_pwd )
PS1='%1v%# '

This approach does not perform tilde replacement like the %~ prompt sequence does. It shortens the $PWD variable by splitting it into its components, keeping only the first character of each component by using #? with the (M) flag, then joins them together. This process is only applied to the part before the last / separator. The "tail" component is appended to the end.

With ~ replacement for the home directory, and extending #? to ##(.|)? so that the first character after the dot is shown for dot-directories:

prompt_pwd () {
  local p=${${PWD:/~/\~}/#~\//\~/}
  psvar[1]="${(@j[/]M)${(@s[/]M)p##*/}##(.|)?}$p:t"
}
rowboat
  • 401
  • 1
  • 8
1

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}}/\/\///}//\%/%%} '
Gairfowl
  • 2,226
  • 6
  • 9
  • 2
    Note that `printf %c` prints the first byte not first character (at least not for multibyte characters). You'd need `printf %.1s` for the first character. When using prompt_subst, you generally need to make sure the `%`s (and potentially other characters) in the substitutions are escaped. – Stephane Chazelas Sep 03 '22 at 08:07
  • @StephaneChazelas - thanks! I updated the format string in the answer. If my quick tests are accurate, `zsh` processes the command substitution prior to looking for any `%` escapes, so the format specifier may not need any special handling. – Gairfowl Sep 03 '22 at 18:56
  • 1
    @rowboat - thanks for the notes. I added some other versions to the answer. – Gairfowl Sep 05 '22 at 22:03
0
setopt extended_glob
print ${(j:/:)${(s: :):-${${(s:/:)${(A)pwd::=${PWD/#$HOME/\~}}}[1,-2]//(#m)^.?*/${MATCH[1]}} ${${pwd:t}:-/}}}
Kira
  • 1
  • 1