.zshrcでPROMPTを変更した後、Zshタブを完成させると、コマンドは右に数行移動します。

.zshrcでPROMPTを変更した後、Zshタブを完成させると、コマンドは右に数行移動します。

私は最近、素晴らしいプロンプトを印刷する関数を書いて、zuperPrompt.zshrcにPROMPT変数を設定して、次のように関数を呼び出しました。

setopt PROMPT_SUBST
PROMPT='$(zuperPrompt)'

同じ問題を発見しましたここしかし、問題はawkコマンドで使用される改行エスケープ文字にあると思います。改行なしでフルプロンプトを試しましたが、同じ結果が得られました。

機能は次のとおりです。

zuperPrompt() {
 
    # count chars in directory name
    num_chars=1
    if [ ! $(dirs) = '~' ]; then
       let "num_chars=$(basename "`pwd`" | wc -m) - 1"
    fi
 
    # draw line
    line="\n╭──"
    for i in {1..$num_chars}; do
       line+="─"
    done
    line+="──╯"
 
    directory="%F{8}%K{8}%B%F{blue}%1~%k%F{8}"
    arrows="%B%F{magenta}❯%B%F{yellow}❯%F{cyan}❯ "
 
    row1="\n%B%F{green}◆ "$directory"─╮"
    row2=$line
    row3="\n╰─ "$arrows
 
    print -P $row1$row2$row3
}

入力後、Tab キーを押した結果です。python3

ここに画像の説明を入力してください。

ここで何が起こっているのか分かりません。誰でも助けてくれたら、よろしくお願いします!

修正する 私のWebブラウザでこの構文の強調表示を見た後、$ row3が他の色と異なることがわかりました。 $row1$row2 を使用してコードを実行しましたが、正常に動作し、カーソルが移動しません。そこで何が起こっているのか知っている人はいますか?

アップデート2 最後のprint文から-Pオプションを削除しましたが、3行すべてで動作しました。

答え1

シェルのデフォルトのプロンプト文字列は、zshまたは変数に割り当てられた文字列です。これには、文字列を目に見えるプロンプトに動的に拡張する方法をシェルに伝えるさまざまなエスケープ文字とコードを含めることができます。PROMPTPS1

print -rP -- $PROMPTプロンプト文字列を直接使用または拡張できますprint -rP -- $PS1

PROMPTただし、拡張文字列に文字列を割り当てると、print -Pエスケープコードはプロンプト文字列のどの部分が表示され、プロンプト文字列のどの部分が色を変更する端末コードにのみ使用されるかをシェルに伝えます。シェルヒントのサイズを長期間追跡することはできません。これにより、説明する問題の種類が発生します。もう一つの一般的な問題は、長い命令の改行がめちゃくちゃになることです。

print -Pしたがって、後で割り当てる文字列を生成するのではなく、「生」を出力するために使用してくださいPROMPTprint -r

これは説明したのと同じ問題です。zshプロンプトが正しくエスケープされていません。しかし、わずかに異なる理由があります(印刷されない文字に関する情報を追加せずに、プロンプト文字列から印刷されない文字に関する有用な情報を削除します)。


少し恥ずかしいコード

num_chars=1
if [ ! $(dirs) = '~' ]; then
   let "num_chars=$(basename "`pwd`" | wc -m) - 1"
fi

一行で作成可能

num_chars=$( print -n -P '%1~' | wc -m )

%1~は現在のディレクトリ名のプロンプトコードで、後で実際にプロンプ​​トに文字列を含めるために使用します。)

ループもあります。

line="\n╭──"
for i in {1..$num_chars}; do
   line+="─"
done
line+="──╯"

次のように書くことができます

printf -v line '\n╭──%*s──╯' $num_chars ''
line=${line// /─}

ただし、これを次のように組み合わせることができます。

print -v wd -n -P '%1~'
printf -v line '\n╭──%*s──╯' $#wd ''
line=${line// /─}

これは醜いコマンド置換を削除しwc、 。

答え2

このpromptsubstオプションを有効にすると、次のことができます。パラメータ拡張コマンドの置き換えそして算術拡張プロンプトが展開されると展開されるので、持つことができますが、PS1='$(command-that-generates-PS1-dynamically)'拡張結果はまだ%~/が%m展開されるプロンプト文字列です。

だからあなたがしたい最後のことはcommand-that-generates-PS1-dynamically電話をかけることですprint -P。その逆でなければなりません。command-that-generates-PS1-dynamically出力を文字通り表示するには(オンの場合%でも)、エスケープされていることを確認し、カーソルを移動しないすべての制御シーケンスが含まれていることを確認する必要があります。!promptbang%{%}

$PS1すべてのエスケープが提供できるテキストに加えて、zsh-yに近い別のアプローチは、動的%xテキストを設定せずに$promptsubstフックから動的テキストを生成して参照するファイルに保存することですprecmd$psvar%1v%2v$PS1

したがって、代わりに:

command-that-generates-PS1-dynamically() {
  local some_dynamic_data=$(some-cmd)
  print -r -- "%F{red}${some_dynamic_data//\%/%%}%f$ "
}
set -o promptsubst
PS1='$(command-that-generates-PS1-dynamically)'

${some_dynamic_data//\%/%%}sエスケープのみを担当し、含まれているシーケンスを%処理したりエスケープしたりしません。)!$some_dynamic_data

する:

prompt-hook() {
  psvar[1]=$(some-cmd)
}
precmd_function+=(prompt-hook)
PS1='%F{red}%1v%f$ '

これにより、サブシェルを分岐する手間を省き(prompt-hookプロンプト拡張で一部のデータを保持することができます)、%1v拡張(印刷可能テキスト用)は印刷できない文字を処理または変換して表示することができます。

あなたの場合は、次のように見えることがあります

display_width() REPLY=$(($#1 * 3 - ${#${(ml[$#1 * 2])1}}))

prompt-hook() {
  display_width ${(%):-%1~}
  psvar[1]=${(l[$REPLY][─])}
}

precmd_functions+=(prompt-hook)

() {
  local directory='%F{8}%K{8}%F{blue}%1~%k%F{8}'
  local arrows='%B%F{magenta}❯%F{yellow}❯%F{cyan}❯'
  local line=$'─╮\n╭──%1v──╯\n╰─'
  PS1=$'\n'"%B%F{green}◆ ${directory}${line} ${arrows}%b%f "
}

ここでは$PS1静的です(必要ありません$promptsubst。ここでは、読みやすくするために一時的なセクションを使用しています)。ただし、動的カスタマイズセクション(拡張表示幅と同じ長さの行セクション%1~)がフックの一部として作成されますprecmd

このプロセス中に、サブシェルがフォークされないことがわかります。

バラよりこの回答到着文字列の表示幅を取得します。文字列の表示幅を計算する関数の詳細display_width(必ずしも文字数と同じである必要はありません)

関連情報