Skip to content

Extension points

In this section you can learn how to create new extension points, how to configure existing ones, and how to query for registered extensions when generating documentation.

Declaring extension points

If you are writing a plugin, you can create your own extension points that other developers (or you) can use in other plugins / parts of code.

class MyPlugin : DokkaPlugin() {
    val sampleExtensionPoint by extensionPoint<SampleExtensionPointInterface>()
}

interface SampleExtensionPointInterface {
    fun doSomething(input: Input): List<Output>
}

class Input
class Output

Usually, you would want to provide some default implementations for your extension points. You can do that within the same plugin class by extending an extension point you've just created. See Extending from extension points for examples.

Extending from extension points

You can use extension points to provide your own implementations in order to customize a plugin's behaviour.

If you want to provide an implementation for an extension point declared in an external plugin (including DokkaBase), you can use plugin querying API to do that.

The example below shows how to extend MyPlugin (that was created above) with an implementation of SampleExtensionPointInterface.

class MyExtendedPlugin : DokkaPlugin() {

    val mySampleExtensionImplementation by extending {
        plugin<MyPlugin>().sampleExtensionPoint with SampleExtensionImpl()
    }
}

class SampleExtensionImpl : SampleExtensionPointInterface {
    override fun doSomething(input: Input): List<Output> = listOf()
}

Alternatively, if it is your own plugin, you can do that within the same class as the extension point itself:

open class MyPlugin : DokkaPlugin() {
    val sampleExtensionPoint by extensionPoint<SampleExtensionPointInterface>()

    val defaultSampleExtension by extending {
        sampleExtensionPoint with DefaultSampleExtension()
    }
}

class DefaultSampleExtension : SampleExtensionPointInterface {
    override fun doSomething(input: Input): List<Output> = listOf()
}

Providing

If you need to have access to DokkaContext when creating an extension, you can use the providing keyword instead.

val defaultSampleExtension by extending {
    sampleExtensionPoint providing { context ->
        // can use context to query other extensions or get configuration 
        DefaultSampleExtension() 
    }
}

You can read more on what you can do with context in Obtaining extension instance.

Override

By extending an extension point, you are registering an additional extension. This behaviour is expected by some extension points, for example the Documentable transformers, because all registered transformer extensions do their own transformations independently and one after the other.

However, a plugin can expect only a single extension to be registered for an extension point. In this case, you can use the override keyword to override the existing registered extension:

class MyExtendedPlugin : DokkaPlugin() {
    private val myPlugin by lazy { plugin<MyPlugin>() }

    val mySampleExtensionImplementation by extending {
        (myPlugin.sampleExtensionPoint
                with SampleExtensionImpl()
                override myPlugin.defaultSampleExtension)
    }
}

This is also useful if you wish to override some extension from DokkaBase, to disable or alter it.

Order

Sometimes, the order in which extensions are invoked matters. This is something you can control as well using the order construct:

class MyExtendedPlugin : DokkaPlugin() {
    private val myPlugin by lazy { plugin<MyPlugin>() }

    val mySampleExtensionImplementation by extending {
        myPlugin.sampleExtensionPoint with SampleExtensionImpl() order {
            before(myPlugin.firstExtension)
            after(myPlugin.thirdExtension)
        }
    }
}

Conditional apply

If you want your extension to be registered only if some condition is true, you can use the applyIf construct:

class MyExtendedPlugin : DokkaPlugin() {
    private val myPlugin by lazy { plugin<MyPlugin>() }

    val mySampleExtensionImplementation by extending {
        myPlugin.sampleExtensionPoint with SampleExtensionImpl() applyIf {
            Random.Default.nextBoolean()
        }
    }
}

Obtaining extension instance

After an extension point has been created and some extensions have been registered, you can use query and querySingle functions to find all or just a single implementation.

class MyExtension(context: DokkaContext) {
    // returns all registered extensions for the extension point
    val allSampleExtensions = context.plugin<MyPlugin>().query { sampleExtensionPoint }

    // will throw an exception if more than one extension is found.
    // use if you expect only a single extension to be registered for the extension point
    val singleSampleExtensions = context.plugin<MyPlugin>().querySingle { sampleExtensionPoint }

    fun invoke() {
        allSampleExtensions.forEach { it.doSomething(Input()) }

        singleSampleExtensions.doSomething(Input())
    }
}

In order to have access to DokkaContext, you can use the providing keyword when registering an extension.