回答:
詳細な回答と例はこちらをご覧ください。考えは、precmd
コールバックを使用してPS1の前に行を記述し、を使用し$COLUMNS
、画面の右側のテキストの位置を計算するために少しの数学を使用することです。エスケープシーケンスの知識は、カーソルの配置と色付けにも役立ちます。
別の解決策は、Oh My ZSHのテーマを使用することです。
私もこれを探していました。私にとっては、precmd()
描画された線がサイズ変更や^L
画面のクリアに使用されたときに再描画されないという事実は、私を悩ませていました。私が今していることは、ANSIエスケープシーケンスを使用して、カーソルを少し動かします。それらを発行するよりエレガントな方法があると思うが、これは私のために働いています:
_newline=$'\n'
_lineup=$'\e[1A'
_linedown=$'\e[1B'
PROMPT=...whatever...${_newline}...whatever...
RPROMPT=%{${_lineup}%}...whatever...%{${_linedown}%}
zshマニュアルでは、%{...%}はカーソルを移動しないリテラルエスケープシーケンス用であると記載されていることに注意してください。それでも、コンテンツの長さを無視できるため、これらを使用しています(ただし、それらを使用してカーソルを移動するエスケープを発行する方法を理解できませんでした)
ここに、このことを今どのように構成したかを示します。このアプローチでは、エスケープシーケンスの操作は必要ありませんが、プライマリプロンプトに2つの異なる変数があります。PS1
色付きとNPS1
なしです。
# Here NPS1 stands for "naked PS1" and isn't a built-in shell variable. I've
# defined it myself for PS1-PS2 alignment to operate properly.
PS1='%S%F{red}[%l]%f%s %F{green}%n@%m%f %B%#%b '
NPS1='[%l] %n@%m # '
RPS1='%B%F{green}(%~)%f%b'
# Hook function which gets executed right before shell prints prompt.
function precmd() {
local expandedPrompt="$(print -P "$NPS1")"
local promptLength="${#expandedPrompt}"
PS2="> "
PS2="$(printf "%${promptLength}s" "$PS2")"
}
print -P
プロンプトの展開、${#variable}
変数に格納されている文字列の長さの取得、およびスペースprintf "%Nd"
を使用した左側の埋め込みの使用に注意してくださいN
。print
とprintf
は両方とも組み込みコマンドであるため、パフォーマンスに影響はありません。
このレイアウトでプロンプトを定義しましょう:
top_left top_right
bottom_left bottom_right
これを行うには、指定された文字列が印刷時に何文字を取るかを示す関数が必要です。
# 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.
#
# 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 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
local xy
while (( y > x + 1 )); do
m=$(( x + (y - x) / 2 ))
typeset ${${(%):-$1%$m(l.x.y)}[-1]}=$m
done
fi
echo $x
}
2つの引数を取り、画面の反対側にこれらの引数を使用して完全な罰金を印刷する別の関数が必要になります。
# Usage: fill-line LEFT RIGHT
#
# Prints LEFT<spaces>RIGHT with enough spaces in the middle
# to fill a terminal line.
function fill-line() {
emulate -L zsh
local left_len=$(prompt-length $1)
local right_len=$(prompt-length $2 9999)
local 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.
echo -E - ${1}
else
local pad=${(pl.$pad_len.. .)} # pad_len spaces
echo -E - ${1}${pad}${2}
fi
}
最後に、PROMPT
およびを設定する関数を定義して、RPROMPT
すべてのプロンプトの前にZSHを呼び出すようにZSHに指示し、適切なプロンプト拡張オプションを設定できます。
# 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 '%' and '\'
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'
PROMPT="$(fill-line "$top_left" "$top_right")"$'\n'$bottom_left
RPROMPT=$bottom_right
}
autoload -Uz add-zsh-hook
add-zsh-hook precmd set-prompt
setopt noprompt{bang,subst} prompt{cr,percent,sp}
これにより、次のプロンプトが生成されます。
~/foo/bar master
% █ 10:51
#
ルートの場合、%
そうでない場合; 成功すると緑、エラーで赤。