GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/eintr/append-save_002dexcursion.html ・ emacs-doc.conao3.com

4.4.3 append-to-buffer 内の save-excursion

append-to-buffer における let 式の本体は、save-excursion 式で構成される。

save-excursion 関数はポイントの位置を保存し、save-excursion の本体内の式の実行が完了した後にその位置を復元します。さらに、save-excursion は元のバッファを追跡し、それを復元します。このようにして、save-excursion が append-to-buffer で使用されます。

ちなみに、ここで注目すべき点は、Lispの関数は通常、複数行にわたる広がりの中に囲まれたすべての要素が、最初の記号よりも右側にインデントされるように記述されるということです。この関数定義では、let は defun よりも、save-excursion は let よりも、次のようにインデントされています。

(defun ...
  ...
  ...
  (let...
    (save-excursion
      ...

この書式規則により、save-excursionの本文中の行がsave-excursionに対応する括弧で囲まれていることが容易に分かります。同様に、save-excursion自体もletに対応する括弧で囲まれています:

(let ((oldbuf (current-buffer)))
  (save-excursion
    ...
    (set-buffer ...)
    (insert-buffer-substring oldbuf start end)
    ...))

save-excursion関数の使用は、テンプレートの空欄を埋めていくプロセスと見なすことができます:

(save-excursion
  first-expression-in-body
  second-expression-in-body
   ...
  last-expression-in-body)

この関数では、save-excursion の本体には let* 式という1つの式のみが含まれています。let 関数についてはご存知かと思います。let* 関数は異なります。この関数により、Emacs は varlist 内の各変数を順次、一つずつ設定できるようになります。これにより、varlist の後半にある変数が、それより前に Emacs が設定した値を利用できるようになります。

append-to-buffer 内の let* 式を見ると:

(let* ((append-to (get-buffer-create buffer))
       (windows (get-buffer-window-list append-to t t))
       point)
  BODY...)

append-to が (get-buffer-create buffer) の戻り値にバインドされていることがわかります。次の行では、append-to が get-buffer-window-list の引数として使用されていますが、これは let 式では不可能です。なお、point は let 文の場合と同様に、自動的に nil にバインドされる点に注意してください。

それでは、let*式の本体にある関数set-bufferとinsert-buffer-substringに注目してみましょう。

昔は、set-buffer という表現は単に

(set-buffer (get-buffer-create buffer))

だが今は

(set-buffer append-to)

これは、let* 式のより前の箇所で append-to が (get-buffer-create buffer) に束縛されていたためです。

append-to-buffer 関数定義は、現在使用しているバッファから名前付きバッファへテキストを挿入します。たまたま insert-buffer-substring はその逆、つまり別のバッファから現在のバッファへテキストをコピーします。そのため、append-to-buffer の定義は、ローカルシンボル oldbuf を current-buffer が返す値にバインドする let から始まるようになっています。

insert-buffer-substring 式は次のようになります。

(insert-buffer-substring oldbuf start end)

insert-buffer-substring 関数は、第1引数で指定されたバッファから文字列をコピーし、その文字列を現在のバッファに挿入します。この場合、insert-buffer-substring の引数は、let によって作成・バインドされた変数の値、すなわち append-to-buffer コマンドを実行した際に現在のバッファであった oldbuf の値となります。

insert-buffer-substring の処理が完了すると、save-excursion がアクションを元のバッファに復元し、append-to-buffer が処理を完了します。

骨格形式で記述すると、身体の仕組みは次のようになります。

(let (bind-oldbuf-to-value-of-current-buffer)
  (save-excursion                       ; Keep track of buffer.
    change-buffer
    insert-substring-from-oldbuf-into-buffer)

  change-back-to-original-buffer-when-finished
let-the-local-meaning-of-oldbuf-disappear-when-finished

まとめると、append-to-bufferは次のように動作します。まず、現在のバッファの値をoldbufという変数に保存します。次に、新しいバッファを取得し(必要に応じて作成し)、Emacsのフォーカスをそこへ切り替えます。oldbufの値を用いて、旧バッファのテキスト範囲を新バッファに挿入し、最後にsave-excursionを用いて元のバッファに戻ります。

append-to-bufferを見ると、かなり複雑な関数について学ぶことができます。この関数では、letやsave-excursionの使い方や、別のバッファへの切り替えと復帰の方法が示されています。多くの関数定義において、let、save-excursion、set-bufferがこのように使われています。