-
Notifications
You must be signed in to change notification settings - Fork 0
Description
tmux/Zellij + starship 環境での Tab 補完時の文字重複問題
調査日: 2025-10-23
環境: macOS (Darwin 24.6.0), Ghostty, zsh 5.9, tmux 3.5a, starship
問題の概要
tmux または Zellij + starship プロンプト環境で Tab 補完を使用すると、入力した文字が残って補完結果と重複表示される問題が発生。
症状
# 操作
gi[Tab][Tab]
# 期待する表示
git
# 実際の表示
gigit # 入力した "gi" が残って重複発生環境
| 要素 | 状態 |
|---|---|
| Ghostty 直接起動 + starship | ✅ 問題なし |
| Ghostty + シンプルプロンプト + tmux | ✅ 問題なし |
| Ghostty + starship + tmux | ❌ 重複発生 |
| Ghostty + starship + Zellij | ❌ 重複発生 |
根本原因
starship プロンプトがターミナルマルチプレクサ(tmux/Zellij)環境で zsh にプロンプトの幅を誤認識させている
詳細な仕組み
-
starship が生成するプロンプトの構造
starship が生成するプロンプトには ANSI エスケープシーケンス(色やスタイル)が含まれる 例: \033[1;32m~/workspaces/dotfiles\033[0m これらのエスケープシーケンスは画面上では幅を持たない(表示されない制御文字) -
幅の誤計算
実際のプロンプト幅: 30文字(見た目) zsh が認識する幅: 40文字(エスケープシーケンスを含めて計算) 誤差: 10文字 -
補完時の画面描画で問題発生
zsh は "gi" を消去して "git" を書き込もうとするが、 消去位置が誤差分ずれているため、"gi" が残る 結果: 画面には "gigit" と表示される -
マルチプレクサ環境でのみ発生する理由
Ghostty 直接: starship → zsh → Ghostty (幅計算が正確) マルチプレクサ経由: starship → zsh → tmux/Zellij(仮想端末) → Ghostty ↑ ここで幅計算が狂う
調査プロセス
1. 初期仮説と検証
仮説: zsh-autosuggestions が原因
結果: ❌ プラグインを無効化しても問題は継続
仮説: カスタム補完関数(_workspace_complete)が原因
結果: ❌ 標準コマンドでも同じ問題が発生
2. tmux 設定の調整
試した変更:
default-terminalをtmux-256colorに変更terminal-overridesの調整
結果: ❌ 効果なし
3. zsh 補完設定の調整
試した変更:
zstyle ':completion:*' list-prompt ''
zstyle ':completion:*' select-prompt ''
setopt always_last_prompt結果: ❌ 効果なし
4. 原因の特定
シンプルなプロンプトでテスト:
export PS1='%F{green}%~%f $ '結果: ✅ 問題が完全に解消
→ starship プロンプトが原因と確定
5. Zellij での検証
tmux の代替として Zellij をテスト
結果: ❌ 同じ問題が発生
→ tmux 固有の問題ではなく、starship + マルチプレクサの組み合わせが原因と確定
試した解決策
1. Tab 押下時に行をクリア(部分的に成功)
if [[ -n "$TMUX" ]]; then
_fix_completion_display() {
# Clear the line using terminal escape sequences
printf '\r\033[2K'
# Redraw the prompt
zle reset-prompt
# Perform completion
zle expand-or-complete
# After completion, ensure clean redisplay
zle redisplay
}
zle -N fix-completion-display _fix_completion_display
bindkey '^I' fix-completion-display
fi効果:
- ✅ 1回目の Tab + 候補選択: 正しく動作(
gi→git) - ❌ 2回目の Tab(補完候補リスト表示時):
gigitと重複
説明:
printf '\r\033[2K': カーソルを行頭に移動(\r)し、行全体を消去(\033[2K)zle reset-prompt: プロンプトを再描画zle expand-or-complete: 補完実行zle redisplay: 画面全体を再描画
制限:
- 2回目の Tab では、zsh が内部で候補リストを描画する際、再度幅計算の誤差が発生
- 候補リスト描画のタイミングでは外部から介入できない
2. line-pre-redraw フックで継続的にクリア(失敗)
add-zle-hook-widget line-pre-redraw _tmux_completion_pre_redraw結果: ❌ 画面が再描画されるたびに行をクリアしてしまい、候補選択後も継続的にクリアされてすべてが消える
3. プロンプト変更(根本的解決)
Powerlevel10k への変更を検討したが、starship を使い続けることを優先
最終的な対処法
現状維持 + 部分的な修正
.zshrc に以下を追加(tmux/Zellij 環境でのみ有効):
# Fix completion display issues in tmux
# Tab 1回 + 候補選択で正しく動作するようにする
if [[ -n "$TMUX" ]]; then
_fix_completion_display() {
# Clear the line using terminal escape sequences
printf '\r\033[2K'
# Redraw the prompt
zle reset-prompt
# Perform completion
zle expand-or-complete
# After completion, ensure clean redisplay
zle redisplay
}
zle -N fix-completion-display _fix_completion_display
bindkey '^I' fix-completion-display
fi運用方針
- ✅ 最も一般的な操作(1回の Tab で候補選択)は完璧に動作
- ✅ セッション永続化を維持(tmux/Zellij 必須)
- ✅ starship の美しいプロンプトを維持
⚠️ 2回目の Tab(補完候補リスト表示時)のgigit重複は許容
他のユーザーの対処法
Stack Overflow などで同様の報告が多数確認された:
一般的な原因
-
プロンプトのエスケープシーケンス問題(最多)
- 色やフォーマット用のエスケープシーケンスが
%{と%}で囲まれていない - zsh がプロンプトの幅を誤計算
- 色やフォーマット用のエスケープシーケンスが
-
ロケール設定の不一致
- ターミナルのエンコーディングと shell のロケール設定が合っていない
-
Unicode 幅の計算問題
- 絵文字や日本語などの全角文字の幅を誤認識
一般的な対処法
- 手動で再描画: Ctrl+L で画面をクリア(一時的対処)
- プロンプト変更: Powerlevel10k など互換性の高いプロンプトに変更
- マルチプレクサを使わない: セッション永続化を諦める(非推奨)
完全な解決策(未採用)
選択肢 1: Powerlevel10k への変更
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k
source ~/powerlevel10k/powerlevel10k.zsh-themeメリット:
- ✅ 問題が完全に解決
- ✅ starship 並みに機能豊富
- ✅ tmux/Zellij で動作実績あり
デメリット:
⚠️ starship から乗り換える必要がある
選択肢 2: シンプルなプロンプト
export PS1='%F{green}%~%f %F{blue}$(git branch 2>/dev/null | grep "^\*" | cut -d" " -f2)%f $ 'メリット:
- ✅ 問題が完全に解決
- ✅ 軽量・高速
デメリット:
⚠️ starship のような豊富な機能がない
技術的考察
なぜ完全な解決が難しいか
-
starship 側の問題
- starship が生成するプロンプト文字列の幅計算に問題がある可能性
- ただし、多くの環境では正しく動作している
- マルチプレクサとの組み合わせ特有の問題
-
zsh の補完システム
- 補完候補リスト表示は zsh の内部処理
- 外部から完全に制御できない
- フックポイントが限定的
-
マルチプレクサの画面管理
- tmux/Zellij の画面バッファ管理を外部から制御できない
- エスケープシーケンスの解釈を変更できない
3つのシステムの相互作用
starship (プロンプト生成)
↓ ANSI エスケープシーケンスを含む文字列
zsh (画面描画計算)
↓ 幅計算が必要
tmux/Zellij (仮想端末・画面バッファ管理)
↓ エスケープシーケンスの再解釈
Ghostty (実際の表示)
この処理の連鎖で、幅計算の誤差が発生する。
参考情報
関連する Stack Overflow の質問
- zsh - First characters of the command repeated in the display when completing
- Zsh + tmux + oh-my-zsh: Autocomplete produces remnant characters
- Tab completion in zsh makes duplicate characters
使用した ANSI エスケープシーケンス
\r # Carriage Return(カーソルを行頭に移動)
\033[2K # 行全体を消去
\033[0K # カーソル位置から行末まで消去
\033[1K # 行頭からカーソル位置まで消去
\033[2J # 画面全体をクリアzle (Zsh Line Editor) コマンド
zle reset-prompt # プロンプトを再描画
zle expand-or-complete # 補完実行
zle redisplay # 画面全体を再描画
zle -N <name> <function> # カスタムウィジェットを登録
bindkey <key> <widget> # キーバインドを設定まとめ
- 根本原因: starship プロンプトがマルチプレクサ環境で zsh に幅を誤認識させる
- 完全な解決: Powerlevel10k などへのプロンプト変更が必要
- 採用した対処: 1回目の Tab 補完を修正、2回目は許容
- 運用方針: 実用上最も重要な操作は正しく動作するため、現状維持
この問題は starship、zsh、tmux/Zellij の3つのシステムが絡む複雑な問題であり、外部から完全に制御することは困難である。