Goを書いている方なら誰しもお世話になっているのがGoDoc。標準ライブラリのリファレンスだけではなく、godoc.orgで、Go製のサードパーティ製パッケージのドキュメントもブラウザでいつでも閲覧できます。
書くことも、読むことも多いGoDocですが、細かい仕様がまとまって説明されていることがあまりないため、Go 1.10リリースパーティにあわせていろいろ細かく調べてみて、その後少し改訂しました。現時点で、世界で一番詳しい説明だと思います。
さて、GoDocはいくつあるでしょう?
GoDocといっても、ツールがいくつかあります。
ツール名 | UI |
---|---|
go doc | CLI |
godoc | CLI |
godoc -http | Web |
gddo (godoc dot org)サーバー | Web |
上の2つがCLIで、下の2つがブラウザです。歴史的な経緯を見てみましょう。
- 〜1.1: go docはバンドルされているツールで、ソースもgo本体に同梱
- 1.2: go docは別のリポジトリにわけられてgodocになり、go本体から外れた
- 1.3: godocで-analysisオプションが追加
- 1.5: 新しい"go doc"コマンドがgo本体に同梱
- 1.11: godocがウェブだけになるため、go docを使えというアナウンスが出るように
- 1.12(予定): godocが-httpだけをサポートしてCLIの機能は削除予定
わかりましたか?よくわかりませんよね?まあ、godocとgo docは同じドキュメントツールですが、別のツールです。本家と元祖みたいなものとして、長らく平行で提供されてきました。ちょうどこの記事を書いている少し前にリリースされた1.11で、godocの方がウェブだけになるとリリースノートに書かれました。
また、godoc.orgのサーバーはローカルのgodocコマンドよりも機能が増えていたり、テンプレートが微妙に違ったりします。とはいえ、ドキュメントを書くときはこれらの違いはそこまで気にすることはありません。
基礎編:GoDocについて知るべきこと
実際に書く方法を学ぶにはだいたい次のことを把握していれば良いでしょう。順番に説明していきます。
- 基本ルール
- 出力結果
- マークアップ
- Exampleテスト
- ビルド・チェック方法
基本ルール
Effective Goのコメントの章を見ると、いろいろ書かれています。ダイジェストを紹介します。
Comments that appear before top-level declarations, with no
intervening newlines, are extracted along with the declaration
to serve as explanatory text for the item.
トップレベルの宣言の直前に空行なしで書かれたコメントは、宣言文とともに抽出されて
その要素の説明用テキストとして提供されます。
Every package should have a package comment, a block comment
preceding the package clause.
すべてのパッケージは、package節の前のブロックコメントであるパッケージ
コメントを持つべきです。
Inside a package, any comment immediately preceding a top-level
declaration serves as a doc comment for that declaration. Every
exported (capitalized) name in a program should have a doc comment.
パッケージ内では、トップレベルの宣言の直前にあるコメントがその宣言の
ドキュメンテーションコメントとして提供されます。プログラム内のすべての
エクスポート(大文字)名はコメントを持つべきです。
If every doc comment begins with the name of the item it describes,
the output of godoc can usefully be run through grep.
すべてのドキュメンテーションのコメントがそれが記述する項目の名前で始まる場合、
godocの出力とgrepをを組み合わせて便利に使用できます。
この最後の項目はgolintでチェックをするとよくgodoc関連のエラーに遭遇しますよね?なぜコメントの書き出しのルール(コメント対象の名前で書き始める)なんてあるのかというと、この最後の段落に答えがありました。他のツールとの組み合わせを考えた上でのルールということですね。比較的ウザいやつですが。元々はshouldではなくて、推奨するぐらいの軽いものだったようです。
大雑把にはこんな感じです。
// Action is an interface of actions in scenario.
type Action interface {
Type() ActionType
}
空行はあけるのか?あけないのか?
空行をはさんではいけない、というのがハマりどころで、初心者がよく混乱するところだと思います。結果をみてみて、出力されていないときは、まずこの空行を見直してみてください。
なお、build constraintsは、空行が必要です。
cgoはimport "C"
の直前に書きますが、こちらはgodoc同様、空行をあけるとダメです。
Goの三大ハマりポイントだと思っています。
出力結果の項目
godocの生成された結果を見ると、たくさん項目があるように見えますが、プログラマーが書くべきところは「宣言」「サンプル」だけです。あとは自動生成されるコンテンツです。また、最初のリポジトリへのリンクと、最後のおまけのフッターはgodoc.orgだけのコンテンツです。
要素 | godoc.orgのみ | 自動生成 | 自分で書く |
---|---|---|---|
リポジトリへのリンク *
|
✔︎ | ||
タイトル (パッケージ名) | ✔︎ | ||
import文 | ✔︎ | ||
パッケージ説明 | ✔︎ | ||
パッケージサンプル | ✔︎ | ||
パッケージ内容、サンプル、ソースファイル一覧へのインデックス | ✔︎ | ||
for _, member := range members | |||
メンバータイトル | ✔︎ | ||
メンバー宣言 | ✔︎ | ||
メンバー説明 | ✔︎ | ||
メンバーサンプル | ✔︎ | ||
おまけのフッター (依存しているパッケージ、リフレッシュリンク、GitHubのバッジ) | ✔︎ |
マークアップ
それでは書き方を見ていきます。みなさん、何かしらのマークアップは知っていると思うので著名なものと比較してみます。
reStructuredText | Markdown | godoc | |
---|---|---|---|
セクションタイトル | 最大28階層 | 6階層 | 1階層 |
段落 | ✔ | ✔ | ✔ |
インラインスタイル | 太字・斜体・固定幅 | 太字・斜体・固定幅 | |
URL自動リンク | ✔ | ✔ | ✔ |
画像 | ✔ | ✔ | |
リスト | ✔ | ✔ | |
テーブル | 4種類の記法 | GFM | |
フォーマット済みテキスト | ✔ | ✔ | ✔ |
コードハイライト | ✔ | GFM | |
ラベル付きリンク | ✔ | ✔ | |
水平線 | ✔ | ✔ | |
脚注 | ✔ | ||
宣言リスト | ✔ | ||
フィールドリスト | ✔ | ||
オプションリスト | ✔ | ||
置換 | ✔ | ||
コメントアウト | ✔ | ||
リンクターゲットへのリンク | ✔ | ||
置換 | ✔ | ||
Todo | Sphinx (todo) | ✔ (notes) | |
サイドバー | ✔ | ||
トピック | ✔ | ||
巻頭句 | ✔ | ||
数式 | ✔ | ||
目次 | ✔ | ||
HTMLメタタグ | ✔ | ||
イメージマップ | ✔ | ||
Raw HTML | ✔ | GFM | |
HTMLクラス | ✔ | ||
外部ファイルのinclude | ✔ | ||
docテスト | Sphinx (python) | ✔ (example test) | |
RFCへのリンク | Sphinx | godoc.org | |
packageへのリンク | Sphinx (domain) | godoc.org |
reSTにしかない記法はいっぱいあるのが目立ちますが、それと比べると、godocのシンプルさが際立ちます。表もリストもありません。自然言語で書ききるか、フォーマット済みテキスト(ハイライトなし)はあるので、それを駆使してリストを表現したり、アスキーアートで表を表現しているプロジェクトもあります。
使える要素にしぼると、使える要素はごくわずかです。
-
段落
空行で区切りられた行のかたまりのテキストが段落になります。
-
セクションタイトル
MarkdownやreSTのようなセクションタイトルを表す記号はなく、Title Case (先頭がすべて大文字)の1行の段落がセクションタイトルになります。階層はありません。
-
フォーマット済みテキスト
空白でインデントされたところはフォーマット済みのテキストとなります。
-
URL
URLはHTMLのリンクになります。
-
ノート
出力結果の最後の方に、BUG、TODO、Deprecatedのような追加のメモのセクションができて、ノートがついた要素へのリンク集となります。
デフォルトではBUGのみがノートの項目として設定されていますが、ノートの種類を自分で設定するには、godocコマンドで、
-notes="BUG|TODO"
のように正規表現で指定します。書き方は後で紹介します。
次のスクリーンショットは、各マークアップと、出力結果の対比表です。
godoc.orgのみに追加されたマークアップ
ローカルでは効果がないものの、godoc.orgにアップロードしたときだけ有効になるマークアップが2つあります。後者は意識しなくても、英語で書いているとうっかり適用されることも多いかと思います。
- RFCへのリンク:
RFC 822
のようなテキストがあると、tools.ietf.org以下のページへのリンクが貼られます。RFC 822 section 1.2.3
のように書くと、セクション番号も付与できます。 - パッケージへのリンク:
package time
のように書くと、timeパッケージへのリンクが作成されます。
Exampleテスト
Goプログラマーが作成できる要素は,コメントとExampleの2つだけです。もう片方の大事な片割れも紹介します。Exampleテストは、ユニットテストの1形態です。Pythonをやったことがあるのであれば、doctestと言えば伝わりやすいでしょう。Exampleテストを書くと、godoc.orgでサンプルコードとして表示されます。また、go testでテストとしても実行が可能です。
まずはサンプルを提示しておきます。
package mockconn_test
import (
"fmt"
"github.com/shibukawa/mockconn"
)
func Example() {
mock := mockconn.New(nil)
mock.SetExpectedActions(
mockconn.Read([]byte("welcome from server")),
mockconn.Write([]byte("hello!!")),
mockconn.Close(),
)
buffer := make([]byte, 100)
n, _ := mock.Read(buffer)
fmt.Println(string(buffer[:n]))
// Output:
// welcome from server
mock.Write([]byte("hello!!"))
mock.Close()
}
Exampleテストのキーポイントは次の通りです
- テストファイルであること(
XX_test.go
) - 通常のパッケージと異なる、テストパッケージ(
package [パッケージ名]_test
)であること - テストターゲットと対応する名前の関数名を持つ
- パッケージのサンプルであれば
Example()
。 - トップレベル要素(型、インタフェース、関数)であれば、
Example[要素名]()
。 - 構造体のメソッドであれば、
Example[構造体名]_[メソッド名]()
。
- パッケージのサンプルであれば
- テストコードはprint文とその結果で表す
-
fmt
パッケージを使って出力する - コメントには期待される出力を書く
-
- パッケージ内にヘルパー関数を作って、それを利用しても良い
- godocは必要なヘルパー関数を集めてきて、出力するHTMLドキュメントの中に一緒に出力してくれる。
テストとして実行した結果は次のようになります。
=== RUN TestWriteError
--- PASS: TestWriteError (0.00s)
=== RUN Example
--- PASS: Example (0.00s)
=== RUN ExampleConn_Verify
--- FAIL: ExampleConn_Verify (0.00s)
got:
Error: socket scenario 1 - Write() expected="hello!!" actual="good morning!!".
Error: mock socket scenario 1 - there is remained data to write: "hello!!"
Error: Unconsumed senario exists - 2/2
want:
Error: socket scenario 1 - Write() expected="hello!!" actual="good morning!!"
Error: mock socket scenario 1 - there is remained data to write: "hello!!"
Error: Unconsumed senario exists - 2/2
FAIL
exit status 1
FAIL github.com/shibukawa/mockconn 0.002s
ドキュメントとしては次のように見えます。
ビルド方法
他の言語のドキュメントツールをやったことがある人にとって、戸惑うポイントがどのようにビルドして、ウェブサイトの情報を更新するか、でしょう。結論から言ってしまえば、ビルドコマンドといったローカルのツールなどは存在していません。
godoc.orgの場合
- github.comなどにアップした後に、そのパッケージの名前のURLにアクセスすると裏で自動で生成されます
- 一度ビルドされると、自動では更新されません。
- 最下部に「refresh now」というリンクがあり、このリンクを辿ると再取得とビルドが行われます。
ローカル環境の場合
- godocコマンドを起動すると、ドキュメントが作成されます。
Local Godoc サーバーの追加機能
godocサーバー(-http
)には便利な追加機能がいくつかあります。業務で開発したローカルなプライベートパッケージのドキュメントをホストしておくことがあると思いますが、その場合にはこのあたりを知っておくと良いでしょう。
- 静的解析
- インデックスの利用(
-index
) - オリジナルのテンプレートの利用(
-template
) - Exampleテストのプレイグランドの有効化(
-play
)・・・ただし使えない
godocサーバーによる静的解析
詳細は次のところに書かれています。
2つのオプションが提供されています。
-
godoc -analysis=type
- コンパイラのエラー
- 識別子の解決
- 型情報: サイズ、アラインメント、メソッドの集合、インタフェース
-
godoc -analysis=pointer
- コールグラフのナビゲーション
- パッケージ内部のコールグラフ
- チャネルのペア(送信と受信)の解析
例えば、次のコードで、ticker.C
の受信の演算子を選択すると、このチャネルを受信している箇所が表示されます。
インデックスの利用
ローカルのgodocサーバーで、検索機能が使えるようになります。ただし、インデックスの作成には多くの時間がかかります。事前にインデックスを作成し、ファイルへの書き出しをしておくと時間が短縮できます。
オリジナルのテンプレートの利用(-template
)
オリジナルのテンプレート(HTML/JS/CSS)を使って飾り付けることができます。
ゼロから作るのは大変ですが、次の場所をコピーして作成すると楽になるでしょう。
-
$GOPATH/src/golang.org/x/tools/godoc/static
変わったことがわかるように、背景色を大胆にいじってみました。
Exampleテストのプレイグランドの有効化
godoc.orgのような、自由に試せるプレイグランドを提供しますが、どうも、godoc.orgで動いている仕組みをそのままリモートで使っているようです。そのため、ローカルの$GOPATH
内にあるパッケージを利用したくても使えません。つまりは標準ライブラリしか使えないので使い道は特にないでしょう。
1.10のリリースパーティで質問されましたが、プレイグランドのサービスのURLを選択するオプションはありません。
まとめと参考文献
長々と説明してきましたが、開発者が心がけるべきことはあまりないです。
- パッケージ、およびパブリックなトップレベルの要素(構造体、インタフェース、関数)に対して書く
- 一番大事なのは空行をあげずにブロックコメントを付与する
マークアップもごく限られていて、複雑なことはあまりできない
-
The Go Blog “Godoc: documenting Go code”
-
Effective Go: commentary
-
Markup specification of standard go doc
-
Markup specification only for godoc.org
-
Example test
-
Static analysis features of godoc