Writerside logo

On Writer's Side

Intelligent tools for writers and new ways to write better

Solutions
News

利用 Kotlin DSL 的强大功能编写文档

Read this post in other languages:

本文最初发表于在阿姆斯特丹举办的 API The Docs 会议上,这项活动重点关注开发者成功以及 API 文档和开发者门户对企业的帮助。 我们向想要提升技术写作相关能力的读者强烈推荐这场会议。 所有演讲都很实用,信息丰富,并包括现场演示。

有时,几行代码就可以帮助您完成文档任务。 但是,文档不是代码,在编写文档时添加外部功能或自动化可能会更困难。 如果能像编写代码一样编写文档,完成特定任务,然后将代码转换回文档就好了!

在这篇博文中,我们将探讨如何使用 Kotlin DSL 编写文档来做到这一点。

DSL? 什么是 DSL?

我们来定义“领域专用语言”,即 DSL。

简单来说,它是一种专用于解决特定问题的编程语言。 这些语言的概念和规则特定于具体领域。

DSL 也称为小语言或面向问题的语言。 这些定义更好地描述了其本质 – DSL 专为解决特定问题而设计。

DSL 有两种类型。 外部 DSL 是独立语言,具有自己的语法、词法分析器和解析器。 SQL 就是这种语言的一个例子。 内部 DSL 则重用通用语言的语法和解析器,并利用该语言的可用扩展选项。

适用于文档的 Kotlin DSL 属于内部 DSL 类别。

使用 Kotlin,您可以构建具有外部 DSL 外观的内部 DSL。 因此,我们开发的是专为文档设计的内部 DSL。

在 JetBrains MPS 网站 上详细了解外部 DSL 与内部 DSL 之间的差异。

Kotlin DSL 的本质

适用于文档的 Kotlin DSL 是专为创建文档和静态网站而设计的框架。

框架是在基本文档元素上运行的类型安全构建器,额外功能作为用户代码或外部依赖项插入。 从特定源或输出格式到自然语言处理,依赖项可以提供任何类型的功能。

借助 Kotlin DSL,技术文档撰写人可以定义语义元素,例如章节、过程、表、列表等。 虽然 DSL 最初关注我们自己的标记,但其灵活性允许自定义扩展,因此理论上,我们可以将任何输入格式转换为任何输出格式。

在元素层面,用户可以将复杂逻辑封装在自定义元素中,然后像段落或列表一样简单使用。

Kotlin DSL 的一大显著优势是 IntelliJ IDEA 等集成式开发环境 (IDE) 已提供内置支持,包括错误检查、代码补全和重构。 这非常有助于提高书面内容的质量。

重复项检测运作方式示例

由于源是常规 Kotlin 代码,所有代码智能和语言支持功能均开箱即用。 需要浏览代码? 检测死代码和重复代码? 或者将另一种语言注入字符串文字? 这些 IDE 功能全都适用于 Kotlin DSL。 语言本身提供了显著好处,例如完整的静态类型和实用工具生态系统。

这篇博文展示了 DSL 如何生成自定义 XML 标记,但请注意,它也可以根据您的具体需求生成多种输出格式,例如 HTML 或 .docx。

此外,DSL 不仅可以在 IntelliJ IDEA 中使用,还可以在其他编辑器中甚至通过命令行使用。 它提供了与编写内容类似的体验,同时还利用了 Kotlin 编程语言的无限可能。

以下是我们标记的一些示例。

在第一个示例中,我们创建一个段落和一个包含两个列表条目的列表,并在这些元素内附加一些内容。

val content = StardustDslContentProvider {
   p { +"Hello, DSL"}
   list {
       li { +"List item 1" }
       li { +"List item 2" }
   }
   this
}

运行脚本后,结果如下:

在第二个示例中,我们创建一个包含两列的表。

val content = StardustDslContentProvider {
   table {
       tr {
           td {
               +"Parameter name"
           }
           td {
               +"Parameter type"
           }
       }
       tr {
           td {
               +"Value 1"
           }
           td {
               +"Value 2"
           }
       }
       this
   }

变为:

在第三个示例中,我们演示如何注入任何其他语言,例如 XML 或 Markdown。 在下面的示例中,我们注入一个包含三个步骤的过程:

xml("""
    <procedure title="Title">
        <step>Step 1</step>
        <step>Step 2</step>
        <step>Step 3</step>
    </procedure>
""")

结果如下:

包含 3 个步骤的过程

当然,仅用标记编写很难说是突破性成就。 那么,我们就来看一些更创新、更有趣的内容。

演示

首先,请注意,我们想展示的是方式,而不是具体实现。 以下演示使用我们的自定义 XML 标记执行,但我们可以桥接任何其他格式,例如 AsciiDoc、Markdown、reStructuredText、LaTeX 到 HTML,甚至是 .docx。

我们方式的三大关键优势

关注点分离

我们谈论了很多将内容与其表示分离的话题。 这种方式从 CSS 早期就存在于 Web 开发领域,后在轻量级标记语言中得到重新解释和强化。 这项技术涉及将文档的视觉和设计方面与其内容和结构分离。 这样一来,文档撰写人可以专注于内容,同时轻松调整其表示。

但我们可以更进一步,将数据与其表示分离。 文档可以基于数据本身生成,而不是基于与所述事物没有直接关系的标记。 我们可以从应用程序运行时获取数据,然后定义其表示。

此外,我们可以根据数据设计使表示具有条件。
例如,如果方法描述表明其已被弃用,则将其转换为警告元素。 在其他情况下,将其变为段落。

示例如下:

val descriptionTag = if (method.description.contains("DEPRECATED")) "warning" else "p"
xml(
   """
    <p>Method type: <b><font color="$font">${method.type.uppercase()}</font></b></p>
    <deflist>
        <def title="${method.summary}">
            <$descriptionTag>${method.description}</$descriptionTag>
        </def>
    </deflist>
   """
)

全新水平的文档即代码

我们经常谈论将文档视为代码,但我们通常指的是有限数量的工具和做法,例如使用轻量级标记编写、将文档置于版本控制之下,以及使用代码审查和 linter。 但是,我们可以从开发领域吸取更多。

借助 Kotlin DSL,文档成为代码的代名词,让文档撰写人可以利用开发者常用的做法。 部分做法包括:

  • 将内容的某些部分作为模块,然后将其作为许多文档集的依赖项导入,例如一些前端组件或者版权或许可协议等法律信息。
  • 在编程中使用函数组合,可以将复杂的标记结构和模式分解为更简单的结构和模式,使源更加精细并且易于维护和重用。
  • 将语义和前端组件视为类型,在需要的地方释放面向对象编程的优势。

可扩展性

DSL 的可扩展性将打破现有规则。 它不仅允许您添加自定义元素,还支持修改 DSL 本身,这开辟了无限可能。

您的想象力不会受到限制。 在计算机上可以实现的一切,都可以通过文档实现!

得益于与现有 JVM 库的无缝集成,您可以合并数据源、解析和丰富内容,甚至与 Selenium 和 Cucumber 等外部工具和框架集成。

Kotlin DSL 可扩展性选项示例如下:

  • 解析和集成任何数据格式,包括 XML、JSON、YAML、自定义序列化格式,甚至应用程序运行时的对象。
  • 添加映射并使用额外内容丰富文档。
  • 使用 Readability Analysis Tool 和 Apache OpenNLP 等库处理自然语言。
  • 为自动生成的部件应用测试和断言。

为什么选择 Kotlin?

先前已经有过从代码生成 HTML 的尝试,所以您可能想知道为什么我们选择 Kotlin 用于文档目的?

首先,Kotlin 具有一些高级功能,有助于创建强劲 DSL。

其次,Kotlin 以实用和对初学者友好而闻名。 它遵循简化日常任务的原则,确保新手能够快速轻松地掌握语言。

例如,在 Kotlin 中编写基本的“Hello, World!” 程序时,初学者不会被迫使用不理解的概念和关键字。

以下示例比较了 Java 与 Kotlin 中的“Hello, World!”程序:

Java:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Kotlin:

fun main() = println("Hello, World!")

在 Kotlin 中,您不需要定义类或使用 public static void 签名。 main() 函数作为入口点,println() 可以直接用于输出所需消息。 这种简单性是 Kotlin 的主要优势之一,使编写基本程序更为轻松愉快。

如前所述,Kotlin 与 JVM 生态系统无缝集成。 Java 已诞生接近 30 周年,具有大量实用工具和库。 在构建脚本中添加一行,即可将这些工具合并到工作流中。

选择 Kotlin,我们优先考虑在简单性和强劲功能之间取得平衡的语言,确保愉快高效的开发者体验。

我需要成为程序员才能使用 Kotlin DSL 吗?

我们经常听到文档撰写人说:“我只是一个普通的技术文档撰写人,不是开发者也不是特别精通技术! 我该怎么用它? 我需要什么编程水平才能从中受益?”

要开始使用 DSL 元素作为标记,您不需要任何编码技能,也不需要了解 DSL 的运作方式。

只有在某些自动化相关任务上,您才需要对 Kotlin 有基本的了解。 JetBrains Academy 的 Kotlin Core 课程提供了所有必备知识。

但是,我们相信,学习 Kotlin DSL 从长远来看很有价值。 凭借其直观的语法和 IDE 支持,文档撰写人可以快速精通并更充分地利用强劲的文档工具。

将这种方式合并到文档工作流时,您可能需要学习一些编程知识。 将 Kotlin DSL 集成到工作中,您可以动手学习专门适用于文档职责的编程。 毕竟,技术文档撰写人可能会更喜欢文档相关任务,而不是假设课程的分数计算或基本计算器构建等综合练习。 此外,学习 Kotlin 还有助于理解其他语言和生态系统。

这种方式不一定适合所有技术文档撰写人,但对于撰写开发者工具内容的人员来说,这会非常有助于提升技能。

结论

适用于文档的 Kotlin DSL 引入了范式转变,使技术文档撰写人能够利用编程语言的强大功能轻松创作精彩内容。 利用 Kotlin 的优势、IDE 支持以及 DSL 固有的可扩展性,文档不再只是页面上的文字。 它成为开发流程中动态、集成的部分,增强协作并提供卓越结果。

快来为您的文档需求探索 Kotlin DSL 的无限可能吧! 

? 我们已经创建了一个示例项目,供您下载试用 Kotlin DSL。 如果您有任何疑问,请加入我们的 Slack,我们的专属频道是 #kotlin-dsl-for-docs。

拥抱代码的力量,将您的文档提升到全新高度。

祝您写作和编码快乐!

本博文英文原作者: