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

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

このコミットは、Go言語のEmacsメジャーモードであるgo-mode.elに対する変更です。go-mode.elは、Emacsエディタ内でGo言語のコードを編集する際に、シンタックスハイライト、インデント、コードフォーマット(gofmtの実行)、定義へのジャンプなどの機能を提供するLispスクリプトです。

コミット

commit c7ad7a1af4ed8cb832f429eb57da182daf8427dd
Author: Dominik Honnef <dominik.honnef@gmail.com>
Date:   Thu Mar 21 20:03:27 2013 -0700

    misc/emacs: Kill gofmt error buffer if the buffer was formatted correctly already.
    
    R=adonovan, cw, patrick.allen.higgins, bradfitz
    CC=golang-dev
    https://golang.org/cl/7844045

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

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

元コミット内容

misc/emacs: Kill gofmt error buffer if the buffer was formatted correctly already.

このコミットは、Emacsのgo-modeにおいて、gofmtツールを実行した際に、もしバッファが既に正しくフォーマットされていた場合、gofmtのエラーバッファを自動的に閉じるようにする変更です。

変更の背景

Emacsのgo-modeでは、Go言語のコードをgofmtによって自動整形する機能が提供されています。通常、gofmtを実行すると、その出力(エラーや変更内容など)を表示するための補助的なバッファ(この場合はerrbuf)が作成されます。

この変更が導入される前は、もし編集中のGoコードが既にgofmtの規約に従って正しくフォーマットされていた場合でも、gofmtの実行によって作成された一時的なエラーバッファが閉じられずに残ってしまう可能性がありました。これはユーザーにとって不要なバッファが残り、Emacsのワークスペースが散らかる原因となっていました。

このコミットの目的は、このような場合に不要なエラーバッファを自動的に閉じ、ユーザーに「Buffer is already gofmted」(バッファは既にgofmtによって整形されています)という明確なメッセージを表示することで、ユーザーエクスペリエンスを向上させることにあります。これにより、gofmtの実行結果が「変更なし」であった場合に、よりクリーンで分かりやすいフィードバックが提供されるようになります。

前提知識の解説

  • Emacs: GNU Emacsは、非常に強力で拡張性の高いテキストエディタです。Lisp言語(Emacs Lisp)で記述されており、ユーザーはLispコードを記述することでエディタの機能を自由にカスタマイズ・拡張できます。
  • Emacs Lisp (Elisp): Emacsの拡張機能や設定を記述するために使用されるプログラミング言語です。Emacsのほぼ全ての機能はEmacs Lispで実装されており、ユーザーはこれを活用して独自のコマンドやモードを作成できます。
  • go-mode.el: EmacsでGo言語のコードを編集するためのメジャーモードです。Go言語特有のシンタックスハイライト、インデント、gofmtなどの外部ツールとの連携機能を提供します。.el拡張子はEmacs Lispファイルを示します。
  • gofmt: Go言語の公式なコードフォーマッタです。Goのソースコードを標準的なスタイルに自動的に整形します。gofmtはGo言語のツールチェインの一部として提供され、Goコミュニティではコードの整形にgofmtを使用することが強く推奨されています。これにより、Goコードの可読性と一貫性が保たれます。
  • call-process: Emacs Lispの関数で、外部のプログラムを実行するために使用されます。この関数は、指定されたプログラムを実行し、その標準出力、標準エラー出力、および終了コードを処理することができます。
  • call-process-region: Emacs Lispの関数で、バッファの特定のリージョン(範囲)の内容を外部プログラムの標準入力として渡し、そのプログラムを実行します。
  • zerop: Emacs Lispの関数で、引数がゼロである場合に真(t)を返します。外部コマンドの終了コードがゼロであるか(つまり、正常終了したか)をチェックするためによく使用されます。
  • kill-buffer: Emacs Lispの関数で、指定されたバッファを閉じます。
  • message: Emacs Lispの関数で、Emacsのミニバッファ(通常は画面下部に表示されるメッセージ領域)にメッセージを表示します。
  • progn: Emacs Lispの特殊フォームで、複数のフォーム(式)を順番に評価し、最後のフォームの評価結果を返します。これは、C言語の{}ブロックのように、複数の式を一つの論理的なブロックとしてまとめるために使用されます。

技術的詳細

このコミットの変更は、go-mode.el内のgofmt実行ロジックの一部を修正しています。具体的には、gofmtが正常に実行され、かつdiffコマンドを使って元のバッファ内容とgofmt後の内容を比較した結果、差分がない(つまり、既にフォーマット済みである)と判断された場合の処理フローを変更しています。

元のコードでは、gofmtが正常終了し、かつ差分がない場合に単に(message "Buffer is already gofmted")を実行していました。しかし、この時点ではgofmtの出力(たとえそれが空であっても)を受け取るために作成された一時的なエラーバッファ(errbuf)がまだ開いたままになっている可能性がありました。

変更後のコードでは、この条件が満たされた場合に、progn特殊フォームを使用して2つのアクションを連続して実行するようにしています。

  1. (kill-buffer errbuf): gofmtの出力バッファであるerrbufを閉じます。これにより、ユーザーのEmacsワークスペースから不要なバッファが削除されます。
  2. (message "Buffer is already gofmted"): ユーザーに、バッファが既に正しくフォーマットされていることを明確に通知します。

この修正により、gofmtがコードに変更を加える必要がなかった場合に、EmacsのUIがよりクリーンになり、ユーザーへのフィードバックもより適切になります。

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

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -537,7 +537,9 @@ buffer.\"\
      ;; output in case of success.
      (if (zerop (call-process \"gofmt\" nil errbuf nil \"-w\" tmpfile))\
          (if (zerop (call-process-region (point-min) (point-max) \"diff\" nil patchbuf nil \"-n\" \"-\" tmpfile))\
-            (message \"Buffer is already gofmted\")
+            (progn
+              (kill-buffer errbuf)\
+              (message \"Buffer is already gofmted\"))
            (go--apply-rcs-patch patchbuf)\
            (kill-buffer errbuf)\
            (message \"Applied gofmt\"))

コアとなるコードの解説

変更されたコードブロックは、gofmtの実行結果を処理する条件分岐の中にあります。

  • (if (zerop (call-process "gofmt" nil errbuf nil "-w" tmpfile)))

    • これは、gofmtコマンドを一時ファイル(tmpfile)に対して実行し、その標準エラー出力をerrbufにリダイレクトします。
    • zeropgofmtの終了コードが0(正常終了)であるかをチェックします。
    • このif文の真のブランチ(gofmtが正常終了した場合)に進みます。
  • (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)))

    • この内部のif文は、現在のEmacsバッファの内容と、gofmtによって整形された一時ファイル(tmpfile)の内容との差分をdiffコマンドを使って比較します。
    • call-process-regionは、現在のバッファ全体(point-minからpoint-maxまで)をdiffコマンドの標準入力として渡します。
    • diffコマンドの出力はpatchbufにリダイレクトされます。
    • zeropdiffコマンドの終了コードが0であるかをチェックします。diffコマンドは、差分がない場合に終了コード0を返します。
    • 変更点はこの内部if文の真のブランチにあります。
  • 変更前:

    (message "Buffer is already gofmted")
    

    gofmtが正常終了し、かつ差分がない場合、単に「Buffer is already gofmted」というメッセージを表示していました。しかし、errbufが閉じられない可能性がありました。

  • 変更後:

    (progn
      (kill-buffer errbuf)
      (message "Buffer is already gofmted"))
    

    gofmtが正常終了し、かつ差分がない場合、prognブロックが実行されます。

    1. (kill-buffer errbuf): gofmtの実行によって作成された一時的なエラーバッファerrbufを閉じます。これにより、Emacsのバッファリストから不要なエントリが削除され、ワークスペースが整理されます。
    2. (message "Buffer is already gofmted"): ユーザーに、バッファが既にgofmtによって整形済みであることを明確に通知します。

この変更により、gofmtがコードに変更を加える必要がなかった場合に、ユーザーはよりクリーンなEmacs環境と、より適切なフィードバックを得られるようになりました。

関連リンク

参考にした情報源リンク

  • コミット情報から直接解析
  • Emacs Lispの一般的な知識
  • Go言語およびgofmtの一般的な知識