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

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

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elに対する互換性改善を目的としています。具体的には、GNU Emacs 23およびXEmacs 21.5.32以降のバージョンでの動作を改善し、特にフォント表示(シンタックスハイライト)に関する問題を修正しています。

コミット

commit cf8434fa3119d14abbc7f1ecec61f0cc947dfac4
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date:   Wed Mar 6 14:35:29 2013 -0500

    misc/emacs: Add compatibility for GNU Emacs 23 and XEmacs >=21.5.32
    
    This CL adds compatibility for GNU Emacs 23 (fixing fontification
    issues) and XEmacs >=21.5.32 (fixing a lot of issues). Earlier
    versions of XEmacs will not be supported because they do not
    support POSIX character classes. Because of that, we also make use
    of a lot of functions that were added in 21.5.32.
    
    A known and currently unfixable issue with XEmacs is that go-mode
    will not always fontify identifiers that use unicode correctly.
    
    All changes for XEmacs are annotated in the diff.
    
    Note: go--position-bytes is not currently used anywhere, but will
    be in a future CL.
    
    Fixes #4927.
    
    R=golang-dev, adonovan, cw, patrick.allen.higgins, sameer
    CC=golang-dev
    https://golang.org/cl/7456051

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

https://github.com/golang/go/commit/cf8434fa3119d14abbc7f1ecec61f0cc947dfac4

元コミット内容

この変更は、GNU Emacs 23およびXEmacs 21.5.32以降のバージョンとの互換性を追加するものです。GNU Emacs 23ではフォント表示の問題を修正し、XEmacsでは多くの問題を修正します。XEmacsの以前のバージョンは、POSIX文字クラスをサポートしていないため、サポートされません。そのため、バージョン21.5.32で追加された多くの関数も利用しています。

XEmacsにおける既知の、そして現在のところ修正不可能な問題として、go-modeがUnicodeを使用する識別子を常に正しくフォント表示できない点が挙げられます。

XEmacsに関するすべての変更は、差分に注釈が付けられています。

注記: go--position-bytesは現在どこでも使用されていませんが、将来の変更リスト(CL)で使用される予定です。

このコミットはIssue #4927を修正します。

変更の背景

このコミットの主な背景は、go-mode.elが当時の主要なEmacsディストリビューションであるGNU EmacsとXEmacsの両方で、より安定して機能するようにすることです。特に、シンタックスハイライト(フォント表示)の不具合や、特定のEmacs Lisp関数の互換性の問題が顕在化していました。

  • GNU Emacs 23のフォント表示問題: 特定のEmacsバージョンでGoコードのシンタックスハイライトが正しく機能しない問題があったと考えられます。これは、Emacsのフォントロック(font-lock)システムが、新しいEmacsバージョンで変更されたか、Go言語の構文解析ロジックが特定のEmacsの正規表現エンジンや構文テーブルの解釈と合致しなかったためと考えられます。
  • XEmacsの互換性問題: XEmacsはGNU Emacsからフォークしたプロジェクトであり、独自の進化を遂げています。特に、正規表現のPOSIX文字クラス(例: [:word:])のサポート状況や、特定のEmacs Lisp関数の存在・挙動がGNU Emacsと異なることが問題でした。コミットメッセージにあるように、XEmacs 21.5.32より前のバージョンではPOSIX文字クラスがサポートされていなかったため、それらのバージョンを切り捨て、21.5.32以降のバージョンに焦点を当てる必要がありました。これにより、より新しいXEmacsの機能を利用できるようになります。
  • Unicode識別子の問題: Go言語はUnicode識別子をサポートしていますが、Emacsの正規表現エンジン、特にXEmacsのそれが、Unicode文字を含む識別子の境界を正確に認識できないという根本的な問題がありました。これは、\_<\_>のような単語境界を示す正規表現アンカーの挙動が、Emacsのバージョンや実装によって異なることに起因します。

これらの問題に対処し、go-mode.elのユーザーエクスペリエンスを向上させることが、この変更の動機となっています。

前提知識の解説

このコミットを理解するためには、以下の知識が役立ちます。

  1. Emacs Lisp (Elisp): Emacsエディタの拡張言語であり、Emacsの動作のほとんどを制御しています。.elファイルはEmacs Lispで書かれたプログラムです。
  2. Emacsのメジャーモード: 特定のプログラミング言語やファイルタイプ(例: Go言語、Python、Markdownなど)に特化した編集機能を提供するEmacsのモードです。シンタックスハイライト、インデント、コード補完などが含まれます。go-mode.elはGo言語用のメジャーモードです。
  3. シンタックスハイライト (Font-Lock): Emacsのfont-lock-modeは、コード内のキーワード、文字列、コメントなどを異なる色やスタイルで表示する機能です。これは、正規表現と構文テーブル(syntax-table)に基づいて行われます。
    • font-lock-keywords: シンタックスハイライトのルールを定義する変数で、正規表現とそれに対応するフェイス(表示スタイル)のリストです。
    • regexp-opt: 複数の文字列から最適な正規表現を生成するEmacs Lisp関数です。
    • syntax-table: Emacsが文字をどのように解釈するか(例: 単語文字、区切り文字、コメント開始文字など)を定義するテーブルです。
  4. 正規表現 (Regular Expressions): テキストパターンを記述するための強力なツールです。Emacs Lispでは、正規表現を使用してテキストを検索・置換したり、構文解析を行ったりします。
    • POSIX文字クラス: [:word:], [:blank:]などの形式で、特定の文字集合を表します。これにより、異なるロケールや文字エンコーディングでも一貫したマッチングが可能になります。
    • 単語境界アンカー \<, \>, \_<, \_>:
      • \<\>は、単語の開始と終了にマッチします。これらは通常、word構文クラスに属する文字とそうでない文字の境界にマッチします。
      • \_<\_>は、より厳密な「シンボル」の境界にマッチします。これは、symbol構文クラスに属する文字とそうでない文字の境界を意味します。Unicode文字を含む識別子を正確に扱うためには、\_<\_>がより適切とされる場合があります。しかし、これらのアンカーのサポートはEmacsのバージョンや実装によって異なります。
  5. GNU EmacsとXEmacs: Emacsは元々Richard Stallmanによって開発されたGNU Emacsが主流ですが、XEmacsはGNU Emacsからフォークした別のEmacs実装です。両者は多くの共通点を持つ一方で、互換性のない機能やAPIの差異が存在します。特に、Emacs Lispの特定の関数名や正規表現エンジンの挙動に違いが見られます。
  6. position-bytes: Emacs Lispの関数で、バッファ内のポイント(カーソル位置)をバイト単位で返すものです。マルチバイト文字(UTF-8など)を扱う際に、文字数ではなくバイト数で位置を計算する必要がある場合に重要になります。XEmacsではこの関数が提供されていないことが問題となります。
  7. completing-read: Emacs Lispの関数で、ユーザーにリストから項目を選択させるための補完機能を提供します。この関数の引数として渡すリストの形式が、Emacsのバージョンによって異なる場合があります。

技術的詳細

このコミットは、go-mode.elが異なるEmacs環境で適切に動作するための複数の技術的課題に対処しています。

  1. XEmacsのバージョン判定と条件分岐:

    • go--xemacs-pマクロを導入し、現在のEmacsがXEmacsであるかどうかをfeaturep 'xemacsで判定しています。これにより、XEmacs固有の挙動や関数名の違いに対応するための条件分岐が可能になります。
    • go--kill-whole-linego--position-bytesのように、GNU EmacsとXEmacsで関数名が異なる、あるいはXEmacsに存在しない関数に対して、fboundp(関数が定義されているかチェック)を使って適切な関数を呼び出すようにしています。position-bytesが存在しないXEmacsでは、point(文字単位の位置)で代替していますが、これはマルチバイト文字を扱う場合に不正確になる可能性があると明記されています。
  2. 正規表現の互換性問題とgo--regexp-enclose-in-symbol:

    • 最も重要な変更点の一つは、正規表現の単語境界アンカーの扱いです。GNU Emacsでは\_<\_>がUnicode識別子を含むシンボル境界を正確に扱うために推奨されますが、XEmacsではこれらがサポートされていません。
    • go--regexp-enclose-in-symbol関数が導入され、XEmacsでは\<\>(通常の単語境界)を使用し、GNU Emacsでは\_<\_>を使用するように切り替えています。
    • この切り替えにより、XEmacsではUnicode文字を含む識別子(例: typeµ)のフォント表示が正しく行われないという既知の制限が生まれています。これは、XEmacsが_を単語構成文字として扱う一方で、\_<のようなシンボル境界アンカーをサポートしないため、typeがキーワードとして認識され、typeµ全体が識別子として認識されない可能性があるためです。
  3. _文字の構文クラスの変更:

    • go-syntax-tableにおいて、_(アンダースコア)文字の構文クラスが_(シンボル構成文字)からw(単語構成文字)に変更されています。
    • これは、XEmacsが\_<アンカーをサポートしないことと関連しています。_を単語構成文字として扱うことで、\<\>がより適切に機能するように調整されています。しかし、これによりGNU Emacsでの\_<の意図する挙動とは異なる結果になる可能性があります。
  4. font-lock-keywordsの更新:

    • go--build-font-lock-keywords関数内で、キーワード、組み込み関数、定数などの正規表現生成にgo--regexp-enclose-in-symbolが適用されています。これにより、Go言語のキーワード(func, type, map, chan, new, make, goto, break, continueなど)が、Emacsのバージョンに応じて適切な単語境界アンカーで囲まれるようになります。
    • regexp-optの第2引数が'symbolsからtに変更されています。これは、Emacs 24より前のバージョンが'symbolsを理解しないため、より広範な互換性を持たせるための変更です。tは、regexp-optが与えられた文字列をそのまま正規表現として扱うことを意味します。
  5. completing-readのリスト形式の調整:

    • go--old-completion-list-style関数が追加され、completing-readに渡すリストの形式を調整しています。古いEmacsバージョンでは、補完リストが("item1" "item2")のような単純な文字列リストではなく、(("item1" nil) ("item2" nil))のような連想リストの形式を期待する場合があります。この関数は、単純な文字列リストを後者の形式に変換します。
  6. シェルコマンド実行の改善:

    • go-root-and-paths関数で、go env GOROOT GOPATHコマンドの実行にprocess-linesではなくshell-command-to-stringを使用するように変更されています。process-linesは外部プロセスを非同期で実行し、その出力を処理するのに対し、shell-command-to-stringはコマンドの出力を文字列として直接取得します。これは、よりシンプルで同期的なコマンド実行が必要な場合に適しています。
  7. ファイルシステム操作のカスタム実装:

    • find-lispライブラリへの依存を減らすため、go--string-prefix-pgo--directory-dirsというカスタム関数が導入されています。
    • go--string-prefix-pは、文字列が別の文字列のプレフィックスであるかをチェックするもので、string-prefix-pの代替として使用されます。
    • go--directory-dirsは、指定されたディレクトリ内のすべてのサブディレクトリを再帰的にリストアップするものです。これは、go-packages関数内でパッケージディレクトリを探索するために使用されます。これにより、find-lisp-find-files-internalのような外部ライブラリの関数に依存することなく、ディレクトリ構造を走査できるようになります。

これらの変更は、Emacs Lispの異なるバージョンや実装間での挙動の差異を吸収し、go-mode.elの堅牢性と互換性を高めることを目的としています。特に、正規表現の挙動の違いはシンタックスハイライトに直接影響するため、慎重な対応が求められています。

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

misc/emacs/go-mode.elファイルが変更されています。主な変更点は以下の通りです。

  1. XEmacs互換性関数の追加:

    • go--xemacs-p (マクロ)
    • go--kill-whole-line (エイリアス)
    • go--position-bytes (エイリアス/関数)
    • go--old-completion-list-style (関数)
    • go--regexp-enclose-in-symbol (関数)
  2. 正規表現定数の変更:

    • go-identifier-regexp: _が文字クラスから削除。
    • go-type-regexp: _が文字クラスから削除。
    • go-func-regexp, go-func-meth-regexp: go--regexp-enclose-in-symbolを使用するように変更。
  3. 構文テーブルの変更 (go-syntax-table):

    • /の構文エントリがXEmacsとGNU Emacsで条件分岐。
    • _の構文エントリが_からwに変更。
  4. フォントロックキーワードの構築 (go--build-font-lock-keywords):

    • regexp-optの引数を'symbolsからtに変更。
    • キーワード、組み込み関数、定数、型、チャネル、new/makegoto/break/continueなどの正規表現にgo--regexp-enclose-in-symbolを適用。
  5. ユーティリティ関数の変更:

    • go--skip-whitespace-backward: skip-chars-backwardの引数を変更。
    • godoc--get-buffer, go-import-package: go--old-completion-list-styleを使用。
    • go-root-and-paths: process-linesからshell-command-to-stringに変更。
    • go-packages: string-prefix-pの代わりにgo--string-prefix-pを使用し、find-lisp-find-files-internalの代わりにgo--directory-dirsを使用。
  6. インポート削除関数 (go-remove-imports):

    • kill-lineの代わりにgo--kill-whole-lineを使用。

コアとなるコードの解説

go--regexp-enclose-in-symbol 関数

(defun go--regexp-enclose-in-symbol (s)
  ;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make
  ;; extensive use of \_< to support unicode in identifiers. Until we
  ;; come up with a better solution for XEmacs, this solution will
  ;; break fontification in XEmacs for identifiers such as "typeµ".
  ;; XEmacs will consider "type" a keyword, GNU Emacs won't.

  (if (go--xemacs-p)
      (concat "\\<" s "\\>")
    (concat "\\_<" s "\\_>")))

この関数は、正規表現の文字列sを単語境界アンカーで囲む役割を担います。Emacs Lispの正規表現では、\<\>は一般的な単語境界を示しますが、\_<\_>はより厳密な「シンボル」の境界を示し、Unicode文字を含む識別子を正確に扱うためにGNU Emacsで導入されました。

しかし、XEmacsは\_<\_>をサポートしていません。そのため、この関数はgo--xemacs-pで現在の環境がXEmacsであるかを判定し、XEmacsの場合は互換性のある\<\>を使用します。GNU Emacsの場合は、より高機能な\_<\_>を使用します。

この条件分岐の副作用として、XEmacsではUnicode文字を含む識別子(例: typeµ)のフォント表示が正しく行われない可能性があることがコメントで明記されています。これは、XEmacsがtypeをキーワードとして認識し、µを別の文字として扱うため、全体が正しくハイライトされないためです。

go-syntax-table_ 構文エントリ変更

    ;; It would be nicer to have _ as a symbol constituent, but that
    ;; would trip up XEmacs, which does not support the \_< anchor
    (modify-syntax-entry ?_  "w" st)

Go言語の識別子にはアンダースコア(_)が含まれることがありますが、Emacsの構文テーブルにおける_の扱いが、正規表現の単語境界アンカーの挙動に影響を与えます。

本来であれば、_をシンボル構成文字(_)として定義することが望ましいですが、XEmacsが\_<アンカーをサポートしないため、このコミットでは_を単語構成文字(w)として定義しています。これにより、XEmacs環境で\<\>_を含む識別子に対してより適切に機能するように調整されています。この変更は、XEmacsでのフォント表示の互換性を優先した結果です。

go-packages 関数におけるファイルシステム走査の変更

          (mapcan (lambda (dir)
                    (mapcar (lambda (file)
                              (let ((sub (substring file (length pkgdir) -2)))\n-                               (unless (or (string-prefix-p "obj/" sub) (string-prefix-p "tool/" sub))\n+                               (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))\n                                  (mapconcat 'identity (cdr (split-string sub "/")) "/"))))\n                            (if (file-directory-p dir)\n                                (directory-files dir t "\\.a$"))))\n                  (if (file-directory-p pkgdir)\n-                     (find-lisp-find-files-internal pkgdir 'find-lisp-file-predicate-is-directory 'find-lisp-default-directory-predicate)))))\n+                     (go--directory-dirs pkgdir)))))\n```

`go-packages`関数は、Goのパッケージを検索し、補完リストなどを生成するために使用されます。このコミットでは、パッケージディレクトリの走査方法が変更されています。

以前は`find-lisp-find-files-internal`という関数(`find-lisp`ライブラリの一部)を使用していましたが、これは外部ライブラリへの依存を意味します。この変更では、`find-lisp`への依存を排除し、代わりに新しく定義されたカスタム関数`go--directory-dirs`を使用しています。

`go--directory-dirs`は、指定されたディレクトリ内のすべてのサブディレクトリを再帰的にリストアップするシンプルなEmacs Lisp関数です。これにより、`go-mode.el`は外部ライブラリに依存することなく、Goのパッケージ構造を探索できるようになり、より自己完結的で互換性の高いコードベースになります。また、`string-prefix-p`の代わりに`go--string-prefix-p`というカスタム関数を使用している点も、互換性向上のための変更です。

これらの変更は、Emacs Lispの異なるバージョンや実装間での挙動の差異を吸収し、`go-mode.el`の堅牢性と互換性を高めることを目的としています。特に、正規表現の挙動の違いはシンタックスハイライトに直接影響するため、慎重な対応が求められています。

## 関連リンク

*   Go Issue #4927: [https://github.com/golang/go/issues/4927](https://github.com/golang/go/issues/4927)
*   Gerrit Change-ID: [https://golang.org/cl/7456051](https://golang.org/cl/7456051)

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

*   GNU Emacs Manual: [https://www.gnu.org/software/emacs/manual/](https://www.gnu.org/software/emacs/manual/)
*   XEmacs Documentation: [http://www.xemacs.org/Documentation/](http://www.xemacs.org/Documentation/)
*   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 Wiki - Font Lock: [https://www.emacswiki.org/emacs/FontLock](https://www.emacswiki.org/emacs/FontLock)
*   Emacs Wiki - Regular Expression: [https://www.emacswiki.org/emacs/RegularExpression](https://www.emacswiki.org/emacs/RegularExpression)
*   Emacs Wiki - XEmacs: [https://www.emacswiki.org/emacs/XEmacs](https://www.emacswiki.org/emacs/XEmacs)
*   Emacs Lisp: `position-bytes` (GNU Emacs 24.1で導入): [https://www.gnu.org/software/emacs/manual/html_node/elisp/Text-Properties.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Text-Properties.html) (このコミットの時期にはXEmacsにはなかった可能性が高い)
*   Emacs Lisp: `regexp-opt` (GNU Emacs 24.1で`'symbols`引数が導入): [https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Functions.html](https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Functions.html)