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

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

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elにおける、typeキーワードのシンタックスハイライト(fontification)の挙動を修正するものです。具体的には、typeキーワードの後に少なくとも1つのスペースが続く場合にのみ型としてハイライトされるように正規表現が調整され、また型スイッチの閉じ括弧が誤って型としてハイライトされる問題が修正されています。これにより、Emacs上でのGoコードの可読性が向上します。

コミット

commit 79653e412132c02b455ea010733c3adfe40c7313
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date:   Mon Jan 6 11:11:03 2014 -0500

    misc/emacs: fontify type switch correctly
    
    Require at least one space after "type" and do not fontify closing
    parenthesis of type switch as a type.
    
    R=adonovan
    CC=golang-codereviews
    https://golang.org/cl/37720050

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

https://github.com/golang/go/commit/79653e412132c02b455ea010733c3adfe40c7313

元コミット内容

misc/emacs: fontify type switch correctly

Require at least one space after "type" and do not fontify closing
parenthesis of type switch as a type.

R=adonovan
CC=golang-codereviews
https://golang.org/cl/37720050

変更の背景

このコミットの背景には、EmacsのGoモード(go-mode.el)におけるシンタックスハイライトの不正確さがありました。特に、Go言語の「型スイッチ(type switch)」構文において、typeキーワードの後のスペースの有無や、型スイッチの閉じ括弧が誤って型として認識され、不適切な色付けがされるという問題が存在していました。

Go言語の型スイッチは、インターフェース変数の動的な型を判定し、それに基づいて異なる処理を行うための強力な機能です。その構文は以下のようになります。

switch v := i.(type) {
case int:
    fmt.Printf("Integer: %d\n", v)
case string:
    fmt.Printf("String: %s\n", v)
default:
    fmt.Printf("Unknown type\n")
}

このi.(type)の部分が型スイッチの構文であり、typeはキーワードとして特別な意味を持ちます。しかし、既存のgo-mode.elの正規表現では、このtypeキーワードの後に続くパターンを正確に識別できていなかったため、以下のような問題が発生していました。

  1. typeキーワードの後のスペースの扱い: typeキーワードの直後にスペースがない場合でも、正規表現がマッチしてしまい、意図しない部分が型としてハイライトされる可能性がありました。これは、typeがキーワードとして使われる文脈と、例えば変数名の一部として使われる場合などを区別するために重要です。
  2. 型スイッチの閉じ括弧の誤認識: i.(type)の閉じ括弧)が、正規表現によって誤って型の一部として認識され、不適切にハイライトされることがありました。これは、シンタックスハイライトの目的であるコードの構造と意味の明確化を妨げるものでした。

これらの問題を解決し、Emacs上でのGoコードのシンタックスハイライトをより正確にすることで、開発者のコードリーディング体験を向上させることが、このコミットの主な目的です。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. Emacs Lisp (Elisp): Emacsエディタの拡張言語であり、Emacsの動作をカスタマイズしたり、新しい機能を追加したりするために使用されます。go-mode.elはElispで書かれたファイルであり、Go言語の編集モードを定義しています。
  2. シンタックスハイライト (Fontification): プログラミング言語のキーワード、変数、文字列、コメントなどを異なる色やスタイルで表示することで、コードの可読性を高める機能です。Emacsでは「fontification」と呼ばれ、font-lock-modeによって制御されます。シンタックスハイライトのルールは、通常、正規表現(Regular Expression)を用いて定義されます。
  3. 正規表現 (Regular Expression): 文字列のパターンを記述するための強力なツールです。このコミットでは、Emacs Lispの正規表現が使用されており、特に以下の要素が重要です。
    • concat: 複数の文字列を結合するElisp関数。
    • go--regexp-enclose-in-symbol: go-mode.el内で定義されているヘルパー関数で、特定のキーワードをシンボルとして認識するための正規表現を生成します。これにより、例えばtypeという単語が、mytypeのような別の単語の一部としてではなく、独立したキーワードとして扱われるようになります。
    • [[:space:]]*: 0個以上の空白文字(スペース、タブなど)にマッチします。
    • [[:space:]]+: 1個以上の空白文字にマッチします。この変更がこのコミットの核心部分です。
    • \\(\\): キャプチャグループを定義します。マッチした部分文字列を後で参照するために使用されます。
    • [^[:space:]]+: 1個以上の空白文字以外の文字にマッチします。
    • go-identifier-regexp: Go言語の識別子(変数名、関数名など)にマッチする正規表現。
    • go-type-name-regexp: Go言語の型名にマッチする正規表現。
  4. Go言語の型スイッチ (Type Switch): Go言語の特定の構文で、インターフェース変数の動的な型を判定し、その型に基づいて異なるコードブロックを実行するために使用されます。構文はswitch v := i.(type) { ... }の形式を取ります。

これらの知識を組み合わせることで、go-mode.elがどのようにGoコードを解析し、シンタックスハイライトを適用しているか、そしてこのコミットがそのプロセスをどのように改善しているかを理解することができます。

技術的詳細

このコミットは、misc/emacs/go-mode.elファイル内の正規表現の定義を修正することで、Go言語のtypeキーワードのシンタックスハイライトの精度を向上させています。具体的には、font-lock-type-face(型をハイライトするためのスタイル)が適用される正規表現パターンが変更されています。

変更された行は以下の2行です。

変更前:

     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types

変更後:

     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types

この変更の核心は、正規表現内の[[:space:]]*[[:space:]]+に置き換えられた点です。

  • [[:space:]]*: これは「0個以上の空白文字」にマッチします。つまり、typeキーワードの直後に空白がなくてもマッチしてしまいます。
  • [[:space:]]+: これは「1個以上の空白文字」にマッチします。これにより、typeキーワードの直後に少なくとも1つの空白文字が続く場合にのみ、この正規表現がマッチするようになります。

この変更がもたらす具体的な影響は以下の通りです。

  1. typeキーワードの厳密なマッチング: Go言語においてtypeキーワードが型定義や型スイッチで使用される場合、通常その後にスペースが続きます。この変更により、typeの直後にスペースがないような、意図しない文脈でのtypeのハイライトが防がれます。例えば、mytypeのような変数名の一部としてのtypeが誤ってハイライトされることがなくなります。
  2. 型スイッチの閉じ括弧の誤認識の修正: コミットメッセージには「do not fontify closing parenthesis of type switch as a type」とありますが、これは直接的な正規表現の変更ではなく、[[:space:]]+への変更によって間接的に解決される問題であると考えられます。typeキーワードの後のパターンマッチがより厳密になることで、i.(type)のような型スイッチ構文において、typeの後の)が誤って型の一部として認識されることがなくなります。以前の[[:space:]]*では、type)のようなパターンもマッチしてしまう可能性がありましたが、[[:space:]]+にすることで、type)の間にスペースがないため、このパターンはマッチしなくなります。これにより、閉じ括弧が型としてハイライトされることがなくなります。

これらの変更は、Emacsのfont-lock-modeがGoコードを解析し、適切なシンタックスハイライトを適用する際の精度を大幅に向上させ、開発者にとってより正確で視覚的に分かりやすいコード表示を提供します。

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

変更はmisc/emacs/go-mode.elファイル内の2箇所です。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -264,8 +264,8 @@ For mode=set, all covered lines will have this weight.\"\
      `((,go-func-meth-regexp 1 font-lock-function-name-face))) ;; method name
 
    `(\
-     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
-     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
+     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
+     (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
      (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
      (,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face)
      (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type

コアとなるコードの解説

このコミットで変更されたのは、go-mode.el内のfont-lock-keywordsリストの一部です。このリストは、EmacsがGoコードをシンタックスハイライトする際に使用する正規表現と、それに対応するハイライトスタイル(face)のペアを定義しています。

変更された2つの正規表現パターンは、Go言語の「型(types)」を識別するためのものです。

  1. 最初のパターン:

    • 変更前: (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face)
    • 変更後: (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face)

    このパターンは、typeキーワードの後に続く型名をハイライトすることを目的としています。

    • (go--regexp-enclose-in-symbol "type"): typeという単語が独立したキーワードとして扱われるように正規表現を生成します。
    • [[:space:]]* から [[:space:]]+ への変更: ここが最も重要な変更点です。これにより、typeキーワードの直後に「少なくとも1つの空白文字」が続く場合にのみマッチするようになります。これにより、typeがキーワードとして使われる文脈(例: type MyStruct struct {}switch v := i.(type))でのみ正しくハイライトされるようになります。
    • \\([^[:space:]]+\\): 1つ以上の空白文字以外の文字(つまり型名)をキャプチャします。
    • 1 font-lock-type-face: キャプチャグループ1(型名)にfont-lock-type-faceというハイライトスタイルを適用することを示します。
  2. 二番目のパターン:

    • 変更前: (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face)
    • 変更後: (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face)

    このパターンは、より複雑な型定義、特に構造体やインターフェースのフィールドにおける型をハイライトすることを目的としている可能性があります。

    • 同様に、[[:space:]]* から [[:space:]]+ への変更が適用されています。
    • go-identifier-regexp: Goの識別子(フィールド名など)にマッチします。
    • go-type-name-regexp: Goの型名にマッチします。

これらの変更により、go-mode.eltypeキーワードの後の文脈をより正確に解釈し、シンタックスハイライトの誤りを減らすことができます。特に、型スイッチ構文におけるi.(type)のようなケースで、typeキーワードが正しく認識され、その後の閉じ括弧が誤ってハイライトされることがなくなります。これは、正規表現のマッチングがより厳密になった結果として得られる副作用です。

関連リンク

参考にした情報源リンク