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

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

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elに対する変更です。具体的には、misc/emacs/go-mode.elファイルが修正されています。このファイルは、EmacsエディタでGo言語のコードを編集する際に、シンタックスハイライト、インデント、コード補完、定義へのジャンプなどの機能を提供するLispコードを含んでいます。

コミット

  • コミットハッシュ: 3b0e6c21aef32c5439fdcdaff44418a875565fcc
  • Author: Dominik Honnef dominik.honnef@gmail.com
  • Date: Tue Oct 29 11:14:56 2013 -0400

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

https://github.com/golang/go/commit/3b0e6c21aef32c5439fdcdaff44418a875565fcc

元コミット内容

    misc/emacs: support godef-jump on import statements
    
    The newest version of godef supports jumping to a package's source
    directory if point is on an import statement.
    
    R=adonovan
    CC=golang-dev
    https://golang.org/cl/18230043

変更の背景

このコミットの背景には、Go言語の定義ジャンプツールであるgodefの機能拡張があります。以前のgodefは、変数、関数、型などの定義元にジャンプする機能を提供していましたが、Goのimportステートメント(パッケージのインポート宣言)上では、そのパッケージのソースディレクトリに直接ジャンプする機能は持っていませんでした。

新しいバージョンのgodefが、この「importステートメントからパッケージのソースディレクトリへジャンプする」機能を追加しました。これに伴い、Emacsのgo-mode.elも、この新しいgodefの出力を適切に解釈し、ユーザーを目的のディレクトリまたはファイルに誘導できるように更新する必要が生じました。

具体的には、godefがパッケージのソースディレクトリを指す場合、その出力はファイル名や行番号、列番号を含まない単なるディレクトリパスになる可能性があります。go-mode.elは、このような新しい形式のgodefの出力を処理できるように、既存のgo-goto-location関数を修正する必要がありました。また、godefがインポートパスを見つけられなかった場合のエラーメッセージも適切に処理できるように変更されています。

前提知識の解説

Emacs

Emacsは、拡張性とカスタマイズ性に優れたテキストエディタです。その機能の多くはEmacs Lisp(Elisp)というプログラミング言語で記述されており、ユーザーはElispを使ってEmacsの動作を自由にカスタマイズしたり、新しい機能を追加したりできます。

go-mode.el

go-mode.elは、EmacsでGo言語のコードを編集するためのメジャーモードです。Go言語特有のシンタックスハイライト、自動インデント、コードフォーマット、そして外部ツール(godefなど)との連携機能を提供します。これにより、EmacsユーザーはGo言語の開発を効率的に行うことができます。

godef

godefは、Go言語のソースコード内で識別子(変数名、関数名、型名など)がどこで定義されているかを特定し、その定義元のファイルパス、行番号、列番号を出力するコマンドラインツールです。開発者がコードを読み解く際に、定義元に素早くジャンプできるため、非常に便利なツールとして広く利用されています。

定義へのジャンプ (Jump to Definition)

プログラミングエディタにおける「定義へのジャンプ」機能は、カーソルが置かれている識別子(変数、関数、型など)の定義が記述されているソースコードの場所に、エディタの表示を移動させる機能です。これにより、開発者はコードベースを効率的にナビゲートし、特定の要素がどのように定義されているかを素早く理解できます。

importステートメントとパッケージのソースディレクトリ

Go言語では、import "path/to/package"という形式で他のパッケージをインポートします。このpath/to/packageは、通常、Goのワークスペース(GOPATH)内のsrcディレクトリ以下に存在するパッケージのソースコードへのパスに対応します。例えば、import "fmt"であれば、標準ライブラリのfmtパッケージのソースコードディレクトリを指します。

技術的詳細

このコミットの技術的な核心は、go-mode.el内のgo-goto-location関数の変更にあります。この関数は、godefコマンドの出力(定義元の場所を示す文字列)を解析し、Emacsでその場所にジャンプする役割を担っています。

従来のgodefの出力は、filename:line:columnという形式でした。go-goto-location関数は、この形式を正規表現でマッチングし、ファイル名、行番号、列番号を抽出して、該当するファイルを開き、指定された行と列にカーソルを移動させていました。

しかし、新しいgodefは、importステートメント上で実行された場合、filename:line:column形式ではなく、単にパッケージのソースディレクトリのパスを返すようになりました。このコミットでは、この新しい出力形式に対応するために、go-goto-location関数が以下のように変更されました。

  1. ディレクトリパスの直接処理: godefの出力がfilename:line:columnの形式にマッチしない場合、それはディレクトリパスであると判断し、そのディレクトリをEmacsで開くように変更されました。これは、funcall (if other-window #'find-file-other-window #'find-file) specifier)という行で実現されています。find-fileはファイルを開く関数ですが、ディレクトリパスが与えられた場合はそのディレクトリのDiredバッファ(ファイル一覧)を開きます。

  2. 新しいエラーメッセージの処理: godefがインポートパスを見つけられなかった場合に返す可能性のある新しいエラーメッセージ "error finding import path for " を認識し、それをEmacsのメッセージバッファに表示するように追加されました。これにより、ユーザーはgodefがなぜジャンプできなかったのかを理解できるようになります。

これらの変更により、go-mode.elは、godefの新しい機能(importステートメントからのパッケージソースディレクトリへのジャンプ)を完全にサポートし、より堅牢なエラーハンドリングを提供するようになりました。

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

diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
index 6cc03edb06..b74bc45e8d 100644
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -950,11 +950,12 @@ will be commented, otherwise they will be removed completely."
   "Given a file name in the format of `filename:line:column',
 visit FILENAME and go to line LINE and column COLUMN."
   (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier))
-      (error "Unexpected godef output: %s" specifier)
+      ;; We've only been given a directory name
+      (funcall (if other-window #'find-file-other-window #'find-file) specifier)
     (let ((filename (match-string 1 specifier))
           (line (string-to-number (match-string 2 specifier)))
           (column (string-to-number (match-string 3 specifier))))
-      (with-current-buffer (funcall (if other-window 'find-file-other-window 'find-file) filename)
+      (with-current-buffer (funcall (if other-window #'find-file-other-window #'find-file) filename)
         (go--goto-line line)
         (beginning-of-line)
         (forward-char (1- column))))))
@@ -1008,6 +1009,8 @@ description at POINT."
           (message "%s" file))
          ((go--string-prefix-p "godef: no declaration found for " file)
           (message "%s" file))
+         ((go--string-prefix-p "error finding import path for " file)
+          (message "%s" file))
          (t
           (push-mark)
           (ring-insert find-tag-marker-ring (point-marker))

コアとなるコードの解説

go-goto-location関数の変更

   (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier))
-      (error "Unexpected godef output: %s" specifier)
+      ;; We've only been given a directory name
+      (funcall (if other-window #'find-file-other-window #'find-file) specifier)
     (let ((filename (match-string 1 specifier))
           (line (string-to-number (match-string 2 specifier)))
           (column (string-to-number (match-string 3 specifier))))
-      (with-current-buffer (funcall (if other-window 'find-file-other-window 'find-file) filename)
+      (with-current-buffer (funcall (if other-window #'find-file-other-window #'find-file) filename)
         (go--goto-line line)
         (beginning-of-line)
         (forward-char (1- column))))))
  • 変更前: (error "Unexpected godef output: %s" specifier)
    • godefの出力specifierfilename:line:columnの形式にマッチしない場合、Emacsは「予期しないgodef出力」というエラーを発生させていました。これは、godefがディレクトリパスを返した場合にエラーとなることを意味します。
  • 変更後: (funcall (if other-window #'find-file-other-window #'find-file) specifier)
    • 正規表現にマッチしない場合、specifierはディレクトリパスであると仮定し、find-file関数(またはfind-file-other-window関数)を呼び出してそのパスを開くように変更されました。find-fileは、ファイルパスが与えられればそのファイルを開き、ディレクトリパスが与えられればそのディレクトリのDiredバッファ(ファイル一覧)を開くというEmacsの標準的な動作を利用しています。
    • other-windowは、現在のウィンドウではなく別のウィンドウでファイルを開くかどうかを制御する変数です。
    • #'は、関数を引用符で囲む(quote)ためのElispの構文で、関数をデータとして扱うことを示します。

エラーメッセージ処理の追加

          ((go--string-prefix-p "godef: no declaration found for " file)
           (message "%s" file))
+         ((go--string-prefix-p "error finding import path for " file)
+          (message "%s" file))
          (t
           (push-mark)
           (ring-insert find-tag-marker-ring (point-marker))
  • 追加: ((go--string-prefix-p "error finding import path for " file) (message "%s" file))
    • これは、godefからの出力fileが文字列 "error finding import path for " で始まる場合に、そのメッセージをEmacsのミニバッファ(メッセージ領域)に表示するための条件分岐が追加されたことを示します。
    • go--string-prefix-pは、文字列が特定のプレフィックスで始まるかどうかをチェックするヘルパー関数です。
    • これにより、godefがインポートパスを解決できなかった場合に、ユーザーにその旨が明確に通知されるようになりました。

これらの変更により、go-mode.elgodefの新しい動作に適切に対応し、ユーザーエクスペリエンスが向上しました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Emacs Lispの公式ドキュメント
  • godefのソースコード(Go言語リポジトリ内)
  • go-mode.elのソースコード(Go言語リポジトリ内)
  • 一般的なEmacs Lispプログラミングの知識