[インデックス 12140] ファイルの概要
このコミットは、Go言語のドキュメンテーション生成ツール go/doc
における重要な改善を導入しています。具体的には、パッケージや関数のドキュメンテーションコメントから「最初の文(first sentence)」を抽出するロジックを、src/cmd/go/pkg.go
から src/pkg/go/doc
パッケージへと移動し、より汎用的な Synopsis
関数として再利用可能にしています。さらに、この Synopsis
関数は、一般的な略語(例: "T.S.Eliot" の "T.S.")を正しく処理し、文の区切りをより正確に判断するよう改良されています。
コミット
commit 0c2f3b7ffdae1c796f077c08d0cf4b5e7830ee4a
Author: Robert Griesemer <gri@golang.org>
Date: Wed Feb 22 10:49:37 2012 -0800
go/doc: move firstSentence into go/doc
- renamed firstSentence -> Synopsis
- also deal with common abbreviations
R=rsc
CC=golang-dev
https://golang.org/cl/5676088
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0c2f3b7ffdae1c796f077c08d0cf4b5e7830ee4a
元コミット内容
go/doc: move firstSentence into go/doc
- renamed firstSentence -> Synopsis
- also deal with common abbreviations
変更の背景
Go言語のドキュメンテーションツール(godoc
コマンドなど)は、Goのソースコードに記述されたコメントから自動的にドキュメントを生成します。この際、パッケージや関数、型などの概要を簡潔に表示するために、ドキュメンテーションコメントの「最初の文」を抽出する機能が不可欠です。
以前は、この「最初の文」を抽出するロジックが src/cmd/go/pkg.go
の firstSentence
関数として実装されていました。しかし、このロジックは godoc
だけでなく、他のドキュメンテーション関連ツールやライブラリでも必要とされる汎用的な機能です。特定のコマンドの内部に閉じ込めておくのではなく、より適切な場所、すなわち go/doc
パッケージに移動し、再利用可能な形で提供することが望ましいと判断されました。
また、従来の firstSentence
関数は、文の区切りを単純に「ピリオドの後にスペースが続く場合」と定義していました。このルールは、"T.S.Eliot" のような一般的な略語(イニシャルなど)の後にピリオドが続く場合に、誤って文の終わりと判断してしまうという問題がありました。これにより、生成される概要が不正確になる可能性がありました。このコミットは、この問題を解決し、より賢明な文の区切り判断を導入することを目的としています。
前提知識の解説
- Go言語のドキュメンテーションシステム (
go/doc
): Go言語には、ソースコードのコメントから自動的にドキュメントを生成する強力なシステムが組み込まれています。godoc
コマンドはその代表的なツールで、Goの標準ライブラリやサードパーティライブラリのドキュメントをブラウザで閲覧できるようにします。このシステムは、コメントの構造(例: パッケージコメント、関数コメント)を解析し、構造化されたドキュメントを生成します。 - パッケージコメント: Goのソースファイルの一番最初に記述されるパッケージ宣言の直前のコメントは、そのパッケージ全体のドキュメンテーションとして扱われます。通常、パッケージの目的や機能の概要が記述されます。
go/build
パッケージ: Goのビルドシステムに関する情報を提供するパッケージです。ソースファイルの解析やパッケージの依存関係の解決などに利用されます。このコミットでは、build.Package
構造体のPackageComment
フィールドからドキュメントテキストを取得しています。go/token
パッケージ: Goのソースコードを解析する際に使用されるトークン(キーワード、識別子、演算子など)やファイル位置に関する情報を提供するパッケージです。unicode
パッケージ: Unicode文字のプロパティ(大文字/小文字、数字、記号など)を扱うための機能を提供するパッケージです。このコミットでは、文の区切りを判断する際にunicode.IsUpper
を使用して、文字が大文字であるかどうかを判定しています。Synopsis
(概要): ドキュメンテーションコメントの最初の文を指します。これは、パッケージや関数の目的を簡潔に伝えるために使用されます。
技術的詳細
このコミットの主要な技術的変更点は、以下の2つです。
-
firstSentence
関数のgo/doc
パッケージへの移動とリネーム:- 以前
src/cmd/go/pkg.go
に存在したfirstSentence
関数は、src/pkg/go/doc/synopsis.go
という新しいファイルに移動され、Synopsis
という名前に変更されました。 - これにより、ドキュメンテーションの概要抽出ロジックが、Goのドキュメンテーションシステムの中核を担う
go/doc
パッケージに集約され、他のツールやライブラリからの再利用が容易になりました。 src/cmd/go/pkg.go
とsrc/cmd/godoc/dirtrees.go
の両方で、firstSentence
の呼び出しがdoc.Synopsis
の呼び出しに置き換えられています。
- 以前
-
Synopsis
関数の文区切りロジックの改善:- 新しい
Synopsis
関数は、内部でfirstSentenceLen
というヘルパー関数を使用しています。 firstSentenceLen
は、文の終わりを判断する際に、単に「ピリオドの後にスペースが続く」だけでなく、「ピリオドの後にスペースが続き、かつそのピリオドの直前がちょうど1つの大文字ではない」という条件を追加しています。- この新しいロジックは、
unicode.IsUpper
関数を使用して、ピリオドの前の文字が大文字であるかどうかをチェックします。 - 具体的には、
ppp
,pp
,p
という3つのrune
変数を使って、現在の文字 (q
) とその直前の2つの文字 (p
,pp
)、さらにその前の文字 (ppp
) を追跡します。 q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp))
という条件が文の終わりを判定します。q == ' '
: 現在の文字がスペースである。p == '.'
: 直前の文字がピリオドである。!unicode.IsUpper(pp) || unicode.IsUpper(ppp)
: これが新しいロジックの核心です。!unicode.IsUpper(pp)
: ピリオドの直前の文字 (pp
) が大文字ではない場合(例: "foo." の "o")。これは通常の文の終わりです。unicode.IsUpper(ppp)
: ピリオドの2つ前の文字 (ppp
) が大文字である場合(例: "T.S.Eliot" の "T")。これは略語の可能性があり、文の終わりではないと判断されます。
- この改良により、"T.S.Eliot" のような略語が文の途中で誤って区切られることを防ぎ、より正確な概要抽出が可能になります。
- また、
Synopsis
関数は、抽出された文から改行文字 (\n
,\r
,\t
) を削除し、複数のスペースを単一のスペースに正規化する処理も行います。これにより、整形されたクリーンな概要文字列が生成されます。
- 新しい
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードスニペットは以下の通りです。
-
src/cmd/go/pkg.go
:firstSentence
関数が削除されました。import "go/doc"
が追加されました。p.Doc = firstSentence(info.PackageComment.Text())
の行がp.Doc = doc.Synopsis(info.PackageComment.Text())
に変更されました。
-
src/cmd/godoc/dirtrees.go
:import "go/doc"
が追加されました。synopses[i] = firstSentence(file.Doc.Text())
の行がsynopses[i] = doc.Synopsis(file.Doc.Text())
に変更されました。
-
src/pkg/go/doc/synopsis.go
(新規ファイル):firstSentenceLen
関数が追加されました。この関数は、文の長さを計算する主要なロジックを含みます。func firstSentenceLen(s string) int { var ppp, pp, p rune for i, q := range s { if q == '\n' || q == '\r' || q == '\t' { q = ' ' } if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) { return i } ppp, pp, p = pp, p, q } return len(s) }
Synopsis
関数が追加されました。この関数はfirstSentenceLen
を呼び出し、結果の文字列を整形します。func Synopsis(s string) string { n := firstSentenceLen(s) var b []byte p := byte(' ') for i := 0; i < n; i++ { q := s[i] if q == '\n' || q == '\r' || q == '\t' { q = ' ' } if q != ' ' || p != ' ' { b = append(b, q) p = q } } // remove trailing blank, if any if n := len(b); n > 0 && p == ' ' { b = b[0 : n-1] } return string(b) }
-
src/pkg/go/doc/synopsis_test.go
(新規ファイル):Synopsis
関数とfirstSentenceLen
関数の動作を検証するための単体テストが追加されました。特に、略語の処理やスペースの正規化に関するテストケースが含まれています。
コアとなるコードの解説
src/pkg/go/doc/synopsis.go
に追加された firstSentenceLen
関数と Synopsis
関数がこのコミットの核心です。
firstSentenceLen(s string) int
この関数は、入力文字列 s
の中で最初の文がどこで終わるかを示すインデックスを返します。
- 文字の追跡:
ppp
,pp
,p
という3つのrune
変数を使って、現在の文字q
とその直前の2つの文字、さらにその前の文字を保持します。これにより、ピリオドの前後関係を詳細にチェックできます。 - 改行・タブのスペース化:
q == '\n' || q == '\r' || q == '\t'
の条件で、改行やタブ文字をスペースに変換します。これは、文の区切りを判断する際にこれらの文字がノイズにならないようにするためです。 - 文の区切り判定:
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp))
が文の終わりを判定する条件です。q == ' '
: 現在の文字がスペースであること。p == '.'
: 直前の文字がピリオドであること。(!unicode.IsUpper(pp) || unicode.IsUpper(ppp))
: この部分が略語の処理を改善しています。!unicode.IsUpper(pp)
: ピリオドの直前の文字 (pp
) が大文字でない場合。これは通常の文の終わり(例: "Hello world.")。unicode.IsUpper(ppp)
: ピリオドの2つ前の文字 (ppp
) が大文字である場合。これは略語(例: "T.S.Eliot" の "T.S.")の可能性があり、このピリオドは文の終わりではないと判断されます。この条件がtrue
の場合、文はまだ続いていると見なされます。
- ループと返り値: 文字列を1文字ずつ走査し、上記の条件が満たされた時点でそのインデックスを返します。最後まで文の区切りが見つからない場合は、文字列全体の長さを返します。
Synopsis(s string) string
この関数は、firstSentenceLen
を利用して最初の文を抽出し、さらに整形処理を施します。
- 文の長さの取得:
n := firstSentenceLen(s)
で、最初の文の長さを取得します。 - バイトスライスへの構築:
b
というバイトスライスに整形された文を構築します。 - 文字の整形:
q == '\n' || q == '\r' || q == '\t'
の条件で、改行やタブ文字をスペースに変換します。q != ' ' || p != ' '
の条件で、連続するスペースを単一のスペースに正規化します。これにより、"foo bar"
のような文字列が"foo bar"
になります。
- 末尾のスペースの削除:
if n := len(b); n > 0 && p == ' '
の条件で、抽出された文の末尾に余分なスペースがある場合にそれを削除します。 - 文字列への変換: 最終的に整形されたバイトスライス
b
を文字列に変換して返します。
これらの変更により、Goのドキュメンテーションシステムは、より正確で読みやすいパッケージや関数の概要を生成できるようになりました。
関連リンク
- Go言語の公式ドキュメンテーション: https://go.dev/doc/
go/doc
パッケージのドキュメンテーション: https://pkg.go.dev/go/docgodoc
コマンドについて: https://go.dev/blog/godoc
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/go/doc/synopsis.go
と関連ファイル) - Go言語のコミット履歴 (GitHub)
- Unicodeの文字プロパティに関する一般的な知識
- 正規表現における文の区切りに関する一般的な知識 (略語の処理など)
- Go言語の
unicode
パッケージのドキュメンテーション: https://pkg.go.dev/unicode