[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
この章では、GNU Emacs で使われている関数を、幾つか詳しく見てみることにす る。こういうことは、"walk-through" と呼ばれる。これらの関数は Lisp コー ドの例として扱うのであるが、最初の単純化された例を除いて、決して「机上」 のものではない。実際に GNU Emacs で使われている現実のコードである。これ らの関数定義から多くのことを学ぶことが出来る。この章で説明する関数は、皆 バッファに関連したものである。他のものについては後で学ぶことにしよう。
そして
4.1 情報の探し方 4.2 簡略版 beginning-of-buffer
の定義goto-char
,point-min
,push-mark
を見てみよう
4.3 mark-whole-buffer
の定義これは beginning-of-buffer
と殆ど同じ4.4 append-to-buffer
の定義これは save-excursion
とinsert-buffer-substring
の利用している。
4.5 復習 4.6 練習問題
この walk-through の中では、新しく出て来た関数はその都度、時には細かく、 時には簡単に、説明することにする。出て来た関数に興味を持った場合は、 C-h f に続けてその関数名をタイプして RET を押せば、どんな Emacs Lisp 関数についても何時でも、完全な説明文を見ることが出来る。同様 に変数についても C-h v に続けてその変数名(と RET) を押せば、 その変数の完全な説明文を見ることが出来る。
また、もしその関数の元のソースを見たくなったら、関数 find-tag
を
使ってその関数に飛ぶことが出来る。まず M-. とタイプし (即ち、
META キーとピリオドキーを同時に押すか、または ESC キーを押し
てからピリオドキーを押し), 出てきたプロンプトに対してソースコードを見た
い関数の名前、例えば mark-whole-buffer
等、をタイプする。すると、
Emacs はバッファを切り替えて、その関数のソースコードをスクリーンに表示し
てくれる。元のバッファに戻るには、C-x b RET とタイプすれば良
い。
あなたの使っている Emacs のデフォルトの初期値いかんでは `タグテーブル'
を指定する必要があるかもしれない。これは `TAGS' と呼ばれるファイル
である。`emacs/src' ディレクトリにあるファイルを指定することになる
場合が殆どであろう。その場合 M-x visit-tags-table
コマンドを使っ
て、
`/usr/local/lib/emacs/19.23/src/TAGS'
などのようにパス名を指定する。section `Tag Tables' in The GNU Emacs Manual, 参照。 また、自分自身のものを作成する方法については 自分自身の `TAGS' ファイルの作成, を見よ。
Emacs Lisp に慣れてしまってからは、ソースコードを眺めるために頻繁に
find-tag
を使うことになるだろう。そして自分自身の `TAGS' テー
ブルを作ることになるはずだ。
ついでに付け加えておくと、Lisp コードを含むファイルは便宜上ライブラリ
(library
) と呼ばれる。このメタファは、一般的なライブラリではなく、
例えば法律とか工学とかのような特定分野のライブラリから来ている。各々の
ライブラリないしはファイルには特定のトピックや動作に関する関数が含まれて
いる。例えば、`abbrev.el' には省略等のショートカットキーを扱う関数
が、また `help.el' にはオンラインヘルプを扱う関数が収められている、
といった感じである。(時には複数のライブラリが一つの動作に関するコードを
提供していることもある。電子メールを読むためのコードを提供する
`rmail...' ファイルなんかがそうだ。) The GNU Emacs
Manual
を見れば、「C-h p コマンドによって標準的な Emacs Lisp ライ
ブラリをトピックをキーワードにして検索することが出来る」と言った記述を
見つけることが出来る。
beginning-of-buffer
の定義
まず beginning-of-buffer
という関数から始めるのが良いであろう。こ
の関数には既に慣れているだろうし、理解するのも易しいからだ。インタラクティ
ブなコマンドとして使われるものであろうと、beginning-of-buffer
は
カーソルをバッファの先頭に移動させ、以前の位置にマークを置く。この関数は
大抵は M-< にバインドされている。
この章では、この関数の短縮されたバージョンを説明することで、これがどうい
うふうに使われることが多いのかを示そう。この短縮版も上に書いたような動作
をするが複雑なオプションは付いていない。完全版については別のセクションで
説明することにする。(beginning-of-buffer
の完全な定義, 参照。)
コードを見る前に、どんな関数定義が含まれているかを考えてみよう。まず、関 数をインタラクティブにするための、即ち M-x beginning-of-buffer と タイプしたり、あるいは M-< といったキーコードをタイプすることで呼 び出せるようにするためのS式を含んでいなければならない。また、バッファの 元の位置にマークするためのコードも必要だ。更にカーソルをバッファの先頭に 移動するコードも入っていなくてはならない。
これがこの関数の短縮版の完全なテキストである。
(defun simplified-beginning-of-buffer () "Move point to the beginning of the buffer; leave mark at previous position." (interactive) (push-mark) (goto-char (point-min))) |
他の関数定義と同様に、この定義も特殊形式 defun
に続く五つの部
分からなっている。
simplified-beginning-of-buffer
。
()
この関数定義では、引数リストは空である。これは、この関数が引数を必要とし ないことを意味する。(もっとも完全版の方の定義を見れば、そちらにはオプショ ンの引数を渡すことが出来ることが分る。)
インタラクティブにするためのS式は、Emacs にこの関数がインタラクティブに
使われることを伝えるための物である。今の場合 interactive
は引数を
持たない。というのも simplified-beginning-of-buffer
は引数を必要
としないからである。
関数の本体部分は次の二行からなる。
(push-mark) (goto-char (point-min)) |
一行目は (push-mark)
というS式である。このS式が Lisp インタプリ
タによって評価されると、カーソルが何処にあろうと、その位置にマークが設定
される。マークの位置はマークリングに保存される。
二行目は (goto-char (point-min))
である。このS式はカーソルをバッ
ファ内での最小ポイント、即ちバッファの先頭に移動する。(あるいは、もし
ナローイングがかかっていれば、アクセス出来る範囲内での最初の位置に移動す
る。ナローイングとワイドニング, 参照。)
push-mark
コマンドによって、(goto-char (point-min))
でカー
ソルがバッファの先頭に飛ぶ前に、そのカーソルの位置にマークがセットされる。
結果として、もし元の位置に戻ろうと思えば、C-x C-x によって元の位置
に戻ることが出来る。
これがこの関数定義の全てである!
もし、このようなコードを読んでいて goto-char
のようによく知らない
関数に出くわしたとする。その場合には describe-function
コマンドを
使ってこれがどんな関数かを見ることが出来る。このコマンドを使うには、まず
C-h f とタイプし、次に知りたい関数の名前をタイプしてから RET
を押せば良い。describe-function
コマンドは `*Help*' ウィンド
ウにその関数の説明文字列を表示してくれる。例えば goto-char
の説明
文は次の通りである。
One arg, a number. Set point to that number. Beginning of buffer is position (point-min), end is (point-max). |
(describe-function
はプロンプトを出す時にデフォルトの値としてその
時のカーソルの直前にあるシンボルの値を設定する。従って、カーソルをその関
数の直後に持っていって C-h f RET とタイプすることも出来る。)
end-of-buffer
関数についても beginning-of-buffer
とほぼ同
じである。ただし、関数の本体部分で、(goto-char (point-min))
の部
分が (goto-char (point-max))
となっている。
mark-whole-buffer
の定義
関数 mark-whole-buffer
も、関数
simplified-beginning-of-buffer
と同じくらい簡単に理解出来る。
ただし、今回は単純化したものではなく完全な関数を見ることにする。
mark-whole-buffer
関数は beginning-of-buffer
関数ほどには
頻繁には使われない。しかし、同じくらい有用である。この関数はポイントをバッ
ファの先頭に、マークをバッファの最後に置くことで、バッファ全体をリージョ
ンとして指定する。また、大抵は C-x h にバインドされている。
この関数の完全なコードは次の通りである。
(defun mark-whole-buffer () "Put point at beginning and mark at end of buffer." (interactive) (push-mark (point)) (push-mark (point-max)) (goto-char (point-min))) |
他の全ての関数と同様、mark-whole-buffer
も関数定義のテンプレート
にきちんと当てはまっている。このテンプレートは次のようなものだった。
(defun 関数名 (引数リスト) "説明文字列..." (インタラクティブ式...) 本体...) |
この関数の動作であるが、関数名は mark-whole-buffer
である。引数リ
ストが空リスト `()' になっているので、この関数は引数を必要としない
ことが分る。次に説明文字列が来ている。
次の行は (interactive)
である。これをつけると Emacs はこの関数を
インタラクティブに使えるものとみなす。ここら辺のことは前節の
simplified-beginning-of-buffer
関数の所でも説明した通りである。
4.3.1 mark-whole-buffer
の本体本体は三行しかない
mark-whole-buffer
の本体
関数 mark-whole-buffer
の本体は次の三行からなる。
(push-mark (point)) (push-mark (point-max)) (goto-char (point-min)) |
この三行のうち最初の行はS式 (push-mark (point))
である。
この行は関数 simplified-beginning-of-buffer
の本体の最初の行の
(push-mark)
と全く同じ働きをする。どちらの場合にも Lisp インタプ
リタはカーソルの現在の位置にマークを設定する。
私には何故 mark-whole-buffer
関数では (push-mark (point))
と書かれ、beginning-of-buffer
関数では (push-mark)
と書か
れているか分らない。多分、このコードを書いた人は (push-mark)
の引
数は省略可能で、引数を受け取らない場合、デフォルトでは自動的に現在のポイ
ントの位置にマークが設定されるということを知らなかったんだろうと思う。あ
るいはこのS式が次の行と同じような構造になるように書いたのかもしれない。
いずれにしてもこの行によって Emacs はポイントの位置を決定し、その位置に
マークを設定する。
mark-whole-buffer
の次の行は (push-mark (point-max))
であ
る。このS式は、バッファの中でポイントの数が最大の位置にマークを設定する。
これは大抵はバッファの最後である。(もし、バッファがナローイングされてい
れば、バッファの中のアクセス可能な範囲での最後尾になる。ナローイングにつ
いての詳細は ナローイングとワイドニング,
参照。) このマークが設定された時点で、現在のポイントの位置に設定されてい
た、以前のマークは解除される。しかし、Emacs は最近設定されたマークの位置
と同様、その位置を記憶している。もし戻りたければ、C-u C-SPC
を二度タイプすることで、その位置に戻ることが出来る。
さて、いよいよ最後の行の (goto-char (point-min))
である。これは
beginning-of-buffer
と全く同じように書かれている。このS式はカー
ソルをバッファの中でポイントが最小の位置に移動させる。つまり、バッファの
先頭(もしくはアクセス可能な範囲での先頭)に移動させるのである。その結果、
ポイントはバッファの先頭に移動し、バッファの最後尾にマークが設定される。
このようにして、バッファ全体がリージョンになる。
append-to-buffer
の定義
append-to-buffer
コマンドも mark-whole-buffer
コマンドと同
じくらい単純な構造をしている。このコマンドがすることはカレントバッファの
リージョン(つまり、バッファ中のポイントとマークある部分) を指定したバッ
ファに複写することである。
append-to-buffer
コマンドは、リージョンを複写するのに関数
insert-buffer-substring
を使う。この関数は名前から察せられる通り、
バッファのある部分から文字からなる文字列、即ち「部分文字列」を取り出して、
それを他のバッファに挿入する。関数 append-to-buffer
の大部分は
insert-buffer-substring
がうまく動作するような状態に設定すること
に関するものである。このコードはテキストが写されるバッファとそのコピー元
となるリージョンの両方の指定が必要となる。この関数の完全なコードは次の通
りである。
(defun append-to-buffer (buffer start end) "Append to specified buffer the text of the region. It is inserted into that buffer before its point. When calling from a program, give three arguments: a buffer or the name of one, and two character numbers specifying the portion of the current buffer to be copied." (interactive "BAppend to buffer: \nr") (let ((oldbuf (current-buffer))) (save-excursion (set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end)))) |
この関数は一連のテンプレートが埋められたものと見倣すと理解しやすい。
最も外側のテンプレートは、関数定義のものである。今の場合、次のようになっ ている(幾つか既に埋めてあるスロットもある)。
(defun append-to-buffer (buffer start end) "説明文字列..." (interactive "BAppend to buffer: \nr") 本体...) |
最初の一行は関数の名前とその三つの引数を含んでいる。引数はテキストが複写
される行き先のバッファ及び、複写元のリージョンを指定する start
と
end
である。
関数の次の部分には簡潔で完全な説明が記されている。
4.4.1 インタラクティブ式 append-to-buffer
二つの部分からなるインタラクティブ式 4.4.2 append-to-buffer
の本体let
式との組み合わせ4.4.3 append-to-buffer
の中のsave-excursion
save-excursion
はどう働くか
append-to-buffer
append-to-buffer
という関数はインタラクティブに使われる関数なので、
interactive
式が必要である。(interactive
について復習する
には、関数をインタラクティブにする, を見よ。)
(interactive "BAppend to buffer: \nr") |
このS式は二重引用符に囲まれた引数を持ち、その引数は `\n' によって 分けられた二つの部分からなっている。
一つ目の部分は `BAppend to buffer:' である。ここで、`B' は
Emacs に対し、この関数に渡すバッファ名を尋ねるように要求している。これに
よって Emacs はミニバッファに `B' に続く文字列---今の場合は
`Append to buffer: '---からなるプロンプトを出して、ユ-ザーに名前
を入力するよう促す。Emacs はこの関数の引数リストにある変数 buffer
に、そこで指定されたバッファをバインドするのである。
改行コード `\n' は引数の最初の部分と二つ目の部分を分けるために用
いられる。`\n' の次には `r' が続いているが、これは関数の引数リ
ストの中の buffer
に続く二つのシンボル (つまり、start
と
end
) にポイントとマークの値をバインドすることを示している。
append-to-buffer
の本体
関数 append-to-buffer
の本体部分は let
から始まっている。
以前見たように (let
, を参照) let
式の目的は、
let
式内部だけで使われる変数を一つないしは複数作り、それに初期値
を与えることである。これは、そのような変数は let
式外部の同じ名前
の変数と混同されることがない、ということを意味している。
let
式の部分をアウトラインにして append-to-buffer
のテンプ
レートを書いてみる。これによって let
式がどういうふうに関数に組み込
まれているかが分る。
(defun append-to-buffer (buffer start end) "説明文字列..." (interactive "BAppend to buffer: \nr") (let ((変数 値)) 本体...) |
let
式は三つの要素を持っている。
let
。
(variable
value)
が一つあるだけ。
let
式の本体。
関数 append-to-buffer
の中では変数リストは次のようになっている。
(oldbuf (current-buffer)) |
let
式のこの部分では、変数 oldbuf
が
(current-buffer)
を評価して返された値にバインドされる。変数
oldbuf
は現在作業しているバッファの記録のために使われるものである。
変数リストの要素は一組の括弧に囲まれている。これによって Lisp インタプリ
タは変数リストと let
式の本体を区別出来るのである。その結果として
変数リストの中の、二つの要素からなるリストであるような要素は更に一組の括
弧によって囲まれることになる。ということで、この行は次のようになる。
(let ((oldbuf (current-buffer))) ... ) |
ひょっとすると oldbuf
の前に二つの括弧があることに驚くかもしれな
いが、oldbuf
の前の最初の括弧は変数リストの区切りを示すものであり、
次の括弧は二つの要素からなるリスト (oldbuf (current-buffer))
の最
初の括弧であることさえ分れば問題ないだろう。
append-to-buffer
の中の save-excursion
append-to-buffer
の中の let
式の本体は
save-excursion
式からなっている。
関数 save-excursion
はポイントとマークの位置を保存し、
save-excursion
の本体部分のS式の実行が完了した時点でそれらを元の
位置に戻す。更に save-excursion
は元々のバッファが何であったかも
憶えていて、そのバッファに戻してくれる。save-excursion
は
append-to-buffer
の中でこのように使われている。
ついでだが、ここで Lisp の関数の書式としては、通常複数の行に広がって括弧
で閉じられているようなものは全て、最初のシンボルよりも深くインデントされ
ていることに注意しておいた方が良いだろう。今回の関数定義の中では次のよう
に let
式は defun
よりも深くインデントされている。
(defun ... ... ... (let... (save-excursion ... |
このように書式を工夫することで、save-excursion
の本体部分の二行が
save-excursion
に付随する括弧で囲まれていることが簡単に見てとれる。
同様に、save-excursion
それ自身が let
に付随する括弧で囲ま
れていることもすぐに分る。
(let ((oldbuf (current-buffer))) (save-excursion (set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end)))) |
関数 save-excursion
の使い方は、次のテンプレートのスロットが埋め
られているものだと思うとよく理解出来るだろう。
(save-excursion 本体の最初の式 本体の二番目の式 ... 本体の最後の式) |
この関数では save-excursion
には二つのS式しか含まれていない。そ
の本体は次の通りである。
(set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end) |
関数 append-to-buffer
が評価されると、save-excursion
の本体
部分の二つのS式が順に評価される。最後のS式の値が save-excursion
関数の値として返される。もう一方のS式は単に副作用だけのために評価される。
save-excursion
の本体の最初の行は、現在のバッファを
append-to-buffer
の引数の最初の引数で指定されたものに切り替えるた
めに関数 set-buffer
を使っている。(バッファの切替は副作用である。
前にも言ったように Lisp にとっての副作用こそが我々の主目的であることが多
い。) 二行目がこの関数の本来の作業を行う。
関数 set-buffer
は Emacs の注意をテキストをコピーしようとしている
バッファの方に向けさせる。そのバッファから save-excursion
によっ
て元のバッファに帰ってくるのである。
(set-buffer (get-buffer-create buffer)) |
このリストのもっとも内側にあるS式は (get-buffer-create buffer)
というものである。このS式は get-buffer-create
関数を使っている。
これはその名前のバッファの内容を取り込むか、もし存在しない場合は新しくそ
の名前を持つバッファを作成する。これは append-to-buffer
関数を使
うことで、それ以前には存在してなかったバッファにもテキストを出力すること
が出来ることを示している。
get-buffer-create
はまた set-buffer
が不必要なエラーに遭遇
するのを防いでもいる。つまり set-buffer
を使う時は行き先のバッファ
が存在している必要があるのだ。もしありもしないバッファを指定したりすると
Emacs はそこで仕事をやめてしまう。存在しないバッファである場合には
get-buffer-create
がそのバッファを作ってくれるので、
set-buffer
は常にバッファを得ることが出来るというわけである。
append-to-buffer
の最後の行はテキストを追加する働きをする。
(insert-buffer-substring oldbuf start end) |
関数 insert-buffer-substring
は最初の引数で指定されたバッファ
から文字列をコピーし、それを現在のバッファに挿入する。今の場合は
insert-buffer-substring
の引数は let
で生成されバインドさ
れた変数 oldbuf
の値であり、これは append-to-buffer
コマン
ドを実行した時点でのカレントバッファである。
insert-buffer-substring
が作業を終えると、save-excursion
が元のバッファに戻してくれる。そうして append-to-buffer
の仕事は
完了する。
骨組みだけ書くと、本体部分がやっていることは次の通りである。
(let ( |
以上まとめると、append-to-buffer
は次のような働きをする。まずはカ
レントバッファの値を oldbuf
という変数に保存する。次に別のバッファ
をユーザーから聞き出し、もし必要なら新規に作成して、そのバッファに移る。
oldbuf
の値を利用して元のバッファのリージョン内のテキストを取り出
し新しいバッファに挿入する。そして最後に save-excursion
を用いて
元のバッファに戻る。
append-to-buffer
を見ていく中で、かなり複雑な関数を探検したことと
思う。let
や save-excursion
の使い方も見られたし、複数のバッ
ファ間の行き来の仕方も見た。他の多くの関数定義の中でもこのようにして
let
, save-excursion
, そして set-buffer
を利用している。
ここで、この章で議論した様々な関数について簡単にまとめておく。
describe-function
describe-variable
find-tag
save-excursion
save-excursion
の引数が評価された
後で、それらの値を元に戻す。また、カレントバッファも憶えていて、そのバッ
ファに戻してくれる。
push-mark
goto-char
(point-min)
のように位置を表わす数値を返すようなS式でなけれ
ばならない。
insert-buffer-substring
mark-whole-buffer
set-buffer
get-buffer-create
get-buffer
get-buffer
はもしその名前のバッファが無い場合には nil
を返す。
simplified-end-of-buffer
を書きなさい。そして、それ
が実際に動くことかどうかテストしてみなさい。
find-tag
を利用して、copy-to-buffer
関数のソースを見つけな
さい。
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |