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

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

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elファイルに対する変更です。go-mode.elは、Emacsエディタ内でGo言語のコードを記述する際に、シンタックスハイライト、インデント、コード補完、定義ジャンプなどの機能を提供するLispスクリプトです。

コミット

misc/emacs: actually use point argument that is passed to godef--call

LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/62600043

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

https://github.com/golang/go/commit/0d9b9aafd575099fcbd05e0c8f05f087d8d2b922

元コミット内容

misc/emacs: actually use point argument that is passed to godef--call

このコミットメッセージは、godef--call関数に渡されるpoint引数が、実際にその関数内で使用されるように修正されたことを示しています。

変更の背景

この変更の背景には、go-mode.elにおけるgodefツールの利用方法に関するバグがありました。godefはGo言語の識別子の定義元を検索するためのコマンドラインツールです。Emacsのgo-mode.elは、このgodefツールを呼び出して、ユーザーがカーソルを置いた場所(または指定した場所)のシンボルの定義元を特定する機能を提供しています。

問題は、godef--callというEmacs Lisp関数が、定義元を検索する位置を示すpoint引数を受け取るにもかかわらず、その引数を適切に利用していなかった点にありました。具体的には、godefツールに渡す位置情報を生成する際に、引数として渡されたpointではなく、常に現在のバッファのカーソル位置(Emacs Lispの(point)関数が返す値)を使用していました。

このため、godef--callが特定のpoint(例えば、現在のカーソル位置とは異なる場所のシンボル定義を調べたい場合)を指定して呼び出されたとしても、期待通りの動作をせず、常に現在のカーソル位置のシンボル定義を返してしまうという不具合が発生していました。このコミットは、この論理的な誤りを修正し、godef--callが受け取ったpoint引数を正しく利用するようにすることで、より正確な定義ジャンプ機能を提供することを目的としています。

前提知識の解説

Emacs Lisp (Elisp)

Emacs Lispは、Emacsエディタの拡張機能やカスタマイズを記述するために使用されるプログラミング言語です。Emacsのほぼ全ての機能はEmacs Lispで実装されており、ユーザーはEmacs Lispコードを記述することで、エディタの動作を細かく制御したり、新しい機能を追加したりできます。go-mode.elもEmacs Lispで書かれたファイルです。

Emacsにおけるpoint

Emacsにおいて「ポイント (point)」とは、現在のカーソル位置を指す概念です。バッファ内の文字と文字の間に位置し、通常は1から始まる整数値で表現されます。Emacs Lispでは、(point)関数を呼び出すことで、現在のバッファのポイント値を取得できます。また、特定のポイント値を指定して、その位置で何らかの操作を行うことも頻繁に行われます。

godefツール

godefは、Go言語のソースコード内で特定の識別子(変数、関数、型など)がどこで定義されているかを特定するためのコマンドラインツールです。Goのソースファイルを解析し、指定されたファイルパスとバイトオフセットに基づいて、その位置にある識別子の定義元(ファイルパス、行番号、列番号)を出力します。Emacsのgo-mode.elのようなIDE連携ツールは、このgodefをバックエンドとして利用し、ユーザーがコード上で定義ジャンプ(Go to Definition)を行う機能を実現しています。

go-mode.elgodefの連携

go-mode.elは、Emacs Lispのコードを通じてgodefコマンドを外部プロセスとして実行します。この際、godefには、定義を調べたいGoファイルのパスと、そのファイル内でのバイトオフセット(文字位置)を引数として渡します。godefはこれらの情報に基づいて定義元を特定し、その結果を標準出力に返します。go-mode.elはその出力を解析し、Emacsバッファ内で定義元にジャンプしたり、情報を表示したりします。

go--position-bytes関数

go--position-bytesは、Emacsのポイント(文字位置)を、godefが期待するバイトオフセットに変換するためのgo-mode.el内のヘルパー関数です。Emacsは内部的に文字コードを考慮してポイントを扱いますが、godefのような外部ツールは通常、ファイルの先頭からのバイトオフセットを期待するため、この変換が必要になります。

技術的詳細

このコミットが修正しているのは、go-mode.el内のgodef--call関数がgodefツールを呼び出す際の引数渡しに関する問題です。

godef--call関数は、以下のような構造でgodefコマンドを構築し、実行していました(簡略化)。

(defun godef--call (point)
  ;; ...
  (let ((command-args
         (list "-f"
               (file-truename (buffer-file-name (go--coverage-origin-buffer)))
               "-o"
               (number-to-string (go--position-bytes (point)))))) ; ここが問題
    ;; ... godefコマンドを実行
    ))

ここで注目すべきは、godefに渡す-oオプションの値、すなわちバイトオフセットを生成している部分です。

(number-to-string (go--position-bytes (point)))

このコードでは、godef--call関数が引数としてpointを受け取っているにもかかわらず、go--position-bytes関数に渡しているのは、引数のpointではなく、Emacs Lispの組み込み関数である(point)の呼び出し結果です。組み込みの(point)関数は、常に「現在のバッファのカーソル位置」を返します。

したがって、godef--callが例えば(godef--call 100)のように、特定のポイント値(例: 100)を指定して呼び出されたとしても、godefツールには常に現在のカーソル位置のバイトオフセットが渡されていました。これにより、ユーザーが意図した場所ではなく、カーソルがある場所のシンボル定義が検索されてしまうという、機能的な不整合が生じていました。

このコミットは、この部分を修正し、godef--callに渡された引数のpointを正しくgo--position-bytesに渡すように変更しています。

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

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -995,7 +995,7 @@ description at POINT."
                            "-f"
                            (file-truename (buffer-file-name (go--coverage-origin-buffer)))
                            "-o"
-                           (number-to-string (go--position-bytes (point))))
+                           (number-to-string (go--position-bytes point))))
       (with-current-buffer outbuf
         (split-string (buffer-substring-no-properties (point-min) (point-max)) "\\n"))))

変更はmisc/emacs/go-mode.elファイルの1箇所のみです。

コアとなるコードの解説

変更された行は以下の通りです。

  • 変更前: (number-to-string (go--position-bytes (point)))
  • 変更後: (number-to-string (go--position-bytes point))

この変更は非常にシンプルですが、その影響は重要です。

  1. 変更前: (point)というEmacs Lispの組み込み関数が呼び出されています。この関数は、そのコードが実行された時点での「現在のバッファのカーソル位置」を返します。したがって、godef--call関数が引数としてpointを受け取っていたとしても、この内部の呼び出しではその引数が無視され、常に現在のカーソル位置が使用されていました。

  2. 変更後: pointというシンボルが直接go--position-bytes関数に渡されています。このpointは、godef--call関数の引数として受け取った値です。これにより、godef--callが呼び出し元から指定された特定のポイント値に基づいてgodefツールに位置情報を渡すことができるようになります。

この修正により、godef--call関数は、そのインターフェース(引数としてpointを受け取る)と実際の動作が一致するようになり、より柔軟かつ正確な定義ジャンプ機能を提供できるようになりました。例えば、プログラム的に特定のコード位置の定義を調べたい場合など、現在のカーソル位置に依存しないgodefの呼び出しが可能になります。

関連リンク

参考にした情報源リンク