第3回 キー設定をするために

早くもサボって全然日記じゃなくなってますが、まぁネタを考えたりしてたんですが、そんなにあるわけでもないので、キー設定について書きたいと思います。

よく使うコマンドならどのmodeでも同じキーで呼び出せるように、global-set-keyを使って、


(global-set-key "\C-cg" 'goto-line)
などと書いてもいいのですが、define-keyの方が、特定のmodeで効く設定ができるので、.emacsではこちらを使う方が多いでしょう。

(define-key global-map "\C-cg" 'goto-line)
となります。ただglobal-set-keyインタラクティブな関数で、実際に打ったキーにコマンドをバインドできるので、矢印キーやファンクションキーなど、どう書いたらよいかわからないようなキーを設定するときには便利です。手順を書きますと。

まずM-x global-set-keyと打つと、

Set key globally:

と出てくるので、実際に設定したいキーを打つ。そうするとコマンド入力をするよう求められますので、とりあえずコマンドを打つ。

まぁこれでキー設定は出来ますが、もちろん毎回こんなことは繰り返したくありません。そこで、

M-x list-command-historyと打ちます。そうすると今まで打ったコマンドが、*Command History*というバッファにEmacs-Lispとして出てきます。これを.emacsに書けば、次からはこのキー設定が有効になります。これで、打つことが出来るキーならどれでも設定することが出来るようになったと思います。

ただ、^M(Ctrl+m)などは.emacsに書くとファイルの文字コード認識がおかしくなったような気がします。"\C-m"などと書いたほうがいいと思います。

次に特定のmodeでのキーの設定ですが、上でも書いた


(define-key global-map "\C-cg" 'goto-line)
global-mapを特定のmapに書き換えます。ただこのmapになんて書いたらいいかの調べ方ですが、キーを設定したいバッファでC-h v major-modeと打ちます。するとそのバッファのmode名が分かります。

簡単のためにw3m-modeとしますが(なぜ簡単なのかは後で書きますが、とりあえずemacs-w3mが好きなんです。とくにemacs-w3mに含まれるhttp://www.kanshin.com/keyword/489659が好きです)。まぁemacs-w3mは開発者が日本人なので日本語で情報が集まってますし、メーリングリストなんか見てもバグ報告や要望などはほとんどその日のうちに返事が返ってきてますし安心して使えるかと思います。

mixiなんかも職場でこっそり見たり出来ますし(ただmixiへの書き込みはw3m本体にパッチを当てないと、改行コードの問題で改行が全部なくなって1行になってしまったりしたはずですが)、あとメーリングリストとか見ていると英語で"まずは、このすばらしいソフトの開発者に感謝します"とかメールが来てたりして、ちょっとひとごとながらうれしかったりして。

ともかくmajor-modeの値がw3m-modeだったとします。このmodeに入るためには同名のw3m-modeというコマンドを呼んでいるはずです。C-h f w3m-modeでそのコマンドの実体の関数を見てみましょう。そのなかに


(use-local-map w3m-mode-map)
という行があるはずです。ここからmapの名前がw3m-mode-mapという事がわかります(10/21追加。まぁctl-x-mapisearch-mode-mapのようにmap名を調べにくいmapもあるわけですが)。

emacs-w3mの新しいバージョンでは、~/.emacs-w3mに設定を書くとw3mの立ち上げ時に設定を読んでくれたはずですので(今手元に新しいのがないので記憶で書いてます)、~/.emacs-w3m


(define-key w3m-mode-map [M-left] 'w3m-view-previous-page)
(define-key w3m-mode-map [M-right] 'w3m-view-next-page)
(define-key w3m-mode-map [left] 'backward-char)
(define-key w3m-mode-map [right] 'forward-char)
(define-key w3m-mode-map [up] 'previous-line)
(define-key w3m-mode-map [down] 'next-line)
(define-key w3m-mode-map "\C-xk" 'w3m-quit)
と書くと、Firefoxのキャレットモード(と言うんでしたっけ?)のように、カーソルが動くようになります。これで矢印キーでカーソルを動かして英単語の上に持ってきてM-x sdic-describe-wordなどとやると、いい感じです。まぁもとからC-f,C-n,C-b,C-pなどでカーソルは動かせますが。あとC-x k*w3m*バッファを消すと、w3mが終了してしまうのも、なれないうちは好きになれなかったのでw3m-quitにしています。

ちょっと最近0からキー設定したことないので最近の事情がよく分かりませんが、.emacsで立ち上げに値を設定しようとしても、mapが存在しないので値が設定できない場合があるかと思います。.emacs-w3mに書く時はw3m立ち上げ時に読み込むのでいいのですが、そうでなくエラーが出る場合はadd-hookを使って


(add-hook 'w3m-mode-hook
(lambda ()
(define-key w3m-mode-map [M-left] 'w3m-view-previous-page)
(define-key w3m-mode-map [M-right] 'w3m-view-next-page)
(define-key w3m-mode-map [left] 'backward-char)
(define-key w3m-mode-map [right] 'forward-char)
(define-key w3m-mode-map [up] 'previous-line)
(define-key w3m-mode-map [down] 'next-line)
(define-key w3m-mode-map "\C-xk" 'w3m-quit)))
と書くとw3m-modeのinitializationの時にキー設定を行ってくれるので、w3m-mode-mapが存在するのでOKです。

hookの名前の効率の良い探し方はちょっと分かりませんが、M-x apropos w3mなどして-hookという文字列を探したりして説明を読んでいくしかないと思います。まぁlambdaの意味は難しいですが、例えば前回、前々回書いた、M-x my-ls ですが、*scratch*バッファに


(symbol-function 'my-ls)
と書いてC-jと打つと

(lambda nil "Run \"ls -l (buffer-file-name)\"" (interactive) (if buffer-file-name (shell-command (concat "ls -l \"" ...)) (error "Buffer is not associated with a file")))
と出ますが、これが本当の関数の定義です。add-hook関数の2番目の引数はFUNCTIONですが、

(add-hook 'w3m-mode-hook
(lambda ()
(...
の(lambda...)の部分は関数なわけです。まぁlambdaはみなさんの.emacsにも書いてあると思いますが、

'(lambda () ...
とか

(function (lambda () ...
など、書き方がまちまちなんかじゃないかと思いますが、Emacs Lispをみると、

(lambda () ...
でいいと書いてあります。
→■たった3行のelisp関数
http://d.hatena.ne.jp/lightcyan/20090612/1244794753