[インデックス 15408] ファイルの概要
このコミットは、Go言語のドキュメンテーションツールである godoc
に、テキストモードでGoのコード例(Examples)を表示する機能を追加するものです。具体的には、godoc
コマンドに -ex
フラグが導入され、これによりパッケージや関数のドキュメントを表示する際に、関連するコード例も一緒に表示されるようになります。
コミット
commit d97b975d5c1f87ecdda29211c46fa81b747248dc
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date: Mon Feb 25 10:37:17 2013 +1100
cmd/godoc: show examples in text mode
Added the command line flag -ex to godoc to print examples in
text output.
Samples from the generated output:
$ godoc -ex strings Index
...
func Index(s, sep string) int
Index returns the index of the first instance of sep in s, or -1 if sep
is not present in s.
Example:
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
// Output:
// 4
// -1
...
$ godoc -ex container/heap
...
package heap
import "container/heap"
Package heap provides heap operations for any type that implements
heap.Interface. A heap is a tree with the property that each node is the
minimum-valued node in its subtree.
Example:
// This example demonstrates an integer heap built using the heap interface.
package heap_test
import (
"container/heap"
"fmt"
...)
Example:
// This example demonstrates a priority queue built using the heap interface.
package heap_test
import (
"container/heap"
"fmt"
)
...
Fixes #3587.
R=golang-dev, minux.ma, adg, rsc, gri
CC=golang-dev
https://golang.org/cl/7356043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d97b975d5c1f87ecdda29211c46fa81b747248dc
元コミット内容
このコミットの元々の目的は、godoc
コマンドラインツールがテキスト出力モードでGoのExampleコードを表示できるようにすることです。これまでは、godoc
はHTMLモード(ウェブインターフェース)ではExampleを表示できましたが、コマンドラインでのテキスト出力では表示されませんでした。この機能追加により、開発者はターミナルから直接、より包括的なドキュメント(コード例を含む)を参照できるようになります。
コミットメッセージには、godoc -ex strings Index
や godoc -ex container/heap
といった具体的な使用例が示されており、出力されるExampleのフォーマットも確認できます。
変更の背景
この変更の背景には、Go言語のドキュメンテーションシステムにおけるExampleの重要性があります。Goでは、Example
関数として記述されたコードスニペットが、パッケージや関数の使用方法を具体的に示すための重要な手段として推奨されています。これらはテストとしても機能し、go test
コマンドで実行可能であり、同時に godoc
によってドキュメントとして表示されます。
しかし、このコミットが作成される以前は、godoc
のコマンドラインインターフェース(テキストモード)ではこれらのExampleが表示されませんでした。これは、開発者がターミナルでドキュメントを参照する際に、Exampleという重要な情報源が欠落していることを意味しました。
コミットメッセージにある Fixes #3587
は、この変更がGitHubのIssue #3587を解決するものであることを示しています。このIssueは、godoc
のテキスト出力にExampleを含めることの要望であったと推測されます。開発者の利便性向上と、Goのドキュメンテーション文化におけるExampleの活用を促進することが、この変更の主な動機です。
前提知識の解説
Go言語のドキュメンテーションとExample
Go言語では、コードのコメントがそのままドキュメンテーションとして機能します。特に、Example
関数は、特定の関数やパッケージの使用例を示すために用いられます。これらの関数は Example<Name>
の形式で命名され、go test
コマンドで実行可能なテストとしても扱われます。// Output:
コメントをExample関数内に記述することで、そのExampleの実行結果をドキュメントに含めることができ、go test
は実際の出力とこのコメントを比較してExampleが正しく動作するかを検証します。
godoc
ツール
godoc
はGo言語の標準ドキュメンテーションツールです。Goのソースコードからドキュメントを抽出し、ウェブインターフェースまたはコマンドラインで表示します。ウェブインターフェースでは、Exampleを含む完全なドキュメントが表示されますが、このコミット以前はコマンドライン(テキストモード)ではExampleが表示されませんでした。
text/template
パッケージ
Goの標準ライブラリである text/template
パッケージは、テキストベースのテンプレートを生成するための強力なツールです。HTML、XML、プレーンテキストなど、様々な形式の出力を生成するのに使用されます。godoc
はこのパッケージを利用して、Goのソースコードから抽出したドキュメント情報を整形し、最終的な出力(HTMLまたはテキスト)を生成しています。テンプレート内では、データ構造のフィールドにアクセスしたり、関数を呼び出したりすることができます。
flag
パッケージ
Goの標準ライブラリである flag
パッケージは、コマンドライン引数を解析するための機能を提供します。このコミットでは、godoc
コマンドに新しいフラグ -ex
を追加するために flag
パッケージが使用されています。
技術的詳細
このコミットの技術的な核心は、godoc
がテキスト出力時にExampleをレンダリングするための新しいロジックと、それを制御するコマンドラインフラグの追加です。
-
-ex
フラグの追加:src/cmd/godoc/godoc.go
にshowExamples = flag.Bool("ex", false, "show examples in command line mode")
が追加されました。これにより、godoc
コマンドに-ex
フラグが導入され、デフォルトではfalse
(Exampleを非表示)ですが、フラグが指定されるとtrue
になりExampleが表示されるようになります。 -
example_textFunc
の実装:src/cmd/godoc/godoc.go
にexample_textFunc
という新しい関数が追加されました。この関数はtext/template
パッケージから呼び出されるカスタム関数として登録され、Exampleのテキスト表現を生成する役割を担います。showExamples
フラグがfalse
の場合、この関数は空文字列を返し、Exampleは表示されません。- Exampleのコードは
printer.CommentedNode
を使用して整形され、bytes.Buffer
に書き込まれます。 - Exampleのコードが
{...}
で囲まれている場合(関数本体の形式)、外側の波括弧が削除され、インデントが調整されます。これは、Exampleが通常、関数本体として記述されるため、ドキュメント表示時にはその部分を整形する必要があるためです。 - 最終的に、整形されたExampleコードは
Example:
というプレフィックスと適切なインデントと共にバッファに書き込まれ、文字列として返されます。
-
テンプレートの変更 (
lib/godoc/package.txt
):lib/godoc/package.txt
は、godoc
がテキストモードでドキュメントを生成する際に使用するテンプレートファイルです。このファイルに{{example_text ...}}
というテンプレートアクションが追加されました。- パッケージ全体のドキュメント、関数、型、メソッドのそれぞれに対して、関連するExampleを表示するための
example_text
関数呼び出しが追加されています。 - これにより、
godoc
がドキュメントをレンダリングする際に、example_textFunc
が呼び出され、-ex
フラグの状態に応じてExampleがテキスト出力に組み込まれるようになります。
- パッケージ全体のドキュメント、関数、型、メソッドのそれぞれに対して、関連するExampleを表示するための
この変更により、godoc
はHTML出力と同様に、テキスト出力でもExampleを適切に整形して表示できるようになり、コマンドラインでのドキュメント参照体験が大幅に向上しました。
コアとなるコードの変更箇所
lib/godoc/package.txt
このファイルは、godoc
がテキストモードでドキュメントを生成する際に使用するGoの text/template
テンプレートです。
--- a/lib/godoc/package.txt
+++ b/lib/godoc/package.txt
@@ -9,7 +9,8 @@ package {{.Name}}\n \n {{else}}COMMAND DOCUMENTATION\n \n-{{end}}{{comment_text .Doc " " "\t"}}{{/*\n+{{end}}{{comment_text .Doc " " "\t"}}\n+{{example_text "" $.Examples $.FSet " "}}{{/*
\n ---------------------------------------\n \n@@ -36,6 +37,7 @@ FUNCTIONS\n \n {{range .}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n+{{example_text .Name $.Examples $.FSet " "}}\n {{end}}{{end}}{{/*
\n ---------------------------------------\n@@ -43,16 +45,19 @@ FUNCTIONS\n */}}{{with .Types}}\n TYPES\n \n-{{range .}}{{node .Decl $.FSet}}\n+{{range .}}{{$tname := .Name}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n {{range .Consts}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n {{end}}{{range .Vars}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n-{{end}}{{range .Funcs}}{{node .Decl $.FSet}}\n+{{end}}{{example_text .Name $.Examples $.FSet " "}}\n+{{range .Funcs}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n+{{example_text .Name $.Examples $.FSet " "}}\n {{end}}{{range .Methods}}{{node .Decl $.FSet}}\n {{comment_text .Doc " " "\t"}}\n+{{$name := printf "%s_%s" $tname .Name}}{{example_text $name $.Examples $.FSet " "}}\n {{end}}{{end}}{{end}}{{/*
\n ---------------------------------------\
src/cmd/godoc/godoc.go
このファイルは godoc
コマンドの主要なロジックを含んでいます。
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -65,6 +65,7 @@ var (\
showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings")
templateDir = flag.String("templates", "", "directory containing alternate template files")
showPlayground = flag.Bool("play", false, "enable playground in web interface")
+ showExamples = flag.Bool("ex", false, "show examples in command line mode")
// search index
indexEnabled = flag.Bool("index", false, "enable search index")
@@ -329,6 +330,47 @@ func stripExampleSuffix(name string) string {
return name
}
+func example_textFunc(funcName string, examples []*doc.Example, fset *token.FileSet, indent string) string {
+ if !*showExamples {
+ return ""
+ }
+
+ var buf bytes.Buffer
+ first := true
+ for _, eg := range examples {
+ name := stripExampleSuffix(eg.Name)
+ if name != funcName {
+ continue
+ }
+
+ if !first {
+ buf.WriteString("\n")
+ }
+ first = false
+
+ // print code
+ cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
+ var buf1 bytes.Buffer
+ writeNode(&buf1, fset, cnode)
+ code := buf1.String()
+ // Additional formatting if this is a function body.
+ if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' {
+ // remove surrounding braces
+ code = code[1 : n-1]
+ // unindent
+ code = strings.Replace(code, "\n ", "\n", -1)
+ }
+ code = strings.Trim(code, "\n")
+ code = strings.Replace(code, "\n", "\n\t", -1)
+
+ buf.WriteString(indent)
+ buf.WriteString("Example:\n\t")
+ buf.WriteString(code)
+ buf.WriteString("\n")
+ }
+ return buf.String()
+}
+
func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string {
var buf bytes.Buffer
for _, eg := range examples {
@@ -494,6 +536,7 @@ var fmap = template.FuncMap{
// formatting of Examples
"example_html": example_htmlFunc,
+ "example_text": example_textFunc,
"example_name": example_nameFunc,
"example_suffix": example_suffixFunc,
}
コアとなるコードの解説
lib/godoc/package.txt
の変更点
このテンプレートファイルでは、{{example_text ...}}
という新しいテンプレートアクションが複数箇所に追加されています。
{{example_text "" $.Examples $.FSet " "}}
: これはパッケージ全体のExampleを表示するために追加されました。最初の引数""
は、特定の関数名に紐づかないパッケージレベルのExampleを意味します。{{example_text .Name $.Examples $.FSet " "}}
: これは関数や型のExampleを表示するために追加されました。.Name
は現在の関数や型の名前をexample_textFunc
に渡し、その関数や型に関連するExampleをフィルタリングして表示します。{{$tname := .Name}}{{example_text $name $.Examples $.FSet " "}}
および{{$name := printf "%s_%s" $tname .Name}}{{example_text $name $.Examples $.FSet " "}}
: これらは型やメソッドのExampleを表示するために追加されました。特にメソッドの場合、printf "%s_%s" $tname .Name
を使用してTypeName_MethodName
の形式でExample名を構築し、関連するExampleを正確に取得できるようにしています。
これらの変更により、godoc
はテキスト出力時に、パッケージ、関数、型、メソッドのそれぞれに関連するExampleを、テンプレートエンジンを通じて動的に組み込むことができるようになりました。
src/cmd/godoc/godoc.go
の変更点
-
showExamples
フラグの定義:var showExamples = flag.Bool("ex", false, "show examples in command line mode")
この行は、godoc
コマンドに-ex
という新しいブーリアン型のコマンドラインフラグを追加します。デフォルト値はfalse
で、Exampleは表示されません。ユーザーがgodoc -ex ...
と実行した場合にtrue
になります。 -
example_textFunc
関数の追加: この関数は、godoc
のテキスト出力用にExampleを整形するロジックをカプセル化しています。if !*showExamples { return "" }
: このガード句により、-ex
フラグが指定されていない場合は、Exampleの処理をスキップし、空文字列を返します。これにより、Exampleの表示がユーザーの選択に依存するようになります。for _, eg := range examples
: 渡されたExampleのスライスをイテレートします。name := stripExampleSuffix(eg.Name)
: Example名から_test
などのサフィックスを削除します。if name != funcName { continue }
:funcName
と一致するExampleのみを処理します。これにより、特定の関数や型に関連するExampleのみが表示されます。- Exampleのコードの整形:
cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments}
: ExampleのASTノードとコメントをprinter.CommentedNode
にラップします。writeNode(&buf1, fset, cnode)
:go/printer
パッケージを使用して、ASTノードをコード文字列に変換します。if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}'
: Exampleが関数本体として記述されている場合(func ExampleFoo() { ... }
のように波括弧で囲まれている場合)を検出します。code = code[1 : n-1]
: 外側の波括弧を削除します。code = strings.Replace(code, "\n ", "\n", -1)
: Exampleコード内の余分なインデントを削除し、整形します。code = strings.Trim(code, "\n")
: 先頭と末尾の改行を削除します。code = strings.Replace(code, "\n", "\n\t", -1)
: 各行の先頭にタブ文字を追加し、適切なインデントを適用します。
buf.WriteString(indent)
: テンプレートから渡されたインデント文字列を追加します。buf.WriteString("Example:\n\t")
: "Example:" というプレフィックスとそれに続くタブインデントを追加します。buf.WriteString(code)
: 整形されたExampleコードを書き込みます。buf.WriteString("\n")
: Exampleの終わりに改行を追加します。
-
fmap
への登録:"example_text": example_textFunc,
この行は、example_textFunc
をtext/template
の関数マップfmap
にexample_text
という名前で登録します。これにより、package.txt
テンプレート内で{{example_text ...}}
としてこの関数を呼び出すことが可能になります。
これらの変更により、godoc
はコマンドラインでExampleを表示する機能が追加され、その表示はユーザーが -ex
フラグを指定した場合にのみ有効になります。Exampleの整形ロジックも、テキスト出力に適した形に調整されています。
関連リンク
- Go言語のドキュメンテーション: https://go.dev/doc/
godoc
コマンドのドキュメンテーション: https://pkg.go.dev/cmd/godocgo/doc
パッケージ (Exampleの構造を定義): https://pkg.go.dev/go/doctext/template
パッケージ: https://pkg.go.dev/text/templateflag
パッケージ: https://pkg.go.dev/flag
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/d97b975d5c1f87ecdda29211c46fa81b747248dc
- Go CL 7356043: https://golang.org/cl/7356043 (Goのコードレビューシステム)
- Go Issue #3587 (関連する可能性のあるIssue): https://github.com/golang/go/issues/3587 (このIssueは公開されていないか、番号が異なる可能性がありますが、コミットメッセージに記載されているため参照しました。)
- Go Examples: https://go.dev/blog/examples (GoブログのExampleに関する記事)
- GoDoc: https://go.dev/blog/godoc (Goブログのgodocに関する記事)
- GoDoc and Go Examples: https://go.dev/blog/godoc-and-go-examples (GoブログのgodocとExampleに関する記事)