[インデックス 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
gofmtdocumentation: https://go.dev/blog/gofmtgodocdocumentation: https://go.dev/blog/godoc- Go Playground: https://go.dev/play/
- Emacs
syntax-ppssexplanation (Stack Overflow, etc. - general knowledge): https://stackoverflow.com/questions/tagged/emacs-lisp+syntax-ppss (具体的なURLはコミット内容には含まれないが、概念理解のために参照される可能性のある情報源) goflymakeGitHub repository: https://github.com/dougm/goflymakegocodeGitHub repository: https://github.com/nsf/gocodeyasnippet-goGitHub repository: https://github.com/dominikh/yasnippet-go