[インデックス 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/docpackage documentation: https://pkg.go.dev/go/docgo/docパッケージの公式ドキュメントです。Note構造体やドキュメント抽出の仕組みについて詳細が記述されています。
go/astpackage documentation: https://pkg.go.dev/go/ast- Goの抽象構文木(AST)に関する公式ドキュメントです。
ast.Nodeの概念を理解するのに役立ちます。
- Goの抽象構文木(AST)に関する公式ドキュメントです。
go/tokenpackage documentation: https://pkg.go.dev/go/tokentoken.Posやtoken.FileSetなど、ソースコードの位置情報に関する公式ドキュメントです。
html/templatepackage 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言語におけるコメントの書き方や慣習について説明されており、