[インデックス 11943] ファイルの概要
コミット
commit f3c3130685e3b457c356156b8b7de2b41e496f10
Author: Andrew Gerrand <adg@golang.org>
Date: Thu Feb 16 09:44:01 2012 +1100
godoc: support for package examples, display example suffixes
Fixes #2896.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/5677047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f3c3130685e3b457c356156b8b7de2b41e496f10
元コミット内容
このコミットは、Go言語のドキュメンテーションツールであるgodoc
に、パッケージレベルのExample(例)のサポートと、Example関数のサフィックス(接尾辞)の表示機能を追加します。これにより、godoc
が生成するドキュメントにおいて、より柔軟で分かりやすいコード例の提示が可能になります。具体的には、Example()
という名前の関数をパッケージ全体の例として扱い、ExampleFoo_Bar()
のような関数名からBar
のようなサフィックスを抽出し、ドキュメント上で適切に表示するようになります。
変更の背景
Go言語のgodoc
は、ソースコードから自動的にドキュメントを生成する強力なツールです。これには、関数の使用例を示すExample関数も含まれます。しかし、このコミット以前は、Example関数は特定の関数や型に関連付けられている必要があり、パッケージ全体の使用方法を示す「パッケージレベルのExample」を直接サポートしていませんでした。また、ExampleFoo_Bar
のようにアンダースコアで区切られたサフィックスを持つExample関数は、そのサフィックスがドキュメント上で適切に表示されず、Exampleの意図が伝わりにくいという問題がありました。
この変更の背景には、以下の課題がありました。
- パッケージ全体のExampleの欠如: 特定の関数や型に紐づかない、パッケージ全体の利用方法を示すExampleを
godoc
で表示する方法がなかったため、ユーザーがパッケージの全体像を把握しづらい場合がありました。 - Exampleサフィックスの表示改善:
ExampleFoo_Bar
のような命名規則を持つExample関数において、Bar
の部分がドキュメント上で単なる関数名の一部として扱われ、そのサフィックスが持つ意味(例: 特定のシナリオ、入力データなど)が視覚的に強調されていませんでした。これにより、複数のExampleがある場合に、それぞれのExampleが何を示しているのかを区別しにくくなっていました。
これらの課題を解決し、godoc
が生成するドキュメントの品質と利便性を向上させることが、このコミットの目的です。特に、Fixes #2896
という記述から、GitHubのIssue #2896で報告された問題に対応していることがわかります。
前提知識の解説
1. godoc
godoc
は、Go言語のソースコードからドキュメンテーションを生成するためのツールです。Goのコードは、コメントの書き方やExample関数の命名規則に従うことで、godoc
によって自動的に解析され、HTML形式のドキュメントとして提供されます。開発者はgodoc
サーバーを起動することで、ローカルでGoの標準ライブラリや自身のプロジェクトのドキュメントをブラウザで閲覧できます。
2. GoのExample関数
Go言語では、Example
というプレフィックスを持つ関数を記述することで、コードのExample(使用例)をドキュメントに含めることができます。これらの関数は、go test
コマンドによってテストとしても実行され、Exampleの出力が期待される出力と一致するかどうかが検証されます。
Example関数の命名規則にはいくつか種類があります。
Example()
: パッケージ全体のExample。このコミットでサポートが強化されました。ExampleFoo()
: 関数Foo
のExample。ExampleType()
: 型Type
のExample。ExampleType_Method()
: 型Type
のメソッドMethod
のExample。ExampleFoo_Suffix()
: 関数Foo
の特定のシナリオを示すExample。_Suffix
の部分がサフィックスとして扱われます。このコミットでこのサフィックスの表示が改善されました。
Example関数は通常、_test.go
ファイル内に記述されます。
3. html/template
パッケージ
Goの標準ライブラリであるhtml/template
パッケージは、HTMLドキュメントを生成するためのテンプレートエンジンを提供します。このパッケージは、セキュリティ上の理由から、HTMLインジェクション攻撃を防ぐための自動エスケープ機能を持っています。
4. template.FuncMap
html/template
パッケージでは、テンプレート内でカスタム関数を使用することができます。これらのカスタム関数はtemplate.FuncMap
というマップに登録され、テンプレートエンジンに渡されます。template.FuncMap
はstring
をキーとし、interface{}
を値とするマップで、値は任意の関数を保持できます。これにより、テンプレート内で複雑なロジックやデータ整形を行うことが可能になります。
5. doc.Example
構造体
go/doc
パッケージには、Example関数に関する情報(名前、コード、出力など)を保持するExample
構造体があります。godoc
はこの構造体を利用してExampleの情報を取得し、ドキュメントを生成します。
技術的詳細
このコミットの主要な技術的変更点は、godoc
がExample関数を解析し、HTMLテンプレートで表示する方法を拡張したことにあります。
-
パッケージレベルExampleのサポート:
src/cmd/godoc/godoc.go
のexample_htmlFunc
が、Exampleの名前を直接テンプレートに渡すように変更されました。これにより、Example()
という名前の関数が、特定の関数や型に紐づかないパッケージ全体のExampleとして認識され、package.html
テンプレートでレンダリングされるようになりました。lib/godoc/package.html
に{{example_html "" $.Examples $.FSet}}
が追加され、パッケージの概要セクションに直接Exampleが表示されるようになりました。これにより、ユーザーはパッケージのトップページで主要な使用例をすぐに確認できるようになります。
-
Exampleサフィックスの表示:
src/cmd/godoc/godoc.go
にexample_nameFunc
、example_suffixFunc
、splitExampleName
という新しいヘルパー関数が追加されました。splitExampleName(s string) (name, suffix string)
: Example関数の名前(例:ExampleFoo_Bar_quux
)を受け取り、ベースとなる名前(Foo_Bar
)とサフィックス(quux
)に分割します。サフィックスは、最後のアンダースコアの後に続く文字列で、かつその文字列が小文字で始まる場合に認識されます。これにより、ExampleFoo_Bar
のような命名規則からBar
をサフィックスとして正確に抽出できます。example_nameFunc(s string) string
:splitExampleName
を使用してExampleの名前とサフィックスを抽出し、表示用の整形された名前を返します。例えば、Foo_Bar_quux
はFoo.Bar (Quux)
のように変換されます。アンダースコアをドットに変換することで、メソッドのExampleのように見せかけ、サフィックスを括弧で囲んで強調します。example_suffixFunc(name string) string
:splitExampleName
を使用してサフィックスのみを抽出し、括弧で囲んだ形式(例:(Quux)
)で返します。
- これらの新しい関数(
example_nameFunc
とexample_suffixFunc
)は、src/cmd/godoc/godoc.go
のfmap
(template.FuncMap
)に登録されました。これにより、HTMLテンプレート内で{{example_name .Name}}
や{{example_suffix .Name}}
として呼び出すことが可能になりました。 lib/godoc/example.html
とlib/godoc/package.html
が更新され、example_suffix
とexample_name
テンプレート関数が使用されるようになりました。これにより、Exampleのタイトルやリンクテキストにサフィックスが適切に表示されるようになります。
-
Example関数の命名規則の変更例:
src/pkg/container/heap/example_test.go
では、既存のExampleInterface()
関数がExample()
にリネームされました。これは、このコミットが導入するパッケージレベルのExampleの新しい命名規則に合わせた変更です。これにより、container/heap
パッケージのドキュメントに、パッケージ全体のExampleとしてこのコードが表示されるようになります。
これらの変更により、godoc
はExample関数をよりセマンティックに解釈し、ユーザーにとってより分かりやすい形でドキュメントに表示できるようになりました。特に、サフィックスの表示は、複数のExampleがある場合にそれぞれのExampleがどのようなシナリオをカバーしているのかを一目で理解するのに役立ちます。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は以下のファイルに集中しています。
-
lib/godoc/example.html
:--- a/lib/godoc/example.html +++ b/lib/godoc/example.html @@ -1,9 +1,9 @@ <div id="example_{{.Name}}" class="example"> <div class="collapsed"> - <p class="exampleHeading">▹ Example</p> + <p class="exampleHeading">▹ Example{{example_suffix .Name}}</p> </div> <div class="expanded"> - <p class="exampleHeading">▾ Example</p> + <p class="exampleHeading">▾ Example{{example_suffix .Name}}</p> <p>Code:</p> <pre class="code">{{.Code}}</pre> {{if .Output}}
Exampleのヘッディングに
example_suffix
テンプレート関数が適用され、Example名にサフィックスが含まれる場合に表示されるようになりました。 -
lib/godoc/package.html
:--- a/lib/godoc/package.html +++ b/lib/godoc/package.html @@ -20,6 +20,7 @@ <h2 id="overview">Overview</h2> <!-- The package's Name is printed as title by the top-level template --> {{comment_html .Doc}} + {{example_html "" $.Examples $.FSet}} <h2 id="index">Index</h2> <!-- Table of contents for API; must be named manual-nav to turn off auto nav. --> @@ -56,7 +57,7 @@ <h4>Examples</h4> <dl> {{range $.Examples}} -\t\t\t<dd><a class="exampleLink" href="#example_{{.Name}}">{{.Name}}</a></dd> +\t\t\t<dd><a class="exampleLink" href="#example_{{.Name}}">{{example_name .Name}}</a></dd> {{end}} </dl> {{end}}
パッケージの概要セクションに
example_html
が追加され、パッケージレベルのExampleが表示されるようになりました。また、Exampleのリンクテキストにexample_name
テンプレート関数が適用され、整形された名前が表示されるようになりました。 -
src/cmd/godoc/godoc.go
:--- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -526,7 +526,7 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File err := exampleHTML.Execute(&buf, struct { Name, Code, Output string - }{name, code, eg.Output}) + }{eg.Name, code, eg.Output}) if err != nil { log.Print(err) } @@ -534,6 +534,38 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File return buf.String() } +// example_nameFunc takes an example function name and returns its display +// name. For example, "Foo_Bar_quux" becomes "Foo.Bar (Quux)". +func example_nameFunc(s string) string { + name, suffix := splitExampleName(s) + // replace _ with . for method names + name = strings.Replace(name, "_", ".", 1) + // use "Package" if no name provided + if name == "" { + name = "Package" + } + return name + suffix +} + +// example_suffixFunc takes an example function name and returns its suffix in +// parenthesized form. For example, "Foo_Bar_quux" becomes " (Quux)". +func example_suffixFunc(name string) string { + _, suffix := splitExampleName(name) + return suffix + +} + +func splitExampleName(s string) (name, suffix string) { + i := strings.LastIndex(s, "_") + if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { + name = s[:i] + suffix = " (" + strings.Title(s[i+1:]) + ")" + return + } + name = s + return +} + func pkgLinkFunc(path string) string { relpath := relativeURL(path) // because of the irregular mapping under goroot @@ -610,7 +642,9 @@ var fmap = template.FuncMap{\n "posLink_url": posLink_urlFunc,\n \n // formatting of Examples\n -\t"example_html": example_htmlFunc,\n +\t"example_html": example_htmlFunc,\n +\t"example_name": example_nameFunc,\n +\t"example_suffix": example_suffixFunc,\n }\n \n func readTemplate(name string) *template.Template {
example_htmlFunc
の引数渡しが修正され、example_nameFunc
、example_suffixFunc
、splitExampleName
という新しいヘルパー関数が追加されました。これらの関数はtemplate.FuncMap
に登録され、HTMLテンプレートから利用可能になります。 -
src/pkg/container/heap/example_test.go
:--- a/src/pkg/container/heap/example_test.go +++ b/src/pkg/container/heap/example_test.go @@ -58,10 +58,7 @@ func (pq *PriorityQueue) Pop() interface{} { } // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight -func ExampleInterface() { -\t// The full code of this example, including the methods that implement -\t// heap.Interface, is in the file src/pkg/container/heap/example_test.go.\n-\n +func Example() { const nItem = 10 // Random priorities for the items (a permutation of 0..9, times 11)). priorities := [nItem]int{
ExampleInterface()
関数がExample()
にリネームされ、パッケージレベルのExampleとして扱われるようになりました。
コアとなるコードの解説
src/cmd/godoc/godoc.go
における変更
このファイルはgodoc
ツールのバックエンドロジックを担っており、特にHTMLテンプレートに渡されるデータや、テンプレート内で使用されるカスタム関数を定義しています。
-
example_htmlFunc
の修正:err := exampleHTML.Execute(&buf, struct { Name, Code, Output string }{eg.Name, code, eg.Output})
以前は
name
というローカル変数を使用していた部分が、eg.Name
(doc.Example
構造体のName
フィールド)を直接使用するように変更されました。これにより、Exampleの実際の名前がテンプレートに正確に渡されるようになります。 -
example_nameFunc
:func example_nameFunc(s string) string { name, suffix := splitExampleName(s) // replace _ with . for method names name = strings.Replace(name, "_", ".", 1) // use "Package" if no name provided if name == "" { name = "Package" } return name + suffix }
この関数は、Example関数の生の名前(例:
ExampleFoo_Bar_quux
)を受け取り、godoc
のドキュメント上で表示される整形された名前を生成します。splitExampleName
を呼び出して、ベースとなる名前とサフィックスを分離します。- ベース名に含まれる最初のアンダースコアをドットに置換します。これは、
ExampleType_Method
のようなメソッドのExampleをType.Method
のように表示するためです。 - もしベース名が空(つまり、
Example()
のようなパッケージレベルのExampleの場合)であれば、"Package"
という文字列を使用します。 - 最後に、整形されたベース名とサフィックスを結合して返します。サフィックスは
(Suffix)
のような形式で追加されます。
-
example_suffixFunc
:func example_suffixFunc(name string) string { _, suffix := splitExampleName(name) return suffix }
この関数は、Example関数の名前からサフィックスのみを抽出し、それを返します。この返り値は、
example.html
テンプレートでExampleのヘッディングに直接追加され、Example (Suffix)
のような表示を実現します。 -
splitExampleName
:func splitExampleName(s string) (name, suffix string) { i := strings.LastIndex(s, "_") if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { name = s[:i] suffix = " (" + strings.Title(s[i+1:]) + ")" return } name = s return }
このヘルパー関数は、Exampleの名前を解析し、ベース名とサフィックスに分割するロジックを含んでいます。
- 文字列の最後のアンダースコア(
_
)の位置を探します。 - アンダースコアが見つかり、それが文字列の末尾ではない(
i < len(s)-1
)、かつアンダースコアの直後の文字列が大文字で始まらない(!startsWithUppercase(s[i+1:])
)という条件を満たす場合、そのアンダースコア以降の文字列をサフィックスと判断します。!startsWithUppercase(s[i+1:])
の条件は重要で、ExampleFoo_Bar
のようなケースでBar
がサフィックスとして認識される一方で、ExampleFoo_HTTP
のようにHTTP
が大文字で始まる場合はサフィックスとして扱わないようにしています。これは、HTTP
が略語や特定の識別子としてExample名の一部である可能性が高いためです。
- サフィックスと判断された場合、ベース名をアンダースコアの手前までとし、サフィックスを
(TitleCaseSuffix)
の形式で整形します(strings.Title
で先頭を大文字化)。 - 上記の条件を満たさない場合は、文字列全体をベース名とし、サフィックスは空文字列とします。
- 文字列の最後のアンダースコア(
-
fmap
への登録:var fmap = template.FuncMap{ // ... "example_html": example_htmlFunc, "example_name": example_nameFunc, "example_suffix": example_suffixFunc, }
新しく定義された
example_nameFunc
とexample_suffixFunc
がtemplate.FuncMap
に追加されました。これにより、godoc
のHTMLテンプレート(example.html
やpackage.html
など)からこれらの関数を呼び出して、Exampleの名前やサフィックスを動的に整形・表示できるようになります。
lib/godoc/example.html
とlib/godoc/package.html
における変更
これらのファイルは、godoc
が生成するHTMLドキュメントのテンプレートです。
-
example.html
: Exampleの個別の表示を担当するテンプレートです。{{example_suffix .Name}}
がExampleのヘッディングに追加されました。これにより、Example (Suffix)
のように、Exampleのタイトルにそのサフィックスが括弧付きで表示されるようになります。
-
package.html
: パッケージ全体のドキュメント表示を担当するテンプレートです。{{example_html "" $.Examples $.FSet}}
がoverview
セクションに追加されました。これにより、Example()
という名前のパッケージレベルのExampleが、パッケージの概要部分に直接レンダリングされるようになります。- Exampleのリンクリストにおいて、
{{example_name .Name}}
が使用されるようになりました。これにより、リンクテキストもFoo.Bar (Quux)
のように整形され、ユーザーがExampleの内容をより正確に把握できるようになります。
src/pkg/container/heap/example_test.go
における変更
このファイルは、container/heap
パッケージのExampleコードを含んでいます。
ExampleInterface()
からExample()
へのリネームは、このコミットが導入する「パッケージレベルのExample」の概念を具体的に示すものです。Example()
という名前の関数は、特定の型や関数に紐づかず、パッケージ全体の動作を示すExampleとしてgodoc
によって認識され、package.html
の概要セクションに表示されるようになります。これにより、container/heap
パッケージの利用者が、ヒープの基本的な使い方をすぐに理解できるようになります。
これらの変更は、godoc
がGoのExampleをより洗練された方法で表示し、ドキュメントの可読性と情報量を向上させるための重要なステップです。
関連リンク
- Go言語のドキュメンテーション: https://go.dev/doc/
godoc
コマンドについて: https://go.dev/cmd/godoc/- GoのExampleについて: https://go.dev/blog/examples
- Goの
html/template
パッケージ: https://pkg.go.dev/html/template - Goの
go/doc
パッケージ: https://pkg.go.dev/go/doc
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のIssueトラッカー (Issue #2896)