読者がEmacsを起動すると、Emacsはほぼただちにエディタコマンドループ (editor command loop)に入ります。 このループは、キー列を読み取り、それらの定義を実行し、結果を表示します。 本章では、これがどのように行われるのか、および、 Lispプログラムからこれを行うためのサブルーティンについて述べます。
コマンドループがまず始めに行うことはキー列、
つまり、コマンドへ変換されるイベント列を読むことです。
これには関数read-key-sequenceを呼び出します。
読者のLispコードでもこの関数を呼び出せます(see section キー列の入力)。
Lispプログラムでは、read-event(see section 単一イベントの読み取り)で
低レベルの入力を行ったり、
discard-input(see section その他のイベント入力機能)で
処理待ち中の入力を破棄できます。
キー列は現在活性なキーマップを介してコマンドに変換されます。
この処理方法についてはSee section キー探索。
この結果は、キーボードマクロであるか、
対話的に呼び出し可能な関数であるはずです。
キーがM-xであると、別のコマンドの名前を読み取り、
そのコマンドを呼び出します。
これはコマンドexecute-extended-command(see section 対話的呼び出し)で
処理されます。
コマンドを実行するには、まず、その引数を読む必要があります。
これは、command-execute(see section 対話的呼び出し)を呼び出して
行います。
Lispで書かれたコマンドでは、
interactive指定が引数の読み方を指示します。
前置引数(see section 前置コマンド引数)を使ったり、
プロンプトを表示してミニバッファ(see section ミニバッファ)から読みます。
たとえば、コマンドfind-fileには、
ミニバッファからファイル名を読むことを指示した
interactive指定があります。
コマンドの関数本体ではミニバッファを使いません。
このコマンドをLispコードから関数として呼び出す場合、
通常のLisp関数の引数としてファイル名文字列を指定する必要があります。
コマンドが文字列やベクトル(つまり、キーボードマクロ)である場合、
execute-kbd-macroを用いてそれらを実行します。
読者自身がこの関数を呼び出してもかまいません(see section キーボードマクロ)。
動作中のコマンドの実行を止めるには、C-gを打ちます。 この文字は中断(quitting)を引き起こします(see section 中断)。
this-commandにはこれから実行するコマンドが保持され、
last-commandには直前のコマンドがある。
see section フック。
this-commandには実行し終えたばかりのコマンドがあり、
last-commandにはその前のコマンドがある。
see section フック。
pre-command-hookやpost-command-hookの実行中は、
中断を禁止します。
これらのフックの1つを実行中にエラーが起きると、
エラーの無限ループを防ぐために、
フックの実行を終了しフック変数をnilにします。
Lisp関数の本体に、スペシャルフォームinteractiveを呼び出す
フォームがトップレベルにあると、Lisp関数はコマンドになります。
このフォームは実際に呼び出されてもなにもしませんが、
このフォームがあることで、対話的に呼び出せることを表します。
その引数が、対話的呼び出しにおける引数の読み方を制御します。
interactiveの使い方
本節では、Lisp関数を対話的に呼び出し可能なコマンドにするフォーム
interactiveの書き方について述べます。
他の関数と同様に、コマンドはLispプログラムからも呼び出せるが、 その場合、呼び出し側が引数を渡し、arg-descriptorにはなんの効果もない。
フォームinteractiveが効果を発揮するのは、
コマンドループ(実際にはサブルーティンcall-interactively)が
関数を呼び出すまえに関数定義を走査してこのフォームを探すからである。
関数が呼び出されると、フォームinteractiveを含めて
その本体のフォームが実行されるが、そのとき、
interactiveは引数を評価せずに単にnilを返す。
引数arg-descriptorには3つの可能性があります。
nil。
この場合、コマンドは引数なしで呼ばれる。
コマンドが1つ以上の引数を必要とする場合、これはただちにエラーになる。
(interactive
(list (region-beginning) (region-end)
(read-string "Foo: " nil 'my-history)))
キーボード入力を読み終えてからポイントやマークを調べることで、
問題を回避する。
(interactive (let ((string (read-string "Foo: " nil 'my-history))) (list (region-beginning) (region-end) string)))
(interactive "bFrobnicate buffer: ")コード文字`b'は、補完を用いて既存のバッファ名を読むことを指示する。 バッファ名は、コマンドに渡される唯一の引数である。 文字列の残りはプロンプトである。 文字列内に改行文字があると、それはプロンプトを終える。 その部分で文字列が終らないときには、 文字列の残りの部分には、別の引数を指定するコード文字やプロンプトがある。 このようにして、何個の引数でも指定できる。 プロンプトの文字列では、プロンプト内の(第1引数から始まる) まえの引数値を含めるために`%'を使える。 これは
format(see section 文字列の書式付け)を用いて行う。
たとえば、既存バッファの名前を読み、
続けてそのバッファに与える新たな名前を読むにはつぎのようにする。
(interactive "bBuffer to rename: \nsRename buffer %s to: ")文字列の最初の文字が`*'である場合、 バッファが読み出し専用であるとエラーを通知する。 文字列の最初の文字が`@'であり、 コマンドを起動したキー列にマウスイベントが含まれる場合、 コマンドを実行するまえに それらのイベントの最初のものに関連したウィンドウを選択する。 `*'と`@'は同時に使え、その順序は関係ない。 引数の実際の読み取りはプロンプトの (`*'でも`@'でもない最初の文字で始まる)残りの部分で制御される。
interactiveのコード文字以下に述べるコード文字の説明では、 つぎに定義するいくつかのキーワードを含みます。
completing-readを使って引数を読むため、
TAB、SPC、RETは名前を補完する
(see section 補完)。
?は補完候補のリストを表示する。
以下に、interactiveに使うコード文字を説明します。
fboundpを満たすシンボル)。
「既存」、「補完」、「プロンプト」。
commandpを満たすシンボル)。
「既存」、「補完」、「プロンプト」。
default-directory(see section オペレーティングシステム環境)。
「既存」、「補完」、「デフォルト」、「プロンプト」。
default-directory。
「既存」、「補完」、「デフォルト」、「プロンプト」。
nilを与える。
「入出力なし」。
describe-keyやglobal-set-keyなどの
コマンドで使われる。
user-variable-pを満たす)。
see section 高レベルの補完関数。
「既存」、「補完」、「プロンプト」。
nil。
see section コーディングシステム。
「補完」、「既存」、「プロンプト」。
nilを与える。
「補完」、「既存」、「プロンプト」。
interactiveの使用例
ここではinteractiveの例を示します。
(defun foo1 () ;foo1は引数なし (interactive) ; 2単語分先へ進める (forward-word 2)) => foo1 (defun foo2 (n) ;foo2は1引数 (interactive "p") ; 数値前置引数 (forward-word (* 2 n))) => foo2 (defun foo3 (n) ;foo3は1引数 (interactive "nCount:") ; ミニバッファで読む (forward-word (* 2 n))) => foo3 (defun three-b (b1 b2 b3) "Select three existing buffers. Put them into three windows, selecting the last one." (interactive "bBuffer1:\nbBuffer2:\nbBuffer3:") (delete-other-windows) (split-window (selected-window) 8) (switch-to-buffer b1) (other-window 1) (split-window (selected-window) 8) (switch-to-buffer b2) (other-window 1) (switch-to-buffer b3)) => three-b (three-b "*scratch*" "declarations.texi" "*mail*") => nil
コマンドループでは、キー列をコマンドへ変換し終えると、
関数command-executeを用いてそのコマンドを起動します。
コマンドが関数であれば、command-executeは引数を読み取り、
コマンドを呼び出すcall-interactivelyを呼びます。
読者自身がこれらの関数を呼び出してもかまいません。
tを返す。
さもなければnilを返す。
対話的呼び出しが可能なオブジェクトには、
(キーボードマクロとして扱われる)文字列やベクトル、
トップレベルでinteractiveを呼び出しているラムダ式、
そのようなラムダ式をコンパイルしたバイトコード関数オブジェクト、
対話的(autoloadの4番目の引数がnil以外)
と宣言された自動ロードオブジェクト、
一部の基本関数が含まれる。
シンボルの関数定義がcommandpを満たせば、
シンボルもcommandpを満たす。
キーやキーマップはコマンドではない。 それらはコマンドを探すために使われる(see section キーマップ)。
commandpの実用的な使用例については、
section 説明文字列の参照のdocumentationを参照。
record-flagがnil以外であると、
コマンドとその引数を無条件にリストcommand-historyに追加する。
さもなければ、引数を読むために
コマンドがミニバッファを使った場合にのみ追加する。
see section コマンド履歴。
もし引数keysを指定すると、コマンドがそれを起動したイベントを 問い合わせたときに与えるイベント列を指定する。
commandpを満たすこと。
つまり、対話的呼び出し可能な関数かキーボードマクロであること。
commandが文字列やベクトルであると、
execute-kbd-macroで実行される。
関数であると、省略可能なrecord-flagとともに関数を
call-interactivelyに渡す。
シンボルは、その関数定義を使って処理する。
autoloadで定義されたシンボルは、
対話的呼び出し可能な関数と宣言されていればコマンドとみなす。
そのような定義では、指定されたライブラリをロードしてから
シンボルの定義を再検査して処理する。
もし引数keysを指定すると、コマンドがそれを起動したイベントを 問い合わせたときに与えるイベント列を指定する。
completing-read(see section 補完)を使って
ミニバッファでコマンド名を読む。
そしてcommand-executeを使って指定されたコマンドを実行する。
コマンドが返した値がexecute-extended-commandの値になる。
コマンドが前置引数を必要とする場合、prefix-argumentの値を受け取る。
execute-extended-commandが対話的に呼ばれた場合、
現在の生の前置引数がprefix-argumentとして使われ、
それが実行するコマンドへ渡される。
execute-extended-commandは通常M-xに定義付けられ、
そのため、プロンプトとして文字列`M-x 'を使う。
(execute-extended-commandを起動するために使われた
イベントをプロンプトにするべきであるが、
それを実装するのは手間がかかる。)
もし前置引数を指定すると、その内容もプロンプトの一部になる。
(execute-extended-command 1)
---------- Buffer: Minibuffer ----------
1 M-x forward-word RET
---------- Buffer: Minibuffer ----------
=> t
interactive-pの呼び出し)を含んだ関数が
call-interactivelyで対話的に呼び出されるとtを返す。
(Lispからcall-interactivelyが呼び出されても、
エディタコマンドループが直接呼び出しても違いはない。)
これを含んだ関数がLispの評価(あるいはapplyやfuncall)で
呼び出された場合は、対話的呼び出しではない。
interactive-pのもっとも一般的な用途は、
情報メッセージを表示するかどうか決めることです。
特別な例外として、キーボードマクロを実行中にはいつでも、
interactive-pはnilを返します。
これは情報メッセージを省いてマクロの実行を速くするためです。
つぎのように使います。
(defun foo ()
(interactive)
(when (interactive-p)
(message "foo")))
=> foo
(defun bar ()
(interactive)
(setq foobar (list (foo) (interactive-p))))
=> bar
;; M-x fooと打つ
-| foo
;; M-x barと打つ
;; これはなにも表示しない
foobar
=> (nil t)
この種のことを行う別の方法は、コマンドを
対話的呼び出しではnil以外の値になる引数print-messageを
取るようにし、その引数がnil以外になるようなinteractive指定を
使うことです。
つぎのようにします。
(defun foo (&optional print-message)
(interactive "p")
(when print-message
(message "foo")))
`p'で与えられる数値前置引数はけっしてnilになりません。
エディタコマンドループは、自身や実行中のコマンドのために 状態記録を数個のLisp変数に設定します。
コマンドが後続のコマンドに対する前置引数を指定する場合を除いて、
コマンドからコマンドループへ戻るとthis-commandから値をコピーする。
この変数は現在の端末に対してつねにローカルであり、 バッファに対してローカルにはならない。 see section 複数ディスプレイ。
last-commandと同様にEmacsがこの変数に設定するが、
Lispプログラムではけっして変更しない。
last-commandと同様に、通常は関数定義を持つシンボルである。
コマンドループは、コマンドを実行する直前にこの変数に設定し、
コマンドが終了すると(コマンドが後続のコマンドに対する
前置引数を指定する場合を除いて)
この値をlast-commandにコピーする。
後続のコマンドに対するフラグとして
実行中にこの変数に設定するコマンドもある。
特に、テキストをキルする関数群はthis-commandにkill-regionを
設定して、直後に続くキルコマンドでは
キルしたテキストをまえのキルに追加するようにする。
特定のコマンドがエラーを起こした場合に
直前のコマンドとは認識されたくない場合には、
読者はそのコマンドがそれを防ぐように書く必要があります。
1つの方法は、以下に示すように、
コマンドの始めでthis-commandにtを設定し、
コマンドの終りでthis-commandに正しい値を戻します。
(defun foo (args...)
(interactive ...)
(let ((old-this-command this-command))
(setq this-command t)
...do the work...
(setq this-command old-this-command)))
letでthis-commandを束縛しません。
というのは、エラーがあるとletは古い値を復元するからです。
これこそがここでは避けたいletの機能です。
(this-command-keys)
;; C-u C-x C-eを使ってこの式を評価する
=> "^U^X^E"
this-command-keysと同様だが、つねにベクトルでイベントを返すため、
文字列に入力イベントを保持する際の複雑さを扱う必要がない
(see section キーボードイベントを文字列で保持する)。
この変数の1つの用途は、
メニューをポップアップする位置をx-popup-menuに指示することである。
y-or-n-p(see section Yes/Noの問い合わせ)も内部的に使っている。
self-insert-commandが使うことである。
last-command-event
;; C-u C-x C-eを使ってこの式を評価する
=> 5
C-eのASCIIコードは5なので、値は5である。
Emacs 18版との互換性のために別名last-command-charがある。
Emacsのコマンドループは、キーボードやマウスのユーザーの操作を表す 入力イベント(input event)列を読みます。 キーボード操作に対するイベントは、文字かシンボルです。 マウスイベントはつねにリストです。 本節では、入力イベントの表現方法やその意味を詳しく説明します。
nil以外を返す。
任意のシンボルがイベントやイベント型として使われることに注意。
eventpは、Lispのプログラムコードがシンボルを
イベントとして使うかどうか区別できない。
そのかわりに、シンボルが、Emacsの現在のセッションにおいて入力として読まれた
イベントに使われたことがあるかどうかを区別する。
シンボルがそのように使われたことがなければ、
eventpはnilを返す。
キーボードからは2種類の入力があります。 普通のキーとファンクションキーです。 普通のキーは文字に対応します。 それらが生成するイベントは、Lispでは文字として表現されます。 文字イベントのイベント型は文字自身(整数)です。 section イベントの分類を参照してください。
入力文字イベントは、0から524287までの基本コード(basic code)と 以下の修飾ビット(modifier bit)の任意の組み合わせです。
読者のプログラム内では、
特定の修飾ビットの値を明示することは避けるのが最良です。
文字の修飾ビットを検査するには、
関数event-modifiers(see section イベントの分類)を使います。
キーバインディングを作るときには、
(`\C-'、`\M-'などの)修飾ビットを伴う文字の
入力表現を使います。
define-keyでキーバインディングを作るときには、
文字の指定には(control hyper ?x)のようなリストを使います
(see section キーバインディングの変更)。
関数event-convert-listは、そのようなリストを
イベント型に変換します(see section イベントの分類)。
ほとんどのキーボードには、ファンクションキー(function key)、
つまり、文字ではない名前や記号のキーがあります。
Emacs Lispでは、ファンクションキーはシンボルで表現されます。
シンボルの(小文字の)名前がファンクションキーのラベルです。
たとえば、F1というラベルのキーを押すと、
入力ストリームにはシンボルf1が置かれます。
ファンクションキーイベントのイベント型は、イベントシンボルそれ自身です。 See section イベントの分類。
ファンクションキーに対するシンボル命名慣習の特例を以下に示します。
backspace, tab, newline, return, delete
tabと
表現することで、EmacsはLispプログラムに区別を伝える。
ほとんどの場面では、これら2つを区別しても有用ではない。
そのため、通常、function-key-map(see section 入力イベントの変換)は、
tabを9に対応付けるようには設定してある。
したがって、文字コード9(文字C-i)に対するキーバインディングは
tabにも適用される。
この種の他のシンボルについても同様である。
関数read-charも同様にこれらのイベントを文字に変換する。
ASCIIでは、BSは実際にはC-hである。
しかし、backspaceは文字コード127(DEL)に変換され、
文字コード8(BS)には変換されない。
ほとんどのユーザーはこれを好む。
left, up, right, down
kp-add, kp-decimal, kp-divide, ...
kp-0, kp-1, ...
kp-f1, kp-f2, kp-f3, kp-f4
kp-home, kp-left, kp-up, kp-right, kp-down
home、left、...のキーに変換する。
kp-prior, kp-next, kp-end, kp-begin, kp-insert, kp-delete
ファンクションキーにもALT、CTRL、HYPER、 META、SHIFT、SUPERの修飾キーを使えます。 それらを表現するには、シンボル名に接頭辞を付けます。
したがって、METAを押し下げたF3キーのシンボルはM-f3です。
複数の接頭辞を使うときには、アルファベット順に書くことを勧めますが、
キーバインディングの探索関数や修飾関数の引数では関係ありません。
Emacsでは4種類のマウスイベント、つまり、クリックイベント、ドラッグイベント、 ボタン押し下げイベント、モーションイベントを扱えます。 すべてのマウスイベントは、リストで表現します。 リストのCARはイベント型であり、 どの修飾キーとともにどのマウスボタンを使ったかを表します。 イベント型では、ダブル(連続2回)/トリプル(連続3回)の 押し下げも区別できます(see section 繰り返しイベント)。 リストの残りの要素は、位置情報と時間情報です。
キーの探索では、イベント型のみが意味を持ちます。
型が同じであれば、異なるイベントでも同じコマンドを実行します。
コマンドでは、対話指定コード`e'を用いてイベントの完全な値を参照できます。
See section interactiveのコード文字。
マウスイベントで始まるキー列は、カレントバッファのキーマップではなく、 マウスが入っているウィンドウのバッファのキーマップを用いて読まれます。 つまり、あるウィンドウ内でクリックしても、 当該ウィンドウやバッファを選択するとは限らず、 その動作はキー列のコマンドバインディングで完全に制御されます。
ユーザーがマウスのボタンを同じ場所で押し下げてから離すと、 クリック(click)イベントが生成されます。 マウスクリックイベントはつぎの形式です。
(event-type (window buffer-pos (x . y) timestamp) click-count)
通常の各要素の意味はつぎのとおりです。
mouse-1、mouse-2、...の1つである。
ファンクションキーの場合と同様に、
アルト、コントロール、ハイパー、メタ、シフト、スーパーの
修飾キーを表す接頭辞`A-'、`C-'、`H-'、`M-'、
`S-'、`s-'も使える。
このシンボルはイベントのイベント型としての役割も果たす。
キーバインディングはイベント型でイベントを指定する。
したがって、mouse-1に対するキーバインディングは、
イベント型event-typeがmouse-1であるすべてのイベントに適用される。
(0 . 0)とした
クリック位置のピクセル単位の座標。
モード行やスクロールバーなどのスクリーンの特別な部分で発生したイベントでは、 buffer-pos、xとyの意味は少々異なります。
スクロールバーの内側でのクリックでは、
buffer-posはシンボルvertical-scroll-barか
horizontal-scroll-barであり、
(x . y)は(portion . whole)に
置き換えられます。
ここで、portionはスクロールバーの先頭や左端からのクリック位置、
wholeはスクロールバー全体の長さです。
モード行やウィンドウwindowを右隣のものと区切る
縦方向の区切り行の内側では、
buffer-posはシンボルmode-lineかvertical-lineです。
モード行では、yは意味のあるデータではありません。
縦方向の区切り行では、xは意味のあるデータではありません。
1つの特別な場面では、 buffer-posは単一のシンボルではなく(上に述べた1つの)シンボルを 含んだリストになります。 イベントに対する仮想的なプレフィックスキーを入力ストリームに挿入すると このようになります。 See section キー列の入力。
Emacsには、ドラッグイベントがあります。 ユーザーがマウスボタンを押し下げてから、 ボタンを離すまえに別の文字位置へマウスを動かすと ドラッグ(drag)イベントが発生します。 マウスのすべてのイベントのように、Lispではドラッグイベントは リストとして表現されます。 つぎのように、リストは開始マウス位置と終了位置を記録しています。
(event-type (window1 buffer-pos1 (x1 . y1) timestamp1) (window2 buffer-pos2 (x2 . y2) timestamp2) click-count)
ドラッグイベントでは、シンボルevent-typeの名前には
接頭辞`drag-'が付きます。
たとえば、ボタン2を押し下げてマウスをドラッグすると
イベントdrag-mouse-2が生成されます。
イベントの2番目と3番目の要素は、ドラッグの開始位置と終了位置を与えます。
なお、データにはクリックイベントと同じ意味があります(see section クリックイベント)。
ドラッグイベントかどうかを区別せずに、
マウスの任意のイベントの2番目の要素は同じ方法で参照できます。
接頭辞`drag-'は、 `C-'や`M-'のような修飾キー接頭辞に続きます。
read-key-sequenceが、
キーバインディングを持たないドラッグイベントを受け取り、かつ、
それに対応するクリックイベントにはバインディングがある場合、
ドラッグイベントの開始位置をクリック位置とするクリックイベントに変換します。
つまり、望まなければ、読者はクリックイベントとドラッグイベントを区別する
必要がありません。
クリックイベントとドラッグイベントは、 ユーザーがマウスボタンを離したときに発生します。 ボタンを離すまではクリックとドラッグを区別する方法がないため、 ボタンを離すまで発生しえません。
ボタンを押し下げたらただちに動作を始めたい場合には、 読者はボタン押し下げ(button-down)イベントを処理する必要があります。 (10) ボタンを押し下げるとただちに発生します。 それらは、シンボルevent-typeの名前に 接頭辞`down-'があることを除けば、 クリックイベント(see section クリックイベント)とまったく同じリストで表現されます。 接頭辞`down-'は、`C-'や`M-'のような修飾キー接頭辞に続きます。
関数read-key-sequenceは、
コマンドバインディングを持たないボタン押し下げイベントを無視します。
したがって、Emacsのコマンドループもそれらを無視します。
つまり、読者がボタン押し下げイベントでなにかをしたいのでなければ、
読者はボタン押し下げイベントを定義する必要はありません。
ボタン押し下げイベントを定義する理由は、
ボタンが離されるまで(モーションイベントを読んで)マウスの動きを
追跡するためです。
See section モーションイベント。
マウスを動かさずに同一のマウスボタンを素早く連続して押し下げると、 Emacsは2回目以降の押し下げに対して 特別な繰り返し(repeat)マウスイベントを生成します。
もっとも一般的な繰り返しイベントはダブルクリック(double-click) イベントです。 ボタンを2回クリックすると、Emcasはダブルクリックイベントを生成します。 (他のすべてのクリックイベントのように)読者がボタンを離したときに イベントが生成されます。
ダブルクリックイベントのイベント型には、接頭辞`double-'が含まれます。
したがって、metaを押し下げて2番目のボタンをダブルクリックすると、
LispプログラムにはM-double-mouse-2が送られます。
ダブルクリックイベントにバインディングがなければ、
対応する普通のクリックイベントを用いて実行します。
したがって、実際に利用したくない限りは、
読者はダブルクリック機能に注意する必要はありません。
ユーザーがダブルクリックすると、Emacsはまず普通のクリックイベントを生成し、 つぎにダブルクリックイベントを生成します。 したがって、ダブルクリックイベントのコマンドバインディングでは、 すでに普通のクリックコマンドが動作済みであると仮定して設計する必要があります。 普通のクリックの結果をもとに望みのダブルクリックの結果を得るようにします。
普通のクリックの意味にダブルクリックの意味を 『追加』するようにすると便利です。 ダブルクリックのユーザーインターフェイスはこのようにすることを勧めます。
ボタンをクリックして、ふたたびボタンを押し下げてそのままマウスを動かすと、 最終的にボタンを離した時点で、ダブルドラッグ(double-drag)イベントが 生成されます。 そのイベント型には`drag'のかわりに`double-drag'が含まれます。 ダブルドラッグイベントにバインディングがなければ、 Emacsは普通のドラッグイベントとしてバインディングを探します。
ダブルクリックイベントやダブルドラッグイベントを生成するまえに、 ユーザーがボタンを2回目に押し下げたとき、 Emacsはダブルダウン(double-down)イベントを生成します。 このイベント型には`down'のかわりに`double-down'が含まれます。 ダブルダウンイベントにバインディングがなければ、 Emacsは普通のボタン押し下げイベントとしてバインディングを探します。 どちらでもバインディングがみつからなければ、ダブルダウンイベントは無視します。
まとめると、ボタンをクリックしてただちに再度ボタンを押し下げると、 Emacsは、はじめのクリックに対してボタン押し下げイベントと クリックイベントを生成し、 再度ボタンを押し下げるとダブルダウンイベントを生成し、 最後にダブルクリックイベントかダブルドラッグイベントを生成します。
ボタンを2回クリックしてから再度押し下げる操作を素早く行うと、 Emacsは、トリプルダウン(triple-down)イベントに続けて トリプルクリック(triple-click)イベントか トリプルドラッグ(triple-drag)イベントを生成します。 これらのイベント型には`double'のかわりに`triple'が含まれます。 トリプルのイベントにバインディングがなければ、 Emacsは対応するダブルのイベントを使います。
ボタンを3回以上クリックしてから再度押し下げると、 3回目以降の押し下げに対するイベントはすべてトリプルのイベントです。 Emacsは、クアドラプル(4回)、クインタプル(5回)、…などの イベントは生成しません。 しかし、イベントリストを調べれば、ボタンを何回押したか正確にわかります。
double-click-timeの値未満(ミリ秒)である必要がある。
double-click-timeにnilを設定すると、
連続したクリックの検出を禁止する。
tを設定すると時間制限をなくし、
Emacsは連続したクリックの検出を位置だけで行う。
Emacsは、ボタン操作を伴わないマウスの移動を表す マウスモーション(mouse motion)イベントを生成することがあります。 マウスモーションイベントはつぎのようなリストで表現されます。
(mouse-movement (window buffer-pos (x . y) timestamp))
リストの2番目の要素は、クリックイベント(see section クリックイベント)と同様に、 マウスの現在位置を表します。
スペシャルフォームtrack-mouseにより、
その本体の内側ではモーションイベントの生成を可能にできます。
フォームtrack-mouseの外側では、
Emacsはマウスの移動のみに対するイベントを生成しないので、
それらのイベントは現れません。
See section マウスの追跡。
ウィンドウシステムは、どのウィンドウにキーボード入力を与えるかを ユーザーが制御するための一般的な方法を提供します。 ウィンドウを選ぶことをフォーカス(focus)と呼びます。 ユーザーがEmacsのフレームを切り替える操作を行うと、 フォーカスイベント(focus event)が生成されます。 グローバルキーマップにあるフォーカスイベントの普通の定義は、 Emcasの新たなフレームを選択するようになっていて、 これはユーザーが期待することです。 See section 入力フォーカス。
Lispでは、フォーカスイベントはつぎのようなリストで表現されます。
(switch-frame new-frame)
ここで、new-frameは切り替え先のフレームです。
Xのほとんどのウィンドウマネージャは、 マウスをウィンドウへ入れるだけで当該ウィンドウにフォーカスが設定される ようになっています。 フレームにマウスが入るとカーソルの形状を変更するので、 Emacsでもそのようにします。 しかし、Lispプログラムにとっては、 なんらかの入力が到着するまではフォーカスの変更について知る必要がありません。 そのため、ユーザーが実際にキーボードのキーを打つか 新たなフレームでマウスボタンを押し下げたときだけ、 Emacsはフォーカスイベントを生成します。 フレーム間でマウスを動かしただけでは、フォーカスイベントは生成されません。
キー列の途中にフォーカスイベントが現れると、キー列を乱します。 そのため、Emacsはキー列の途中にはフォーカスイベントを生成しません。 ユーザーがキー列の途中で、つまり、 プレフィックスキーのあとでフォーカスを変更すると、 複数イベントのキー列のまえかうしろにフォーカスイベントを移動し、 途中には現れないようにEmacsはイベントの順序を並び替えます。
ウィンドウシステム内で起きたことを表す他のイベントもあります。
(delete-frame (frame))
delete-frameの標準定義はフレームframeの削除である。
(iconify-frame (frame))
ignoreである。
というのは、フレームはすでにアイコンになっているので、
Emacsが行うことはなにもないからである。
このイベント型の目的は、
必要ならばその種のイベントを読者が追跡できるようにしておくことである。
(make-frame-visible (frame))
ignoreである。
というのは、フレームはすでに見えるようになっているので、
Emacsが行うことはなにもないからである。
(mouse-wheel position delta)
(drag-n-drop position files)
これらのイベントがキー列の途中、つまり、 プレフィックスキーのうしろに現れると、 複数イベントのキー列のまえかうしろに当該イベントを移動し、 途中には現れないようにEmacsはイベントの順序を並び替えます。
ユーザーが同じ場所でマウスの左ボタンを押し下げてから離すと、 つぎのようなイベント列が生成されます。
(down-mouse-1 (#<window 18 on NEWS> 2613 (0 . 38) -864320)) (mouse-1 (#<window 18 on NEWS> 2613 (0 . 38) -864180))
コントロールキーを押し下げた状態で、 ユーザーがマウスの2番目のボタンを押し下げ、 マウスをつぎの行へドラッグすると、つぎのような2つのイベントが生成されます。
(C-down-mouse-2 (#<window 18 on NEWS> 3440 (0 . 27) -731219))
(C-drag-mouse-2 (#<window 18 on NEWS> 3440 (0 . 27) -731219)
(#<window 18 on NEWS> 3510 (0 . 28) -729648))
メタキーとシフトキーを押し下げた状態で、 ユーザーがマウスの2番目のボタンをウィンドウのモード行で押し下げ、 マウスを別のウィンドウへドラッグすると、 つぎのような2つのイベントが生成されます。
(M-S-down-mouse-2 (#<window 18 on NEWS> mode-line (33 . 31) -457844))
(M-S-drag-mouse-2 (#<window 18 on NEWS> mode-line (33 . 31) -457844)
(#<window 20 on carlton-sanskrit.tex> 161 (33 . 3)
-453816))
各イベントにはイベント型(event type)があって、 キーバインディング処理のためにイベントを分類します。 キーボードイベントでは、イベント型はイベントの値に等しいです。 したがって、文字に対するイベント型は文字であり、 ファンクションキーに対するイベント型はシンボルそのものです。 リストであるイベントでは、イベント型はリストのCARにあるシンボルです。 したがって、イベント型はつねにシンボルか文字です。
イベント型が同じであるイベントは、キーバインディングに関する限り同じです。 つまり、それらは同じコマンドを実行します。 しかし、これは、それらが必ずしも同じことを行うという意味ではありません。 イベント全体を調べてなにを行うかを決定するコマンドもあります。 たとえば、マウスイベントの生起位置を使って、 バッファのどの部分を処理するかを決めるコマンドもあります。
イベントをおおまかに分類すると有用な場合もあります。 たとえば、他の修飾キーやマウスボタンには関係なしに、 METAキーが使われているイベントかどうか調べたいことがあるでしょう。
関数event-modifiersやevent-basic-typeは、
そのような情報を便利に与えるためのものです。
shift、control、
meta、alt、hyper、superである。
さらに、マウスイベントシンボルの修飾子リストには、
必ず、click、drag、downの1つが含まれる。
引数eventは、イベントオブジェクト全体であるか、単なるイベント型である。
例を示す。
(event-modifiers ?a)
=> nil
(event-modifiers ?\C-a)
=> (control)
(event-modifiers ?\C-%)
=> (control)
(event-modifiers ?\C-\S-a)
=> (control shift)
(event-modifiers 'f5)
=> nil
(event-modifiers 's-f5)
=> (super)
(event-modifiers 'M-S-f5)
=> (meta shift)
(event-modifiers 'mouse-1)
=> (click)
(event-modifiers 'down-mouse-1)
=> (down)
クリックイベントに対する修飾子リストにはclickが明示的に含まれるが、
イベントシンボルの名前自体には`click'は含まれない。
(event-basic-type ?a)
=> 97
(event-basic-type ?A)
=> 97
(event-basic-type ?\C-a)
=> 97
(event-basic-type ?\C-\S-a)
=> 97
(event-basic-type 'f5)
=> f5
(event-basic-type 's-f5)
=> f5
(event-basic-type 'M-S-f5)
=> f5
(event-basic-type 'down-mouse-1)
=> mouse-1
nil以外を返す。
(event-convert-list '(control ?a))
=> 1
(event-convert-list '(control meta ?a))
=> -134217727
(event-convert-list '(control super f1))
=> C-s-f1
本節では、マウスボタンイベントやモーションイベント内のデータを 参照するための便利な関数について述べます。
つぎの2つの関数は、以下の形式のリストであるマウスボタンイベントの 開始位置や終了位置を返します。
(window buffer-position (x . y) timestamp)
eventがクリックイベントやボタン押し下げイベントであると、 イベントの位置を返す。 eventがドラッグイベントであると、ドラッグの開始位置を返す。
eventがドラッグイベントであると、 ユーザーがマウスボタンを離したときの位置を返す。 eventがクリックイベントかボタン押し下げイベントであると、 実際の値は開始位置であり、 その種のイベントにある唯一の位置情報である。
つぎの5つの関数は、上に述べた位置情報のリストを引数として、 そのさまざまな部分を返す。
(x . y)として返す。
(col . row)として返す。
これらは実際にはposition内のxとyの値から計算される。
つぎの関数はスクロールバーでのイベントを解読するのに便利です。
(portion . whole)であり、
その比は位置の割合を表す。
(num . denom)であり、
典型的にはscroll-bar-event-ratioが返す値である。
この関数はスクロールバー内での位置を バッファ内での位置へ換算するのに便利である。 つぎのように行う。
(+ (point-min)
(scroll-bar-scale
(posn-x-y (event-start event))
(- (point-max) (point-min))))
スクロールバー内でのイベントには、 xy座標のかわりに比を表す2つの整数があることに注意。
文字列が使われるほとんどの場面では、 文字列にはテキスト文字、つまり、 バッファやファイルにある文字と同じ種類のものが入っていると考えています。 文字列にはキーボード文字が入っているとみなして使うLispプログラムもあります。 たとえば、文字列には、キー列やキーボードマクロの定義が入っているのです。 しかし、キーボード文字を文字列に保持するのは複雑であり、 それは歴史的な互換性を保つためにするのであり、 また、つねに可能とは限りません。
新しいプログラムでは、キーボードイベントを文字列に保持しないで、 このような複雑さを避けるように推奨します。 つぎのようにします。
lookup-keyやdefine-keyに対する
引数以外にも使うつもりならば、文字列のかわりにベクトルを使う。
たとえば、read-key-sequenceのかわりにread-key-sequence-vectorを
this-command-keysのかわりにthis-command-keys-vectorを使う。
define-keyに直接渡す場合であっても、ベクトルで書く。
listify-key-sequence(see section その他のイベント入力機能)を使って、
それをリストに変換しておく。
複雑さの原因は、キーボード入力に含まれる修飾ビットにあります。 メタ修飾子以外の修飾ビットを文字列に入れることは不可能であり、 メタ修飾子は特別な場合として唯一許されているのです。
初期のGNU Emacsでは、メタ文字を128から255の範囲のコードで表現していました。
その当時、基本文字コードは0から127でしたから、
キーボード文字のすべてのコードは文字列に収まったのです。
多くのLispプログラムでメタ文字を表すために文字列定数内で`\M-'を使い、
特に、define-keyや類似の関数に対する引数に使われ、
キー列やイベント列はつねに文字列で表現されていました。
127を超える大きな基本文字コードと追加の修飾ビットを扱えるようにしたとき、 メタ文字の表現方法を変更せざるをえませんでした。 現在、メタ修飾子を表す文字内のビットは であり、そのような数を文字列に入れることはできません。
文字列定数で`\M-'を使っているプログラムを扱えるように、 文字列にメタ文字を入れるための特別な規則があります。 以下は、文字列を入力文字の列として解釈するための規則です。
キーボード入力文字の文字列を作るread-key-sequenceなどの関数は
つぎの規則に従います。
つまり、文字列に収まらないイベントであるときには、
文字列のかわりにベクトルを作ります。
読者が文字列で`\M-'の入力構文を使うと、 それらは128から255の範囲のコードになります。 対応するキーボードイベントを文字列に保存するように変更したときに得られる コードと同じです。 したがって、文字列内のメタイベントは、それらがどのように文字列に 収められたかに関わらず、整合性のある動作をします。
しかし、本節のはじめに述べた推奨方法に従ってこれらのことがらを避けるほうが、 ほとんどのプログラムはよりよく動作するでしょう。
エディタコマンドループは、
関数read-key-sequenceを使ってキー列を読み取ります。
なお、関数read-key-sequenceは関数read-eventを使います。
これらやイベント入力を扱う他の関数は、Lispプログラムからも使えます。
section 一時的な表示のmomentary-string-display、
および、section 時間待ちと入力待ちのsit-forを参照してください。
端末の入力モードの制御や端末入力のデバッグに関する関数や変数については、
See section 端末入力。
入力イベントを読むときにそれらを変換したり修正する機能については、
See section 入力イベントの変換。
上位レベルの入力機能については、section ミニバッファを参照してください。
コマンドループは、read-key-sequenceを呼ぶことで
キー列の入力を読み取ります。
Lispプログラムからこの関数を呼び出してもよく、
たとえば、describe-keyは、
説明対象とするキーを読むためにこの関数を使います。
イベントがすべて文字であり、かつ、それらが文字列に収まるならば、
read-key-sequenceは文字列(see section キーボードイベントを文字列で保持する)を返す。
さもなければ、ベクトルを返す。
ベクトルならば、任意の種類のイベント、つまり、
文字、シンボル、リストを保持できるからである。
文字列やベクトルの要素は、キー列のイベントである。
引数promptは、プロンプトとしてエコー領域に表示する文字列であるか、
あるいは、プロンプトを表示しないことを意味するnilである。
以下の例では、プロンプト`?'がエコー領域に表示され、 ユーザーはC-x C-fと打つ。
(read-key-sequence "?")
---------- Echo Area ----------
?C-x C-f
---------- Echo Area ----------
=> "^X^F"
関数read-key-sequenceは中断を抑止する。
この関数が動作中にC-gを打っても、他の文字と同様に扱い、
quit-flagを設定しない。
see section 中断。
read-key-sequenceと同様であるが、
つねにベクトルとしてキー列を返し、文字列としてはけっして返さない。
see section キーボードイベントを文字列で保持する。
入力文字が大文字であって、それらにキーバインディングがないとき、
対応する小文字にキーバインディングがあれば、
read-key-sequenceは文字を小文字に変換します。
lookup-keyはこのような変換を行わないことに注意してください。
関数read-key-sequenceは、ある種のマウスイベントも変換します。
バインディングのないドラッグイベントをクリックイベントに変換したり、
バインディングのないボタン押し下げイベントを完全に無視します。
さらに、フォーカスイベントとその他のウィンドウイベントを並び替えて、
それらが他のイベントのキー列の途中に現れないようにします。
マウスイベントが、モード行やスクロールバーなどのウィンドウの特別な部分で
生起しても、特別なイベント型はなく、マウスボタンや修飾キーの
組み合わせを普通どおりに表したシンボルです。
ウィンドウのどの部分かに関する情報は、
イベント内の別の部分、つまり、座標に入っています。
しかし、read-key-sequenceは、その情報を
シンボルmode-line、vertical-line、horizontal-scroll-bar、
vertical-scroll-barを用いた仮想的な『プレフィックスキー』に変換します。
ウィンドウの特別な部分におけるマウスクリックの意味は、
これらの仮想的なプレフィックスキーを用いてキー列を定義することで
定義できます。
たとえば、read-key-sequenceを呼び出してから、
ウィンドウのモード行でクリックすると、
つぎのような2つのイベントを得ます。
(read-key-sequence "Click on the mode line: ")
=> [mode-line
(mouse-1
(#<window 6 on NEWS> mode-line
(40 . 63) 5959987))]
コマンド入力用の最低レベルの関数は、単一イベントを読み取る関数です。
promptがnil以外であると、
これはプロンプトとしてエコー領域に表示される文字列であること。
さもなければ、read-eventは
入力待ちであることを示すメッセージを表示せずに、
そのかわりに、現在のコマンドを実行するに至ったイベントや
現在のコマンドが読み取ったイベントをプロンプトとして表示する。
see section エコー領域。
suppress-input-methodがnil以外であると、
このイベントの読み取りに関しては現在の入力方式を使わない。
入力方式の処理をせずにイベントを読みたいときには、
つねにこのようにすること。
input-method-functionを束縛してはならない(下記参照)。
変数cursor-in-echo-areaがnil以外であると、
read-eventは、エコー領域に表示されたメッセージの末尾に
カーソルを一時的に移動する。
さもなければ、read-eventはカーソルを移動しない。
read-eventがヘルプ文字と定義されたイベントを受け取ると、
それを返さずにread-eventがイベントを直接処理してしまう場合がある。
see section ヘルプ機能。
特殊イベント(special event)と呼ばれる他のイベントも
read-eventが直接処理する(see section 特殊イベント)。
read-eventを呼んで右矢印のファンクションキーを押すとつぎのようになる。
(read-event)
=> right
最初の例では、ユーザーは文字1(ASCIIコード49)を打つ。
2番目の例は、eval-expressionを使って
ミニバッファからread-charを呼び出すキーボードマクロの定義である。
read-charはキーボードマクロの直後の文字、つまり、1を読む。
そして、eval-expressionはその戻り値をエコー領域に表示する。
(read-char)
=> 49
;; これを評価するために読者はM-:を使うと仮定する
(symbol-function 'foo)
=> "^[:(read-char)^M1"
(execute-kbd-macro 'foo)
-| 49
=> nil
read-eventは、あれば現在の入力方式も起動します。
input-method-functionがnil以外であれば、
それは関数であるはずです。
read-eventが修飾ビットのない(SPCを含む)印字文字を読み取ると、
引数としてイベントを渡してその関数を呼び出します。
nil以外であると、その値は現在の入力方式関数を指定する。
注意:
この変数をletで束縛しないこと。
この変数はしばしばバッファローカルであり、
入力を読む周囲で束縛すると(読者がこれをもっとも束縛しそうなところ)、
Emacsが入力を待っているときにバッファが非同期に切り替わると、
誤ったバッファに値を復元してしまうことがある。
入力方式関数は、入力として使われるイベントのリストを返すべきです。
(リストがnilであると入力がなかったことを意味し、
read-eventは別のイベントを待つ。)
これらのイベントは、
unread-command-events内のイベントよりまえに処理されます。
入力方式関数が返したイベントは、それらが修飾ビットがない印字文字であっても、
入力方式関数に再度渡されることはありません。
入力方式関数がread-eventやread-key-sequenceを呼び出すときには、
input-method-functionをnilに束縛して
再帰呼び出しを防ぐべきです。
キー列の2番目以降のイベントを読むときには、入力方式関数を呼び出しません。
したがって、それらの文字は、入力方式処理の対象ではありません。
入力方式の処理では、
overriding-local-mapとoverriding-terminal-local-mapの値を
検査するのがよいです。
これらの変数のいずれかがnil以外であるときには、
入力方式ではその引数をリストに入れ、
それ以上処理せずにそのリストを返すべきです。
ユーザーに文字入力を促して、コントロール文字やメタ文字を
文字そのものや文字の8進数コードで手軽に入力できるようにするには、
関数read-quoted-charを使います。
コマンドquoted-insertは、この関数を使っています。
read-charに似ているが、
最初に読んだ文字が8進数字文字(0-7)であると、
任意個数の8進数字文字を読み取り(8進数字文字以外が現れると止める)、
その数値の文字コードが表す文字を返す。
最初の文字を読むと中断を抑制するので、 ユーザーはC-gを入力できる。 see section 中断。
promptを与えると、それはユーザーへのプロンプトを表す文字列を指定する。 プロンプト文字列はつねにエコー領域に表示され、あとに`-'が続く。
つぎの例では、ユーザーは8進数177(10進数では127)を打つ。
(read-quoted-char "What character")
---------- Echo Area ----------
What character-177
---------- Echo Area ----------
=> 127
本節では、イベントを処理せずに『まえもって覗き見』する方法、
処理待ちの入力の有無の検査方法、処理待ちの入力の破棄方法について述べます。
関数read-passwdも参照してください(see section パスワードの読み取り)。
関数でイベントを読んだあとにそれを使わない場面があるため、 この変数が必要になる。 この変数にイベントを保存すると、 コマンドループやコマンド入力を読む関数によって通常どおり処理される。
たとえば、数値前置引数を実現する関数は、任意個数の数字文字を読み取る。 数字文字でないイベントをみつけたら、そのイベントを読み戻して、 コマンドループが通常どおりに読めるようにする必要がある。 同様に、インクリメンタルサーチでは、この機能を使って 探索においては意味を持たないイベントを読み戻す。 なぜなら、そのようなイベントは探索を終了させ、 通常どおり実行される必要があるからである。
unread-command-eventsに入れられるように
キー列からイベントを確実に簡単に取り出す方法は
listify-key-sequenceを使うことである(see section キーボードイベントを文字列で保持する)。
もっとも最近に読み戻したイベントが最初に再度読まれるように、 普通はこのリストの先頭にイベントを追加する。
unread-command-eventsに入れられる。
この変数はほとんど廃れており、
かわりにunread-command-eventsを使うべきである。
Emacs 18版以前向けに書かれたプログラムを扱うためだけに存在する。
tを、
さもなければnilを返す。
入力がないのにtを返すことが稀にある。
以下の例で、Lispプログラムは文字1(ASCIIコード49)を読む。
それがlast-input-eventの値になるが、
(この式を評価するコマンドはC-x C-eと仮定するので)
last-command-eventの値はC-eのままである。
(progn (print (read-char))
(print last-command-event)
last-input-event)
-| 49
-| 5
=> 49
Emacs 18版との互換性のために、別名last-input-charが存在する。
nilを返す。
以下の例で、フォームを評価しはじめてから、ユーザーは何文字か打つ。
sleep-forが待機を終えると、
discard-inputは待機中に打たれた文字をすべて破棄する。
(progn (sleep-for 2)
(discard-input))
=> nil
特殊イベントは、読まれるとただちに非常に低レベルで処理されます。
関数read-eventはこれらのイベントをそれ自身で処理してしまい、
それらを返すことはありません。
このように処理されるイベントは表示されることはなく、
キー列に組み込まれることもなく、
last-command-eventや(this-command-keys)の値に
現れることもありません。
特殊イベントが数値引数を破棄することはなく、
unread-command-eventsで読み戻すことはできません。
特殊イベントがキーボードマクロに現れることはなく、
読者がキーボードマクロを定義しているときに、
特殊イベントがキーボードマクロに記録されることはありません。
しかし、それらのイベントは、
読まれた直後にはlast-input-eventに現れますから、
これからイベントの定義で実際のイベントを見ることができます。
iconify-frame、make-frame-visible、delete-frameの
イベント型は、通常このように処理されます。
特殊イベントをどのように処理するか、
どのイベントが特殊イベントであるかを定義する
キーマップは変数special-event-mapにあります(see section 活性なキーマップ)。
待機関数は、指定時間経過するか入力がくるまで待つように設計してあります。
たとえば、ユーザーに表示を眺める時間を与えるために
計算途中で休止したいでしょう。
sit-forは、休止してスクリーンを更新し、
入力がくるとただちに戻ります。
一方、sleep-forはスクリーンを更新せずに休止します。
input-pending-pを参照)
指定時間だけ休止した場合は、戻り値はtである。
さもなければ、戻り値はnilである。
引数secondsは整数である必要はない。
それが浮動小数点数であると、sit-forは秒の小数も待つ。
秒単位しか扱えないシステムもあり、
そのようなシステムではsecondsを秒に切り下げる。
省略可能な引数millisecは、ミリ秒単位の追加待ち時間を指定する。 これはsecondsで指定した時間に加えられる。 秒未満を扱えないシステムでは、 millisecに0以外を指定するとエラーになる。
入力がくると再表示をつねに取り止め、
再表示開始まえに入力がくると、いっさい再表示しない。
したがって、処理待ちの入力があると、再表示を強制する方法はない。
しかし、処理待ちの入力がなければ、(sit-for 0)で再表示を強制できる。
nodispがnil以外であると、
sit-forは再表示はしないが、
入力がくるとただちに(あるいは指定時間だけ経過すると)戻る。
フレームをアイコンにしたりアイコンにしたフレームを開くと
イベントが生成されるため、sit-forは戻る。
see section ウィンドウシステムのその他のイベント。
sit-forの普通の目的は、
読者が表示したテキストを読む時間をユーザーに与えることである。
nilを返す。
引数secondsは整数である必要はない。
それが浮動小数点数であると、sleep-forは秒の小数も待つ。
秒単位しか扱えないシステムもあり、
そのようなシステムではsecondsを秒に切り下げる。
省略可能な引数millisecは、ミリ秒単位の追加待ち時間を指定する。 これはsecondsで指定した時間に加えられる。 秒未満を扱えないシステムでは、 millisecに0以外を指定するとエラーになる。
遅延を保証したい場合にsleep-forを使う。
現在時刻を取得する関数についてはSee section 時刻。
Lisp関数が動作中にC-gを打つと、 Emacsがなにを行っていても中断を引き起こします。 つまり、もっとも内側の活性なコマンドループに制御が戻ります。
コマンドループがキーボード入力を待っているときにC-gを打っても、
中断を引き起こさずに、普通の入力文字として動作します。
もっとも単純な場合、C-gはコマンドkeyboard-quitを実行しますが、
その効果は中断を引き起こすことですから、読者には区別できないはずです。
しかし、プレフィックスキーに続けてC-gを打つと、
それらは組み合わされて未定義キーになります。
その効果は、前置引数を含めてプレフィックスキーを取り消します。
ミニバッファでは、C-gには別の定義があって、 ミニバッファを強制終了させます。 つまり、ミニバッファから抜け出て中断します。 (単に中断したのでは、ミニバッファ内で コマンドループに戻るだけである。) コマンドループで入力を読んでいるときにC-gで直接中断しない理由は、 このようにミニバッファでその意味を再定義できるようにするためです。 ミニバッファでは、プレフィックスキーに続くC-gは再定義してなく、 プレフィックスキーと前置引数を取り消すという通常の効果を持ちます。 C-gがつねに直接中断するのでは、このようにすることさえ不可能です。
C-gが直接に中断するときには、
変数quit-flagにtを設定します。
Emacsはこの変数を適切なときに検査しnilでないと中断します。
したがって、quit-flagにnil以外を設定すると
中断を引き起こします。
Cのコードのレベルでは、どこでも中断できるわけではありません。
quit-flagを検査している特別な箇所だけです。
このようにするのは、それ以外の箇所で中断すると
Emacsの内部状態に矛盾をきたす可能性があるからです。
中断は安全な場所まで延期されるので、
中断によってEmcasがクラッシュすることはありません。
read-key-sequenceやread-quoted-charなどのある種の関数は、
入力を待っている場合であっても中断を完全に抑制します。
中断するかわりに、C-gは入力として働きます。
read-key-sequenceの場合、コマンドループにおいて
C-gの特別なふるまいをもたらします。
read-quoted-charの場合、
C-qでC-gをクォートできるようになります。
変数inhibit-quitにnil以外の値を束縛することで
Lisp関数のある部分において中断を抑制できます。
そうすると、C-gはそれでもいつもどおり
quit-flagをtにしますが、
その通常の結果である中断は抑制されます。
最終的にフォームletの終りで束縛が解除されるなどして
inhibit-quitが再度nilになります。
その時点でもquit-flagがnil以外であると
要求した中断がただちに起こります。
このふるまいは、プログラムの『臨界領域』では
中断が起こらないことを保証する理想的なものです。
(read-quoted-charなどの)ある種の関数では、
C-gは特別に処理され中断を引き起こしません。
inhibit-quitにtを束縛して入力を読み取り、
inhibit-quitが再度nilになるまえに
quit-flagをnilにすることでそのようにします。
これをread-quoted-charの定義の以下の抜粋で示しましょう。
最初の入力文字のあとで通常の中断を許す方法も示しています。
(defun read-quoted-char (&optional prompt)
"...documentation..."
(let ((message-log-max nil) done (first t) (code 0) char)
(while (not done)
(let ((inhibit-quit first)
...)
(and prompt (message "%s-" prompt))
(setq char (read-event))
(if inhibit-quit (setq quit-flag nil)))
...変数codeに設定する...)
code))
inhibit-quitがnilであれば、
この変数がnil以外であるとEmacsはただちに中断する。
C-gは、inhibit-quitに関わらず、
通常、quit-flagにnil以外を設定する。
quit-flagがnil以外の値に設定されたときに
Emacsが中断すべきかどうかを決定する。
inhibit-quitがnil以外であると、
quit-flagに特別な意味はない。
(signal 'quit nil)でquit条件を通知する。
これは中断と同じことを行う。
(section エラーのsignalを参照。)
中断として使うC-g以外の特殊文字を使えます。
section 端末入力の関数set-input-modeを参照してください。
Emacsのほとんどのコマンドは、前置引数(prefix argument)、
つまりコマンド自身のまえに指定された数を利用できます。
(前置引数とプレフィックスキーを混同しないこと。)
前置引数はつねに値で表現され、
nilであると現在は前置引数がないことを表します。
各コマンドは、前置引数を使ってもよいし、無視してもかまいません。
前置引数には2つの表現方法があります。 生(raw)と数値(numeric)です。 エディタコマンドループでは、内部的には生の表現を使い、 その情報を保持するLisp変数もそのようにしますが、 コマンドではどちらの表現を要求してもかまいません。
生の前置引数の値にはつぎの可能性があります。
nil。
その数値としての値は1であるが、
多くのコマンドではnilと整数1を区別する。
-。
数字文字なしにM--やC-u -を打ったことを表す。
これに等価な数値は-1であるが、
整数-1とシンボル-を区別するコマンドもある。
いろいろな前置引数でつぎの関数を呼び出す例を示します。
(defun display-prefix (arg) "Display the value of the raw prefix arg." (interactive "P") (message "%s" arg))
以下は、生の前置引数でdisplay-prefixを呼び出した結果です。
M-x display-prefix -| nil
C-u M-x display-prefix -| (4)
C-u C-u M-x display-prefix -| (16)
C-u 3 M-x display-prefix -| 3
M-3 M-x display-prefix -| 3 ; (C-u 3と同じ)
C-u - M-x display-prefix -| -
M-- M-x display-prefix -| - ; (C-u -と同じ)
C-u - 7 M-x display-prefix -| -7
M-- 7 M-x display-prefix -| -7 ; (C-u -7と同じ)
Emacsは、前置引数を保持するために2つの変数、
prefix-argとcurrent-prefix-argを使います。
他のコマンド向けに前置引数を設定するuniversal-argumentなどの
コマンドは、前置引数をprefix-argに保持します。
対照的に、current-prefix-argには
現在のコマンドに対して前置引数を運ぶ役割があり、
この変数に設定しても以後のコマンドに対する前置引数には
なんの効果もありません。
通常、コマンドは、interactive宣言により、
「生」か「数値」のいずれの表現の前置引数を使うか指定します。
(See section interactiveの使い方。)
あるいは、変数current-prefix-argにある前置引数の値を
関数から直接見てもかまいませんが、
これは見通しのよい方法ではありません。
nilであると、戻り値は1である。
-であると、戻り値は-1である。
数であると、その数を返す。
リストであると、リストの(数であるはずの)CARを返す。
(interactive "P")を使うことである。
universal-argumentなどの
コマンドは、この変数に設定することで動作する。
つぎのコマンドは、後続のコマンド向けの前置引数を設定するためのものです。 それ以外の目的には呼ばないでください。
Emacsが動作を始めると、Emacsのコマンドループに自動的に入ります。 このトップレベルのコマンドループからはけっして抜けることはなく、 Emacsが動いている限り動作し続けます。 Lispプログラムからコマンドループを起動することもできます。 そうすると、活性なコマンドループが複数作られることになるので、 それを再帰編集(recursive editing)と呼びます。 再帰編集レベルには、それを起動したコマンドを一時休止させ、 当該コマンドを再開するまでユーザーにどんな編集でも許す効果があります。
再帰編集中に利用可能なコマンドは、トップレベルのコマンドループと 同じものでありキーマップで定義されます。 再帰編集を抜けるための数個の特別なコマンドがあり、 完了すると再帰編集レベルから抜ける別のコマンドもあります。 (再帰編集を抜けるコマンドはつねに利用可能であるが、 再帰編集中でないとなにも行わない。)
再帰編集を含むすべてのコマンドループでは、 コマンドループから実行したコマンドのエラーによって コマンドループから抜け出さないように汎用目的のエラーハンドラを設定します。
ミニバッファでの入力は、特別な種類の再帰編集です。 これには、ミニバッファやミニバッファ用ウィンドウを表示するなどの特別な 処理がありますが、読者が考えるよりは少ないのです。 ミニバッファでは特別なふるまいをするキーもありますが、 それらはミニバッファのローカルマップによるものです。 ウィンドウを切り替えると、Emacsの普通のコマンドを使えます。
再帰編集レベルを起動するには、関数recursive-editを呼び出します。
この関数にはコマンドループが含まれています。
さらに、exitを伴ったcatchの呼び出しもあり、
これにより、exitへ投げることで
再帰編集レベルから抜け出せるようになっています
(see section 明示的な非ローカル脱出: catchとthrow)。
t以外の値を投げると、recursive-editは、
呼び出し側の関数へ普通に戻ります。
コマンドC-M-c(exit-recursive-edit)は、これを行います。
値tを投げるとrecursive-editに中断を引き起こし、
1つ上のレベルのコマンドループへ制御を戻します。
これを強制終了(aborting)と呼び、
C-](abort-recursive-edit)で行えます。
ミニバッファを使う場合を除いて、ほとんどのアプリケーションでは 再帰編集を使うべきではありません。 カレントバッファのメジャーモードを 一時的な特別なメジャーモードに変更するほうが、 一般にはユーザーにとってより便利です。 ただし、当該メジャーモードには、 まえのモードに戻るコマンドを用意しておきます。 (rmailのコマンドeは、この方式を使っている。) あるいは、『再帰的に』編集する別のテキストをユーザーに与えたい場合には、 特別なモードの新たなバッファを作成してそれを選択します。 当該モードには、処理を終えてまえのバッファに戻るコマンドを 定義しておきます。 (rmailのコマンドmは、このようにする。)
再帰編集はデバッグに便利です。
ブレークポイントの一種として関数定義に
debugの呼び出しを挿入しておくと、
その箇所に達したときにいろいろと調べられます。
debugは再帰編集を起動しますが、デバッガとしての機能も提供します。
query-replaceでC-rを打ったり、
C-x q(kbd-macro-query)を使ったときにも
再帰編集レベルが使われます。
以下の例では、関数simple-recは、まず1単語分ポイントを進め、
エコー領域にメッセージを表示して再帰編集に入る。
そうすると、ユーザーは望むことはなんでもできるようになり、
(再帰編集を)抜けるためにC-M-cを打つと、
simple-recの実行を継続する。
(defun simple-rec ()
(forward-word 1)
(message "Recursive edit in progress")
(recursive-edit)
(forward-word 1))
=> simple-rec
(simple-rec)
=> nil
(throw 'exit nil)である。
quitを通知することで、
(ミニバッファでの入力を含む)もっとも内側の再帰編集を
要請したコマンドを強制終了する。
その関数定義は実質的には(throw 'exit t)である。
see section 中断。
コマンドを禁止するとは、 コマンドを実行するまえにユーザーの確認を必要とするように コマンドに印を付けることです。 コマンドを禁止するのは、 初心者に混乱をもたらす可能性のあるコマンドに対してや、 コマンドの誤用を防ぐためです。
コマンドを禁止する低レベルの機構は、
コマンドのLispシンボルにnil以外の属性disabledを入れることです。
これらの属性は、通常、ユーザーの`.emacs'ファイルにて
つぎのようなLisp式で設定します。
(put 'upcase-region 'disabled t)
数個のコマンドにはデフォルトでこれらの属性がありますから、 `.emacs'ファイルで属性を取り除きます。
属性disabledの値は文字列であり、
コマンドを禁止したことを表すメッセージです。
たとえばつぎのとおりです。
(put 'delete-region 'disabled
"Text deleted this way cannot be yanked back!\n")
禁止したコマンドを対話的に起動するとどうなるかについて 詳しくはSee section `使用禁止コマンド' in GNU Emacs マニュアル。 コマンドを禁止しても、Lispプログラムから関数として呼び出すことには なんの影響もありません。
this-command-keysを使って
コマンドを実行するためにユーザーがなにを入力したかを調べ、
コマンドそのものを探し出せる。
see section フック。
デフォルトでは、disabled-command-hookには、
ユーザーに処理を進めるかどうかを問い合わせる関数が入っている。
コマンドループでは、実行した複雑なコマンドの履歴を管理していて、
それらのコマンドを簡単に繰り返せるようにしています。
複雑なコマンドとは、ミニバッファを使って引数を読むコマンドです。
これには、任意のM-xコマンド、任意のM-:コマンド、
ミニバッファから引数を読み取るinteractive指定を持つ任意のコマンドが
含まれます。
コマンド自身の実行中に明示的にミニバッファを使っても、
複雑なコマンドとはみなしません。
history-lengthで指定される)最大サイズに達すると
新しい要素を追加するたびに古い要素を削除する。
command-history
=> ((switch-to-buffer "chistory.texi")
(describe-key "^X^[")
(visit-tags-table "~/emacs/src/")
(find-tag "repeat-complex-command"))
この履歴リストは、実際にはミニバッファ履歴(see section ミニバッファの履歴) の特別な場合です。 要素は文字列ではなく式なのです。
まえのコマンドを編集したり取り出すための専用コマンドがたくさんあります。
コマンドrepeat-complex-commandとlist-command-historyは、
ユーザーマニュアル
(see section `ミニバッファコマンドの繰り返し' in GNU Emacs マニュアル)に説明してあります。
ミニバッファ内では、通常のミニバッファ履歴コマンドを使えます。
キーボードマクロは、 コマンドとみなせる入力イベントのまとまった列であり、 キーの定義から構成されます。 Lispにおけるキーボードマクロの表現は、 イベントを含んだ文字列やベクトルです。 キーボードマクロとLispマクロ(see section マクロ)を混同しないでください。
kbdmacroがシンボルであると、 kbdmacroのかわりにその関数定義を用いる。 それがさらに別のシンボルであると、この処理を繰り返す。 最終的な結果は、文字列かベクトルであること。 結果がシンボルでも文字列でもベクトルでもないと、 エラーを通知する。
引数countは繰り返し回数であり、
kbdmacroをその回数だけ実行する。
countを省略するかnilであると、kbdmacroを1回実行する。
countが0であると、
kbdmacroの実行をエラーに出会うか探索に失敗するまで繰り返す。
execute-kbd-macroを使った例についてはsee section 単一イベントの読み取り。
nilであると、現在実行中のマクロはない。
コマンドでこの変数を検査することで、
マクロ実行で起動されたときのふるまいを変更できる。
読者自身はこの変数に設定しないこと。
start-kbd-macroとend-kbd-macroがこの変数に設定する。
読者自身は設定しないこと。
この変数は現在の端末に対してつねにローカルであり、 バッファに対してローカルにはならない。 see section 複数ディスプレイ。
nilである。
この変数は現在の端末に対してつねにローカルであり、 バッファに対してローカルにはならない。 see section 複数ディスプレイ。
Go to the first, previous, next, last section, table of contents.