How to draw a line between commands in zsh shell

In order to extend your current PS1 prompt by a preceding horizontal line, you can use one of the following methods. The basic principle is the same but which one works best depends on your terminal emulator, locale setting and font settings.

Print a bunch of minus (-) or underscore (_) characters

setopt promptsubst
PS1=$'${(r:$COLUMNS::_:)}'$PS1

Explanation

  • The option promptsubst enables the substitution of parameters inside the prompt each time the prompt is drawn, in this case COLUMNS.
  • The parameter expansion flag r:$COLUMNS::_: pads the right side of the parameter with underscores (given between the last two : :) until a width of $COLUMNS is reached. As in this case no parameter is given, only the padding is printed.
  • The padding takes up the whole width of the terminal, the original PS1 is automatically wrapped to the next line. So there is no need to add an extra newline. This is also important as explicit newlines may in some cases lead to the prompt overwriting the last line(s) of the output. (In my case this happened when the prompt text before the explicit newline was exactly as long as the terminal was wide.)

This works on every terminal emulator (or console), every locale and every font. But it may not look that good: At least with minuses as there will be gaps between them (----), with undercores it depends on the font. The other methods use different ways to build on that.


Underline with zsh Prompt Escapes for visual effects

setopt promptsubst
PS1=$'%U${(r:$COLUMNS:: :)}%u'$PS1

Explanation:

  • Everything between %U and %u is underlined.
  • print spaces (which will be underlined) instead of underscores

This should work with most terminal emulators, locales and fonts as it just uses underlining. A possible drawback is, that the horizontal line will not be centered but very low on the output line, just above the next line of the prompt.


Line-drawing (aka Box-drawing) using alternate character set

setopt promptsubst
PS1=$'%{\e(0%}${(r:$COLUMNS::q:)}%{\e(B%}'$PS1

Explanation:

  • %{...%} tells zsh to expect only escape codes that do not actually move the cursor
  • \e(0 switches to the alternate character set
  • q maps to a horizontal line in the alternate character set
  • \e(B switches back to the regular character set

This also should work with most terminal emulators (but probably not console), locales and fonts. The thickness of the line seems to vary between fonts and even terminal emulators using the same font (on my Machine with the Terminus font urxvt prints a thin line, while roxterm prints a very thick line).


Box-drawing using Unicode characters

setopt promptsubst
PS1=$'${(r:$COLUMNS::\u2500:)}'$PS1

Explanation

  • use the Unicode character U+2500 ("Box Drawing Light Horizontal", ) for padding.

This obviously requires the terminal emulator to support Unicode characters, a font that has the required character and an UTF-8 locale. But it also provides a few styles of line to chose from, like thick () or double lines (). (see the Official Unicode Consortium code chart for more)


I think the line is coded into the prompt and realized via the underline prompt escape %U:

PS1="%U                         %u
%~ "

where %~ is your usual prompt (check with print $PS1):

enter image description here

More tricky is to make the line as wide as your terminal. The number of characters is stored in $COLUMNS, so we construct a string of an appropriate number of blanks, surrounded by %U/%u:

drawline=""
for i in {1..$COLUMNS}; drawline=" $drawline"
drawline="%U${drawline}%u"

As we want to update the length if the size is altered, we redefine the prompt every time before it is redrawn, which can be accomplished by putting the code in the precmd() function:

precmd() {
   drawline=""
   for i in {1..$COLUMNS}; drawline=" $drawline"
   drawline="%U${drawline}%u"
   PS1="${drawline}
%~ "
}

Et voilà:

enter image description here

Tags:

Zsh

Oh My Zsh