Software Design連載記事を掲載します。

株式会社技術評論社の許可を得て掲載しています。
草稿なので細かい部分は実際の記事とは異なることがあります。

他の記事は左下にある「■雑誌連載中(全文公開)」から見られます。

Emacs の操作性アップシリーズ、今回は入力支援編です。Emacsに標準で備わる、動/静的略語展開、スケルトンといったEmacs 標準の入力支援についておさらいしたあと、これらを統合したパッケージとも言える「yasnippet」を紹介します。

確実なテキスト入力を

ども、るびきちです。
先々月、先月と操作性を高めるためにはキーボードでの操作をしっかりと整えることだとお伝えしました。
キーバインドを適切に設定しておけば、いつでも素早くコマンドが呼び出せるからです。
それと同時に、入力支援機能を活用しミスタイプなく確実に目的の入力する方法を確立すれば一気に使いやすくなります。

標準的な入力支援

動的略語展開

まず真っ先に使うべき入力支援機能はdabbrev(動的略語展開)です。
長い文字列の入力を省力化する重要な機能です。
この機能がないテキストエディタは正直、使いものになりません。

テキストエディタを使っていると、どうしても同じ単語を何度もタイプすることが多くなります。
しかし、毎回馬鹿正直にタイプすると、時間かかるしタイプミスが起こりやすくなります。
Emacsではその問題に対する解決策が用意されています。
最初の数文字をタイプしてからM-/を押してみましょう。
すると「魔法」が発動し、その数文字から始まる単語に補完されます。

たとえば、interの後にM-/を押すとinternet、interesting、interactive、interactivelyなどの単語に変化します。
再度M-/を押すと別な単語になります。

カラクリはというと、入力された文字列から始まる単語をカーソルに近い方から順次探索していくことです。
カレントバッファで見付からない場合は他のバッファからも探索します。
知ってしまえば当たり前に思える仕組みも、初めて使うとあたかも魔法が発動したかのような感動を覚えることでしょう。

入力支援基本のキとしてM-/は常用しましょう。

略語展開

動的略語展開が動的ならば、静的な略語展開もあるのではないか?もちろんあります。
略語展開はシンプルに略語から長い文字列を展開する機能です。
動的略語展開が動的なのは、変化する単語の結果がバッファの内容に依存するからです。
対して略語展開は変化する単語の結果が決まっています。

たとえばinetからinternetに展開されるように略語展開の設定ができます。
その上でM-x abbrev-modeでマイナーモードを有効にし、inetとタイプし、スペースやカンマなどの単語区切り文字を入力すれば、internetと展開されます。

この機能は後で紹介するyasnippetに完全に置き換わってしまうので、詳しくは述べません。

もし日本語入力にSKKを使っているのならば、アスキー文字から変換する機能があります。
たとえば「/file」→「ファイル」のように変換できます。
SKKは単語登録中心主義であるため、よく使う略語をガンガン登録すれば快適に入力できます。
そう考えればSKKそれ自体が略語展開の機能を持っているといえます。
筆者は長年この機能を略語展開として使っています。

hippie-expand

M-x hippie-expandは動的略語展開、略語展開、ファイル名補完、シンボル補完などを統合した単語補完の十徳ナイフです。
M-/よりも高機能である反面、望みの補完をしてくれないことがあるのが玉に瑕です。

そこで筆者はhippie-exp-extパッケージにてhippie-expandの機能性を活かしつつ補完の目的に沿ったコマンドを作成しました。
M-x hippie-expand-dabbrev-limited-charsは、1バイト文字限定の動的略語展開及び、「-」あるいは「_」から入力した場合に限り長い文字列を途中から補完できるようにしました。
たとえば「-li」から「hippie-expand-dabbrev-limited-chars」と補完できます。
M-x hippie-expand-file-nameはファイル名補完に限定したコマンドです。

スケルトンによる定型文入力

コンピュータの世界におけるスケルトンとは骸骨…ではなくてコードの骨格を意味します。
Emacsにおけるスケルトンとはパラメータを対話的に入力することで定型文を入力するコマンドです。
スケルトンのコマンドを実行することを「スケルトンを展開する」といいます。

たとえば、以下のコードを挿入するスケルトンを考えます。

(defun find-file--my-advice (&rest them)
  )
(advice-add 'find-file :around
            'find-file--my-advice)

これはEmacs 24.4から使える新しいアドバイス定義法で、関数を再定義せずに関数の挙動を変更できます。
そのうち、以下の情報が必要なのでミニバッファから入力を求めます。

説明 変数名 具体的な文字列
元の関数名 symbol find-file
場所 where around
アドバイス名 name my-advice

これをスケルトンで表現すると以下のようになります。
入力が複数なのでスケルトンそのものではなく、コマンド定義とスケルトン展開の合わせ技です。

(defun emacs-lisp-insert-advice-add (symbol where name)
  (interactive "s元の関数名: \ns場所: \nsアドバイス名: ")
  (skeleton-insert
   '("" nil                             ;おまじない
     "(defun " symbol "--" name " (&rest them)" > \n
     _ ")" > \n
     "(advice-add '" symbol " :" where > \n
     " '" symbol "--" name ")" > \n)))

スケルトン定義において「> \n」がインデントして改行するという指定で、「_」が展開後のカーソル位置です。

M-x emacs-lisp-insert-advice-addを実行し、必要な情報を入力すれば上記のコードが挿入されます。
しかし、わかりづらいですよね。

真打yasnippet

略語展開とスケルトンの融合

略語展開は単に略語と展開結果の対応を表したもので、大した機能ではありません。
入力作業全体から見てみれば略語→単語の略語展開による恩恵は微々たるものです。

一方で、略語展開の結果に関数(コマンド)を渡すことができます。
スケルトンはコマンドなので略語展開の結果にスケルトンを割り当てられます。
それをうまくやっているのがEmacs標準添付のpython.elによるpython-modeです。
以下の設定を加え、if/while/for/try/def/classの後にスペースを押せばスケルトンが展開されます。

(setq python-skeleton-autoinsert t)

スケルトンの問題

メジャーモード側で略語展開+スケルトンの設定をしてくれているのは、ユーザからすれば親切といえます。
しかし、スケルトンはS式であるため、細かい指定こそできるものの可読性が低いという欠点があります。
穴埋めが複数あるスケルトンを定義するには、emacs-lisp-insert-advice-addのようにコマンドを定義しなければなりません。
テンプレート展開はテキストエディタを効率よく使いたい一般ユーザとしては是非とも身に付けておきたいところですが、elispプログラミングを要求するのは敷居が高すぎます。
読みづらいのはともかくとして、高々定型文のテンプレートを登録するのになぜelispの知識が必要なのでしょうか!!!

yasnippet登場

スケルトンの使いづらさからか、テンプレート展開のelispは数多く存在します。
スケルトンのようにS式ベースのテンプレート展開elispもありますが、やはりelispの知識を要求するので一般ユーザにはおすすめできません。
elispがわかる人にとっても可読性の問題があり、おすすめできません。
elispにはヒアドキュメントなどの高可読性の文字列表現がサポートされていないので、文字列を表現するには常に文字列リテラルを使う必要があります。
elispの言語としての限界がそこにあります。

そうなると、必然的にテンプレートを独立したファイルに記述する方式が望まれます。
この方式のelispもいくつか登場してきましたが、今ではyasnippetが定番です。

スニペットの例

先程のアドバイス定義のテンプレート(yasnippetではスニペットという)をyasnippetで定義すると、このようになります。
暗号的なスケルトンと比較すれば可読性は明らかに上です。

# -*- mode: snippet -*-
# name: advice-add with function
# key: advice
# --
(defun ${1:symbol}--${2:name} (${3:&rest them})
  $0)
(advice-add '$1 :${4:where}
  '$1--$2)

冗長になっていますが、定義時(M-x yas-new-snippet)に予め雛型が用意されるので丸暗記する必要はありません。
スニペットの先頭から「#」で始まる行はコメントです。
nameはスニペットの1行説明文、keyはそのスニペットに展開する略語です。
スニペットの内容は「# --」行の後に記述します。

スニペットの実体は、穴埋め部分を含むことができる定型文です。
穴埋め部分が存在しない場合は、普通の略語展開と同じ機能です。

穴埋め部分は「$latex {1:symbol}」のように、数字と表示文字列を指定します。 数字は穴埋めされる順番で、表示文字列は穴埋めの説明の役割とデフォルト値の役割を果たします。 スケルトンとの対比のため「${4:where}」と書いていますが、aroundと指定するケースが多いのならば「${4:around}」と書いてデフォルト値として使いましょう。
スニペット展開時はこの部分で文字列を入力することになります。

表示文字列を書いていない「$1」のような指定は、穴埋め部分で入力されたのと同じ文字列に置き換わります。

最後に「$0」はスニペット展開後に移動するカーソル位置です。

インストールと設定

yasnippetはMELPAに登録されているのでM-x package-installからインストールできます。
パッケージをインストールするとyasnippet.el本体だけでなく、数多くのスニペットも同時にインストールされます。
そして、以下の設定をします。

(yas-global-mode 1)
;;; スニペット名をidoで選択する
(setq yas-prompt-functions '(yas-ido-prompt))

スニペットの置き場は yas-snippet-dirs で指定しますが、デフォルトはこうなっています。

("~/.emacs.d/snippets" yas-installed-snippets-dir)

~/.emacs.d/snippets は自分で定義したスニペットを置くディレクトリです。
yas-installed-snippets-dir はパッケージによってインストールされたスニペットディレクトリの変数です。
他のスニペットも使いたい場合は yas-snippet-dirs を適宜設定してください。

スニペットを展開する

スニペットを展開するには、略語(key)を入力してTABを押します。
穴埋め部分が存在する場合はそこにカーソルが移動し、入力できるようになるので入力したらTABで次の穴埋め部分に移動します。
穴埋め部分を空白にするにはC-dです。
すべての穴埋めが終われば展開終了です。
ミニバッファで情報を入力するスケルトンと違い、穴埋め部分にカーソルが移動するので、入力するべきテキストが明らかになるのが強みです。

略語が思い出せない場合はM-x yas-insert-snippetで展開します。
またM-x yas-describe-tablesでスニペットを一覧します。

終わりに

今回は基本的な入力支援をおさらいしてから、yasnippetという強力なテンプレート入力の入口を紹介しました。
yasnippetはそのまま使うだけでも入力を省力化できますが、可読性が高いフォーマットなので自分でスニペットを定義すれば面白いです。
次回はスニペット定義・テスト方法から始まり、いろいろな応用技を見ていきます。

筆者はサイト「日刊Emacs」を運営し、毎日パッケージの紹介記事を書いています。
マイナーなものも紹介しているので、新たなパッケージを求めている人の役に立てば幸いです。

また、EmacsユーザのQOLを上げるための厳選した情報を週間メルマガで配信しています。
Emacsについてはもちろんのこと、ライフハックなどいろいろな分野について書いています。
登録はこちら→http://rubikitch.com/juku/

本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。