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

[インデックス 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の意図が伝わりにくいという問題がありました。

この変更の背景には、以下の課題がありました。

  1. パッケージ全体のExampleの欠如: 特定の関数や型に紐づかない、パッケージ全体の利用方法を示すExampleをgodocで表示する方法がなかったため、ユーザーがパッケージの全体像を把握しづらい場合がありました。
  2. 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.FuncMapstringをキーとし、interface{}を値とするマップで、値は任意の関数を保持できます。これにより、テンプレート内で複雑なロジックやデータ整形を行うことが可能になります。

5. doc.Example構造体

go/docパッケージには、Example関数に関する情報(名前、コード、出力など)を保持するExample構造体があります。godocはこの構造体を利用してExampleの情報を取得し、ドキュメントを生成します。

技術的詳細

このコミットの主要な技術的変更点は、godocがExample関数を解析し、HTMLテンプレートで表示する方法を拡張したことにあります。

  1. パッケージレベルExampleのサポート:

    • src/cmd/godoc/godoc.goexample_htmlFuncが、Exampleの名前を直接テンプレートに渡すように変更されました。これにより、Example()という名前の関数が、特定の関数や型に紐づかないパッケージ全体のExampleとして認識され、package.htmlテンプレートでレンダリングされるようになりました。
    • lib/godoc/package.html{{example_html "" $.Examples $.FSet}}が追加され、パッケージの概要セクションに直接Exampleが表示されるようになりました。これにより、ユーザーはパッケージのトップページで主要な使用例をすぐに確認できるようになります。
  2. Exampleサフィックスの表示:

    • src/cmd/godoc/godoc.goexample_nameFuncexample_suffixFuncsplitExampleNameという新しいヘルパー関数が追加されました。
      • 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_quuxFoo.Bar (Quux)のように変換されます。アンダースコアをドットに変換することで、メソッドのExampleのように見せかけ、サフィックスを括弧で囲んで強調します。
      • example_suffixFunc(name string) string: splitExampleNameを使用してサフィックスのみを抽出し、括弧で囲んだ形式(例: (Quux))で返します。
    • これらの新しい関数(example_nameFuncexample_suffixFunc)は、src/cmd/godoc/godoc.gofmaptemplate.FuncMap)に登録されました。これにより、HTMLテンプレート内で{{example_name .Name}}{{example_suffix .Name}}として呼び出すことが可能になりました。
    • lib/godoc/example.htmllib/godoc/package.htmlが更新され、example_suffixexample_nameテンプレート関数が使用されるようになりました。これにより、Exampleのタイトルやリンクテキストにサフィックスが適切に表示されるようになります。
  3. 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_nameFuncexample_suffixFuncsplitExampleNameという新しいヘルパー関数が追加されました。これらの関数は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.Namedoc.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のドキュメント上で表示される整形された名前を生成します。

    1. splitExampleNameを呼び出して、ベースとなる名前とサフィックスを分離します。
    2. ベース名に含まれる最初のアンダースコアをドットに置換します。これは、ExampleType_MethodのようなメソッドのExampleをType.Methodのように表示するためです。
    3. もしベース名が空(つまり、Example()のようなパッケージレベルのExampleの場合)であれば、"Package"という文字列を使用します。
    4. 最後に、整形されたベース名とサフィックスを結合して返します。サフィックスは (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の名前を解析し、ベース名とサフィックスに分割するロジックを含んでいます。

    1. 文字列の最後のアンダースコア(_)の位置を探します。
    2. アンダースコアが見つかり、それが文字列の末尾ではない(i < len(s)-1)、かつアンダースコアの直後の文字列が大文字で始まらない(!startsWithUppercase(s[i+1:]))という条件を満たす場合、そのアンダースコア以降の文字列をサフィックスと判断します。
      • !startsWithUppercase(s[i+1:])の条件は重要で、ExampleFoo_BarのようなケースでBarがサフィックスとして認識される一方で、ExampleFoo_HTTPのようにHTTPが大文字で始まる場合はサフィックスとして扱わないようにしています。これは、HTTPが略語や特定の識別子としてExample名の一部である可能性が高いためです。
    3. サフィックスと判断された場合、ベース名をアンダースコアの手前までとし、サフィックスを (TitleCaseSuffix)の形式で整形します(strings.Titleで先頭を大文字化)。
    4. 上記の条件を満たさない場合は、文字列全体をベース名とし、サフィックスは空文字列とします。
  • fmapへの登録:

    var fmap = template.FuncMap{
        // ...
        "example_html":   example_htmlFunc,
        "example_name":   example_nameFunc,
        "example_suffix": example_suffixFunc,
    }
    

    新しく定義されたexample_nameFuncexample_suffixFunctemplate.FuncMapに追加されました。これにより、godocのHTMLテンプレート(example.htmlpackage.htmlなど)からこれらの関数を呼び出して、Exampleの名前やサフィックスを動的に整形・表示できるようになります。

lib/godoc/example.htmllib/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言語の公式ドキュメント
  • Go言語のソースコード
  • Go言語のIssueトラッカー (Issue #2896)