8

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

 2 years ago
source link: https://www.phodal.com/blog/api-ducumentation-design-dsl-base/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Posted by: Phodal Huang Sept. 25, 2021, 3:05 p.m.

去年,我们在那篇《编程语言的 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 编程。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK