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

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

このコミットは、EmacsのGoモード(go-mode.el)におけるインデントの不具合を修正するものです。具体的には、ピリオドで終わるコメントの後に余分なインデントが挿入される問題を解決します。

コミット

commit a55a6cb925639c9379b3cf53427d5205050a3b5b
Author: Sameer Ajmani <sameer@golang.org>
Date:   Mon Mar 5 14:58:35 2012 -0500

    misc/emacs: fix extra indentation after comments that end with a period
    in emacs go mode.  Thanks Alex Shinn for the patch.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/5728063

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

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

元コミット内容

EmacsのGoモードにおいて、ピリオドで終わるコメントの後に余分なインデントが発生する問題を修正します。このパッチはAlex Shinn氏によって提供されました。

変更の背景

EmacsのGoモードは、Go言語のコードを編集する際に適切なインデントを提供するためのLispコードです。しかし、特定の状況下、特にコメントがピリオドで終わる場合に、インデント計算が誤り、期待されるインデントよりも余分なスペースが挿入されるというバグが存在していました。これはコードの可読性を損ない、開発者の作業効率を低下させる可能性がありました。このコミットは、この特定のインデント問題を解決し、より正確なコードフォーマットを保証することを目的としています。

前提知識の解説

  • Emacs Lisp (Elisp): Emacsエディタの拡張機能や設定を記述するために使用されるプログラミング言語です。Emacsのほとんどの機能はElispで実装されており、ユーザーはElispを使ってEmacsを高度にカスタマイズできます。go-mode.elもElispで書かれています。
  • Emacsのインデントシステム: Emacsは、プログラミング言語の構文に基づいて自動的にコードをインデントする機能を持っています。これは通常、現在の行のコンテキスト(前の行の構文、括弧の対応など)を解析して、適切なインデントレベルを決定します。
  • go-mode.el: EmacsでGo言語のコードを編集するためのメジャーモードです。Go言語の構文ハイライト、インデント、その他の編集支援機能を提供します。
  • point: Emacs Lispにおいて、カーソルが現在位置しているバッファ内の位置を示す概念です。
  • char-syntax: Emacs Lispの関数で、指定された文字の構文クラス(例: w for word, for whitespace, . for punctuation, ( for open parenthesis, # for comment startなど)を返します。インデントや構文解析において重要な役割を果たします。
  • skip-syntax-backward: Emacs Lispの関数で、指定された構文クラスの文字を後方にスキップします。
  • forward-comment: Emacs Lispの関数で、コメントを前方にスキップします。このコミットでは、この関数の代わりに新しいgo-mode-backward-skip-commentsが導入されています。
  • go-mode-cs: go-mode.el内で定義されている可能性のある関数または変数で、現在のpointがコメントまたは文字列内にあるかどうかを判定するために使用されると推測されます。

技術的詳細

この修正の核心は、インデント計算の基準となる位置を特定するロジックの改善にあります。以前のバージョンでは、go-mode-indentation関数内でインデントを計算する際に、forward-comment (- (buffer-size))という呼び出しを使用していました。このforward-commentはコメントを前方にスキップする関数であり、インデントの計算において、行の先頭からコメントの終わりまでを適切にスキップし、その後のコード要素に基づいてインデントを決定することを意図していました。

しかし、ピリオドで終わるコメントの場合、forward-commentがコメントの終わりを正確に認識できない、あるいはその後のインデント計算ロジックがピリオドを特殊な文字として扱い、誤ったインデントを引き起こしていた可能性があります。

このコミットでは、この問題を解決するために、go-mode-backward-skip-commentsという新しいヘルパー関数が導入されました。この関数は、現在のカーソル位置から後方に、空白文字とコメントを正確にスキップすることを目的としています。

go-mode-backward-skip-commentsの内部ロジックは以下の要素を含んでいます:

  • bobp (Beginning Of Buffer P): バッファの先頭にいるかどうかをチェックします。
  • backward-char: カーソルを1文字後方に移動させます。
  • char-syntax (char-after (point)): 現在のカーソル位置の次の文字の構文クラスを取得します。これにより、空白文字やコメントの開始文字などを識別します。
  • go-mode-cs: これはgo-mode.el内で定義されている関数またはマクロで、現在のpointがコメントまたは文字列内にあるかどうかを判定するために使用されます。
  • skip-syntax-backward "-": 構文クラスがハイフン(コメントの開始文字など)である文字を後方にスキップします。
  • previous-single-property-change (point) 'go-mode-cs): go-mode-csプロパティが変更された直前の位置を見つけます。これは、コメントや文字列の開始位置を特定するのに役立ちます。

go-mode-indentation関数では、以前のforward-commentの呼び出しが、beginning-of-lineと新しく追加されたgo-mode-backward-skip-commentsの呼び出しに置き換えられました。

  1. beginning-of-line: まず、現在の行の先頭にカーソルを移動させます。
  2. go-mode-backward-skip-comments: その後、行の先頭から後方に、コメントと空白をスキップして、インデント計算の真の開始点を見つけます。

この変更により、インデント計算の基準点がより正確になり、ピリオドで終わるコメントの後の余分なインデントが解消されます。これは、コメントの終わりをより堅牢に処理し、その後のコード要素のインデントを正しく決定するための改善です。

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

misc/emacs/go-mode.el ファイルが変更されています。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -406,6 +406,22 @@ token on the line."
        (when (/= (skip-chars-backward "[:word:]_") 0)
          (not (looking-at go-mode-non-terminating-keywords-regexp))))))

+(defun go-mode-backward-skip-comments ()
+  "Skip backward over comments and whitespace."
+  (when (not (bobp))
+    (backward-char))
+  (while (and (not (bobp))
+              (or (eq 32 (char-syntax (char-after (point))))
+                  (go-mode-cs)))
+    (skip-syntax-backward "-")
+    (when (and (not (bobp)) (eq 32 (char-syntax (char-after (point))))))
+      (backward-char))
+    (when (go-mode-cs)
+      (let ((pos (previous-single-property-change (point) 'go-mode-cs)))
+        (if pos (goto-char pos) (goto-char (point-min))))))
+  (when (and (not (go-mode-cs)) (eq 32 (char-syntax (char-after (1+ (point))))))
+    (forward-char 1)))
+
 (defun go-mode-indentation ()
   "Compute the ideal indentation level of the current line.

@@ -451,7 +467,8 @@ indented one level."
                    (incf indent tab-width))
                   ((?\)
                    (goto-char (car nest))
-                   (forward-comment (- (buffer-size)))
+                   (beginning-of-line)
+                   (go-mode-backward-skip-comments)
                    ;; Really just want the token before
                    (when (looking-back "\\<import\\|const\\|var\\|type"
                                        (max (- (point) 7) (point-min)))
@@ -465,7 +482,8 @@ indented one level."
             (decf indent tab-width))

           ;; Continuation lines are indented 1 level
-          (forward-comment (- (buffer-size)))
+          (beginning-of-line)
+          (go-mode-backward-skip-comments)
           (when (case (char-before)
                   ((nil ?\{ ?:)
                    ;; At the beginning of a block or the statement

コアとなるコードの解説

  1. go-mode-backward-skip-comments 関数の追加:

    • この新しい関数は、現在のカーソル位置から後方に、空白文字とコメントをスキップするために導入されました。
    • when (not (bobp)) (backward-char)): バッファの先頭でない限り、まず1文字後方に移動します。これは、char-after (point)が現在の文字ではなく、その次の文字を対象とするため、現在の文字を考慮に入れるための調整です。
    • while ループ: バッファの先頭に到達しておらず、かつ現在の位置が空白文字であるか、go-mode-cs(コメントまたは文字列内)である限りループを続けます。
      • skip-syntax-backward "-": コメントの開始文字(通常は;//など、Emacs Lispの構文テーブルでハイフン構文として定義されているもの)を後方にスキップします。
      • when (and (not (bobp)) (eq 32 (char-syntax (char-after (point)))))) (backward-char)): バッファの先頭でなく、かつ現在のカーソル位置の次の文字が空白文字であれば、さらに1文字後方に移動します。これは、空白が連続している場合に対応するためです。
      • when (go-mode-cs) ...: もしgo-mode-csが真(コメントまたは文字列内)であれば、previous-single-property-changeを使って、go-mode-csプロパティが設定された直前の位置(つまりコメントや文字列の開始位置)にジャンプします。これにより、コメントブロック全体を効率的にスキップできます。
    • when (and (not (go-mode-cs)) (eq 32 (char-syntax (char-after (1+ (point)))))) (forward-char 1)): ループ終了後、もしコメントや文字列の内部でなく、かつ現在のカーソル位置の2つ先の文字が空白であれば、1文字前方に移動します。これは、スキップしすぎた場合に微調整を行うためのものです。
  2. go-mode-indentation 関数の変更:

    • go-mode-indentation関数内の2箇所で、既存のインデント計算ロジックが変更されました。
    • 変更前: (forward-comment (- (buffer-size)))
    • 変更後:
      (beginning-of-line)
      (go-mode-backward-skip-comments)
      
    • この変更により、インデントを計算する前に、まず現在の行の先頭に移動し、その後、新しく定義されたgo-mode-backward-skip-comments関数を使って、行の先頭から後方にコメントと空白を正確にスキップします。これにより、インデントの基準となる位置がより正確に特定され、ピリオドで終わるコメントの後のインデント問題が解決されます。

関連リンク

参考にした情報源リンク