作業環境
スニペット

sheet、peco、Alfred workflowを使って、スニペット環境を見直す

More than 1 year has passed since last update.

スニペットは非常に便利な反面、有効活用するには上手な管理が必要で、使い回せるコードは記録しておいて、サッと取り出せるようにしておくことが大事です。
ところがこの管理が非常に難しいと感じます。

少しでも使いやすくするため、以下のように手元の環境を見直しています。

スニペットの保存場所

Sublime Textのスニペットに整理する

私はメインのテキストエディタとしてSublime Text 3を使用しています。Go to anything(操作をキーワード検索で絞り込んで実行する機能)のおかげでスニペットは非常に呼び出しやすくなっています。
Sublime Text: The text editor you'll fall in love with

ProjectManagerでスニペットファイル置き場をプロジェクト化

Sublime Textのスニペットは呼び出しやすい反面、編集と整理が面倒に感じていました。1ファイル1スニペットなので、ファイルの一覧を確認したくなるものの、設定ファイルの場所にたどり着くのが辛いのです。

ならば設定ファイルのあるディレクトリをプロジェクトとして登録すればよさそうです。ただ、プロジェクト機能は標準では少し不便なのでProjectManagerというパッケージを入れることにしました。

Project Managerはプロジェクトを扱うGo to anythingが充実しているので、
これでいつでもスニペット用のプロジェクトを呼び出すことができます。編集のためにファイルを開くのも楽です。

スクリーンショット 2017-01-18 10.51.51.png

ProjectManagerをインストールするには、その前にPackage Controlをインストールする必要があります。下記公式サイトにインストール方法が載っています。(英語)
Installation - Package Control

また、下記に詳しい解説があります。
Sublime Text - Package Control のインストール - 開発メモ - Webkaru

sheetに記録する

Sublime Textではコードスニペットを記録していますが、ソースコード以外もそうしたいと思っていました。スニペット管理ツールも色々あります。

  • Alfred3 (Mac)
  • Dash (Mac)
  • sheet

私はsheetに可能性を感じて使い始めました。「黒い画面」なので抵抗がある人もいるでしょうが、私は気に入っています。

  • スニペットの内容がテキストベースなので他で使い回しが期待できる。
  • コマンドラインで操作できるので拡張が期待できる。
  • ファイル単位で保存するので、登録/編集/削除が楽。
  • ファイル単位で保存するので、このツールをやめたいと思ったときにも他へ流用がしやすい。

sheetの便利さについては、下記が詳しいです。

CUIなスニペットツールsheetが便利なのでzshでsheetファイルを補完 - Glide Note

1行コマンドの一覧

sheetはやり方次第で1ファイル1スニペットでも、1ファイル複数スニペットでも保存可能です。私はコマンドラインとの組み合わせで呼び出すため、1つのファイルに1行コマンドを複数並べてファイルを保存しています。(後述)

履歴

履歴もある意味スニペットと言えます。ディレクトリの移動など、手打ちより呼び出せた方が便利です。

  • zshの履歴保存数を上げる(私は100万にしています)
  • 履歴呼び出しにpecoを活用する。(後述。インタラクティブな絞り込みは必須です。)

履歴の件数は.zrhrcに追記して変更できます。

~/.zshrc
HISTSIZE=1000000
SAVEHIST=1000000

.zshrcに関数定義を追記することで、pecoを使った履歴の絞り込みができます。デフォルトではCtrl+rで履歴を参照できますが、pecoを使った方が便利なので、キーバインドを上書きしています。
関数の中身は使いやすくカスタマイズすればよいでしょう。やっていることは履歴一覧をpecoに渡しているだけです。選択したものをコマンドラインに表示させます。コマンド実行はしていません。

~/.zshrc
if exists peco; then
    function peco_select_history() {
        local tac
        if which tac > /dev/null; then
              tac="tac"
        else
              tac="tail -r"
        fi
        BUFFER=$(fc -l -n 1 | eval $tac | peco --query "$LBUFFER")
        CURSOR=$#BUFFER         # move cursor
        zle -R -c               # refresh
    }

    zle -N peco_select_history
    bindkey '^R' peco_select_history
fi

作ってきたスニペットをどこからでも呼び出したい

上記で作ったスニペットは作った場所がバラバラですが、一元的に扱いたくなってきます。そのための設定を作りまくっているところですが、早速便利になりそうです。

zsh + pecoで保存した1行コマンドをインタラクティブに絞り込み

まず、スニペットファイルを用意します。1行コマンドの寄せ集めを1つのファイルにまとめます。pecoで選択時、検索に引っかかりやすく、また分かりやすくする目的で、

### コメント ### コマンド

の形式で記述しています。

~/.sheets/sh_command
...
### mysql ###
### mysql 接続 ### mysql -u ユーザー名 -D データベース名 -p
### mysql 接続してコマンド実行 ### mysql -u ユーザー名 -D データベース名 -p < SQLファイルへのパス
### mysql 接続してコマンド実行2 ### mysql -u ユーザー名 -D データベース名 -p -e "クエリ"
### mysql データディレクトリ変更 ### vi /etc/mysql/my.cnf
### mysql 全データベースのダンプ ### mysqldump -u ユーザー名 -x --all-databases > dump.sql
### mysql 特定のデータベースのダンプ ### mysqldump -u ユーザー名 データベース名 -p > dump.sql
### mysql 特定のデータベース、特定のテーブルだけダンプ ### mysqldump -p -u ユーザー名 データベース名 テーブル名1 テーブル名2 ... > ダンプ先ファイル;
### mysql INSERT文だけダンプ ### mysqldump -p -u ユーザー名 -t データベース名 テーブル名1 テーブル名2 ... > ダンプ先ファイル;
### mysql WHERE句で絞り込んだ結果をダンプ ### mysqldump -p -u ユーザー名 DB名 --where 'WHERE句' > ダンプ先ファイル;
### mysql リストア ### mysql -u ユーザー名 データベース名 -p < dump.sql
### mysql ファイルに記述したSQL文を実行 ### mysql [-t -N] データベース名 < ファイル名

### sheet ###
### sheet スニペット一覧 ### sheet list
### sheet スニペット出力 ### sheet スニペット名
### sheet スニペット作成 ### sheet new スニペット名
### sheet スニペット編集 ### sheet edit スニペット名
### sheet スニペット削除 ### rm ~/.sheets/スニペット名
### sheet スニペット複製 ### cp ~/.sheets/スニペット名 ~/.sheets/スニペット名2

.zshに関数とキーバインドを追記します。pecoを使った履歴絞り込みで使った処理とほとんど同じですが、ここでは絞り込み結果からコメント部分を除外して入力します。

~/.zshrc
if exists peco; then
    function peco_select_command() {
        local tac
        if which tac > /dev/null; then
              tac="tac"
        else
              tac="tail -r"
        fi
    BUFFER=$(cat ~/.sheets/sh_command | eval $tac | peco --query "$LBUFFER" | sed 's/^### .\+ ### //')
        CURSOR=$#BUFFER         # move cursor
        zle -R -c               # refresh
    }

    zle -N peco_select_command
    bindkey '^xpc' peco_select_command
fi

巷のAnythingインタフェースのように、よく選んだものが上に上がってくるなど賢いことはしませんが、絞り込みだけでも充分に便利です。
スクリーンショット 2017-01-18 10.43.49.png

余談ですが、キーバインドは迷います。
ここではCtrl + x(Ctrl + xは標準では割り当てられていない), p(peco), c(command)の3ストロークですが、バリバリにキーバインドを当てまくってる人はどうやってるんでしょう?

Alfred Workflowを作ってAlfredから1行コマンドをインタラクティブに絞り込み

Alfred Workflow作成にはbash、zsh、Ruby、Python、Perl、PHPなどを使用できます。サンプルは絞り込んだスニペットをクリップボードにコピーするものです。絞り込み部分をPHPで書いて次の処理(クリップボードに保存)に渡しています。

Workflowの作成方法は省略しますが、候補に上がったコマンドを選択するとコマンドがクリップボードにコピーされるものを作ります。Workflowの中身は下記です。スクリプトを使って絞り込んだものをクリップボードにコピーするものです。

スクリーンショット 2017-01-18 10.35.44.png

下記がコマンドをフィルタリングするスクリプトです。

require_once('workflows.php');
$wf = new Workflows();

$query = "{query}";
$words = explode(' ', str_replace(' ', ' ', $query));
$lines = file($_SERVER['HOME'] . '/.sheets/sh_command');

$icon = null;

$int = 1;
foreach($lines as $line) {
    $found = false;
    foreach ($words as $word) {
        if(strpos($line, $word) === false) {
            $found = false;
            break;
        }
        $found = true;
    }
    if ($found) {
        $pattern = '/^### (.+) ### (.+)$/';
        $uid = sprintf('%d.%d', $int, time());
        $arg = preg_replace($pattern, '$2', $line);
        $arg = str_replace(PHP_EOL, '', $arg);
        $title = $arg;
        $sub = preg_replace($pattern, '$1', $line);
        $wf->result($uid, $arg, $title, $sub, $icon);
        $int++;
    }
}

$results = $wf->results();
if (count($results) < 1) {
    $uid = -1;
    $arg = null;
    $title = 'Not found';
    $sub = 'Not found: ' . $query;
    $wf->result($uid, $arg, $title, $sub, $icon);
}

echo $wf->toxml();

Workflowを作って適切なコマンドを入力すると、候補が絞り込まれて表示されます。
 スクリーンショット 2017-01-18 10.40.16.png

Alfred Workflowの作り方については下記が参考になります。
Alfred2のWorflowsを自作して作業を効率化する | WEB EGG

Sublime Text用のスニペットをpecoやAlfredから呼び出す

サンプルコードは載せませんが、Sublime Textでは1スニペットに1ファイル(拡張子.sublime-snippet)が対応しています。ファイルの実態はXMLなので、これを解析すればコマンドラインやAlfredから呼び出せます。