[インデックス 11259] ファイルの概要
このコミットは、Go言語の公式ドキュメンテーションツールであるgodoc
の機能改善に関するものです。具体的には、コマンドのドキュメントにおける見出しにアンカー(HTMLのid
属性)を追加し、クライアントサイドでの目次生成をより適切に行うための変更が含まれています。また、コマンドドキュメントにおいては、サーバーサイドでの目次生成を無効化しています。
コミット
commit 3358a5068a87bc25bd551698f4f0be7c5677168d
Author: Andrew Gerrand <adg@golang.org>
Date: Thu Jan 19 18:59:06 2012 +1100
godoc: add anchors to cmd documentation headings
Also, disable server-side generation of TOC for commands as they would
only ever show Overview. The JS does a better job (for now).
Fixes #2732.
R=gri, dsymonds
CC=golang-dev
https://golang.org/cl/5558046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3358a5068a87bc25bd551698f4f0be7c5677168d
元コミット内容
godoc
: コマンドドキュメントの見出しにアンカーを追加。
また、コマンドの目次が「概要」しか表示しないため、サーバーサイドでの目次生成を無効化。JavaScriptが(現時点では)より良い仕事をする。
Issue #2732 を修正。
変更の背景
この変更の背景には、godoc
ツールが生成するドキュメント、特にGoコマンド(例: go build
, go run
など)のドキュメントにおけるユーザビリティの向上が挙げられます。
-
見出しへの直接リンクの必要性: 従来の
godoc
が生成するHTMLドキュメントでは、各セクションの見出し(h3
タグなど)に一意のID(アンカー)が付与されていませんでした。これにより、特定のセクションへの直接リンクを共有したり、ブラウザの「ページ内検索」機能で正確な位置にジャンプしたりすることが困難でした。ユーザーはドキュメント全体をスクロールして目的のセクションを探す必要がありました。 -
コマンドドキュメントの目次問題:
godoc
はパッケージドキュメントに対しては詳細な目次を生成しますが、コマンドドキュメントの場合、その構造上、サーバーサイドで生成される目次が「概要 (Overview)」といった非常に限定的な内容しか表示しないという問題がありました。これはユーザーにとって有用な目次とは言えず、むしろスペースの無駄になっていました。 -
クライアントサイド(JavaScript)の優位性: 一方で、
godoc
にはクライアントサイドでJavaScriptを用いて目次を動的に生成する機能も存在しました。このJavaScriptによる目次生成は、HTMLのDOM構造を解析してより柔軟かつ詳細な目次を作成できるため、サーバーサイドの限定的な目次よりも優れていました。
これらの問題を解決し、ユーザーがgodoc
で生成されたドキュメントをより効率的に閲覧できるようにするために、本コミットが実施されました。特に、Issue #2732 はこのアンカー追加の必要性を指摘していたものと考えられます。
前提知識の解説
このコミットを理解するためには、以下の技術的知識が役立ちます。
-
Go言語の
godoc
ツール:godoc
はGo言語の標準ドキュメンテーションツールです。Goのソースコード内のコメント(特にエクスポートされた識別子に付随するコメント)を解析し、HTML形式でドキュメントを生成します。- 開発者は
godoc -http=:6060
のように実行することで、ローカルでドキュメントサーバーを立ち上げ、ブラウザでGoの標準ライブラリや自身のプロジェクトのドキュメントを閲覧できます。 godoc
は、Goのパッケージだけでなく、Goコマンド(例:go build
,go run
)のドキュメントも表示します。
-
HTMLのアンカー(
id
属性):- HTML要素に付与される
id
属性は、その要素を一意に識別するためのグローバル属性です。 id
属性を持つ要素には、URLのフラグメント識別子(例:https://example.com/page.html#section-id
)を使って直接リンクすることができます。これにより、ページの特定の位置にジャンプすることが可能になります。- JavaScriptから特定の要素にアクセスする際にも
document.getElementById()
などで利用されます。
- HTML要素に付与される
-
目次 (Table of Contents - TOC):
- ドキュメント内の主要な見出しを一覧表示し、各見出しへのリンクを提供するナビゲーション要素です。
- サーバーサイド生成: サーバーがHTMLを生成する際に、ドキュメントの内容を解析して目次をHTMLに埋め込む方式です。静的なコンテンツに適しています。
- クライアントサイド生成: ブラウザがHTMLを読み込んだ後、JavaScriptがDOM(Document Object Model)を解析し、動的に目次要素を生成してページに挿入する方式です。ページの読み込み後にコンテンツが動的に変更される場合や、より複雑なロジックで目次を生成したい場合に適しています。
-
Go言語の
text/template
パッケージ:- Go言語の標準ライブラリで、データ構造をテキスト(HTML、XML、プレーンテキストなど)に変換するためのテンプレートエンジンを提供します。
godoc
は内部でこのテンプレートエンジンを使用してHTMLドキュメントを生成しています。{{if .IsPkg}}
のような構文は、Goテンプレートにおける条件分岐であり、.IsPkg
という変数が真の場合にのみ、そのブロック内のHTMLが出力されます。
-
正規表現 (Regular Expression):
- 文字列のパターンマッチングや置換を行うための強力なツールです。
- このコミットでは、見出しテキストからHTMLのIDとして安全な文字列を生成するために使用されています。
技術的詳細
このコミットは、主に以下の3つのファイルにわたる変更で構成されており、それぞれが異なる役割を担っています。
-
src/pkg/go/doc/comment.go
:- このファイルは、Goのソースコードコメントを解析し、HTMLに変換するロジックを担っています。
- アンカーID生成ロジックの追加:
nonAlphaNumRx = regexp.MustCompile(
[^a-zA-Z0-9])
: 英数字以外の文字にマッチする正規表現が定義されました。func anchorID(line string) string
: この新しい関数は、入力された文字列(見出しテキスト)から、正規表現を使って英数字以外の文字をアンダースコア(_
)に置換し、HTMLのid
属性として安全な文字列を生成します。例えば、「Go Command Line」という見出しは「Go_Command_Line」のようなIDに変換されます。
- HTML出力の変更:
ToHTML
関数内のopHead
(見出し処理)のケースが修正されました。- 以前は単に
<h3>
タグを出力していましたが、変更後はanchorID
関数を使って生成したIDをid
属性として<h3>
タグに埋め込むようになりました。具体的には、<h3 id="generated_id">
という形式で出力されます。これにより、各見出しに一意のアンカーが設定され、直接リンクが可能になります。
-
lib/godoc/package.html
:- これは
godoc
がHTMLドキュメントを生成する際に使用するGoテンプレートファイルです。 - サーバーサイド目次生成の条件化:
manual-nav
というIDを持つdiv
要素(サーバーサイドで生成される目次が含まれる部分)全体が、{{if .IsPkg}}...{{end}}
というGoテンプレートの条件分岐で囲まれました。.IsPkg
は、現在表示しているドキュメントがGoパッケージである場合にtrue
となるコンテキスト変数です。- この変更により、コマンドドキュメント(
.IsPkg
がfalse
となる)の場合には、サーバーサイドで目次が生成されなくなり、HTML出力からmanual-nav
ブロックが完全に削除されます。これは、コマンドドキュメントの目次が「概要」しか表示しないという問題を解決するためのものです。
- これは
-
doc/godocs.js
:- このJavaScriptファイルは、クライアントサイドで動的に目次を生成するスクリプトです。
- クライアントサイド目次生成ロジックの改善:
godocs_generateTOC()
関数がリファクタリングされました。- 以前は
h2
とh3
タグに対して別々の処理ブロックがありましたが、これらが統合され、より汎用的なロジックになりました。 - すべての
h2
およびh3
要素に対して、もしid
属性がなければ、JavaScript側で一時的なID(tmp_
プレフィックス付き)を生成して付与するようになりました。 - これにより、サーバーサイドでIDが付与されていない見出し(例えば、
godoc
が生成する他の種類のドキュメントや、将来的に変更される可能性のあるHTML構造)に対しても、クライアントサイドのJavaScriptが確実にアンカーを生成し、目次を作成できるようになります。 h2
タグはdt
要素(定義リストの用語)、h3
タグはdd
要素(定義リストの定義)として目次項目を生成するロジックは維持されています。
これらの変更により、godoc
はコマンドドキュメントの見出しにアンカーを付与し、サーバーサイドの冗長な目次を削除し、クライアントサイドのJavaScriptがより堅牢に目次を生成できるようになりました。
コアとなるコードの変更箇所
src/pkg/go/doc/comment.go
--- a/src/pkg/go/doc/comment.go
+++ b/src/pkg/go/doc/comment.go
@@ -68,7 +68,8 @@ var (
thtml_endp = []byte("</p>\\n")
thtml_pre = []byte("<pre>")
thtml_endpre = []byte("</pre>\\n")
- thtml_h = []byte("<h3>")
+ thtml_h = []byte(`<h3 id="`)
+ thtml_hq = []byte(`">`)
thtml_endh = []byte("</h3>\\n")
)
@@ -225,6 +226,12 @@ type block struct {
lines []string
}
+var nonAlphaNumRx = regexp.MustCompile(`[^a-zA-Z0-9]`)
+
+func anchorID(line string) string {
+ return nonAlphaNumRx.ReplaceAllString(line, "_")
+}
+
// ToHTML converts comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
@@ -253,9 +260,18 @@ func ToHTML(w io.Writer, text string, words map[string]string) {
case opHead:
w.Write(html_h)
+ id := ""
for _, line := range b.lines {
+ if id == "" {
+ id = anchorID(line)
+ w.Write([]byte(id))
+ w.Write(html_hq)
+ }
commentEscape(w, line, true)
}
+ if id == "" {
+ w.Write(html_hq)
+ }
w.Write(html_endh)
case opPre:
w.Write(html_pre)
lib/godoc/package.html
--- a/lib/godoc/package.html
+++ b/lib/godoc/package.html
@@ -3,6 +3,7 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
+{{if .IsPkg}}
<!-- Table of contents; must be named manual-nav to turn off auto nav. -->
<div id="manual-nav">
{{with .PDoc}}
@@ -37,6 +38,7 @@
</dl>
{{end}}
</div>
+{{end}}
<!-- Main page -->
{{with .PAst}}
doc/godocs.js
--- a/doc/godocs.js
+++ b/doc/godocs.js
@@ -66,44 +66,32 @@ function godocs_generateTOC() {
var i;
for (i = 0; i < navbar.parentNode.childNodes.length; i++) {
var node = navbar.parentNode.childNodes[i];
- if ((node.tagName == 'h2') || (node.tagName == 'H2')) {
- if (!node.id) {
- node.id = 'tmp_' + i;
- }
- var text = godocs_nodeToText(node);
- if (!text) { continue; }
-
- var textNode = document.createTextNode(text);
-
- var link = document.createElement('a');
- link.href = '#' + node.id;
- link.appendChild(textNode);
-
- // Then create the item itself
- var item = document.createElement('dt');
-
- item.appendChild(link);
- toc_items.push(item);
+ if ((node.tagName != 'h2') && (node.tagName != 'H2') &&
+ (node.tagName != 'h3') && (node.tagName != 'H3')) {
+ continue;
}
- if ((node.tagName == 'h3') || (node.tagName == 'H3')) {
- if (!node.id) {
- node.id = 'tmp_' + i;
- }
- var text = godocs_nodeToText(node);
- if (!text) { continue; }
-
- var textNode = document.createTextNode(text);
+ if (!node.id) {
+ node.id = 'tmp_' + i;
+ }
+ var text = godocs_nodeToText(node);
+ if (!text) { continue; }
- var link = document.createElement('a');
- link.href = '#' + node.id;
- link.appendChild(textNode);
+ var textNode = document.createTextNode(text);
- // Then create the item itself
- var item = document.createElement('dd');
+ var link = document.createElement('a');
+ link.href = '#' + node.id;
+ link.appendChild(textNode);
- item.appendChild(link);
- toc_items.push(item);
+ // Then create the item itself
+ var item;
+ if ((node.tagName == 'h2') || (node.tagName == 'H2')) {
+ item = document.createElement('dt');
+ } else { // h3
+ item = document.createElement('dd');
}
+
+ item.appendChild(link);
+ toc_items.push(item);
}
if (toc_items.length <= 1) { return; }
コアとなるコードの解説
src/pkg/go/doc/comment.go
の変更
このファイルは、GoのドキュメンテーションコメントをHTMLに変換するGo言語のコードです。
最も重要な変更は、anchorID
関数の導入と、ToHTML
関数内で<h3>
タグに動的にid
属性を付与するロジックです。
-
nonAlphaNumRx
とanchorID
関数:nonAlphaNumRx
は、正規表現[^a-zA-Z0-9]
をコンパイルしたものです。これは、アルファベット(大文字・小文字)と数字以外のすべての文字にマッチします。anchorID(line string)
関数は、この正規表現を使って、入力された文字列(見出しのテキスト)から英数字以外の文字をアンダースコア_
に置換します。これにより、HTMLのid
属性として有効で、かつ見出しの内容をある程度反映したユニークな文字列が生成されます。例えば、「func (c *Client) Do(req *Request) (*Response, error)
」のような関数シグネチャが見出しになった場合でも、「func__c__Client__Do_req__Request____Response__error
」のような形式でIDが生成され、URLのフラグメントとして利用可能になります。
-
ToHTML
関数内のopHead
処理:ToHTML
関数は、ドキュメントのブロックタイプ(段落、見出し、整形済みテキストなど)に応じてHTMLを生成します。opHead
ケースは、見出しブロックを処理します。- 変更前は単に
<h3>
タグを出力していましたが、変更後は、見出しの最初の行(b.lines[0]
)からanchorID
関数を使ってIDを生成し、それを<h3 id="生成されたID">
という形式でHTMLに出力するようにしました。 - これにより、
godoc
が生成するすべての<h3>
見出しに一意のIDが付与され、ブラウザのアンカーリンク機能が利用できるようになります。
lib/godoc/package.html
の変更
このファイルは、godoc
がHTMLページをレンダリングする際に使用するGoテンプレートです。
変更は非常にシンプルですが、その影響は大きいです。
manual-nav
というIDを持つdiv
要素(サーバーサイドで生成される目次が含まれる)全体が、{{if .IsPkg}}...{{end}}
という条件分岐で囲まれました。.IsPkg
は、現在表示しているページがGoパッケージのドキュメントである場合にtrue
となるテンプレート変数です。- この変更により、Goコマンドのドキュメント(パッケージではないため
.IsPkg
がfalse
となる)を表示する際には、このmanual-nav
ブロックがHTML出力に含まれなくなります。結果として、コマンドドキュメントからサーバーサイドで生成される冗長な目次が削除されます。
doc/godocs.js
の変更
このJavaScriptファイルは、ブラウザ上で動的に目次を生成するクライアントサイドスクリプトです。
godocs_generateTOC()
関数がリファクタリングされ、h2
とh3
タグの処理が統合されました。- 最も重要な変更は、
if (!node.id) { node.id = 'tmp_' + i; }
という行です。これは、HTML要素にid
属性がまだ存在しない場合、JavaScriptが動的にtmp_
プレフィックスとインデックス番号を組み合わせた一時的なIDを付与することを意味します。 - この変更は、
src/pkg/go/doc/comment.go
で<h3>
タグにIDが付与されるようになったことと合わせて、クライアントサイドの目次生成がより堅牢になることを保証します。たとえサーバーサイドでIDが付与されなかったとしても、JavaScriptがフォールバックとしてIDを生成するため、目次機能が損なわれることがありません。 - また、
h2
タグはdt
要素(定義リストの用語)、h3
タグはdd
要素(定義リストの定義)として目次項目を生成するという既存のロジックは維持されており、これにより階層的な目次が表現されます。
これらの変更が連携することで、godoc
はより使いやすいドキュメントを生成できるようになりました。
関連リンク
- Go Issue 2732: https://github.com/golang/go/issues/2732
- このコミットが修正したとされるIssueです。タイトルは「godoc: add anchors to headings」であり、まさにこのコミットの目的と合致しています。
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- HTML
id
属性に関するMDN Web Docs: https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/id - Go言語
text/template
パッケージに関するドキュメント: https://pkg.go.dev/text/template - Go言語
regexp
パッケージに関するドキュメント: https://pkg.go.dev/regexp - Gerrit Change-ID: https://golang.org/cl/5558046 (現在はGoのGerritインスタンスはGitHubに移行しているため、このリンクは直接機能しない可能性がありますが、当時のレビュープロセスを示すものです。)
- Go言語の
godoc
ツールに関する情報 (Go Wiki): https://go.dev/wiki/Godoc - Go言語の
go doc
コマンドに関する情報: https://go.dev/cmd/go/#hdr-Show_documentation_for_package_or_symbol