共计 6054 个字符,预计需要花费 16 分钟才能阅读完成。
刚入门 Go 开发时,在开源我的项目的主页上咱们常常能够看到这样的一个徽章:
点击徽章,就能够关上 https://pkg.go.dev/
的网页,网页中给出了这个开源我的项目所对应的 Go 文档。在刚接触 Go 的时候,我一度认为,pkg.go.dev
下面的文档是须要开发者上传并审核的——要不然那些文档咋都显得那么业余呢。
然而当我写本人的轮子时,缓缓的我就发现并非如此。划重点:在 pkg.go.dev
上的文档,都是 Go 主动从开源我的项目的工程代码中爬取、格式化后展示进去的。换句话说,每个人都能够写本人的 GoDoc
并且展现在 pkg.go.dev
上,只须要听从 GoDoc 的格局规范即可,也不须要任何审核动作。
本文章的目标是通过例子,简要阐明 GoDoc 的格局,让读者也能够本人写一段高大上的 godoc
。以下内容以我本人的 jsonvalue 包为例子。其对应的 GoDoc 在这里。读者能够点开,并与代码中的内容做参考比照。
什么是 GoDoc
顾名思义,GoDoc 就是 Go 语言的文档。在理论利用中,godoc
可能能够指以下含意:
- 在 2019.11 月之前,示意
https://godoc.org
中的内容 - 当初
godoc.org
曾经下线,会重定向到pkg.go.dev
,并且其性能也都从新迁徙到这下面——下文以“pkg.go.dev
”指代这个含意 - Go 开发工具的一个命令,就叫做
godoc
——下文间接以“godoc
”指代这个工具 pkg.go.dev
的相干命令,被叫做pkgsite
,代码托管在 GitHub 上——下文以“pkgsite
”指代这个工具- Go 工具包的文档以及生成该文档所相干的格局——下文以“
GoDoc
”指代这个含意
目前的 godoc 和 pkgsite 有两个作用,一个是用来本地调试本人的 GoDoc 显示成果;另一个是在无奈迷信上网的时候,用来本地搭建 GoDoc 服务器之用。
godoc 命令
咱们从工具命令开始讲起吧。在 2019 年之前,Go 应用的是 godoc
这个工具来格式化和展现 Go 代码中自带的文档。当初这个命令曾经不再蕴含于 Go 工具链中,而须要额定装置:
go get -v golang.org/x/tools/cmd/godoc
godoc 命令有多种模式和参数,这里咱们列出最罕用和最简便的模式:
cd XXXX; godoc -http=:6060
其中 XXXX 是蕴含 go.mod 的一个仓库目录。假如 XXX 是我的 jsonvalue 库的本地目录,依据 go.mod,这个库的地址是 github.com/Andrew-M-C/go.jsonvalue
,那么我就能够在浏览器中关上 http://${IP}:${PORT}/pkg/github.com/Andrew-M-C/go.jsonvalue/
,就能够拜访我的 jsonvalue 库的 GoDoc 页面了,如下图所示:
pkgsite 命令
正如前文所说,当初 Go 官网保护和应用的是 pkg.go.dev
,因而本文次要阐明 pkgsite
的用法。
以后的 pkgsite
要求 Go 1.18 版,因而请把 Go 版降级到 1.18。而后咱们须要装置 pkgsite
:
go install golang.org/x/pkgsite/cmd/pkgsite@latest
而后和 godoc 相似:
cd XXXX; pkgsite -http=:6060
一样用 jsonvalue
举例。浏览器的地址与 godoc 相似,然而少了 pkg/
: http://${IP}:${PORT}/github.com/Andrew-M-C/go.jsonvalue/
,页面如下图所示:
pkg.go.dev 内容
总体内容
因为笔者在 jsonvalue 中对 GoDoc 玩得比拟多,因而还是以这个库为例子。咱们关上 pkg.go.dev
中相干包的主页,能够看到这些内容:
- A – 以后 package 的残缺门路
- B – 以后 package 的名称,其中的
module
示意这是一个合乎 go module 的包 - C – 以后 package 的一些根底信息,包含最新版本、公布工夫、证书、依赖的包数量(包含零碎包)、被援用的包数量
- D – 如果以后 package 蕴含 README 文件,则展现 README 文件的内容
- E – 以后 package 内的 comment as document 文档内容
- F – 以后 package 的文件列表,能够点击疾速浏览
- G – 以后 package 的子目录列表
如果你的 README (markdown 格局) 有子标题,那么 pkgsite
会生成 README 下的二级目录索引。Markdown 的格局在本文就不予阐明,置信码农们都耳熟能详了。
Documentation
让咱们点开 Documentation,一个残缺的 package,可能蕴含以下这些内容:
大节 | 阐明 |
---|---|
Overview | 这是整个 package 的概览阐明,取的是 go 代码中的“包正文”局部 |
Index | 这是整个 GoDoc 内容的总目录,蕴含了所有可导出的函数、办法、常量、变量和示例代码 |
Variables | 这里列出了所有可导出变量。实际上一个封装得比拟好的 package,这里点进去之后应该是空的 |
Functions | 所有的可导出函数(返回可导出类型的函数除外) |
Types | 所有的可导出类型及其办法,以及可能生成对应类型的可导出函数列表(比方各种构造函数) |
其实 Documentation 的内容,就是 GoDoc。Go 秉承“正文即文档”的理念,其中 pkg.go.dev
、godoc
和 pkgsite
都应用同一套 GoDoc 格局,三者都依照该格局从文档的正文中提取,并生成文档。
上面咱们具体来说明一下 GoDoc 的语法。
GoDoc 语法
在 GoDoc 中,以后 package 的所有可导出类型,都会在 pkg.go.dev
页面中展现进去,即使某个可导出类型没有任何的正文,GoDoc 也会将这个可导出内容的原型展现进去——当然了,咱们应该时时刻刻记住:所有的可导出内容,都应该写好正文。
GoDoc 反对 //
和 /* ... */
两种模式的正文符。然而笔者还是举荐应用 //
,这也是目前的正文符支流,而且大部分 IDE 也都反对一键将多行文本间接转为正文(比方 Mac 的 VsCode,应用 command
+ /
)。尽管 /* */
在多行正文中十分不便,但一旦看到这个,总感觉如同是上古时代的代码 (狗头)。
绑定 GoDoc 与指定类型
对于任意一个可导出内容,紧跟着代码定义上方一行的正文,都会被视为该内容的 GoDoc,从而被提取进去。比如说:
// 这一行,会被视为 SomeTypeA 的 GoDoc,// 因为它紧挨着 SomeTypeA 的定义。type SomeTypeA struct{}
// 这一行与 SomeTypeB 的定义之间隔了一行,// 所以并不会认为是 SomeTypeB 的 GoDoc。type SomeTypeB struct{}
/*
应用这种正文符的正文也是同理,因为整个正文块紧挨着 SomeTypeC 的定义,因而会被视为 SomeTypeC 的正文。*/
type SomeTypeC struct{}
这三个类型在 pkgsite
页面上的展现成果是这样的:
然而,请读者留神,依照 Go 官网的举荐,代码正文的第一个单词,应该是被正文的内容自身。比方前文中,SomeTypeA
的正文应该是 // SomeTypeA
结尾。下文开始将会对立应用这一标准。
换行(段落)
读者能够留神到,前文中的所有无效正文,我都换了一行;然而在 pkgsite
的页面展现中,并没有产生换行。
实际上,在正文中如果只是单纯的一个换行另写正文的话,在页面是不会将其当作 新的一段 来对待的,GoDoc 的逻辑,也仅仅渲染完这一行之后,再加一个空格,而后持续渲染下一行。
如果要在同一个正文块中新加一个段落,那么咱们须要插入一行空正文,如下:
// SomeNewLine 只是用来展现如何在 GoDoc 中换行。//
// 你看,这就是新的一行了,耶~✌️
func SomeNewLine() error {return nil}
内嵌代码
如果有需要的话,咱们能够在正文中内嵌一小段代码,代码会被独立为一个段落,并且应用等宽字符展现。比方上面的一个例子:
// IntsElem 用于不 panic 地从一个 int 切片中读取元素,并且返回值和理论在切片中的地位。//
// 不论是任何状况,如果切片长为 0,则 actual Index 返回 -1.
//
// 依据参数 index 能够有几种状况://
// - 零值,则间接取切片的第一个值
//
// - 正值,则从切片 0 地位开始,如果遇到切片完结了,那么就循环从头开始数
//
// - 负值,则示意逆序,此时则循环从切片的最初一个值开始数
//
// 负值的例子:
//
// sli := []int{0, -1, -2, -3}
// val, idx := IntsElem(sli, -2)
//
// 返回得 val = -2, idx = 2
func IntsElem(ints []int, index int) (value, actualIndex int) {// ......}
总结一下:在正文块中,如果局部正文行合乎以下规范之一,则视为代码块:
- 正文行以制表符
\t
结尾 - 正文行以以多于一个空格(包含制表符)结尾
一般正文和代码块之间能够不必专门的空正文行,但集体倡议还是加上比拟好。
Overview 局部
在 Documentation 中的 Overview 局部,是整个 package 的阐明,这种类型的正文,被称为“包正文”。包正文是写在 go 文件最开始的 package xxx
下面。尽管 GoDoc 没有限度、然而 Go 官网倡议包正文该当以 // Package xxx
结尾作为文本的主语。
如果在一个 package 中,有多个文件都蕴含了包正文,那么 GoDoc 会依照文件的字典序,顺次展现这些文件中的包正文。但这样可能会带来凌乱,因而一个 package 咱们该当 只在一个文件 中写包正文。
一般而言,咱们能够抉择以下的文件写包正文:
- 很多 package 上面会有一个与 package 名称同名的 xxx.go 文件,那咱们能够对立就在这个文件里写包正文,比方这样;
- 如果 xxx.go 文件自身承载了较多代码,或者是包正文比拟长,那么咱们能够专门开一个
doc.go
文件,用来写包正文,比方这样。
弃用代码申明
Go 所应用的版本号是 vX.Y.Z
的模式,依照官网的思维,每当 package 降级时,尽量不要降级大版本 X 值,这也同时代表着,本次降级是齐全向前兼容的。然而实际上,咱们在做一些小版本或中版本升级时,有些函数 / 类型可能不再举荐应用。此时,GoDoc 提供了一个关键字 Deprecated:
,作为整个正文块的第一个单词,比方咱们能够这么写:
// Deprecated: ElemAt 这个函数弃用,后续请迁徙到 IntsElem 函数中.
func ElemAt(ints []int, index int) int {// ......}
针对 deprecated 的内容,pkgsite
一方面会在目录中标识进去:
此外,在注释中,也会刻意用灰色字体低调展现,并且暗藏正文注释,须要点开能力显示:
代码示例文档
读者如果看我 jsonvalue 的文档,在 At()
函数下,除了上文提到的文档注释之外,还有五个代码示例:
那么,文档中的代码示例又应该如何写呢?
首先,咱们应该新建至多一个文件,专门用来寄存示例代码。比方我就把示例代码写在了 example_jsonvalue_test.go
文件中。这个文件的 package
名 不得 与以后包名雷同,而应该命名为 包名_test
的格局。
此外,须要留神的是,示例代码文件也属于单元测试文件的内容,当执行 go test
的时候,示例文件也会纳入测试逻辑中。
示例代码的申明
如何申明一个示例代码,这里我举两个例子。首先是在 At()
函数下名为“Example (1)”的示例。在代码中,我把这个函数命名为:
func ExampleSet_At_1() {......}
这个函数命名有几个局部:
函数名组成部分 | 阐明 |
---|---|
Example |
这是示例代码的固有结尾 |
Set |
示意这是类型 Set 的示例 |
第一个下划线 _ |
分隔符,在这个分隔符前面的,是 Set 类型的成员函数名 |
At |
示意这是函数 At() 的示例,搭配后面的内容,则示意这是类型 Set 的成员函数 At() 的示例 |
第二个下划线 _ |
分隔符,在这个分隔符前面的内容,是示例代码的额定阐明 |
1 |
这是示例代码的额定阐明,也就是后面“Example (1)”括号里的局部 |
另外,示例代码中应该蕴含规范输入内容,这样便于读者理解执行状况。规范输入内容在函数内的最初,采纳 // Output:
独自起一行结尾,剩下的每一行规范输入写一行正文。
绝对应地,如果你想要给(不属于任何一个类型的)函数写示例的话,则去掉上文中对于“类型”的字段;如果你不须要示例的额定说明符,则去掉“额定阐明”字段。比如说,我给类型 Opt
写的示例就只有一个,在代码中,只有一行:
func ExampleOpt() {........}
甚至连示例阐明都没有。
如果一个元素蕴含多个例子,那么 godoc
会依照字母序对示例及其相应的阐明排序。这也就是为什么我罗唆在 At()
函数中,示例标为一二三四五的起因,因为这是我心愿读者浏览示例的程序。
在官网上公布 GoDoc
好了,当你写好了本人的 GoDoc 之后,总不是本人看本人自娱自乐吧,总归是要公布进去给大家看的。
其实公布也很简略:当你将蕴含了 godox 的代码 push 之后(比方公布到 github 上),就能够在浏览器中输出 https://pkg.go.dev/${package 路径名}
。比方 jsonvalue
的 Github 门路(也等同于 import 门路)为 github.com/Andrew-M-C/go.jsonvalue
,因而输出 https://pkg.go.dev/github.com/Andrew-M-C/go.jsonvalue
。
如果这是该页面第一次进入,那么 pkg.go.dev
会首先获取、解析和更新代码仓库中的文档内容,并且格式化之后展现。在 pkg.go.dev
中,如果可能找到 package 的最新的 tag 版本,那么会列出 tag(而不是骨干分支)上的 GoDoc。
接下来更重要的是,把这份官网 GoDoc
的链接,附到你本人的 README 中。咱们能够进入 pkg.go.dev
的徽章生成页
输出仓库地址就能够看到相应的徽标的链接了。有 html
和 markdown
格局任君抉择。
参考资料
- 万字长文解读 pkg.go.dev 的设计和实现
- pkg.go.dev 源码
本文章采纳 常识共享署名 - 非商业性应用 - 雷同形式共享 4.0 国内许可协定 进行许可。
本文最早公布于 云 + 社区,也是自己的博客。
原作者:amc,欢送转载,但请听从上述协定注明出处。
原文题目:作为 Gopher,你晓得 Go 的正文即文档应该怎么写吗?
公布日期:2022/03/24
原文链接:https://segmentfault.com/a/1190000041604192。
另:本文局部内容与笔者以前公布过的《如何写高大上的 godoc》一文相似,但过后成文与还没有 pkg.go.dev
的时代,很多内容曾经掉队。因而我从新写了这篇。