
- はじめに
- 基礎編: Emacsからローカル開発環境のVagrantを操作する
- 基礎編: Vagrant上のログをtailする
- 応用編: バッファ内の文字列を置き換える
- 応用編: 選択中のテストを回す
- まとめ
はじめに
Emacsアドベントカレンダー1日目にもかかわらず出遅れてしまって申し訳ありません。*1 普段Emacsを使ってPHPで開発をしております。
Emacsを使っているのは、
- 自分の手に馴染んでいる
- IntelliJのようにお金を払わなくてもすべての機能が使える
- コンソール上で起動でき、サーバーに入ってファイルを弄るときにも使える
- 軽い
などの理由があるのですが、開発中にIntelliJではなくEmacsを使っているからには、Emacsを使っている意味を見出していかなければなりません。
カスタマイズ性の高いEmacs。しかし、Emacsをカスタマイズして便利に使っていくには、Emacs Lispは避けて通れません。ただし、Emacs Lispを自分で書くのは抵抗のある方も多いと思います。*2
そこで今回は、僕が開発中に実際に利用している簡単で便利なEmacs Lispを紹介し、皆さんへのEmacs Lispに対する抵抗感をなくし、そしてもっと便利にEmacsを使えるようにしたいと思います。
基礎編: Emacsからローカル開発環境のVagrantを操作する
実はIntelliJにもローカルのVagrantを立ち上げたりする機能があります。今回は、
をしてみたいと思います。
といっても超簡単で、Elispからシェルのコマンドを呼び出すだけです。以下の関数を定義します。
(defun my-vagrant-reload() (interactive) (async-shell-command "cd ~/myvagrant && vagrant reload"))
僕はこれを my-vagrant.el のようなファイルに定義し、init.el から読むようにしています。
簡単に解説します。
defun: 関数定義を表しますmy-vagrant-reload()関数名です。引数なしであることを表していますinteractive: これを書くことで、Emacs上からM-x my-vagrant-reloadのように呼ぶことができるようになりますasync-shell-commandシェルのコマンドを実行します
試しに Emacs を起動し、M-x my-vagrant-reload とすると、Vagrantが再起動します。*3 コードを書いていると動作確認の際にVagrantを再起動したくなることがあるので、非常に重宝しています。
基礎編: Vagrant上のログをtailする
動作確認の際にログをtailすることがよくあります。しかし、いちいちVagrantにSSH→ログをtailとするのが面倒なので、これもEmacsの関数として定義してしまいましょう。
(defun my-vagrant-tail-info-log() (interactive) (async-shell-command "cd ~/myvagrant && vagrant ssh && tail -f /var/log/myapp/info.log"))
これも先程の vagrant reload とほぼ変わりません。非常にシンプルです。
応用編: バッファ内の文字列を置き換える
PHPのバージョンで関数が使えなくなったので置き換えなければならない!といった場面があるかと思います。規則性のある置換ならEmacs Lispを書いて一発置換できます。
たとえば、PHPUnitのgetMockクラスがPHPUnit 6から削除されました。そこで、新しく Mockery というモックフレームワークを導入し、$this->getMock(...) メソッドを \Mockery::mock(...) メソッドに置き換えていきたいです。
以下のような関数を定義します。
(defun replace-mockery-getmock ()
(interactive)
(beginning-of-buffer)
(replace-regexp "$this->getMock('\\([^()]+\\)')" "\Mockery::mock(\\1::class)"))
この関数は
$this->getMock('HogeClass')
のような文字列を
\Mockery::mock(HogeClass::class)
のように置き換えています。手順としては
beginning-of-buffer: バッファの先頭にカーソルを移動します。replace-regexpが現在のカーソルの位置から下に向かって検索するため、最初にカーソルをバッファの先頭に移動しておく必要がありますreplace-regexp正規表現での置き換えです。正規表現に関しては詳しく説明しませんが、\\1:\\(...\\)でマッチした1番目のぶぶんを取り出します
置き換えの正規表現さえ書いてしまえば、バッファ内の関数などを M-x replace-mockery-getmock で一発変換できて便利です。
応用編: 選択中のテストを回す
すこし応用編です。
phpunit --filter="hoge" NanikaTest.php
とコマンドを叩くと、 NanikaTest.php の中にある、hoge という文字列を含むテストだけを実行できます。これを利用し、現在カーソルがある部分のテストを実行します。
まず、現在カーソルがある関数の関数名を取得します。
;; 今カーソルがいるpublic methodの名前を取得する
(defun php-current-public-method-name ()
;; カーソルから一番近い"public function"を探す
(search-backward "public function")
;; " function " の後ろの部分にカーソルを移動
(search-forward " function ")
;; ここをメソッド名のスタートとする
(setq method-name-start (point))
;; "(" を探す
(search-forward "(")
;; "(" の一つ前にカーソルを移動
(left-char)
;; ここがメソッド名の終わりとする
(setq method-name-end (point))
;; 最後に文字列を切り出した返す
(string-trim (buffer-substring-no-properties method-name-start method-name-end))
)
かなり複雑ですが、Emacs Lispの関数を駆使し、現在のカーソルから一番近い public function の名前を取得できます。PHPUnitのテストはすべて public function なので、これで取得したメソッド名に対してテストを実行すれば問題ありません。
(defun phpunit-current-function ()
(interactive)
(async-shell-command
(concat
"phpunit "
"--filter=\""
(php-current-public-method-name)
"\" "
buffer-file-name)))
concat関数で文字列結合できるので、長いコマンドなどは適当に分割して書くと見やすいですbuffer-file-nameで今開いているファイル名を取得できます。
複雑ですが、なんとなくわかってもらえれば大丈夫です。結局はEmacs Lispの関数とシェルのコマンドだけで、そんなに難しいことはしていません。
まとめ
僕が実際に定義して利用しているEmacs Lispの関数をいくつか紹介しました。
僕のやり方よりももっと効率的な方法もあるかもしれませんし、僕の定義した関数があらゆる人に役に立つとも限りません。
しかし、よく使うスクリプトなどをEmacsで関数を定義することにより、かなりの効率化がはかれます。とりあえずよく使うコマンドを定義してみるなど、簡単なところから始めてみてはいかがでしょうか。