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

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

このコミットは、Go言語のドキュメンテーションツールである godoc-notes フラグの挙動を変更するものです。以前はカンマ区切りのリストで指定されていたノートマーカー(例: BUG,TODO)が、正規表現で指定できるようになりました(例: BUG|TODO)。これにより、より柔軟なノートのフィルタリングが可能になり、特に .* を指定することで全てのノートを表示できるようになります。

コミット

commit 5e6a1a3f4894b3995c546178dd6de52d0f77493b
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Mar 21 15:56:15 2013 -0700

    go/doc: use regexp for -notes instead of comma-sep. list
    
    -notes="BUG|TODO" instead of -notes="BUG,TODO".
    Permits -notes=".*" to see all notes.
    
    R=cnicolaou
    CC=golang-dev
    https://golang.org/cl/7951043

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

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

元コミット内容

go/doc: use regexp for -notes instead of comma-sep. list -notes="BUG|TODO" のように、カンマ区切りではなく正規表現を使用するように変更。 これにより、-notes=".*" を指定することで全てのノートを表示できるようになる。

変更の背景

godoc はGo言語のソースコードからドキュメンテーションを生成するツールです。ソースコード内には、BUGTODO といった特定のマーカーを持つコメント(ノート)を記述することができます。これまでの godoc では、これらのノートを表示するかどうかを -notes フラグで指定する際、カンマ区切りの文字列(例: -notes="BUG,TODO")でマーカーのリストを渡していました。

この方式にはいくつかの制限がありました。

  1. 柔軟性の欠如: 特定のパターンに一致するノートをまとめて表示するといった、より高度なフィルタリングができませんでした。例えば、「FIXMEで始まる全てのノート」を表示するといったことは不可能でした。
  2. 全てのノートの表示が困難: 全ての種類のノートを表示したい場合、既知の全てのマーカーをカンマで区切って列挙する必要があり、新しいマーカーが追加されるたびにフラグの値を更新する必要がありました。これは非常に不便で、見落としが発生する可能性がありました。

これらの問題を解決し、ユーザーがより柔軟にノートをフィルタリングできるようにするために、-notes フラグの値を正規表現として解釈するように変更されました。これにより、BUG|TODO のように複数のマーカーをOR条件で指定したり、.* のように全てのノートを包括的に表示したりすることが可能になります。

前提知識の解説

godoc

godoc はGo言語に標準で付属するツールで、Goのソースコードからドキュメンテーションを生成し、表示する役割を担います。開発者はコード内に特定のコメント形式でドキュメンテーションを記述することで、godoc を使ってそのドキュメンテーションをHTML形式で閲覧したり、コマンドラインで表示したりできます。特に、BUGTODO といった特定のキーワードを含むコメントは「ノート」として認識され、godoc の出力に含めるかどうかを制御できます。

Go言語の regexp パッケージ

Go言語の標準ライブラリには、正規表現を扱うための regexp パッケージが含まれています。このパッケージは、Perlのような正規表現構文をサポートしており、文字列の検索、置換、分割など、正規表現を用いた高度なテキスト処理を可能にします。

主要な関数とメソッド:

  • regexp.Compile(expr string) (*Regexp, error): 指定された正規表現文字列をコンパイルし、*Regexp オブジェクトを返します。正規表現の構文が不正な場合はエラーを返します。
  • (*Regexp).MatchString(s string) bool: コンパイルされた正規表現が、与えられた文字列 s にマッチするかどうかを判定し、ブール値を返します。

Go言語の flag パッケージ

Go言語の標準ライブラリには、コマンドライン引数を解析するための flag パッケージが含まれています。このパッケージを使用すると、プログラムが受け取るコマンドラインフラグ(オプション)を簡単に定義し、その値をプログラム内で利用できます。

主要な関数:

  • flag.String(name string, value string, usage string) *string: 文字列型のフラグを定義します。name はフラグ名(例: "notes")、value はデフォルト値、usage はフラグの説明です。この関数は、フラグの値を保持する *string 型のポインタを返します。
  • flag.Parse(): コマンドライン引数を解析し、定義されたフラグに値を割り当てます。この関数は、フラグの定義後に一度だけ呼び出す必要があります。

strings.Split 関数

Go言語の標準ライブラリ strings パッケージに含まれる Split 関数は、指定されたセパレータ文字列に基づいて文字列を分割し、部分文字列のスライスを返します。

例: strings.Split("BUG,TODO", ",")[]string{"BUG", "TODO"} を返します。

doc.Note 構造体と info.PDoc.Notes

go/doc パッケージは、Goのソースコードから抽出されたドキュメンテーション情報を表現するためのデータ構造を提供します。 doc.Note は、ソースコード内の BUGTODO などの特定のマーカーを持つコメントを表す構造体です。 info.PDoc.Notes は、*doc.Package 型のフィールドで、パッケージ内の全てのノートを保持するマップです。このマップのキーはノートマーカー(例: "BUG")、値はそのマーカーを持つ doc.Note のスライスです。

技術的詳細

この変更の核心は、godoc-notes フラグの処理方法を、カンマ区切りの文字列解析から正規表現マッチングへと移行した点にあります。

具体的な変更点は以下の通りです。

  1. flag.Stringusage の変更: src/cmd/godoc/doc.go において、-notes フラグの説明が「comma separated list of Note markers as per pkg:go/doc」から「regular expression matching note markers to show (e.g., "BUG|TODO", ".*")」に変更されました。これは、ユーザーに対して新しい挙動を明確に伝えるためのドキュメンテーションの更新です。

  2. notesToShow 変数の削除: src/cmd/godoc/godoc.go から、以前のカンマ区切りリストを保持していた notesToShow []string 変数が削除されました。これは、もはやカンマ区切りでノートマーカーを管理する必要がなくなったためです。

  3. strings.Split の呼び出しの削除: initHandlers 関数内で、以前は -notes フラグの値をカンマで分割して notesToShow に格納していた strings.Split(*notes, ",") の呼び出しが削除されました。

  4. 正規表現によるノートのフィルタリングロジックの導入: docServergetPageInfo メソッド内で、ノートを収集するロジックが大幅に変更されました。

    • 以前は notesToShow スライスをイテレートし、各マーカーが info.PDoc.Notes に存在するかどうかを確認していました。
    • 新しいロジックでは、まず flag.String で定義された *notes の値(正規表現文字列)を使用して regexp.Compile(*notes) を呼び出し、正規表現オブジェクト rx をコンパイルします。
    • コンパイルが成功した場合(エラーがない場合)、info.PDoc.Notes マップをイテレートします。各ノートマーカー m に対して rx.MatchString(m) を呼び出し、正規表現がマーカーにマッチするかどうかをチェックします。
    • マッチした場合、そのノートを info.Notes マップに追加します。info.Notes は、表示されるべきノートを保持するマップです。必要に応じて info.Notesnil であれば初期化されます。

この変更により、godoc は実行時にユーザーが指定した正規表現をコンパイルし、それを使ってソースコードから抽出されたノートマーカーを動的にフィルタリングするようになりました。これにより、非常に柔軟なノートの表示制御が可能になります。

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

--- a/src/cmd/godoc/doc.go
+++ b/src/cmd/godoc/doc.go
@@ -67,6 +67,9 @@ The flags are:\n \t-maxresults=10000\n \t\tmaximum number of full text search results shown\n \t\t(no full text index is built if maxresults <= 0)\n+\t-notes="BUG"\n+\t\tregular expression matching note markers to show\n+\t\t(e.g., "BUG|TODO", ".*")\n \t-html\n \t\tprint HTML in command-line mode\n \t-goroot=$GOROOT\ndiff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go
index 82ede0d14e..5774321130 100644
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -84,15 +84,11 @@ var (\n \tcmdHandler docServer\n \tpkgHandler docServer\n \n-\t// which code 'Notes' to show\n-\tnotes = flag.String("notes", "BUG", "comma separated list of Note markers as per pkg:go/doc")\n-\t// list of 'Notes' to show\n-\tnotesToShow []string
+\t// source code notes
+\tnotes = flag.String("notes", "BUG", "regular expression matching note markers to show")
 )\n \n func initHandlers() {\n-\tnotesToShow = strings.Split(*notes, ",")\n-\
 \tfileServer = http.FileServer(&httpFS{fs})\n \tcmdHandler = docServer{"/cmd/", "/src/cmd"}\n \tpkgHandler = docServer{"/pkg/", "/src/pkg"}\n@@ -1100,10 +1096,15 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (inf\n \n \t\t\t// collect any notes that we want to show\n \t\t\tif info.PDoc.Notes != nil {\n-\t\t\t\tinfo.Notes = make(map[string][]*doc.Note)\n-\t\t\t\tfor _, m := range notesToShow {\n-\t\t\t\t\tif n := info.PDoc.Notes[m]; n != nil {\n-\t\t\t\t\t\tinfo.Notes[m] = n\n+\t\t\t\t// could regexp.Compile only once per godoc, but probably not worth it\n+\t\t\t\tif rx, err := regexp.Compile(*notes); err == nil {\n+\t\t\t\t\tfor m, n := range info.PDoc.Notes {\n+\t\t\t\t\t\tif rx.MatchString(m) {\n+\t\t\t\t\t\t\tif info.Notes == nil {\n+\t\t\t\t\t\t\t\tinfo.Notes = make(map[string][]*doc.Note)\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\tinfo.Notes[m] = n\n+\t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n \t\t\t}\n```

## コアとなるコードの解説

### `src/cmd/godoc/doc.go` の変更

```diff
--- a/src/cmd/godoc/doc.go
+++ b/src/cmd/godoc/doc.go
@@ -67,6 +67,9 @@ The flags are:\n \t-maxresults=10000\n \t\tmaximum number of full text search results shown\n \t\t(no full text index is built if maxresults <= 0)\n+\t-notes="BUG"\n+\t\tregular expression matching note markers to show\n+\t\t(e.g., "BUG|TODO", ".*")\n \t-html
 \t\tprint HTML in command-line mode\n \t-goroot=$GOROOT

この部分では、godoc コマンドのヘルプメッセージに表示される -notes フラグの説明が更新されています。

  • 以前は「comma separated list of Note markers as per pkg:go/doc」(pkg:go/doc に従ってカンマ区切りのノートマーカーリスト)と説明されていました。
  • 変更後、「regular expression matching note markers to show (e.g., "BUG|TODO", ".")」(表示するノートマーカーにマッチする正規表現、例: "BUG|TODO", ".")となり、正規表現の使用が明示されました。これにより、ユーザーは新しい挙動を理解しやすくなります。

src/cmd/godoc/godoc.go の変更

グローバル変数の定義部分

--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -84,15 +84,11 @@ var (\n \tcmdHandler docServer\n \tpkgHandler docServer\n \n-\t// which code 'Notes' to show\n-\tnotes = flag.String("notes", "BUG", "comma separated list of Note markers as per pkg:go/doc")\n-\t// list of 'Notes' to show\n-\tnotesToShow []string
+\t// source code notes
+\tnotes = flag.String("notes", "BUG", "regular expression matching note markers to show")
  • 以前は notes フラグの定義に加えて、その値をカンマで分割した結果を保持するための notesToShow []string というスライスが宣言されていました。
  • 変更後、notesToShow スライスは不要になったため削除されました。
  • notes フラグの usage 文字列も doc.go と同様に更新され、正規表現を使用することが示されています。

initHandlers 関数の変更

--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -84,15 +84,11 @@ var (\n \tcmdHandler docServer\n \tpkgHandler docServer\n \n-\t// which code 'Notes' to show\n-\tnotes = flag.String("notes", "BUG", "comma separated list of Note markers as per pkg:go/doc")\n-\t// list of 'Notes' to show\n-\tnotesToShow []string
+\t// source code notes
+\tnotes = flag.String("notes", "BUG", "regular expression matching note markers to show")
 )\n \n func initHandlers() {\n-\tnotesToShow = strings.Split(*notes, ",")\n-\
 \tfileServer = http.FileServer(&httpFS{fs})\n \tcmdHandler = docServer{"/cmd/", "/src/cmd"}\n \tpkgHandler = docServer{"/pkg/", "/src/pkg"}\n```
*   以前は `initHandlers` 関数内で、`flag.String` で取得した `*notes` の値(ポインタなので `*` でデリファレンス)を `strings.Split` でカンマ分割し、その結果を `notesToShow` スライスに格納していました。
*   この処理は正規表現によるフィルタリングに置き換えられるため、完全に削除されました。

#### `getPageInfo` メソッドの変更

```diff
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -1100,10 +1096,15 @@ func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (inf\n \n \t\t\t// collect any notes that we want to show\n \t\t\tif info.PDoc.Notes != nil {\n-\t\t\t\tinfo.Notes = make(map[string][]*doc.Note)\n-\t\t\t\tfor _, m := range notesToShow {\n-\t\t\t\t\tif n := info.PDoc.Notes[m]; n != nil {\n-\t\t\t\t\t\tinfo.Notes[m] = n\n+\t\t\t\t// could regexp.Compile only once per godoc, but probably not worth it\n+\t\t\t\tif rx, err := regexp.Compile(*notes); err == nil {\n+\t\t\t\t\tfor m, n := range info.PDoc.Notes {\n+\t\t\t\t\t\tif rx.MatchString(m) {\n+\t\t\t\t\t\t\tif info.Notes == nil {\n+\t\t\t\t\t\t\t\tinfo.Notes = make(map[string][]*doc.Note)\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\tinfo.Notes[m] = n\n+\t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n \t\t\t}\n```
この部分が、ノートのフィルタリングロジックの主要な変更点です。

*   **旧ロジック**:
    *   `info.Notes = make(map[string][]*doc.Note)`: 表示するノートを格納するマップを初期化。
    *   `for _, m := range notesToShow`: `notesToShow` スライス(カンマ区切りで指定されたマーカーのリスト)をループ。
    *   `if n := info.PDoc.Notes[m]; n != nil`: 現在のマーカー `m` が `info.PDoc.Notes` マップに存在し、対応するノートがあるかチェック。
    *   `info.Notes[m] = n`: 存在すれば、そのノートを `info.Notes` に追加。

*   **新ロジック**:
    *   `if rx, err := regexp.Compile(*notes); err == nil`:
        *   `*notes` はコマンドラインで指定された正規表現文字列です。
        *   `regexp.Compile(*notes)` を呼び出して、この正規表現文字列をコンパイルします。
        *   コンパイルが成功した場合(`err` が `nil` の場合)のみ、後続の処理に進みます。これにより、不正な正規表現が指定された場合でもパニックを起こさずに処理を続行できます。
        *   コメント `// could regexp.Compile only once per godoc, but probably not worth it` は、正規表現のコンパイルはコストがかかる処理であるため、理想的には一度だけ行うべきだが、このコンテキストではその最適化は不要であるという開発者の判断を示しています。
    *   `for m, n := range info.PDoc.Notes`: `info.PDoc.Notes` マップ(ソースコードから抽出された全てのノート)をループします。`m` はノートマーカー(例: "BUG")、`n` はそのマーカーに対応する `doc.Note` のスライスです。
    *   `if rx.MatchString(m)`: コンパイルされた正規表現 `rx` が、現在のノートマーカー `m` にマッチするかどうかをチェックします。これが新しいフィルタリングの核心です。
    *   `if info.Notes == nil { info.Notes = make(map[string][]*doc.Note) }`: `info.Notes` マップがまだ初期化されていなければ、ここで初期化します。これは、マッチするノートが初めて見つかったときにマップが作成されることを保証します。
    *   `info.Notes[m] = n`: 正規表現にマッチしたノートマーカーとそのノートを `info.Notes` マップに追加します。

この変更により、`godoc` はより強力で柔軟なノートのフィルタリング機能を提供できるようになりました。

## 関連リンク

*   Go CL 7951043: [https://golang.org/cl/7951043](https://golang.org/cl/7951043)

## 参考にした情報源リンク

*   Go言語 `regexp` パッケージ: [https://pkg.go.dev/regexp](https://pkg.go.dev/regexp)
*   Go言語 `flag` パッケージ: [https://pkg.go.dev/flag](https://pkg.go.dev/flag)
*   Go言語 `strings` パッケージ: [https://pkg.go.dev/strings](https://pkg.go.dev/strings)
*   Go言語 `go/doc` パッケージ: [https://pkg.go.dev/go/doc](https://pkg.go.dev/go/doc)
*   `godoc` コマンドの基本的な使い方 (Go公式ドキュメントなど): (一般的な知識のため特定のURLは記載しませんが、`go doc` や `godoc` で検索すると多くの情報が見つかります。)
*   正規表現の基本的な概念: (一般的な知識のため特定のURLは記載しませんが、正規表現のチュートリアルやリファレンスで検索すると多くの情報が見つかります。)