Zshプロンプトのカスタマイズ

# 経緯
Gitのブランチ表示・タブ補完などをしようとおもった
→いろんな記事のコードを組み合わせたらうまく動かずぐちゃぐちゃになった
（おそらくZsh使ってるのにBash向けの記事も参考にしたため）
→なんとかなったので整理がてらの備忘録を書く

# 環境
| 項目 | 内容 |
| :-- | :-- |
| マシン | MacBookPro 2017 |
| OS | macOS 12.4 Monterey |
| シェル | Zsh |
| Python: バージョン管理 | pyenv (from HomeBrew) |
| Python: パッケージ管理 | pip |
| Python: 仮想環境 | venv |

# 導入
## 完成イメージ
黄色：仮想環境、緑色：ユーザー名、白色：フォルダ名、水色：gitブランチ
![IMG_0243.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614520/f4132782-1f06-803f-21e8-682e32a57e03.png)


## 導入したもの
- zsh-completions
- zsh-autosuggestions
- zsh-syntax-highlighting
- git-prompt

## 導入の流れ
- pyenvやvenvなどの導入方法、利用方法は割愛します
- homebrewは入っているものとします

1 以下のコマンドを実行
```zsh:zsh
brew install wget
```
2 `/Users/hoge/.zsh/` を作成する
3 このディレクトリで以下のコマンドを実行
```zsh:zsh
# zshのシンタックスハイライト用
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git
# gitのコマンド補完など用
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -O ~/.zsh/git-prompt.sh
chmod a+x ~/.zsh/git-prompt.sh
```

4 brewで以下のパッケージをインストール

```zsh:zsh
brew install zsh-completions zsh-autosuggestions
```

5 後述のファイルを作成していく

## 備考
プロンプトのブランチ表記・色・順番はいろいろ変えられるので、参考資料ページを見て自分好みにすると良いと思います。

# 一覧
## `~/.zshrc`
```zsh:.zshrc
############################
# 分割ファイルを読み込む様にする
############################


source ${HOME}/.zsh/basic.zsh
source ${HOME}/.zsh/git_utils.zsh

# pyenvで複数バージョンをインストールできない不具合の対処用; なんで入れたか忘れた
export LDFLAGS="-L/usr/local/opt/zlib/lib -L/usr/local/opt/bzip2/lib"
export CPPFLAGS="-I/usr/local/opt/zlib/include -I/usr/local/opt/bzip2/include"

source ${HOME}/.zsh/python.zsh
source ${HOME}/.zsh/venv.zsh
```

## `.zsh/basic.zsh`

```zsh:basic.zsh
############################
# 基本設定の読み込み
############################


# ビープ音を鳴らさない
setopt nolistbeep
# コマンドのスペルミスを指摘する
setopt correct
# 諸々のパスを通す
export PATH="/usr/local/bin:$PATH"

# 履歴ファイルを明示
HISTFILE=~/.zsh_history

# タブ補完などを有効にする
autoload -Uz compinit && compinit

# zsh config
source ~/.zsh/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
typeset -A ZSH_HIGHLIGHT_STYLES
# 諸々の色などを設定
ZSH_HIGHLIGHTING_HIGHLIGHTERS=(main brackets cursor)
ZSH_HIGHLIGHT_STYLES[precommand]='fg=190,bold'
ZSH_HIGHLIGHT_STYLES[command]='fg=190'
ZSH_HIGHLIGHT_STYLES[builtin]='fg=178'

# completion & suggestion
if type brew &>/dev/null; then
  FPATH=$(brew --prefix)/share/zsh-completions:$FPATH
  source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
fi

# 補完候補に色つける
autoload -U colors
colors
zstyle ':completion:*' list-colors "${LS_COLORS}"

# 単語の入力途中でもTab補完を有効化
setopt complete_in_word
# 補完候補をハイライト
zstyle ':completion:*:default' menu select=1
# キャッシュの利用による補完の高速化
zstyle ':completion::complete:*' use-cache true
# 大文字、小文字を区別せず補完する
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
# 補完リストの表示間隔を狭くする
setopt list_packed

# コマンド実行後に1行空ける
add_newline() {
  if [[ -z $PS1_NEWLINE_LOGIN ]]; then
    PS1_NEWLINE_LOGIN=true
  else
    printf '\n'
  fi
}
precmd() { add_newline }

# nodebrew
export PATH=/usr/local/var/nodebrew/current/bin:$PATH

```

## `.zsh/git_utils.zsh`

```zsh:git_utils.zsh
############################
# git関連の読み込み
############################

# git config
source ${HOME}/.zsh/git-prompt.sh

# いろいろ表示させる
GIT_PS1_SHOWDIRTYSTATE=true
GIT_PS1_SHOWUNTRACKEDFILES=true
GIT_PS1_SHOWSTASHSTATE=true
GIT_PS1_SHOWUPSTREAM=auto

# ブランチ名を表示する
setopt PROMPT_SUBST; PS1=$'%F{green}[%n]%f %c %F{cyan}$(__git_ps1 "(%s)") %f\n%# '
```

## `.zsh/python.zsh`
よくあるpyenvの基本設定です

```zsh:python.zsh
############################
# Pythonの読み込み
############################

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
```

## `.zsh/venv.zsh`
venvでは仮想環境を立てたディレクトリ内で `source .venv/bin/activate`および`deactivate`を実行するのが一般的ですが、いろんな仮想環境を作って移動していると「いまどこのディレクトリの仮想環境だっけ……」となりがちだったので、プロンプトに表示できるように `.venv/bin/activate` の中身をオーバーライドしています。

```zsh:venv.zsh
############################
# venvのオーバーライド
############################

function deactivate () {
    # reset old environment variables
    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
        PATH="${_OLD_VIRTUAL_PATH:-}"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi
    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
        export PYTHONHOME
        unset _OLD_VIRTUAL_PYTHONHOME
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
        hash -r 2> /dev/null
    fi

    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
        PS1="${_OLD_VIRTUAL_PS1:-}"
        export PS1
        unset _OLD_VIRTUAL_PS1
    fi

    unset VIRTUAL_ENV
    # 不要
    # if [ ! "${1:-}" = "nondestructive" ] ; then
    # # Self destruct!
    #     unset -f deactivate
    # fi
}

function activate_venv () {
    # 今入っている仮想環境があれば出る
    deactivate
    # カレントディレクトリ直下にあるvenvを利用する
    VIRTUAL_ENV="$PWD/.venv"
    export VIRTUAL_ENV

    _OLD_VIRTUAL_PATH="$PATH"
    PATH="$VIRTUAL_ENV/bin:$PATH"
    export PATH

    # unset PYTHONHOME if set
    # this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
    # could use `if (set -u; : $PYTHONHOME) ;` in bash
    if [ -n "${PYTHONHOME:-}" ] ; then
        _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
        unset PYTHONHOME
    fi

    if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
        _OLD_VIRTUAL_PS1="${PS1:-}"
        # ここは起動したディレクトリの名前に変更する
        PS1="%B%F{yellow}($(basename $PWD))%b%f ${PS1:-}"
        export PS1
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
        hash -r 2> /dev/null
    fi

}
function activate () {
	if [ ! -e .venv/bin/activate ]; then
        # 新たに仮想環境を作成
		python -m venv .venv
        activate_venv
		echo "environments has been constructed"
        # pipのアップグレード
		pip install -U pip
		pip install -U setuptools
        pip install wheel
        echo "pip in venv has been updated to latest version"
		# ディレクトリ直下のrequirementsを読み込んでパッケージをインストール
		if [ -e requirements.txt ]; then
			pip install -r requirements.txt
			echo "libraries has been installed from requirements"
		fi
    else
        activate_venv
    fi
}

# venvからdeactivateしたときに元のPS1に戻せるようにする
# これはgit-promptとvenvを併用した時にプロンプトの表示がおかしくなるため
export _OLD_VIRTUAL_PS1=$PS1
```

# 参考
## Zshの基本設定
https://qiita.com/akkijp/items/9dd56f775078f5bd5815

https://zenn.dev/sprout2000/articles/bd1fac2f3f83bc

https://qiita.com/sprout2000/questions/125fe50199c4fcdc3935

https://wonderwall.hatenablog.com/entry/2016/06/25/205033

# gitのブランチ表示、タブ補完など
https://zenn.dev/aruneko/articles/26815f95ea2124

https://qiita.com/varmil/items/9b0aeafa85975474e9b6

https://qiita.com/tanishi/items/2adb71024d675bc43177