8

In zsh my current (left) prompt is the following

PROMPT="%F{106}%22<…<%3~%f%(?..%{$fg[red]%} %?%{$reset_color%})%(1j.%{$fg[cyan]%} %j%{$reset_color%}.)%# "

The %22<…< specifies that the prompt will become truncated when it is longer than 22 characters. This is because I don't want the prompt to take up too much horizontal space.

However, when I have a terminal that is very wide, I could have spent more than 22 characters on the prompt and I would still have had plenty of horizontal space to spare.

So, I'd like to format the prompt so that it has a maximum width that is a percentage of the full terminal width (e.g. 20%). Is there a way to do that?

Ideally, the prompt width should be recalculated if the terminal changes width.

(In case it matters: the shell will normally be inside tmux on MacOS)

3 Answers 3

8

Zsh's prompt expansion behaviour is defined in man zshmisc. Regarding setting custom values for prompt length it says:

   %<string<
   %>string>
   %[xstring]
      Specifies  truncation  behaviour for the remainder of the prompt
      string.   The  third,  deprecated,   form   is   equivalent   to
      `%xstringx',  i.e. x may be `<' or `>'.  The string will be dis‐
      played in place of the truncated portion  of  any  string;  note
      this does not undergo prompt expansion.

      The numeric argument, which in the third form may appear immedi‐
      ately after the `[', specifies the maximum permitted  length  of
      the various strings that can be displayed in the prompt.  In the
      first two forms, this numeric argument may be negative, in which
      case  the  truncation  length  is  determined by subtracting the
      absolute value of the numeric argument from the number of  char‐
      acter  positions  remaining on the current prompt line.  If this
      results in a zero or negative length, a length of 1 is used.  In
      other  words, a negative argument arranges that after truncation
      at least n characters remain before the right margin (left  mar‐
      gin for RPROMPT).

      The  forms  with `<' truncate at the left of the string, and the
      forms with `>' truncate at the right of the string.   For  exam‐
      ple,  if  the  current  directory  is  `/home/pike',  the prompt
      `%8<..<%/' will expand to `..e/pike'.  In this string, the  ter‐
      minating  character (`<', `>' or `]'), or in fact any character,
      may be quoted by a preceding `\'; note when using print -P, how‐
      ever, that this must be doubled as the string is also subject to
      standard  print  processing,  in  addition  to  any  backslashes
      removed  by a double quoted string:  the worst case is therefore
      `print -P "%<\\\\<<..."'.

      If the string is longer than the specified truncation length, it
      will appear in full, completely replacing the truncated string.

      The part of the prompt string to be truncated runs to the end of
      the string, or to the end of the next  enclosing  group  of  the
      `%('  construct,  or  to  the next truncation encountered at the
      same grouping level (i.e. truncations inside a  `%('  are  sepa‐
      rate), which ever comes first.  In particular, a truncation with
      argument zero (e.g., `%<<') marks the end of the  range  of  the
      string  to  be truncated while turning off truncation from there
      on. For example, the prompt  `%10<...<%~%<<%#  '  will  print  a
      truncated representation of the current directory, followed by a
      `%' or `#', followed by a space.  Without the `%<<',  those  two
      characters  would  be  included  in  the string to be truncated.
      Note that `%-0<<' is not equivalent to `%<<' but specifies  that
      the prompt is truncated at the right margin.

      Truncation  applies  only  within  each  individual  line of the
      prompt, as delimited by embedded  newlines  (if  any).   If  the
      total  length  of  any  line  of  the prompt after truncation is
      greater than the terminal width, or if the part to be  truncated
      contains embedded newlines, truncation behavior is undefined and
      may  change  in  a   future   version   of   the   shell.    Use
      `%-n(l.true-text.false-text)' to remove parts of the prompt when
      the available space is less than n.

From that you can infer

  • PROMPT="%/ " will give you a working directory
  • PROMPT='%10<..<%/ ' will give you a working directory - if the working directory string is longer than 10 chars it will be truncated on the left edge, and where the truncation occurs, the .. pattern will be displayed (to indicate trimmed value)

The terminal width can be obtained with $COLUMNS, so if you want a prompt constrained at 25% of the terminal width replace the 10 with a variable:

width=$(($COLUMNS / 4))

i.e.

PROMPT="%${width}<..<%/ %% "
9
  • You probably want $((int($COLUMNS/4))) to ensure that an integer is used and possibly also to mention the PROMPT_SUBST option.
    – thrig
    Commented Jun 7, 2017 at 23:26
  • @thrig do you mean to cast the value to an int? (zsh tells me current value is scalar. However the expression $((int($COLUMNS/4))) currently throws zsh: unknown function: int Commented Jun 7, 2017 at 23:30
  • 1
    ... and add zmodload zsh/mathfunc to get int() support ...
    – thrig
    Commented Jun 7, 2017 at 23:45
  • @thrig ah thats interesting thanks, also interesting int() looks more like a C syntax than a shell syntax. Commented Jun 7, 2017 at 23:48
  • 1
    Thanks, but this answer does not recalculate when the screen size changes Commented Jun 9, 2017 at 21:22
5

This is what I eventually ended up writing.

# this variable can be changed later to change the fraction of the line 
export PROMPT_PERCENT_OF_LINE=20

# make a function, so that it can be evaluated repeatedly
function myPromptWidth() { 
  echo $(( ${COLUMNS:-80} * PROMPT_PERCENT_OF_LINE / 100 )) 
}

# for some reason you can't put a function right in PROMPT, so make an
# intermediary variable
width_part='$(myPromptWidth)'

# use ${} to evaluate the variable containing function
PROMPT="%F{106}%${width_part}<…<%3~%f%(?..%{$fg[red]%} %?%{$reset_color%})%(1j.%{$fg[cyan]%} %j%{$reset_color%}.)%# "

This will recalculate the width immediately on terminal resize.

3
  • 1
    glad you got it working :) So others can learn from this, can you please provide an explanation of how your code works? And how it dynamically resizes the prompt width? Commented Jun 9, 2017 at 22:06
  • @the_velour_fog sure, I've updated my answer Commented Jun 10, 2017 at 8:08
  • 1
    I think the reason you can't put the function right in the PROMPT is because it needs to be enclosed in single quotes to avoid being evaluated once when sourced rather than every time it is displayed in the terminal. If you used single quotes when setting your PROMPT it would work, but then your colour variables would not. The way you did it is perfect, just wanted to explain why for anyone interested.
    – Matthew
    Commented Feb 16, 2018 at 17:22
0
# Set prompt substitution on so that ${} are evaluated. 
set -o PROMPT_SUBST

# Add a horizontal line to the prompt.
PS1=$'${(r:$COLUMNS::—:)}'

# Add UserName@HostName and pwd to the prompt.
PS1=$PS1'%n@%m %~ » '

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .