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

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

このコミットは、Go言語の公式ドキュメンテーションツールであるgodocの機能改善に関するものです。具体的には、GoのExample関数に記述されたドキュメンテーションコメントが、godocのWeb UI上で表示されるように変更が加えられました。これにより、Exampleコードが何を示しているのか、どのような意図で書かれたのかといった背景情報が、ユーザーにとってより明確に伝わるようになります。

コミット

commit 7c9662f4612979298642a17cb4e8a52559e204ba
Author: Andrew Gerrand <adg@golang.org>
Date:   Thu Feb 16 12:43:22 2012 +1100

    godoc: show example function doc comments in UI
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/5677061

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

https://github.com/golang/go/commit/7c9662f4612979298642a17cb4e8a52559e204ba

元コミット内容

godoc: show example function doc comments in UI

R=gri
CC=golang-dev
https://golang.org/cl/5677061

変更の背景

Go言語では、コードのExample(例)を記述するための特別な関数(Exampleで始まる関数)がサポートされています。これらのExample関数は、単にコードの動作を示すだけでなく、そのExampleが何を目的としているのか、どのような状況で役立つのかといった説明をドキュメンテーションコメントとして記述することが推奨されています。

しかし、このコミット以前のgodocでは、Example関数のコードと出力は表示されるものの、それに付随するドキュメンテーションコメントはWeb UI上に表示されていませんでした。このため、Exampleの意図を完全に理解するためには、ソースコードを直接参照する必要がありました。

この変更の背景には、godocが提供するドキュメンテーションの完全性と利便性を向上させ、ユーザーがExampleコードの意図をより簡単に把握できるようにするという目的があります。Example関数のドキュメンテーションコメントを表示することで、godocはより包括的なドキュメンテーション体験を提供できるようになります。

前提知識の解説

godoc

godocは、Go言語のソースコードからドキュメンテーションを生成し、表示するためのツールです。Go言語の設計思想の一つに「ドキュメンテーションはコードと共にあるべき」という考え方があり、godocはその思想を具現化したものです。

  • コードからのドキュメンテーション生成: godocは、Goのソースコードに記述されたコメント(特にパッケージ、関数、型、変数などの宣言の直前に記述されたコメント)を解析し、HTML形式のドキュメンテーションを生成します。
  • Example関数のサポート: Goでは、Exampleというプレフィックスを持つ関数を記述することで、そのパッケージのExampleコードを提供できます。これらのExample関数は、go testコマンドで実行可能であり、その出力が期待される出力と一致するかどうかをテストすることもできます。godocはこれらのExampleコードも解析し、ドキュメンテーションに含めます。
  • Webサーバー機能: godocは、生成したドキュメンテーションをWebブラウザで閲覧するためのHTTPサーバーとしても機能します。これにより、ローカル環境で簡単にGoの標準ライブラリや自身のプロジェクトのドキュメンテーションを参照できます。

GoのExample関数とドキュメンテーションコメント

GoのExample関数は、以下のような形式で記述されます。

package mypackage

import (
	"fmt"
)

// ExampleHello は、Hello関数がどのように使われるかを示すExampleです。
// このExampleは、"Hello, World!"という文字列を出力します。
func ExampleHello() {
	fmt.Println("Hello, World!")
	// Output: Hello, World!
}

func Hello() {
	fmt.Println("Hello, World!")
}

上記のExampleHello関数の場合、// ExampleHello は、...から始まるコメントがExample関数のドキュメンテーションコメントです。このコメントは、Exampleコードの目的や動作を説明するために使用されます。// Output:行は、Exampleの期待される出力を指定するために使用され、go testによって検証されます。

Goのtext/templateパッケージ

godocのWeb UIは、Goの標準ライブラリであるtext/templateパッケージを使用してHTMLを生成しています。text/templateは、データ構造をテンプレートに適用してテキスト出力を生成するための強力なツールです。

  • アクション: テンプレート内では、{{.FieldName}}のようなプレースホルダーや、{{if .Condition}}...{{end}}{{range .Slice}}...{{end}}{{with .Value}}...{{end}}のような制御構造(アクション)を使用できます。
  • {{with .Value}}...{{end}}: このアクションは、.Valueがnilまたはゼロ値でない場合に、そのブロック内のテンプレートを実行し、.Valueを現在のコンテキスト(ドット)に設定します。これは、特定のフィールドが存在する場合にのみコンテンツを表示する際に便利です。

Goのgo/astパッケージ

go/astパッケージは、Goのソースコードの抽象構文木(AST: Abstract Syntax Tree)を表現するためのデータ構造と、それを操作するための関数を提供します。godocのようなツールは、このパッケージを使用してGoのソースコードを解析し、その構造やコメントなどの情報を抽出します。

  • ast.File: Goの単一のソースファイルを表すASTノードです。
  • ast.FuncDecl: 関数宣言を表すASTノードです。
  • ast.CommentGroup: コメントのグループを表すASTノードです。関数や型の宣言に付随するドキュメンテーションコメントは、このCommentGroupとしてAST内に格納されます。
  • f.Doc.Text(): ast.FuncDeclDocフィールドは、その関数に付随するドキュメンテーションコメントの*ast.CommentGroupを保持します。Text()メソッドは、そのコメントグループから整形されたテキストを返します。

技術的詳細

このコミットは、Example関数のドキュメンテーションコメントをgodocのWeb UIに表示するために、以下の3つの主要なコンポーネントにわたる変更を加えています。

  1. AST(抽象構文木)の解析とデータ構造の拡張:

    • src/pkg/go/ast/example.goが変更され、Example構造体にDoc stringフィールドが追加されました。
    • Examples関数内で、Example関数を解析する際に、その関数のドキュメンテーションコメント(f.Doc.Text())を抽出し、新しく追加されたDocフィールドに格納するように変更されました。これにより、Example関数のドキュメンテーションコメントが、godocの内部データモデルの一部として扱われるようになります。
  2. godocコマンドのデータ渡し:

    • src/cmd/godoc/godoc.goが変更され、example_htmlFunc関数内でHTMLテンプレートに渡すデータ構造に、Exampleのドキュメンテーションコメント(eg.Doc)が追加されました。これにより、HTMLテンプレートがExampleのドキュメンテーションコメントにアクセスできるようになります。
  3. HTMLテンプレートの更新:

    • lib/godoc/example.htmlが変更され、Exampleのドキュメンテーションコメントを表示するためのHTML要素が追加されました。具体的には、{{with .Doc}}<p>{{html .}}</p>{{end}}という行が追加され、Docフィールドが存在する場合にその内容を段落として表示するようにしました。
    • また、{{if .Output}}{{with .Output}}に変更されました。これは機能的には大きな違いはありませんが、withアクションは、変数が存在する場合にその変数を現在のコンテキストに設定するため、テンプレートの記述をより簡潔にするGoテンプレートの慣用的な書き方です。

これらの変更により、godocはExample関数のドキュメンテーションコメントを内部的に取得し、それをWeb UIにレンダリングする一連のパイプラインが完成しました。

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

lib/godoc/example.html

--- a/lib/godoc/example.html
+++ b/lib/godoc/example.html
@@ -4,11 +4,12 @@
  	</div>
  	<div class="expanded">
  		<p class="exampleHeading">▾ Example{{example_suffix .Name}}</p>
+		{{with .Doc}}<p>{{html .}}</p>{{end}}
  		<p>Code:</p>
  		<pre class="code">{{.Code}}</pre>
-		{{if .Output}}\
+		{{with .Output}}\
  		<p>Output:</p>
-		<pre class="output">{{html .Output}}</pre>
+		<pre class="output">{{html .}}</pre>
  		{{end}}
  	</div>
  </div>

src/cmd/godoc/godoc.go

--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -539,8 +539,8 @@ func example_htmlFunc(funcName string, examples []*ast.Example, fset *token.File
  		}
  
  		err := exampleHTML.Execute(&buf, struct {
-			Name, Code, Output string
-		}{eg.Name, code, out})
+			Name, Doc, Code, Output string
+		}{eg.Name, eg.Doc, code, out})
  		if err != nil {
  			log.Print(err)
  		}

src/pkg/go/ast/example.go

--- a/src/pkg/go/ast/example.go
+++ b/src/pkg/go/ast/example.go
@@ -16,6 +16,7 @@ import (
  
  type Example struct {
  	Name     string // name of the item being exemplified
+	Doc      string // example function doc string
  	Code     Node
  	Comments []*CommentGroup
  	Output   string // expected output
@@ -45,8 +46,13 @@ func Examples(files ...*File) []*Example {
  			if !isTest(name, "Example") {
  				continue
  			}
+			var doc string
+			if f.Doc != nil {
+				doc = f.Doc.Text()
+			}
  			flist = append(flist, &Example{
  				Name:     name[len("Example"):],
+				Doc:      doc,\
  				Code:     f.Body,
  				Comments: file.Comments,
  				Output:   exampleOutput(f, file.Comments),

コアとなるコードの解説

lib/godoc/example.htmlの変更

  • {{with .Doc}}<p>{{html .}}</p>{{end}}:
    • {{with .Doc}}は、テンプレートに渡されたデータ構造のDocフィールドが空でない場合に、その内部のブロックを実行します。
    • DocフィールドはExample関数のドキュメンテーションコメントのテキストを含みます。
    • <p>タグで囲むことで、ドキュメンテーションコメントが独立した段落として表示されます。
    • {{html .}}は、Docフィールドの内容をHTMLエスケープして出力します。これにより、コメント内にHTML特殊文字が含まれていても、正しく表示され、XSS(クロスサイトスクリプティング)などのセキュリティリスクを防ぎます。
  • {{if .Output}}から{{with .Output}}への変更:
    • これは機能的な変更というよりは、Goのtext/templateにおける慣用的な書き方への修正です。withアクションは、変数が存在する場合にその変数を現在のコンテキスト(.)に設定するため、{{html .Output}}{{html .}}と短縮して記述できるようになります。

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

  • example_htmlFunc関数内のstruct定義の変更:
    • HTMLテンプレートに渡される匿名構造体にDoc stringフィールドが追加されました。
    • {eg.Name, eg.Doc, code, out}という形で、eg.Docast.Example構造体から取得したドキュメンテーションコメント)がこの構造体に渡されるようになりました。これにより、HTMLテンプレートが{{.Doc}}としてこの値にアクセスできるようになります。

src/pkg/go/ast/example.goの変更

  • Example構造体へのDoc stringフィールドの追加:
    • type Example struct { ... Doc string ... }という行が追加され、Exampleのメタデータとしてドキュメンテーションコメントを保持できるようになりました。
  • Examples関数内の変更:
    • var doc stringdoc変数を宣言し、Example関数のASTノードff.Docフィールドがnilでない場合に、f.Doc.Text()を呼び出してドキュメンテーションコメントのテキストを取得しています。
    • 取得したdoc変数の値を、新しく作成されるExample構造体のDocフィールドに代入しています。
    • f.Doc*ast.CommentGroup型であり、Text()メソッドはコメントグループから整形された文字列を返します。これにより、Example関数のドキュメンテーションコメントが正確に抽出され、Example構造体に格納されるようになります。

これらの変更が連携することで、Example関数のドキュメンテーションコメントがgodocのWeb UIに表示されるという機能が実現されています。

関連リンク

参考にした情報源リンク

  • https://golang.org/cl/5677061 (このコミットのGerrit Code Reviewリンク)
  • Go言語の公式ドキュメントおよびブログ記事
  • Go言語のソースコード(特にsrc/cmd/godocsrc/pkg/go/astlib/godocディレクトリ)
  • text/templateパッケージの挙動に関する一般的なGoのドキュメンテーションとチュートリアル
  • go/astパッケージのAST解析に関する一般的なGoのドキュメンテーションとチュートリアル
  • godocのExample表示に関する情報(Web検索)
  • GoのExample関数の書き方に関する情報(Web検索)