zsh でスムーズなコマンドライン操作を実現するプラグインを作った話

dotfiles 弄りが趣味で、数年に渡って zsh を弄り続けてきました。最近ではコマンドラインの操作感が満足のいくものになり、改善個所が少なくなってきてしまったので、一旦 zsh 弄りの集大成として、作ってきた widget の一部を同一のコンセプトにまとめてプラグイン化してみる事にしました。

## コマンドライン操作で改善したかった事

自分が普段行うコマンドライン操作の半分くらいは、ディレクトリの移動、ディレクトリ内のファイルリスト表示、エディタの起動ですが、例えば「あのファイルを変更しよう」と考えた時、 vim と入力した後補完でファイルパスを入力している途中で「やっぱり一度ディレクトリ移動してから編集しよう」と思ったりする事が良くあり、そのたびに行頭に戻って cd を入力し直す事がストレスになっていました。

これは、まず行いたい事を決めてから、それを行う対象のファイルパスを指定する、という操作の順番が悪いのではと思っていて、とりあえず何をするかは分からないけどファイルパスを入力し、入力後に行いたい事を決められるような、思考の順番を逆転した操作を実現しようと考えました。

そういった考えから作り上げた widget をまとめたものが [sys-diver-zsh](https://github.com/ToruIwashita/sys-diver-zsh) です。

- https://github.com/ToruIwashita/sys-diver-zsh

## sys-diver-zsh の操作について

![sys-diver-zsh-demo](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo.gif)

[sys-diver-zsh](https://github.com/ToruIwashita/sys-diver-zsh) の中核になっているのはファイルパス補完 widget と[インタラクティブ補完](https://qiita.com/ToruIwashita/items/5cfa382e9ae2bd0502be)です。

とりあえずファイルパスを入力する、という操作を実現させるためには、コマンドラインが空の状態でスムーズにファイル補完を開始する必要がありました。

通常、補完を開始するキーである TAB に割り当てられている補完は、コマンドラインが空の状態で補完開始すると、 cd や ls といったコマンドをはじめ、ファイルパスではない様々なものが補完候補に入ってきてしまい、まともにファイルパス補完ができるのは1つめのワードとしてコマンドを入力した後になってしまいます。

これでは今回やりたいとりあえずのファイルパス入力ができないので、 _files というファイル補完専用の補完関数を widget に割り当て、任意のキーに設定できるようにする事で、いつでもファイル補完を開始できるようにしました。 [^1] 

そして、それによって入力されたファイルパスに対する操作（エディタ起動やディレクトリ移動など）も widget 化する事で、思考の順番を逆転した操作を実現させました。

### 特徴

上記の内容から特徴をまとめると、以下2つになります。

- `bindkey`で設定したキー入力で、ファイル補完を開始できる
- `bindkey`で設定したキー入力で、コマンドラインの文字列に対してエディタ起動、ディレクトリ移動等ができる

## 使い方

簡単に使い方を紹介します。

### インストール

[antigen](https://github.com/zsh-users/antigen) などプラグインマネージャを使っている場合は、通常通りインストールして下さい。

```
antigen bundle ToruIwashita/sys-diver-zsh  
```

手動でインストールする場合は以下

```
# /path/to/ は任意に設定
git clone git@github.com:ToruIwashita/sys-diver-zsh.git /path/to/sys-diver-zsh
echo 'source /path/to/sys-diver-zsh/sys-diver.plugin.zsh' >> ~/.zshrc
```

### 設定例

もっとも簡単な方法は以下の一行を zshrc に追加することです。

```
sd-default-setting
```

これにより以下のキーバインド設定が追加された事になります。

```
setopt autocd
setopt menucomplete
setopt completeinword

bindkey -v '^k' menu-complete-files
bindkey -v '^l' edge-start-editor
bindkey -v '^o' cd-leaf-dir
bindkey -v '^^' cdup
bindkey -v '^z^i' immediate-ls
bindkey -v '^z^j' immediate-cat
bindkey -v '^z^k' menu-complete-recent-dirs
bindkey -v '^z^l' start-editor
bindkey -v '^z^o' cd-leaf-dir-and-insert-leaf-file-in-prompt

bindkey -M menuselect '^a' beginning-of-line
bindkey -M menuselect '^b' backward-char
bindkey -M menuselect '^e' end-of-line
bindkey -M menuselect '^f' forward-char
bindkey -M menuselect '^g' .send-break
bindkey -M menuselect '^i' forward-char
bindkey -M menuselect '^j' .accept-line
bindkey -M menuselect '^k' infer-next-history
bindkey -M menuselect '^m' accept-line
bindkey -M menuselect '^n' down-line-or-history
bindkey -M menuselect '^p' up-line-or-history
bindkey -M menuselect '^r' history-incremental-search-forward
bindkey -M menuselect '^u' send-break
bindkey -M menuselect '^]' accept-and-hold
bindkey -M menuselect '^z' vi-insert
```

※ この機能を使うと zsh のデフォルトのキーバインド設定が上書きされてしまうので、必要であれば個別にキーバインド設定を行って下さい。

### 動作デモ

デフォルトのキーバインド設定を行っている場合のデモを掲載

１. `Ctrl+k`で補完を開始し、選択されたディレクトリ上で再度`Ctrl+k`を入力すると次の補完候補を展開、`Ctrl+o`でディレクトリ移動する

![sys-diver-zsh-demo-cd-1](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo-cd-1.gif)

２. 選択したパスがファイルの場合、`Ctrl+z Ctrl+o`と入力する事でディレクトリ移動するかつ、ファイル名がコマンドラインに挿入される

![sys-diver-zsh-demo-cd-2](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo-cd-2.gif)

３. 複数のパスが入力されている時に`Ctrl+l`と入力すると、最後のパスを引数としてエディタが起動する

![sys-diver-zsh-demo-editor-1](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo-editor-1.gif)

4. `Ctrl+z Ctrl+l`と入力すると、エディタはすべてのパスに対して開始される

![sys-diver-zsh-demo-editor-2](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo-editor-2.gif)

５. `Ctrl+z Ctrl+k`の入力で、最近使用されたディレクトリを補完する

![sys-diver-zsh-demo-recent-dir-completion](https://raw.githubusercontent.com/wiki/ToruIwashita/sys-diver-zsh/images/sys-diver-zsh-demo-recent-dir-completion.gif)

### 最後に

[awesome-zsh-plugins](https://github.com/unixorn/awesome-zsh-plugins) というリポジトリに zsh のプラグインがまとめられているので、プラグイン作りの締めに Pull Request を送って載せてもらいました。

プラグイン作成に際しては、このリポジトリにある色々なプラグインの構成を参考にさせてもらいました。自分なりにしっくりとくる構成も整えらたかつ、意外と簡単にプラグイン化できる事が分かったのは良かったです。

[sys-diver-zsh](https://github.com/ToruIwashita/sys-diver-zsh) 良ければ使ってみて下さい。

- - -

[^1]: 単純な設定とか別の方法で実現できそうな気もしつつ、それが発見できなかった。誰か知っていたら教えて欲しい。