Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17488] ファイルの概要

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elファイルに対する変更です。go-mode.elは、Emacsエディタ内でGo言語のコードを編集する際に、シンタックスハイライト、インデント、コード補完、Goツールとの連携(例: go test -coverによるカバレッジ表示)などの機能を提供するLispスクリプトです。

具体的には、この変更はGoコードのカバレッジ表示機能に関連する内部変数の扱いを改善しています。

コミット

misc/emacs: 独自の変数ではなく、組み込みのbuffer-base-bufferを使用する

このコミットは、EmacsのGoモードにおけるカバレッジ表示機能において、カバレッジの元となるバッファを特定するために使用していた独自の変数go--coverage-origin-bufferを廃止し、Emacs Lispに組み込まれている関数buffer-base-bufferを使用するように変更するものです。これにより、コードの簡素化とEmacsの標準機能への準拠が図られています。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/b7a609b660a0d656b32a3a2a1e2e0e4420b7f049

元コミット内容

変更前のgo-mode.elでは、go--coverage-origin-bufferというEmacs Lispの変数が定義されており、go--coverage-origin-buffer関数内でこの変数が束縛されているかどうか(boundp)を確認し、もし束縛されていればその値を使用し、そうでなければ現在のバッファ(current-buffer)を返すというロジックになっていました。

(defvar go--coverage-origin-buffer) ; 変数の宣言

(defun go--coverage-origin-buffer ()
  "Return the buffer to base the coverage on."
  (if (boundp 'go--coverage-origin-buffer)
      go--coverage-origin-buffer
    (current-buffer)))

;; 別の場所でこの変数に値をセットしていた
(set (make-local-variable 'go--coverage-origin-buffer) origin-buffer)

このgo--coverage-origin-buffer変数は、カバレッジ情報を表示する際に、どの元のソースコードバッファに基づいてカバレッジを計算・表示するかを追跡するために使用されていました。特に、カバレッジ表示のために一時的に作成される「間接バッファ」の場合、その間接バッファがどの「基底バッファ」から派生したのかを記憶する必要がありました。

変更の背景

この変更の背景には、Emacs Lispの組み込み機能である「間接バッファ(indirect buffer)」と、それに関連するbuffer-base-buffer関数の存在があります。

Emacsでは、一つのバッファの内容を共有しつつ、異なる表示設定やモードを持つ「間接バッファ」を作成することができます。例えば、同じソースコードファイルを複数のウィンドウで開き、それぞれ異なる表示設定(例: 一方はカバレッジ表示、もう一方は通常表示)で作業したい場合に便利です。間接バッファは、その内容を「基底バッファ(base buffer)」と共有します。

元の実装では、間接バッファがどの基底バッファから派生したかを独自にgo--coverage-origin-bufferという変数で管理していました。しかし、Emacs Lispには既にこの目的のための標準的な関数buffer-base-bufferが存在します。この関数は、与えられたバッファが間接バッファであればその基底バッファを返し、そうでなければそのバッファ自身を返します。

したがって、独自の変数でこの情報を管理することは冗長であり、Emacsの標準的な慣習に反していました。このコミットは、既存のEmacsの強力な機能を活用することで、コードの複雑さを減らし、より堅牢で慣用的な実装にすることを目的としています。

前提知識の解説

Emacs Lisp (Elisp)

Emacs Lispは、Emacsエディタの拡張言語であり、Emacsのほぼ全ての機能がこの言語で実装されています。ユーザーはEmacs Lispを使って、Emacsの動作をカスタマイズしたり、新しい機能を追加したりすることができます。

Emacsのバッファ (Buffer)

Emacsにおける「バッファ」は、編集中のテキストを保持するメモリ上のオブジェクトです。ファイルの内容を読み込んだり、新しいテキストを作成したりする際に、必ずバッファが作成されます。各バッファは独立した名前を持ち、それぞれ異なるモード(例: go-modetext-mode)を持つことができます。

間接バッファ (Indirect Buffer)

間接バッファは、Emacsのバッファの一種で、他のバッファ(「基底バッファ」と呼びます)のテキスト内容を共有します。間接バッファと基底バッファは、テキスト内容に関しては常に同期していますが、それ以外の多くのプロパティ(カーソル位置、マーカー、ローカル変数、メジャーモードなど)は独立しています。

間接バッファの主な用途は以下の通りです。

  • 同じ内容の複数表示: 同じファイルの内容を異なる設定(例: 一方は読み取り専用、もう一方は編集可能)で同時に表示したい場合。
  • 一時的なビュー: 特定の目的のために、元のバッファの内容を一時的に表示・操作するが、元のバッファの表示状態には影響を与えたくない場合。Goのカバレッジ表示のように、元のソースコードバッファとは異なる表示(色付けなど)を適用したい場合に特に有用です。

buffer-base-buffer 関数

buffer-base-bufferはEmacs Lispの組み込み関数です。

  • 機能: 引数として与えられたバッファが間接バッファである場合、その基底バッファを返します。
  • 動作:
    • もし引数のバッファが間接バッファであれば、そのバッファが内容を共有している元のバッファ(基底バッファ)を返します。
    • もし引数のバッファが間接バッファでなければ(つまり、それ自身が基底バッファであるか、独立したバッファである場合)、そのバッファ自身を返します。
  • 引数: バッファオブジェクト(省略された場合はcurrent-buffer、つまり現在のバッファが使用されます)。
  • 戻り値: 基底バッファ、または引数のバッファ自身。

この関数は、間接バッファと基底バッファの関係を透過的に扱うための標準的な方法を提供します。

技術的詳細

このコミットの技術的詳細は、Emacs Lispにおけるバッファ管理のベストプラクティスと効率性に関わっています。

元のコードでは、go--coverage-origin-bufferという独自の変数を定義し、make-local-variableを使ってバッファローカル変数として設定していました。これは、カバレッジ表示のために作成される間接バッファが、どの元のソースコードバッファに対応するかを記憶するためのものでした。

しかし、Emacsの設計思想では、特定のバッファが別のバッファの「ビュー」である場合、その関係性はbuffer-base-bufferのような組み込み関数を通じて標準的に表現されるべきです。独自の変数でこの関係を管理すると、以下のような問題が生じる可能性があります。

  1. 冗長性: Emacsが既に提供している機能と同じ目的のコードを重複して書くことになります。
  2. 保守性: Emacsの将来のバージョンでbuffer-base-bufferの動作が改善されたり、新しい関連機能が追加されたりした場合、独自のコードはそれらの恩恵を受けられず、手動で更新する必要が生じる可能性があります。
  3. 一貫性: Emacsの他の部分や他のパッケージがbuffer-base-bufferを使ってバッファの基底を特定している場合、go-modeだけが異なる方法を用いると、予期せぬ相互作用やバグの原因となる可能性があります。
  4. 堅牢性: buffer-base-bufferはEmacsのコア機能として十分にテストされ、最適化されています。独自の変数を介した管理は、エッジケースでの挙動が保証されにくい場合があります。

この変更により、go--coverage-origin-buffer変数の定義と、その変数に値をセットするロジックが削除されました。代わりに、go--coverage-origin-buffer関数は単に(or (buffer-base-buffer) (current-buffer))を返すようになりました。

  • (buffer-base-buffer): 現在のバッファが間接バッファであればその基底バッファを、そうでなければ現在のバッファ自身を返します。
  • or: buffer-base-buffernilを返すことは通常ありませんが、念のため(例えば、何らかの理由でバッファが有効な基底を持たない場合など)、フォールバックとしてcurrent-bufferを使用しています。これにより、常に有効なバッファが返されることが保証されます。

この変更は、go-mode.elがEmacsの標準的なAPIと慣習に沿うようにするための、クリーンアップと改善のコミットと言えます。

コアとなるコードの変更箇所

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -129,7 +129,6 @@
 
 (defvar go-dangling-cache)
 (defvar go-godoc-history nil)
-(defvar go--coverage-origin-buffer)
 (defvar go--coverage-current-file-name)
 
 (defgroup go nil
@@ -1033,9 +1032,7 @@ current coverage buffer or by prompting for it."
 
 (defun go--coverage-origin-buffer ()
   "Return the buffer to base the coverage on."
-  (if (boundp 'go--coverage-origin-buffer)
-      go--coverage-origin-buffer
-    (current-buffer)))\n+  (or (buffer-base-buffer) (current-buffer)))\n \n (defun go--coverage-face (count divisor)\n   "Return the intensity face for COUNT when using DIVISOR
@@ -1129,7 +1126,6 @@ for."
 
     (with-current-buffer (or (get-buffer gocov-buffer-name)\n                              (make-indirect-buffer origin-buffer gocov-buffer-name t))\n-      (set (make-local-variable 'go--coverage-origin-buffer) origin-buffer)\n       (set (make-local-variable 'go--coverage-current-file-name) coverage-file)\n \n       (save-excursion\n```

## コアとなるコードの解説

このコミットでは、主に以下の2つの変更が行われています。

1.  **`go--coverage-origin-buffer`変数の削除**:
    ```diff
    - (defvar go--coverage-origin-buffer)
    ```
    `go--coverage-origin-buffer`という変数の宣言が削除されました。これは、この変数がもはや必要なくなったためです。

2.  **`go--coverage-origin-buffer`関数のロジック変更**:
    ```diff
    - (if (boundp 'go--coverage-origin-buffer)
    -     go--coverage-origin-buffer
    -   (current-buffer)))
    + (or (buffer-base-buffer) (current-buffer)))
    ```
    `go--coverage-origin-buffer`関数の実装が大幅に簡素化されました。
    *   変更前は、`go--coverage-origin-buffer`変数が束縛されているかを確認し、その値を使用するか、現在のバッファを使用するかを条件分岐していました。
    *   変更後は、Emacsの組み込み関数`buffer-base-buffer`を呼び出し、その結果(基底バッファまたは現在のバッファ自身)を直接返します。`or`は、`buffer-base-buffer`が`nil`を返す可能性に備えたフォールバックとして`current-buffer`を指定しています。

3.  **`go--coverage-origin-buffer`変数への値設定箇所の削除**:
    ```diff
    -       (set (make-local-variable 'go--coverage-origin-buffer) origin-buffer)
    ```
    `with-current-buffer`ブロック内で、`go--coverage-origin-buffer`をバッファローカル変数として設定していた行が削除されました。この変数が不要になったため、その設定も不要になりました。

これらの変更により、`go-mode.el`は、カバレッジ表示の基底となるバッファを特定する際に、独自の変数管理ではなく、Emacsの標準的な間接バッファのメカニズムを直接利用するようになりました。これにより、コードがより簡潔になり、Emacsの内部動作との整合性が向上しました。

## 関連リンク

*   [GNU Emacs Lisp Reference Manual: Buffers](https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffers.html)
*   [GNU Emacs Lisp Reference Manual: Indirect Buffers](https://www.gnu.org/software/emacs/manual/html_node/elisp/Indirect-Buffers.html)
*   [GNU Emacs Lisp Reference Manual: `buffer-base-buffer`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer-Properties.html#index-buffer-base-buffer)

## 参考にした情報源リンク

*   コミット情報: `/home/orange/Project/comemo/commit_data/17488.txt`
*   GitHubコミットページ: [https://github.com/golang/go/commit/b7a609b660a0d656b32a3a2a1e2e0e4420b7f049](https://github.com/golang/go/commit/b7a609b660a0d656b32a3a2a1e2e0e4420b7f049)
*   Web検索: "Emacs Lisp buffer-base-buffer" の検索結果 (Google Search)
    *   [https://www.gnu.org/software/emacs/manual/html_node/elisp/Indirect-Buffers.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Indirect-Buffers.html)
    *   [https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer-Properties.html#index-buffer-base-buffer](https://www.gnu.org/software/emacs/manual/html_node/elisp/Buffer-Properties.html#index-buffer-base-buffer)
    *   その他、Emacs Lispのバッファに関する一般的な情報源。