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

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

このコミットは、Go言語のEmacsモードであるgo-mode.elにおけるgofmtのパッチ適用に関するバグ修正を扱っています。具体的には、一時ディレクトリ(TMPDIR)がデフォルトの/tmpではない環境(macOSなど)でgofmtの出力が正しく処理されない問題に対応しています。

コミット

commit 11cafa3a42e216140adcb9f29d1ec3e320850682
Author: Jean-Marc Eurin <jmeurin@google.com>
Date:   Fri Jun 29 12:49:31 2012 -0400

        misc/emacs: Fix the gofmt patching when the TMPDIR is not the default.
    
    The previous code assumed the gofmt output referred to /tmp but
    that's not true if TMPDIR points somewhere else (like on Macs).
    Fixes #3782.
    
    R=sameer
    CC=golang-dev
    https://golang.org/cl/6346050

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

https://github.com/golang/go/commit/11cafa3a42e216140adcb9f29d1ec3e320850682

元コミット内容

misc/emacs: Fix the gofmt patching when the TMPDIR is not the default.

以前のコードは、gofmtの出力が/tmpを参照していると仮定していましたが、TMPDIRが別の場所(macOSなど)を指している場合はそうではありませんでした。 これにより、Issue #3782が修正されます。

変更の背景

このコミットの背景には、Go言語のコードフォーマッタであるgofmtとEmacsエディタの連携における問題がありました。gofmtは、コードを整形する際に一時ファイルを使用することがあります。Emacsのgo-mode.elは、gofmtの出力を解析し、その結果を現在のバッファに適用する機能を持っています。

しかし、この処理において、go-mode.elgofmtが生成する一時ファイルのパスが常に/tmpディレクトリにあると決めつけていました。これは、多くのUnix系システムにおける一般的な一時ディレクトリの場所ですが、macOSなど一部のシステムではTMPDIR環境変数が異なるパス(例: /var/folders/...)を指すことがあります。

この仮定の不一致により、TMPDIR/tmp以外に設定されている環境でgofmtを実行すると、go-mode.elgofmtの出力に含まれる一時ファイルのパスを正しく認識できず、結果として整形されたコードがEmacsバッファに適用されないという問題が発生していました。この問題はGoのIssueトラッカーで「Issue #3782」として報告されていました。

前提知識の解説

  • gofmt: Go言語の公式なコードフォーマッタです。Goのソースコードを標準的なスタイルに自動的に整形します。開発者が手動でフォーマットを調整する手間を省き、コードの一貫性を保つために非常に重要なツールです。
  • Emacs: 高機能なテキストエディタであり、統合開発環境(IDE)としても利用されます。Lispをベースとした拡張性があり、様々なプログラミング言語に対応する「モード」が提供されています。
  • go-mode.el: EmacsでGo言語のコードを編集するためのメジャーモードです。シンタックスハイライト、インデント、gofmtとの連携など、Go開発を支援する機能を提供します。
  • TMPDIR環境変数: 一時ファイルが作成されるディレクトリのパスを指定する環境変数です。多くのプログラムやシステムがこの変数を利用して一時ファイルを管理します。この変数が設定されていない場合、通常は/tmp/var/tmpなどのデフォルトの場所が使用されます。
  • 正規表現 (Regular Expression): 文字列のパターンを記述するための強力なツールです。このコミットでは、gofmtの出力から一時ファイルのパスを抽出するために正規表現が使用されています。
  • re-search-forward (Emacs Lisp): Emacs Lispの関数で、現在のポイントから前方に向かって正規表現にマッチする文字列を検索します。
  • replace-match (Emacs Lisp): re-search-forwardなどで見つかったマッチを置換するEmacs Lispの関数です。

技術的詳細

この修正は、go-mode.el内のgo-fmt-buffer関数(またはそれに相当するgofmtの出力を処理する部分)に焦点を当てています。問題は、gofmtが一時ファイルを作成し、そのパスをEmacsに返す際に、Emacs側がそのパスのプレフィックスを/tmp/と決め打ちしていた点にありました。

変更前は、一時ファイルのパスを抽出するための正規表現が以下のように記述されていました。

"^--- \\(/tmp/gofmt[0-9]*\\)"

この正規表現は、gofmtの出力に含まれる差分ヘッダ(--- /tmp/gofmtXXXXXのような形式)から、/tmp/gofmtで始まる一時ファイル名をキャプチャすることを意図していました。しかし、TMPDIR/tmp以外に設定されている環境では、gofmtは例えば/var/folders/abcdef/gofmtXXXXXのようなパスで一時ファイルを作成します。この場合、上記の正規表現はマッチせず、Emacsは一時ファイルのパスを正しく認識できませんでした。

このコミットでは、正規表現を以下のように変更することでこの問題を解決しています。

"^--- \\(.*/gofmt[0-9]*\\)"

この新しい正規表現の重要な変更点は、/tmp/の部分が.*(任意の文字に0回以上マッチ)に置き換えられたことです。これにより、gofmtが生成する一時ファイルのパスが/tmp/であろうと、/var/folders/であろうと、あるいはその他の任意のディレクトリであろうと、gofmtで始まるファイル名であれば正しくキャプチャできるようになりました。

コミットメッセージにある「The .* is for TMPDIR, but to avoid dealing with TMPDIR having a trailing / or not, it's easier to just search for .* especially as we're only replacing the first instance.」という説明は、TMPDIR環境変数の値が末尾にスラッシュを持つか持たないかといった細かな違いを考慮するよりも、単に.*で任意のパスにマッチさせる方が実装が簡潔で堅牢であることを示唆しています。replace-matchの第5引数(1)は、正規表現の最初のキャプチャグループ(\\( ... \\)で囲まれた部分)を置換対象とすることを意味しており、これにより一時ファイルのフルパスがfilename変数(現在のバッファのファイル名)に置き換えられます。

この修正により、Emacsのgo-mode.elは、TMPDIRの設定に関わらず、gofmtの出力を安定して処理し、整形されたコードをユーザーのEmacsバッファに適用できるようになりました。

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

変更はmisc/emacs/go-mode.elファイルに集中しています。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -820,7 +820,10 @@ Replace the current buffer on success; display errors on failure."
   ;; apply all the patch hunks
   (with-current-buffer patchbuf
     (goto-char (point-min))
-    (if (re-search-forward "^--- \\(/tmp/gofmt[0-9]*\\)" nil t)\
+    ;; The .* is for TMPDIR, but to avoid dealing with TMPDIR
+    ;; having a trailing / or not, it's easier to just search for .*
+    ;; especially as we're only replacing the first instance.
+    (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t)\
       (replace-match filename nil nil nil 1))\
     (condition-case nil\
         (while t\

コアとなるコードの解説

上記の差分が示すように、変更はgo-mode.el内のgo-fmt-buffer関数(または関連するgofmtの出力を処理するロジック)の一部です。

  • - (if (re-search-forward "^--- \\(/tmp/gofmt[0-9]*\\)" nil t):

    • これは変更前のコードです。re-search-forward関数を使って、バッファ内で正規表現^--- \\(/tmp/gofmt[0-9]*\\)にマッチする部分を検索していました。
    • ^--- は差分ヘッダの開始を示します。
    • \\(/tmp/gofmt[0-9]*\\)は、/tmp/gofmtで始まり、その後に数字が続く文字列(例: /tmp/gofmt12345)をキャプチャグループとして指定しています。この部分が、gofmtが作成する一時ファイルのパスを特定する役割を担っていました。
  • + (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t):

    • これが変更後のコードです。正規表現が^--- \\(.*/gofmt[0-9]*\\)に変更されています。
    • \\(.*/gofmt[0-9]*\\)の部分が修正の核心です。
    • .*は、任意の文字(改行を除く)に0回以上マッチします。これにより、/tmp/のような固定パスではなく、任意のディレクトリパスにマッチできるようになりました。
    • この変更により、TMPDIR/tmp以外のパスを指している場合でも、gofmtが生成する一時ファイルのパスを正しく認識し、その後のパッチ適用処理が正常に行われるようになります。
  • replace-match filename nil nil nil 1:

    • この行は変更されていませんが、重要な役割を担っています。
    • re-search-forwardで見つかったマッチ(つまり、一時ファイルのパス)を、現在のEmacsバッファのファイル名(filename変数に格納されている)に置換します。これにより、gofmtの出力が現在のファイルに適用されるように見せかけます。
    • 最後の1は、正規表現の最初のキャプチャグループ(\\( ... \\)で囲まれた部分)を置換対象とすることを意味します。

この修正は、Emacs Lispの正規表現と文字列操作の知識、そしてTMPDIR環境変数の挙動に関する理解に基づいています。

関連リンク

  • Go Issue #3782: https://code.google.com/p/go/issues/detail?id=3782 (古いGoのIssueトラッカーのリンクですが、コミットメッセージに記載されています)
  • Gerrit Change-Id: 6346050 (GoプロジェクトのコードレビューシステムGerritの変更ID)

参考にした情報源リンク

  • コミットメッセージと差分情報: /home/orange/Project/comemo/commit_data/13421.txt
  • Go言語の公式ドキュメント (gofmtについて): https://go.dev/blog/gofmt
  • Emacs Lispのドキュメント (re-search-forward, replace-match, 正規表現について): Emacsのヘルプドキュメント (C-h f re-search-forward, C-h f replace-match, C-h i m elisp)
  • TMPDIR環境変数に関する一般的な情報: Unix/Linuxのmanページやオンラインリソース
  • Go Issue #3782の検索結果 (Google検索): gofmt TMPDIR issue #3782 などのキーワードで検索し、関連する議論や解決策の背景を理解しました。

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

このコミットは、Go言語のEmacsモードであるgo-mode.elにおけるgofmtのパッチ適用に関するバグ修正を扱っています。具体的には、一時ディレクトリ(TMPDIR)がデフォルトの/tmpではない環境(macOSなど)でgofmtの出力が正しく処理されない問題に対応しています。

コミット

commit 11cafa3a42e216140adcb9f29d1ec3e320850682
Author: Jean-Marc Eurin <jmeurin@google.com>
Date:   Fri Jun 29 12:49:31 2012 -0400

        misc/emacs: Fix the gofmt patching when the TMPDIR is not the default.
    
    The previous code assumed the gofmt output referred to /tmp but
    that's not true if TMPDIR points somewhere else (like on Macs).
    Fixes #3782.
    
    R=sameer
    CC=golang-dev
    https://golang.org/cl/6346050

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

https://github.com/golang/go/commit/11cafa3a42e216140adcb9f29d1ec3e320850682

元コミット内容

misc/emacs: Fix the gofmt patching when the TMPDIR is not the default.

以前のコードは、gofmtの出力が/tmpを参照していると仮定していましたが、TMPDIRが別の場所(macOSなど)を指している場合はそうではありませんでした。 これにより、Issue #3782が修正されます。

変更の背景

このコミットの背景には、Go言語のコードフォーマッタであるgofmtとEmacsエディタの連携における問題がありました。gofmtは、Goのソースコードを標準的なスタイルに自動的に整形するツールであり、その処理過程で一時ファイルを使用することがあります。Emacsのgo-mode.elは、Goコードの編集を支援するメジャーモードであり、gofmtの機能をEmacs内で利用できるように統合しています。具体的には、gofmtの出力を解析し、その結果を現在のEmacsバッファに適用することで、コードの自動整形を実現しています。

しかし、このgofmtの出力処理において、go-mode.elgofmtが生成する一時ファイルのパスが常に/tmpディレクトリにあると決めつけていました。/tmpは多くのUnix系システムにおける一般的な一時ディレクトリの場所ですが、macOSなど一部のシステムでは、TMPDIR環境変数が異なるパス(例: /var/folders/...)を指すことがあります。

この仮定の不一致により、TMPDIR/tmp以外に設定されている環境でgofmtを実行すると、go-mode.elgofmtの出力に含まれる一時ファイルのパスを正しく認識できませんでした。その結果、gofmtによって整形されたコードがEmacsバッファに適用されず、ユーザーは期待通りのフォーマット結果を得ることができないという問題が発生していました。この問題はGoのIssueトラッカーで「Issue #3782」として報告されており、このコミットはその解決を目的としています。

前提知識の解説

  • gofmt: Go言語の公式なコードフォーマッタです。GoのソースコードをGoコミュニティで推奨される標準的なスタイルに自動的に整形します。これにより、開発者が手動でフォーマットを調整する手間を省き、プロジェクト全体でコードの一貫性を保つことができます。gofmtは、構文解析木(AST)に基づいてコードを整形するため、単なる文字列置換ではなく、より賢い整形が可能です。
  • Emacs: 高機能なテキストエディタであり、その強力な拡張性から統合開発環境(IDE)としても広く利用されています。EmacsはEmacs Lispという独自のLisp方言で記述されており、ユーザーはEmacs Lispを用いてEmacsの機能を自由に拡張・カスタマイズできます。
  • go-mode.el: EmacsでGo言語のコードを編集するためのメジャーモードです。シンタックスハイライト、自動インデント、コード補完、そしてgofmtとの連携など、Go開発を効率的に行うための様々な機能を提供します。このモードはEmacs Lispで実装されています。
  • TMPDIR環境変数: 一時ファイルが作成されるディレクトリのパスを指定するために使用される環境変数です。多くのプログラムやシステムは、この変数の値を利用して一時ファイルを管理します。この変数が設定されていない場合、通常は/tmp/var/tmpなどのシステムデフォルトの場所が一時ディレクトリとして使用されます。異なるOSやシステム設定によって、TMPDIRのデフォルト値や挙動は異なります。
  • 正規表現 (Regular Expression): 文字列のパターンを記述するための強力なツールです。特定の文字列の検索、置換、抽出などに広く用いられます。このコミットでは、gofmtの出力に含まれる一時ファイルのパスを特定するために正規表現が使用されています。
  • re-search-forward (Emacs Lisp): Emacs Lispの関数で、現在のポイント(カーソル位置)から前方に向かって指定された正規表現にマッチする文字列を検索します。マッチが見つかると、ポイントをマッチの終わりに移動させ、tを返します。見つからない場合はnilを返します。
  • replace-match (Emacs Lisp): re-search-forwardなどの検索関数によって見つかった直前のマッチを、指定された文字列で置換するEmacs Lispの関数です。この関数は、正規表現のキャプチャグループ(括弧で囲まれた部分)を利用して、マッチした文字列の一部のみを置換することも可能です。

技術的詳細

この修正は、go-mode.el内のgo-fmt-buffer関数(またはそれに相当する、gofmtの出力を処理する内部ロジック)に焦点を当てています。問題の根源は、gofmtがコード整形のために一時ファイルを作成し、その一時ファイルのパスをEmacsに返す際に、Emacs側がそのパスのプレフィックスを/tmp/と決め打ちしていた点にありました。

変更前は、gofmtの出力に含まれる差分情報から一時ファイルのパスを抽出するために、以下のEmacs Lispの正規表現が使用されていました。

"^--- \\(/tmp/gofmt[0-9]*\\)"

この正規表現は、gofmtの出力に含まれる差分ヘッダ(例: --- /tmp/gofmt12345 のような形式)から、/tmp/gofmtで始まり、その後に数字が続く一時ファイル名をキャプチャすることを意図していました。具体的には、

  • ^--- は行頭の--- というリテラル文字列にマッチします。
  • \\(\\) はキャプチャグループを定義します。このグループ内のパターンにマッチした部分が後で参照可能になります。
  • /tmp/gofmt はリテラル文字列 /tmp/gofmt にマッチします。
  • [0-9]* は0個以上の数字にマッチします。これはgofmtが一時ファイル名に付与するランダムな数字に対応します。

しかし、TMPDIR環境変数が/tmp以外に設定されている環境(例えばmacOSでは/var/folders/以下のパスが使われることが多い)では、gofmt/var/folders/abcdef/gofmt12345のようなパスで一時ファイルを作成します。この場合、上記の正規表現は/tmp/の部分でマッチせず、Emacsは一時ファイルのパスを正しく認識できませんでした。その結果、gofmtによる整形結果をEmacsバッファに適用する処理が失敗していました。

このコミットでは、この問題を解決するために、正規表現を以下のように変更しています。

"^--- \\(.*/gofmt[0-9]*\\)"

この新しい正規表現の重要な変更点は、/tmp/の部分が.*(任意の文字に0回以上マッチ)に置き換えられたことです。

  • .* は、改行を除く任意の文字に0回以上マッチします。これにより、/tmp/のような固定パスではなく、/var/folders/abcdef/のような任意のディレクトリパスに柔軟にマッチできるようになりました。

コミットメッセージにある「The .* is for TMPDIR, but to avoid dealing with TMPDIR having a trailing / or not, it's easier to just search for .* especially as we're only replacing the first instance.」という説明は、この変更の意図を明確にしています。つまり、TMPDIR環境変数の値が末尾にスラッシュを持つか持たないかといった細かな違いを考慮して複雑な正規表現を書くよりも、単に.*で任意のパスにマッチさせる方が実装が簡潔で堅牢であることを示唆しています。

この正規表現の変更により、gofmtが生成する一時ファイルのパスが/tmp/であろうと、/var/folders/であろうと、あるいはその他の任意のディレクトリであろうと、gofmtで始まるファイル名であれば正しくキャプチャできるようになりました。

replace-match filename nil nil nil 1という行は変更されていませんが、この修正の文脈で重要な役割を担っています。re-search-forwardで見つかったマッチ(つまり、一時ファイルのフルパス)を、現在のEmacsバッファのファイル名(filename変数に格納されている)に置換します。これにより、gofmtの出力が現在のファイルに適用されるように見せかけ、ユーザーは整形されたコードをEmacsバッファで確認できるようになります。最後の1は、正規表現の最初のキャプチャグループ(\\( ... \\)で囲まれた部分)を置換対象とすることを意味しており、これにより一時ファイルのフルパスがfilename変数に置き換えられます。

この修正により、Emacsのgo-mode.elは、TMPDIRの設定に関わらず、gofmtの出力を安定して処理し、整形されたコードをユーザーのEmacsバッファに適用できるようになりました。これは、Go開発者が様々な環境でEmacsを快適に利用できるようにするための重要な改善です。

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

変更はmisc/emacs/go-mode.elファイルに集中しています。

--- a/misc/emacs/go-mode.el
+++ b/misc/emacs/go-mode.el
@@ -820,7 +820,10 @@ Replace the current buffer on success; display errors on failure."
   ;; apply all the patch hunks
   (with-current-buffer patchbuf
     (goto-char (point-min))
-    (if (re-search-forward "^--- \\(/tmp/gofmt[0-9]*\\)" nil t)\
+    ;; The .* is for TMPDIR, but to avoid dealing with TMPDIR
+    ;; having a trailing / or not, it's easier to just search for .*
+    ;; especially as we're only replacing the first instance.
+    (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t)\
       (replace-match filename nil nil nil 1))\
     (condition-case nil\
         (while t\

コアとなるコードの解説

上記の差分が示すように、変更はgo-mode.el内のgo-fmt-buffer関数(または関連するgofmtの出力を処理するロジック)の一部です。このコードブロックは、gofmtが生成したパッチ(差分情報)をEmacsバッファに適用する処理を行っています。

  • - (if (re-search-forward "^--- \\(/tmp/gofmt[0-9]*\\)" nil t):

    • これは変更前のコードです。re-search-forward関数を使って、patchbufgofmtの出力が格納されているバッファ)内で正規表現^--- \\(/tmp/gofmt[0-9]*\\)にマッチする部分を検索していました。
    • ^--- は差分ヘッダの開始を示します。
    • \\(/tmp/gofmt[0-9]*\\)は、/tmp/gofmtで始まり、その後に数字が続く文字列(例: /tmp/gofmt12345)をキャプチャグループとして指定しています。このキャプチャグループが、gofmtが作成する一時ファイルのパスを特定する役割を担っていました。
    • nil tは、検索オプションで、nilは検索失敗時にエラーを発生させないことを意味し、tは検索を非インタラクティブに行うことを意味します。
  • + (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t):

    • これが変更後のコードです。正規表現が^--- \\(.*/gofmt[0-9]*\\)に変更されています。
    • \\(.*/gofmt[0-9]*\\)の部分が修正の核心です。
    • .*は、任意の文字(改行を除く)に0回以上マッチします。これにより、/tmp/のような固定パスではなく、任意のディレクトリパスにマッチできるようになりました。
    • この変更により、TMPDIR/tmp以外のパスを指している場合でも、gofmtが生成する一時ファイルのパスを正しく認識し、その後のパッチ適用処理が正常に行われるようになります。
  • replace-match filename nil nil nil 1:

    • この行は変更されていませんが、重要な役割を担っています。
    • re-search-forwardで見つかったマッチ(つまり、一時ファイルのパス)を、現在のEmacsバッファのファイル名(filename変数に格納されている)に置換します。これにより、gofmtの出力が現在のファイルに適用されるように見せかけます。
    • nil nil nilは、置換オプション(FIXME, LITERAL, NO-CREATE)を指定しないことを意味します。
    • 最後の1は、正規表現の最初のキャプチャグループ(\\( ... \\)で囲まれた部分)を置換対象とすることを意味します。これにより、gofmtの出力に含まれる一時ファイルのパスの部分だけが、現在のファイル名に置き換えられます。

この修正は、Emacs Lispの正規表現と文字列操作の知識、そしてTMPDIR環境変数の挙動に関する深い理解に基づいています。これにより、Go開発者はmacOSを含む様々な環境で、Emacsとgofmtをより安定して連携させることができるようになりました。

関連リンク

  • Go Issue #3782: https://code.google.com/p/go/issues/detail?id=3782 (古いGoのIssueトラッカーのリンクですが、コミットメッセージに記載されています。現在はGitHubのIssueに移行している可能性があります。)
  • Gerrit Change-Id: 6346050 (GoプロジェクトのコードレビューシステムGerritにおけるこの変更のIDです。GerritはGoプロジェクトでコードレビューを行うために使用されています。)

参考にした情報源リンク

  • コミットメッセージと差分情報: /home/orange/Project/comemo/commit_data/13421.txt
  • Go言語の公式ドキュメント (gofmtについて): https://go.dev/blog/gofmt
  • Emacs Lispのドキュメント (re-search-forward, replace-match, 正規表現について): Emacsのヘルプドキュメント (Emacs内で C-h f re-search-forward, C-h f replace-match, C-h i m elisp などで参照可能)
  • TMPDIR環境変数に関する一般的な情報: Unix/Linuxのmanページやオンラインリソース (例: man environman tmpdir)
  • Go Issue #3782の検索結果 (Google検索): gofmt TMPDIR issue #3782 などのキーワードで検索し、関連する議論や解決策の背景を理解しました。特に、GitHubのgolang/goリポジトリにおける関連するIssueやコミット履歴を参照しました。