How does Kotlin’s documentation engine Dokka work and can it be extended?

How does Kotlin’s documentation engine Dokka work and can it be extended?

Kotlin code is documeted with KDoc. It looks similar to Javadoc, but uses Markdown instead of HTML.

What is Dokka?

Dokka is a documentation engine for Kotlin, performing the same function as the Javadoc tool for Java. Just like Kotlin itself, Dokka fully supports mixed-language Java/Kotlin projects. It understands standard Javadoc comments in Java files and KDoc comments in Kotlin files, and can generate documentation in multiple formats including minimalistic HTML, Javadoc HTML, and Markdown.

Dokka abstracts from Kotlin and Java by converting input in both languages to a graph of documentation nodes. The same is true for the documentation in KDoc and Javadoc where the result is a tree of content nodes. A documentation node has other documentation nodes attached that a grouped, e.g. owner (reference to parent), members, details, or annotations. In addition, a documentation node has content attached that itself is the root of a tree. If the code that generates a specific output format, for example, receives a documentation node representing a class, the code does not have to know whether it was created out of a Kotlin or a Java class. However, to fully support both languages, the super set of the features of both languages has to be supported, i.e. all node types have to be handled.

The abstraction for the documentation itself can, for example, be shown with a list. In Javadoc an unordered list is represented with HTML and thus with

  • Item 1

. In KDoc Markdown is used and thus an unordered list is just * Item . Both inputs get converted to the same content node structure where the top node is an unordered list that has a list of list items as children. However, there might be small differences resulting tree: In the case of KDoc the first content node nested under a list item is a paragraph, but in the case of Javadoc the content is not wrapped with an additional paragraph. So one might get a paragraph node with a single text node as a child or just a text node under a list item with pure text.

Transformation of an unordered list from KDoc — An additional ContentParagraph appears
Transformation of an unordered list from Javadoc — No ContentParagrah is added

Dokka can be used in several ways. There are plugins for Maven and Gradle, an Ant task, and a command line runner. The Dokka Readme contains instructions on how to set it up and also lists all the configuration options.

Why do I want to have a custom output format?

I’m one of the maintainers of Spring Auto REST Docs — an extension to Spring REST Docs that uses introspection and Javadoc that helps to produce beautiful API documentation with even less work.

For Javadoc we created a custom Doclet that converts Javadoc into JSON files that can be consumed by Spring Auto REST Docs. To support Kotlin and thus KDoc we needed a tool that converts KDoc to the custom JSON files. This is how my journey into Dokka started.

Custom output formats are of course a niche and are mostly of interest for tool creators. Other examples of tools making use of Javadoc are UMLDocletTeXDoclet, and Swagger Doclet.

Extending Dokka to add a custom format

When I first looked at Dokka, it was not clear to me whether one can create an extension for it or whether one has to go the ugly way and copy the project to able to add a new output format. Back then I found no resources on this topic and thus started digging into Dokka.

Luckily, it turns out that one can add a custom output with library that depends on Dokka and does not copy any code from there. The main interfaces for a custom format are FormatDescriptor, FormatService and FormattedOutputBuilder. Dependency injection works via property files and one can make the custom format visible by creating property file in the right folder, e.g. dokka/format/.properties

class=somepackage.CustomFormatDescriptor
description=Awesome new output format

In the FormatDescriptor one can take over most of the setting from KotlinFormatDescriptorBase, but in the end this is the place to customize everything. For example, to control the folder structure and files names, one has to create a custom Generator and set it as the generatorServiceClass in the custom FormatDescriptor.

Using a Dokka extension with Maven

Using a custom Dokka extension with Maven is straight forward. One sets up the Dokka Maven plugin and then adds the extension has a dependency. The following extract of a Maven POM shows how this looks like for spring-auto-restdocs-dokka-json:

  org.jetbrains.dokka
  dokka-maven-plugin
  ${dokka.version}
  
    
      compile
      
        dokka
      
    
  
  
    
      capital.scalable
      spring-auto-restdocs-dokka-json
      ${spring-auto-restdocs-dokka-json.version}
    
  
  
    auto-restdocs-json
    ${jsonDirectory}

Full version of the pom.xml file.

Using a Dokka extension with Gradle

Using a Dokka extension with Gradle turned out to be more complicated than getting it to work with Maven. First step is to figure out where to hand over the extension to the Dokka plugin. After some digging I found the dokkaFatJar configuration option and then also a Dokka issue explaining it. Next issue was that the Dokka extension needs to be a far JAR that includes all dependencies. I didn’t find any explicit documentation on that, but there are several hints:

  1. If you don’t create a fat JAR, there will be an error that the class org.jetbrains.dokka.DokkaBootstrapImpl can not be found.
  2. The configuration option is called dokkaFatJar.
  3. Dokka itself is released as a fat JAR.

A fat JAR can be created with the Maven shade plugin or the Gradle shadow plugin. The spring-auto-restdocs-dokka-json Dokka extension can be used as follows in a Gradle file. So after getting everything right, the actual usage is simple.

buildscript {
    ext {
        springAutoRestDocsVersion = "2.0.2"
        dokkaVersion = "0.9.16"
    }
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"
        ...
    }
}
apply plugin: "org.jetbrains.dokka"
...
dokka {
    outputFormat = "auto-restdocs-json"
    dokkaFatJar = "capital.scalable:spring-auto-restdocs-dokka-json:$springAutoRestDocsVersion"
}

Full version of the build.gradle file.

Conclusion

It took a bit to get started with Dokka, especially because I found no documentation or example project on how to create a custom output format. But in the end I’m happy with the functionality that Dokka offers and with the newly created extension for Spring Auto REST Docs. I hope that this article well help fellow developers that getting started with Dokka a bit quicker than I did. I’m also happy for feedback on how steps described in this article can be improved.

Author: Pawan Kumar

Leave a Reply

Close Menu
%d bloggers like this:
Skip to toolbar