はじめに
この記事は Emacs Advent Calendar 2017 - Qiita の18日目の記事です。
パッケージをインストールしているだけでは物足りない時がある
EmacsでPHPのシンタックスハイライトやコードフォーマッタを導入する、といった場合には、適当なパッケージを導入すればすみます。しかし、実際現場で開発をしていると、パッケージの導入だけでは物足りないこともあります。そんな時にカスタマイズできるのがEmacsの良さであります。
とは言え、パッケージを自作するというのはハードルが高いので、ここでは以下の2つを紹介します。
- interactive 関数を定義してで簡単な処理をEmacsから実行できるようにする
- Easy-Mmode で簡単なマイナーモードを定義する
interactive 関数を定義して簡単な処理をEmacsから実行できるようにする
簡単にできて地味に便利です。実際に僕が定義している関数を紹介します。その前に、念のためelispでの関数定義の方法について書きます。
vagrantをreloadする関数を定義する
僕は以下のような関数を定義しています。
(defun local-vagrant-reload() (interactive) (async-shell-command "cd ~/vagrant && vagrant reload"))
これにより、Emacsで M-x local-vagrant-reload
すると、vagrant reload してくれます。
軽く説明します。
(defun local-vagrant-reload() ... )
これでlocal-vagrant-reload
という関数を定義します。(interactive)
これがあると、M-x <関数名>
で呼べるようになります。(async-shell-command ...)
関数本体です。シェルのコマンドを実行するだけです。
シェルのコマンドを関数として定義するのは結構便利です。
logをtailする関数を定義する
以下のような関数を定義しています。
(defun local-vagrant-log-tail(logname) (interactive "sLogName:") (async-shell-command (concat"ssh vagrant@local-vagrant 'tail -f /var/log/" logname ".log'")))
これを実行するにはこうします。
M-x local-vagrant-log-tail
を実行LogName:
と表示されるので、apache/access_log
などと入力- local-vagrant上の
/var/log/apache/access_log
を tail した結果が表示されます
先程のよりちょっとパワーアップしました。関数が引数を取っている点が大きな違いです。
(defun local-vagrant-log-tail(logname) ... )
logname という引数を取る関数の定義です。(interactive "sLogName:")
後で詳しく説明します。(async-shell-command (concat"ssh vagrant@local-vagrant 'tail -f /var/log/" logname ".log'"))
先程と同様にシェルのコマンドを実行します。concat は文字列を結合する関数です。
(interactive "sLogName:")
について
(interactive "sLogName:")
は、 M-x <関数名>
で関数を呼べるようにして、かつミニバッファで引数を受けられるようにするための一文です。
sLogName:
という部分がキモになります。これは、一文字目の s
と LogName:
に分かれます。 LogName:
の方は、引数を受け取る時にバッファに表示する文字列です。 s
の方は、引数が文字列(string)である事を表しています。
s
の変わりに n
を入れると、数値を受け取れます。例えば nCount:
と書くと、 Count:
をいう文字列をバッファに表示しつつ、数値の引数を受け取れます。
s
, n
以外にも色々ありますが、大体この2があれば簡単な処理は足りると思いますのでここでは割愛します。
これだけできれば簡単なことは大体Emacsからできるようになりますし、Emacsで開発している人はちょっと便利になるでしょう。
Easy-Mmode で簡単なマイナーモードを定義する
業務でコーディングしていると、複数のプロジェクトに関わることがあります。同じ言語でも異なるコーディング規約を採用している場合もあるでしょう。JavaScriptだけど、こっちはインデントが2で、こっちは4。あるいは、インデントがタブの所もあれば4スペースのところもある...などなど。プロジェクトによってコーディング規約を変更したい場合はマイナーモードを定義すると便利です。そして、マイナーモードの定義には Easy-Mmode が便利です。
Easy-Mmode でマイナーモードを定義する
インデントをtabに変更するモードを定義してみます。
(easy-mmode-define-minor-mode tab-mode ;モード名 "Indent tab mode." ;モードの説明 nil ;初期値がONかどうか " Tab" ;モードラインに表示される文字列 nil) ; キーバインドの設定 ;; hookを登録 (add-hook 'tab-mode-hook '(lambda () (setq indent-tabs-mode t) ;インデントにはタブを使用 (setq c-basic-offset 4) ;タブ幅を4に設定 ))
Easy-Mmode で tab-mode
というモードを定義したうえで、そのhook(tab-modeに切り替わった時に呼ばれる関数)を設定、中では intent-tabs-mode
を t
に設定して、インデントにタブを仕様するようにしています。
この定義だと、 tab-mode
を抜けた時に、インデントがスペースに戻らないので、きちっと定義するのであればもう少し工夫が必要ですが、個人で使う時には十分です。
インデントがtabのプロジェクトを開いた時は、 M-x tab-mode
を手動で実行します。ここも自動でやりたいのですが、面倒なので一旦このような運用をしています。
まとめ
パッケージだけではなかなか設定しづらいこまかいところを、独自のelispで解決する話でした。みなさんもelispを書いて開発しやすいエディタにしていきましょう。