fzfでモノレポの移動をしやすくする
モノレポで別のワークスペースに移動するのが面倒でした。cd ../foo
とかcd packages/foo
とか、けっこうタイプ数が多いです。
この問題をfzfを使って解決しました。この記事では、そのスクリプトを解説します。
スクリプトの概要
筆者が使っているモノレポは「npmを使ってpackages/*
でワークスペースを区切っているもの」がほとんどです。今回はその形式で対応しました。
たとえばこんなリポジトリがあったとして。
.`-- packages |-- sample |-- server `-- client
カレントディレクトリがpackages/sample
のときはこんなfzfになります。
> server | /home/eetann/piyo/packages/server/ client | /home/eetann/piyo/packages/client/ <root> | /home/eetann/piyo
「カレントディレクトリ以外のpackages/*
」と「リポジトリのルート」が候補です。選択したらそのディレクトリに移動します。
カレントディレクトリがリポジトリのルートの場合は単にpackages/*
が候補になります。
> sample | /home/eetann/piyo/packages/sample/ server | /home/eetann/piyo/packages/server/ client | /home/eetann/piyo/packages/client/
スクリプトの解説
先に実装の全体を載せます。
function monorepo_cd() { local packages_dir candidates=() local cwd="$PWD"
# 現在のディレクトリ名に'packages'が含まれているか判定 if [[ "$cwd" == *"/packages"* ]]; then # 'packages' ディレクトリの直下を探す packages_dir="${cwd%%/packages*}/packages" if [[ ! -d "$packages_dir" ]]; then return fi if [[ -d "$packages_dir" ]]; then # candidates: packages直下のディレクトリ(ただし現在のディレクトリは除外) for d in "${packages_dir}"/*(/); do [[ "$d" != "$cwd/" ]] && candidates+=("$d") done # packagesより上のディレクトリも候補に追加 local upper_dir="${packages_dir%/packages}" [[ "$upper_dir" != "$cwd" ]] && candidates+=("$upper_dir") else return fi elif [[ -d "$cwd/packages" ]]; then packages_dir="$cwd/packages" candidates=("${packages_dir}"/*(/)) else return fi
# ディレクトリがなければ終了 if [[ ${#candidates[@]} -eq 0 ]]; then return fi
# fzfで表示名付きで選択 local display_list=() for d in "${candidates[@]}"; do if [[ "$d" == "${packages_dir%/packages}" ]]; then display_list+=("<root> | $d") else display_list+=("$(basename "$d") | $d") fi done
local selected=$(printf '%s\n' "${display_list[@]}" | fzf) if [[ -n "$selected" ]]; then local dest="${selected##*$' | '}" BUFFER="cd $dest" zle accept-line fi}zle -N monorepo_cdbindkey '^xc' monorepo_cd
スクリプトの解説
*"/packages"*
: まるで*
で囲っているように見えますが、単にグロブです。
変数から末尾削除
${cwd%%/packages*}
: 変数cwd
で/packages*
の部分を削除してます。${packages_dir%/packages}
: 変数packages_dir
で/packages
の部分を削除してます。
${変数名%%パターン}
(%%
)だとマッチの部分が最長、${変数名%パターン}
(%
)だと最短になります。
参考:zsh のあの記号 (チートシート) - blog.livewing.net
以上、fzfを使ったスクリプトの紹介でした。
今回のスクリプトはほとんどGTP-4.1
に書かせてみました。「自分のdotfilesから作った雛形」と「やってほしいことをTODOコメントで書いた状態」で渡したら業務中にサクッとできて良かったです。