[インデックス 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言語のソースコードからドキュメンテーションを生成するツールです。ソースコード内には、BUG
や TODO
といった特定のマーカーを持つコメント(ノート)を記述することができます。これまでの godoc
では、これらのノートを表示するかどうかを -notes
フラグで指定する際、カンマ区切りの文字列(例: -notes="BUG,TODO"
)でマーカーのリストを渡していました。
この方式にはいくつかの制限がありました。
- 柔軟性の欠如: 特定のパターンに一致するノートをまとめて表示するといった、より高度なフィルタリングができませんでした。例えば、「
FIXME
で始まる全てのノート」を表示するといったことは不可能でした。 - 全てのノートの表示が困難: 全ての種類のノートを表示したい場合、既知の全てのマーカーをカンマで区切って列挙する必要があり、新しいマーカーが追加されるたびにフラグの値を更新する必要がありました。これは非常に不便で、見落としが発生する可能性がありました。
これらの問題を解決し、ユーザーがより柔軟にノートをフィルタリングできるようにするために、-notes
フラグの値を正規表現として解釈するように変更されました。これにより、BUG|TODO
のように複数のマーカーをOR条件で指定したり、.*
のように全てのノートを包括的に表示したりすることが可能になります。
前提知識の解説
godoc
godoc
はGo言語に標準で付属するツールで、Goのソースコードからドキュメンテーションを生成し、表示する役割を担います。開発者はコード内に特定のコメント形式でドキュメンテーションを記述することで、godoc
を使ってそのドキュメンテーションをHTML形式で閲覧したり、コマンドラインで表示したりできます。特に、BUG
や TODO
といった特定のキーワードを含むコメントは「ノート」として認識され、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
は、ソースコード内の BUG
や TODO
などの特定のマーカーを持つコメントを表す構造体です。
info.PDoc.Notes
は、*doc.Package
型のフィールドで、パッケージ内の全てのノートを保持するマップです。このマップのキーはノートマーカー(例: "BUG")、値はそのマーカーを持つ doc.Note
のスライスです。
技術的詳細
この変更の核心は、godoc
の -notes
フラグの処理方法を、カンマ区切りの文字列解析から正規表現マッチングへと移行した点にあります。
具体的な変更点は以下の通りです。
-
flag.String
のusage
の変更: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", ".*")」に変更されました。これは、ユーザーに対して新しい挙動を明確に伝えるためのドキュメンテーションの更新です。 -
notesToShow
変数の削除:src/cmd/godoc/godoc.go
から、以前のカンマ区切りリストを保持していたnotesToShow []string
変数が削除されました。これは、もはやカンマ区切りでノートマーカーを管理する必要がなくなったためです。 -
strings.Split
の呼び出しの削除:initHandlers
関数内で、以前は-notes
フラグの値をカンマで分割してnotesToShow
に格納していたstrings.Split(*notes, ",")
の呼び出しが削除されました。 -
正規表現によるノートのフィルタリングロジックの導入:
docServer
のgetPageInfo
メソッド内で、ノートを収集するロジックが大幅に変更されました。- 以前は
notesToShow
スライスをイテレートし、各マーカーがinfo.PDoc.Notes
に存在するかどうかを確認していました。 - 新しいロジックでは、まず
flag.String
で定義された*notes
の値(正規表現文字列)を使用してregexp.Compile(*notes)
を呼び出し、正規表現オブジェクトrx
をコンパイルします。 - コンパイルが成功した場合(エラーがない場合)、
info.PDoc.Notes
マップをイテレートします。各ノートマーカーm
に対してrx.MatchString(m)
を呼び出し、正規表現がマーカーにマッチするかどうかをチェックします。 - マッチした場合、そのノートを
info.Notes
マップに追加します。info.Notes
は、表示されるべきノートを保持するマップです。必要に応じてinfo.Notes
がnil
であれば初期化されます。
- 以前は
この変更により、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は記載しませんが、正規表現のチュートリアルやリファレンスで検索すると多くの情報が見つかります。)