key-chord.el を読む(前編)。

key-chord.el は何度も言うけど、修飾キー以外の2つのキーの組み合わせで、関数を発動するというゲームコマンドの様なキーバインドを提供してくれるマイナーモードです。
んで、僕はkey-chord で実行するコマンドに引数を渡したいのですが、できれば、その引数をタイプした文字を渡したいと思っているのですが、その方法がよく分からないので、key-chord.el を読んでみる事にしました。

初期設定値。

(defvar key-chord-two-keys-delay 0.1	; 0.05 or 0.1
  "Max time delay between two key press to be considered a key chord.")

(defvar key-chord-one-key-delay 0.2	; 0.2 or 0.3 to avoid first autorepeat
  "Max time delay between two press of the same key to be considered a key chord.
This should normally be a little longer than `key-chord-two-keys-delay'.")

最初の初期値の部分。two-keys-delay は2つの異なるキーをタイプして発動する場合のその間隔の秒数。初期値は0.1秒とけっこう長いので、ちょっと縮めた方が無難かも。
one-key-delay は、同じキーの2度押しで発動する場合の間隔。こっちはまぁ短かい方が誤動作しなくて良いかも。
んで、実際に自分で設定する場合には、

(setq key-chord-two-keys-delay 0.08)

という感じに設定すんですが、なんで key-chord.el 本体では、defvar で変数が定義してあって、自分の設定では setq 使うんかを調べてみたら、defvar は通常様々なプログラムで変更される値を与えるそうな。
んでもって、setq はシンボルの値を変更するもっとも手軽な方法だとかで。defvar は説明文も書く事ができる。グローバル変数シンボルの値を参照/変更する

モードのオンオフ

ちょっと飛ばして、モードの定義の部分。

(defvar key-chord-mode nil)
;; 中略
(defun key-chord-mode (arg)
  "Toggle key chord mode.
With positive ARG enable the mode. With zero or negative arg disable the mode.
A key chord is two keys that are pressed simultaneously, or one key quickly
pressed twice.
See functions `key-chord-define-global' or `key-chord-define'
and variables `key-chord-two-keys-delay' and `key-chord-one-key-delay'."
  (interactive "P")
  (setq key-chord-mode (if arg
			   (> (prefix-numeric-value arg) 0)
			 (not key-chord-mode)))
  (cond (key-chord-mode
	 (setq input-method-function 'key-chord-input-method)
	 (message "Key Chord mode on"))
	(t
	 (setq input-method-function nil)
	 (message "Key Chord mode off"))))

まず、key-chord-mode という変数と関数があるのに注意。むお?っと思ったけど、そういうのはプログラムでは良くある話なので、あまり気にしない。
でも一応調べてみると、Lisp勉強中(2) 変数と関数とシンボルに詳しい説明があった。
key-chord-mode関数の方は、interactive "P" である。interactive は対話的、つまりコマンドとして呼び出す事ができるんですが、"P" って何?となるわけです。P が一番良く見る気がしますけどね。
interactiveのコード文字によると、それぞれのコード文字の指定によって、引数arg-descriptorを interactive を含む関数に渡す。
P は生の前置引数で入出力はなし。意味分からん。が、何も渡さず、すでにある値を使うといった感じか。
呼び出されたら、さっそく key-chord-mode 変数に何やら複雑なものを代入している。たぶん、key-chord-mode が nil 以外であれば、nil を。nil であれば、t を返す感じか。トグル部分だと思うが、もう眠いのでよくわかんない。条件: if, cond, not など前置コマンド引数 prefix-numeric-value arg など。。
んでまぁ、それぞれの状態をメッセージで出すと。

キーマップ定義部分。

(defun key-chord-define-global (keys command)
  (interactive "sSet key chord globally (2 keys): \nCSet chord \"%s\" to command: ")
  (key-chord-define (current-global-map) keys command))

まず、グローバルなキーマップを設定する関数。インタラクティブにも起動でき、対話的にキーバインドとコマンドを定義できる。知らんかったー。
定義したキーバインドは (current-global-map) と一緒に key-chord-define() 関数へ渡されるのだが、current-global-map ってのは、組込みのモヨー。
scratch で評価してみたら、なんか色々なキーマップがどばーっと出てきたので、読んだ通り、現在のグローバルキーマップって所だろう。

(defun key-chord-define (keymap keys command)
  (if (/= 2 (length keys))
      (error "Key-chord keys must have two elements"))
  ;; Exotic chars in a string are >255 but define-key wants 128..255 for those
  (let ((key1 (logand 255 (aref keys 0)))
	(key2 (logand 255 (aref keys 1))))
    (if (eq key1 key2)
	(define-key keymap (vector 'key-chord key1 key2) command)
      ;; else
      (define-key keymap (vector 'key-chord key1 key2) command)
      (define-key keymap (vector 'key-chord key2 key1) command))))

んで、key-chord-define() に渡されてからどうなるかというと、まずkeysが2文字じゃないものはエラーを出します。
次に、よく分からない関数で、keys が分解され、1文字目を key1、2文字目を key2 変数に入れています。
んでもって、正式に keymap として追加する様ですが、key1 と key2 が一緒であれば一個だけを。異なっていれば、逆順も追加している様です。これで、微妙な同時押しに対処しているわけですね。

続きは次回。

思ったよりも長くなったので、とりあえず持ち越し。
defadvice が火を吹くぜ。