🎇

【気合い】Starshipを使わずにzshプロンプトをカスタマイズする

2023/12/24に公開

はじめまして。一般の大学生です。
最近やたらzshのプロンプトに懲りだして、苦節数日、starshipを使わないでそれっぽいやつを作る事に成功したので備忘録として残します。
参考にさせていただいたのは以下の通りです。
https://qiita.com/guri3/items/dd6dc6168fc4c984c4ae
https://dev.macha795.com/zsh-prompt-customize/
https://tomiylab.com/2020/03/prompt/

Starship使えよ
なんか上手く使えなかったんですよね。早々に断念しました。今から思えば絶対Starship頑張ったほうがよかったと思います。

本編

じゃあやっていきます。

目標

こんな感じ
Normal
Git umpush
x86_64
linux
関係ないけどiTerm2の背景は原神です(布教)

環境

macOS 14.2.1(23C71)
M1 Max 32GB
zsh 5.9 (x86_64-apple-darwin23.0)
/bin/zsh
OrbstackのLinux
zsh 5.9 (x86_64-pc-linux-gnu)
/home/linuxbrew/.linuxbrew/bin/zsh

Requirements?

なにかしらNerd Font(本記事ではJetBrain Mono Nerd Fontを使用しています)
iTerm2 (ほかのターミナルエミュレータでは検証していません。)
homebrew
>> zsh-git-prompt

zsh-git-promptは使います。

.zshrc

#brew
if [ "$(uname -s)" = "Darwin" ]; then
    if [ "$(uname -m)" = "arm64" ]; then
        eval "$(/opt/homebrew/bin/brew shellenv)"
        export PATH="/opt/homebrew/bin:$PATH"
    elif [ "$(uname -m)" = "x86_64" ]; then
        eval "$(/usr/local/bin/brew shellenv)"
    fi
    # zsh-git-prompt
    source /opt/homebrew/opt/zsh-git-prompt/zshrc.sh
elif [ "$(uname -s)" = "Linux" ]; then
    eval "$(/home/linuxbrew/.linuxbrew/Homebrew/bin/brew shellenv)"
    # zsh-git-prompt
    source $(brew --prefix)/opt/zsh-git-prompt/zshrc.sh
fi

# Prompt editing
function orb_boat {
    # コマンド呼びだし
    time=$(date +" %H:%M:%S")
    username=$(whoami)
    # 形を変える
    sharp="\uE0B0 \e[0m"
    # OS とアーキテクチャを決定する
    os_arch_prompt=""
    if [ "$(uname -s)" = "Darwin" ]; then
        os_arch_prompt="macOS[$(uname -m)]"
    else
        os_arch_prompt="$(uname -s)@$(uname -n)"
    fi

    # Check if inside a Git work tree
    if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = true ]; then
        # Display Git information in the prompt
        echo "$bg[blue]$fg[cyan]${sharp}$bg[blue]${os_arch_prompt}$bg[green]$fg[blue]${sharp}$bg[green]${username}$bg[yellow]$fg[green]${sharp}$bg[yellow]$(git_super_status)${reset_color}"
    else
        # Prompt when not inside a Git work tree
        echo "$bg[blue]$fg[cyan]${sharp}$bg[blue]${os_arch_prompt}$bg[green]$fg[blue]${sharp}$bg[green]${username}${reset_color}$fg[green]${sharp}${reset_color}"
    fi
}


# add newline
function add_line {
    if [[ -z $PS1_NEWLINE_LOGIN ]]; then
        PS1_NEWLINE_LOGIN=true
    else
        printf '\n'
    fi
}

precmd() {
    add_line
    PROMPT="$bg[cyan] %*$(orb_boat)
%F{cyan}%B╭@%b%f%F{green}%d%f
%F{cyan}%B╰─→%b %f"
}

# others
## colors
autoload -Uz colors
colors

内容

homebrewのパス関係

if [ "$(uname -s)" = "Darwin" ]; then
    if [ "$(uname -m)" = "arm64" ]; then
        eval "$(/opt/homebrew/bin/brew shellenv)"
        export PATH="/opt/homebrew/bin:$PATH"
    elif [ "$(uname -m)" = "x86_64" ]; then
        eval "$(/usr/local/bin/brew shellenv)"
    fi
    # zsh-git-prompt
    source /opt/homebrew/opt/zsh-git-prompt/zshrc.sh
elif [ "$(uname -s)" = "Linux" ]; then
    eval "$(/home/linuxbrew/.linuxbrew/Homebrew/bin/brew shellenv)"
    # zsh-git-prompt
    source $(brew --prefix)/opt/zsh-git-prompt/zshrc.sh
fi

armになってhomebrewのインストール場所が変わったので、それに合わせて読み込む場所も変わっています。
これはつまり、それぞれに必要なものをインストールする必要があるという事なのですが……試したらzsh-git-promptは問題なく動いたのでOSで共通の場所(arm版)のほうを読み込むようにしていますが、brew --prefixでインストール場所の一番上が出てくるので、$(brew --prefix)/opt/zsh-git-prompt/zshrc.shでそれぞれのところにパスが通ります。

Prompt

ネーミングセンスはおいておくとしてfunction orb_boatの中身についてです

username=$(whoami)

echoで呼び出すだけなのでべつに設定しなくても良いと思います。

sharp="\uE0B0 \e[0m"

\uE0B0これです。 これのためだけにNerd Fontを使うといっても過言ではありません。
\e[0mは{reset_color}と同じ働きです。後述の仕組み上、ここに置くほうが可読性が高いと思います。

    os_arch_prompt=""
    if [ "$(uname -s)" = "Darwin" ]; then
        os_arch_prompt="macOS[$(uname -m)]"
    else
        os_arch_prompt="$(uname -s)@$(uname -n)"
    fi

unameでOSとアーキテクチャを調べます。macの場合はOSとアーキテクチャを返しますが、それ以外の場合はOSとマシン名を返します。
現状、Homebrew(Linuxbrew)がLinux on armに対応しておらず、zsh-git-promptを動かす大前提が成り立たないためです。homebrewのない生活なんて禁酒みたいなもんですからね。

# Check if inside a Git work tree
    if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = true ]; then
        # Display Git information in the prompt
        echo "$bg[blue]$fg[cyan]${sharp}$bg[blue]${os_arch_prompt}$bg[green]$fg[blue]${sharp}$bg[green]${username}$bg[yellow]$fg[green]${sharp}$bg[yellow]$(git_super_status)${reset_color}"
    else
        # Prompt when not inside a Git work tree
        echo "$bg[blue]$fg[cyan]${sharp}$bg[blue]${os_arch_prompt}$bg[green]$fg[blue]${sharp}$bg[green]${username}${reset_color}$fg[green]${sharp}${reset_color}"
    fi

これはもうちょと良い書き方がある気がします。Starship使うとか。
仕組みとしては、背景色付きで出力した後、次のブロックの色を背景色にしたうえで直前のブロックの背景色を前景色にして、をだして、その後に色をリセットする。というものです。
gitが動いてますかでプロンプトの末尾を変えているので似たようなコードが続いてます。
$bg[]で呼びだした色はどうやら背景の透明度に影響を受けるらしい(?)のでウィンドウの不透明度は100%ですね。

# add newline
function add_line {
    if [[ -z $PS1_NEWLINE_LOGIN ]]; then
        PS1_NEWLINE_LOGIN=true
    else
        printf '\n'
    fi
}

色んな方が書いているのでいるんだろうな、と思ってます。

PROMPT="
hogehoge
huga"

とかでもいい気はするんですけどね。

precmd() {
    add_line
    PROMPT="$bg[cyan] %*$(orb_boat)
%F{cyan}%B╭@%b%f%F{green}%d%f
%F{cyan}%B╰─→%b %f"
}

毎回実行します。

# others
## colors
autoload -Uz colors
colors

いるのかな。

zshrc.sh

zsh-git-promptの設定です。
$(brew --prefix)/opt/zsh-git-prompt/zshrc.shにあります。
このパスはカレントバージョンへのシンボリックリンクになってますから、直接編集するよりも別なところでやったほうがよさそうですね。バージョンアップでなくなったりするリスクがあるかも、知らんけど。

git_super_status() {
	precmd_update_git_vars
    if [ -n "$__CURRENT_GIT_STATUS" ]; then
	  STATUS="$ZSH_THEME_GIT_PROMPT_PREFIX$ZSH_THEME_GIT_PROMPT_BRANCH $GIT_BRANCH ${reset_color}"
	  if [ "$GIT_BEHIND" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_BEHIND$GIT_BEHIND ${reset_color}"
	  fi
	  if [ "$GIT_AHEAD" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_AHEAD$GIT_AHEAD ${reset_color}"
	  fi
	  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_SEPARATOR"
	  if [ "$GIT_STAGED" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STAGED$GIT_STAGED ${reset_color}"
	  fi
	  if [ "$GIT_CONFLICTS" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_CONFLICTS$GIT_CONFLICTS${reset_color}"
	  fi
	  if [ "$GIT_CHANGED" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_CHANGED$GIT_CHANGED ${reset_color}"
	  fi
	  if [ "$GIT_UNTRACKED" -ne "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNTRACKED ${reset_color}"
	  fi
	  if [ "$GIT_CHANGED" -eq "0" ] && [ "$GIT_CONFLICTS" -eq "0" ] && [ "$GIT_STAGED" -eq "0" ] && [ "$GIT_UNTRACKED" -eq "0" ]; then
		  STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_CLEAN"
	  fi
	  STATUS="$STATUS${reset_color}$ZSH_THEME_GIT_PROMPT_SUFFIX"
	  echo "$STATUS"
	fi
}

# Default values for the appearance of the prompt. Configure at will.
sharp="\uE0B0\e[0m"
ZSH_THEME_GIT_PROMPT_PREFIX=" "
ZSH_THEME_GIT_PROMPT_SUFFIX=""
ZSH_THEME_GIT_PROMPT_SEPARATOR=""
ZSH_THEME_GIT_PROMPT_BRANCH="$bg[magenta]"
ZSH_THEME_GIT_PROMPT_STAGED="$bg[yellow] ●"
ZSH_THEME_GIT_PROMPT_CONFLICTS="$bg[red] ✖"
ZSH_THEME_GIT_PROMPT_CHANGED="$bg[blue] ✚"
ZSH_THEME_GIT_PROMPT_BEHIND="$bg[yellow] ↓"
ZSH_THEME_GIT_PROMPT_AHEAD="$bg[cyan] ↑"
ZSH_THEME_GIT_PROMPT_UNTRACKED="$bg[magenta] ?"
ZSH_THEME_GIT_PROMPT_CLEAN="$bg[green] ✔ ${reset_color}$fg[green]${sharp}"

git_super_status()以降を編集しています。変えていいよ!って言われているのは一番下だけですが……。
だいたいは元と同じです。空白の幅変えてみたりしてるくらい。
echoで呼びだしてるので%{は必要ありません。出てきちゃうので消します。
CLEANのときだけを出して視覚的にCLEANを分かりやすくしようとしています。
は半角みたいなツラしてますけど全然全角のスペースで出力されるんで後ろの空白が必要になります。

結論

echoでやる部分がアレなせいでちょっと重いですけど満足行くものが出来たと思います。
ありがとうございました。

Discussion