[インデックス 15993] ファイルの概要
このコミットは、Go言語の公式ドキュメンテーションツールである godoc
において、生成されるHTMLドキュメント内の「ノート」(TODO
, BUG
などの特殊なコメント)から、そのノートが記述されているソースコード上の正確な位置へ直接リンクできるようにする機能追加です。これにより、ドキュメントを閲覧している開発者が、関連するソースコードを素早く参照できるようになり、開発効率とドキュメントの有用性が向上します。
コミット
commit f1b7c140ffe42db59b51937037b2af6c48fa94b0
Author: Robert Griesemer <gri@golang.org>
Date: Thu Mar 28 14:40:59 2013 -0700
cmd/godoc: provide a link from notes to source location
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/8122043
---
lib/godoc/package.html | 4 ++--
src/cmd/godoc/godoc.go | 32 +++++++++++++++++++++++---------\
src/pkg/go/doc/doc.go | 6 +++---\
src/pkg/go/doc/reader.go | 1 +
4 files changed, 29 insertions(+), 14 deletions(-)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f1b7c140ffe42db59b51937037b2af6c48fa94b0
元コミット内容
cmd/godoc: provide a link from notes to source location
このコミットは、godoc
コマンドが生成するドキュメントにおいて、ノート(例: TODO
, BUG
コメント)から、そのノートが記述されているソースコードの場所へリンクを提供する機能を追加します。
変更の背景
godoc
はGo言語のコードから自動的にドキュメントを生成し、Webブラウザで閲覧可能な形式で提供するツールです。Goのソースコード内には、開発者が将来の作業や既知の問題を示すために TODO:
や BUG:
といった特定のマーカーを持つコメント(これらを godoc
では「ノート」と呼びます)を記述することがよくあります。
このコミット以前は、godoc
が生成するドキュメントにはこれらのノートが表示されていましたが、そのノートがソースコードのどの部分に書かれているのかを直接知る手段がありませんでした。開発者がドキュメントを読んでいて特定のノートに興味を持った場合、手動でソースコードを検索する必要があり、これは非効率的でした。
この変更の背景には、ドキュメントとソースコードの間のナビゲーションをシームレスにし、開発者がより迅速にコードのコンテキストを理解できるようにするという目的があります。ノートから直接ソースコードの該当行へジャンプできる機能は、コードベースの探索や問題の追跡において非常に有用です。
前提知識の解説
このコミットを理解するためには、以下のGo言語のツールや概念に関する知識が必要です。
godoc
: Go言語のソースコードからドキュメントを生成し、HTTPサーバーとして提供するツールです。Goの標準ライブラリのドキュメントもgodoc
によって生成されています。コード内のコメントや宣言から情報を抽出し、整形されたHTMLとして表示します。go/doc
パッケージ:godoc
の中核をなすパッケージの一つで、Goのソースコードを解析し、ドキュメント構造(パッケージ、型、関数、変数、定数、ノートなど)を抽出する役割を担います。このパッケージがast
パッケージとtoken
パッケージを利用して、ソースコードの抽象構文木(AST)を走査し、ドキュメント情報を収集します。go/ast
パッケージ: Go言語のソースコードの抽象構文木(AST)を表現するためのデータ構造と関数を提供します。ソースコードはまず字句解析され、次に構文解析されてASTが構築されます。ASTの各ノードは、関数宣言、変数宣言、コメントなど、コードの特定の要素を表します。go/token
パッケージ: ソースコード内の位置(ファイル名、行番号、列番号、オフセットなど)を表現するためのtoken.Pos
型を提供します。token.Pos
は、ソースコード内の特定のバイトオフセットを抽象的に表現するもので、token.FileSet
と組み合わせることで具体的なファイル名や行番号に変換できます。html/template
パッケージ: Go言語でHTMLテンプレートを扱うためのパッケージです。godoc
はこれを利用して、抽出したドキュメント情報をHTML形式でレンダリングします。テンプレート内でGoの構造体のフィールドやメソッドを呼び出すことができます。- ノート (Notes):
godoc
における「ノート」とは、TODO:
,BUG:
,FIXME:
などの特定のキーワードで始まるコメントのことです。go/doc
パッケージがこれらのコメントを特別に扱い、ドキュメントに抽出して表示します。
技術的詳細
このコミットは、godoc
が生成するHTMLドキュメント内のノートに、対応するソースコードへのリンクを追加するために、以下の主要な変更を導入しています。
-
go/doc.Note
構造体の拡張:- 以前の
doc.Note
構造体は、ノートの開始位置を示すPos token.Pos
フィールドのみを持っていました。 - このコミットでは、ノートの終了位置を示す
End token.Pos
フィールドが追加されました。これにより、ノートがコメントブロック全体をカバーする範囲を正確に指定できるようになります。 - 変更前:
Pos token.Pos
- 変更後:
Pos, End token.Pos
- 以前の
-
go/doc/reader.go
でのEnd
フィールドの取得:go/doc
パッケージ内のreader.go
は、ソースコードを読み込み、コメントからノートを抽出するロジックを含んでいます。readNote
関数が、ノートを構成するコメントのリストの最後のコメントの終了位置 (list[len(list)-1].End()
) を取得し、新しく追加されたdoc.Note
のEnd
フィールドに設定するように変更されました。これにより、ノートの正確な範囲情報がdoc.Note
オブジェクトに格納されるようになります。
-
cmd/godoc/godoc.go
でのposLink_urlFunc
の拡張:godoc.go
に存在するposLink_urlFunc
は、ast.Node
(抽象構文木のノード)からソースコードへのリンクURLを生成するためのテンプレート関数です。- このコミットでは、
posLink_urlFunc
がast.Node
だけでなく、新しく*doc.Note
型の引数も受け取れるように拡張されました。 - 関数内で型アサーション (
switch n := n.(type)
) を使用して、引数がast.Node
なのか*doc.Note
なのかを判別します。 *doc.Note
の場合、そのPos
とEnd
フィールドを使用して、ソースコード内の正確な範囲(オフセット)を特定し、それに基づいてURLを構築します。このURLは、godoc
が提供するソースコードビューアの特定の行や範囲にジャンプするためのものです。
-
lib/godoc/package.html
テンプレートの変更:godoc
がパッケージドキュメントを生成する際に使用するHTMLテンプレート (package.html
) が変更されました。- ノートを表示するセクション (
{{with $.Notes}}
) 内で、各ノートの表示の隣に新しいリンクが追加されました。 - 具体的には、
<li>
タグ内に<a>
タグが追加され、そのhref
属性にposLink_url
テンプレート関数が呼び出されます。この関数に現在のノートオブジェクト (.
) が渡され、ソースコードへのリンクURLが生成されます。 - リンクのテキストには、右向きの矢印 (
☞
) が使用され、視覚的にソースへのジャンプを示します。 - また、ノートのリストのスタイルが
list-style: none; padding: 0;
に変更され、デフォルトのリストマーカーが削除され、よりクリーンな表示になるように調整されています。
これらの変更により、go/doc
パッケージがノートの正確なソースコード範囲を抽出し、godoc
コマンドがその情報を使ってHTMLテンプレート内でクリック可能なソースコードリンクを生成する、という一連のフローが確立されました。
コアとなるコードの変更箇所
src/pkg/go/doc/doc.go
Note
構造体に End
フィールドが追加されました。
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -69,9 +69,9 @@ type Func struct {
// at least one character is recognized. The ":" following the uid is optional.
// Notes are collected in the Package.Notes map indexed by the notes marker.
type Note struct {
- Pos token.Pos // position of the comment containing the marker
- UID string // uid found with the marker
- Body string // note body text
+ Pos, End token.Pos // position range of the comment containing the marker
+ UID string // uid found with the marker
+ Body string // note body text
}
// Mode values control the operation of New.
src/pkg/go/doc/reader.go
readNote
関数で、Note
構造体の End
フィールドが設定されるようになりました。
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -419,6 +419,7 @@ func (r *reader) readNote(list []*ast.Comment) {
marker := text[m[2]:m[3]]
r.notes[marker] = append(r.notes[marker], &Note{
Pos: list[0].Pos(),
+ End: list[len(list)-1].End(),
UID: text[m[4]:m[5]],
Body: body,
})
src/cmd/godoc/godoc.go
posLink_urlFunc
が *doc.Note
型も処理できるようになり、Pos
と End
を利用してリンクを生成します。
--- a/src/cmd/godoc/godoc.go
+++ b/src/cmd/godoc/godoc.go
@@ -481,19 +481,33 @@ func pkgLinkFunc(path string) string {
return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
}
-func posLink_urlFunc(info *PageInfo, node ast.Node) string {
+// n must be an ast.Node or a *doc.Note
+func posLink_urlFunc(info *PageInfo, n interface{}) string {
+ var pos, end token.Pos
+
+ switch n := n.(type) {
+ case ast.Node:
+ pos = n.Pos()
+ end = n.End()
+ case *doc.Note:
+ pos = n.Pos
+ end = n.End
+ default:
+ panic(fmt.Sprintf("wrong type for posLink_url template formatter: %T", n))
+ }
+
var relpath string
var line int
- var low, high int // selection
+ var low, high int // selection offset range
- if p := node.Pos(); p.IsValid() {
- pos := info.FSet.Position(p)
- relpath = pos.Filename
- line = pos.Line
- low = pos.Offset
+ if pos.IsValid() {
+ p := info.FSet.Position(pos)
+ relpath = p.Filename
+ line = p.Line
+ low = p.Offset
}
- if p := node.End(); p.IsValid() {
- high = info.FSet.Position(p).Offset
+ if end.IsValid() {
+ high = info.FSet.Position(end).Offset
}
var buf bytes.Buffer
lib/godoc/package.html
ノートの表示部分にソースコードへのリンクが追加されました。
--- a/lib/godoc/package.html
+++ b/lib/godoc/package.html
@@ -169,9 +169,9 @@
{{with $.Notes}}
{{range $marker, $content := .}}
<h2 id="pkg-note-{{$marker}}">{{noteTitle $marker | html}}s</h2>
- <ul>
+ <ul style="list-style: none; padding: 0;">
{{range .}}
- <li>{{html .Body}}</li>
+ <li><a href="{{posLink_url $ .}}">☞</a> {{html .Body}}</li>
{{end}}
</ul>
{{end}}
コアとなるコードの解説
このコミットの核心は、go/doc
パッケージがノートのソースコード上の範囲を正確に把握し、その情報を godoc
コマンドがHTMLリンクとして利用できるようにする点にあります。
-
doc.Note
構造体の拡張:src/pkg/go/doc/doc.go
でNote
構造体にEnd token.Pos
が追加されたことで、ノートがソースコードのどこからどこまでを指すのかという「範囲」情報が保持できるようになりました。これは、単一のコメント行だけでなく、複数行にわたるコメントブロック全体をノートとして扱う場合に特に重要です。
-
reader.go
でのEnd
の設定:src/pkg/go/doc/reader.go
のreadNote
関数は、ソースコードからコメントを読み取り、それがノートであると判断した場合にNote
オブジェクトを生成します。- 以前はコメントリストの最初のコメントの開始位置 (
list[0].Pos()
) のみを使用していましたが、変更後はlist[len(list)-1].End()
を使用して、ノートを構成する最後のコメントの終了位置を取得し、Note.End
に設定します。これにより、ノートの正確な範囲がgo/doc
レベルで捕捉されます。
-
godoc.go
のposLink_urlFunc
の多態性:src/cmd/godoc/godoc.go
のposLink_urlFunc
は、HTMLテンプレートから呼び出される関数で、ソースコード内の位置へのURLを生成します。- この関数は元々
ast.Node
を引数としていましたが、interface{}
を受け取るように変更され、内部でswitch n := n.(type)
を用いてast.Node
または*doc.Note
のどちらが渡されたかを判別します。 *doc.Note
が渡された場合、そのNote
オブジェクトのPos
とEnd
フィールドからソースコードのオフセット情報を取得します。このオフセット情報(low
とhigh
)は、godoc
のソースコードビューアが特定の範囲をハイライト表示するために使用されます。- 最終的に、ファイルパス、行番号、そしてオフセット範囲を含むURL(例:
/src/pkg/foo/bar.go?s=123:456#L789
のような形式)が生成されます。
-
package.html
でのリンクのレンダリング:lib/godoc/package.html
テンプレートでは、{{range .}}
ループで各ノートを処理する際に、<li>
要素内に<a>
タグが追加されました。href="{{posLink_url $ .}}"
の部分が重要で、ここでposLink_urlFunc
が呼び出され、現在のノートオブジェクト (.
) が引数として渡されます。これにより、各ノートに対して個別のソースコードリンクが動的に生成されます。☞
はHTMLエンティティで、右向きの矢印(☛)を表示します。これは、ユーザーがクリックすることでソースコードへジャンプできることを視覚的に示します。
これらの変更が連携することで、godoc
はノートの正確なソース位置を把握し、それをユーザーフレンドリーな形でドキュメントに表示できるようになりました。
関連リンク
- Go Change List 8122043: https://golang.org/cl/8122043
- このコミットの元となったGoのコードレビューシステム(Gerrit)上の変更リストです。コミットメッセージに記載されているリンクであり、変更の議論や詳細な差分を確認できます。
参考にした情報源リンク
- GoDoc (Go Documentation) - The Go Programming Language: https://go.dev/blog/godoc
godoc
の基本的な機能と使い方について解説しているGo公式ブログの記事です。
go/doc
package documentation: https://pkg.go.dev/go/docgo/doc
パッケージの公式ドキュメントです。Note
構造体やドキュメント抽出の仕組みについて詳細が記述されています。
go/ast
package documentation: https://pkg.go.dev/go/ast- Goの抽象構文木(AST)に関する公式ドキュメントです。
ast.Node
の概念を理解するのに役立ちます。
- Goの抽象構文木(AST)に関する公式ドキュメントです。
go/token
package documentation: https://pkg.go.dev/go/tokentoken.Pos
やtoken.FileSet
など、ソースコードの位置情報に関する公式ドキュメントです。
html/template
package documentation: https://pkg.go.dev/html/template- GoのHTMLテンプレートに関する公式ドキュメントです。
godoc
がどのようにHTMLを生成しているかを理解するのに役立ちます。
- GoのHTMLテンプレートに関する公式ドキュメントです。
- Go言語のソースコードコメントの慣習 (TODO, BUGなど): https://go.dev/doc/effective_go#commentary
- Go言語におけるコメントの書き方や慣習について説明されており、
TODO
などのノートの背景を理解するのに役立ちます。
- Go言語におけるコメントの書き方や慣習について説明されており、