[インデックス 17289] ファイルの概要
このコミットは、Go言語のEmacsモード(go-mode.el
)におけるgodef
ツールの挙動を改善するものです。具体的には、コードカバレッジバッファでgodef
を使用した場合の動作を修正し、より堅牢なエラーハンドリングを導入しています。
コミット
commit 0b25ba9ced9d0dda8a73545a2cc3eb377515d9c7
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date: Fri Aug 16 00:22:38 2013 -0400
misc/emacs: allow godef to work in coverage buffers
Jumps to the same file will use the original buffer, not the
coverage buffer. Making it work for the coverage buffer isn't
worth the trouble, especially because it would break as soon as
you jump to a different file and back.
Use error instead of message so it actually terminates
R=adonovan
CC=golang-dev
https://golang.org/cl/13041043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b25ba9ced9d0dda8a73545a2cc3eb377515d9c7
元コミット内容
misc/emacs: allow godef to work in coverage buffers
このコミットは、EmacsのGoモードにおいて、godef
コマンドがコードカバレッジバッファ内で動作するように変更を加えるものです。同じファイルへのジャンプは、カバレッジバッファではなく元のバッファを使用するようにします。カバレッジバッファで完全に機能させることは、別のファイルにジャンプして戻ると壊れる可能性があるため、その手間をかける価値はないと判断されています。また、エラーメッセージの表示にmessage
ではなくerror
を使用することで、処理が適切に終了するように修正されています。
変更の背景
Go言語の開発において、godef
は非常に便利なツールであり、コード内の識別子の定義元にジャンプする機能を提供します。EmacsのGoモードは、Go開発者がEmacsをIDEのように利用するための重要なコンポーネントです。
しかし、Goのテストツールにはコードカバレッジを生成する機能があり、その結果は通常、元のソースコードにカバレッジ情報がオーバーレイされた「カバレッジバッファ」としてEmacs内に表示されます。このカバレッジバッファは、元のファイルとは異なる一時的なバッファとして扱われるため、godef
のようなファイルパスに依存するツールが正しく機能しないという問題がありました。
具体的には、godef
は現在のバッファのファイル名を基に動作するため、カバレッジバッファのファイル名(通常は存在しないか、一時的なもの)を参照してしまうと、定義元を見つけられなくなっていました。このコミットは、この問題を解決し、カバレッジバッファでgodef
を使用した場合でも、元のソースファイルのコンテキストで定義元を検索できるようにすることを目的としています。
また、既存の実装では、エラー時にmessage
関数を使用しており、これは単にメッセージを表示するだけで、Lispの処理を中断しませんでした。これにより、godef
が失敗した場合でも、Emacsの処理が意図せず続行され、予期せぬ動作を引き起こす可能性がありました。このコミットでは、error
関数に置き換えることで、エラー発生時に適切なLispエラーを発生させ、処理を中断するように改善されています。
前提知識の解説
EmacsとGo-mode
- Emacs: 高度にカスタマイズ可能なテキストエディタであり、多くのプログラマーに利用されています。Lisp言語(Emacs Lisp)で拡張可能であり、様々なプログラミング言語のモードが提供されています。
- Go-mode: EmacsでGo言語のコードを編集するためのメジャーモードです。シンタックスハイライト、インデント、コード補完、定義へのジャンプなど、Go開発を支援する機能を提供します。
godef
- godef: Go言語のソースコード解析ツールの一つです。与えられたGoのソースファイルとカーソル位置に基づいて、その位置にある識別子(変数、関数、型など)の定義元を特定し、そのファイルパス、行番号、列番号を出力します。これは、IDEにおける「定義へ移動 (Go to Definition)」機能のバックエンドとして利用されます。
コードカバレッジバッファ
- コードカバレッジ: ソフトウェアテストにおいて、テストがどれだけソースコードを実行したかを示す指標です。Go言語には、
go test -cover
コマンドでコードカバレッジを測定する機能があります。 - カバレッジバッファ: EmacsのGoモードでは、コードカバレッジの結果を視覚的に表示するために、元のソースコードにカバレッジ情報(例えば、実行された行が緑色、未実行の行が赤色など)をオーバーレイした一時的なバッファを作成することがあります。このバッファは、元のファイルとは異なる内部的な表現を持つため、ファイルパスの扱いが通常のバッファとは異なります。
Emacs Lispの関数
buffer-file-name
: 現在のバッファに関連付けられているファイル名を文字列で返します。バッファがファイルに関連付けられていない場合はnil
を返します。go--coverage-origin-buffer
: Goモードの内部関数で、カバレッジバッファの場合にその元のソースコードバッファを返すものと推測されます。message
: Emacsのミニバッファにメッセージを表示する関数です。処理は中断されません。error
: Emacs Lispのエラーを発生させる関数です。これにより、現在のLispの評価が中断され、エラーハンドラが呼び出されます。
技術的詳細
このコミットの主要な変更点は、godef
コマンドがファイルパスを取得するロジックと、エラーハンドリングのメカニズムにあります。
-
ファイルパスの取得ロジックの変更:
- 変更前:
(not buffer-file-name)
- これは、現在のバッファ(
godef
が呼び出されたバッファ)にファイル名が関連付けられていない場合にエラーを発生させていました。カバレッジバッファは通常、一時的なバッファであり、直接ファイル名を持たないため、このチェックでgodef
が機能しませんでした。
- これは、現在のバッファ(
- 変更後:
(not (buffer-file-name (go--coverage-origin-buffer)))
- この変更により、
godef
はまずgo--coverage-origin-buffer
という関数を呼び出し、現在のバッファがカバレッジバッファである場合に、そのカバレッジバッファが参照している「元の」ソースコードバッファを取得しようとします。 - そして、その「元の」ソースコードバッファにファイル名が関連付けられているかどうかを
buffer-file-name
で確認します。これにより、カバレッジバッファでgodef
が呼び出されても、元のソースファイルのコンテキストでgodef
が実行されるようになります。 file-truename
関数は、シンボリックリンクなどを解決してファイルの真のパスを返します。これにより、godef
が正確なファイルパスを受け取ることを保証します。
- この変更により、
- 変更前:
-
エラーハンドリングの変更:
- 変更前:
(message "Cannot use godef on a buffer without a file name")
- ファイル名がない場合のエラーを
message
で表示していました。これはユーザーに通知するだけで、Lispの実行は継続されます。
- ファイル名がない場合のエラーを
- 変更後:
(error "Cannot use godef on a buffer without a file name")
message
をerror
に置き換えることで、ファイル名がない場合にLispのエラーを発生させ、godef
の処理を中断させます。これにより、不完全な情報でgodef
が実行され続けることを防ぎ、より堅牢な動作を実現します。
- 同様に、XEmacsでの
godef
の信頼性の問題についても、message
からerror
に変更されています。
- 変更前:
-
call-process-region
の引数変更:call-process-region
は、Emacsのバッファの特定領域の内容を外部プロセス(この場合はgodef
)に渡し、その出力を別のバッファに書き込むための関数です。- 変更前は引数が一行に記述されていましたが、変更後は可読性を高めるために複数行に分割されています。機能的な変更はありませんが、コードの保守性が向上しています。
- 特に重要なのは、
-f
オプションに渡されるファイル名が、buffer-file-name
から(buffer-file-name (go--coverage-origin-buffer))
に変更されている点です。これにより、godef
はカバレッジバッファではなく、元のソースファイルのパスを引数として受け取るようになります。
コアとなるコードの変更箇所
misc/emacs/go-mode.el
ファイルの以下の部分が変更されています。
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -948,13 +948,24 @@ visit FILENAME and go to line LINE and column COLUMN.\"\
\"Call godef, acquiring definition position and expression\
description at POINT.\"\
(if (go--xemacs-p)\
- (message \"godef does not reliably work in XEmacs, expect bad results\"))\
- (if (not buffer-file-name)\
- (message \"Cannot use godef on a buffer without a file name\")\
+ (error \"godef does not reliably work in XEmacs, expect bad results\"))\
+ (if (not (buffer-file-name (go--coverage-origin-buffer)))\
+ (error \"Cannot use godef on a buffer without a file name\")\
(let ((outbuf (get-buffer-create \"*godef*\")))\
(with-current-buffer outbuf\
(erase-buffer))\
- (call-process-region (point-min) (point-max) \"godef\" nil outbuf nil \"-i\" \"-t\" \"-f\" (file-truename buffer-file-name) \"-o\" (number-to-string (go--position-bytes (point))))\
+ (call-process-region (point-min)\
+ (point-max)\
+ \"godef\"\
+ nil\
+ outbuf\
+ nil\
+ \"-i\"\
+ \"-t\"\
+ \"-f\"\
+ (file-truename (buffer-file-name (go--coverage-origin-buffer)))\
+ \"-o\"\
+ (number-to-string (go--position-bytes (point))))\
(with-current-buffer outbuf\
(split-string (buffer-substring-no-properties (point-min) (point-max)) \"\\n\")))))\
コアとなるコードの解説
変更されたコードブロックは、go-mode.el
内のgo-godef
関数の一部です。この関数は、Emacsでgodef
コマンドが実行されたときに呼び出されます。
-
(if (go--xemacs-p) ...)
:- XEmacsを使用しているかどうかをチェックします。XEmacsでは
godef
が信頼できないという既知の問題があったため、以前は警告メッセージを表示していました。 - 変更点:
(message ...)
が(error ...)
に変更されました。これにより、XEmacsでの使用時にエラーを発生させ、処理を中断するようになりました。これは、信頼性の低い環境での予期せぬ動作を防ぐための堅牢化です。
- XEmacsを使用しているかどうかをチェックします。XEmacsでは
-
(if (not (buffer-file-name (go--coverage-origin-buffer))) ...)
:- これがこのコミットの最も重要な変更点です。
- 変更前:
(if (not buffer-file-name) ...)
- 現在のバッファにファイル名が関連付けられていない場合(例: 新規バッファ、またはカバレッジバッファのような一時的なバッファ)にエラーメッセージを表示していました。
- 変更後:
(not (buffer-file-name (go--coverage-origin-buffer)))
go--coverage-origin-buffer
関数を呼び出し、現在のバッファがカバレッジバッファであれば、その元のソースコードバッファを取得します。- 次に、その元のバッファのファイル名が存在するかどうかを
buffer-file-name
で確認します。 - もし元のバッファにもファイル名がない場合(これは通常ありえませんが、念のため)、
error
を発生させて処理を中断します。 - この変更により、カバレッジバッファで
godef
が呼び出された場合でも、godef
は元のソースファイルのコンテキストで動作できるようになります。
-
(call-process-region ...)
:- この部分は、
godef
外部コマンドを実行するためのEmacs Lispの関数呼び出しです。 point-min
とpoint-max
は、現在のバッファ全体をgodef
に渡すことを意味します。"godef"
は実行するコマンド名です。nil
は入力のバッファを指定しないことを意味します(標準入力から読み込む)。outbuf
はgodef
の標準出力を書き込むバッファです(*godef*
という名前の一時バッファ)。nil
はエラー出力を指定しないことを意味します。"-i"
,"-t"
はgodef
コマンドのオプションです。"-f"
オプションに続く引数が、godef
が解析するファイルパスです。- 変更前:
(file-truename buffer-file-name)
- 現在のバッファのファイル名を直接使用していました。
- 変更後:
(file-truename (buffer-file-name (go--coverage-origin-buffer)))
go--coverage-origin-buffer
で取得した元のバッファのファイル名をgodef
に渡すように変更されました。これにより、カバレッジバッファからでも元のソースコードの定義にジャンプできるようになります。
- 変更前:
"-o"
オプションに続く引数は、カーソル位置のバイトオフセットです。go--position-bytes (point)
で現在のカーソル位置のバイトオフセットを計算し、number-to-string
で文字列に変換して渡しています。
- この部分は、
この一連の変更により、EmacsのGoモードにおけるgodef
の使い勝手と堅牢性が向上し、特にコードカバレッジを扱う開発ワークフローにおいて、よりシームレスな定義へのジャンプが可能になりました。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Emacsの公式ウェブサイト: https://www.gnu.org/software/emacs/
godef
ツールのGitHubリポジトリ (当時のものとは異なる可能性がありますが、参考として): https://github.com/rogpeppe/godef
参考にした情報源リンク
- Go言語のコミット履歴: https://github.com/golang/go/commits/master
- Emacs Lispのドキュメント (例:
buffer-file-name
,message
,error
,call-process-region
): https://www.gnu.org/software/emacs/manual/html_node/elisp/ - Go言語のテストとカバレッジに関するドキュメント: https://go.dev/blog/cover
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されている
https://golang.org/cl/13041043
は、当時のGerritのURL形式です。) - XEmacsに関する情報: https://www.xemacs.org/