Go
godoc

チョットできるGoプログラマーになるための詳解GoDoc

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
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"のように正規表現で指定します。書き方は後で紹介します。

次のスクリーンショットは、各マークアップと、出力結果の対比表です。

Screen Shot 2018-08-28 at 0.50.47.png

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

ドキュメントとしては次のように見えます。

Screen Shot 2018-08-28 at 1.21.09.png

ビルド方法

他の言語のドキュメントツールをやったことがある人にとって、戸惑うポイントがどのようにビルドして、ウェブサイトの情報を更新するか、でしょう。結論から言ってしまえば、ビルドコマンドといったローカルのツールなどは存在していません。

godoc.orgの場合

  1. github.comなどにアップした後に、そのパッケージの名前のURLにアクセスすると裏で自動で生成されます
  2. 一度ビルドされると、自動では更新されません。
  3. 最下部に「refresh now」というリンクがあり、このリンクを辿ると再取得とビルドが行われます。

ローカル環境の場合

  1. godocコマンドを起動すると、ドキュメントが作成されます。

Local Godoc サーバーの追加機能

godocサーバー(-http)には便利な追加機能がいくつかあります。業務で開発したローカルなプライベートパッケージのドキュメントをホストしておくことがあると思いますが、その場合にはこのあたりを知っておくと良いでしょう。

  • 静的解析
  • インデックスの利用(-index
  • オリジナルのテンプレートの利用(-template
  • Exampleテストのプレイグランドの有効化(-play)・・・ただし使えない

godocサーバーによる静的解析

詳細は次のところに書かれています。

2つのオプションが提供されています。

  • godoc -analysis=type
    • コンパイラのエラー
    • 識別子の解決
    • 型情報: サイズ、アラインメント、メソッドの集合、インタフェース
  • godoc -analysis=pointer
    • コールグラフのナビゲーション
    • パッケージ内部のコールグラフ
    • チャネルのペア(送信と受信)の解析

例えば、次のコードで、ticker.Cの受信の演算子を選択すると、このチャネルを受信している箇所が表示されます。

Screen Shot 2018-08-28 at 1.43.10.png

インデックスの利用

ローカルのgodocサーバーで、検索機能が使えるようになります。ただし、インデックスの作成には多くの時間がかかります。事前にインデックスを作成し、ファイルへの書き出しをしておくと時間が短縮できます。

Screen Shot 2018-08-28 at 1.45.13.png

オリジナルのテンプレートの利用(-template

オリジナルのテンプレート(HTML/JS/CSS)を使って飾り付けることができます。

ゼロから作るのは大変ですが、次の場所をコピーして作成すると楽になるでしょう。

  • $GOPATH/src/golang.org/x/tools/godoc/static

変わったことがわかるように、背景色を大胆にいじってみました。

Screen Shot 2018-08-28 at 1.46.42.png

Exampleテストのプレイグランドの有効化

godoc.orgのような、自由に試せるプレイグランドを提供しますが、どうも、godoc.orgで動いている仕組みをそのままリモートで使っているようです。そのため、ローカルの$GOPATH内にあるパッケージを利用したくても使えません。つまりは標準ライブラリしか使えないので使い道は特にないでしょう。

1.10のリリースパーティで質問されましたが、プレイグランドのサービスのURLを選択するオプションはありません。

Screen Shot 2018-08-28 at 1.45.00.png

まとめと参考文献

長々と説明してきましたが、開発者が心がけるべきことはあまりないです。