ZshとiTerm2の環境下で、プロンプトにemojiを用いた場合にコマンド補完後に最初の1文字が重複して表示される問題を解決した。

ある日久々にiTerm2をアップデートしたところ、コマンド補完後に最初の1文字が重複して表示される問題が起こった。

と言われてもどのような問題か分からないと思う。ぼくも上手く説明できない。実際に見るとすぐ分かるので動画を撮った。

iTerm上でZshを使っている様子のアニメーションGIF。プロンプトにemojiが含まれるため、補完後に表示が崩れ、元々のコマンドの最初の1文字が重複して表示されている。

以上のように、補完を行うと、入力していた文字列の1文字目が重複して表示される上に、行を消しても1文字だけ残ってしまう状況になる。これは表示上だけの問題で、Enterキーを押しても入力されて無いものとして扱われる。この状況で作業するとかなりストレスが溜まった。

プロンプトの文字列を変更して問題を切り分けると、emojiがプロンプト中に含まれているのが原因だと分かった。ぼくの環境では$の代わりに💰のemojiを使っている。金運アップを見込んでだ。

解決するにはプロンプト指定において、emoji部分を%1{%}で囲めばよい。%1{💰%}という具合だ。

解決にあたっては、StackexchangeのFirst characters of the command repeated in the display when completingという質問への回答を参考にした。

Zsh needs to know the width of the prompt in order to know where the characters of the command are placed. It assumes that each character occupies one position unless told otherwise.

One possibility is that your prompt contains escape sequences which are not properly delimited. Escape sequences that change the color or other formatting aspects of the text, or that change the window title or other effects, have zero width. They need to be included within a percent-braces construct %{…%}. More generally, an escape sequence like %42{…%} tells zsh to assume that what is inside the braces is 42 characters wide.

zsh - First characters of the command repeated in the display when completing - Unix & Linux Stack Exchange

要は、Zshは現在何カラム目までコマンドを打っているかを知る必要があって(レンダリングのためだろうか)、基本的には1つの文字は1カラム幅であるとみなすところ、%n{, %} (nは自然数)を使うと、囲んだ範囲の文字列に対してカラムの消費量を強制的に指定することができるようだ。

Zshが💰を2カラム長であると誤認していたところ(n=2とすると同じ問題が起こった。また、n>3とすると、n-1文字分重複することが確認されたため、このように結論づけた)を、%1{とすることで1カラム長だと正しく認識することで、表示上の問題が解決されたということだろう。とはいえ、💰の文字はそもそも2カラム使っているはずで、なぜ1カラム分であると認識させる必要があるのかは、本当のところよく分からない。

ともかく、ZshとiTerm2の環境下で、プロンプトにemojiを用いた場合にコマンド補完後に最初の1文字が重複して表示される問題は、プロンプト指定内で、emojiを%1{%}で囲むことで解決することができた。プロンプトにemojiを使うのをやめろ?嫌です。我こそは🍣SUSHI (U+1F363)のスポンサーぞ