この記事はQiitaで公開されていました
Goでドキュメントを書くとき、一般的にはGoDocを使うと思います。GoDocはシンプルにみえて、実際は色々な書き方をサポートしていますし、ブラウザで単純に表示する以外の読み方もあるので、一通りの方法をまとめてみました。
ドキュメントの書き方
GoDocではソースコードの中に、ある決まった形でコメントを書くと、そのコメントをドキュメントとして扱うことができます。具体的には、パッケージ、型、定数、構造体フィールド、関数・メソッドそれぞれの直前に、空行を入れずコメントを書きます。これらの前に改行を入れてしまうと、ただのコメントになってしまいます。
装飾について
GoDocは、大きなドキュメントのために、ヘッダと整形されたテキストの2通り装飾ができます。ただし、リストやテーブルなどは対応していません。
ヘッダ
以下の条件を全て満たせば、ヘッダとして認識されます。
- 大文字で開始される(日本語文字は適用外)
- 1行だけで構成されている
- 句読点を含まない
- 直前の要素がヘッダではない
- 最初の要素ではない
最後の2つは、
/* Paragraph 1 Paragraph 2 Paragraph 3 */ package qiita
というコメントブロックがあった場合、Paragraph 1はコメントブロック中の最初にある要素なので、ヘッダにはなりません。また、Paragraph 3は直前の要素がヘッダなので、通常の段落になります。
整形済みテキスト
他の要素よりもインデントを増やせば整形されたテキストになります。
リンク
URLを書けばそのままリンクに変換されます。任意のテキストにリンクを付けることはできません。
具体例
以下で、それぞれのコメントがどのように表示されるかを紹介します。
パッケージドキュメント
パッケージに対するコメントは、ドキュメントの一番上にOverviewとして表示されます。ここには、パッケージのざっくりとした説明を書いたり、パッケージ全体を通して使われる仕様などの情報を記載するために記述します。以下はパッケージコメントの例です。
// Copyright xxx // +build darwin /* Godoc はGoのパッケージドキュメント情報をQiitaで紹介するためのパッケージです。そのままサンプルとして使えます。 リポジトリは https://github.com/lufia/godoc-sample です。 How to read a document ドキュメントを読むためにはgo docコマンドまたはgodocコマンドが使えます。 How to write a document パッケージドキュメントはpackage句の直前に書く必要がありますが、 Build constraintsはpackageよりも前に書かなければなりません。 そのため、記述する順番としては、Build constraintsが先になります。 空行を入れると、別の段落として区切ることができます。 インデントすると、ソースコードのような 整形されたテキストも書けます Heading アルファベットの大文字で始まり、句読点を含まない1行だけの段落があれば、 それはヘッダとして装飾されます。ただし、ヘッダを2つ以上続けることはできません。 次の行はヘッダになりますが、その次は同じルールにも関わらず普通の段落です。 This is a header This is not a header */ package qiita
このコメントをGoDocで読むと、以下のように表示されます。
パッケージ一覧での表示
パッケージドキュメントの最初に現れる文は、パッケージ一覧ページでも使われます。パッケージドキュメントが1文以上書かれていても、最初の文だけがパッケージ一覧ページに概要として使われ、残りは無視されます。例えば日本語の場合、読点(。)で文が終わります。英語の場合はピリオドで終わりますが、この場合は後ろにスペースがなければ文の終わりとしては認識しません。
型とフィールド
型宣言の直前にコメントを書くと、それは型に対するドキュメントになります。もし型が構造体だったなら、フィールドのコメントも一緒にドキュメントとして表示されます。フィールドの上に書いてもいいし、行末に書いても構いません。例えば以下のコードは、
// Article は1つの記事を表します。 type Article struct { // 記事のタイトル Title string // 記事本文 Body string // 状態 Status PostStatus // Draft or Publish }
次のように表示されます。
変数・定数
変数や定数も同じで、直前のコメントがドキュメントになります。
// PostStatus は記事の投稿状態を表現します。 type PostStatus int // 記事の投稿状態。 const ( StatusDraft PostStatus = iota // 下書き StatusPublish // 公開済み )
このコードは、次のように表示されます。
const
の直前と定数それぞれにドキュメントが付きます。これはvar
も同様です。
メソッド・関数
メソッドや関数へのコメントもドキュメントになります。
// NewArticle はタイトルをtitleに設定した新しい記事を作成します。 func NewArticle(title string) *Article { return &Article{Title: title} } // Save は、記事aの状態をデータベースに保存します。 func (a *Article) Save() error { // BUG(lufia): 保存機能は未実装です。 // TODO(lufia): 実装する return nil }
これをドキュメントにすると、以下のように表示されます。
GoDocは、型に関連するメソッドや関数を、なるべく近くに表示するように並び替えます。
コード例を書く
GoDocでは、パッケージを使ったコードサンプルをExampleとして掲載できます。コード例はコメントではなく、テストコードとしてExampleで始まる名前のメソッドを実装すると、名前に対応した場所にドキュメントとして表示されるものです。対応する場所とは以下の通りです。
関数名 | 場所 |
---|---|
Example() |
パッケージ全体 |
ExampleFunc() |
Func関数 |
ExampleType_Func() |
Type型のFuncメソッド |
また、それぞれのExample関数名の終わりに、最初が小文字で始まる名前を付けると、別のパターンとして複数のコード例を書くことができます。一通り包括した例を挙げます。
package qiita_test import ( "fmt" "log" "github.com/lufia/godoc-sample" ) func Example() { a := qiita.NewArticle("テスト") fmt.Println(a.Title) // Output: テスト } func Example_other() { a := qiita.NewArticle("テスト") a.Body = "サンプル" fmt.Println(a.Body) // Output: サンプル } func ExampleNewArticle() { a := qiita.NewArticle("テスト") fmt.Println(a.Status) // Output: 0 } func ExampleNewArticle_otherStatus() { a := qiita.NewArticle("テスト") a.Status = qiita.StatusPublish fmt.Println(a.Status) // Output: 1 } func ExampleArticle_Save() { a := qiita.NewArticle("テスト") a.Save() } func ExampleArticle_Save_errorHandling() { a := qiita.NewArticle("エラー") if err := a.Save(); err != nil { log.Fatalln(err) } }
上記のコードで、パッケージ名をqiita_test
としてパッケージ本体と分けているのは、そうした方が、コード例にパッケージ名を記述することができて親切だからです。Goのパッケージは通常、異なるパッケージを同じディレクトリに入れることはできませんが、_test
の場合は外部テストパッケージ(external test といって特別らしいです。
この例を表示させると、以下のようになります。
NewArticle
関数の例は次のように表示されます。他も同じです。
また、コード例のなかには、1つの関数で表現できない大きな例も必要になるかもしれません。その場合、1つの*_test.goファイルにExample関数を1つだけ書いて、さらに、Exampleの外に定義したなにかを1つ以上参照しましょう。パッケージグローバルに他の宣言が行われていると、GoDocはファイル全体をひとつの大きなコード例として扱います。以下はその例です。
package qiita_test import ( "fmt" "github.com/lufia/godoc-sample" ) // グローバルに1つ以上、Example以外の何かが必要 const defaultTitle = "untitled" // 1つだけExampleの実装がされていること func Example_wholeFileExample() { a := qiita.NewArticle(defaultTitle) fmt.Println(a.Title) // Output: untitled }
通常のExampleは、関数の中しか例として表示しませんが、このExampleはファイル全体を表示します。
ノート
コードの途中で、// BUG(who): xxx
のようにコメントを書くと、それもドキュメントの末尾に表示してくれます。例えば、上記のSave()
メソッドを次のように変更してみましょう。
// Save は、記事aの状態をデータベースに保存します。 func (a *Article) Save() error { // BUG(lufia): 保存機能は未実装です。 // TODO(lufia): 実装する。 return nil }
これを、-notes=(正規表現)
オプションを指定したGoDocで表示すると、次のように脚注として表示できます。
GoDocのデフォルトで表示されるノートはBUG(who)
だけですが、godoc
のオプションでgodoc -notes='.*'
のようにラベルを正規表現で指定すると、BUG(who)
以外のラベルも一緒に表示します。
他にも、公式ドキュメントを眺めると、
// Deprecated: xx // See: https://xxx
などが使われていますが、これらはノートではなく普通のテキストとしてドキュメントに表示されます。
参考情報
ドキュメントを読む方法
godoc.org
オンラインのGoDocは、URLのパス部分にimport pathを与えると、パッケージのドキュメントを表示してくれるサービスです。例えばこの記事で書いたサンプルリポジトリの場合、以下のURLで参照できます。
このサービスはGOOS=linux
で動作しているようで、他のOSをターゲットとしているファイルは対象となりません。クエリパラメータとしてGOOSとGOARCHがサポートされているので、例えばPlan 9/386のドキュメントを表示したければ以下のURLにアクセスすると読めます。
godoc
コマンド
godoc
コマンドは標準パッケージに含まれませんので、go get
でインストールして使います。
$ go get golang.org/x/tools/cmd/godoc
このコマンドは、一般的にはgodoc -http=:6060
のようにHTTPサーバとして動作させて、ブラウザで参照することが多いと思われます。ブラウザで閲覧すると、Goの公式サイトと似たようなページが表示されますが、公式と異なり、godocのパッケージ一覧ページには、ローカルの$GOPATH以下にある全パッケージがリストされています。
Go 1.11から、型や関数の右側に対応したバージョンが表記されるようになりました。このデータは $GOROOT/api/go*.txt から読み込んでいるようです。
静的解析
公式やオンラインのgodoc.orgと比べて特に便利だと思うのは、-analysis=
オプションの存在です。-analysis=
オプションは以下の2つの値をとります。
- type
- pointer
-analysis=type
とすると、型がどのインターフェイスを実装しているかを調べることができます。以下の画像はbufio.Reader
の例です。io.Reader
などのインターフェイスを実装していることが確認できます。
また、ソースコードを表示した場合には、普段より詳細な解析が行われています。こちらもbufio.Reader
の例です。カーソルをリンクに乗せると、定義がその場で表示されますし、定義された場所までのリンクも追加されています。
また、-analysis=pointer
とすると、上記に加えて静的コールグラフ等の情報が追加されます。以下はbufio.Reader.ReadByte()
が呼び出す関数のグラフです。
ソースコードを表示して、func
の部分に追加されたリンクを選択すると、その関数を呼び出しているソースコードがリストされます。以下の例はbufio.Reader.ReadByte()
を呼び出している場所です。
コマンドラインモード
コマンドラインモードはGo 1.11が最終バージョンです。以後サポートされなくなります。
godoc
は、ブラウザで使う以外にも、コマンドラインでドキュメントを読むためにも使うことができます。コマンドラインモードの使い方は
$ godoc [options] full/path/to/pkg [name ...]
のように引数としてパッケージと名前(省略可能)を受け取ります。godoc
は、name
を省略すると、パッケージに含まれる全てのドキュメントをコンソールに出力します。name
を与えると、その部分だけ抽出します。注意すべき点として、パッケージのimport pathは必ず完全なimport pathで渡す必要があります。
コマンドラインモードのgodoc
は、-src
オプションを与えると、対象のソースコードを標準出力に書き出します。他にも-q
オプションを使うと、検索することも可能です。
$ godoc -src sync WaitGroup # WaitGroupのソースを表示 $ godoc -q 'Buffer' # BufferをGoDocから検索
参考情報
go doc
コマンド
go
コマンドのサブコマンドです。godoc
のコマンドラインモードと利用する場面は似ています。godoc
よりも機能はシンプルで、コマンドラインでのドキュメント出力しかできませんが、引数の渡し方が少し便利になっています。例えばgodoc
はパッケージをフルパスで渡す必要がありましたが、go doc
は
$ go doc appengine.Context
のように、パッケージが一意に特定できるならimport pathの一部だけを渡せば検索してくれます。また、特定メソッドだけを調べる場合、以下のように.
でつなげて引数で渡すと、メソッドのドキュメントだけを絞り込んで表示します。
$ go doc oauth2.Config.AuthCodeURL
参考情報
その他
この記事ではパッケージのドキュメント例を紹介しましたが、他にも、present
コマンドを使うと、Go関連のスライドでよく使われる形式のスライドが作れたり、記事が作成できたりします。