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

[インデックス 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コマンドがファイルパスを取得するロジックと、エラーハンドリングのメカニズムにあります。

  1. ファイルパスの取得ロジックの変更:

    • 変更前: (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が正確なファイルパスを受け取ることを保証します。
  2. エラーハンドリングの変更:

    • 変更前: (message "Cannot use godef on a buffer without a file name")
      • ファイル名がない場合のエラーをmessageで表示していました。これはユーザーに通知するだけで、Lispの実行は継続されます。
    • 変更後: (error "Cannot use godef on a buffer without a file name")
      • messageerrorに置き換えることで、ファイル名がない場合にLispのエラーを発生させ、godefの処理を中断させます。これにより、不完全な情報でgodefが実行され続けることを防ぎ、より堅牢な動作を実現します。
    • 同様に、XEmacsでのgodefの信頼性の問題についても、messageからerrorに変更されています。
  3. 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コマンドが実行されたときに呼び出されます。

  1. (if (go--xemacs-p) ...):

    • XEmacsを使用しているかどうかをチェックします。XEmacsではgodefが信頼できないという既知の問題があったため、以前は警告メッセージを表示していました。
    • 変更点: (message ...)(error ...)に変更されました。これにより、XEmacsでの使用時にエラーを発生させ、処理を中断するようになりました。これは、信頼性の低い環境での予期せぬ動作を防ぐための堅牢化です。
  2. (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は元のソースファイルのコンテキストで動作できるようになります。
  3. (call-process-region ...):

    • この部分は、godef外部コマンドを実行するためのEmacs Lispの関数呼び出しです。
    • point-minpoint-maxは、現在のバッファ全体をgodefに渡すことを意味します。
    • "godef"は実行するコマンド名です。
    • nilは入力のバッファを指定しないことを意味します(標準入力から読み込む)。
    • outbufgodefの標準出力を書き込むバッファです(*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の使い勝手と堅牢性が向上し、特にコードカバレッジを扱う開発ワークフローにおいて、よりシームレスな定義へのジャンプが可能になりました。

関連リンク

参考にした情報源リンク