Blog

Blog

PHODAL

API 库的文档体系支持:主流编程语言的文档设计

去年,我们在那篇《编程语言的 IDE 支持》详细讨论了在不同 IDE、编辑器里,它们是如何提供对于编程语言的支持。在这一篇文章里,我们将不那么详细地讨论一下:不同的编程语言如何提供文档支持?如此一来,也能在未来为 Datum Lang 提供相关的理论体系支持。这里所指的编程语言的文档体系,主要是指语言标准库中的文档

新一代编程语言的文档体系

作为一个程序员,和诸多人一样,我并不热衷于在代码中写文档/注释。只是呢,诸多的情况下,我们依旧不得不写文档,如编程语言的标准库、API、SDK 等。在各个语言中,它们有各自不同的实践,有各自不同的特点。

起先,我只分析过 Rust 语言中的 rustdoc,分析它是如何自动化处理相关文档的。后来,联想到了其它语言的文档形式是不是也会类似。接着,便尝试性的整理自 2012 年后“比较”(相对,至少是我听得多的)主流的语言的文档方式。

于是,便花了点时间,从相关的代码库中快速梳理而来。因时间有限,加之不擅长某些语言。所以,结论上的可能并不是这么完整,欢迎各位读者指正。

语言 文档编写方式 文档代码一致性 源码
Rust 定制 markdown
1.对语法部分处理(默认 Rust 语言)
文档测试 执行不报错 断言使用 assert https://doc.rust-lang.org/rustdoc/
Julia 定制 markdown 借助
1. index.md 来编排
2. 自定义语法:note
3. 自定义类型:@contents
文档测试
1. 自定义标签:jldoctest
2. 使用 #output 进行断言(可选)
3. 内建 setup 方法
https://github.com/JuliaDocs/DocumenterTools.jl
Dart 定制 markdown
1. 使用 [xx] 来链接元素
不支持文档测试 https://github.com/dart-lang/dartdoc
Kotlin 定制 markdown
1. 使用 [xx] 来链接元素 2. 自定义标签(如 @param)
没有 https://github.com/Kotlin/dokka/
Swift 定制 markdown
1. 自定义 callout 类型的系列标注(如 copyright、date、important 等)
过去支持文档测试 https://github.com/SwiftDocOrg/swift-doc
https://github.com/SwiftDocOrg/SwiftMarkup

从文档编写方式上来看,Rust、Julia、Dart、Kotlin、Swift 的文档工具都是相似的:基于 markdown 语法的基础上,进行了一系列的定制。如 Swift 文档中添加的一些特别的标准,如关于复杂度(Complexity)的特别说明,便使用了类似的方式来实现。而像 Julia 语言里的 DocumenterTools 则做了大量的定制,则可以基于 index.md 对内容进行特别处理,并添加大量的自定义语法。

从文档代码一致性上来看,从我初步阅读代码的情况来看,Rust、Julia 的文档工具都会校验文档中的代码是不是正常的,并能对其进行测试。如 Rustdoc 会编译、运行文档中的代码,可以通过 Rust 自身提供的断言语法(如 assert_eq!)进行测试。而像 Julia 里的 DocumenterTools 则做了更多的定制,如可以为文档添加 setup 方法,还能进行一系列相关的断言等。

除此,在 R 语言中提供的 R Markdown 也比较有意思,它提供了一种可执行文档的新思路,即让 markdown 与代码有机的结合在一起,结合 pandoc 构建了新的文档可能性。后续,可能会在文档体验设计相关的文章中,进行详细的介绍。

Rust 的文档测试示例

说了那么多,让我们先简单看个示例。如下是一段 Rust 中的注释代码:

 # Examples
  ```rust
assert_eq!(2 + 2, 4);
  ```

Rust 在遍历了语法树之后,会:

  1. 解析 markdown,寻找 Rust 语言的语法块(如果没有标注语言类型,默认是 Rust)
  2. 根据语法块,做一些简单的处理,生成可编译的代码
  3. 编译上述的测试代码 (如果编译失败,则说明测试失败)
  4. 运行这些测试 or 文档

如下是 Rustdoc 中将上述的代码生成测试代码的测试用例:

#![allow(unused)]

fn main() {
   #[allow(non_snake_case)]
   fn _doctest_main__some_unique_name() {
       assert_eq!(2 + 2, 4);
   }
   _doctest_main__some_unique_name()
}

一旦上述的代码编译并运行通过,则说明文档中的注释是正确的。

详细见 Rustdoc 相关源码:librustdoc

构建文档体系:markdown 即 DSL

基于上述语言的文档体系,我们就能浮现出基本的原型。核心便是:基于 markdown 而扩展,将 markdown 视为可扩展的 DSL。如此以来,便可以按需添加功能,并根据不同需要进行可视化展示。

0. 基于 markdown

这一点倒是没有啥说的,markdown 在今天已经成为了事实上的开发文档标准。

1. 为扩展设计:文档 DSL

从做法上可以分为:

让语法块可运行。让 markdown 中的代码块包含更多的含义,根据不同的代码块来对语言进行特殊处理。如 Julia 的 Documenter.jl 便定义了一系列的如可执行的 eval 、REPL 环境输出的 repl、文档测试 jldoctest 等。除此,一旦我们使用特定的符号来标记时,我们就可以在代码中添加更多的可能性。

添加自定义符号。添加一些自定义的语法,以支持更多的高级功能。如 KotlinDoc/KDoc 中使用的 Dokka,使用 [] 来链接元素,这种方式和 markdown 比较贴进。

文档编排。通过自定义元素,实现在 markdown 对于内容的“编程”(管理、引用等)。如 Julia 的 Documenter.jl 中创建的 @ref@docs@meta@content 等语法。

而除了上述的内容,我们还可以做更多的事,诸如于对于“代码的引用”等。

2. 为准确性设计:文档测试

为了确保文档与代码保持一致,又或者是文档中代码的准确性,我们需要引入文档测试的方式来检查 API。以 Rust 和 Julia 这两个语言来分析的话,主要可以分为多种情况:

  • 代码编译通过。
  • 代码运行不报错。可以在其中支持语言本身的断言。
  • 与输出一致。文档需要输出程序的输出,它可以通过运行对应的测试,并保持两者是一致的。

主要的实现步骤可以参见上述的 Rust 语言实现。

3. 构建开放协作平台:开放协作

作为一个代码库的文档体系,它应该是开源的,能让所有的人为之做贡献,才会吸引到更多的开发者。

其它

人家苦短,让我们用 markdown 编程。

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签