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

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

このコミットは、EmacsのGoモード(go-mode.el)におけるシンタックスハイライトの挙動を修正するものです。具体的には、Go言語の組み込み関数名が変数名として使用されている場合に、誤って組み込み関数としてハイライトされる問題を解決します。これにより、「new」のような頻繁に変数名として使われる組み込み関数名が、関数呼び出しでない限りハイライトされないようになります。

コミット

commit 5f5e280e14efde7c576c282fc7ee3a2c734eb8c5
Author: Rui Ueyama <ruiu@google.com>
Date:   Thu Mar 27 17:35:07 2014 -0400

    misc/emacs: do not highlight built-in function if not followed by '('
    
    Name of built-in function is not reserved word in Go, and you can
    use it as variable name. "new" is often used as local variable, for
    instance.
    
    This patch is to apply font-lock-builtin-face only when built-in
    function name is followed by '(', so that it doesn't highlight
    non-function variable that happen to have the same name as built-in
    function.
    
    LGTM=dominik.honnef
    R=golang-codereviews, dominik.honnef, adonovan
    CC=golang-codereviews
    https://golang.org/cl/79260043

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

https://github.com/golang/go/commit/5f5e280e14efde7c576c282fc7ee3a2c734eb8c5

元コミット内容

Go言語の組み込み関数名が、その後に括弧 ( が続かない場合にはハイライトしないように、EmacsのGoモード(go-mode.el)の挙動を変更します。Goでは組み込み関数名が予約語ではないため、変数名として使用されることがあり、特に「new」のような名前はローカル変数として頻繁に利用されます。この変更は、組み込み関数名と同じ名前を持つ非関数変数が誤ってハイライトされるのを防ぐことを目的としています。

変更の背景

Go言語の設計上の特徴として、newmakeといった組み込み関数(built-in functions)の名前が、言語の予約語(reserved words)ではないという点があります。これは、開発者がこれらの名前を通常の変数名や関数名として自由に利用できることを意味します。例えば、newという名前の変数を宣言することはGo言語の文法上全く問題ありません。

しかし、EmacsのGoモード(go-mode.el)は、以前のバージョンでは組み込み関数名を一律にハイライトする設定になっていました。このため、コード内で組み込み関数名と同じ名前の変数が使われている場合でも、それが関数呼び出しではないにもかかわらず、組み込み関数としてハイライトされてしまうという問題がありました。これは、特にnewのように頻繁に変数名として使われる名前において、コードの可読性を損ねる原因となっていました。

このコミットは、このような誤ったハイライトを是正し、EmacsのGoモードがより正確にGoコードのセマンティクスを反映するようにするために行われました。具体的には、組み込み関数名が実際に「関数呼び出し」として使われている(つまり、その直後に(が続く)場合にのみハイライトを行うように変更することで、この問題を解決しています。

前提知識の解説

Go言語の予約語と組み込み関数

Go言語には、特定の意味を持つキーワード(var, func, if, forなど)が予約語として存在し、これらは変数名や関数名として使用できません。しかし、len, cap, new, make, panic, recover, print, println, complex, real, imag, close, delete, append, copyといった組み込み関数は、予約語ではありません。これは、これらの名前を開発者が変数名や型名として再定義できることを意味します。例えば、var new intのような宣言はGoでは有効です。この柔軟性が、Emacsのハイライトにおける問題の根源となっていました。

Emacsのfont-lock-modeとシンタックスハイライト

Emacsのfont-lock-modeは、ソースコードのシンタックスハイライト(構文強調表示)を提供する主要な機能です。これは、テキストバッファ内の特定のパターン(通常は正規表現)を検出し、それらに対応する「フェイス」(色、フォント、スタイルなどの視覚的属性のセット)を適用することで機能します。

font-lock-modeは、各メジャーモード(例: go-modepython-modeなど)が定義するfont-lock-keywordsという変数に依存します。この変数には、ハイライトのルールを記述したリストが格納されており、各ルールは通常、正規表現とそれに適用するフェイスのペアで構成されます。

  • font-lock-keyword-face: if, for, funcなどの言語のキーワードに適用されるフェイス。
  • font-lock-builtin-face: new, makeなどの組み込み関数や組み込み型に適用されるフェイス。
  • font-lock-function-name-face: ユーザー定義の関数名に適用されるフェイス。

Emacs Lispの正規表現関連関数

Emacs Lispでは、正規表現を操作するためのいくつかの便利な関数が提供されています。

  • regexp-opt: 複数の文字列のリストを受け取り、それらのいずれかにマッチする最適化された単一の正規表現を生成します。例えば、 (regexp-opt '("foo" "bar"))\\(?:foo\\|bar\\) のような正規表現を返します。これは、多数のキーワードを効率的にマッチさせる際に非常に有用です。
  • go--regexp-enclose-in-symbol: go-mode.el内で定義されているヘルパー関数で、与えられた正規表現をシンボル(単語の境界)で囲むためのものです。これにより、例えば「new」が「renewal」の一部としてマッチするのを防ぎ、「new」という単語全体としてのみマッチするようにします。内部的には\\b(単語境界)を使用していると考えられます。
  • concat: 複数の文字列を結合して新しい文字列を生成します。正規表現の構築において、動的にパターンを組み合わせる際に使用されます。

font-lock-keywordsのルールは、(REGEXP . FACE)または(REGEXP CAPTURE-GROUP FACE)のような形式を取ります。CAPTURE-GROUPが指定されている場合、正規表現全体ではなく、指定されたキャプチャグループにマッチした部分にのみフェイスが適用されます。

技術的詳細

このコミットの核心は、go-mode.el内のgo-font-lock-keywordsリストにおける、組み込み関数をハイライトするための正規表現の変更にあります。

変更前は、組み込み関数をハイライトするルールは以下のようになっていました。

(,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)

このルールは、go-builtins(Goの組み込み関数名のリスト)から生成された正規表現(例: \b(?:new|make|len)\b)にマッチするすべてのテキストにfont-lock-builtin-faceを適用していました。このため、var new intのようなコードでも「new」がハイライトされてしまっていました。

変更後のルールは以下のようになります。

(,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)

この新しい正規表現は、以下の要素を結合して構築されています。

  1. "\\(": これは正規表現のキャプチャグループの開始を示すリテラルな(です。
  2. (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)): これは変更前と同じく、newmakeなどの組み込み関数名に単語境界でマッチする正規表現を生成します。この部分が最初のキャプチャグループの内容となります。
  3. "\\)[[:space:]]*(": これは正規表現のキャプチャグループの終了を示すリテラルな)、その後に0個以上の空白文字([[:space:]]*)、そしてリテラルな開き括弧(が続くパターンです。

この正規表現全体がマッチした場合、ルールは1 font-lock-builtin-faceと指定されています。これは、「正規表現全体がマッチした部分のうち、1番目のキャプチャグループにマッチした部分にのみfont-lock-builtin-faceを適用する」という意味です。

結果として、この新しいルールは「単語境界で囲まれた組み込み関数名」の直後に「0個以上の空白」と「開き括弧 (」が続く場合にのみマッチし、そのマッチした組み込み関数名(1番目のキャプチャグループ)にハイライトを適用します。これにより、new(Type)のような関数呼び出しはハイライトされますが、var new intのような変数宣言はハイライトされなくなります。

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

misc/emacs/go-mode.el ファイルの変更点です。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -262,7 +262,7 @@ For mode=set, all covered lines will have this weight.\"
   ;; doesn\'t understand that
   (append
    `(,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
-     (,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)
+     (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
      (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
      (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
 

コアとなるコードの解説

変更は、go-mode.el内のgo-font-lock-keywordsリストの一部である、組み込み関数(go-builtins)のハイライトルールにあります。

元の行:

(,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)

この行は、go-builtinsリスト内のすべての組み込み関数名(例: new, make)を単語として認識し、それらすべてにfont-lock-builtin-faceを適用していました。

変更後の行:

(,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)

この行は、以下の点で大きく異なります。

  1. concatの使用: 複数の文字列を結合して新しい正規表現文字列を動的に構築しています。
  2. "\\(""\\)": これらは正規表現のリテラルな括弧 () を表します。\はエスケープ文字です。これにより、組み込み関数名自体がキャプチャグループ(()で囲まれた部分)になります。
  3. "[[:space:]]*(": これは、キャプチャグループの直後に「0個以上の空白文字」と「リテラルな開き括弧 (」が続くことを要求する正規表現パターンです。
  4. 1 font-lock-builtin-face: これが最も重要な変更点です。font-lock-modeのルールにおいて、正規表現の後に数字が続く場合、その数字は「キャプチャグループのインデックス」を示します。ここでは1が指定されているため、正規表現全体がマッチした場合でも、1番目のキャプチャグループ(つまり、組み込み関数名自体)にのみfont-lock-builtin-faceが適用されます。

この変更により、newという単語がコード中に現れても、その直後に(が続かない限り(つまり、関数呼び出しでない限り)、font-lock-builtin-faceによるハイライトは適用されなくなりました。これにより、Go言語のセマンティクスに合致した、より正確なシンタックスハイライトが実現されています。

関連リンク

参考にした情報源リンク

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

このコミットは、EmacsのGoモード(go-mode.el)におけるシンタックスハイライトの挙動を修正するものです。具体的には、Go言語の組み込み関数名が変数名として使用されている場合に、誤って組み込み関数としてハイライトされる問題を解決します。これにより、「new」のような頻繁に変数名として使われる組み込み関数名が、関数呼び出しでない限りハイライトされないようになります。

コミット

commit 5f5e280e14efde7c576c282fc7ee3a2c734eb8c5
Author: Rui Ueyama <ruiu@google.com>
Date:   Thu Mar 27 17:35:07 2014 -0400

    misc/emacs: do not highlight built-in function if not followed by '('
    
    Name of built-in function is not reserved word in Go, and you can
    use it as variable name. "new" is often used as local variable, for
    instance.
    
    This patch is to apply font-lock-builtin-face only when built-in
    function name is followed by '(', so that it doesn't highlight
    non-function variable that happen to have the same name as built-in
    function.
    
    LGTM=dominik.honnef
    R=golang-codereviews, dominik.honnef, adonovan
    CC=golang-codereviews
    https://golang.org/cl/79260043

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

https://github.com/golang/go/commit/5f5e280e14efde7c576c282fc7ee3a2c734eb8c5

元コミット内容

Go言語の組み込み関数名が、その後に括弧 ( が続かない場合にはハイライトしないように、EmacsのGoモード(go-mode.el)の挙動を変更します。Goでは組み込み関数名が予約語ではないため、変数名として使用されることがあり、特に「new」のような名前はローカル変数として頻繁に利用されます。この変更は、組み込み関数名と同じ名前を持つ非関数変数が誤ってハイライトされるのを防ぐことを目的としています。

変更の背景

Go言語の設計上の特徴として、newmakeといった組み込み関数(built-in functions)の名前が、言語の予約語(reserved words)ではないという点があります。これは、開発者がこれらの名前を通常の変数名や関数名として自由に利用できることを意味します。例えば、newという名前の変数を宣言することはGo言語の文法上全く問題ありません。

しかし、EmacsのGoモード(go-mode.el)は、以前のバージョンでは組み込み関数名を一律にハイライトする設定になっていました。このため、コード内で組み込み関数名と同じ名前の変数が使われている場合でも、それが関数呼び出しではないにもかかわらず、組み込み関数としてハイライトされてしまうという問題がありました。これは、特にnewのように頻繁に変数名として使われる名前において、コードの可読性を損ねる原因となっていました。

このコミットは、このような誤ったハイライトを是正し、EmacsのGoモードがより正確にGoコードのセマンティクスを反映するようにするために行われました。具体的には、組み込み関数名が実際に「関数呼び出し」として使われている(つまり、その直後に(が続く)場合にのみハイライトを行うように変更することで、この問題を解決しています。

前提知識の解説

Go言語の予約語と組み込み関数

Go言語には、特定の意味を持つキーワード(var, func, if, forなど)が予約語として存在し、これらは変数名や関数名として使用できません。しかし、len, cap, new, make, panic, recover, print, println, complex, real, imag, close, delete, append, copyといった組み込み関数は、予約語ではありません。これは、これらの名前を開発者が変数名や型名として再定義できることを意味します。例えば、var new intのような宣言はGoでは有効です。この柔軟性が、Emacsのハイライトにおける問題の根源となっていました。

Emacsのfont-lock-modeとシンタックスハイライト

Emacsのfont-lock-modeは、ソースコードのシンタックスハイライト(構文強調表示)を提供する主要な機能です。これは、テキストバッファ内の特定のパターン(通常は正規表現)を検出し、それらに対応する「フェイス」(色、フォント、スタイルなどの視覚的属性のセット)を適用することで機能します。

font-lock-modeは、各メジャーモード(例: go-modepython-modeなど)が定義するfont-lock-keywordsという変数に依存します。この変数には、ハイライトのルールを記述したリストが格納されており、各ルールは通常、正規表現とそれに適用するフェイスのペアで構成されます。

  • font-lock-keyword-face: if, for, funcなどの言語のキーワードに適用されるフェイス。
  • font-lock-builtin-face: new, makeなどの組み込み関数や組み込み型に適用されるフェイス。
  • font-lock-function-name-face: ユーザー定義の関数名に適用されるフェイス。

Emacs Lispの正規表現関連関数

Emacs Lispでは、正規表現を操作するためのいくつかの便利な関数が提供されています。

  • regexp-opt: 複数の文字列のリストを受け取り、それらのいずれかにマッチする最適化された単一の正規表現を生成します。例えば、 (regexp-opt '("foo" "bar"))\\(?:foo\\|bar\\) のような正規表現を返します。これは、多数のキーワードを効率的にマッチさせる際に非常に有用です。
  • go--regexp-enclose-in-symbol: go-mode.el内で定義されているヘルパー関数で、与えられた正規表現をシンボル(単語の境界)で囲むためのものです。これにより、例えば「new」が「renewal」の一部としてマッチするのを防ぎ、「new」という単語全体としてのみマッチするようにします。内部的には\\b(単語境界)を使用していると考えられます。
  • concat: 複数の文字列を結合して新しい文字列を生成します。正規表現の構築において、動的にパターンを組み合わせる際に使用されます。

font-lock-keywordsのルールは、(REGEXP . FACE)または(REGEXP CAPTURE-GROUP FACE)のような形式を取ります。CAPTURE-GROUPが指定されている場合、正規表現全体ではなく、指定されたキャプチャグループにマッチした部分にのみフェイスが適用されます。

技術的詳細

このコミットの核心は、go-mode.el内のgo-font-lock-keywordsリストにおける、組み込み関数をハイライトするための正規表現の変更にあります。

変更前は、組み込み関数をハイライトするルールは以下のようになっていました。

(,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)

このルールは、go-builtins(Goの組み込み関数名のリスト)から生成された正規表現(例: \b(?:new|make|len)\b)にマッチするすべてのテキストにfont-lock-builtin-faceを適用していました。このため、var new intのようなコードでも「new」がハイライトされてしまっていました。

変更後のルールは以下のようになります。

(,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)

この新しい正規表現は、以下の要素を結合して構築されています。

  1. "\\(": これは正規表現のキャプチャグループの開始を示すリテラルな(です。
  2. (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)): これは変更前と同じく、newmakeなどの組み込み関数名に単語境界でマッチする正規表現を生成します。この部分が最初のキャプチャグループの内容となります。
  3. "\\)[[:space:]]*(": これは正規表現のキャプチャグループの終了を示すリテラルな)、その後に0個以上の空白文字([[:space:]]*)、そしてリテラルな開き括弧(が続くパターンです。

この正規表現全体がマッチした場合、ルールは1 font-lock-builtin-faceと指定されています。これは、「正規表現全体がマッチした部分のうち、1番目のキャプチャグループにマッチした部分にのみfont-lock-builtin-faceを適用する」という意味です。

結果として、この新しいルールは「単語境界で囲まれた組み込み関数名」の直後に「0個以上の空白」と「開き括弧 (」が続く場合にのみマッチし、そのマッチした組み込み関数名(1番目のキャプチャグループ)にハイライトを適用します。これにより、new(Type)のような関数呼び出しはハイライトされますが、var new intのような変数宣言はハイライトされなくなります。

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

misc/emacs/go-mode.el ファイルの変更点です。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -262,7 +262,7 @@ For mode=set, all covered lines will have this weight.\"
   ;; doesn\'t understand that
   (append
    `(,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
-     (,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)
+     (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
      (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
      (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
 

コアとなるコードの解説

変更は、go-mode.el内のgo-font-lock-keywordsリストの一部である、組み込み関数(go-builtins)のハイライトルールにあります。

元の行:

(,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face)

この行は、go-builtinsリスト内のすべての組み込み関数名(例: new, make)を単語として認識し、それらすべてにfont-lock-builtin-faceを適用していました。

変更後の行:

(,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)

この行は、以下の点で大きく異なります。

  1. concatの使用: 複数の文字列を結合して新しい正規表現文字列を動的に構築しています。
  2. "\\(""\\)": これらは正規表現のリテラルな括弧 () を表します。\はエスケープ文字です。これにより、組み込み関数名自体がキャプチャグループ(()で囲まれた部分)になります。
  3. "[[:space:]]*(": これは、キャプチャグループの直後に「0個以上の空白文字」と「リテラルな開き括弧 (」が続くことを要求する正規表現パターンです。
  4. 1 font-lock-builtin-face: これが最も重要な変更点です。font-lock-modeのルールにおいて、正規表現の後に数字が続く場合、その数字は「キャプチャグループのインデックス」を示します。ここでは1が指定されているため、正規表現全体がマッチした場合でも、1番目のキャプチャグループ(つまり、組み込み関数名自体)にのみfont-lock-builtin-faceが適用されます。

この変更により、newという単語がコード中に現れても、その直後に(が続かない限り(つまり、関数呼び出しでない限り)、font-lock-builtin-faceによるハイライトは適用されなくなりました。これにより、Go言語のセマンティクスに合致した、より正確なシンタックスハイライトが実現されています。

関連リンク

参考にした情報源リンク