key-comboを使って読みやすいコードを書く (Emacs Advent Calendar 8日目)

新しい情報
key-combo v0.3をリリース - むしゃくしゃしてやった
も合わせてご覧下さい。

Emacs Advent Calendar jp: 2011の8日目の記事です。
昨日7日目はふぁいてんさんでした。http://www.phyten.net/summary/emacs-advent.html
6日目のid:kiwanamiさんの分も本日アップされたようです。
Emacs で IPC や RPC (Emacs Advent Calendar jp: 2011) - 技術日記@kiwanami

この記事では、任意のキーの組み合わせで任意の文字列を挿入するkey-combo.elというのを書いたので、その紹介をします。
GitHub - uk-ar/key-combo: emacs plugin:enhanced smartchr

きっかけ

みなさん、smartchr.elはご存知でしょうか?id:IMAKADOさんが作ったEmacs Lispで、キー入力に対して様々な文字列を割り当てることができます。id:IMAKADOさんによる説明を引用します。

さて、皆さんはプログラム中に = を書くとき、両端にスペースを入れているでしょうか? 私は、入れています。入れないより入れた方が、プログラムがずっと読みやすくなるからです。 ですが、前後にスペースを入れない時と比べて、2回タイプ数が増えてしまうという問題があります。

そういった問題を解決する smartchr.el というemacs拡張をリリースしました。

smartchr.elに出会ったときCtrlやMetaなどの修飾キーを押さずに一般的なキーに動作を割り当てる発想が斬新で私もいろいろ設定して使っていました。
特に

  • 「=」を入力すると「 = 」
  • 「==」を入力すると「 == 」

を挿入する設定をしてしばらく使っていました。ところが、使ってるうちに不満が出てきたのです。

何が問題?

私はよくrubyを使ってますが、上の設定をした上でハッシュの記号「=>」を打つ場合、そのまま「=>」を入力すると「 = >」となって間にスペースが入ってしまいます。もちろん回避策はいくつかあって、「===」や「>>」を入力すると「 => 」を挿入する設定や「===」で「=」を挿入する設定を試してみました。
が、やはり長年のくせで「=>」を入力してしまい、やるせない想いをかみしめてまいりました。

これは何?

前置きが長くなりましたが、そんなやるせない夜とはもうおさらばです。そうkey-comboならね。
具体的には

  • 「=」を入力すると「 = 」
  • 「==」を入力すると「 == 」
  • 「=>」を入力すると「 => 」
  • 「>=」を入力すると「 >= 」

を挿入するよう設定できます。

インストール方法

ソースがgithubにおいてあるので

(auto-install-from-url "https://raw.github.com/uk-ar/key-combo/master/key-combo.el")

とするか、
今話題のmarmaladeから
M-x package-install key-combo
でもインストール可能です。
load-path の通ったディレクトリに配置して、

(require 'key-combo)

すると使えるようになります。

設定方法

「=」を入力すると「 = 」、「==」を入力すると「 == 」を挿入するコマンドを定義してみます。定義には key-combo-define-global を使用します。この関数は global-set-key に対応しますが(第一引数が単一キーの時は)第二引数には挿入する文字列のリストをとることができます。具体的には

(key-combo-define-global (kbd "=") '(" = " " == "))

となります。
続いて、「=>」を入力すると「 => 」を挿入するコマンドを定義してみます。

(key-combo-define-global (kbd "=>") " => ")

となります。
さらに、「>=」を入力すると「 >= 」を挿入するコマンドを定義します。

(key-combo-define-global (kbd ">") '(">"))
(key-combo-define-global (kbd ">=") " >= ")

となります。一行目の(kbd ">")に関しては冗長な感じがしますが今のところ必要です。コンビネーションのキーを登録する場合は前もって一文字目をプレフィックスの様に登録しているからです。("=>"の場合は前もって"="が登録されていた)
なお、ここでの設定例は自分で設定しなくとも

(key-combo-load-default)

とすれば、設定されます。

細かい使い方

smartchr.elより工夫した点として、一回目の入力はundoで本来の動作にキャンセルできます。例えば、「=」を入力すると「 = 」が挿入されますが、そこでundoすると「=」を挿入した状態に戻ります。Wordとかの英単語の先頭を大文字に自動訂正する機能の動作がキャンセルできるとこからパクりました。これで、イラッとしてもすぐに元に戻せます。

smartchr.elと同様に引数の文字列中の `!!' によりカーソルの位置の指定もできるので、以下のような定義も可能です。

(key-combo-define-global (kbd "{") '("{`!!'}"))
(key-combo-define-global (kbd "{}") "{}")

こうすれば、「{」を入力すると「{」「}」がカーソルの前後に挿入されますが、直後に癖で「}」を打ってしまっても余分な「}」が挿入されません。

問題点

疲れてきたので箇条書きです。

  • それでもおせっかいに感じるときがあるので一括で機能をオンオフしたい(マイナーモード?)
  • 何が実行されるかがdescribe-bindingしてもわかりづらい。
  • smartchr.elでの関数を登録する機能が未実装
  • ターミナルでemacsを起動した時、screen経由でコピペしたり、ブラウザからコピペしたりすると不要な文字がついてしまい悲しい。
  • 文の途中を編集するときには希望しない結果になることが多いかも(文末だけ有効にする機能をつける?)
  • 作ったあとに気付いたけど、コード整形だけならリージョンやバッファを指定して一括処理した方が便利かも。

他にも問題点があればgithubのissueトラッカまでお願いします。
ソースもばんばんforkして改造してpull requestをしてみてください。
(デフォルトで便利そうな設定を取り込んできたいです)

あとがき

key-combo.elについて紹介しました。
イデアの元となったsmartchr.elの開発者のid:IMAKADOさんに感謝します。
コードの大半はsmartchr.elとkey-chord.elを盛大にパクって参考にしています。

明日はid:buzztaikiさんです。
お楽しみに。