aboutsummaryrefslogtreecommitdiff
path: root/docs-developer/src/doc/docs/developer_guide/architecture/extension_points
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2023-08-04 18:59:28 +0200
committerGitHub <noreply@github.com>2023-08-04 18:59:28 +0200
commitf7bd2ce1a5ef194643b078bd11a90fdf9b389c2b (patch)
tree2f26d61ed1f90f5a2c3405b70c651607f97506d7 /docs-developer/src/doc/docs/developer_guide/architecture/extension_points
parentb559131ddda8efea3394a0ea641460c4189769db (diff)
downloaddokka-f7bd2ce1a5ef194643b078bd11a90fdf9b389c2b.tar.gz
dokka-f7bd2ce1a5ef194643b078bd11a90fdf9b389c2b.tar.bz2
dokka-f7bd2ce1a5ef194643b078bd11a90fdf9b389c2b.zip
Update Developer Guides (#3088)
Diffstat (limited to 'docs-developer/src/doc/docs/developer_guide/architecture/extension_points')
-rw-r--r--docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md33
-rw-r--r--docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md103
-rw-r--r--docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md162
-rw-r--r--docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md40
4 files changed, 338 insertions, 0 deletions
diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md
new file mode 100644
index 00000000..88579be7
--- /dev/null
+++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md
@@ -0,0 +1,33 @@
+# Base plugin
+
+`DokkaBase` represents Dokka's _Base_ plugin, which provides a number of sensible default implementations for
+`CoreExtensions`, as well as declares its own, more high-level abstractions and extension points to be used from other
+plugins and output formats.
+
+If you want to develop a simple plugin that only changes a few details, it is very convenient to rely on
+default implementations and use extension points defined in `DokkaBase`, as it reduces the scope of changes you need to make.
+
+`DokkaBase` is used extensively in Dokka's own output formats.
+
+You can learn how to add, use, override and configure extensions and extension points in
+[Introduction to Extensions](extension_points.md) - all of that information is applicable to the `DokkaBase` plugin as well.
+
+## Extension points
+
+Some notable extension points defined in Dokka's Base plugin.
+
+### PreMergeDocumentableTransformer
+
+`PreMergeDocumentableTransformer` is very similar to the
+[DocumentableTransformer](core_extension_points.md#documentabletransformer) core extension point, but it is used during
+an earlier stage by the [Single module generation](generation_implementations.md#singlemodulegeneration).
+
+This extension point allows you to apply any transformations to the [Documentables model](../data_model/documentable_model.md)
+before the project's [source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) are merged.
+
+It is useful if you want to filter/map existing documentables. For example, if you want to exclude members annotated with
+`@Internal`, you most likely need an implementation of `PreMergeDocumentableTransformer`.
+
+For simple condition-based filtering of documentables, consider extending
+`SuppressedByConditionDocumentableFilterTransformer` - it implements `PreMergeDocumentableTransformer` and only
+requires one function to be overridden, whereas the rest is taken care of.
diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md
new file mode 100644
index 00000000..fc0088c9
--- /dev/null
+++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md
@@ -0,0 +1,103 @@
+# Core extension points
+
+Core extension points represent the main stages of generating documentation.
+
+These extension points are plugin and output format independent, meaning it's the very core functionality and as
+low-level as can get in Dokka.
+
+For higher-level extension functions that can be used in different output formats, have a look at the
+[Base plugin](base_plugin.md).
+
+You can find all core extensions in the `CoreExtensions` class:
+
+```kotlin
+object CoreExtensions {
+ val preGenerationCheck by coreExtensionPoint<PreGenerationChecker>()
+ val generation by coreExtensionPoint<Generation>()
+ val sourceToDocumentableTranslator by coreExtensionPoint<SourceToDocumentableTranslator>()
+ val documentableMerger by coreExtensionPoint<DocumentableMerger>()
+ val documentableTransformer by coreExtensionPoint<DocumentableTransformer>()
+ val documentableToPageTranslator by coreExtensionPoint<DocumentableToPageTranslator>()
+ val pageTransformer by coreExtensionPoint<PageTransformer>()
+ val renderer by coreExtensionPoint<Renderer>()
+ val postActions by coreExtensionPoint<PostAction>()
+}
+```
+
+On this page, we'll go over each extension point individually.
+
+## PreGenerationChecker
+
+`PreGenerationChecker` can be used to run some checks and constraints.
+
+For example, Dokka's Javadoc plugin does not support generating documentation for multi-platform projects, so it uses
+`PreGenerationChecker` to check for multi-platform
+[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets), and fails if it finds any.
+
+## Generation
+
+`Generation` is responsible for generating documentation as a whole, utilizing higher-level extensions and extension
+points where applicable.
+
+See [Generation implementations](generation_implementations.md) to learn about the default implementations.
+
+## SourceToDocumentableTranslator
+
+`SourceToDocumentableTranslator` translates any given sources into the Documentable model.
+
+Kotlin and Java sources are supported by default by the [Base plugin](base_plugin.md), but you can analyze any language
+as long as you can map it to the [Documentable](../data_model/documentable_model.md) model.
+
+For reference, see
+
+* `DefaultDescriptorToDocumentableTranslator` for Kotlin sources translation
+* `DefaultPsiToDocumentableTranslator` for Java sources translation
+
+## DocumentableMerger
+
+`DocumentableMerger` merges all `DModule` instances into one. Only one extension of this type is expected to be
+registered.
+
+## DocumentableTransformer
+
+`DocumentableTransformer` performs the same function as `PreMergeDocumentableTransformer`, but after merging source
+sets.
+
+Notable example is `InheritorsExtractorTransformer`, it extracts inheritance information from
+[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) and creates an inheritance
+map.
+
+## DocumentableToPageTranslator
+
+`DocumentableToPageTranslator` is responsible for creating pages and their content. See
+[Page / Content model](../data_model/page_content.md) page for more information and examples.
+
+Output formats can either use the same page structure or define their own.
+
+Only a single extension of this type is expected to be registered.
+
+## PageTransformer
+
+`PageTransformer` is useful if you need to add, remove or modify generated pages or their content.
+
+Using this extension point, plugins like `org.jetbrains.dokka:mathjax-pligin` can add `.js` scripts to the HTML pages.
+
+If you want all overloaded functions to be rendered on the same page instead of separate ones,
+you can use `PageTransformer` to combine the pages into a single one.
+
+## Renderer
+
+`Renderer` - defines the rules on how to render pages and their content: which files to create and how to display
+the content properly.
+
+Custom output format plugins should use the `Renderer` extension point. Notable examples are `HtmlRenderer`
+and `CommonmarkRenderer`.
+
+## PostAction
+
+`PostAction` can be used for when you want to run some actions after the documentation has been generated - for example,
+if you want to move some files around or log some informational messages.
+
+Dokka's [Versioning plugin](https://github.com/Kotlin/dokka/tree/master/plugins/versioning) utilizes `PostAction`
+to move generated documentation to the versioned directories.
+
diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md
new file mode 100644
index 00000000..97b02a7d
--- /dev/null
+++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md
@@ -0,0 +1,162 @@
+# 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.
+
+```kotlin
+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](#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`.
+
+```kotlin
+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:
+
+```kotlin
+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.
+
+```kotlin
+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](#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:
+
+```kotlin
+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:
+
+```kotlin
+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:
+
+```kotlin
+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](#declaring-extension-points) and some extensions have been
+[registered](#extending-from-extension-points), you can use `query` and `querySingle` functions to find all or just a
+single implementation.
+
+```kotlin
+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](#providing) keyword when registering an extension.
diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md
new file mode 100644
index 00000000..3d857fec
--- /dev/null
+++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md
@@ -0,0 +1,40 @@
+# Generation implementations
+
+There are two main implementations of the [Generation](core_extension_points.md#generation) core extension point:
+
+* `SingleModuleGeneration` - generates documentation for a single module, for instance when `dokkaHtml` task is invoked
+* `AllModulesPageGeneration` - generates multi-module documentation, for instance when `dokkaHtmlMultiModule` task is
+ invoked.
+
+## SingleModuleGeneration
+
+`SingleModuleGeneration` is at the heart of generating documentation. It utilizes [core](core_extension_points.md) and
+[base](base_plugin.md) extensions to build the documentation from start to finish.
+
+Below you can see the flow of how Dokka's [data model](../architecture_overview.md#overview-of-data-model) is transformed
+by various core and base extensions.
+
+```mermaid
+flowchart TD
+ Input -- SourceToDocumentableTranslator --> doc1[Documentables]
+ subgraph documentables [ ]
+ doc1 -- PreMergeDocumentableTransformer --> doc2[Documentables]
+ doc2 -- DocumentableMerger --> doc3[Documentables]
+ doc3 -- DocumentableTransformer --> doc4[Documentables]
+ end
+ doc4 -- DocumentableToPageTranslator --> page1[Pages]
+ subgraph ide2 [ ]
+ page1 -- PageTransformer --> page2[Pages]
+ end
+ page2 -- Renderer --> Output
+```
+
+You can read about what each stage does in [Core extension points](core_extension_points.md) and
+[Base plugin](base_plugin.md).
+
+## AllModulesPageGeneration
+
+`AllModulesPageGeneration` utilizes the output generated by `SingleModuleGeneration`.
+
+Under the hood, it just collects all of the pages generated for individual modules, and assembles it all together,
+creating navigation links between the modules and so on.