[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
本章では、GNU Emacsで使われている数個の関数を詳しく調べよう。 つまり「ウォークスルー」をしてみよう。 これらの関数はLispコードの例題として取り上げたが、仮想的な例題ではない。 最初の簡略した関数定義を除いて、GNU Emacsで実際に使っているコードである。 これらの定義から多くのことを学べるはずである。 ここで説明する関数は、すべて、バッファに関連するものである。 それ以外の関数についてはのちほど説明する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
このウォークスルーでは、個々の新しい関数を、あるときは詳しく、 あるときはその概要を説明する。 Emacs Lisp関数に興味を持ったときには、 C-h fに続けて関数名(とRET)を入力すれば、 いつでもその関数の説明文を得られる。 同様に、変数の説明文が必要な場合には、C-h vに続けて 変数名(とRET)を入力すればよい。
また、関数のソースファイルを見るには、関数find-tags
を使う。 M-.(つまり、METAと同時にピリオドキーを押すか、 ESCキーに続けてピリオドキー)をタイプし、 mark-whole-buffer
のようなソースコードを見たい関数の名前を プロンプトに対して入力し、RETをタイプする。 Emacsはソースコードのバッファに切り替えて画面に表示する。 もとのバッファに戻るにはC-x b RETとタイプする。
読者のEmacsの初期設定状態に依存して、 `TAGS'と呼ばれる「タグテーブル(tags table)」を指定する必要もあろう。 読者が使用するのはディレクトリ`emacs/src'であろうから、 コマンドM-x visit-tags-table
を使って、 `/usr/local/lib/emacs/19.23/src/TAGS'のようなパス名を指定する。 読者独自のタグテーブルの作成方法については、 12.5 専用タグファイルの作成方法や See 節 `Tag Tables' in
Emacs Lispに慣れてくると、ソースコードを読む際にはfind-tags
を多用する ようになり、独自のタグテーブルを作成するようになるであろう。
Lispコードを収めたファイルのことをライブラリ(libraries)と呼ぶ。 この用法は、法律図書や技術図書のような特化した図書(ライブラリ)からきている。 各ライブラリ、つまり、ファイルには、 ある特定の目的や動作に関連する関数群を収める。 たとえば、`abbrev.el'は省略入力を、 `help.el'はオンラインヘルプを扱うものである (ある1つの目的のために複数のライブラリがある場合もある。 たとえば、`rmail...'のファイル群には、 電子メールを読むためのコードが収めてある)。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
beginning-of-buffer
の簡略した定義beginning-of-buffer
の簡略した定義"へのコメント(無し)
コマンドbeginning-of-buffer
には十分慣れていて理解しやすいであろうから、 この関数から始めよう。 対話的関数として使うと、beginning-of-buffer
は、 バッファの先頭にカーソルを移動し、それまでカーソルがあった位置にマークを 設定する。 一般にはM-<にバインドしてある。
本節では、もっとも多く使われる形式に簡略した関数を説明する。 簡略版の関数は正しく動作するが、複雑なオプションを処理するコードは含まない。 別の節で、完全な関数を説明する (See 節 5.3 beginning-of-buffer
の完全な定義)。
コードを調べるまえに、関数定義には何が含まれるかを考えてみよう。 M-x beginning-of-bufferやM-<のようなキー列で関数を呼べるように、 関数を対話的にする式を含んでいる必要がある。 バッファのもとの位置にマークを設定するコードが必要である。 バッファの先頭にカーソルを移動するコードも必要である。
では、簡略版の関数のコード全体を示そう。
(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
に 続けて5つの部分がある。
simplified-beginning-of-buffer
である。
()
である。
この関数定義では、引数リストは空である。 つまり、この関数は引数を必要としない (関数の完全な定義では、省略可能な引数を取る)。
interactive
式は、関数が対話的に使われることをEmacsに伝える。 simplified-beginning-of-buffer
は引数を必要としないので、 interactive
に引数はない。
関数の本体はつぎの2行である。
(push-mark) (goto-char (point-min)) |
最初の式は(push-mark)
である。 Lispインタープリタがこの式を評価すると、 カーソルがどこにあってもその現在位置にマークを設定する。 また、このマークの位置はマークリングに保存される。
つぎの行は(goto-char (point-min))
である。 この式はバッファで取りえるポイントの最小位置にカーソルを移動する。 つまり、カーソルの先頭(あるいは、ナロイングしている場合には、 バッファの参照可能な範囲の先頭。 See 節 6. ナロイングとワイドニング)に移動する。
式(goto-char (point-min))
でカーソルをバッファの先頭に移動するまえに、 コマンドpush-mark
でカーソルの位置にマークを設定する。 そのため、必要ならば、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))
を使う。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
mark-whole-buffer
の定義mark-whole-buffer
の定義"へのコメント(無し)
関数mark-whole-buffer
を理解するのは、 関数simplified-beginning-of-buffer
を理解するのと同じくらい容易である。 ここでは、簡略版ではなく完全な関数を見てみよう。
関数beginning-of-buffer
ほどは多用されないが、 関数mark-whole-buffer
も有用である。 関数mark-whole-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 関数名 (引数リスト) "説明文..." (interactive-expression...) 本体...) |
この関数はつぎのように動作する。 関数名はmark-whole-buffer
である。 空の引数リスト`()'がこれに続き、関数には引数を必要としないことを意味する。 さらに、説明文が続く。
つぎの行の式(interactive)
は、関数が対話的に使われることをEmacsに 指示する。 これらの詳細は、前節で述べた関数simplified-beginning-of-buffer
と 同様である。
4.3.1 mark-whole-buffer
の本体Only three lines of code.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
mark-whole-buffer
の本体mark-whole-buffer
の本体"へのコメント(無し)
関数mark-whole-buffer
の本体は、つぎの3行である。
(push-mark (point)) (push-mark (point-max)) (goto-char (point-min)) |
最初の行は、式(push-mark (point))
である。
この行は、関数simplified-beginning-of-buffer
の本体では (push-mark)
と書かれた行とまったく同じことを行う。 いずれの場合も、Lispインタープリタはカーソルの現在位置にマークを設定する。
なぜ、mark-whole-buffer
では(push-mark (point))
と書き、 beginning-of-buffer
では(push-mark)
と書いたのかはわからない。 たぶん、コードを書いた人が、push-mark
の引数を省略でき、 引数がない場合にはpush-mark
はポイント位置に自動的にマークを 設定することを知らなかったのではないかと想像する。 あるいは、つぎの行と同じような構造にしたかったのであろう。 いずれにしても、この行により、Emacsはポイントの位置を調べて、 そこにマークを設定する。
mark-whole-buffer
のつぎの行は(push-mark (point-max))
である。 この式は、バッファで取り得るポイントの最大値の位置にマークを設定する。 これはバッファの最後である(バッファをナロイングしている場合には、 バッファの参照可能な部分の最後である。 ナロイングについてより詳しくは See 節 6. ナロイングとワイドニング)。 このマークを設定すると、ポイントに設定されていた直前のマークはなくなるが、 Emacsはその位置を最近の他のマークと同様に記録しておく。 つまり、必要ならばC-u C-SPCを2回タイプすれば その位置に戻れるのである。
関数の最後の行は、(goto-char (point-min)))
である。 これはbeginning-of-buffer
のときとまったく同じに書いてある。 この式では、カーソルをバッファの最小ポイント つまり、バッファの先頭(あるいは、バッファの参照可能な部分の先頭)に移動する。 この結果、バッファの先頭にポイントがあり、バッファの最後にマークが設定される。 したがって、バッファ全体がリージョンとなる。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
append-to-buffer
の定義append-to-buffer
の定義"へのコメント(無し)
コマンドappend-to-buffer
は、コマンドmark-whole-buffer
と 同様に単純である。 このコマンドは、カレントバッファのリージョン(つまり、バッファのポイントと マークのあいだの部分)を指定したバッファにコピーする。
コマンドappend-to-buffer
は、関数insert-buffer-substring
を 用いてリージョンをコピーする。 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") 本体...) |
関数の最初の行には、関数名と3つの引数がある。 引数は、テキストのコピーを受け取るバッファbuffer
、 カレントバッファのコピーすべきリージョンの 始めstart
と最後end
である。
関数のつぎの部分は説明文であるが、これは明白であろう。
4.4.1 append-to-buffer
のinteractive
式A two part interactive expression. 4.4.2 append-to-buffer
の本体Incorporates a let
expression.4.4.3 append-to-buffer
のsave-excursion
How the save-excursion
works.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
append-to-buffer
のinteractive
式append-to-buffer
のinteractive
式"へのコメント(無し)
関数append-to-buffer
は対話的に使われるので、 関数にはinteractive
式が必要である (interactive
の復習は、 3.3 関数を対話的にするを参照)。 この式はつぎのように読める。
(interactive "BAppend to buffer: \nr") |
この式では、二重引用符のあいだに引数が、`\n'で区切られて2つある。
最初の部分は`BAppend to buffer: 'である。 この`B'は、関数にバッファ名を渡すことをEmacsに指示する。 Emacsは、`B'に続く文字列`Append to buffer: 'をミニバッファに 表示してユーザーに名前を問い合わせる。 そして、Emacsは、指定されたバッファを 関数の引数リストの変数buffer
に束縛する。
改行`\n'は引数の最初の部分と2番目の部分を区切る。 `\n'に続く`r'は、関数の引数リストのシンボル buffer
に続く2つの引数(つまり、start
とend
)に ポイントとマークの値を束縛するようにEmacsに指示する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
append-to-buffer
の本体append-to-buffer
の本体"へのコメント(無し)
関数append-to-buffer
の本体はlet
で始まる。
すでに説明したように(see 節 3.6 let
)、let
式の目的は、 let
の本体の内側のみで使う変数を作り初期値を設定することである。 つまり、let
式の外側で同じ名前の変数があっても、 それらとは混乱しないようにする。
let
式の概略を含んだappend-to-buffer
の雛型を示せば、 関数定義全体にどのようにlet
式をあてはめるかがわかるであろう。
(defun append-to-buffer (buffer start end) "説明文..." (interactive "BAppend to buffer: \nr") (let ((変数 値)) 本体...) |
let
式には3つの要素がある。
let
。
(variable value)
。
let
式の本体。関数append-to-buffer
では、変数リストはつぎのとおりである。
(oldbuf (current-buffer)) |
let
式のこの部分では、式(current-buffer)
が 返す値を変数oldbuf
に束縛する。 変数oldbuf
には、どのバッファを使用していたかを記録する。
Lispインタープリタが変数リストとlet
式の本体とを区別できるように、 変数リストの要素を括弧で囲む。 そのため、変数リストの2要素リストを括弧で囲むのである。 したがって、つぎのようになる。
(let ((oldbuf (current-buffer))) ... ) |
oldbuf
のまえにある2つの括弧のうち、 最初の括弧は変数リストの区切りを表し、 2番目の括弧は2要素リスト(oldbuf (current-buffer))
の始まりを表す ことに注意してほしい。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
append-to-buffer
のsave-excursion
append-to-buffer
のsave-excursion
"へのコメント(無し)
append-to-buffer
のlet
式の本体は、 save-excursion
式から成っている。
関数save-excursion
は、ポイントとマークの位置を記録し、 save-excursion
の本体の式の実行を完了すると これらの位置を復元する。 さらに、save-excursion
はもとのバッファも記録しておき復元する。 append-to-buffer
ではこのようにsave-excursion
を使う。
複数行にまたがるリストは、最初のシンボル以外は最初のシンボルよりも 字下げして書き、Lisp関数もこのように書く。 この関数定義では、つぎに示すように、let
はdefun
よりも字下げし、 save-excursion
はlet
よりも字下げする。
(defun ... ... ... (let... (save-excursion ... |
このような書き方をすると、save-excursion
の本体の2行が 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 本体の最初の式 本体の2番目の式 ... 本体の最後の式) |
この関数では、save-excursion
の本体には2つの式があるだけである。 本体はつぎのとおりである。
(set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end) |
関数append-to-buffer
を評価すると、 save-excursion
の本体の2つの式が順番に評価される。 最後の式の値が関数save-excursion
の値として返される。 これ以外の式は、副作用を起こすためだけに評価される。
save-excursion
の本体の最初の行では、 関数set-buffer
を用いてカレントバッファをappend-to-buffer
の 第1引数で指定したものに切り替える (バッファを切り替えることは副作用である。 まえにも述べたように、Lispでは副作用が主要な目的である)。 2番目の行で、関数の主要な処理を行う。
関数set-buffer
は、Emacsの注意をテキストをコピーする先のバッファに向け、 save-excursion
でもとに戻す。 この行はつぎのとおりである。
(set-buffer (get-buffer-create buffer)) |
このリストのもっとも内側の式は(get-buffer-create buffer)
である。 この式では関数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
は、第1引数で指定したバッファから カレントバッファに文字列をコピーする。 ここでは、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
をこのように使う。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
本章で説明したさまざまな関数の概要をまとめておく。
describe-function
describe-variable
find-tag
save-excursion
save-excursion
の引数を評価し終えたら、 もとの値に戻す。 カレントバッファも記録しておき、終了後にもとに戻す。
push-mark
goto-char
(point-min)
のような位置を返す式のいずれかである。
insert-buffer-substring
mark-whole-buffer
set-buffer
get-buffer-create
get-buffer
get-buffer
は、指定した名前のバッファが 存在しなければnil
を返す。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
simplified-end-of-buffer
の関数定義を書き、動作を確認せよ。
if
とget-buffer
を用いて書け。
find-tag
を用いて、関数copy-to-buffer
のソースを探してみよ。[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |