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

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

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

コミット

commit 580ea8b5fdea04f0bcfc809a6545a7ebced8c358
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date:   Tue Oct 22 12:35:04 2013 -0400

    misc/emacs: handle empty "import ()" in go-goto-imports
    
    R=adonovan
    CC=golang-dev
    https://golang.org/cl/14454058

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

https://github.com/golang/go/commit/580ea8b5fdea04f0bcfc809a6545a7ebced8c358

元コミット内容

このコミットは、EmacsのGoモードにおいて、go-goto-imports関数が空のimport ()ブロックを正しく処理できるようにするためのものです。これにより、新しいimport文を空のimport ()ブロック内に追加する際に発生していた問題を解決します。

変更の背景

Go言語では、複数のパッケージをインポートする際に、以下のようなグループ化されたimport宣言を使用できます。

import (
	"fmt"
	"net/http"
)

また、Goのコードを生成するツールや、手動でコードを記述する際に、将来のimportのために空のimportブロックを事前に用意しておくことがあります。

import (
)

このコミットが導入される前は、Emacsのgo-mode.elに含まれるgo-goto-imports関数が、この「空のimport ()ブロック」を適切に認識し、その中に新しいimport文を挿入することができませんでした。具体的には、go-add-importのような関数が新しいimportを追加しようとした際に、空のimport ()ブロックが存在しても、それを有効な挿入位置として認識せず、結果としてimportが正しく追加されない、あるいは予期せぬ場所に挿入されるといった問題が発生していました。

この変更の目的は、go-mode.elが空のimport ()ブロックを特別なケースとして扱い、その中に新しいimport文を適切に挿入できるようにすることです。これにより、EmacsでのGo開発体験が向上し、より堅牢なimport管理が可能になります。

前提知識の解説

  • Emacs Lisp (Elisp): Emacsエディタの拡張言語であり、Emacsの動作をカスタマイズしたり、新しい機能を追加したりするために使用されます。go-mode.elもElispで書かれています。
  • go-mode.el: EmacsでGo言語のコードを編集するためのメジャーモードです。Go言語の構文解析、インデント、コード補完、そしてGoツールチェーンとの連携(例: goimportsgofmt)などの機能を提供します。
  • go-goto-imports関数: go-mode.el内で定義されている関数の一つで、Goソースコード内のimport宣言の位置を特定する役割を担っています。この関数は、新しいimport文を挿入する適切な場所を見つけるために使用されます。
  • Go言語のimport宣言: Go言語で外部パッケージを使用するために必要な宣言です。単一のimport文 (import "fmt") や、複数のパッケージを括弧で囲んでグループ化する形式 (import ("fmt"; "net/http")) があります。
  • re-search-forward: Emacs Lispの関数で、現在のカーソル位置から前方に向かって正規表現を検索します。
  • backward-char: Emacs Lispの関数で、カーソルを後方に移動させます。
  • insert: Emacs Lispの関数で、現在のカーソル位置にテキストを挿入します。
  • cond: Emacs Lispの条件分岐構文で、複数の条件を順番に評価し、最初に真となった条件に対応する式を実行します。
  • case: Emacs Lispの条件分岐構文で、式の結果に基づいて複数の選択肢の中から一つを実行します。

技術的詳細

このコミットは、go-mode.el内のgo-goto-imports関数と、その関数からの戻り値を処理する呼び出し元(おそらくgo-add-import関数の一部)に修正を加えています。

  1. go-goto-imports関数の変更:

    • 既存のcond式に新しい節が追加されました。この新しい節は、正規表現^import ()\"を使用して、行頭に「import ()」という文字列が存在するかどうかを検索します。
    • re-search-forwardの引数nil tは、検索が失敗した場合にエラーを発生させず、検索が成功した場合はマッチした領域の末尾にカーソルを移動させることを意味します。
    • もし「import ()」が見つかった場合、backward-char 1によってカーソルを括弧の閉じ括弧の直前(つまり、()の間に)移動させます。
    • そして、シンボル'block-emptyを返します。これは、呼び出し元に対して「空のimportブロックが見つかり、カーソルがその中に配置された」という状態を通知するためのものです。
  2. 呼び出し元のcase文の変更:

    • go-goto-importsの戻り値を処理するcase文に、新しい'block-emptyというケースが追加されました。
    • このケースが実行されるのは、go-goto-importsが空のimport ()ブロックを見つけて'block-emptyを返した場合です。
    • このケースでは、insert関数を使用して、新しいimport文(変数lineに格納されている)を挿入します。挿入される文字列は"\n\t" line "\n"となっており、これは新しい行、タブ、import文、そしてもう一つの新しい行を意味します。これにより、新しいimport文が空のimport ()ブロック内に適切にインデントされて追加されます。

この変更により、go-mode.elは、Go言語のソースコード内に空のimport ()ブロックが存在する場合でも、その中に新しいimport文を自動的に挿入できるようになり、ユーザーが手動で編集する手間を省き、よりスムーズな開発フローを提供します。

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

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -749,6 +749,9 @@ declaration.\"\n   (let ((old-point (point)))\n     (goto-char (point-min))\n     (cond\n+     ((re-search-forward \"^import ()\" nil t)\n+      (backward-char 1)\n+      \'block-empty)\n      ((re-search-forward \"^import ([^)]+)\" nil t)\n       (backward-char 2)\n       \'block)\n@@ -843,6 +846,8 @@ uncommented, otherwise a new import will be added.\"\n           (uncomment-region (line-beginning-position) (line-end-position))\n         (case (go-goto-imports)\n           (\'fail (message \"Could not find a place to add import.\"))\n+          (\'block-empty\n+           (insert \"\\n\\t\" line \"\\n\"))\n           (\'block\n               (save-excursion\n                 (re-search-backward \"^import (\")\n```

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

### `go-mode.el`の`go-goto-imports`関数内の変更

```emacs-lisp
+     ((re-search-forward "^import ()\"" nil t)\n+      (backward-char 1)\n+      \'block-empty)\n```
*   `((re-search-forward "^import ()\"" nil t)`: これは`cond`式の新しい条件節です。
    *   `re-search-forward "^import ()\"" nil t`: 現在のバッファのカーソル位置から前方に向かって、正規表現`^import \(\)`(行頭の`import ()`)を検索します。`nil t`は、検索が失敗してもエラーを発生させず、成功した場合はマッチした文字列の直後にカーソルを移動させることを意味します。
*   `(backward-char 1)`: `re-search-forward`が`import ()`を見つけた場合、カーソルは閉じ括弧`(`の直後に移動しています。この行は、カーソルを1文字後方(つまり、`()`の間に)移動させます。これにより、新しいimport文を挿入する準備が整います。
*   `'block-empty`: このシンボルは、`go-goto-imports`関数の呼び出し元に返されます。これは、空の`import ()`ブロックが見つかり、カーソルがその中に適切に配置されたことを示します。

### `go-mode.el`の`go-add-import`(または関連する)関数内の変更

```emacs-lisp
+          (\'block-empty\n+           (insert \"\\n\\t\" line \"\\n\"))\n```
*   `('block-empty`: これは`case`文の新しい分岐です。`go-goto-imports`関数が`'block-empty`を返した場合にこの分岐が実行されます。
*   `(insert "\\n\\t" line "\\n")`: この行は、新しいimport文をバッファに挿入します。
    *   `"\\n"`: 改行コード。
    *   `"\\t"`: タブ文字。Goの慣例に従って、import文をインデントします。
    *   `line`: 挿入される実際のimport文の文字列(例: `"\"fmt\""`)。
    *   `"\\n"`: 挿入されたimport文の後に改行を追加します。

これらの変更により、`go-mode.el`は空の`import ()`ブロックを認識し、その中に新しいimport文を自動的に、かつ正しくインデントして挿入できるようになりました。

## 関連リンク

*   Go CL 14454058: [https://golang.org/cl/14454058](https://golang.org/cl/14454058)

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

*   Emacs Lisp Reference Manual: [https://www.gnu.org/software/emacs/manual/html_node/elisp/](https://www.gnu.org/software/emacs/manual/html_node/elisp/)
*   Go Programming Language Specification - Import declarations: [https://go.dev/ref/spec#Import_declarations](https://go.dev/ref/spec#Import_declarations)
*   `go-mode.el` source code (for general context): [https://github.com/golang/go/blob/master/misc/emacs/go-mode.el](https://github.com/golang/go/blob/master/misc/emacs/go-mode.el) (Note: This link points to the current master branch, not the exact version at the time of the commit, but provides general context.)