emacs でのキー入力の表現方法
概要
Emacs のキーバインドを変更するときに, Control キーや Meta キーを等の修飾キー含むキー入力 "C-a", "M-a" は修飾キーをエスケープして, "\C-a" や "\M-a" のように表記する.
しかし, Shift 等を含んだキー入力を指定する際に, 直感に従って
(global-set-key "\C-\S-a" 'backward-char)
のように "\C-\S-a" を文字列として渡してもうまくいかない.
このため, 複雑なキー入力を指定する際に, global-set-key に何を渡せばよいのかが途端に判らなくなる.
例えば, "C-S-a" (Control + Shift + a) をバインドするときは, [?\C-\S-a] を渡せばよいのだが, このように表記が異なる理由をきちんと理解していなかったので, いつも混乱してネットに頼る羽目になっていた.
この混乱を解消すべく, キー入力の表現方法についてきちんと調べたのでまとめておく.
参考サイト
簡単なキー入力の指定方法
内部的なことは気にせずに, 単に簡単にキー入力を指定したいだけなら, 1つ目の参考サイトを見ればよい.
1つ目のサイトに書かれているように, emacs には kbd というマクロがあり, kbd マクロを介することで, エスケープなしの通常の表記 "C-S-a" によって, キー入力を指定できる.
例えば, シフトを含む場合でも
(global-set-key (kbd "C-S-a") 'backward-char)
のように表記すればうまくいく.
キーボード入力と文字列の対応が判らなければ, C-h k (M-x describe-key) の後にキーを入力してみれば対応がわかる.
なので, C-h k で調べた文字列を単に kbd マクロを介して渡してやれば, それで事足りる.
キー入力の内部での取り扱い
global-set-key は第1引数として, vector 型か string 型の文字列を受けとる.
つまり, "\C-a"という表記と [?\C-a] という表記が許されている.
"\C-a" は string型 で, [?\C-a] という表記は char を1つ格納した vector型 である.
vector は角括弧 [] で表され, char は ? とそれに続く文字で表されることを思い出すとよい.
複雑なキー入力を表すために, 簡単な string 型の表記ではなく, vector 型の表記を使わなくてはならないことになる.
このような事情を理解するためには, emacs の文字や文字列の取り扱いを把握する必要がある.
char 型について
emacs では char 型は整数と全く同じである.
?a も ?\C-a も ?\M-a も ?\C-\S-a も1つの整数を表している.
つまり, 修飾キーつきのキー入力そのものが1つの文字とみなされる.
?a ; => 97 ?A ; => 65 ?\C-a ; => 1 ?\M-a ; => 134217825 ?\C-\S-a ; => 33554433
emacs において, 文字列内, バッファ内, ファイル内の文字は 0 から 524287 までの範囲, すなわち22ビット長に制限されていてる.
(?\M-a や ?\C-\S-a はこの範囲に収まっていないことがわかる.)
22ビットのうち, 0から127までの7ビットで表される文字はASCIIコードと呼ばれる.
アルファベット, 改行・タブ等の特殊文字, コントロール + アルファベットなどターミナルの入力として許される文字がASCIIコードに含まれる.
上記の例では,`?a'と`?A'と`?\C-a'が ASCII コードに含まれる.
`?\C-a'のようにエスケープを含むASCII文字はASCIIコントロール文字と呼ばれる.
(Control キーを含むからコントロール文字というわけではない.)
ASCIIコード以外の文字は非ASCII文字と呼ばれる.
さらに, キー入力を表す文字は22ビットよりも長いビットで表され, 23ビット目以降が Meta, Shift 等の修飾キー入力を表すフラグとして用いられる.
ASCIIコントロール文字
`?\C-a'などのASCIIコントロール文字は7ビット以内で表現されることには, 注意が必要である.
また, ASCIIコントロール文字は大文字と小文字を区別できないことにも注意したい.
さらに, ASCIIコントロール文字は`\C-a'の代わりに`\^a'とも書ける.
?\C-a ; => 1 ?\C-A ; => 1 ?\^a ; => 1 ?\^A ; => 1
キーボード入力を表すときは`\C-'を用い, バッファ内の文字を表すときは`\^'を用いることが推奨されている.
修飾キー
キー入力を表す文字は 23 bit 目以降のフラグを用いて, 修飾キーを表していると書いたが, それぞれのキーが何 bit 目をフラグとして用いているかをまとめておく.
(emacs reference manual では "Meta は 2**27 bit を使う" という風に書かれているが, これは 2の27乗を表す 28 bit 目が Meta キーに対応するという意味である)
- Meta (\M-): 28 bit 目
- Control (\C-): 27 bit 目
- Shift (\S-): 26 bit 目
- Hyper (\H-): 25 bit 目
- Super (\s-): 24 bit 目
- Alt (\A-): 23 bit 目
上記のリストの通り, Control は 27 bit 目をフラグとして使っているが, このフラグを用いるのは`?\C-1'のような非ASCIIコントロール文字だけである.
`?\C-a'のようなASCIIコントロール文字は, 27 bit 目を用いないので注意したい.
?\C-a ; => 1 ?\C-1 ; => 67108913
結局, キーボード入力を表す文字は, "ASCII文字" + "修飾キーフラグ" で表されることになる.
注意すべきは専らControlキーを含むASCIIコントロール文字である.
ASCIIコントロール文字における Control キーは修飾キーとは見なされない.
例えば, `?\C-\M-a' と書いても `?\M-\C-a' と書いても, "ASCII文字(\C-a)" + "修飾キー(\M-)" と解釈される.
参考までに, 非ASCIIコントロール文字を評価した結果を示しておく.
対応する bit フラグが立っていることがわかる:
?\A-a ; => 4194401 ?\s-a ; => 8388705 ?\H-a ; => 16777313 ?\S-a ; => 33554529 ?\M-a ; => 134217825 ?\M-\C-a ; => 134217729 ?\C-\M-a ; => 134217729
結局, char型はフラグを用いることで, すべてのキー入力が表現できるので, それを, vector に格納して渡せば一般のキー入力を指定できることになる.
string 型について
char 型の説明内でも少し触れたが, 基本的に string 型に含まれる文字として許されるのは, 修飾キーを含まず 22 bit 以内で表される文字である.
なので, コントロール文字として許されているのは ASCII コントロール文字だけということになる.
このルールに従えば "\C-a" は OK だが "\M-a", "\s-a" 等は文字列として許されないということになる.
しかし, キーバインドを設定する際に "\M-a" という表記はよく使う.
これは, string 型で "\M-a" という表記をした際には 8bit 目を立てることでMeta キーを区別できるようにするという例外があるためである.
(string-to-char "\M-a") ; => 225
例外として立てられた 8bit 目は define-key (global-set-key の内部で呼ばれる)
や lookup-key などで, 正しく Meta キーとして認識され, 適切に変換される.
このような例外があるのは Meta キーのみで, 残りの修飾キー(Super等)を文字列として表すことはできない.
結局, 文字列として指定できるキー入力は
- ASCII文字
- Meta キーのみを修飾キーとする ASCII文字
の2種類だけということになる.
例を挙げておくと
- "\C-a", "\M-a", "\M-\C-a" はOK
- "\C-1", "\s-a" はダメ
ということになる.
キーシーケンスの指定について
"C-x a"のように複数のキー入力を含むキーシーケンスにコマンドを割り当てる方法について述べる.
キーシーケンスの場合もキー入力として, Control (ASCIIに限る) と Meta 以外の修飾キーをもつような入力は string 型では表現できないことになる.
string を用いた指定方法
次のように, 区切り文字なしでキー入力を続けて表記すればよい.
(global-set-key "\C-xa" 'backward-char)
kbd マクロについて
結局 kbd マクロはキーシーケンスを表す文字列を, global-set-key に渡すための適切なオブジェクトに変換してくれるマクロである.
適当に, ASCIIコントロール文字や非ASCIIコントロール文字を含むキーシーケンスを与えて評価すると, 以下のようになる:
(kbd "A") ; => "A" (kbd "C-a") ; => "\^A" (kbd "M-a") ; => [134217825] (kbd "M-C-a") ; => [134217729] (kbd "C-x a") ; => \^Xa" (kbd "C-x M-a") ; => [24 134217825]