[インデックス 15926] ファイルの概要
このコミットは、EmacsのGo言語用メジャーモードであるgo-mode.el
の定義を改善し、Emacs 24.1以降で導入されたprog-mode
を親モードとして利用するように変更するものです。これにより、go-mode
はprog-mode
が提供する共通のプログラミングモード機能群を継承し、コードの重複を避け、将来的な互換性と機能拡張性を向上させます。古いEmacsバージョンやXEmacsとの互換性を保つためのフォールバックロジックも含まれています。
コミット
commit 4ad1a87f8573b355b814589c63b75a7657df4069
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date: Mon Mar 25 08:58:13 2013 -0700
misc/emacs: Derive mode from prog-mode if possible
R=golang-dev, bradfitz
CC=adonovan, cw, golang-dev, patrick.allen.higgins
https://golang.org/cl/7956044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4ad1a87f8573b355b814589c63b75a7657df4069
元コミット内容
misc/emacs: Derive mode from prog-mode if possible
R=golang-dev, bradfitz
CC=adonovan, cw, golang-dev, patrick.allen.higgins
https://golang.org/cl/7956044
変更の背景
この変更の背景には、Emacsのメジャーモードの設計思想の進化があります。Emacs 24.1でprog-mode
が導入される以前は、各プログラミング言語のメジャーモードは、通常fundamental-mode
(基本的なテキスト編集モード)から派生し、それぞれのモードでプログラミングに特化した機能(コメントの扱い、インデント、シンタックスハイライトなど)を個別に実装する必要がありました。
しかし、多くのプログラミング言語モードで共通して必要とされる機能が多く存在するため、これらの共通機能を一元的に提供するprog-mode
が設計されました。prog-mode
は、プログラミング言語モードの共通の親として機能し、例えばコメントの自動挿入、括弧の対応、基本的なインデントルールなど、多くのプログラミングモードで共有されるべき振る舞いを定義します。
このコミットは、go-mode
がfundamental-mode
から直接派生するのではなく、より適切な親であるprog-mode
から派生するように変更することで、以下の利点を得ることを目的としています。
- コードの簡素化と再利用:
prog-mode
が提供する共通機能をgo-mode
が自動的に継承できるようになり、go-mode.el
内でそれらの機能を再実装する必要がなくなります。 - 一貫性の向上: Emacsの他のプログラミングモードとの間で、基本的な振る舞いの一貫性が向上します。
- 将来的な互換性:
prog-mode
の改善や新機能が、go-mode
にも自動的に適用されるようになります。 - 保守性の向上: 共通機能のバグ修正や改善が
prog-mode
で行われれば、それを継承するすべてのモードに恩恵がもたらされます。
ただし、この変更はEmacs 24.1以降のバージョンに依存するため、古いEmacsバージョンやXEmacsのようなprog-mode
をサポートしない環境との互換性を維持するための考慮も必要とされました。
前提知識の解説
このコミットを理解するためには、以下のEmacs LispおよびEmacsのモードに関する基本的な知識が必要です。
Emacs Lisp (Elisp)
Emacsの拡張言語であり、Emacs自体がEmacs Lispで書かれています。Emacsの動作をカスタマイズしたり、新しい機能を追加したりするために使用されます。
メジャーモード (Major Mode)
Emacsのバッファ(ファイルの内容を表示する領域)は、常に一つのメジャーモードに関連付けられています。メジャーモードは、特定の種類のテキスト(例: プログラミング言語のソースコード、プレーンテキスト、Markdownファイルなど)を編集するための主要な機能セットを提供します。これには、シンタックスハイライト、インデントルール、特定のコマンドなどが含まれます。例えば、Go言語のソースコードを編集する際にはgo-mode
が、Pythonのソースコードを編集する際にはpython-mode
が使用されます。
define-derived-mode
Emacs Lispで新しいメジャーモードを定義するためのマクロです。このマクロは、既存のモード(親モード)から派生して新しいモードを作成する際に使用されます。親モードの機能や設定を継承しつつ、新しいモード固有のカスタマイズを追加できます。
構文は以下のようになります。
(define-derived-mode NEW-MODE PARENT-MODE NAME DOCSTRING BODY...)
NEW-MODE
: 新しく定義するモードの名前(シンボル)。PARENT-MODE
: 派生元の親モードの名前(シンボル)。NAME
: モードラインに表示されるモードの短い名前(文字列)。DOCSTRING
: モードの目的を説明するドキュメント文字列。BODY
: モードがアクティブになったときに実行されるEmacs Lispコード。
fundamental-mode
Emacsで最も基本的なメジャーモードです。特別な機能やシンタックスハイライト、インデントルールなどは提供せず、純粋なテキスト編集機能のみを提供します。多くのプログラミングモードは、prog-mode
が導入される前はfundamental-mode
から派生していました。
prog-mode
Emacs 24.1で導入された、プログラミング言語モードのための汎用的なメジャーモードです。fundamental-mode
の上に構築され、すべてのプログラミング言語モードに共通する基本的な機能(コメントの扱い、基本的なインデント、括弧の対応など)を提供します。これにより、個々の言語モードはprog-mode
から派生することで、これらの共通機能を再実装する手間を省き、言語固有の機能に集中できるようになります。
fboundp
Emacs Lispの関数で、引数として与えられたシンボルが関数として定義されているかどうかをチェックします。このコミットでは、prog-mode
が現在のEmacs環境で利用可能かどうか(関数として定義されているか)を判断するために使用されています。
defalias
Emacs Lispの関数で、あるシンボルを別のシンボルのエイリアス(別名)として定義します。これにより、既存の関数や変数を新しい名前で参照できるようになります。このコミットのコメントでは、XEmacsでの互換性の問題がなければdefalias
を使いたかったと述べられています。
XEmacs
Emacsの派生版の一つで、GNU Emacsとは異なる開発ラインを持っています。一部の機能やAPIの互換性がない場合があります。このコミットでは、XEmacsがprog-mode
をサポートしていない可能性や、defalias
の挙動の違いが考慮されています。
技術的詳細
このコミットの技術的な核心は、go-mode.el
がdefine-derived-mode
を使用してgo-mode
を定義する際に、その親モードをfundamental-mode
からprog-mode
に変更することにあります。しかし、この変更はprog-mode
が利用可能なEmacs 24.1以降の環境でのみ有効である必要があります。古いEmacsバージョンやXEmacsではprog-mode
が存在しないため、エラーを避けるためのフォールバックメカニズムが必要です。
コミットは以下のロジックでこの問題を解決しています。
-
prog-mode
の存在チェック:(if (not (fboundp 'prog-mode)) (define-derived-mode prog-mode fundamental-mode "" ""))
このコードは、
prog-mode
という関数(またはモード)が現在のEmacs環境で定義されているかどうかをfboundp
関数を使ってチェックします。- もし
prog-mode
が定義されていない場合(fboundp 'prog-mode
がnil
を返す場合)、prog-mode
をfundamental-mode
から派生した空のモードとしてdefine-derived-mode
を使って定義します。 - この「ダミーの
prog-mode
」は、実質的にはfundamental-mode
と同じ振る舞いをしますが、go-mode
がprog-mode
を親として定義する際に、シンボルprog-mode
が存在しないことによるエラーを防ぎます。これにより、古いEmacsバージョンやXEmacsでもgo-mode
が正常にロードされるようになります。
- もし
-
go-mode
の親モード変更:-(define-derived-mode go-mode fundamental-mode "Go" +(define-derived-mode go-mode prog-mode "Go"
go-mode
の定義において、親モードがfundamental-mode
からprog-mode
に変更されています。- Emacs 24.1以降の環境では、この
prog-mode
はEmacs標準のprog-mode
を指し、go-mode
はプログラミングモード共通の機能を継承します。 - 古いEmacsバージョンやXEmacsでは、上記で定義されたダミーの
prog-mode
を親とすることになります。このダミーのprog-mode
はfundamental-mode
から派生しているため、結果的にgo-mode
は以前と同様にfundamental-mode
の機能セットを継承することになり、互換性が保たれます。
- Emacs 24.1以降の環境では、この
このアプローチは、新しいEmacsの機能を利用しつつ、古い環境での動作を損なわないための一般的なパターンです。コメントにあるように、理想的にはdefalias
を使ってprog-mode
をfundamental-mode
にエイリアスすることでより簡潔に記述できたかもしれませんが、XEmacsでの互換性の問題があったため、このような条件付きのdefine-derived-mode
が選択されました。
コアとなるコードの変更箇所
変更はmisc/emacs/go-mode.el
ファイル内で行われています。
--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -52,6 +52,14 @@
(defun go--old-completion-list-style (list)
(mapcar (lambda (x) (cons x nil)) list))\n
+;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not.
+;; Ideally we\'d use defalias instead, but that breaks in XEmacs.
+;;\n
+;; TODO: If XEmacs decides to add prog-mode, change this to use
+;; defalias to alias prog-mode or fundamental-mode to go--prog-mode
+;; and use that in define-derived-mode.\n
+(if (not (fboundp \'prog-mode))\n
+ (define-derived-mode prog-mode fundamental-mode \"\" \"\"))\n
\n (defun go--regexp-enclose-in-symbol (s)\n ;; XEmacs does not support \\_<, GNU Emacs does. In GNU Emacs we make
@@ -370,7 +378,7 @@ current line will be returned.\"\n (forward-char))))\n \n ;;;###autoload\n-(define-derived-mode go-mode fundamental-mode \"Go\"\n+(define-derived-mode go-mode prog-mode \"Go\"\n \"Major mode for editing Go source text.\n \n This mode provides (not just) basic editing capabilities for\n```
## コアとなるコードの解説
変更は主に2つの部分に分けられます。
1. **`prog-mode`のフォールバック定義(追加された部分)**:
```elisp
;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not.
;; Ideally we'd use defalias instead, but that breaks in XEmacs.
;;
;; TODO: If XEmacs decides to add prog-mode, change this to use
;; defalias to alias prog-mode or fundamental-mode to go--prog-mode
;; and use that in define-derived-mode.
(if (not (fboundp 'prog-mode))
(define-derived-mode prog-mode fundamental-mode "" ""))
```
このブロックは、`go-mode.el`がロードされる際に実行されます。
- `fboundp 'prog-mode)`: `prog-mode`という名前の関数(またはモード)がEmacsに存在するかどうかをチェックします。
- `(not (fboundp 'prog-mode))`: `prog-mode`が存在しない場合に真となります。
- `(define-derived-mode prog-mode fundamental-mode "" "")`: `prog-mode`が存在しない場合、`fundamental-mode`から派生した新しいメジャーモード`prog-mode`を定義します。この定義は、モードラインに表示される名前もドキュメント文字列も空の、実質的に何もしないダミーのモードです。これにより、後続の`go-mode`の定義で`prog-mode`が親として指定されても、シンボルが見つからないというエラーが発生しなくなります。
- コメントでは、GNU Emacs 24以降で`prog-mode`が利用可能であること、古いEmacsやXEmacsでは利用できないこと、そして理想的には`defalias`を使いたかったがXEmacsで問題があることが説明されています。また、将来的にXEmacsが`prog-mode`をサポートした場合のTODOも記されています。
2. **`go-mode`の親モードの変更(修正された部分)**:
```diff
-(define-derived-mode go-mode fundamental-mode "Go"
+(define-derived-mode go-mode prog-mode "Go"
```
これは`go-mode`の実際の定義部分です。
- 変更前は`(define-derived-mode go-mode fundamental-mode "Go" ...)`となっており、`go-mode`は`fundamental-mode`から直接派生していました。
- 変更後は`(define-derived-mode go-mode prog-mode "Go" ...)`となり、`go-mode`は`prog-mode`から派生するようになりました。
- この変更により、Emacs 24.1以降の環境では`go-mode`が`prog-mode`の共通機能を継承し、古い環境では上記で定義されたダミーの`prog-mode`(実質的には`fundamental-mode`)を継承することで、後方互換性を維持しています。
このコミットは、Emacsの進化に合わせて`go-mode`の基盤を更新しつつ、幅広いEmacsユーザーが利用できるように互換性を考慮した堅牢な実装を示しています。
## 関連リンク
* Go言語: [https://go.dev/](https://go.dev/)
* Emacs: [https://www.gnu.org/software/emacs/](https://www.gnu.org/software/emacs/)
* Emacs Lisp Reference Manual: [https://www.gnu.org/software/emacs/manual/html_node/elisp/](https://www.gnu.org/software/emacs/manual/html_node/elisp/)
## 参考にした情報源リンク
* Emacs `prog-mode`に関する情報 (Web検索結果より):
* emacsredux.com: `prog-mode`の導入と目的について
* gnu.org: `prog-mode`と`prog-mode-hook`に関する公式ドキュメント
* github.com / stackoverflow.com: `prog-mode`の利用例や関連する議論
* Git commit `4ad1a87f8573b355b814589c63b75a7657df4069` (golang/go): [https://github.com/golang/go/commit/4ad1a87f8573b355b814589c63b75a7657df4069](https://github.com/golang/go/commit/4ad1a87f8573b355b814589c63b75a7657df4069)
* Gerrit Change-Id `7956044` (golang.org/cl): [https://golang.org/cl/7956044](https://golang.org/cl/7956044) (これはコミットメッセージに記載されているリンクであり、詳細なレビュー情報が含まれている可能性があります。)