Skip to content

Instantly share code, notes, and snippets.

@romkatv
Last active June 26, 2024 17:44
Show Gist options
  • Save romkatv/2a107ef9314f0d5f76563725b42f7cab to your computer and use it in GitHub Desktop.
Save romkatv/2a107ef9314f0d5f76563725b42f7cab to your computer and use it in GitHub Desktop.
Two-line ZSH prompt
# Example of two-line ZSH prompt with four components.
#
# top-left top-right
# bottom-left bottom-right
#
# Components can be customized by editing set-prompt function.
#
# Installation:
#
# (cd && curl -fsSLO https://gist.githubusercontent.com/romkatv/2a107ef9314f0d5f76563725b42f7cab/raw/two-line-prompt.zsh)
# echo 'source ~/two-line-prompt.zsh' >>~/.zshrc
#
# Accompanying article:
# https://www.reddit.com/r/zsh/comments/cgbm24/multiline_prompt_the_missing_ingredient/
#
# This is only an example. If you are looking for a good ZSH prompt,
# try https://github.com/romkatv/powerlevel10k/.
# Usage: prompt-length TEXT [COLUMNS]
#
# If you run `print -P TEXT`, how many characters will be printed
# on the last line?
#
# Or, equivalently, if you set PROMPT=TEXT with prompt_subst
# option unset, on which column will the cursor be?
#
# The second argument specifies terminal width. Defaults to the
# real terminal width.
#
# The result is stored in REPLY.
#
# Assumes that `%{%}` and `%G` don't lie.
#
# Examples:
#
# prompt-length '' => 0
# prompt-length 'abc' => 3
# prompt-length $'abc\nxy' => 2
# prompt-length '❎' => 2
# prompt-length $'\t' => 8
# prompt-length $'\u274E' => 2
# prompt-length '%F{red}abc' => 3
# prompt-length $'%{a\b%Gb%}' => 1
# prompt-length '%D' => 8
# prompt-length '%1(l..ab)' => 2
# prompt-length '%(!.a.)' => 1 if root, 0 if not
function prompt-length() {
emulate -L zsh
local -i COLUMNS=${2:-COLUMNS}
local -i x y=${#1} m
if (( y )); then
while (( ${${(%):-$1%$y(l.1.0)}[-1]} )); do
x=y
(( y *= 2 ))
done
while (( y > x + 1 )); do
(( m = x + (y - x) / 2 ))
(( ${${(%):-$1%$m(l.x.y)}[-1]} = m ))
done
fi
typeset -g REPLY=$x
}
# Usage: fill-line LEFT RIGHT
#
# Sets REPLY to LEFT<spaces>RIGHT with enough spaces in
# the middle to fill a terminal line.
function fill-line() {
emulate -L zsh
prompt-length $1
local -i left_len=REPLY
prompt-length $2 9999
local -i right_len=REPLY
local -i pad_len=$((COLUMNS - left_len - right_len - ${ZLE_RPROMPT_INDENT:-1}))
if (( pad_len < 1 )); then
# Not enough space for the right part. Drop it.
typeset -g REPLY=$1
else
local pad=${(pl.$pad_len.. .)} # pad_len spaces
typeset -g REPLY=${1}${pad}${2}
fi
}
# Sets PROMPT and RPROMPT.
#
# Requires: prompt_percent and no_prompt_subst.
function set-prompt() {
emulate -L zsh
local git_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
git_branch=${git_branch//\%/%%} # escape '%'
# ~/foo/bar master
# % █ 10:51
#
# Top left: Blue current directory.
# Top right: Green Git branch.
# Bottom left: '#' if root, '%' if not; green on success, red on error.
# Bottom right: Yellow current time.
local top_left='%F{blue}%~%f'
local top_right="%F{green}${git_branch}%f"
local bottom_left='%B%F{%(?.green.red)}%#%f%b '
local bottom_right='%F{yellow}%T%f'
local REPLY
fill-line "$top_left" "$top_right"
PROMPT=$REPLY$'\n'$bottom_left
RPROMPT=$bottom_right
}
setopt no_prompt_{bang,subst} prompt_{cr,percent,sp}
autoload -Uz add-zsh-hook
add-zsh-hook precmd set-prompt
@romkatv
Copy link
Author

romkatv commented Feb 1, 2022

@Moarram Pass 1000000 as the second (optional) argument.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment