[インデックス 15449] ファイルの概要
このコミットは、EmacsエディタのGo言語メジャーモードであるgo-mode
を大幅に改善するものです。既存のgo-mode
が抱えていた多くの問題(奇妙な挙動、Emacsの一般的なモードに期待される機能の欠如、バグなど)に対処するため、コードベースの大部分が書き直されました。特に、gofmt
とgodoc
の機能は維持しつつ、それ以外の多くの部分が再構築されています。さらに、パッケージインポートの操作など、新しい機能も追加されています。
コミット
- コミットハッシュ:
b302d68ebcc2500e6e013ff5fed9c70b0e1ff3a4
- 作者: Dominik Honnef dominik.honnef@gmail.com
- コミット日時: 2013年2月26日 火曜日 13:48:32 -0500
- コミットメッセージ:
misc/emacs: Greatly improve go-mode for Emacs. The original go-mode is plagued with odd behaviour, lack of behaviour typical to modes in Emacs and bugs. This change rewrites great parts of go-mode (basically only keeping the gofmt and godoc functions). Additionally it adds new features such as manipulating package imports. For more information please see https://groups.google.com/group/golang-nuts/browse_frm/thread/3a9d6dae3369c0b5/1efe65e2f7afb190 Fixes #3618. Fixes #4240. Fixes #4322. Fixes #4671. Fixes #4726. R=golang-dev, fullung, sameer, cw, arthur, proppy, adonovan, rsc, bradfitz CC=golang-dev https://golang.org/cl/7314113
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b302d68ebcc2500e6e013ff5fed9c70b0e1ff3a4
元コミット内容
commit b302d68ebcc2500e6e013ff5fed9c70b0e1ff3a4
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date: Tue Feb 26 13:48:32 2013 -0500
misc/emacs: Greatly improve go-mode for Emacs.
The original go-mode is plagued with odd behaviour, lack of
behaviour typical to modes in Emacs and bugs.
This change rewrites great parts of go-mode (basically only
keeping the gofmt and godoc functions).
Additionally it adds new features such as manipulating package
imports.
For more information please see
https://groups.google.com/group/golang-nuts/browse_frm/thread/3a9d6dae3369c0b5/1efe65e2f7afb190
Fixes #3618.
Fixes #4240.
Fixes #4322.
Fixes #4671.
Fixes #4726.
R=golang-dev, fullung, sameer, cw, arthur, proppy, adonovan, rsc, bradfitz
CC=golang-dev
https://golang.org/cl/7314113
変更の背景
このコミットが行われた背景には、当時のEmacs向けGoモード(go-mode
)が抱えていた深刻な問題がありました。コミットメッセージに明記されているように、元のgo-mode
は「奇妙な挙動」「Emacsのモードに典型的な挙動の欠如」「バグ」に悩まされていました。これは、Go言語のコードをEmacsで効率的に記述する上で大きな障壁となっていたと考えられます。
具体的には、以下のような問題が挙げられていました(コミットメッセージのTo do
セクションから推測されるものも含む):
gofmt
とほぼ同じであるはずのインデントが、実際にはいくつかの点で異なっていた。特に構造体リテラルのキーや関数リテラルのインデントに問題があった。- 構文解析が不十分で、識別子をその文脈(型、変数、関数呼び出し、タグなど)に応じて適切にハイライトできなかった。
- インポートの追加や未使用インポートの削除といった、Go言語開発において頻繁に行われる操作をサポートする機能が不足していた。
- コメントや文字列の解析、ネストの深さの追跡といった基本的な機能が、効率的で堅牢な方法で実装されていなかった。
これらの問題は、Emacsを主要な開発環境として利用するGo開発者にとって、生産性を著しく低下させる要因となっていました。このコミットは、これらの根本的な問題を解決し、より堅牢で機能豊富なgo-mode
を提供することを目的としています。特に、既存のgofmt
とgodoc
の統合は維持しつつ、それ以外の大部分を再設計・再実装することで、よりEmacsの慣習に沿った、信頼性の高いモードを目指しました。
前提知識の解説
このコミットの変更内容を理解するためには、以下の技術的背景知識が必要です。
-
Emacs (GNU Emacs):
- Emacsは、拡張性とカスタマイズ性に優れたテキストエディタであり、統合開発環境(IDE)としても機能します。その核となるのは、Emacs Lisp(Elisp)と呼ばれるLisp方言で記述された豊富な機能群です。
- メジャーモード (Major Mode): Emacsは、編集するファイルのタイプ(プログラミング言語、マークアップ言語など)に応じて「メジャーモード」を切り替えます。メジャーモードは、そのファイルタイプに特化した機能(シンタックスハイライト、インデント、キーバインディングなど)を提供します。
go-mode
はGo言語用のメジャーモードです。 - マイナーモード (Minor Mode): メジャーモードとは異なり、マイナーモードは任意のメジャーモードと組み合わせて使用できる追加機能を提供します(例:
auto-fill-mode
、flycheck-mode
)。 - Elisp (Emacs Lisp): Emacsの拡張言語であり、Emacsのほぼ全ての機能がElispで実装されています。ユーザーはElispを使ってEmacsを高度にカスタマイズしたり、新しい機能を追加したりできます。
font-lock-mode
: Emacsのシンタックスハイライト機能を提供するモードです。正規表現パターンとフェイス(色、フォントスタイルなど)の組み合わせを定義することで、コードの異なる要素(キーワード、文字列、コメントなど)を視覚的に区別します。autoload
: Emacsの起動時に全てのElispファイルをロードするのではなく、特定の関数が初めて呼び出されたときにその関数が定義されているファイルをロードする仕組みです。これにより、Emacsの起動時間を短縮します。go-mode-load.el
はこのautoloadの定義を管理するファイルです。
-
Go言語 (Golang):
- Googleによって開発された静的型付けのコンパイル型プログラミング言語です。シンプルさ、効率性、並行処理のサポートが特徴です。
gofmt
: Go言語の公式フォーマッタです。Goのコードを標準的なスタイルに自動的に整形します。開発者がスタイルガイドラインを覚える手間を省き、コードの一貫性を保つのに役立ちます。godoc
: Go言語のドキュメンテーションツールです。Goのソースコードからドキュメントを生成し、表示します。- Go Playground: Go言語のコードをブラウザ上で記述、実行、共有できるオンラインツールです。
-
バージョン管理システム (Git):
- このコミットはGitリポジトリの一部であり、変更履歴を追跡するために使用されます。
- コミットハッシュ: 各コミットを一意に識別するSHA-1ハッシュ値です。
Fixes #XXXX
: GitHubなどのイシュートラッカーで、このコミットが解決するイシューの番号を示す慣習的な表記です。
技術的詳細
このコミットは、misc/emacs/go-mode-load.el
とmisc/emacs/go-mode.el
の2つのファイルにわたる大規模な変更を含んでいます。主な変更点は以下の通りです。
misc/emacs/go-mode-load.el
の変更
- autoload定義の拡張:
- 以前は
gofmt
とgo-mode
のみがautoloadされていましたが、この変更によりgo-download-play
やgodoc
もautoloadされるようになりました。 go-mode
のautoloadコメントが大幅に拡張され、新しいgo-mode
が提供する機能(構文ハイライト、インデント、関数ナビゲーション、インポート操作、Go Playgroundとの連携など)が詳細に記述されています。gofmt-before-save
フックに関する説明が追加され、autoloadの性質上、ファイル保存時に初めてgo-mode
がロードされる可能性があることが注意書きとして追記されました。go-mode
のインストール方法に関するコメントが更新され、go-mode.el
のドキュメントを参照するように促しています。
- 以前は
misc/emacs/go-mode.el
の変更
このファイルは、go-mode
のコアロジックを定義しており、大幅な書き換えが行われました。
-
依存関係の追加:
cl
(Common Lisp互換機能),diff-mode
,ffap
(Find File At Point),find-lisp
,url
といったEmacs Lispライブラリが新しくrequire
されるようになりました。これにより、より高度な機能の実装が可能になります。
-
定数と正規表現の定義:
- Go言語の構文要素を識別するための新しい定数と正規表現が多数導入されました。
go-dangling-operators-regexp
: ぶら下がり演算子(++
,--
など)を検出するための正規表現。gofmt-stdin-tag
:gofmt
の標準入力タグ。go-identifier-regexp
,go-label-regexp
,go-type-regexp
: 識別子、ラベル、型を定義する正規表現。go-func-regexp
,go-func-meth-regexp
: 関数名とメソッド名を検出する正規表現。go-builtins
,go-mode-keywords
,go-constants
: Go言語の組み込み関数、キーワード、定数のリスト。これらは主にシンタックスハイライトに使用されます。go-type-name-regexp
: 型名を検出する正規表現。
- Go言語の構文要素を識別するための新しい定数と正規表現が多数導入されました。
-
シンタックスハイライト (
font-lock
) の改善:go-mode-font-lock-keywords
が削除され、代わりにgo--build-font-lock-keywords
という関数が導入されました。この関数は、go-fontify-function-calls
という新しいカスタム変数(関数呼び出しをハイライトするかどうかを制御)に基づいて、動的にfont-lock
キーワードリストを構築します。- これにより、キーワード、組み込み関数、定数、関数名、メソッド名、型名、ラベルなど、Go言語のより多くの構文要素が正確にハイライトされるようになりました。特に、型名の検出ロジックが大幅に強化されています。
- 古い
go-mode-syntax-table
の定義が簡素化され、コメントや文字列の構文プロパティがより適切に設定されるようになりました。
-
インデントロジックの全面的な書き換え:
- 以前の
go-mode-delayed-point
,go-mode-delayed-electric
,go-mode-delayed-electric-hook
といった遅延インデントの仕組みや、go-mode-mark-cs-end
,go-mode-mark-nesting-end
などのキャッシュ管理ロジックが完全に削除されました。 - 新しいインデントロジックは、Emacsの組み込み関数
syntax-ppss
(構文解析状態)をより活用しています。 go-paren-level
,go-in-string-or-comment-p
,go-in-string-p
,go-in-comment-p
,go-goto-beginning-of-string-or-comment
といったマクロが導入され、現在のカーソル位置が文字列、コメント、または括弧のネストのどこにあるかを効率的に判断できるようになりました。go--backward-irrelevant
関数は、インデントに影響しない空白、コメント、ラベルなどを後方にスキップするために使用されます。go-previous-line-has-dangling-op-p
関数は、前の行がぶら下がり演算子で終わっているかどうかを検出し、継続行のインデントを決定します。go-indentation-at-point
関数は、これらの新しいヘルパー関数とsyntax-ppss
を組み合わせて、現在の行の適切なインデントレベルを計算します。これにより、gofmt
に近いインデントが実現されました。go-mode-indent-line
関数は、計算されたインデントレベルに基づいて行を整形します。
- 以前の
-
ナビゲーション機能の追加:
go-beginning-of-defun
とgo-end-of-defun
が追加され、関数定義の開始と終了に移動できるようになりました。これはEmacsのC-M-a
やC-M-e
といった標準的なナビゲーションコマンドと統合されます。
-
Go Playgroundとの連携:
go-play-buffer
とgo-play-region
関数が追加され、Emacsバッファの内容(または選択範囲)をGo Playgroundに送信し、生成されたURLをキルリング(クリップボード)にコピーできるようになりました。go-download-play
関数は、Go PlaygroundのURLからコードをダウンロードし、新しいGoバッファに挿入します。
-
インポート操作機能の追加:
go-goto-imports
関数は、ファイルのインポートブロックに移動します。単一行インポートと複数行インポートの両方に対応しています。go-import-add
関数は、新しいパッケージインポートを追加します。既存のコメントアウトされたインポートをアンコメントする機能も持ちます。go-root-and-paths
とgo-packages
は、Goの環境変数(GOROOT, GOPATH)から利用可能なパッケージのリストを取得するためのヘルパー関数です。go-unused-imports-lines
は、go build
コマンドの出力を解析して、未使用のインポートがある行番号を特定します。go-remove-unused-imports
関数は、未使用のインポートを削除またはコメントアウトします。
-
gofmt
とgodoc
関数の改善:gofmt
の内部ヘルパー関数がgofmt--replace-buffer
,gofmt--apply-patch
,gofmt--process-errors
のように命名規則が変更されました。gofmt
実行時に、ファイルの末尾に改行がない場合に自動的に追加するロジックが追加されました。これはdiff-mode
が改行なしのファイルでうまく動作しない問題に対処するためです。godoc
の内部ヘルパー関数もgodoc--read-query
,godoc--get-buffer
,godoc--buffer-sentinel
のように命名規則が変更されました。godoc
の出力表示が改善され、view-mode
が有効になり、バッファが自動的に表示されるようになりました。
-
その他の変更:
go-mode
の定義がdefine-derived-mode
からfundamental-mode
を継承するように変更されました。imenu-generic-expression
が設定され、type
とfunc
定義に基づいてImenu(関数や変数へのジャンプ機能)が利用できるようになりました。syntax-propertize-function
が設定され、より高度な構文プロパティの付与が可能になりました。go-dangling-cache
というハッシュテーブルが導入され、インデント計算のパフォーマンス向上のために使用されます。
変更の要約
このコミットは、単なる修正ではなく、go-mode
のアーキテクチャと実装の根本的な見直しです。特に、構文解析とインデントのロジックが大幅に改善され、より正確でgofmt
に準拠した整形が可能になりました。また、Go開発者が日常的に行うインポート管理やGo Playgroundとの連携といった、実用的な新機能が多数追加されたことで、EmacsでのGo開発体験が飛躍的に向上しました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に以下の機能に関連する部分です。
-
インデントロジック:
go-mode-indentation
関数のロジックがgo-indentation-at-point
に置き換えられ、その周辺のヘルパー関数(go-paren-level
,go-in-string-or-comment-p
,go--backward-irrelevant
,go-previous-line-has-dangling-op-p
など)が新しく追加または大幅に修正されています。- 古いキャッシュ管理(
go-mode-mark-cs-end
,go-mode-mark-nesting-end
など)と関連する関数が削除されました。
-
シンタックスハイライトの定義:
go-mode-font-lock-keywords
が削除され、go--build-font-lock-keywords
関数が新しく導入されました。この関数は、Go言語のキーワード、組み込み関数、定数、関数名、型名などをより正確に識別するための新しい正規表現とロジックを使用しています。
-
インポート操作機能:
go-import-add
,go-remove-unused-imports
,go-goto-imports
といった関数群が新しく追加されました。これらはGo言語のパッケージインポートを管理するための中心的な機能です。- 関連するヘルパー関数(
go-root-and-paths
,go-packages
,go-unused-imports-lines
)もこのセクションに含まれます。
-
Go Playground連携:
go-play-buffer
,go-play-region
,go-download-play
といった関数が新しく追加されました。
コアとなるコードの解説
インデントロジック (go-indentation-at-point
とその周辺)
以前のgo-mode
のインデントロジックは、コメントや文字列の解析、ネストの深さの追跡に独自のキャッシュとプロパティ管理を使用していました。しかし、これは複雑でバグの原因となりやすかったようです。
新しいインデントロジックは、Emacs Lispの強力な組み込み機能であるsyntax-ppss
(Parse State for Syntax) を活用しています。syntax-ppss
は、現在のカーソル位置における構文解析の状態(括弧のネストレベル、文字列内かコメント内かなど)を効率的に提供します。
go-paren-level
:(car (syntax-ppss))
を返すマクロで、現在の括弧のネストレベルを取得します。go-in-string-or-comment-p
,go-in-string-p
,go-in-comment-p
:(syntax-ppss)
の特定の要素をチェックすることで、現在のカーソル位置が文字列内、コメント内、またはその両方であるかを判断します。go--backward-irrelevant
: インデント計算に不要な空白、コメント、ラベルなどを後方にスキップする再帰関数です。これにより、インデントの基準となるコード要素を正確に特定できます。go-previous-line-has-dangling-op-p
: 前の行が++
,--
などのぶら下がり演算子で終わっているかをチェックし、継続行のインデントを調整します。これはGo言語のセミコロン挿入ルールと関連しています。go-indentation-at-point
: これらのヘルパー関数とsyntax-ppss
を組み合わせて、現在の行の適切なインデントレベルを計算します。例えば、閉じ括弧()}
)の行は、対応する開き括弧のインデントレベルに合わせる、ぶら下がり演算子の後の行は追加でインデントするなど、Goの慣習に沿ったインデントを実現します。
この再設計により、インデントの正確性と堅牢性が大幅に向上し、gofmt
の出力により近いインデントが可能になりました。
シンタックスハイライト (go--build-font-lock-keywords
)
以前のgo-mode
のシンタックスハイライトは、静的なキーワードリストと比較的単純な正規表現に依存していました。新しいgo--build-font-lock-keywords
関数は、より動的で詳細なハイライトを可能にします。
- Go言語のキーワード、組み込み関数、定数(
go-mode-keywords
,go-builtins
,go-constants
)が明確に定義され、それぞれ異なるフェイス(色)でハイライトされます。 - 関数名、メソッド名、型名などを識別するためのより複雑な正規表現(
go-func-regexp
,go-func-meth-regexp
,go-type-name-regexp
)が導入されました。これにより、宣言、呼び出し、型変換など、様々な文脈での識別子を正確にハイライトできます。 go-fontify-function-calls
というユーザー設定可能な変数を導入することで、関数呼び出しのハイライトをオン/オフできる柔軟性を提供しています。- コメントや文字列のハイライトは、Emacsの標準的な
syntax-propertize-function
とgo-propertize-syntax
関数を通じて、より堅牢に処理されるようになりました。
インポート操作機能
Go言語では、パッケージのインポートはコードの重要な部分です。このコミットでは、インポートの追加、整理、削除をEmacs内で行うための機能が追加されました。
go-import-add
: 新しいパッケージをインポートリストに追加します。import "fmt"
のような単一行インポート、またはimport (...)
のようなグループ化されたインポートブロックの両方に対応しています。また、既にコメントアウトされているインポート行を自動的にアンコメントする機能も備えています。go-remove-unused-imports
:go build
コマンドの出力を解析し、コンパイラが報告する未使用のインポートを特定します。その後、これらの未使用インポート行をバッファから削除するか、コメントアウトすることができます。これは、コードのクリーンアップとビルドエラーの回避に非常に役立ちます。go-goto-imports
: ファイル内のインポートブロックの末尾にカーソルを移動させます。これにより、インポートの追加や確認が容易になります。
これらの機能は、Go開発者が手動で行っていたインポート管理の作業を自動化し、生産性を向上させます。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/b302d68ebcc2500e6e013ff5fed9c70b0e1ff3a4
- Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/7314113
- golang-nuts Google Groups スレッド: https://groups.google.com/group/golang-nuts/browse_frm/thread/3a9d6dae3369c0b5/1efe65e2f7afb190
- 関連するGoイシュー (Fixes):
参考にした情報源リンク
- Emacs Lisp Reference Manual: https://www.gnu.org/software/emacs/manual/elisp.html
- Go Programming Language Specification: https://go.dev/ref/spec
gofmt
documentation: https://go.dev/blog/gofmtgodoc
documentation: https://go.dev/blog/godoc- Go Playground: https://go.dev/play/
- Emacs
syntax-ppss
explanation (Stack Overflow, etc. - general knowledge): https://stackoverflow.com/questions/tagged/emacs-lisp+syntax-ppss (具体的なURLはコミット内容には含まれないが、概念理解のために参照される可能性のある情報源) goflymake
GitHub repository: https://github.com/dougm/goflymakegocode
GitHub repository: https://github.com/nsf/gocodeyasnippet-go
GitHub repository: https://github.com/dominikh/yasnippet-go