仕事用のマシンをM1 MacBook Proに交換してもらったので、開発環境を整え直しました。
2年ほど fish を使ってきたのだけれど、普段は良いのだけれど、ちょっと自動化したくなったときに、やはりPOSIX準拠じゃないシェルはなかなか難しかった。macOSの標準も zsh になったことだし、久しぶりに戻ってみることにした。
導入
現代なので XDG Base Directory Specification に乗っかっておくことにする。 Arch Linux の Wiki がよくまとまっていて助かるのでこれを参考にして進めた。
zshの場合は ZDOTDIR
を指定するといいのだけれど、これをどこで指定するのかという問題がある。zshの起動時に最初に読み込まれるユーザー設定は ~/.zshenv
なのだけれど、ここに ZDOTDIR
を書くということは .zshenv
だけホームディレクトリに残ってしまう。これはちょっと気持ち悪い。
あんまり嬉しくはないのだけれど、諦めて /etc/zshenv
に書くことにします。
ZDOTDIR=$HOME/.config/zsh
これで zsh
のユーザー毎の設定ファイルは ~/.config/zsh
から読まれることになります。(仕事用の macOS だし自分しかユーザーはいないしまあいいでしょ)
次に仕様に沿った環境変数を ~/.config/zsh/.zshenv
設定していきます。
特にひねることはないので推奨のディレクトリにしておきます。
(GOPATH
を ~/local
に ghq のルートディレクトリを ~/local/src
にしているので ~/.local
はちょっと気持ち悪くはあります。)
export XDG_CONFIG_HOME="$HOME"/.config export XDG_CACHE_HOME="$HOME"/.cache export XDG_DATA_HOME="$HOME"/.local/share export XDG_STATE_HOME="$HOME"/.local/state
はい、これで基本的な準備は整いました。
zsh は最新のものを使いたいので Homebrew でインストールしておきます。
$ brew install zsh
Zim
zsh をそのまま使うなんてことは出来ないので、設定をごりごり書いていきたい訳ですが、最近はもう自分で設定を書くのは面倒になっています。 もうちょっと若いときは無限に設定ファイルを触っていられたのですが、こうやってどんどん新しいことを学べなくなっていくのです。
設定なしに良い感じになって、かつ Oh My Zsh の遅さに辟易としていたのでとにかく速いのを探しました。
いくつか見て回って Zim が良さそうだったのでこれを導入します。
導入は最近よく見る curl
を叩くやつ。
$ curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh
.zshrc
にいくつか設定が書き込まれます。よく見ると設定が読み込まれたときにさらに本体のダウンロードを行うようです。
なんとこれだけでまあまあ良い感じに動きます。そして起動が速い!!!
asciiship という Spaceship や Starship を ASCII 文字だけで表現したテーマがデフォルトになっているのですが、もうこれでいいや感があったのでそのままにしています。どんどんこだわりが失われていっている。
他にもテーマがあったり、公式のモジュールもあるので ドキュメント を参照してください。
その他の設定
履歴ファイルの設定だけ .zshenv
に書いても効いてくれなかったので以下のように .zshrc
に書きました。
zim が上書きしている感じなのですが、設定済なら無視するように書かれているように見えるので多分何かを間違っているだけなはず。しかし調べる元気がない。
export HISTFILE="$XDG_DATA_HOME"/zsh/history
あとは cdr とか AUTO_CD
周りの設定を少し書いた。
AUTO_CD
自体は Zim が有効にしてくれている。
# cdrを有効にして設定する autoload -Uz chpwd_recent_dirs cdr add-zsh-hook add-zsh-hook chpwd chpwd_recent_dirs zstyle ':completion:*:*:cdr:*:*' menu selection zstyle ':chpwd:*' recent-dirs-max 100 zstyle ':chpwd:*' recent-dirs-default true zstyle ':chpwd:*' recent-dirs-insert true zstyle ':chpwd:*' recent-dirs-file "$XDG_DATA_HOME"/zsh/chpwd-recent-dirs # AUTO_CDの対象に ~ と上位ディレクトリを加える cdpath=(~ ..)
u
というリポジトリのルートディレクトリに移動する関数を書いておく。
これはたしか ujihisa さんの設定から持ってきたのをずっと使っている気がする。
今回は補完関数も書いたけど、そこまで便利じゃないしバグがある。
function u() { cd ./"$(git rev-parse --show-cdup)" if [ $# = 1 ]; then cd "$1" fi } _u() { _values $(fd --type d --base-directory $(git rev-parse --show-cdup)) } compdef _u u
zsh-abbr
fishで一番良かったのが abbr だったと思うので、これを zsh でも再現させたい。
abbr は alias とは違って、省略形のコマンドを入力すると省略しない形に展開してくれるやつ。
g
って入力してスペース打つと git
に展開してくれる。
alias を覚えるのがつらくなってきたので展開してくれる方が元のコマンドに紐付けて覚えるので助かる。
zsh で同じ機能を実現するのに zsh-abbr が使えるのでこれを導入した。
zim にはプラグイン機構もあるのでこれを使って導入する。
$DOTDIR/.zimrc
に以下の一行を加えて zimfw install
というコマンドを打つだけ。
zmodule olets/zsh-abbr
これで導入が完了するので設定していく。abbr
コマンドを使っても設定できるが $DOTDIR/abbreviations
に書いておくと一括で指定出来る。
僕は以下のようにしている。
abbr b="bundle" abbr be="bundle exec" abbr d="docker" abbr dc="docker compose" abbr di="git diff" abbr e="exit" abbr g="git" abbr gd="git diff --cached" abbr ggrep="git grep" abbr gl="git log" abbr gr="git restore" abbr gs="git switch" abbr l="git log" abbr p="git pull" abbr pick="git cherry-pick" abbr pop="git stash pop" abbr s="git status -sb" abbr st="git stash"
fzf
これまではコントリビュートする程度には peco を使ってきたのですが、気分を変えようと fzf に移行しました。 なんか勝手に重いイメージだったけれど、別にそんなことはなかった。 fzf は preview が出せるのが見た目が良くて楽しい。
導入はREADMEに従って Homebrew でインストールした。
$ brew install fzf $ $(brew --prefix)/opt/fzf/install
補完の設定が生成されるので、それを .zprofile
とかで読み込んでおく。
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
ここまですると Ctrl-r
で zsh の履歴検索、 Ctrl-t
でカレントディレクトリ以下のファイル検索が fzf 経由で出来るようになる。
モダンなコマンド達と組み合わせるために fzf module を追加しておく。 モダンなコマンドというのは fd と bat のことです。
zmodule fzf
$DOTDIR/.zimrc
に追加して zimfw install
する。
これでファイルの検索が爆速になるし、プレビューがカラフルで見やすくなる。
あとは良い感じのをいくつか定義しておく。
まずはデフォルトの設定を .zshenv
に書いておく。peco
のデフォルトに慣れているので --layout=reverse
だけは外せない。あとはお好みで。
export FZF_DEFAULT_OPTS='--layout=reverse --border --exit-0'
最近はほとんどを VSCode の中で過ごしているので使う機会は限られているのだけれど、必要そうなのを書いておく。
ghqでリポジトリを選んで移動するやつ。最低限これさえ出来ればあとはどうでもいい。
fzf-ghq() { local repo=$(ghq list | fzf --preview "ghq list --full-path --exact {} | xargs exa -h --long --icons --classify --git --no-permissions --no-user --no-filesize --git-ignore --sort modified --reverse --tree --level 2") if [ -n "$repo" ]; then repo=$(ghq list --full-path --exact $repo) BUFFER="cd ${repo}" zle accept-line fi zle clear-screen } zle -N fzf-ghq bindkey '^[s' fzf-ghq
プルリクを検索しつつそのブランチに switch するやつ。 仕事では JIRA を使っていて、ブランチ名をチケットの番号にしているので目的のブランチを探すのが大変なことがある。 そんな時にはプルリクから検索して切り替えられると便利なので追加した。 gh が必要なのでインストールしておきましょう。 最近 id:shiba_yu36 さんが pecoで似たようなこと をされていた
fzf-pullreq() { local pullreq=$(CLICOLOR_FORCE=1 GH_FORCE_TTY=100% gh pr list | tail -n+4 | fzf --ansi --bind "change:reload:CLICOLOR_FORCE=1 GH_FORCE_TTY=100% gh pr list -S {q} | tail -n+4 || true" --disabled --preview "CLICOLOR_FORCE=1 GH_FORCE_TTY=100% gh pr view {1} | bat --color=always --style=grid --file-name a.md") if [ -n "$pullreq" ]; then pullreq=$(echo $pullreq | awk '{ print $1 }') BUFFER="gh pr checkout \"${pullreq}\"" zle accept-line fi zle clear-screen } zle -N fzf-pullreq bindkey '^[p' fzf-pullreq
Git のブランチを切り替えるやつ。特におもしろみはない。
# ブランチを選択して switch する fzf-branch() { local format="\ %(color:yellow)%(refname:short)%(color:reset)|\ %(color:bold red)%(objectname:short)%(color:reset) \ %(color:bold green)(%(committerdate:relative))%(color:reset) \ %(color:bold blue)%(authorname)%(color:reset) \ %(color:yellow)%(upstream:track)%(color:reset)|\ %(contents:subject)" local branch=$(git for-each-ref --color=always --sort=-committerdate "refs/heads/" --format="$format" | column -ts "|" | fzf --preview "git log --color {1}") if [ -n "$branch" ]; then branch=$(echo $branch | awk '{ print $1 }') BUFFER="git switch ${branch}" zle accept-line fi zle clear-screen } zle -N fzf-branch bindkey '^[b' fzf-branch
前半で設定した cdr のディレクトリを選択して移動できるやつ。
# cdrの履歴からディレクトリを移動する fzf-cdr(){ local dir=$(cdr -l | fzf --preview 'f(){ zsh -c "exa -h --long --icons --classify --git --no-permissions --no-user --no-filesize --git-ignore --sort modified --reverse --tree --level 2 $1" }; f {2}') if [ -n "$dir" ]; then dir=$(echo $dir | awk '{ print $1 }') BUFFER="cdr ${dir}" zle accept-line fi zle clear-screen } zle -N fzf-cdr bindkey '^[r' fzf-cdr
しれっと exa を使っているのでこれもインストールしておきましょう。
ls
などを exa に読み替えてくれるモジュールがあるのでこれも入れておく。いつも通り .zimrc
に追加して zimfw install
です。
zmodule exa
ls
の 挙動 をちょっと弄りたかったので以下のようにしています。
alias ls='exa --classify --icons -h --reverse'
これで fzf を最低限は使えるようになりました。
この記事は Cornelius で書きました。