dotfilesを育ててみた

# はじめに

みなさん、dotfiles育ててますか？
これは参考記事に感化された僕が、dotfileを育ててみた記事です。

詳しい育て方と手順は参考記事に載っていますので、そちらを参照ください。
本記事では、どのようなdotfileを作成したのかを記していきます。

https://qiita.com/reireias/items/b33b5c824a56dc89e1f7


# 環境

利用している環境は以下の通りです。
ログインシェルは`zsh`を利用しています。
そのため、zsh関連のプラグインについても設定していきます。

- macOS Catalina (: 19.15.7)
- iTerm2
- zsh
- brew (: 3.0.7)

# 設定

## iTermの設定

ちょっとdotfileとは関係ないですが、macのターミナルにiTerm2に関しての設定です。
僕は、複数のターミナルで操作するのが好きなので、起動時に自動的に6分割になるようにしています。

![スクリーンショット 2021-03-20 0.54.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/9eaa6f16-89a4-aca7-ec83-e61a779659a6.png)


以下の手順で設定可能です。

1. `⌘ + d`で1回垂直方向に分割
2. 縦分割した画面にて`⌘ + shift + d`で水平方向に2回分割
3. 上記の操作で画面を縦3横2の6画面に分割されているので、その状態を`⌘ + shift + s`で適当な名前を指定して保存する。
4. `⌘ + ,`で`Preferences`を開く
    - General -> Closing -> `Quit when all windows are closed`をONにする
    - General -> Startup -> Window restoration policyを`Open Default Window Arrangement`に変更する
    - Arrangements -> にて先ほど保存した画面設定を`Set Default`に設定する


![iTerm_settings.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/81d2ee67-0a01-cd74-9ed9-9fe70de1a48a.gif)

（ちょっと小さいですが、上記の手順を実行した際のgifをのせておきます）

## 追加インストール

便利なshellにするために、brewで以下のパッケージを入れます。

- bat
    - catの高機能版
- exa
    - lsの高機能版
- jq
    - jsonをパースしてくれる
- gwak
    - mac用に最適化されたawk
- peco
    - 「標準入力から受けた行データをインクリメンタルサーチして、選択した行を標準出力に返す」コマンドです。（[引用元](https://qiita.com/xtetsuji/items/05f6f4c1b17854cdd75b)）
- starship
    - コマンドラインをカスタマイズできるプラグイン

以下のコマンドで一発で入ります。

```shell
$ brew install bat exa gwak jq peco starship
```

## zshの設定

`zsh`の設定を行う際に`zinit`というプラグインマネージャーを利用しました。
今まで利用していた`oh-my-zsh`に比べると、起動速度が早かったり、プラグインの管理が簡単らしいので、
dotfileを育てるついでに乗り換えました。
体感レベルですが、zshの起動速度は少し早くなった気がします。

https://qiita.com/crossroad0201/items/17270127732dc20fa8b2

`zinit`のインストールは[公式](https://github.com/zdharma/zinit#option-1---automatic-installation-recommended)を参考に行います。

```shell
$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/zdharma/zinit/master/doc/install.sh)"
```

インストール後に、以下の内容を`.zshrc`に追記していきます。

```shell:.zshrc
### Added by Zinit's installer
  :
  略
  :
### End of Zinit's installer chunk

# setup Prezto via zinit
zinit snippet PZT::modules/helper/init.zsh

# setup oh-my-zsh via zinit
zinit snippet OMZL::git.zsh
zinit snippet OMZP::git
zinit cdclear -q

## ターミナル間でのコマンド履歴を共有しない
## iTermで複数開く際に別windowのコマンドが履歴に追加されるのを防ぐ
setopt no_share_history
unsetopt share_history

## コマンドのシンタックスハイライト
zinit light zdharma/fast-syntax-highlighting

# anyframeのセットアップ
zinit light mollifier/anyframe

## zsh settings
# ヒストリファイルを指定
HISTFILE=~/.zsh_history
# ヒストリに保存するコマンド数
HISTSIZE=10000
# ヒストリファイルに保存するコマンド数
SAVEHIST=10000
# 重複するコマンド行は古い方を削除
setopt hist_ignore_all_dups
# 直前と同じコマンドラインはヒストリに追加しない
setopt hist_ignore_dups
# 履歴を追加 (毎回 .zsh_history を作るのではなく)
setopt append_history
# 履歴をインクリメンタルに追加
setopt inc_append_history
```

その他のプラグインは[awesome-zsh-plugins](https://project-awesome.org/unixorn/awesome-zsh-plugins)から探すと良いと思います。

## 便利alias

### exaへのalias

先ほどのbrewで入れたexaですが、通常のlsコマンドに対してaliasを貼ると便利です。
また、exaコマンドがない場合を想定しています。
exaがどのようなコマンドかは[この記事](https://qiita.com/navitime_tech/items/c249269a3b47666c784b)を見ていただければわかると思います。

```shell:.zshrc

## 〜〜zinitの設定：略〜〜
# ls
if type "exa" > /dev/null 2>&1; then
    alias ls='exa'
    alias l='exa -F'
    alias la='exa -a'
    alias ll='exa -l'
else
    alias ls='ls'
    alias l='ls -CF'
    alias la='ls -A'
    alias ll='ls -l'
fi
```

### batへのalias

ほぼ上と同じ感じですが、batへのaliasも貼ります。

```shell:.zshrc
## 〜〜lsのalias：略〜〜

if type "bat" > /dev/null 2>&1; then
    alias cat="bat"
fi

```

batを利用すると、ファイルの行数が長いファイルでも見やすくなります。
![スクリーンショット 2021-03-20 1.18.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/659ca400-373c-b5ed-1c2e-b1a957e434f7.png)

### dockerのalias

次は、よく使うdockerのvaliasです。僕的には`drm`コマンドをよく利用します。

```shell:.zshrc
## 〜〜exaのalias：略〜〜

# alias for docker
alias di="docker images"
alias dr="docker run --rm"
alias ds='docker stop $(docker ps -q)'
alias dcb="docker-compose build"
alias dcu="docker-compose up"
alias dcd="docker-compose down"
alias dps='docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}"'
## 停止コンテナ、タグ無しイメージ、未使用ボリューム、未使用ネットワーク一括削除
alias drm="docker system prune"
```


## 便利なshell関数

### iTerm2のコマンドの真ん中くらいに移動する

iTerm2上では`^a`でコマンドの先頭、`^e`でコマンドの末尾に移動できます。
ただ、長いコマンドを打った後に、「あっ、真ん中くらいにtypoが・・・」と言うときにカーソルで移動するのは地味に手間取ります。
そんな時に、`^j`を打つとコマンドの真ん中あたりに移動してくれます。
[こちらの記事](https://dev.classmethod.jp/articles/zsh-zle-introduction/)を参考にしました。

![iTerm_ctrl-J.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/880ec71f-d6b5-83be-a358-783999c2fd8a.gif)


```shell:.zshrc
## 〜〜alias：略〜〜

function jump_middle() {
    CURSOR=$((${#BUFFER} / 2))
    zle redisplay
}
zle -N jump_middle
bindkey "^j" jump_middle
```

### 自分専用のmanual

いろいろなaliasを貼っていても、数が多いとどうしても忘れてしまうのが人間かなと思います。
そこで、貼ったaliasの内容を簡単に照会できるコマンドがあると便利です。

まず、前準備として`.manurc`を作成します（名前は任意）。
`大分類・小分類`、`alias`、`コマンドの説明`の想定です。区切り文字として`##`を選んでいますが、なんでも大丈夫です。

```shell:.manurc
iterm:line ## ⌃e ## go tail 
iterm:line ## ⌃j ## go middle
iterm:line ## ⌃a ## go head
iterm:tab ## ⌘t ## new tab
iterm:tab ## ⌘+(RL) ## move tabs
iterm:window ## ⌘r ## clear lines
iterm:window ## ⌘⌥+(RLUD) ## move window
iterm:window ## ⌘d ## split window vertically
iterm:window ## ⌘⇧d ## split window horizontally

alias:docker ## ds ## docker stop $(docker ps -q)
alias:docker ## dcb ## docker-compose build
alias:docker ## dcu ## docker-compose up
alias:docker ## dcd ## docker-compose down
alias:docker ## dps ## docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}"'
alias:docker ## drm ## docker system prune

```

`.manurc`を作成したら、`.zshrc`に次の関数を書きます。

```shell:.zshrc
function mm() {
    if [[ $1 ]]; then
        printf "\033[36m%-20s\033[0m %-20s %-30s\n" "[main:sub]" "[Command]" "[Description]"
        cat $HOME/.manurc | grep $1 | awk -F " *?## *?" '{printf "\033[36m%-20s\033[0m %-20s %-30s\n", $1, $2, $3}'
    else
        echo "mm iterm[:line, :tab, :window]"
        echo "mm alias[:dir, ...]"
    fi
}
```

このコマンドは、任意の場所で確認することができます。
また、フィルターワードを入力して表示項目が多くなりすぎないように調整しています。

コマンド`mm`の場合は、`フィルターワード`の入力を促す。
![スクリーンショット 2021-03-20 1.36.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/83357983-1dd0-873b-dfd6-c28e8110b117.png)

コマンド`mm docker`と入力すると`.manurc`で作成したコマンドの説明を見れます。
![スクリーンショット 2021-03-20 1.36.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/2ddade34-613a-5177-80f8-258d9855d85f.png)

コマンド`mm iterm`ではiTerm関連の内容を見れます。
![スクリーンショット 2021-03-20 1.36.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/72e3238a-3299-5be5-e7e8-8f09c54ae1eb.png)

`alias`コマンドでも登録内容の一覧などは確認できるのですが、
それ以外の簡単な内容などを見たい場合はこちらの方が便利かなと思います。

### node.jsのpackage.jsonに書かれているコマンドの表示

同じような雰囲気で、`package.json`に書かれた`script`の項目も確認できます。
よく、「あれ、`npm run start`だっけ？`serve`だっけ？」ってなる時に重宝します。
（ちなみに、npmのパッケージで同じようなことを行えるものもあります。）

![スクリーンショット 2021-03-20 1.40.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/94b2d1ce-2065-7135-dad5-a93045cfcbe6.png)


```shell:.zshrc
# Node-SCript
function nsc() {
    if [[ -f package.json ]]; then
        printf "\033[36m%-44s\033[0m %-20s\n" "[Command]" "[Description]"
        cat package.json | jq ".scripts" | grep : | sed -e 's/,//g' |  awk -F "\": \"" '{printf "(npm run|yarn)\033[36m%-30s\033[0m %-20s\n", $1, $2}' | sed -e 's/\"//g'
    fi
}
```


### projectディレクトリにどこからでも移動できるコマンド

pecoを利用して、インタラクティブに遷移したいディレクトリを絞りつつ、Enterキーで移動できます。
本当は映像のがわかりがいいのですが、画像で例をお見せします。

任意の場所で`cdp`コマンドを実行すると、次の画面になり、
![スクリーンショット 2021-03-20 1.43.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/c41605f7-ec7a-c60d-b4b4-e351aa5305a5.png)

いきたいディレクトリを選択、Enterを押下すると、こんな感じで移動できます。
![スクリーンショット 2021-03-20 1.44.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/906ae701-9dc9-fa4c-f62c-6fb2d380726c.png)


```shell:.zshrc
# CD Project-directory
# プロジェクトのディレクトリに応じてlsコマンドの`*`を変更してください。
function cdp() {
    local dir="$( ls -1d $HOME/Development/*/* | peco )"
    if [ ! -z "$dir" ] ; then
        echo $dir
        cd $dir
    fi    
}
```

## starship


`oh-my-zsh`でも独自に提供している[theme](https://github.com/ohmyzsh/ohmyzsh/wiki/Themes)があるのですが、完全に自分好みのものを見つけるのは難しいかなと思います。
そこで、コマンドプロンプトの内容を自由にカスタマイズできる`starship`がめっちゃいいです！

まずは、.zshrcにstarshipを読み込むように設定します。

```shell:.zshrc
# set theme via `starship`
eval "$(starship init zsh)"
```

次に、`./config/starship.toml`に設定内容を書き込んでいきます。
と言っても、細かい設定は[公式](https://starship.rs/config/#prompt)を見た方がわかりやすいので、かいつまんで説明します。

starshipでは、プロジェクトディレクトリに、
`package.json`や`pyproject.toml`などの各言語固有の設定ファイルを見つけると、
その環境での言語のバージョンを表示してくれます。

- JavaScript

![スクリーンショット 2021-03-20 1.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/6734848c-ca78-181f-b4e7-80c8c39bb51a.png)

- Python(venvにログイン後)

![スクリーンショット 2021-03-20 1.57.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/148abb37-3b2f-2537-8791-cc6c5376bd21.png)

その他にも、RustやJavaなど有名な言語はサポートしています。
詳しくは[公式](https://starship.rs/config/#prompt)をご覧ください。


また、表示させる内容も、カスタマイズが可能です。
![スクリーンショット 2021-03-20 2.03.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/45a9e9ed-8966-bc8a-2b98-dc9af66346bb.png)

上から順に、

- デフォルト
- symbolの変更（🐍 から python:）
- Pythonの表示色の変更
- 非表示



さらに加えて、独自の内容も表示させることが可能です。
僕は、よくAWSのCDKを利用しているのですが、CDKのバージョンを表示させて見ます。


前準備として、`.zshrc`に環境変数として、`CDK_VERSION`を設定します。
（毎回、`cdk --version`を実行してもいいのですが、地味に時間がかかるためです。）

```shell:.zshrc
# for starship(zsh) and CDK
export CDK_VERSION="$(cdk --version | awk '{printf $1}')"
```

そしたら、`starship.toml`に次のように追記します。

```shell:.config/starship.toml

# custom.***と言う名前で認識してくれる。
[custom.cdk]
# プロンプトに表示される内容。実行結果が$output変数に格納される。
command = "echo $CDK_VERSION"
# cdkのバージョンを表示する条件。
# 実行するディレクトリに`cdk.json`がある場合
when = """ test -e cdk.json """
# シンボル
symbol = "cdk: "
# 表示のフォーマット
format = "[$symbol$output]($style)"
```

実行結果は次の通りです。
一番右側に表示されていますね。
ちなみに、Pythonのバージョンが2.7系なのはvenvに入っておらずシステムのが参照されているためです。

![スクリーンショット 2021-03-20 1.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260295/0e286b2a-6d6b-a8aa-601f-c4e381d509b9.png)



## シンボリックリンクを貼る

ここまでかなり長くなってしまいましたが、ようやく今まで作成したファイルを `$HOME`ディレクトリに入れます。
ただ、普通のコピペだとGit管理が大変になるのでシンボリックリンクを利用しています。
今回作成したファイルである`.zshrc`、`.manurc`、`.config/starship.toml`へ`$HOME`からシンボリックリンクを貼ります。

```shell:scripts/dotfile_symbolic_link.sh
IGNORE_PATTERN="^\.(git|config)"

echo "Create dotfile links."
for dotfile in .??*; do
    [[ $dotfile =~ $IGNORE_PATTERN ]] && continue
    ln -snfv "$(pwd)/$dotfile" "$HOME/$dotfile"
done

# create .config in $HOME
mkdir -p $HOME/.config
for dotfile in "$(ls .config)"; do
    [[ $dotfile =~ $IGNORE_PATTERN ]] && continue
    ln -snfv "$(pwd)/.config/$dotfile" "$HOME/.config/$dotfile"
done

echo "Success"
```


設定は以上です！！
長くまでお疲れ様でした！

# メリット・デメリット

こんな感じでdotfileを育てて見たのですが、メリットとデメリットをまとめてみます。

## メリット

やっぱり、Gitで管理するメリットをそのまま享受できる点（バージョン管理・共有など）です。
新しいパッケージなどをインストールして.zshrcなどが変更されて、変な動作をしても戻しやすくなります。

また、dotfilesに関連するリポジトリは、他のユーザも多くの方がパブリックリポジトリで公開してくださっているので
参考になることが多いです。実際、[この記事の方](https://qiita.com/reireias/items/b33b5c824a56dc89e1f7)が公開しているdotfilesのリポジトリも参考にさせていただきました。

shellの関数を自分で作成する必要があり、今まで避けてきた`awk`や`sed`の基本的な使い方がわかったのも大きいです。
（細かいオプションとかは全然まだ知りませんが・・・！）

あと、これは僕だけかもしれませんが、Gitで管理するとちょっとテンションが上がります。


## デメリット

デメリットは、特に思い浮かばないのですが本当に強いてあげるのであれば
dotfileのGitHubのリポジトリをpublicにしているため、独自の設定などを入れにくいことです。
（気になる方はprivateでリポジトリを作成することをオススメします。）
逆にpublickになっていると、周りの目が気になってきれいなソースコードなどを書くきっかけやメンテのモチベーションになていると思います。（デメリットのようで、メリットですね笑）

# おわりに

今回作成したdotfileのリポジトリは以下に公開してあります。
この記事で、書ききれなかったコマンドなども入っていますで是非見てあげてください。
是非とも参考にしていただきつつ、自分のdotfilesを育ててみてください。

https://github.com/gsy0911/dotfiles

参考にした記事では、テストを実行したり、ansibleでかなり自動化が進んでいました！
僕はそこまでできるかはわかりませんが、やってみたら面白そうですね！