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

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

このコミットは、Go言語のドキュメンテーションツールであるgo/docおよびgodocにおける「ノート(note)」の読み込みと表示方法を改善するものです。具体的には、コメントグループ内でのノートの配置に関する柔軟性を高め、ノートのメタデータ(UIDや位置情報)を保持するための新しい型doc.Noteを導入し、godocの出力におけるノートのフォーマットを改善しています。

コミット

commit 5268119f26728ddd2ee9f8eebcbfcec83ac5bd69
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Mar 19 11:14:35 2013 -0700

    go/doc, godoc: improved note reading
    
    - A note doesn't have to be in the first
    comment of a comment group anymore, and
    several notes may appear in the same comment
    group (e.g., it is fairly common to have a
    TODO(uid) note immediately following another
    comment).
    
    - Define a doc.Note type which also contains
    note uid and position info.
    
    - Better formatting in godoc output. The position
    information is not yet used, but could be used to
    locate the note in the source text if desired.
    
    Fixes #4843.
    
    R=r, cnicolaou
    CC=gobot, golang-dev
    https://golang.org/cl/7496048

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

https://github.com/golang/go/commit/5268119f26728ddd2ee9f8eebcbfcec83ac5bd69

元コミット内容

go/doc, godoc: improved note reading

  • ノートはコメントグループの最初のコメントにある必要がなくなり、同じコメントグループ内に複数のノートが存在できるようになりました(例:別のコメントの直後にTODO(uid)ノートがあるのはよくあることです)。
  • ノートのUIDと位置情報も含むdoc.Note型を定義しました。
  • godoc出力でのフォーマットを改善しました。位置情報はまだ使用されていませんが、必要に応じてソーステキスト内のノートを特定するために使用できます。

Fixes #4843.

変更の背景

Go言語のドキュメンテーションツールであるgodocは、ソースコード内のコメントからドキュメントを生成します。このツールは、特定の形式のコメント、特にMARKER(uid):のような形式で記述された「ノート」を認識し、特別な情報として扱います。例えば、TODO(userid):BUG(userid):といったコメントは、それぞれTODOリストやバグ報告として抽出され、ドキュメントに表示されます。

このコミットが行われる以前は、ノートの認識にはいくつかの制限がありました。

  1. コメントグループの最初のコメントに限定: ノートはコメントグループ(連続するコメントのブロック)の最初のコメントに記述されている必要がありました。これにより、開発者がコメントの途中にTODOなどを追記したい場合に、既存のコメント構造を崩すか、ノートとして認識されないリスクがありました。
  2. 複数のノートの認識の制限: 同じコメントグループ内に複数のノートが存在する場合、それらが適切に認識されない可能性がありました。
  3. メタデータの欠如: 抽出されたノートは単なる文字列として扱われ、そのノートがソースコードのどこに位置しているか、誰が記述したか(UID)といった詳細なメタ情報が失われていました。これにより、godocの出力でノートをよりリッチに表示したり、IDE連携などでソースコード上の位置にジャンプしたりする機能の実装が困難でした。

これらの制限は、開発者がコードにノートを埋め込む際の柔軟性を低下させ、godocが提供できる情報の質を制限していました。特に、Fixes #4843とあるように、このコミットは特定の課題(おそらくGitHub issue #4843)を解決するために行われました。この課題は、コメントグループ内のノートの認識に関する問題であったと推測されます。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念とツールの知識が必要です。

  1. Go言語のコメント: Go言語では、単一行コメント(//)と複数行コメント(/* ... */)が使用されます。godocはこれらのコメントを解析してドキュメントを生成します。
  2. go/astパッケージ: Goの抽象構文木(AST: Abstract Syntax Tree)を扱うためのパッケージです。ソースコードを解析し、その構造をプログラムで操作可能なデータ構造として表現します。コメントもASTの一部として扱われます。
  3. go/tokenパッケージ: ソースコード内のトークン(キーワード、識別子、演算子など)や、それらの位置情報(ファイル名、行番号、列番号など)を扱うためのパッケージです。token.Pos型は、ソースコード内の特定の位置を表します。
  4. go/docパッケージ: Goのソースコードからドキュメントを抽出・生成するためのライブラリです。godocツールはこのパッケージを利用しています。go/docは、パッケージ、関数、型、変数などのドキュメントだけでなく、特定の形式のコメント(例: TODO, BUG)を「ノート」として認識し、抽出する機能を持っています。
  5. godocツール: Go言語の標準ドキュメンテーションツールです。ローカルのGoパッケージのドキュメントを生成し、Webブラウザで表示したり、コマンドラインで表示したりできます。開発者がコードに記述したコメントや、go/docが抽出したノートなどを整形して表示します。
  6. コメントグループ: godocの文脈では、連続するコメント行や、コードブロックの直前にあるコメントブロックは「コメントグループ」として扱われます。通常、このコメントグループが、その後に続く宣言(関数、型など)のドキュメントとして認識されます。
  7. ノートのマーカーとUID: godocが認識するノートは、通常MARKER(uid):という形式で始まります。
    • MARKER: 大文字2文字以上の文字列(例: TODO, BUG, FIXME, SECURITYなど)。
    • uid: ノートの作成者や関連するIDなどを示す文字列。
    • :: UIDの後に続くコロンは、このコミット以前は必須でしたが、このコミットでオプションになりました。

技術的詳細

このコミットの技術的な変更点は多岐にわたりますが、主に以下の3つの側面があります。

  1. doc.Note型の導入:

    • 以前は、抽出されたノートは単なるstringのスライス(map[string][]string)としてPackage.Notesに格納されていました。
    • このコミットでは、src/pkg/go/doc/doc.goに新しい構造体doc.Noteが定義されました。
      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
      }
      
    • このNote型は、ノートの本文(Body)だけでなく、そのノートがソースコードのどこに記述されているかを示すPostoken.Pos型)と、ノートのUID(UID)を保持します。
    • これにより、Package.Notesの型がmap[string][]stringからmap[string][]*Noteに変更され、よりリッチなメタデータを保持できるようになりました。
  2. ノート読み込みロジックの改善 (src/pkg/go/doc/reader.go):

    • go/docパッケージの内部でコメントを解析し、ノートを抽出するreader構造体とその関連メソッドが大幅に修正されました。
    • 柔軟なノート認識: 以前はreadNote関数がコメントグループの最初のコメントのみを対象としていましたが、新しいreadNotes関数はコメントグループ内のすべてのコメントを走査し、個々のコメント行の先頭にMARKER(uid):パターンが存在するかどうかをチェックするようになりました。これにより、コメントグループの途中や、複数のノートが連続して記述されている場合でも、それぞれを独立したノートとして認識できるようになりました。
    • 正規表現の変更: ノートのマーカーを認識するための正規表現noteMarkerが更新され、UIDの後のコロン(:)がオプションになりました(:?)。また、コメントの先頭にマーカーがあることを厳密にチェックするためのnoteMarkerRxnoteCommentRx`が導入されました。
    • clean関数の導入: 抽出されたノートの本文から余分な空白や改行を削除し、整形するためのclean関数が追加されました。これにより、godoc出力での表示がよりきれいになります。
    • Package.Bugsの非推奨化: 以前はPackage.Bugsフィールドにバグ関連のノートが格納されていましたが、このコミットによりPackage.Notesに統一され、Package.Bugsは非推奨(deprecated)となりました。noteBodiesヘルパー関数が追加され、Package.BugsPackage.Notes["BUG"]から派生する形で互換性が保たれています。
  3. godoc出力のフォーマット改善 (lib/godoc/package.html, lib/godoc/package.txt, src/cmd/godoc/godoc.go):

    • godocのHTMLテンプレート(package.html)とテキストテンプレート(package.txt)が更新され、新しいdoc.Note型に対応しました。
    • HTML出力では、ノートが<ul>(リスト)要素で囲まれ、各ノートが<li>要素として表示されるようになりました。これにより、ノートがより構造化されて表示されます。
    • テキスト出力でも、ノートの本文とUIDが適切に表示されるように変更されました。
    • src/cmd/godoc/godoc.goPageInfo構造体内のNotesフィールドの型がmap[string][]stringからmap[string][]*doc.Noteに変更され、新しいデータ構造に対応しました。

これらの変更により、godocはより柔軟にノートを認識し、より詳細な情報を保持し、より見やすい形でドキュメントに表示できるようになりました。

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

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  • src/pkg/go/doc/doc.go:

    • doc.Note構造体の定義が追加されました。
    • Package構造体のNotesフィールドの型がmap[string][]stringからmap[string][]*Noteに変更されました。
    • Bugsフィールドが非推奨となり、Notes["BUG"]から派生するように変更されました。
    • noteBodiesヘルパー関数が追加されました。
  • src/pkg/go/doc/reader.go:

    • ノートを読み込むロジックが大幅に修正されました。
    • readNote関数がreadNotesメソッドに置き換えられ、コメントグループ内の複数のノートを認識できるようになりました。
    • ノートマーカーを認識するための正規表現が更新されました。
    • clean関数が追加されました。
    • readFileメソッド内でsrc.Commentsからノートを収集する部分がr.readNotes(src.Comments)に置き換えられました。
  • src/cmd/godoc/godoc.go:

    • PageInfo構造体のNotesフィールドの型がmap[string][]stringからmap[string][]*doc.Noteに変更されました。
    • getPageInfoメソッド内でノートを収集するロジックが新しいdoc.Note型に対応するように修正されました。
  • lib/godoc/package.html および lib/godoc/package.txt:

    • godocの出力テンプレートが更新され、新しいdoc.Note型と、そのBodyおよびUIDフィールドを使用してノートを整形して表示するようになりました。特にHTMLでは<ul><li>が導入されました。
  • src/pkg/go/doc/testdata/a0.go および src/pkg/go/doc/testdata/*.golden:

    • 新しいノート認識ロジックをテストするためのサンプルコード(a0.go)が追加されました。これには、コメントグループ内に複数のノートがあるケースや、コロンが省略されたケースなどが含まれます。
    • テストの期待される出力(.goldenファイル)が更新され、新しいフォーマットとノートの認識結果が反映されました。

コアとなるコードの解説

src/pkg/go/doc/doc.go

// A Note represents marked comments starting with "MARKER(uid): note body".
// Any note with a marker of 2 or more upper case [A-Z] letters and a uid of
// 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
}

このNote構造体が、このコミットの核となる変更の一つです。以前はノートの本文しか保持していませんでしたが、Pos(ソースコード上の位置)とUID(ノートの識別子)が追加されたことで、ノートに関するより豊富なメタデータが扱えるようになりました。

src/pkg/go/doc/reader.go

var (
	noteMarker    = `([A-Z][A-Z]+)\(([^)]+)\):?`                    // MARKER(uid), MARKER at least 2 chars, uid at least 1 char
	noteMarkerRx  = regexp.MustCompile(`^[ \t]*` + noteMarker)      // MARKER(uid) at text start
	noteCommentRx = regexp.MustCompile(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start
)

// readNote collects a single note from a sequence of comments.
func (r *reader) readNote(list []*ast.Comment) {
	text := (&ast.CommentGroup{List: list}).Text()
	if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {
		body := clean(text[m[1]:])
		if body != "" {
			marker := text[m[2]:m[3]]
			r.notes[marker] = append(r.notes[marker], &Note{
				Pos:  list[0].Pos(),
				UID:  text[m[4]:m[5]],
				Body: body,
			})
		}
	}
}

// readNotes extracts notes from comments.
// A note must start at the beginning of a comment with "MARKER(uid):"
// and is followed by the note body (e.g., "// BUG(gri): fix this").
// The note ends at the end of the comment group or at the start of
// another note in the same comment group, whichever comes first.
func (r *reader) readNotes(comments []*ast.CommentGroup) {
	for _, group := range comments {
		i := -1 // comment index of most recent note start, valid if >= 0
		list := group.List
		for j, c := range list {
			if noteCommentRx.MatchString(c.Text) {
				if i >= 0 {
					r.readNote(list[i:j])
				}
				i = j
			}
		}
		if i >= 0 {
			r.readNote(list[i:])
		}
	}
}

reader.goにおけるreadNotesメソッドの導入が、ノート認識の柔軟性を高める鍵です。このメソッドは、コメントグループ内の各コメントを走査し、noteCommentRx正規表現(コメントの先頭にMARKER(uid):パターンがあるか)にマッチするかどうかをチェックします。マッチした場合、そのコメントから次のノートの開始、またはコメントグループの終わりまでの範囲を一つのノートとして抽出し、readNoteメソッドに渡します。

noteMarker正規表現の:の後に?が付いている点(:?)は、コロンがオプションになったことを示しています。これにより、TODO(uid)のようにコロンがない形式でもノートとして認識されるようになりました。

lib/godoc/package.html

{{with $.Notes}}
	{{range $marker, $content := .}}
		<h2 id="pkg-note-{{$marker}}">{{noteTitle $marker | html}}s</h2>
		<ul>
		{{range .}}
		<li>{{html .Body}}</li>
		{{end}}
		</ul>
	{{end}}
{{end}}

package.htmlの変更は、godocのHTML出力におけるノートの表示方法を改善しています。{{with $.Notes}}PageInfo.Notesが存在するかを確認し、存在する場合には各マーカー(例: TODO, BUG)ごとに見出しを生成します。そして、<ul><li>タグを使用して、各ノートのBodyをリスト形式で表示します。これにより、ノートがより構造化され、視覚的に分かりやすくなりました。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/5268119f26728ddd2ee9f8eebcbfcec83ac5bd69
  • Gerrit Change-Id: https://golang.org/cl/7496048 (GoプロジェクトのコードレビューシステムであるGerritのリンク)
  • 関連するGo Issue: #4843 (コミットメッセージに記載されているが、直接的な公開Issueページは見つからなかった)

参考にした情報源リンク