From 132be5d1b0617f66dd1c07b2ce0fb8484bd4e98c Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Sun, 29 May 2022 19:10:33 +0200 Subject: [PATCH 1/9] Re-write developer's documentation --- core/src/main/kotlin/model/Documentable.kt | 3 +- core/src/main/kotlin/pages/ContentNodes.kt | 5 +- core/src/main/kotlin/pages/PageNodes.kt | 24 +++ docs/src/doc/docs/about/slack_channel.md | 4 - docs/src/doc/docs/community/plugins-list.md | 112 +++++++++- docs/src/doc/docs/community/slack.md | 7 + .../architecture/architecture_overview.md | 76 +++++++ .../architecture/data_model/documentables.md | 194 ++++++++++++++++++ .../architecture/data_model/extra.md | 93 +++++++++ .../architecture/data_model/page_content.md | 130 ++++++++++++ .../{ => architecture}/extension_points.md | 22 +- .../doc/docs/developer_guide/data_model.md | 103 ---------- .../doc/docs/developer_guide/introduction.md | 122 +---------- .../plugin-development/introduction.md | 68 ++++++ .../plugin-development/simple-plugin-guide.md | 30 +++ docs/src/doc/docs/developer_guide/workflow.md | 98 +++++++++ docs/src/doc/docs/dokka_colors.css | 2 +- docs/src/doc/docs/{about/FAQ.md => faq.md} | 0 docs/src/doc/docs/favicon.svg | 13 +- docs/src/doc/docs/index.md | 28 ++- .../{cli/usage.md => applying/cli.md} | 28 +-- .../{gradle/usage.md => applying/gradle.md} | 26 +-- .../{maven/usage.md => applying/maven.md} | 10 +- .../frontend.md => output-formats/html.md} | 7 +- .../android-plugin.md | 0 .../versioning-plugin.md} | 4 +- docs/src/doc/mkdocs.yml | 45 ++-- .../generation/SingleModuleGeneration.kt | 13 +- .../renderers/gfm/SimpleElementsTest.kt | 11 +- 29 files changed, 948 insertions(+), 330 deletions(-) delete mode 100644 docs/src/doc/docs/about/slack_channel.md create mode 100644 docs/src/doc/docs/community/slack.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/architecture_overview.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/data_model/extra.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md rename docs/src/doc/docs/developer_guide/{ => architecture}/extension_points.md (97%) delete mode 100644 docs/src/doc/docs/developer_guide/data_model.md create mode 100644 docs/src/doc/docs/developer_guide/plugin-development/introduction.md create mode 100644 docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md create mode 100644 docs/src/doc/docs/developer_guide/workflow.md rename docs/src/doc/docs/{about/FAQ.md => faq.md} (100%) mode change 100644 => 100755 docs/src/doc/docs/favicon.svg rename docs/src/doc/docs/user_guide/{cli/usage.md => applying/cli.md} (81%) rename docs/src/doc/docs/user_guide/{gradle/usage.md => applying/gradle.md} (93%) rename docs/src/doc/docs/user_guide/{maven/usage.md => applying/maven.md} (96%) rename docs/src/doc/docs/user_guide/{base-specific/frontend.md => output-formats/html.md} (93%) rename docs/src/doc/docs/user_guide/{android-plugin => plugins}/android-plugin.md (100%) rename docs/src/doc/docs/user_guide/{versioning/versioning.md => plugins/versioning-plugin.md} (95%) diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index 73ab35d41e..01a53e5fea 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -151,8 +151,7 @@ data class DClass( override val sourceSets: Set, override val isExpectActual: Boolean, override val extra: PropertyContainer = PropertyContainer.empty() -) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, - WithExtraProperties { +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, WithExtraProperties { override val children: List get() = (functions + properties + classlikes + constructors) diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt index 0e89b3f6b2..59364af93e 100644 --- a/core/src/main/kotlin/pages/ContentNodes.kt +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -317,9 +317,8 @@ interface Style interface Kind /** - * [ContentKind] represents a grouping of content of one kind. This can be rendered - * as either a part of a composite page (one tab/block within a class's page, for instance) - * or as a separate page altogether. + * [ContentKind] represents a grouping of content of one kind that can can be rendered + * as part of a composite page (one tab/block within a class's page, for instance). */ enum class ContentKind : Kind { diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index c643fd4be1..5cd9adce23 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -1,8 +1,12 @@ package org.jetbrains.dokka.pages +import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.WithChildren +import org.jetbrains.dokka.model.properties.PropertyContainer import java.util.* interface PageNode : WithChildren { @@ -39,6 +43,26 @@ interface WithDocumentables { val documentables: List } +fun create() { + + DRI( + packageName = "kotlinx.coroutines", + classNames = "MainCoroutineDispatcher", + callable = Callable( + name = "limitedParallelism", + receiver = null, + params = listOf( + TypeConstructor( + fullyQualifiedName = "kotlin.Int", + params = emptyList() + ) + ) + ), + target = PointingToDeclaration, + extra = null + ) +} + abstract class RootPageNode(val forceTopLevelName: Boolean = false) : PageNode { val parentMap: Map by lazy { IdentityHashMap().apply { diff --git a/docs/src/doc/docs/about/slack_channel.md b/docs/src/doc/docs/about/slack_channel.md deleted file mode 100644 index 6879dc7de0..0000000000 --- a/docs/src/doc/docs/about/slack_channel.md +++ /dev/null @@ -1,4 +0,0 @@ -# Slack channel - -For more information or help, feel free to ask questions in the [official Kotlin Slack Channel](https://kotlinlang.slack.com) - diff --git a/docs/src/doc/docs/community/plugins-list.md b/docs/src/doc/docs/community/plugins-list.md index 4a65965ed7..11e8933c0c 100644 --- a/docs/src/doc/docs/community/plugins-list.md +++ b/docs/src/doc/docs/community/plugins-list.md @@ -1,18 +1,108 @@ # Dokka community plugins -Here is a list of plugins created by dokka team or community. +TODO add a link on how to apply plugins -In order to add your plugin to this list it needs to be: +## Output Formats - * an open source project - sharing is caring so let others learn and improve your plugin - * present in any public artefacts repository like bintray +### Javadoc (Alpha) -| Plugin name | Description | Source | -| :--------- | :--------- | :------------ | -| [Kotlin as Java](https://kotlin.github.io/dokka/1.7.0/user_guide/introduction/#plugins) | Display Kotlin code as seen from Java | [Github](https://github.com/Kotlin/dokka/tree/master/plugins/kotlin-as-java) -| [GFM](https://kotlin.github.io/dokka/1.7.0/user_guide/introduction/#plugins) | Renders documentation in a GFM format | [Github](https://github.com/Kotlin/dokka/tree/master/plugins/gfm) -| [Javadoc](https://kotlin.github.io/dokka/1.7.0/user_guide/introduction/#plugins) | Renders documentation in a Javadoc format | [Github](https://github.com/Kotlin/dokka/tree/master/plugins/javadoc) -| [Jekyll](https://kotlin.github.io/dokka/1.7.0/user_guide/introduction/#plugins) | Renders documentation in a Jekyll format | [Github](https://github.com/Kotlin/dokka/tree/master/plugins/jekyll) -| [Mermaid-HTML](https://mermaid-js.github.io/mermaid/#/) | Renders Mermaid graphs for HTML renderer. | [Github](https://github.com/glureau/dokka-mermaid) +Javadoc plugin adds a Javadoc output format looks like Java's Javadoc, but it's for the most part +a lookalike, so you may experience problems if you try to use it with a tool that expects native +Javadocs generated by Java. +Javadoc plugin does not support multiplatform projects and does not have a multi-module task. +Javadoc plugin is shipped with Dokka, so you can start using it right away with one of the following tasks: + +* `dokkaJavadoc` - builds Javadoc documentation for single-module projects or for a specific module. +* `dokkaJavadocCollector` - collects generated Javadoc documentation from submodules and assembles it together. + +* TODO add an example or a screenshot +* TODO copy description to the source code + +**This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to +[report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see. + +[Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/javadoc) + +### GFM (Alpha) + +GFM plugins adds an ability to generate documentation in GitHub flavoured Markdown format. Supports both +multi-module and multiplatform projects, and is shipped together with Dokka, so you can start using it +right away with one of the following tasks: + +* `dokkaGfm` - generate documentation for a non multi-module project or one specific module. +* `dokkaGfmMultiModule` - generate documentation for a multi-module project, assemble it together and + generate navigation page/menu for all the modules. + +* TODO add an example or a screenshot +* TODO copy description to the source code + +**This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to +[report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see. + +[Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/gfm) + +### Jekyll (Alpha) + +Jekyll plugins adds an ability to generate documentation in Jekyll flavoured Markdown format. Supports both +multi-module and multiplatform projects, and is shipped together with Dokka, so you can start using it +right away with one of the following tasks: + +* `dokkaJekyll` - generate documentation for a non multi-module project or one specific module. +* `dokkaJekyllMultiModule` - generate documentation for a multi-module project, assemble it together and + generate navigation page/menu for all the modules. + +* TODO add an example or a screenshot +* TODO copy description to the source code + +**This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to +[report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see. + +[Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/jekyll) + +## Extensions + +### Mathjax + +[MathJax](https://docs.mathjax.org/) allows you to include mathematics in your web pages. Dokka MathJax plugin +adds an ability to render mathematics from source code comments. + +If MathJax plugin encounters `@usesMathJax` KDoc tag, it adds `MathJax.js` 2.7.6 with `config=TeX-AMS_SVG` +to generated HTML pages. + +Usage example: +```kotlin +/** + * @usesMathJax + * + * \(\sqrt{3x-1}+(1+x)^2\) + */ +class Clazz {} +``` + +TODO add a screenshot + +[Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/mathjax) + +### Mermaid + +[Mermaid JS](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code. +Mermaid plugin allows rendering such diagrams and visualizations found in source code documentation. + +For more information and examples, see +[Html Mermaid Dokka plugin](https://github.com/glureau/dokka-mermaid) repository on GitHub. + +### Kotlin as Java + +With Kotlin as Java plugin applied, all Kotlin signatures will be rendered as Java signatures. For instance, +`fun foo(bar: Bar): Baz` will be rendered as `public final Baz foo(Bar bar)`. + +Kotlin as Java plugin is published to maven central as a +[separate artifact](https://mvnrepository.com/artifact/org.jetbrains.dokka/kotlin-as-java-plugin): +`org.jetbrains.dokka:kotlin-as-java-plugin:1.6.21`. See instructions on how to apply it. + +TODO insert applying link +TODO insert screenshot + +[Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/kotlin-as-java) diff --git a/docs/src/doc/docs/community/slack.md b/docs/src/doc/docs/community/slack.md new file mode 100644 index 0000000000..98716523f9 --- /dev/null +++ b/docs/src/doc/docs/community/slack.md @@ -0,0 +1,7 @@ +# Slack channel + +Dokka has a dedicated `#dokka` channel in the Kotlin Community Slack, where you can ask questions and chat +about using, customizing or contributing to Dokka. + +[Follow the instructions](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up) +to get an invite or [connect directly](https://kotlinlang.slack.com). diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md new file mode 100644 index 0000000000..a6133c9493 --- /dev/null +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -0,0 +1,76 @@ +# Architecture overview + +Normally, you would think that a tool like Dokka simply parses some programming language sources and generates +`HTML` pages for whatever it sees along the way, with little to no abstractions. That would be the simplest and +shortest way to implement a documentation engine. + +However, it was clear Dokka may need to generate documentation from various sources (not only `Kotlin`), that users +might request additional output formats (like `Markdown`), that users might need additional features like supporting +custom KDoc tags or rendering `mermaid.js` diagrams from documentation in the source code - all these things would +require changing a lot of code inside Dokka itself if all solutions were hardcoded. + +For this reason, Dokka was built from the ground up to be extensible and customizable. You can think of the general +flow of generating documentation with Dokka as mapping one intermediate representation / abstraction into another. +Then you, as a Dokka developer or a plugin writer, can use extension points and introduce selective changes to the +model on any level without touching everything else. + +## Overview of data model + +```mermaid +flowchart TD + Input --> Documentables --> Documentables --> Pages --> Pages --> Output +``` + +* `Input` - generalization of sources, by default `Kotlin`/`Java` sources, but could be virtually anything +* `Documentables` - unified data model that represents any parsed sources as a tree. + Examples: class/function/package/property +* `Pages` - universal model that represents pages (e.g a function/property page) and its content + (lists, text, code blocks) that the users needs to see +* `Output` - specific output format like `HTML`/`Markdown`/`Javadoc`/etc. This is a mapping of content to + some human-readable and visual representation. For instance: + * `ContentList` is mapped as + * `
  • ` / `
      ` for `HTML` format + * `1.` / `*` for `Markdown` format + * `ContentCodeBlock` is mapped as + * `` or `
      ` with some CSS styles in `HTML` format
      +    * Text wrapped in triple backticks for `Markdown` format
      +
      +
      +For a deeper dive into Dokka's data model with more examples and details,
      +see sections about [Documentables](data_model/documentables.md) and [Page/Content](data_model/page_content.md)
      +
      +## Overview of extension points
      +
      +Below you can find the main stages and extension points.
      +
      +```mermaid
      +flowchart TD
      +    Input -- SourceToDocumentableTranslator --> 
      +    Documentables -- DocumentableTransformer --> 
      +    Documentables -- DocumentableToPageTranslator --> 
      +    Pages -- PageTransformer --> 
      +    Pages -- Renderer --> 
      +    Output
      +```
      +
      +* `SourceToDocumentableTranslator` - translates sources into documentable model. `Kotlin` and `Java` sources are
      +  supported by default, but you can analyze any language as long as you can map it to `Documentables` model
      +* `DocumentableTransformer` - useful if you want to filter/map existing documentables. For instance, if you want
      +  to only include members annotated as `@PublicAPI`, you will need to implement a `DocumentableTransformer`
      +* `DocumentableToPageTranslator` - responsible for creating pages and their content. Different output formats can
      +  either use the same page structure or define their own in case it needs to be different.
      +* `PageTransformer` - useful if you need to add/remove/modify generated pages or their content (such as `css`/`js`).
      +  Plugins like `mathjax` can add js scripts to pages using this extension point. If you want all overloaded functions 
      +  to be rendered on the same page (instead of separate ones), you can also use `PageTransformer` to merge it
      +* `Render` - defines rules on what to do with pages and their content, which files to create and how to display 
      +  it properly. Output formats should use `Renderer` extension point - `HtmlRenderer`, `CommonmarkRenderer`, etc
      +
      +___
      +
      +Plugins themselves might declare additional extension points used in intermediate steps. For instance, during
      +`DocumentableToPageTranslator` phase, a plugin might declare a `signatureProvider` extension point, allowing you
      +to provide your own implementation of generating a text signature of a `Documentable` - this could be used by
      +`kotlin-as-java` plugin to display `Kotlin` sources as `Java` code.
      +
      +For a deeper dive into extension points with examples on how to create and use them, 
      +see [Extension points section](extension_points.md)
      diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md
      new file mode 100644
      index 0000000000..5fd8db5be5
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md
      @@ -0,0 +1,194 @@
      +# Documentables
      +
      +Documentables represent data that is parsed from some sources. Think of this data model as of something that could be
      +seen or produced by a compiler frontend, and it's not far off from the truth.
      +
      +By default, documentables are parsed from `Descriptor` (for `Kotlin`)
      +and [Psi](https://plugins.jetbrains.com/docs/intellij/psi.html)
      +(for `Java`) models. Code-wise, you can have a look at following classes:
      +
      +* `DefaultDescriptorToDocumentableTranslator` - responsible for `Kotlin` -> `Documentable` mapping
      +* `DefaultPsiToDocumentableTranslator` - responsible for `Java` -> `Documentable` mapping
      +
      +Upon creation, it's a collection of trees, each with `DModule` as root.
      +
      +```mermaid
      +flowchart TD
      +    DModule --> firstPackage[DPackage]
      +    firstPackage --> DClass
      +    firstPackage --> toplevelfunction[DFunction] 
      +    DClass --> DProperty
      +    DClass --> DFunction
      +    DFunction --> DParameter
      +    DModule --> secondPackage[DPackage]
      +    secondPackage --> DEnum
      +    secondPackage --> secondPackageProperty[DProperty]
      +```
      +
      +At later stages of transformation, all trees are folded into one (by `DocumentableMerger`).
      +
      +## Documentable
      +
      +The main building block of documentables model is `Documentable` class. It's the base class for all more specific types
      +that represent elements of parsed sources with mostly self-explanatory names (`DFunction`, `DPackage`, `DProperty`, etc)
      +.
      +`DClasslike` is the base class for class-like documentables such as `DClass`, `DEnum`, `DAnnotation`, etc.
      +
      +The contents of each documentable normally represent what you would see in source code. For instance, if you open
      +`DClass`, you should find that it contains references to functions, properties, companion object, constructors and so
      +on.
      +`DEnum` should have references to enum entries, and `DPackage` can have references to both classlikes and top-level
      +functions and properties (`Kotlin`-specific).
      +
      +Here's an example of a documentable:
      +
      +```kotlin
      +data class DClass(
      +    val dri: DRI,
      +    val name: String,
      +    val constructors: List,
      +    val functions: List,
      +    val properties: List,
      +    val classlikes: List,
      +    val sources: SourceSetDependent,
      +    val visibility: SourceSetDependent,
      +    val companion: DObject?,
      +    val generics: List,
      +    val supertypes: SourceSetDependent>,
      +    val documentation: SourceSetDependent,
      +    val expectPresentInSet: DokkaSourceSet?,
      +    val modifier: SourceSetDependent,
      +    val sourceSets: Set,
      +    val isExpectActual: Boolean,
      +    val extra: PropertyContainer = PropertyContainer.empty()
      +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors,
      +    WithGenerics, WithSupertypes, WithExtraProperties
      +```
      +
      +___
      +
      +There are three non-documentable classes important for this model:
      +
      +* `DRI`
      +* `SourceSetDependent`
      +* `ExtraProperty`.
      +
      +### DRI
      +
      +`DRI` stans for _Dokka Resource Identifier_ - a unique value that identifies specific `Documentable`.
      +All references and relations between documentables (other than direct ownership) are described using `DRI`.
      +
      +For example, `DFunction` with a parameter of type `Foo` has only `Foo`'s `DRI`, not the actual reference
      +to `Foo`'s `Documentable` object.
      +
      +#### Example
      +
      +For an example of how a `DRI` can look like, let's look at `limitedParallelism` function from `kotlinx.coroutines`:
      +
      +```kotlin
      +package kotlinx.coroutines
      +
      +import ...
      +        
      +public abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
      +    
      +    override fun limitedParallelism(parallelism: Int): CoroutineDispatcher {
      +        ...
      +    }
      +}
      +```
      +
      +If we were to re-create the DRI of this function in code, it would look something like this:
      +
      +```kotlin
      +DRI(
      +    packageName = "kotlinx.coroutines",
      +    classNames = "MainCoroutineDispatcher",
      +    callable = Callable(
      +        name = "limitedParallelism",
      +        receiver = null,
      +        params = listOf(
      +            TypeConstructor(
      +                fullyQualifiedName = "kotlin.Int",
      +                params = emptyList()
      +            )
      +        )
      +    ),
      +    target = PointingToDeclaration,
      +    extra = null
      +)
      +```
      +
      +If you format it as `String`, it would look like this:
      +
      +```
      +kotlinx.coroutines/MainCoroutineDispatcher/limitedParallelism/#kotlin.Int/PointingToDeclaration/
      +```
      +
      +### SourceSetDependent
      +
      +`SourceSetDependent` helps handling multiplatform data by associating platform-specific data (declared with either
      +`expect` or `actual` modifier) with particular source sets.
      +
      +This comes in handy if `expect`/`actual` declarations differ. For instance, the default value for `actual` might differ
      +from that declared in `expect`, or code comments written for `expect` might be different from what's written
      +for `actual`.
      +
      +Under the hood, it's a `typealias` to a `Map`:
      +
      +```kotlin
      +typealias SourceSetDependent = Map
      +```
      +
      +### ExtraProperty
      +
      +`ExtraProperty` is used to store any additional information that falls outside of the regular model. It is highly
      +recommended to use extras to provide any additional information when creating custom Dokka plugins.
      +
      +This element is a bit more complex, so you can read more about how to use it
      +[in a separate section](extra.md).
      +
      +___
      +
      +## Documentation model
      +
      +Documentation model is used along `Documentable` model to store data obtained by parsing
      +code comments (such as `KDoc`/`Javadoc`).
      +
      +### DocTag
      +
      +`DocTag` describes a specific documentation syntax element, universal across source languages. For instance,
      +DocTag `B` is the same for `**bold**` in `Kotlin` and `bold` in `Java`.
      +
      +However, some `DocTag` elements are specific to a certain language, there are many such example for `Java`
      +because it allows HTML tags inside `Javadoc` comments, some of which are not possible with `Markdown`.
      +
      +`DocTag` elements can be deeply nested with other `DocTag` children elements.
      +
      +### TagWrapper
      +
      +`TagWrapper` described a whole comment description or a specific comment tag.
      +For example: `@see` / `@author` / `@return`.
      +
      +Since each such section may contain formatted text inside of it, each `TagWrapper` has `DocTag` children.
      +
      +```kotlin
      +/**
      + * @author **Ben Affleck*
      + * @return nothing, except _sometimes_ it may throw an [Error]
      + */
      +fun foo() {}
      +```
      +
      +### DocumentationNode
      +
      +`DocumentationNode` acts as a container for multiple `TagWrapper` elements for a specific `Documentable`, usually
      +used like this:
      +
      +```kotlin
      +data class DFunction(
      +    ...
      +    val documentation: SourceSetDependent,
      +    ...
      +)
      +```
      diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md
      new file mode 100644
      index 0000000000..69025d7957
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md
      @@ -0,0 +1,93 @@
      +# Extra
      +
      +## Introduction
      +
      +`ExtraProperty` classes are used both by [Documentable](documentables.md) and [Content](page_content.md) models.
      +
      +```kotlin
      +interface ExtraProperty {
      +    interface Key {
      +        fun mergeStrategyFor(left: T, right: T): MergeStrategy = MergeStrategy.Fail {
      +            throw NotImplementedError("Property merging for $this is not implemented")
      +        }
      +    }
      +
      +    val key: Key
      +}
      +```
      +
      +To declare a new extra, you need to implement `ExtraProperty` interface. It is advised to use following pattern
      +when declaring new extras:
      +
      +```kotlin
      +data class CustomExtra(
      +    [any data relevant to your extra] 
      +): ExtraProperty {
      +    override val key: CustomExtra.Key = CustomExtra
      +    companion object : CustomExtra.Key
      +}
      +```
      +
      +Merge strategy for extras is invoked only if merged objects have different values for same Extra.
      +If you don't expect it to happen, you can omit implementing `mergeStrategyFor` function.
      +
      +## PropertyContainer
      +
      +All extras for `ContentNode` and `Documentable` classes are stored in `PropertyContainer` class instances.
      +
      +```kotlin
      +data class DFunction(
      +    ...
      +    override val extra: PropertyContainer = PropertyContainer.empty()
      +    ...
      +) : WithExtraProperties
      +```
      +
      +`PropertyContainer` has a number of convenient functions for handling extras in a collection-like manner.
      +
      +The `C` generic class parameter limits the type of properties that can be stored in the container -  it must
      +match generic `C` class parameter from `ExtraProperty` interface. This allows creating extra properties
      +which can only be stored in a specific `Documentable`.
      +
      +## Usage example
      +
      +In following example we will create `DFunction`-only property, store it and then retrieve its value:
      +
      +```kotlin
      +data class CustomExtra(val customExtraValue: String) : ExtraProperty {
      +    override val key: ExtraProperty.Key = CustomExtra
      +    companion object: ExtraProperty.Key
      +}
      +
      +fun DFunction.withCustomExtraProperty(data: String): DFunction {
      +    return this.copy(
      +        extra = extra + CustomExtra(data)
      +    )
      +}
      +
      +fun DFunction.getCustomExtraPropertyValue(): String? {
      +    return this.extra[CustomExtra]?.customExtraValue
      +}
      +```
      +
      +___
      +
      +You can also use extras as markers, without storing any data in them:
      +
      +```kotlin
      +
      +object MarkerExtra : ExtraProperty, ExtraProperty.Key {
      +    override val key: ExtraProperty.Key = this
      +}
      +
      +fun Documentable.markIfFunction(): Documentable {
      +    return when(this) {
      +        is DFunction -> this.copy(extra = extra + MarkerExtra)
      +        else -> this
      +    }
      +}
      +
      +fun WithExtraProperties.isMarked(): Boolean {
      +    return this.extra[MarkerExtra] != null
      +}
      +```
      diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
      new file mode 100644
      index 0000000000..a07e90658e
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
      @@ -0,0 +1,130 @@
      +# Page / Content Model
      +
      +Even though `Page` and `Content` models reside on the same level (under `Page`), it's easier to view it as two different
      +models altogether, even though `Content` is only used in conjunction with and inside `Page` model.
      +
      +## Page
      +
      +Page model represents the structure of documentation pages to be generated. During rendering, each page
      +is processed separately, so one page corresponds to exactly one output file.
      +
      +Page model is independent of the final output format, in other words it's universal. Which extension the pages
      +should be created as (`.html`, `.md`, etc) and how is up to the `Renderer` part.
      +
      +Subclasses of `PageNode` represent different kinds of rendered pages, such as `ModulePage`, `PackagePage`,
      +`ClasslikePage`, `MemberPage` (properties, functions), etc.
      +
      +The Page Model is a tree structure, with `RootPageNode` at the root.
      +
      +```mermaid
      +flowchart TD
      +    RootPageNode --> firstPackage[PackagePageNode]
      +    RootPageNode --> secondPackage[PackagePageNode]
      +    RootPageNode --> thirdPackage[PackagePageNode]
      +    firstPackage --> firstPackageFirstMember[MemberPageNode - Function]
      +    firstPackage --> firstPackageSecondMember[MemberPageNode - Property]
      +    firstPackage ---> firstPackageClasslike[ClasslikePageNode - Class]
      +    firstPackageClasslike --> firstPackageClasslikeFirstMember[MemberPageNode - Function]
      +    firstPackageClasslike --> firstPackageClasslikeSecondMember[MemberPageNode - Property]
      +```
      +
      +Almost all pages are `ContentPage` - it's the type of `Page` that has `Content` on it.
      +
      +## Content Model
      +
      +Content model describes how the actual `Page` content is presented. The important thing to understand is that it's
      +also output-format independent and is universal.
      +
      +Content model is essentially a set of building blocks that you can put together to represent some content.
      +Have a look at subclasses of `ContentNode`: `ContentText`, `ContentList`, `ContentTable`, `ContentCodeBlock`, 
      +`ContentHeader` and so on. You can group content together with `ContentGroup` - for instance,
      +to wrap all children with some style.
      +
      +```kotlin
      +// real example of composing content using `DocumentableContentBuilder` DSL
      +orderedList {
      +    item {
      +        text("The following table is nested in a list:")
      +        table {
      +            header {
      +                text("Col1")
      +                text("Col2")
      +            }
      +            row {
      +                group(styles = setOf(TextStyle.Bold)) {
      +                    text("Text1")
      +                    text("Text2")
      +                }
      +            }
      +        }
      +    }
      +}
      +```
      +
      +It is then responsibility of `Renderer` (i.e specific output format) to render it the way it wants. For instance,
      +`HtmlRenderer` might render `ContentCodeBlock` as `text`, but `CommonmarkRenderer` might render it using
      +backticks.
      +
      +___
      +
      +### DCI
      +
      +Each node is identified by unique `DCI`, which stands for _Dokka Content Identifier_. `DCI` aggregates `DRI`s of all
      +`Documentables` that make up specific `ContentNode`.
      +
      +```kotlin
      +data class DCI(val dri: Set, val kind: Kind)
      +```
      +
      +All references to other nodes (other than direct ownership) are described using `DCI`.
      +
      +### ContentKind
      +
      +`ContentKind` represents a grouping of content of one kind that can can be rendered as part of a composite
      +page (one tab/block within a class's page, for instance).
      +
      +For instance, on the same page that describes a class you can have multiple sections (== `ContentKind`).
      +One to describe functions, one to describe properties, another one to describe constructors and so on.
      +
      +### Styles
      +
      +Each `ContentNode` has `styles` property in case you want to incidate to `Renderer` that this content needs to be
      +displayed in a certain way.
      +
      +```kotlin
      +group(styles = setOf(TextStyle.Paragraph)) {
      +    text("Text1", styles = setOf(TextStyle.Bold))
      +    text("Text2", styles = setOf(TextStyle.Italic))
      +}
      +```
      +
      +It is then responsibility of `Renderer` (i.e specific output format) to render it the way it wants. For instance,
      +`HtmlRenderer` might render `TextStyle.Bold` as `text`, but `CommonmarkRenderer` might render it as `**text**`.
      +
      +There's a number of existing styles that you can use, most of them are supported by `HtmlRenderer` out of the box:
      +
      +```kotlin
      +// for code highlighting
      +enum class TokenStyle : Style {
      +    Keyword, Punctuation, Function, Operator, Annotation,
      +    Number, String, Boolean, Constant, Builtin, ...
      +}
      +
      +enum class TextStyle : Style {
      +    Bold, Italic, Strong, Strikethrough, Paragraph, ...
      +}
      +
      +enum class ContentStyle : Style {
      +    TabbedContent, RunnableSample, Wrapped, Indented, ...
      +}
      +```
      +
      +### Extra
      +
      +`ExtraProperty` is used to store any additional information that falls outside of the regular model. It is highly
      +recommended to use extras to provide any additional information when creating custom Dokka plugins.
      +
      +All `ExtraProperty` elements from `Documentable` model are propagated into `Content` model and are available
      +for `Renderer`.
      +
      +This element is a bit complex, so you can read more about how to use it [in a separate section](extra.md).
      diff --git a/docs/src/doc/docs/developer_guide/extension_points.md b/docs/src/doc/docs/developer_guide/architecture/extension_points.md
      similarity index 97%
      rename from docs/src/doc/docs/developer_guide/extension_points.md
      rename to docs/src/doc/docs/developer_guide/architecture/extension_points.md
      index 08b05e20b8..50a6d67fee 100644
      --- a/docs/src/doc/docs/developer_guide/extension_points.md
      +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points.md
      @@ -7,8 +7,8 @@ We will discuss all base extension points along with the steps, that `DokkaGener
       ### Setting up Kotlin and Java analysis process and initializing plugins
       
       The provided Maven / CLI / Gradle configuration is read.Then, all the `DokkaPlugin` classes are loaded and the extensions are created.
      - 
      - No entry points here.
      +
      +No entry points here.
       
       ### Creating documentation models
       
      @@ -28,7 +28,7 @@ By default, two translators are created:
       
       After this step, all data from different source sets and languages are kept separately.
       
      -If you are using Kotlin it is recommended to make use of the asynchronous version, providing you implementation of `invokeSuspending`: 
      +If you are using Kotlin it is recommended to make use of the asynchronous version, providing you implementation of `invokeSuspending`:
       
       ```kotlin
       interface AsyncSourceToDocumentableTranslator : SourceToDocumentableTranslator {
      @@ -47,11 +47,13 @@ interface PreMergeDocumentableTransformer {
           operator fun invoke(modules: List, context: DokkaContext): List
       }
       ```
      +
      +// TODO not true, may more now
       By default, three transformers are created:
       
       * `DocumentableVisibilityFilter` that, depending on configuration, filters out all private members from declared packages
       * `ActualTypealiasAdder` that handles Kotlin typealiases
      -* `ModuleAndPackageDocumentationTransformer` that creates documentation content for models and packages itself 
      +* `ModuleAndPackageDocumentationTransformer` that creates documentation content for models and packages itself
       
       ### Merging
       
      @@ -120,7 +122,7 @@ By default, two transformers are created:
       All pages are rendered to desired format.
       
       This step uses `DokkaCore.renderer` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden.
      -                                    
      +
       The extension is required to implement  `Renderer` interface:
       
       ```kotlin
      @@ -129,7 +131,7 @@ interface Renderer {
       }
       ```
       
      -By default, only `HtmlRenderer`, that extends basic `DefaultRenderer`, is created, but it will be registered only if configuration parameter `format` is set to `html`. Using any other value without providing valid renderer will cause Dokka to fail. 
      +By default, only `HtmlRenderer`, that extends basic `DefaultRenderer`, is created, but it will be registered only if configuration parameter `format` is set to `html`. Using any other value without providing valid renderer will cause Dokka to fail.
       
       ## Multimodule page generation endpoints
       
      @@ -149,7 +151,7 @@ interface PageCreator {
       }
       ```
       
      -By default, `MultimodulePageCreator` is created.  This extension is treated as a fallback, so it can be replaced by a custom one. 
      +By default, `MultimodulePageCreator` is created.  This extension is treated as a fallback, so it can be replaced by a custom one.
       
       ### Multimodule page transformation
       
      @@ -160,7 +162,7 @@ This step uses `CoreExtensions.allModulePageTransformer` entry point. All extens
       ## Default extensions' extension points
       
       Default core extension points already have an implementation for providing basic Dokka functionality. All of them are declared in `DokkaBase` plugin. If you don't want this default extensions to load, all you need to do is not load Dokka base and load your plugin instead.
      - 
      +
       ```kotlin
       val customPlugin by configurations.creating
       
      @@ -176,8 +178,8 @@ tasks {
           }
       }
       ```
      - 
      - You will then need to implement extensions for all core extension points. 
      +
      +You will then need to implement extensions for all core extension points.
       
       `DokkaBase` also register several new extension points, with which you can change default behaviour of `DokkaBase` extensions. In order to use them, you need to add `dokka-base` to you dependencies:
       
      diff --git a/docs/src/doc/docs/developer_guide/data_model.md b/docs/src/doc/docs/developer_guide/data_model.md
      deleted file mode 100644
      index ce0b98adef..0000000000
      --- a/docs/src/doc/docs/developer_guide/data_model.md
      +++ /dev/null
      @@ -1,103 +0,0 @@
      -# Dokka Data Model
      -
      -There a four data models that Dokka uses: Documentable Model, Documentation Model, Page Model and Content Model.
      -
      -## Documentable Model
      -
      -Documentable model represents parsed data, returned by compiler analysis. It retains basic order structure of parsed `Psi` or `Descriptor` models.
      -
      -After creation, it is a collection of trees, each with `DModule` as a root. After the Merge step, all trees are folded into one. 
      -
      -The main building block of this model is `Documentable` class, that is a base class for all more specific types that represents elements of parsed Kotlin and Java classes with pretty self-explanatory names: `DPackage`, `DFunction` and so on. `DClasslike` is a base for class-like elements, such as Classes, Enums, Interfaces and so on.
      -
      -There are three non-documentable classes important for the model: `DRI`, `SourceSetDependent` and `ExtraProperty`.
      -
      -* `DRI` (Dokka Resource Identifier) is a unique value that identifies specific `Documentable`. All references to other documentables different than direct ownership are described using DRIs. For example, `DFunction` with parameter of type `X` has only X's DRI, not the actual reference to X's Documentable object.
      -* `SourceSetDependent` is a map that handles multiplatform data, by connecting platform-specific data, declared with either `expect` or `actual` modifier, to a particular Source Set
      -* `ExtraProperty` is used to store any additional information that falls outside of regular model. It is highly recommended to use extras to provide any additional information when creating custom Dokka plugins. This element is a bit more complex, so you can read more about how to use it below.
      -
      -### `ExtraProperty` class usage
      -
      -`ExtraProperty` classes are used both by Documentable and Content models. To declare a new extra, you need to implement `ExtraProperty` interface.
      -
      -```kotlin
      -interface ExtraProperty {
      -    interface Key {
      -        fun mergeStrategyFor(left: T, right: T): MergeStrategy = MergeStrategy.Fail {
      -            throw NotImplementedError("Property merging for $this is not implemented")
      -        }
      -    }
      -
      -    val key: Key
      -}
      -```
      -
      -It is advised to use following pattern when declaring new extras:
      -
      -```kotlin
      -data class CustomExtra( [any values relevant to your extra ] ): ExtraProperty {
      -    companion object : CustomExtra.Key
      -    override val key: CustomExtra.Key = CustomExtra
      -}
      -```
      -Merge strategy for extras is invoked only if merged objects have different values for same Extra. If you don't expect it to happen, you can omit implementing `mergeStrategyFor` function.
      -
      -All extras for `ContentNode` and `Documentable` classes are stored in `PropertyContainer` class instances. The `C` generic class parameter limits the type of properties, that can be stored in the container -  it must match generic `C` class parameter from `ExtraProperty` interface. For example, if you would create `DFunction`-only `ExtraProperty`, it will be limited to be added only to `PropertyContainer`. 
      -
      -In following example we will create `Documentable`-only property, store it in the container and then retrieve its value:
      -
      -```kotlin
      -data class CustomExtra(val customExtraValue: String) : ExtraProperty {
      -
      -    companion object: ExtraProperty.Key
      -
      -    override val key: ExtraProperty.Key = CustomExtra
      -}
      -
      -val extra : PropertyContainer = PropertyContainer.withAll(
      -    CustomExtra("our value")
      -)
      -
      -val customExtraValue : String? = extra[CustomProperty]?.customExtraValue
      -``` 
      -
      -You can also use extras as markers, without storing any data in them:
      -
      -```kotlin
      -
      -object MarkerExtra : ExtraProperty, ExtraProperty.Key {
      -    override val key: ExtraProperty.Key = this
      -}
      -
      -val extra : PropertyContainer = PropertyContainer.withAll(MarkerExtra)
      -
      -val isMarked : Boolean = extra[MarkerExtra] != null
      -
      -```
      -
      -## Documentation Model
      -
      -Documentation model is used along Documentable Model to store data obtained by parsing code commentaries.
      -
      -There are three important classes here:
      -
      -* `DocTag` describes a specific documentation syntax element, for example: header, footer, list, link, raw text, paragraph, etc.
      -* `TagWrapper` described a whole comment description or a specific comment tag, for example: @See, @Returns, @Author; and holds consisting `DocTag` elements 
      -* `DocumentationNode` acts as a container for `TagWrappers` for a specific `Documentable`
      -
      -DocumentationNodes are references by a specific `Documentable`
      -
      -## Page Model
      -
      -Page Model represents the structure of future generated documentation pages and is independent of the final output format, which each node corresponding to exactly one output file. `Renderer` is processing each page separately.Subclasses of `PageNode` represents different kinds of rendered pages for Modules, Packages, Classes etc.
      -
      -The Page Model is a tree structure, with `RootPageNode` being the root. 
      -
      -## Content Model
      -
      -Content Model describes how the actual page content is presented. It organizes it's structure into groups, tables, links, etc. Each node is identified by unique `DCI` (Dokka Content Identifier) and all references to other nodes different than direct ownership are described using DCIs.
      -
      -`DCI` aggregates `DRI`s of all `Documentables` that make up specific `ContentNode`. 
      -
      -Also, all `ExtraProperty` info from consisting `Documentable`s is propagated into Content Model and available for `Renderer`.
      -
      diff --git a/docs/src/doc/docs/developer_guide/introduction.md b/docs/src/doc/docs/developer_guide/introduction.md
      index f8e5e6c79f..2cd53b6634 100644
      --- a/docs/src/doc/docs/developer_guide/introduction.md
      +++ b/docs/src/doc/docs/developer_guide/introduction.md
      @@ -1,124 +1,4 @@
      -# Guide to Dokka Plugin development
      +# Introduction to Dokka Plugin development
       
      -## Building Dokka
       
      -Dokka is built with Gradle. To build it, use `./gradlew build`.
      -Alternatively, open the project directory in IntelliJ IDEA and use the IDE to build and run Dokka.
      -
      -Here's how to import and configure Dokka in IntelliJ IDEA 2019.3:
      -
      -* Select "Open" from the IDEA welcome screen, or File > Open if a project is
      -  already open
      -* Select the directory with your clone of Dokka
      -  
      -!!! note
      -    IDEA may have an error after the project is initally opened; it is OK
      -    to ignore this as the next step will address this error
      -
      -* After IDEA opens the project, select File > New > Module from existing sources
      -  and select the `build.gradle.kts` file from the root directory of your Dokka clone
      -* After Dokka is loaded into IDEA, open the Gradle tool window (View > Tool
      -  Windows > Gradle) and click on the top left "Refresh all Gradle projects"
      -  button
      -  
      -  
      -## Configuration
      -
      -tldr: you can use a convenient [plugin template](https://github.com/Kotlin/dokka-plugin-template) to speed up the setup.
      -
      -Dokka requires configured `Kotlin plugin` and `dokka-core` dependency.
      -
      -```kotlin
      -plugins {
      -    kotlin("jvm") version ""
      -}
      -
      -dependencies {
      -    compileOnly("org.jetbrains.dokka:dokka-core:")
      -}
      -
      -tasks.withType {
      -    kotlinOptions.jvmTarget = "1.8"
      -}
      -```
      -
      -## Building sample plugin
      -
      -In order to load a plugin into Dokka, your class must extend `DokkaPlugin` class. A fully qualified name of that class must be placed in a file named `org.jetbrains.dokka.plugability.DokkaPlugin` under `resources/META-INF/services`.
      -All instances are automatically loaded during Dokka setup using `java.util.ServiceLoader`.
      -
      -Dokka provides a set of entry points, for which user can create their own implementations. They must be delegated using `DokkaPlugin.extending(definition: ExtendingDSL.() -> Extension)` function,that returns a delegate `ExtensionProvider` with supplied definition. 
      -
      -To create a definition, you can use one of two infix functions`with(T)` or `providing( (DokkaContext) -> T)` where `T` is the type of an extended endpoint. You can also use infix functions:
      -
      -* `applyIf( () -> Boolean )` to add additional condition specifying whether or not the extension should be used
      -* `order((OrderDsl.() -> Unit))` to determine if your extension should be used before or after another particular extension for the same endpoint
      -* `override( Extension )` to override other extension. Overridden extension won't be loaded and overridding one will inherit ordering from it.
      -
      -Following sample provides custom translator object as a `DokkaCore.sourceToDocumentableTranslator`
      -
      -```kotlin
      -package org.jetbrains.dokka.sample
      -
      -import org.jetbrains.dokka.plugability.DokkaPlugin
      -
      -class SamplePlugin : DokkaPlugin() {
      -    extension by extending {
      -        DokkaCore.sourceToDocumentableTranslator with CustomSourceToDocumentableTranslator
      -    }
      -}
      -
      -object CustomSourceToDocumentableTranslator: SourceToDocumentableTranslator {
      -    override fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule
      -}
      -```
      -
      -### Registering extension point
      -
      -You can register your own extension point using `extensionPoint` function declared in `DokkaPlugin` class
      -
      -```kotlin
      -class SamplePlugin : DokkaPlugin() {
      -    val extensionPoint by extensionPoint()
      -}
      -
      -interface SampleExtensionPointInterface
      -```
      -
      -### Obtaining extension instance
      -
      -All registered plugins are accessible with `DokkaContext.plugin` function. All plugins that extends `DokkaPlugin` can use `DokkaPlugin.plugin` function, that uses underlying `DokkaContext` instance. If you want to pass context to your extension, you can obtain it using aforementioned `providing` infix function.
      -
      -With plugin instance obtained, you can browse extensions registered for this plugins' extension points using `querySingle` and `query` methods:
      -
      -```kotlin
      -    context.plugin().query { htmlPreprocessors }
      -    context.plugin().querySingle { samplesTransformer }
      -```
      -
      -You can also browse `DokkaContext` directly, using `single` and `get` methods:
      -
      -```kotlin
      -class SamplePlugin : DokkaPlugin() {
      -
      -    val extensionPoint by extensionPoint()
      -    val anotherExtensionPoint by extensionPoint()
      -
      -    val extension by extending {
      -        extensionPoint with SampleExtension()
      -    }
      -
      -    val anotherExtension by extending { 
      -        anotherExtensionPoint providing { context ->
      -            AnotherSampleExtension(context.single(extensionPoint))
      -        }
      -    }
      -}
      -
      -interface SampleExtensionPointInterface
      -interface AnotherSampleExtensionPointInterface
      -
      -class SampleExtension: SampleExtensionPointInterface
      -class AnotherSampleExtension(sampleExtension: SampleExtensionPointInterface): AnotherSampleExtensionPointInterface
      -```
       
      diff --git a/docs/src/doc/docs/developer_guide/plugin-development/introduction.md b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md
      new file mode 100644
      index 0000000000..248f4fc2fb
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md
      @@ -0,0 +1,68 @@
      +## Configuration
      +
      +tldr: you can use a convenient [plugin template](https://github.com/Kotlin/dokka-plugin-template) to speed up the setup.
      +
      +Dokka requires configured `Kotlin plugin` and `dokka-core` dependency.
      +
      +```kotlin
      +plugins {
      +    kotlin("jvm") version ""
      +}
      +
      +dependencies {
      +    compileOnly("org.jetbrains.dokka:dokka-core:")
      +}
      +
      +tasks.withType {
      +    kotlinOptions.jvmTarget = "1.8"
      +}
      +```
      +
      +### Registering extension point
      +
      +You can register your own extension point using `extensionPoint` function declared in `DokkaPlugin` class
      +
      +```kotlin
      +class SamplePlugin : DokkaPlugin() {
      +    val extensionPoint by extensionPoint()
      +}
      +
      +interface SampleExtensionPointInterface
      +```
      +
      +### Obtaining extension instance
      +
      +All registered plugins are accessible with `DokkaContext.plugin` function. All plugins that extends `DokkaPlugin` can use `DokkaPlugin.plugin` function, that uses underlying `DokkaContext` instance. If you want to pass context to your extension, you can obtain it using aforementioned `providing` infix function.
      +
      +With plugin instance obtained, you can browse extensions registered for this plugins' extension points using `querySingle` and `query` methods:
      +
      +```kotlin
      +    context.plugin().query { htmlPreprocessors }
      +    context.plugin().querySingle { samplesTransformer }
      +```
      +
      +You can also browse `DokkaContext` directly, using `single` and `get` methods:
      +
      +```kotlin
      +class SamplePlugin : DokkaPlugin() {
      +
      +    val extensionPoint by extensionPoint()
      +    val anotherExtensionPoint by extensionPoint()
      +
      +    val extension by extending {
      +        extensionPoint with SampleExtension()
      +    }
      +
      +    val anotherExtension by extending { 
      +        anotherExtensionPoint providing { context ->
      +            AnotherSampleExtension(context.single(extensionPoint))
      +        }
      +    }
      +}
      +
      +interface SampleExtensionPointInterface
      +interface AnotherSampleExtensionPointInterface
      +
      +class SampleExtension: SampleExtensionPointInterface
      +class AnotherSampleExtension(sampleExtension: SampleExtensionPointInterface): AnotherSampleExtensionPointInterface
      +```
      diff --git a/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md b/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md
      new file mode 100644
      index 0000000000..1bac4547fd
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md
      @@ -0,0 +1,30 @@
      +## Building sample plugin
      +
      +In order to load a plugin into Dokka, your class must extend `DokkaPlugin` class. A fully qualified name of that class must be placed in a file named `org.jetbrains.dokka.plugability.DokkaPlugin` under `resources/META-INF/services`.
      +All instances are automatically loaded during Dokka setup using `java.util.ServiceLoader`.
      +
      +Dokka provides a set of entry points, for which user can create their own implementations. They must be delegated using `DokkaPlugin.extending(definition: ExtendingDSL.() -> Extension)` function,that returns a delegate `ExtensionProvider` with supplied definition.
      +
      +To create a definition, you can use one of two infix functions`with(T)` or `providing( (DokkaContext) -> T)` where `T` is the type of an extended endpoint. You can also use infix functions:
      +
      +* `applyIf( () -> Boolean )` to add additional condition specifying whether or not the extension should be used
      +* `order((OrderDsl.() -> Unit))` to determine if your extension should be used before or after another particular extension for the same endpoint
      +* `override( Extension )` to override other extension. Overridden extension won't be loaded and overridding one will inherit ordering from it.
      +
      +Following sample provides custom translator object as a `DokkaCore.sourceToDocumentableTranslator`
      +
      +```kotlin
      +package org.jetbrains.dokka.sample
      +
      +import org.jetbrains.dokka.plugability.DokkaPlugin
      +
      +class SamplePlugin : DokkaPlugin() {
      +    extension by extending {
      +        DokkaCore.sourceToDocumentableTranslator with CustomSourceToDocumentableTranslator
      +    }
      +}
      +
      +object CustomSourceToDocumentableTranslator: SourceToDocumentableTranslator {
      +    override fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule
      +}
      +```
      diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md
      new file mode 100644
      index 0000000000..95c74304c8
      --- /dev/null
      +++ b/docs/src/doc/docs/developer_guide/workflow.md
      @@ -0,0 +1,98 @@
      +Whether you're contributing a feature/fix to Dokka itself or developing a separate plugin, there's 3 things
      +you'll be doing:
      +
      +1. Building Dokka
      +2. Using/Testing locally built Dokka in a (debug) project
      +3. Debugging Dokka / Plugin code
      +
      +We'll go over each step individually in this section.
      +
      +Examples below will be specific to Gradle and [Gradle’s Kotlin DSL](https://docs.gradle.org/current/userguide/kotlin_dsl.html),
      +but you can apply the same principles and run/test/debug with CLI/Maven runners and build configurations if you wish.
      +
      +If you have any specific questions which are not covered in this section, feel free to reach out to devs out through
      +[Slack](../community/slack.md).
      +
      +## Building Dokka
      +
      +Building Dokka is pretty straightforward, with one small caveat: when you run `./gradlew build`, it will run
      +integration tests as well, which might take some time and will consume a lot of RAM, so you would usually want
      +to exclude integration tests when building locally.
      +
      +!!! example
      +    `./gradlew build -x integrationTest`
      +
      +Unit tests which are run as part of `build` should not take much time, but you can also skip it with `-x test`.
      +
      +### Troubleshooting build
      +
      +#### API check failed for project ..
      +
      +If you see messages like `API check failed for project ..` during `build` phase, it indicates that
      +[binary compatibility check](https://github.com/Kotlin/binary-compatibility-validator) has failed, meaning you've 
      +changed/added/removed some public API.
      +
      +If the change was intentional, run `./gradlew apiDump` - it will re-generate `.api` files with signatures,
      +and you should be able to `build` Dokka with no errors. Maintainers will review API changes, so please make sure
      +it's intentional and rational.
      +
      +## Using/testing locally built Dokka
      +
      +Having built Dokka locally, you can publish it to `mavenLocal()`. This will allow you to test your changes in another
      +project as well as debug code remotely.
      +
      +1. Change `dokka_version` in `gradle.properties` to something that you will use later on as the dependency version.
      +   For instance, you can set it to something like `1.6.21-my-fix-SNAPSHOT`. This version will be propagated to plugins
      +   that reside inside Dokka's project (such as `mathjax`, `kotlin-as-java`, etc).
      +2. Publish it to maven local (`./gradlew publishToMavenLocal`). Corresponding artifacts should appear in `~/.m2`
      +3. In the project you want to generate documentation for or debug on, add maven local as a plugin/dependency
      +   repository:
      +```kotlin
      +repositories {
      +   mavenLocal()
      +}
      +```
      +4. Update your dokka dependency to the version you've just published:
      +```kotlin
      +plugins {
      +    id("org.jetbrains.dokka") version "1.6.21-my-fix-SNAPSHOT"
      +}
      +```
      +
      +## Debugging Dokka
      +
      +Dokka is essentially a gradle plugin, so you can debug it the same way you would any other gradle plugin. 
      +
      +Below you'll find instructions on how to debug Dokka's internal logic, but you can apply the same principles if you
      +wish to debug a plugin which resides in a separate project.
      +
      +1. Choose a project to debug on, it needs to have some code for which documentation will be generated.
      +   Prefer using smaller projects that reproduce the exact problem or behaviour you want
      +   since the less code you have, the easier it will be to understand what's going on. You can use example projects
      +   found in [dokka/examples/gradle](https://github.com/Kotlin/dokka/tree/master/examples/gradle), there's both simple 
      +   single-module and more complex multi-module/multiplatform examples.
      +2. For the debug project, set `org.gradle.debug` to `true` in one of the following ways:
      +
      +    * In your `gradle.properties` add `org.gradle.debug=true`
      +    * When running Dokka tasks:
      `./gradlew dokkaHtml -Dorg.gradle.debug=true --no-daemon` + +3. Run desired Dokka task with `--no-daemon`. Gradle should wait until you attach with debugger before proceeding + with the task, so no need to hurry here. +
      Example: `./gradlew dokkaHtml -Dorg.gradle.debug=true --no-daemon`. + +4. Open Dokka in IntelliJ IDEA, set a breakpoint and, using remote debug in IntelliJ IDEA, + [Attach to process](https://www.jetbrains.com/help/idea/attaching-to-local-process.html#attach-to-remote) + running on the default port 5005. You can do that either by creating a `Remote JVM Debug` Run/Debug configuration + or by attaching to the process via `Run` -> `Attach to process` + +!!! note + The reason for `--no-daemon` is that + [Gradle daemons](https://docs.gradle.org/current/userguide/gradle_daemon.html) continue to exist even after the task + has completed execution, so you might hang in debug or experience issues with `port was already in use` if you try + to run it again. + + If you previously ran Dokka with daemons and you are already encountering problems with it, try killing + gradle daemons. For instance, via `pkill -f gradle.*daemon` + +In case you're experiencing need to debug some other part of the build - consult the official Gradle +tutorials on [Troubleshooting Builds](https://docs.gradle.org/current/userguide/troubleshooting.html). diff --git a/docs/src/doc/docs/dokka_colors.css b/docs/src/doc/docs/dokka_colors.css index 9e45c919ea..be10a56070 100644 --- a/docs/src/doc/docs/dokka_colors.css +++ b/docs/src/doc/docs/dokka_colors.css @@ -1,3 +1,3 @@ .md-header { - background-color: #F8873C; + background-color: #bc0fd4; } diff --git a/docs/src/doc/docs/about/FAQ.md b/docs/src/doc/docs/faq.md similarity index 100% rename from docs/src/doc/docs/about/FAQ.md rename to docs/src/doc/docs/faq.md diff --git a/docs/src/doc/docs/favicon.svg b/docs/src/doc/docs/favicon.svg old mode 100644 new mode 100755 index 1b3b3670e0..1fea08773f --- a/docs/src/doc/docs/favicon.svg +++ b/docs/src/doc/docs/favicon.svg @@ -1,3 +1,10 @@ - - - + + + + + + + + + + \ No newline at end of file diff --git a/docs/src/doc/docs/index.md b/docs/src/doc/docs/index.md index d730e11d1e..d1fe1af4b0 100644 --- a/docs/src/doc/docs/index.md +++ b/docs/src/doc/docs/index.md @@ -1,16 +1,24 @@ # Dokka [![official JetBrains project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![TeamCity (build status)](https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:Kotlin_Dokka_DokkaAntMavenGradle)/statusIcon)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_Dokka_DokkaAntMavenGradle&branch_KotlinTools_Dokka=%3Cdefault%3E&tab=buildTypeStatusDiv) -Dokka is a documentation engine for Kotlin, performing the same function as javadoc 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](https://kotlinlang.org/docs/reference/kotlin-doc.html) in Kotlin files, -and can generate documentation in multiple formats including standard Javadoc, HTML and Markdown. +Dokka is an API documentation engine for Kotlin that performs the same function as the Javadoc tool for Java, +but more modern and highly pluggable. + +Just like Kotlin itself, Dokka supports mixed-language Kotlin/Java projects. It understands +[KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in Kotlin source files as well +as Javadoc comments in Java files, and can generate documentation in multiple formats including its +own HTML format, Java's Javadoc lookalike and Markdown. + +Some libraries that use Dokka for API reference docs: + +* [kotlinx.coroutines](https://kotlin.github.io/kotlinx.coroutines/) +* [kotlinx.serialization](https://kotlin.github.io/kotlinx.serialization/) +* [Ktor](https://api.ktor.io/) +* [Spring Framework](https://docs.spring.io/spring-framework/docs/current/kdoc-api/) -## Integrations Dokka provides support for the following build systems: -* [Gradle](user_guide/gradle/usage.md) -* [Maven](user_guide/maven/usage.md) -* [Command line](user_guide/cli/usage.md) +* [Gradle](user_guide/applying/gradle.md) (preffered) +* [Maven](user_guide/applying/maven.md) +* [Command line](user_guide/applying/cli.md) -!!! note - The Gradle plugin is the preferred way to use Dokka +// TODO write about plugins and pluggability diff --git a/docs/src/doc/docs/user_guide/cli/usage.md b/docs/src/doc/docs/user_guide/applying/cli.md similarity index 81% rename from docs/src/doc/docs/user_guide/cli/usage.md rename to docs/src/doc/docs/user_guide/applying/cli.md index a489979dd8..219411b193 100644 --- a/docs/src/doc/docs/user_guide/cli/usage.md +++ b/docs/src/doc/docs/user_guide/applying/cli.md @@ -10,19 +10,19 @@ java -jar dokka-cli.jar Dokka supports the following command line arguments: - * `-outputDir` - the output directory where the documentation is generated - * `-moduleName` - (required) - module name used as a part of source set ID when declaring dependent source sets - * `-cacheRoot` - cache directory to enable package-list caching - * `-pluginsClasspath` - artifacts with Dokka plugins, separated by `;`. At least `dokka-base` and all its dependencies must be added there - * `-pluginsConfiguration` - configuration for plugins in format `fqPluginName=json^^fqPluginName=json...` - * `-offlineMode` - do not resolve package-lists online - * `-failOnWarning` - throw an exception instead of a warning - * `-globalPackageOptions` - per package options added to all source sets - * `-globalLinks` - external documentation links added to all source sets - * `-globalSrcLink` - source links added to all source sets - * `-noSuppressObviousFunctions` - don't suppress obvious functions like default `toString` or `equals` - * `-suppressInheritedMembers` - suppress all inherited members that were not overriden in a given class. Eg. using it you can suppress toString or equals functions but you can't suppress componentN or copy on data class - * `-sourceSet` - (repeatable) - configuration for a single source set. Following this argument, you can pass other arguments: +* `-outputDir` - the output directory where the documentation is generated +* `-moduleName` - (required) - module name used as a part of source set ID when declaring dependent source sets +* `-cacheRoot` - cache directory to enable package-list caching +* `-pluginsClasspath` - artifacts with Dokka plugins, separated by `;`. At least `dokka-base` and all its dependencies must be added there +* `-pluginsConfiguration` - configuration for plugins in format `fqPluginName=json^^fqPluginName=json...` +* `-offlineMode` - do not resolve package-lists online +* `-failOnWarning` - throw an exception instead of a warning +* `-globalPackageOptions` - per package options added to all source sets +* `-globalLinks` - external documentation links added to all source sets +* `-globalSrcLink` - source links added to all source sets +* `-noSuppressObviousFunctions` - don't suppress obvious functions like default `toString` or `equals` +* `-suppressInheritedMembers` - suppress all inherited members that were not overriden in a given class. Eg. using it you can suppress toString or equals functions but you can't suppress componentN or copy on data class +* `-sourceSet` - (repeatable) - configuration for a single source set. Following this argument, you can pass other arguments: * `-sourceSetName` - source set name as a part of source set ID when declaring dependent source sets * `-displayName` - source set name displayed in the generated documentation * `-src` - list of source files or directories separated by `;` @@ -42,7 +42,7 @@ Dokka supports the following command line arguments: * `-jdkVersion` - version of JDK to use for linking to JDK JavaDoc * `-analysisPlatform` - platform used for analysis, see the [Platforms](#platforms) section * `-dependentSourceSets` - list of dependent source sets in format `moduleName/sourceSetName`, separated by `;` - * `-loggingLevel` - one of `DEBUG`, `PROGRESS`, `INFO`, `WARN`, `ERROR`. Defaults to `DEBUG`. Please note that this argument can't be passed in JSON. +* `-loggingLevel` - one of `DEBUG`, `PROGRESS`, `INFO`, `WARN`, `ERROR`. Defaults to `DEBUG`. Please note that this argument can't be passed in JSON. You can also use a JSON file with Dokka configuration: diff --git a/docs/src/doc/docs/user_guide/gradle/usage.md b/docs/src/doc/docs/user_guide/applying/gradle.md similarity index 93% rename from docs/src/doc/docs/user_guide/gradle/usage.md rename to docs/src/doc/docs/user_guide/applying/gradle.md index 399075a5cc..fd4e53bb17 100644 --- a/docs/src/doc/docs/user_guide/gradle/usage.md +++ b/docs/src/doc/docs/user_guide/applying/gradle.md @@ -1,15 +1,15 @@ # Using the Gradle plugin !!! important - If you are upgrading from 0.10.x to a current release of Dokka, please have a look at our - [migration guide](https://github.com/Kotlin/dokka/blob/master/runners/gradle-plugin/MIGRATION.md) +If you are upgrading from 0.10.x to a current release of Dokka, please have a look at our +[migration guide](https://github.com/Kotlin/dokka/blob/master/runners/gradle-plugin/MIGRATION.md) ### Supported versions Dokka should work on gradle newer than 5.6 ### Setup -The preferred way is to use `plugins` block. +The preferred way is to use `plugins` block. build.gradle.kts: ```kotlin @@ -37,7 +37,7 @@ apply(plugin="org.jetbrains.dokka") ``` The plugin adds `dokkaHtml`, `dokkaJavadoc`, `dokkaGfm` and `dokkaJekyll` tasks to the project. - + Each task corresponds to one output format, so you should run `dokkaGfm` when you want to have a documentation in `GFM` format. Output formats are explained in [the introduction](../introduction.md#output-formats) @@ -60,15 +60,15 @@ dokkaHtml { } ``` -!!! note - Dokka extracts the information about sourcesets from the Kotlin Gradle plugin. - Therefore, if you are using Dokka in a [precompiled script plugin](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins), - you will have to add a depencency to the Kotlin Gradle Plugin as well - (`implementation(kotlin("gradle-plugin", ""))` resp. `implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:")`). +!!! note +Dokka extracts the information about sourcesets from the Kotlin Gradle plugin. +Therefore, if you are using Dokka in a [precompiled script plugin](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins), +you will have to add a depencency to the Kotlin Gradle Plugin as well +(`implementation(kotlin("gradle-plugin", ""))` resp. `implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:")`). ## Configuration options -Dokka documents single-platform as well as multi-platform projects. +Dokka documents single-platform as well as multi-platform projects. Most of the configuration options are set per one source set. The available configuration options are shown below: @@ -262,7 +262,7 @@ tasks.withType().configureEach { ``` !!! note - If you want to share the configuration between source sets, you can use Gradle's `configureEach` +If you want to share the configuration between source sets, you can use Gradle's `configureEach` ## Applying plugins Dokka plugin creates Gradle configuration for each output format in the form of `dokka${format}Plugin` (or `dokka${format}PartialPlugin` for multi-module tasks) : @@ -284,7 +284,7 @@ val customDokkaTask by creating(DokkaTask::class) { ``` !!! important - Please note that `dokkaJavadoc` task will properly document only single `jvm` source set +Please note that `dokkaJavadoc` task will properly document only single `jvm` source set To generate the documentation, use the appropriate `dokka${format}` Gradle task: @@ -323,7 +323,7 @@ pluginsMapConfiguration.set(mapOf("" to """` - main content * `<@resources/>` - scripts, stylesheets -* `<@version/>` - version ([versioning-plugin](https://kotlin.github.io/dokka/1.7.0/user_guide/versioning/versioning/) will replace this with a version navigator) +* `<@version/>` - version ([versioning-plugin](../plugins/versioning-plugin.md) will replace this with a version navigator) * `<@template_cmd name="...""> ...` - is used for variables that depend on the root project (such `pathToRoot`, `projectName`). They are available only inside the directive. This is processed by a multi-module task that assembles partial outputs from modules. Example: ``` diff --git a/docs/src/doc/docs/user_guide/android-plugin/android-plugin.md b/docs/src/doc/docs/user_guide/plugins/android-plugin.md similarity index 100% rename from docs/src/doc/docs/user_guide/android-plugin/android-plugin.md rename to docs/src/doc/docs/user_guide/plugins/android-plugin.md diff --git a/docs/src/doc/docs/user_guide/versioning/versioning.md b/docs/src/doc/docs/user_guide/plugins/versioning-plugin.md similarity index 95% rename from docs/src/doc/docs/user_guide/versioning/versioning.md rename to docs/src/doc/docs/user_guide/plugins/versioning-plugin.md index f323d8a837..876ec436ed 100644 --- a/docs/src/doc/docs/user_guide/versioning/versioning.md +++ b/docs/src/doc/docs/user_guide/plugins/versioning-plugin.md @@ -79,8 +79,8 @@ Alternatively, without adding plugin to classpath: pluginsMapConfiguration.set(mapOf("org.jetbrains.dokka.versioning.VersioningPlugin" to """{ "version": "1.0" }""")) ``` -Please consult the [Gradle documentation](../gradle/usage.md#applying-plugins) for more information about configuring Dokka with this build tool. +Please consult the [Gradle documentation](../applying/gradle.md#applying-plugins) for more information about configuring Dokka with this build tool. Please see the [Dokka Gradle versioning multi modules example project](https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-versioning-multimodule-example). -Also see the [generated documentation](https://Kotlin.github.io/dokka/examples/dokka-versioning-multimodule-example/htmlMultiModule). \ No newline at end of file +Also see the [generated documentation](https://Kotlin.github.io/dokka/examples/dokka-versioning-multimodule-example/htmlMultiModule). diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index 139c2297d8..1965f6292d 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Dokka +site_name: Dokka documentation # Meta tags (placed in header) site_description: Dokka is a documentation engine for Kotlin, performing the same function as javadoc for Java @@ -37,30 +37,45 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.magiclink - pymdownx.smartsymbols - - pymdownx.superfences + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format # Dev server binding #dev_addr: 127.0.0.1:3001 nav: - Home: index.md - - User guide: + - User guides: - Introduction: user_guide/introduction.md - - Gradle: user_guide/gradle/usage.md - - Maven: user_guide/maven/usage.md - - Command line: user_guide/cli/usage.md - - Html frontend: user_guide/base-specific/frontend.md - - Versioning: user_guide/versioning/versioning.md - - Android plugin: user_guide/android-plugin/android-plugin.md - - Plugin development: + - Applying Dokka: + - Gradle: user_guide/applying/gradle.md + - Maven: user_guide/applying/maven.md + - Command line: user_guide/applying/cli.md + - Output formats: + - HTML: user_guide/output-formats/html.md + - Plugins: + - Versioning plugin: user_guide/plugins/versioning-plugin.md + - Android plugin: user_guide/plugins/android-plugin.md + - Developer guides: - Introduction: developer_guide/introduction.md - - Data model: developer_guide/data_model.md - - Extension points: developer_guide/extension_points.md + - Workflow: developer_guide/workflow.md + - Architecture: + - Overview: developer_guide/architecture/architecture_overview.md + - Data model: + - Documentables: developer_guide/architecture/data_model/documentables.md + - Page & Content: developer_guide/architecture/data_model/page_content.md + - Extra properties: developer_guide/architecture/data_model/extra.md + - Extension points: developer_guide/architecture/extension_points.md + - Plugin development: + - Introduction: developer_guide/plugin-development/introduction.md + - Simple plugin tutorial: developer_guide/plugin-development/simple-plugin-guide.md - Community: + - Slack: community/slack.md - Community plugins: community/plugins-list.md - - About: - - FAQ: about/FAQ.md - - Slack channel: about/slack_channel.md + - FAQ: faq.md extra_javascript: - expand_navigation.js diff --git a/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt b/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt index 5cdeeb40f3..64d9c76d78 100644 --- a/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt +++ b/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt @@ -24,6 +24,7 @@ class SingleModuleGeneration(private val context: DokkaContext) : Generation { report("Validity check") validityCheck(context) + // Step 1: translate sources into documentables & transform documentables (change internally) report("Creating documentation models") val modulesFromPlatforms = createDocumentationModels() @@ -31,18 +32,20 @@ class SingleModuleGeneration(private val context: DokkaContext) : Generation { val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms) report("Merging documentation models") - val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge) + val transformedDocumentationAfterMerge = mergeDocumentationModels(transformedDocumentationBeforeMerge) ?: exitGenerationGracefully("Nothing to document") report("Transforming documentation model after merging") - val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel) + val transformedDocumentation = transformDocumentationModelAfterMerge(transformedDocumentationAfterMerge) + // Step 2: Generate pages & transform them (change internally) report("Creating pages") val pages = createPages(transformedDocumentation) report("Transforming pages") val transformedPages = transformPages(pages) + // Step 3: Rendering report("Rendering") render(transformedPages) @@ -54,7 +57,7 @@ class SingleModuleGeneration(private val context: DokkaContext) : Generation { override val generationName = "documentation for ${context.configuration.moduleName}" - fun createDocumentationModels() = runBlocking(Dispatchers.Default) { + fun createDocumentationModels(): List = runBlocking(Dispatchers.Default) { context.configuration.sourceSets.parallelMap { sourceSet -> translateSources(sourceSet, context) }.flatten() .also { modules -> if (modules.isEmpty()) exitGenerationGracefully("Nothing to document") } } @@ -69,10 +72,10 @@ class SingleModuleGeneration(private val context: DokkaContext) : Generation { fun transformDocumentationModelAfterMerge(documentationModel: DModule) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } - fun createPages(transformedDocumentation: DModule) = + fun createPages(transformedDocumentation: DModule): RootPageNode = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) - fun transformPages(pages: RootPageNode) = + fun transformPages(pages: RootPageNode): RootPageNode = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } fun render(transformedPages: RootPageNode) { diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt index e102f52a55..81dea14bda 100644 --- a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt @@ -2,10 +2,7 @@ package renderers.gfm import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.pages.ContentEmbeddedResource -import org.jetbrains.dokka.pages.ContentKind -import org.jetbrains.dokka.pages.DCI -import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.pages.* import org.junit.Assert.assertEquals import org.junit.jupiter.api.Test import renderers.RawTestPage @@ -312,8 +309,10 @@ class SimpleElementsTest : GfmRenderingOnlyTestBase() { text("Col2") } row { - text("Text1") - text("Text2") + group(styles = setOf(TextStyle.Paragraph)) { + text("Text1", styles = setOf(TextStyle.Bold)) + text("Text2", styles = setOf(TextStyle.Italic)) + } } } } From cb9fe5f6dbdbc7afbb3d6fb0bbb6cb3ad9de010b Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Wed, 1 Jun 2022 12:07:52 +0200 Subject: [PATCH 2/9] Finish extension points section --- core/src/main/kotlin/model/Documentable.kt | 3 +- core/src/main/kotlin/pages/PageNodes.kt | 24 -- .../architecture/architecture_overview.md | 96 ++++---- .../architecture/extension_points.md | 225 ------------------ .../extension_points/base_extensions.md | 15 ++ .../extension_points/core_extensions.md | 140 +++++++++++ .../extension_points/introduction.md | 159 +++++++++++++ docs/src/doc/docs/developer_guide/workflow.md | 2 +- docs/src/doc/mkdocs.yml | 5 +- .../transformers/HideTagDocumentableFilter.kt | 3 +- .../renderers/gfm/SimpleElementsTest.kt | 11 +- 11 files changed, 384 insertions(+), 299 deletions(-) delete mode 100644 docs/src/doc/docs/developer_guide/architecture/extension_points.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md create mode 100644 docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index 01a53e5fea..73ab35d41e 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -151,7 +151,8 @@ data class DClass( override val sourceSets: Set, override val isExpectActual: Boolean, override val extra: PropertyContainer = PropertyContainer.empty() -) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, WithExtraProperties { +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, + WithExtraProperties { override val children: List get() = (functions + properties + classlikes + constructors) diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index 5cd9adce23..c643fd4be1 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -1,12 +1,8 @@ package org.jetbrains.dokka.pages -import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.WithChildren -import org.jetbrains.dokka.model.properties.PropertyContainer import java.util.* interface PageNode : WithChildren { @@ -43,26 +39,6 @@ interface WithDocumentables { val documentables: List } -fun create() { - - DRI( - packageName = "kotlinx.coroutines", - classNames = "MainCoroutineDispatcher", - callable = Callable( - name = "limitedParallelism", - receiver = null, - params = listOf( - TypeConstructor( - fullyQualifiedName = "kotlin.Int", - params = emptyList() - ) - ) - ), - target = PointingToDeclaration, - extra = null - ) -} - abstract class RootPageNode(val forceTopLevelName: Boolean = false) : PageNode { val parentMap: Map by lazy { IdentityHashMap().apply { diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md index a6133c9493..5fde9f59ec 100644 --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -4,14 +4,14 @@ Normally, you would think that a tool like Dokka simply parses some programming `HTML` pages for whatever it sees along the way, with little to no abstractions. That would be the simplest and shortest way to implement a documentation engine. -However, it was clear Dokka may need to generate documentation from various sources (not only `Kotlin`), that users +However, it was clear that Dokka may need to generate documentation from various sources (not only `Kotlin`), that users might request additional output formats (like `Markdown`), that users might need additional features like supporting -custom KDoc tags or rendering `mermaid.js` diagrams from documentation in the source code - all these things would -require changing a lot of code inside Dokka itself if all solutions were hardcoded. +custom KDoc tags or rendering `mermaid.js` diagrams - all these things would require changing a lot of code inside +Dokka itself if all solutions were hardcoded. For this reason, Dokka was built from the ground up to be extensible and customizable. You can think of the general flow of generating documentation with Dokka as mapping one intermediate representation / abstraction into another. -Then you, as a Dokka developer or a plugin writer, can use extension points and introduce selective changes to the +You, as a Dokka developer or a plugin writer, can use extension points and introduce selective changes to the model on any level without touching everything else. ## Overview of data model @@ -28,12 +28,12 @@ flowchart TD (lists, text, code blocks) that the users needs to see * `Output` - specific output format like `HTML`/`Markdown`/`Javadoc`/etc. This is a mapping of content to some human-readable and visual representation. For instance: - * `ContentList` is mapped as - * `
    • ` / `
        ` for `HTML` format - * `1.` / `*` for `Markdown` format - * `ContentCodeBlock` is mapped as - * `` or `
        ` with some CSS styles in `HTML` format
        -    * Text wrapped in triple backticks for `Markdown` format
        +    * `ContentList` is mapped as
        +        * `
      • ` / `
          ` for `HTML` format + * `1.` / `*` for `Markdown` format + * `ContentCodeBlock` is mapped as + * `` or `
          ` with some CSS styles in `HTML` format
          +        * Text wrapped in triple backticks for `Markdown` format
           
           
           For a deeper dive into Dokka's data model with more examples and details,
          @@ -41,36 +41,50 @@ see sections about [Documentables](data_model/documentables.md) and [Page/Conten
           
           ## Overview of extension points
           
          -Below you can find the main stages and extension points.
          +An extension point usually represents some pluggable interface that performs an action during one of the stages of
          +generating documentation. An extension is therefore an implementation of that interface which is extending the
          +extension point.
           
          -```mermaid
          -flowchart TD
          -    Input -- SourceToDocumentableTranslator --> 
          -    Documentables -- DocumentableTransformer --> 
          -    Documentables -- DocumentableToPageTranslator --> 
          -    Pages -- PageTransformer --> 
          -    Pages -- Renderer --> 
          -    Output
          -```
          +You can create extension points, provide your own implementations (extensions) and configure them. All of
          +this is possible with Dokka's plugin/extension point API.
          +
          +For a deeper dive into extensions and extension points with more examples and details, see
          +[Introduction to Extensions](extension_points/introduction.md).
          +
          +Here's a sneak peek of the DSL:
          +
          +```kotlin
          +class MyPlugin : DokkaPlugin() {
          +    // create an extension point for other developers
          +    val signatureProvider by extensionPoint()
           
          -* `SourceToDocumentableTranslator` - translates sources into documentable model. `Kotlin` and `Java` sources are
          -  supported by default, but you can analyze any language as long as you can map it to `Documentables` model
          -* `DocumentableTransformer` - useful if you want to filter/map existing documentables. For instance, if you want
          -  to only include members annotated as `@PublicAPI`, you will need to implement a `DocumentableTransformer`
          -* `DocumentableToPageTranslator` - responsible for creating pages and their content. Different output formats can
          -  either use the same page structure or define their own in case it needs to be different.
          -* `PageTransformer` - useful if you need to add/remove/modify generated pages or their content (such as `css`/`js`).
          -  Plugins like `mathjax` can add js scripts to pages using this extension point. If you want all overloaded functions 
          -  to be rendered on the same page (instead of separate ones), you can also use `PageTransformer` to merge it
          -* `Render` - defines rules on what to do with pages and their content, which files to create and how to display 
          -  it properly. Output formats should use `Renderer` extension point - `HtmlRenderer`, `CommonmarkRenderer`, etc
          -
          -___
          -
          -Plugins themselves might declare additional extension points used in intermediate steps. For instance, during
          -`DocumentableToPageTranslator` phase, a plugin might declare a `signatureProvider` extension point, allowing you
          -to provide your own implementation of generating a text signature of a `Documentable` - this could be used by
          -`kotlin-as-java` plugin to display `Kotlin` sources as `Java` code.
          -
          -For a deeper dive into extension points with examples on how to create and use them, 
          -see [Extension points section](extension_points.md)
          +    // provide a default implementation
          +    val defaultSignatureProvider by extending {
          +        signatureProvider with KotlinSignatureProvider()
          +    }
          +
          +    // register our own extension in someone else's plugin and override its default
          +    val dokkaBasePlugin by lazy { plugin() }
          +    val customOutputWriter by extending {
          +        (dokkaBasePlugin.outputWriter with MyOutputWriter()
          +                override dokkaBasePlugin.fileWriter)
          +    }
          +}
          +
          +// use a registered extention, pretty much dependency injection
          +class MyExtension(val context: DokkaContext) {
          +    val signatureProvider: SignatureProvider = context.plugin().querySingle { signatureProvider }
          +
          +    fun doSomething() {
          +        signatureProvider.signature(..)
          +    }
          +}
          +
          +interface SignatureProvider {
          +    fun signature(documentable: Documentable): List
          +}
          +
          +class KotlinSignatureProvider : SignatureProvider {
          +    override fun signature(documentable: Documentable): List = listOf()
          +}
          +```
          diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points.md b/docs/src/doc/docs/developer_guide/architecture/extension_points.md
          deleted file mode 100644
          index 50a6d67fee..0000000000
          --- a/docs/src/doc/docs/developer_guide/architecture/extension_points.md
          +++ /dev/null
          @@ -1,225 +0,0 @@
          -# Extension points
          -
          -## Core extension points
          -
          -We will discuss all base extension points along with the steps, that `DokkaGenerator` does to build a documentation.
          -
          -### Setting up Kotlin and Java analysis process and initializing plugins
          -
          -The provided Maven / CLI / Gradle configuration is read.Then, all the `DokkaPlugin` classes are loaded and the extensions are created.
          -
          -No entry points here.
          -
          -### Creating documentation models
          -
          -The documentation models are created.
          -
          -This step uses `DokkaCore.sourceToDocumentableTranslator` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `SourceToDocumentableTranslator` interface:
          -
          -```kotlin
          -interface SourceToDocumentableTranslator {
          -    fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule
          -}
          -```
          -By default, two translators are created:
          -
          -* `DefaultDescriptorToDocumentableTranslator` that handles Kotlin files
          -* `DefaultPsiToDocumentableTranslator` that handles Java files
          -
          -After this step, all data from different source sets and languages are kept separately.
          -
          -If you are using Kotlin it is recommended to make use of the asynchronous version, providing you implementation of `invokeSuspending`:
          -
          -```kotlin
          -interface AsyncSourceToDocumentableTranslator : SourceToDocumentableTranslator {
          -    suspend fun invokeSuspending(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): DModule
          -}
          -```
          -
          -### Pre-merge documentation transform
          -
          -Here you can apply any transformation to model data before different source sets are merged.
          -
          -This step uses `DokkaCore.preMergeDocumentableTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `PreMergeDocumentableTransformer` interface:
          -
          -```kotlin
          -interface PreMergeDocumentableTransformer {
          -    operator fun invoke(modules: List, context: DokkaContext): List
          -}
          -```
          -
          -// TODO not true, may more now
          -By default, three transformers are created:
          -
          -* `DocumentableVisibilityFilter` that, depending on configuration, filters out all private members from declared packages
          -* `ActualTypealiasAdder` that handles Kotlin typealiases
          -* `ModuleAndPackageDocumentationTransformer` that creates documentation content for models and packages itself
          -
          -### Merging
          -
          -All `DModule` instances are merged into one.
          -
          -This step uses `DokkaCore.documentableMerger` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden.
          -
          -The extension is required to implement `DocumentableMerger` interface:
          -
          -```kotlin
          -interface DocumentableMerger {
          -    operator fun invoke(modules: Collection, context: DokkaContext): DModule
          -}
          -```
          -
          -By default, `DefaultDocumentableMerger` is created. This extension is treated as a fallback, so it can be overridden by a custom one.
          -
          -### Merged data transformation
          -
          -You can apply any transformation to already merged data
          -
          -This step uses `DokkaCore.documentableTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `DocumentableTransformer` interface:
          -
          -```kotlin
          -interface DocumentableTransformer {
          -    operator fun invoke(original: DModule, context: DokkaContext): DModule
          -}
          -```
          -
          -By default, `InheritorsExtractorTransformer` is created, that extracts inherited classes data across source sets and creates inheritance map.
          -
          -### Creating page models
          -
          -The documentable model is translated into page format, that aggregates all tha data that will be available for different pages of documentation.
          -
          -This step uses `DokkaCore.documentableToPageTranslator` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden.
          -
          -The extension is required to implement `DocumentableToPageTranslator` interface:
          -
          -```kotlin
          -interface DocumentableToPageTranslator {
          -    operator fun invoke(module: DModule): ModulePageNode
          -}
          -```
          -
          -By default, `DefaultDocumentableToPageTranslator` is created.  This extension is treated as a fallback, so it can be overridden by a custom one.
          -
          -### Transforming page models
          -
          -You can apply any transformations to paged data.
          -
          -This step uses `DokkaCore.pageTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `PageTransformer` interface:
          -
          -```kotlin
          -interface PageTransformer {
          -    operator fun invoke(input: RootPageNode): RootPageNode
          -}
          -```
          -By default, two transformers are created:
          -
          -* `PageMerger` merges some pages depending on `MergeStrategy`
          -* `DeprecatedStrikethroughTransformer` marks all deprecated members on every page
          -
          -### Rendering
          -
          -All pages are rendered to desired format.
          -
          -This step uses `DokkaCore.renderer` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden.
          -
          -The extension is required to implement  `Renderer` interface:
          -
          -```kotlin
          -interface Renderer {
          -    fun render(root: RootPageNode)
          -}
          -```
          -
          -By default, only `HtmlRenderer`, that extends basic `DefaultRenderer`, is created, but it will be registered only if configuration parameter `format` is set to `html`. Using any other value without providing valid renderer will cause Dokka to fail.
          -
          -## Multimodule page generation endpoints
          -
          -Multimodule page generation is a separate process, that declares two additional entry points:
          -
          -### Multimodule page creation
          -
          -Generation of the page that points to all module for which we generates documentation.
          -
          -This step uses `CoreExtensions.allModulePageCreator` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden.
          -
          -The extension is required to implement  `PageCreator` interface:
          -
          -```kotlin
          -interface PageCreator {
          -    operator fun invoke(): RootPageNode
          -}
          -```
          -
          -By default, `MultimodulePageCreator` is created.  This extension is treated as a fallback, so it can be replaced by a custom one.
          -
          -### Multimodule page transformation
          -
          -Additional transformation that we might apply for multimodule page.
          -
          -This step uses `CoreExtensions.allModulePageTransformer` entry point. All extensions registered using this entry point will be invoked.  Each of them is required to implement common `PageTransformer` interface.
          -
          -## Default extensions' extension points
          -
          -Default core extension points already have an implementation for providing basic Dokka functionality. All of them are declared in `DokkaBase` plugin. If you don't want this default extensions to load, all you need to do is not load Dokka base and load your plugin instead.
          -
          -```kotlin
          -val customPlugin by configurations.creating
          -
          -dependencies {
          -    customPlugin("[custom plugin load signature]")
          -}
          -tasks {
          -    val dokka by getting(DokkaTask::class) {
          -        pluginsConfig = alternativeAndIndependentPlugins
          -        outputDirectory = dokkaOutputDir
          -        outputFormat = "html"
          -        [...]
          -    }
          -}
          -```
          -
          -You will then need to implement extensions for all core extension points.
          -
          -`DokkaBase` also register several new extension points, with which you can change default behaviour of `DokkaBase` extensions. In order to use them, you need to add `dokka-base` to you dependencies:
          -
          -```kotlin
          -compileOnly("org.jetbrains.dokka:dokka-base:")
          -```
          -
          -Then, you need to obtain `DokkaBase` instance using `plugin` function:
          -
          -```kotlin
          -class SamplePlugin : DokkaPlugin() {
          -
          -    val dokkaBase = plugin()
          -
          -    val extension by extending {
          -        dokkaBase.pageMergerStrategy with SamplePageMergerStrategy order {
          -           before(dokkaBase.fallbackMerger)
          -       }
          -    }
          -}
          -
          -object SamplePageMergerStrategy: PageMergerStrategy {
          -    override fun tryMerge(pages: List, path: List): List {
          -        ...
          -    }
          -
          -}
          -```
          -
          -### Following extension points are available with base plugin
          -
          -| Entry point | Function | Required interface | Used by | Singular | Preregistered extensions
          -|---|:---|:---:|:---:|:---:|:---:|
          -| `pageMergerStrategy` |  determines what kind of pages should be merged | `PageMergerStrategy` | `PageMerger` | false | `FallbackPageMergerStrategy` `SameMethodNamePageMergerStrategy`   |
          -| `commentsToContentConverter` | transforms comment model into page content model | `CommentsToContentConverter` | `DefaultDocumentableToPageTransformer` `SignatureProvider` | true | `DocTagToContentConverter` |
          -| `signatureProvider`  | provides representation of methods signatures | `SignatureProvider` | `DefaultDocumentableToPageTransformer` | true | `KotlinSignatureProvider` |
          -| `locationProviderFactory` | provides `LocationProvider` instance that returns paths for requested elements | `LocationProviderFactory` | `DefaultRenderer` `HtmlRenderer` `PackageListService` | true | `DefaultLocationProviderFactory` which returns `DefaultLocationProvider` |
          -| `externalLocationProviderFactory` | provides `ExternalLocationProvider` instance that returns paths for elements that are not part of generated documentation | `ExternalLocationProviderFactory` | `DefaultLocationProvider` | false | `JavadocExternalLocationProviderFactory` `DokkaExternalLocationProviderFactory` |
          -| `outputWriter` | writes rendered pages files | `OutputWriter` | `DefaultRenderer` `HtmlRenderer` | true | `FileWriter`|
          -| `htmlPreprocessors` | transforms page content before HTML rendering | `PageTransformer`| `DefaultRenderer` `HtmlRenderer` | false | `RootCreator` `SourceLinksTransformer` `NavigationPageInstaller` `SearchPageInstaller` `ResourceInstaller` `StyleAndScriptsAppender` `PackageListCreator` |
          -| `samplesTransformer` | transforms content for code samples for HTML rendering | `SamplesTransformer` | `HtmlRenderer` | true | `DefaultSamplesTransformer` |
          -
          -
          diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md
          new file mode 100644
          index 0000000000..19b26b4b28
          --- /dev/null
          +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md
          @@ -0,0 +1,15 @@
          +# Base extensions
          +
          +`DokkaBase` is a base plugin which defines a number of default implementations for `CoreExtensions` as well as declares
          +its own, more high-level, extension points to be used from other plugins and output formats.
          +
          +It's very convenient to use extension points and defaults defined in `DokkaBase` if you have an idea for a simple
          +plugin that only needs to provide a few extensions or change a single place and have everything else be the default.
          +
          +`DokkaBase` is used extensively for Dokka's own output formats such as `HTML`, `Markdown`, `Mathjax` and others.
          +
          +If you are developing a plugin, you can find more information on how to use defaults and extension points from 
          +`DokkaBase` in [Introduction to Plugin development](../../plugin-development/introduction.md). 
          +
          +You can learn how to add/use/override/configure extensions and extension points in
          +[Introduction to Extensions](introduction.md), all of the information is applicable to `DokkaBase`.
          diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
          new file mode 100644
          index 0000000000..6288b7b856
          --- /dev/null
          +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
          @@ -0,0 +1,140 @@
          +# 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. For higher-level extension functions that can be used in different output formats, have a look at
          +[Base extensions](base_extensions.md) defined in `DokkaBase`.
          +
          +You can find all extensions in `CoreExtensions` class:
          +```kotlin
          +object CoreExtensions {
          +    val preGenerationCheck by coreExtensionPoint()
          +    val generation by coreExtensionPoint()
          +    val sourceToDocumentableTranslator by coreExtensionPoint()
          +    val documentableMerger by coreExtensionPoint()
          +    val documentableTransformer by coreExtensionPoint()
          +    val documentableToPageTranslator by coreExtensionPoint()
          +    val pageTransformer by coreExtensionPoint()
          +    val renderer by coreExtensionPoint()
          +    val postActions by coreExtensionPoint()
          +}
          +```
          +
          +## PreGenerationChecker
          +
          +`PreGenerationChecker` can be used to run some check to check for constraints. 
          +
          +For instance, `Javadoc` plugin does not support generating documentation for multi-platform project, so it uses
          +`PreGenerationChecker` to check for multi-platform source sets and fails if it finds any.
          +
          +## Generation
          +
          +`Generation` is responsible for generating documentation as a whole, utilizing other extension points where applicable.
          +
          +There are two implementations at the moment:
          +
          +* `AllModulesPageGeneration` - generates multimodule documentation, for instance when `dokkaHtmlMultiModule` task is
          +  invoked.
          +* `SingleModuleGeneration` - generates documentation for a single module, for instance when `dokkaHtml` task is invoked
          +
          +### AllModulesPageGeneration
          +
          +`AllModulesPageGeneration` utilizes output generated by `SingleModuleGeneration`. Under the hood it just collects all
          +pages generated for individual modules and assembles everything together, creating navigation pages between the
          +modules and so on.
          +
          +### SingleModuleGeneration stages
          +
          +When developing a feature or a plugin, it's more convenient to think that you are generating documentation for single
          +module projects, believeing that Dokka will somehow take care of the rest in multimodule environment.
          +
          +`SingleModuleGeneration` is at heart of generating documentation and utilizes other core extension points, so
          +it's worth going over its stages. 
          +
          +```mermaid
          +flowchart TD
          +    Input -- SourceToDocumentableTranslator --> 
          +    docFirst[Documentables] -- PreMergeDocumentableTransformer --> 
          +    docSecond[Documentables] -- DocumentableMerger --> 
          +    docThird[Documentables] -- DocumentableTransformer --> 
          +    docFourth[Documentables] -- DocumentableToPageTranslator --> 
          +    Pages -- PageTransformer --> 
          +    Pages -- Renderer --> 
          +    Output
          +```
          +
          +
          +#### SourceToDocumentableTranslator
          +
          +`SourceToDocumentableTranslator` translates sources into documentable model. 
          +
          +`Kotlin` and `Java` sources are supported by default, but you can analyze any language as long as you can map
          +it to `Documentables` model.
          +
          +For reference, see
          +
          +* `DefaultDescriptorToDocumentableTranslator` for `Kotlin` sources translation
          +* `DefaultPsiToDocumentableTranslator` for `Java` sources translation
          +
          +#### PreMergeDocumentableTransformer
          +
          +This extension point actually comes from `DokkaBase` and is not a core extension point, but it's used in
          +`SingleModuleGeneration` nonetheless. If you are implementing your own plugin without relying on `DokkaBase`,
          +you can either have something similar or rely on [DocumentableTransformer](#documentabletransformer).
          +
          +`PreMergeDocumentableTransformer` allows applying any transformation to 
          +[Documentables model](../data_model/documentables.md) before different source sets are merged. 
          +
          +Useful if you want to filter/map existing documentables. For instance, if you want to only include members annotated
          +as `@PublicAPI`, 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 overriden and takes care of the rest.
          +
          +#### DocumentableMerger
          +
          +`DocumentableMerger` merges all `DModule` instances into one. Only one extension is expected of this type.
          +
          +#### DocumentableTransformer
          +
          +`DocumentableTransformer` performs the same function as `PreMergeDocumentableTransformer`, but after merging source
          +sets.
          +
          +Notable example is `InheritorsExtractorTransformer`, it extracts inherited classes data across 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) section for more information and examples.
          +
          +Different output formats can either use the same page structure or define their own in case it needs to be different.
          +
          +Only a single extension of this type is expected to be registered. 
          +
          +#### PageTransformer
          +
          +`PageTransformer` is useful if you need to add/remove/modify generated pages or their content.
          +
          +Plugins like `mathjax` can add `.js` scripts to pages using this extension point. 
          +
          +If you want all overloaded functions to be rendered on the same page (instead of separate ones),
          +you can also use `PageTransformer` to delete excessive pages and combine them into a new single one.
          +
          +#### Renderer
          +
          +`Renderer` - defines rules on what to do with pages and their content, which files to create and how to display
          +it properly. 
          +
          +Output format implementations should use `Renderer` extension point. Notable examples are `HtmlRenderer`
          +and `CommonmarkRenderer`.
          +
          +## PostAction
          +
          +`PostAction` is useful for when you want to run some actions after the documentation has been generated - for instance
          +if you want to move some files around.
          +
          +[Versioning plugin](../../../user_guide/plugins/versioning-plugin.md) utilizes `PostAction` in order to move
          +generated documentation to versioned folders.
          diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md
          new file mode 100644
          index 0000000000..45c97caeaa
          --- /dev/null
          +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md
          @@ -0,0 +1,159 @@
          +# Introduction to Extension Points
          +
          +In this section you can learn how to create new extension points, how to use and configure existing ones and
          +how to query for extensions when generating documentation.
          +
          +## Declaring extension points
          +
          +If you are writing a plugin, you can create your own extension point that other developers (or you) can use later on
          +in some other part of code.
          +
          +```kotlin
          +class MyPlugin : DokkaPlugin() {
          +    val sampleExtensionPoint by extensionPoint()
          +}
          +
          +interface SampleExtensionPointInterface {
          +    fun doSomething(input: Input): List
          +}
          +
          +class Input
          +class Output
          +```
          +
          +Usually you would want to provide some default implementation(s) for your extension point, 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 implementation(s) in order to customize plugin's behaviour.
          +
          +If you want to provide a default implementation of your plugin's extension point, you can do that within the same class:
          +
          +```kotlin
          +open class MyPlugin : DokkaPlugin() {
          +    val sampleExtensionPoint by extensionPoint()
          +
          +    val defaultSampleExtension by extending {
          +        sampleExtensionPoint with DefaultSampleExtension()
          +    }
          +}
          +
          +...
          +
          +class DefaultSampleExtension : SampleExtensionPointInterface {
          +    override fun doSomething(input: Input): List = listOf()
          +}
          +```
          +
          +If you want to extend someone else's plugin (including `DokkaBase`), you can use plugin querying API to do that.
          +In the example below we will extend `MyPlugin` that was created above with our own implementation of
          +`SampleExtensionPointInterface`.
          +
          +```kotlin
          +class MyExtendedPlugin : DokkaPlugin() {
          +    val mySampleExtensionImplementation by extending {
          +        plugin().sampleExtensionPoint with MyOwnSampleExtensionImplementation()
          +    }
          +}
          +
          +class MyOwnSampleExtensionImplementation : SampleExtensionPointInterface {
          +    override fun doSomething(input: Input): List = listOf()
          +}
          +
          +```
          +
          +### Providing
          +
          +If you need to have access to `DokkaContext` in order to create an extension, you can use `providing` instead. 
          +
          +```kotlin
          +val defaultSampleExtension by extending {
          +    sampleExtensionPoint providing { context ->
          +        // can use context to query other extensions    
          +        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 for some
          +extension points, for instance documentable transformers, since all transformers do their own transformations and all
          +of them will be invoked before proceeding.
          +
          +However, a plugin can expect only a single registered extension for an extension point. In this case, you can `override`
          +existing registered extensions:
          +
          +```kotlin
          +class MyExtendedPlugin : DokkaPlugin() {
          +    private val myPlugin by lazy { plugin() }
          +
          +    val mySampleExtensionImplementation by extending {
          +        (myPlugin.sampleExtensionPoint
          +                with MyOwnSampleExtensionImplementation()
          +                override myPlugin.defaultSampleExtension)
          +    }
          +}
          +```
          +
          +### Order
          +
          +Sometimes the order in which extensions are invoked matters. This is something you can control as well using `order`:
          +
          +```kotlin
          +class MyExtendedPlugin : DokkaPlugin() {
          +    private val myPlugin by lazy { plugin() }
          +
          +    val mySampleExtensionImplementation by extending {
          +        myPlugin.sampleExtensionPoint with MyOwnSampleExtensionImplementation() 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 `applyIf`:
          +
          +```kotlin
          +class MyExtendedPlugin : DokkaPlugin() {
          +    private val myPlugin by lazy { plugin() }
          +    
          +    val mySampleExtensionImplementation by extending {
          +        myPlugin.sampleExtensionPoint with MyOwnSampleExtensionImplementation() applyIf {
          +            Random.Default.nextBoolean()
          +        }
          +    }
          +}
          +```
          +
          +## Obtaining extension instance
          +
          +After an extension point has been [created](#declaring-extension-points) and some extension has been
          +[registered](#extending-from-extension-points), you can use `query` and `querySingle` to find all or just a single
          +implementation for it.
          +
          +```kotlin
          +class MyExtension(context: DokkaContext) {
          +    // returns all registered extensions for this extension point
          +    val allSampleExtensions = context.plugin().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 this extension point
          +    val singleSampleExtensions = context.plugin().querySingle { sampleExtensionPoint }
          +    
          +    fun invoke() {
          +        allSampleExtensions.forEach { it.doSomething(Input()) }
          +        
          +        singleSampleExtensions.doSomething(Input())
          +    }
          +}
          +```
          +
          +In order to have access to context you can use [providing](#providing) when registering this as an extension.
          diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md
          index 95c74304c8..def825895d 100644
          --- a/docs/src/doc/docs/developer_guide/workflow.md
          +++ b/docs/src/doc/docs/developer_guide/workflow.md
          @@ -70,7 +70,7 @@ wish to debug a plugin which resides in a separate project.
              Prefer using smaller projects that reproduce the exact problem or behaviour you want
              since the less code you have, the easier it will be to understand what's going on. You can use example projects
              found in [dokka/examples/gradle](https://github.com/Kotlin/dokka/tree/master/examples/gradle), there's both simple 
          -   single-module and more complex multi-module/multiplatform examples.
          +   single-module and more complex multimodule/multiplatform examples.
           2. For the debug project, set `org.gradle.debug` to `true` in one of the following ways:
           
               * In your `gradle.properties` add `org.gradle.debug=true`
          diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml
          index 1965f6292d..9655b65642 100644
          --- a/docs/src/doc/mkdocs.yml
          +++ b/docs/src/doc/mkdocs.yml
          @@ -68,7 +68,10 @@ nav:
                         -  Documentables: developer_guide/architecture/data_model/documentables.md
                         -  Page & Content: developer_guide/architecture/data_model/page_content.md
                         -  Extra properties: developer_guide/architecture/data_model/extra.md
          -          - Extension points: developer_guide/architecture/extension_points.md
          +          - Extension points:
          +              - Introduction: developer_guide/architecture/extension_points/introduction.md
          +              - Core extension points: developer_guide/architecture/extension_points/core_extensions.md
          +              - Base extensions: developer_guide/architecture/extension_points/base_extensions.md
                 - Plugin development:
                     - Introduction: developer_guide/plugin-development/introduction.md
                     - Simple plugin tutorial: developer_guide/plugin-development/simple-plugin-guide.md
          diff --git a/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt b/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt
          index a8db6668ea..9128e2fa5c 100644
          --- a/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt
          +++ b/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt
          @@ -8,6 +8,7 @@ import org.jetbrains.dokka.plugability.DokkaContext
           
           class HideTagDocumentableFilter(val dokkaContext: DokkaContext) :
               SuppressedByConditionDocumentableFilterTransformer(dokkaContext) {
          +
               override fun shouldBeSuppressed(d: Documentable): Boolean =
                   d.documentation.any { (_, docs) -> docs.dfs { it is CustomTagWrapper && it.name.trim() == "hide" } != null }
          -}
          \ No newline at end of file
          +}
          diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt
          index 81dea14bda..e102f52a55 100644
          --- a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt
          +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt
          @@ -2,7 +2,10 @@ package renderers.gfm
           
           import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer
           import org.jetbrains.dokka.links.DRI
          -import org.jetbrains.dokka.pages.*
          +import org.jetbrains.dokka.pages.ContentEmbeddedResource
          +import org.jetbrains.dokka.pages.ContentKind
          +import org.jetbrains.dokka.pages.DCI
          +import org.jetbrains.dokka.pages.TextStyle
           import org.junit.Assert.assertEquals
           import org.junit.jupiter.api.Test
           import renderers.RawTestPage
          @@ -309,10 +312,8 @@ class SimpleElementsTest : GfmRenderingOnlyTestBase() {
                                       text("Col2")
                                   }
                                   row {
          -                            group(styles = setOf(TextStyle.Paragraph)) {
          -                                text("Text1", styles = setOf(TextStyle.Bold))
          -                                text("Text2", styles = setOf(TextStyle.Italic))
          -                            }
          +                            text("Text1")
          +                            text("Text2")
                                   }
                               }
                           }
          
          From bbc1b09485f1907af4f1d5c55cd76f3801e97a3a Mon Sep 17 00:00:00 2001
          From: Ignat Beresnev 
          Date: Thu, 2 Jun 2022 13:37:42 +0200
          Subject: [PATCH 3/9] Add an example plugin and "creating a plugin" tutorial
          
          ---
           CODE_OF_CONDUCT.md                            |   4 +
           docs/src/doc/docs/community/plugins-list.md   |  88 ++++--
           .../architecture/architecture_overview.md     |  17 +-
           .../extension_points/base_extensions.md       |   5 +-
           .../extension_points/core_extensions.md       |   2 +-
           .../extension_points/introduction.md          |   2 +-
           .../doc/docs/developer_guide/introduction.md  |  17 +-
           .../plugin-development/introduction.md        |  75 ++---
           .../plugin-development/simple-plugin-guide.md | 282 +++++++++++++++++-
           docs/src/doc/docs/developer_guide/workflow.md |   5 +-
           docs/src/doc/docs/dokka_colors.css            |   2 +-
           docs/src/doc/docs/expand_navigation.js        |  14 -
           docs/src/doc/docs/images/mathjax_demo.png     | Bin 0 -> 31853 bytes
           docs/src/doc/docs/index.md                    |  16 +-
           docs/src/doc/mkdocs.yml                       |  30 +-
           examples/plugin/hide-internal-api/LICENSE     | 202 +++++++++++++
           examples/plugin/hide-internal-api/README.md   |   1 +
           .../plugin/hide-internal-api/build.gradle.kts | 115 +++++++
           .../hide-internal-api/gradle.properties       |   1 +
           .../gradle/wrapper/gradle-wrapper.jar         | Bin 0 -> 59203 bytes
           .../gradle/wrapper/gradle-wrapper.properties  |   5 +
           examples/plugin/hide-internal-api/gradlew     | 185 ++++++++++++
           examples/plugin/hide-internal-api/gradlew.bat |  89 ++++++
           .../hide-internal-api/settings.gradle.kts     |   1 +
           .../dokka/plugin/HideInternalApiPlugin.kt     |  34 +++
           ...rg.jetbrains.dokka.plugability.DokkaPlugin |   1 +
           .../dokka/plugin/HideInternalApiPluginTest.kt |  44 +++
           27 files changed, 1103 insertions(+), 134 deletions(-)
           create mode 100644 CODE_OF_CONDUCT.md
           delete mode 100644 docs/src/doc/docs/expand_navigation.js
           create mode 100644 docs/src/doc/docs/images/mathjax_demo.png
           create mode 100644 examples/plugin/hide-internal-api/LICENSE
           create mode 100644 examples/plugin/hide-internal-api/README.md
           create mode 100644 examples/plugin/hide-internal-api/build.gradle.kts
           create mode 100644 examples/plugin/hide-internal-api/gradle.properties
           create mode 100644 examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.jar
           create mode 100644 examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.properties
           create mode 100755 examples/plugin/hide-internal-api/gradlew
           create mode 100644 examples/plugin/hide-internal-api/gradlew.bat
           create mode 100644 examples/plugin/hide-internal-api/settings.gradle.kts
           create mode 100644 examples/plugin/hide-internal-api/src/main/kotlin/org/example/dokka/plugin/HideInternalApiPlugin.kt
           create mode 100644 examples/plugin/hide-internal-api/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
           create mode 100644 examples/plugin/hide-internal-api/src/test/kotlin/org/example/dokka/plugin/HideInternalApiPluginTest.kt
          
          diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
          new file mode 100644
          index 0000000000..d97771288f
          --- /dev/null
          +++ b/CODE_OF_CONDUCT.md
          @@ -0,0 +1,4 @@
          +## Code of Conduct
          +
          +This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct).
          +Please make sure you read it. 
          diff --git a/docs/src/doc/docs/community/plugins-list.md b/docs/src/doc/docs/community/plugins-list.md
          index 11e8933c0c..dc88ade3cb 100644
          --- a/docs/src/doc/docs/community/plugins-list.md
          +++ b/docs/src/doc/docs/community/plugins-list.md
          @@ -6,19 +6,18 @@ TODO add a link on how to apply plugins
           
           ### Javadoc (Alpha)
           
          -Javadoc plugin adds a Javadoc output format looks like Java's Javadoc, but it's for the most part
          +Javadoc plugin adds a `Javadoc` output format that looks like Java's `Javadoc`, but it's for the most part
           a lookalike, so you may experience problems if you try to use it with a tool that expects native
          -Javadocs generated by Java.
          +`Javadocs` generated by `Java`.
           
          -Javadoc plugin does not support multiplatform projects and does not have a multi-module task.
          +`Javadoc` plugin does not support multiplatform projects and does not have a multi-module task.
           
          -Javadoc plugin is shipped with Dokka, so you can start using it right away with one of the following tasks:
          +`Javadoc` plugin is shipped with `Dokka`, so you can start using it right away with one of the following tasks:
           
          -* `dokkaJavadoc` - builds Javadoc documentation for single-module projects or for a specific module.
          -* `dokkaJavadocCollector` - collects generated Javadoc documentation from submodules and assembles it together.
          +* `dokkaJavadoc` - builds `Javadoc` documentation for single-module projects or for a specific module.
          +* `dokkaJavadocCollector` - collects generated `Javadoc` documentation from submodules and assembles it together.
           
          -* TODO add an example or a screenshot
          -* TODO copy description to the source code 
          +`Javadoc` plugin has its own signature provider that essentially translates `Kotlin` signatures to `Java` ones.
           
           **This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to
           [report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see.
          @@ -27,16 +26,46 @@ Javadoc plugin is shipped with Dokka, so you can start using it right away with
           
           ### GFM (Alpha)
           
          -GFM plugins adds an ability to generate documentation in GitHub flavoured Markdown format. Supports both
          -multi-module and multiplatform projects, and is shipped together with Dokka, so you can start using it
          +`GFM` plugins adds an ability to generate documentation in `GitHub flavoured Markdown` format. Supports both
          +multimodule and multiplatform projects, and is shipped together with `Dokka`, so you can start using it
           right away with one of the following tasks:
           
           * `dokkaGfm` - generate documentation for a non multi-module project or one specific module.
           * `dokkaGfmMultiModule` - generate documentation for a multi-module project, assemble it together and
             generate navigation page/menu for all the modules.
           
          -* TODO add an example or a screenshot
          -* TODO copy description to the source code
          +Example:
          +
          +___
          +
          +//[dokka-debug-kts](#gfm)/[org.jetbrains.dokka.test](#gfm)/[MyClass](#gfm)
          +
          +# MyClass
          +
          +[jvm]
          +class [MyClass](#gfm)
          +
          +KDoc that describes this class
          +
          +## Constructors
          +
          +| | |
          +|---|---|
          +| [MyClass](#gfm) | [jvm]
          fun [MyClass](#gfm)() | + +## Functions + +| Name | Summary | +|------------------|---| +| [function](#gfm) | [jvm]
          fun [function](#gfm)(): [String](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
          KDoc comment on top of this function | + +## Properties + +| Name | Summary | +|---|------------------------------------------------------------------------------------------------------------------------------------------------| +| [property](#gfm) | [jvm]
          val [property](#gfm): [String](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
          KDoc comment for a property | + +___ **This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to [report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see. @@ -45,17 +74,14 @@ right away with one of the following tasks: ### Jekyll (Alpha) -Jekyll plugins adds an ability to generate documentation in Jekyll flavoured Markdown format. Supports both -multi-module and multiplatform projects, and is shipped together with Dokka, so you can start using it +`Jekyll` plugins adds an ability to generate documentation in `Jekyll flavoured Markdown` format. Supports both +multi-module and multiplatform projects, and is shipped together with `Dokka`, so you can start using it right away with one of the following tasks: * `dokkaJekyll` - generate documentation for a non multi-module project or one specific module. * `dokkaJekyllMultiModule` - generate documentation for a multi-module project, assemble it together and generate navigation page/menu for all the modules. -* TODO add an example or a screenshot -* TODO copy description to the source code - **This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to [report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see. @@ -65,44 +91,44 @@ right away with one of the following tasks: ### Mathjax -[MathJax](https://docs.mathjax.org/) allows you to include mathematics in your web pages. Dokka MathJax plugin +[MathJax](https://docs.mathjax.org/) allows you to include mathematics in your web pages. `Dokka` `MathJax` plugin adds an ability to render mathematics from source code comments. -If MathJax plugin encounters `@usesMathJax` KDoc tag, it adds `MathJax.js` 2.7.6 with `config=TeX-AMS_SVG` -to generated HTML pages. +If `MathJax` plugin encounters `@usesMathJax` `KDoc` tag, it adds `MathJax.js` 2.7.6 with `config=TeX-AMS_SVG` +to generated `HTML` pages. Usage example: ```kotlin /** * @usesMathJax * - * \(\sqrt{3x-1}+(1+x)^2\) + * Some math \(\sqrt{3x-1}+(1+x)^2\) */ -class Clazz {} +class Foo {} ``` -TODO add a screenshot +Which results in: + +![Mathjax demo](../images/mathjax_demo.png){ width="400" } [Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/mathjax) ### Mermaid [Mermaid JS](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code. -Mermaid plugin allows rendering such diagrams and visualizations found in source code documentation. +`Mermaid` plugin allows rendering such diagrams and visualizations found in source code documentation. For more information and examples, see [Html Mermaid Dokka plugin](https://github.com/glureau/dokka-mermaid) repository on GitHub. ### Kotlin as Java -With Kotlin as Java plugin applied, all Kotlin signatures will be rendered as Java signatures. For instance, -`fun foo(bar: Bar): Baz` will be rendered as `public final Baz foo(Bar bar)`. +With `Kotlin as Java` plugin applied, all `Kotlin` signatures will be rendered as `Java` signatures. -Kotlin as Java plugin is published to maven central as a -[separate artifact](https://mvnrepository.com/artifact/org.jetbrains.dokka/kotlin-as-java-plugin): -`org.jetbrains.dokka:kotlin-as-java-plugin:1.6.21`. See instructions on how to apply it. +For instance, `fun foo(bar: Bar): Baz` will be rendered as `public final Baz foo(Bar bar)`. -TODO insert applying link -TODO insert screenshot +`Kotlin as Java` plugin is published to maven central as a +[separate artifact](https://mvnrepository.com/artifact/org.jetbrains.dokka/kotlin-as-java-plugin): +`org.jetbrains.dokka:kotlin-as-java-plugin:1.6.21`. [Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/kotlin-as-java) diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md index 5fde9f59ec..17bd7568b8 100644 --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -63,11 +63,12 @@ class MyPlugin : DokkaPlugin() { signatureProvider with KotlinSignatureProvider() } - // register our own extension in someone else's plugin and override its default + // register our own extension in another plugin and override its default val dokkaBasePlugin by lazy { plugin() } - val customOutputWriter by extending { - (dokkaBasePlugin.outputWriter with MyOutputWriter() - override dokkaBasePlugin.fileWriter) + val multimoduleLocationProvider by extending { + (dokkaBasePlugin.locationProviderFactory + providing MultimoduleLocationProvider::Factory + override dokkaBasePlugin.locationProvider) } } @@ -88,3 +89,11 @@ class KotlinSignatureProvider : SignatureProvider { override fun signature(documentable: Documentable): List = listOf() } ``` + +## Historical context + +This is a second iteration of Dokka that was built from scratch. + +If you want to learn more about why Dokka has been designed this way, watch this great talk by Paweł Marks: +[New Dokka - Designed for Fearless Creativity](https://www.youtube.com/watch?v=OvFoTRhqaKg). The general principles +and general architecture are the same, although it may be outdated in some areas, so please double-check. diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md index 19b26b4b28..32bb578a1f 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md @@ -8,8 +8,5 @@ plugin that only needs to provide a few extensions or change a single place and `DokkaBase` is used extensively for Dokka's own output formats such as `HTML`, `Markdown`, `Mathjax` and others. -If you are developing a plugin, you can find more information on how to use defaults and extension points from -`DokkaBase` in [Introduction to Plugin development](../../plugin-development/introduction.md). - You can learn how to add/use/override/configure extensions and extension points in -[Introduction to Extensions](introduction.md), all of the information is applicable to `DokkaBase`. +[Introduction to Extensions](introduction.md), all the information is applicable to `DokkaBase` plugin as well. diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md index 6288b7b856..28f1564d6d 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md @@ -6,7 +6,7 @@ These extension points are plugin and output format independent, meaning it's th low-level as can get. For higher-level extension functions that can be used in different output formats, have a look at [Base extensions](base_extensions.md) defined in `DokkaBase`. -You can find all extensions in `CoreExtensions` class: +You can find all core extensions in `CoreExtensions` class: ```kotlin object CoreExtensions { val preGenerationCheck by coreExtensionPoint() diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md index 45c97caeaa..1751ecefce 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md @@ -1,4 +1,4 @@ -# Introduction to Extension Points +# Overview In this section you can learn how to create new extension points, how to use and configure existing ones and how to query for extensions when generating documentation. diff --git a/docs/src/doc/docs/developer_guide/introduction.md b/docs/src/doc/docs/developer_guide/introduction.md index 2cd53b6634..a15a0f2540 100644 --- a/docs/src/doc/docs/developer_guide/introduction.md +++ b/docs/src/doc/docs/developer_guide/introduction.md @@ -1,4 +1,19 @@ -# Introduction to Dokka Plugin development +# Developer guides +The purpose of `Developer guides` section is to get you acquainted with Dokka's internals so that you can start developing +your own plugins or contributing features and fixes to Dokka itself. +If you want to start hacking on Dokka right away, the only thing you need to be aware of is the +[general workflow](workflow.md), it will teach you how to build, debug and test Dokka. +If you want to get into plugin development quick, see +[Introduction to plugin development](plugin-development/introduction.md). + +If you have time to spare and want to know more about Dokka's internals, its architecture and capabilities, follow +[Architecture overview](architecture/architecture_overview.md) and subsequent sections. + +Having read through all the developer guides, you'll have a pretty good unrestanding of Dokka and how to develop +for it. + +If you have any questions, feel free to get in touch with maintainers via [Slack](../community/slack.md) or +[GitHub](https://github.com/kotlin/dokka). diff --git a/docs/src/doc/docs/developer_guide/plugin-development/introduction.md b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md index 248f4fc2fb..d1314fcfad 100644 --- a/docs/src/doc/docs/developer_guide/plugin-development/introduction.md +++ b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md @@ -1,8 +1,20 @@ -## Configuration +# Plugin Development -tldr: you can use a convenient [plugin template](https://github.com/Kotlin/dokka-plugin-template) to speed up the setup. +In order to have an easier time developing plugins, it's a good idea to go through +[Dokka's internals](../architecture/architecture_overview.md) to learn more about +[extensions](../architecture/extension_points/introduction.md) and +[data model](../architecture/data_model/documentables.md). -Dokka requires configured `Kotlin plugin` and `dokka-core` dependency. +## Setup + +### Template + +The easiest way to start is to use a convenient [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). +It has pre-configured dependencies, publishing and signing of your artifacts. + +### Manual + +At a bare minimum, Dokka requires `Kotlin Gradle Plugin` and `dokka-core` dependencies: ```kotlin plugins { @@ -18,51 +30,30 @@ tasks.withType { } ``` -### Registering extension point +In order to load a plugin into Dokka, your class must extend `DokkaPlugin` class. A fully qualified name of that class +must be placed in a file named `org.jetbrains.dokka.plugability.DokkaPlugin` under `resources/META-INF/services`. +All instances are automatically loaded during Dokka setup using `java.util.ServiceLoader`. -You can register your own extension point using `extensionPoint` function declared in `DokkaPlugin` class +## Extension points -```kotlin -class SamplePlugin : DokkaPlugin() { - val extensionPoint by extensionPoint() -} +Dokka provides a set of entry points for which you can create your own implementations. If you are not sure which +extension point to use, have a look at [core extensions](../architecture/extension_points/core_extensions.md) and +[base extensions](../architecture/extension_points/base_extensions.md) - it might give you a general direction. -interface SampleExtensionPointInterface -``` +You can learn how to declare extension points and use extensions in +[Introduction to Extension points](../architecture/extension_points/introduction.md). -### Obtaining extension instance +In case no suitable extension point exists for your use case, do share the details - it might be added in future +versions of Dokka. -All registered plugins are accessible with `DokkaContext.plugin` function. All plugins that extends `DokkaPlugin` can use `DokkaPlugin.plugin` function, that uses underlying `DokkaContext` instance. If you want to pass context to your extension, you can obtain it using aforementioned `providing` infix function. +## Example -With plugin instance obtained, you can browse extensions registered for this plugins' extension points using `querySingle` and `query` methods: +You can follow [a simple plugin guide](simple-plugin-guide.md) which covers creation of a simple plugin: hide members +annotated with `@Internal` annotation, i.e exclude them from generated documentation. -```kotlin - context.plugin().query { htmlPreprocessors } - context.plugin().querySingle { samplesTransformer } -``` +Fore more practical examples, have a look at sources of [community plugins](../../community/plugins-list.md). -You can also browse `DokkaContext` directly, using `single` and `get` methods: +## Help -```kotlin -class SamplePlugin : DokkaPlugin() { - - val extensionPoint by extensionPoint() - val anotherExtensionPoint by extensionPoint() - - val extension by extending { - extensionPoint with SampleExtension() - } - - val anotherExtension by extending { - anotherExtensionPoint providing { context -> - AnotherSampleExtension(context.single(extensionPoint)) - } - } -} - -interface SampleExtensionPointInterface -interface AnotherSampleExtensionPointInterface - -class SampleExtension: SampleExtensionPointInterface -class AnotherSampleExtension(sampleExtension: SampleExtensionPointInterface): AnotherSampleExtensionPointInterface -``` +If you have any further questions, feel free to get in touch with maintainers via [Slack](../../community/slack.md) or +[GitHub](https://github.com/kotlin/dokka). diff --git a/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md b/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md index 1bac4547fd..098b11c861 100644 --- a/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md +++ b/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md @@ -1,30 +1,282 @@ -## Building sample plugin +# Simple plugin guide -In order to load a plugin into Dokka, your class must extend `DokkaPlugin` class. A fully qualified name of that class must be placed in a file named `org.jetbrains.dokka.plugability.DokkaPlugin` under `resources/META-INF/services`. -All instances are automatically loaded during Dokka setup using `java.util.ServiceLoader`. +We'll go over creating a simple plugin that covers a very common use case: generate documentation for everything except +for members annotated with a custom `@Internal` annotation - they should be hidden. -Dokka provides a set of entry points, for which user can create their own implementations. They must be delegated using `DokkaPlugin.extending(definition: ExtendingDSL.() -> Extension)` function,that returns a delegate `ExtensionProvider` with supplied definition. +Plugin functionality will be tested on a simple project with the following code: -To create a definition, you can use one of two infix functions`with(T)` or `providing( (DokkaContext) -> T)` where `T` is the type of an extended endpoint. You can also use infix functions: +```kotlin +package org.jetbrains.dokka.internal.test + +annotation class Internal + +fun shouldBeVisible() {} + +@Internal +fun shouldBeExcludedFromDocumentation() {} +``` + +Expected behavior: function `shouldBeExcludedFromDocumentation` should not be visible in generated documentation. + +Full plugin code can be found in Dokka's examples under +[hide-internal-api](https://github.com/Kotlin/dokka/examples/plugin/hide-internal-api). + +## Preparing the project + +We'll begin by using [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). Press the +green `Use this template` button and open this project in an IDE of your choice (IntelliJ IDEA is recommended). + +We can start by changing `template` package and renaming the pre-made `MyAwesomeDokkaPlugin` class to something +of our own. + +For instance, `org.example.dokka.plugin.ExposePublicApiPlugin`: + +```kotlin +package org.example.dokka.plugin + +import org.jetbrains.dokka.plugability.DokkaPlugin + +class HideInternalApiPlugin : DokkaPlugin() { + +} +``` + +After you do that, make sure to update the path to this class in +`resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin`: +```kotlin +org.example.dokka.plugin.HideInternalApiPlugin +``` + +At this point you can also change project name in `settings.gradle.kts` and `groupId` in `build.gradle.kts`. + +## Extending Dokka + +After preparing the project we can start extending Dokka with our own extension. -* `applyIf( () -> Boolean )` to add additional condition specifying whether or not the extension should be used -* `order((OrderDsl.() -> Unit))` to determine if your extension should be used before or after another particular extension for the same endpoint -* `override( Extension )` to override other extension. Overridden extension won't be loaded and overridding one will inherit ordering from it. +Having read through [Core extensions](../architecture/extension_points/core_extensions.md) it's clear that we need +a `PreMergeDocumentableTransformer` in order to filter out undesired documentables. Moreover, it mentioned a convenient +abstract transformer `SuppressedByConditionDocumentableFilterTransformer`, so we can begin by implementing it. -Following sample provides custom translator object as a `DokkaCore.sourceToDocumentableTranslator` +Create a new class, place it next to your plugin and implement the abstract class method. You should end up with this: ```kotlin -package org.jetbrains.dokka.sample +package org.example.dokka.plugin +import org.jetbrains.dokka.base.transformers.documentables.SuppressedByConditionDocumentableFilterTransformer +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.DokkaPlugin -class SamplePlugin : DokkaPlugin() { - extension by extending { - DokkaCore.sourceToDocumentableTranslator with CustomSourceToDocumentableTranslator +class HideInternalApiPlugin : DokkaPlugin() {} + +class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(context) { + override fun shouldBeSuppressed(d: Documentable): Boolean { + return false + } +} +``` + +Now we somehow need to find all annotations applied to `d: Documentable` and see if our annotation is present. However, +it's not very clear how to do that. What usually helps is stopping in debugger and having a look at what fields +and values a given `Documentable` has. + +To do that, we'll need to register our extension point first, then we can publish our plugin and set a breakpoint. + +Having read through [Introduction to extensions](../architecture/extension_points/introduction.md), we now know +how to register our extensions: + +```kotlin +class HideInternalApiPlugin : DokkaPlugin() { + val myFilterExtension by extending { + plugin().preMergeDocumentableTransformer providing ::HideInternalApiTransformer } } +``` + +At this point we're ready to debug our plugin locally. + +## Debugging -object CustomSourceToDocumentableTranslator: SourceToDocumentableTranslator { - override fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule +Please read through [Debugging Dokka](../workflow.md#debugging-dokka), it goes over the same steps in more detail +and with examples. Below you will find rough instructions. + +First, let's begin by publishing our plugin to `mavenLocal()`. + +```bash +./gradlew publishToMavenLocal +``` + +This will publish your plugin under the package, project name and version that you've specified in your +`build.gradle.kts`. In our case it's `org.example:hide-internal-api:1.0-SNAPSHOT`. + +Open a debug project of your choosing and add our plugin to dependencies: + +```kotlin +dependencies { + dokkaPlugin("org.example:hide-internal-api:1.0-SNAPSHOT") } ``` + +Next, in that project let's run `dokkaHtml` with debug enabled + +```bash +./gradlew clean dokkaHtml -Dorg.gradle.debug=true --no-daemon +``` + +Switch to the plugin project, set a breakpoint inside `shouldBeSuppressed` and run jvm remote debug. + +If you've done everything correctly, it should stop in debugger and you should be able to observe the values contained +inside `d: Documentable`. + +## Implementing plugin logic + +Now that we've stopped at our breakpoint, let's skip until we see `shouldBeExcludedFromDocumentation` function in the +place of `d: Documentable` (observe the changing `name` property). + +Looking at what's inside the object, you might notice it has 3 values in `extra`, one of which is `Annotations`. +Sounds like something we need! + +Having poked around, you come up with the following monstrosity of a code for determining if a given documentable has +`@Internal` annotation (it can of course be refactored.. later): + +```kotlin +override fun shouldBeSuppressed(d: Documentable): Boolean { + val annotations: List = + (d as? WithExtraProperties<*>) + ?.extra + ?.allOfType() + ?.flatMap { it.directAnnotations.values.flatten() } + ?: emptyList() + + return annotations.any { isInternalAnnotation(it) } +} + +private fun isInternalAnnotation(annotation: Annotations.Annotation): Boolean { + return annotation.dri.packageName == "org.jetbrains.dokka.expose.test" + && annotation.dri.classNames == "Internal" +} +``` + +Seems like we're done with writing our plugin and can begin testing it. + +## Manual testing + +At this point you should be looking a plugin that looks roughly like this: + +```kotlin +package org.example.dokka.plugin + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.transformers.documentables.SuppressedByConditionDocumentableFilterTransformer +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin + +class HideInternalApiPlugin : DokkaPlugin() { + val myFilterExtension by extending { + plugin().preMergeDocumentableTransformer providing ::HideInternalApiTransformer + } +} + +class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(context) { + + override fun shouldBeSuppressed(d: Documentable): Boolean { + val annotations: List = + (d as? WithExtraProperties<*>) + ?.extra + ?.allOfType() + ?.flatMap { it.directAnnotations.values.flatten() } + ?: emptyList() + + return annotations.any { isInternalAnnotation(it) } + } + + private fun isInternalAnnotation(annotation: Annotations.Annotation): Boolean { + return annotation.dri.packageName == "org.jetbrains.dokka.internal.test" + && annotation.dri.classNames == "Internal" + } +} +``` + +Bump plugin version in `gradle.build.kts`, publish it to maven local, run `dokkaHtml` and see if it works! It should. +You should **NOT** be able to see `shouldBeExcludedFromDocumentation` function in generated documentation. + +Manual testing is cool and all, but wouldn't it be better if we could somehow write unit tests for it? Indeed! + +## Unit testing + +You might've noticed that plugin template comes with a pre-made test class. Feel free to move it to another package +and rename it. + +We are mostly interested in a single test case - non-annotated functions should be visible, and annotated ones should +be hidden. + +Plugin API comes with a set of convenient test utilities that are used to test Dokka itself, so it covers a wide range +of use cases. When in doubt, see Dokka's tests for reference. + +Below you will find a complete unit tests that passes, and the main takeaways below that. + +```kotlin +package org.example.dokka.plugin + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.Test +import kotlin.test.assertEquals + +class HideInternalApiPluginTest : BaseAbstractTest() { + @Test + fun `should hide annotated functions`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + val hideInternalPlugin = HideInternalApiPlugin() + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package org.jetbrains.dokka.internal.test + | + |annotation class Internal + | + |fun shouldBeVisible() {} + | + |@Internal + |fun shouldBeExcludedFromDocumentation() {} + """.trimMargin(), + configuration = configuration, + pluginOverrides = listOf(hideInternalPlugin) + ) { + preMergeDocumentablesTransformationStage = { modules -> + val testModule = modules.single { it.name == "root" } + val testPackage = testModule.packages.single { it.name == "org.jetbrains.dokka.internal.test" } + + val packageFunctions = testPackage.functions + assertEquals(1, packageFunctions.size) + assertEquals("shouldBeVisible", packageFunctions[0].name) + } + } + } +} +``` + +Note that the package of the test code (inside `testInline` function) is the same as the package that we have +hardcoded in our plugin. Make sure to change that if you are following along, otherwise it will fail. + +Things to note and remember: + +1. Your test class should extend `BaseAbstractTest`, which contains base utility methods for testing. +2. You can configure Dokka to your liking, enable some specific settings, configure source sets, etc +3. `testInline` function is the main entry point for unit tests +4. You can pass plugins to be used in tests, notice `pluginOverrides` parameter +5. You can test Dokka on different stages of generating documentation, the main ones being documentables model + generation, pages generation and output generation. Since we implemented our plugin to work during + `PreMergeDocumentableTransformer` stage, we can test it on the same level. +6. You will need to write asserts using the model of whatever stage you choose. For documentables transformation stage + it's documentables, for page generation stage you would have pages, and for output you can have `.html` pages + that you will need to parse with JSoup (there are also utilities for that). diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md index def825895d..ef13ddaaaf 100644 --- a/docs/src/doc/docs/developer_guide/workflow.md +++ b/docs/src/doc/docs/developer_guide/workflow.md @@ -19,8 +19,9 @@ Building Dokka is pretty straightforward, with one small caveat: when you run `. integration tests as well, which might take some time and will consume a lot of RAM, so you would usually want to exclude integration tests when building locally. -!!! example - `./gradlew build -x integrationTest` +```shell +./gradlew build -x integrationTest +``` Unit tests which are run as part of `build` should not take much time, but you can also skip it with `-x test`. diff --git a/docs/src/doc/docs/dokka_colors.css b/docs/src/doc/docs/dokka_colors.css index be10a56070..7816457f72 100644 --- a/docs/src/doc/docs/dokka_colors.css +++ b/docs/src/doc/docs/dokka_colors.css @@ -1,3 +1,3 @@ -.md-header { +.md-header, .md-tabs { background-color: #bc0fd4; } diff --git a/docs/src/doc/docs/expand_navigation.js b/docs/src/doc/docs/expand_navigation.js deleted file mode 100644 index cef32f2ca0..0000000000 --- a/docs/src/doc/docs/expand_navigation.js +++ /dev/null @@ -1,14 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - let nav = document.getElementsByClassName("md-nav"); - for(let i = 0; i < nav.length; i++) { - if (nav.item(i).getAttribute("data-md-level")) { - nav.item(i).style.display = 'block'; - nav.item(i).style.overflow = 'visible'; - } - } - - nav = document.getElementsByClassName("md-nav__toggle"); - for(let i = 0; i < nav.length; i++) { - nav.item(i).checked = true; - } -}); diff --git a/docs/src/doc/docs/images/mathjax_demo.png b/docs/src/doc/docs/images/mathjax_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..9b14a704ddb24affbb35e0d3e3a19f54d73375e3 GIT binary patch literal 31853 zcmeFZXH-*N)GmrI78DT_5$P|ApdcW<2`DH?S3037z4y=qDpe3r5Rl$GA@rW0A|kyf zQUlTn1Sui3gtPGb&NuFl`|qAR?sx7ukukQ}$=+-2wdQ)}GoLwE-aXe;rn$s?iGqTH zM&;QPZ3+s?Z3>Ei(y9IdXTHC?u>}riZ69kqrl6>bqdtCh4!r-@@|m^<1%*F11;y)7 z3W`H;>h%f*1@r+0#o9{>3hB=j6pU`^%`YB;56)YtDLZ=G8;cVzP;uBYaNh0X+A7*e}R zN4LoPv}4;4vG`9?S@AEzL`&rG*>h*F(%sYk=PVV~*=zqMeo*koI;AaltTanlCpcBV zx!ATD!KmhKlv#W&vgbg{-|qI_w<jTX5wo zG0>vhI4jWS-$SbwG$$r#M*5oP(!490BT{a>%-EMw{<8-6NUk*pYowgLaEkvEZ-?{C z+Avy}&amD}a_iw7`8Lm(1C$sU+m~IhrrOiZ$7_<7pNaVDIc6tpz0Ba-+;VT_gL{=; zbIsN}*Wl*uN4s6IubdS^{wbl$=B`Ckx@AYJB=_2=P+gtLF%`Mn|8*q+ORm(5&u2>b zF)isaNbnY|srEGY+r(9Sm=XopIFLkdO;^#Fop<*Xs1jAZT+B(YT%%Nep+Xsa+s1miM6Xn+{ZM8%>^Hr3 zXy1UP^{Sg9&XX}dl#Xqp&S4)7CiX?A=0#5FnZc!San4J3gIdtrAr$f)!bokC+eInU z7*^!WhroNL;9;xw3g4Jbe}lC;Wu$CC_-13y|B5lBHHo{zCc^-`g;ptp+LdEi$gM+g zfvnD`2a7+hZcNq*Gi%7+V$LZFc;UV}p&=7W*PzK-7HNnTD(~AlX_g>(*dQu0{OIp> zh!N!V+jgWb&%=1Z_Jt| z>MyxW@_zPY(NH3HAjZ8GC|RT2!5DV?$;F)!?G2ldMpIV)iD}Fr{h={*Sp{G)xYDll?{@)-o+0K zUqITjc~$7^u0%(%)Yt%%fBeE7xu+9^>1q2T`ln!{<(D*ms-^IX#x(DrHP)6=gUs)r zdK4lBG=@J8;OVo=O_I$GM^`#7tVL@|UMZ5e>9Zm4!L_6g`N41?cqT|r@rbz2Jo9xg zs`(PBf;7}gmJGNjt45A)Npp;g)YNfhXP8~qLBJY6d)X6yY{b)jeM9!a21Vv^Ib7!=<6)khCh1=uB@x8LsW`J z%ZxW%Ct(kkbWI+=sG?JV>(@CmJoIqGLUu@7I?H=XY;ju8gwpyP>ZBVBf|vK4i>4n= zv@cOi-m_<#B(g@uXnmmnoS@G#bkFJd=bNh;-%16JJ4$0#jimTVFpkeMd22O{*K`bA zCpbetOWB)PEj#EB@ZF7%);Bb{75cnvYy)y3j&;h%?j#diek4WkSekCY4ol~M?5wTu zUulDobRbKKYRMvDbm#N7I($%-_X9geFJ??M)S1bO%gr^AhDz*Hp5D2$t=t-2qXTeF z_Lg)Ei_mkp{yKVE=D^kSnrajWlqI+>8AlG7^eu37>mdDx3H^ROB zLai(2urJPliZj!17vH>YrQk8qO*0uvC&C?Dvd4cv>qXIkDX_pWIx=cz0}`Q*wAZzB zdpWjYWwZG6gyfE^SV^ebo*&G99k8tKINQYQiX9tY#nSdAaILO%{h}NSwz9IO&T(M4 zwm;iy)UOF?>Zg5@^^iXKLz?+>6K!q|Y|@z^#S6R4vias??~>kgkMM26_(NAPw=I4* zzDgwH*Yja(Z(0Bh22Tcafyvd^%rqI}eEh~Tg7)SxLb*=Q;Q=!(hdgrAOdFcza~7ycyD!R z@yExIQHQ$e&I&V3x@x?(Aj^9t&L_T|EUZK!XW~US&edVpqu6xWEW?U2I)!50TvObK zt09N)Z;mQc7*JNtR0$&%$i(O|DZg`x75~a_?30Sv+9nN!gci4zdoW!G1G(iUK9Wnj z9JT{kIjBOxsD_ znj9<(&Ob=#N>9v4OUwvZ*-X;+KAA1t#-L5lvc2y#?+M@R9}^3=DP2+ z7%p!yU<|j`9Owmqu~KXeFZcMTn{`ieP!enVn-Q_b63>|4|Ik?$NSF3`wJLTR$cnoH=QV8?gQI~ow^SXAw8aP_c)AKpkD7Z$pd`?d@_nj|>g!`y#Y9tRNH3zbv z1l_giE8Iv7vgzvL`&BQ)yQDic?O9mwYV5bb)bwx3=_!;(Kh<95DQc5{cSpr( z-x{+g`vQFd;`$)-A${<+df@9UElsmG3H22Q3e)DwO%qA`eQo&3ZJZoVydMw*@TX%d zsE+g-)yRv`oz@(q%qhY5Xu}5ddiFfGFX@*b8L=akm)6$q+a7WRk)1nd6cd@P_;$n9 zR*f7bHh!@{*Gn#JmOh+{*_hc>l`}7&J=2_I zf!;@C7Z)-BWWXNo${=U5wM&$);OS@P!YSml)K*gfs{T=4KkzW^z&J{%#fy49LO_U| z{PW!{SqNT?-oAf;tow;P4C&a^{RziAbKs;0a2~FtbFZ-B(v?w`_m9s6AzLCYoI5-^ z@=xnsN}qZgqI~iU^~p@X;>dqvsX%MmmAIa!QnH#AbQCgvzeKlmjxr#^$BuDoxUDgN zeYc7FLM-{kiyVY~hoFILya?%w-1H&g{biaf_wU}1ZTZ~lO#GVrdw2kKv{eu+|9oU| ztgFj@AzSWa#$>~v-Df>Nr32)I4xg0M{U$03&6x2QPD$pz=nwrq+5;NQ%SC2^9_KslK79!hIR)lL=x}2(6=GlW}@9ssHtSp3prpSWiDJ?CBl(<5oUV z$w7}6XFIpqDWe2{Klon(&!~okziciE1&5Pwu;XKVD^UA;%ZtsI0N6iS-UEItT2*y2 zll*i?GMb&o|H--?BVgb^I;2m#?WTXlie^HHA`ptKMst$a=xA)CF;_Q;sU<|r{1AKgi3C919Cc_O3&BT;*i zNj%$N(`Bxw4V#N$0aL{+SDB3PQCt@I$W`S%az&-oXNdXRkDTNP?uR~{D(p@!`(I}C z3?x$3hlFfH>0kj*3=K>Hv>tiG&+o13C&MJOKJc#n@hdMI3~oCx@au}$MDaWO8R@*lQ|4lLNzS;Q_@K-3_+J0jHvLzx<@b8v;y}UBzrpR6=x#^-o8`=Tf%+Jz-C9tpGa#6PZG$Rhu(cC>wWwypduLH}B@YwX&0@2 z2m>oU!Z+7VFlc48xUH?9mhQ*o`5h{FIlAGC&`F^gmx@>+zDmaFfh;G zhUdG_dsSgd%+sRaLH?KnK2=JkU!ZF1qV4Z%rPJSjM=Tqpt%lL1)o&&qwf1L-b;Ymp z`$ISG`zYHtW_hpk^I@dy>uOw^s}?v=k;DeEKD~qF?Wf!TofkK|N`lc!5iF4C^OZ*ylYcx1 z9y1y%voaeq7(U;{%_u5FehfL%-Eb4OF$_NV%ziZQl*b`689yStaK#k5X=q+icK05C z*=kmxnBD%x%I!|)crHKx#(@@RkAi+8YKEUZT|$REKOjQ{c7O3NGTm8HZ)JVY9fu%y zy89Sl98TDwyPrPDT-NO>saBbf=wUO}gb=t9Ty2#ra4l4i;C&BIs9)e*y8hR?sYoCp z$R8|4fS6gA*B#UQF67V)Vel#=BV)llMnt$~qpV_u-gZGdj>QaCQkKE|fd94{*@;D{ zIIgt0MPjkmRyakZ#Jv5nW#D??OuMoZ{-wNXIwnGh*5~=l8PLyb?1D7 zaG9e)c&c3U2>zUDDidHJ(QZu?2oAzEI=No2IWozRvQK9d#2g}GrJ0Nf0m?FCf5f^x z;){$;Zyl4!5Bt2=Wx34x(+({rXFUHBmG+-)H5<#zIt4va23TiAwU` z^<8cIV`Y}4Uvrc;S{ctr+EktI?qyJ{8jWw4H_R(vF0UUci8G)+a4^r2NN@RY|26j$ z9#&-)b(I7J^~Rc1S=MoyC5UV^BxVEr8yg(iu4R3Q|Cnegnp!%OsHdyFz1kqZw7}3P zDI@E>$yQgWlrVmL&`nd+Z%U`oi?UXj4(3ecDrw9wFK?ZCDca?HE%C=WHT5PZrXNMu z>LAu61&@IG7;VgR1N)>~R%TswRaQ%hQ>7dYym|!6CP^84nFiN@-G%Yt>h0FI3Ti*< z;vO=q!=i1((})~WMk6&qs;J)m;PNvE@eTwy$*~gOP1OOl4TIz_4MIt56;jo ztJ2knySr|J_xNjE$|@LJ2FCm|pxg;fm5uqi3g+7S0w)Dk;r_b^WqC~m*xVdYX8w2y z3fzm9yPEdd!|yp358M$@I==5wsl`C6ocEhBpBRlh*-}rMJ}{Tbzges)&aZ9FuT>{6 z7FES&I4_^98*=CpV+o`HCA52zQHvIZ8g$&S%F#E%+?(yXf{E(DI+jnHLLR1vO6)GD8o-ye2E|s)@WW zz*%W@Lm7mM=KH_j$z$78;e_fkaHKt+4pMyAch*9N8^<8uI%n~b$2B=R^!*Ix0GJ_<dju?l6tpZzA15PR!Hz+M>R>TxMyoQrnQMR=UK za9a3AOJQ?WsB4DkQjKfyrwg~@OWCi&C>iR)GzYMW2L#jmCswfqm$fc5bvl_rsPw}U^^grw_w~A&uzwKg19}wWj zhr%gq{_NqS$CGcT*X0cEs2t8Mo5~Q`J|d(5^8&htZ{43=H&2mNb>$Q0*{K{CJNjm) zF2#u04C$aXljDx-AwwYakCb!gKK!2I4OhG|BPClL&aO|>_Dj6GX*oOEnVy5Whf9_7 z@fk>(dCb(b2@CPp_=lv-J3e8EBT}`7=Rbx)?!sda4@R+BL(I+6c`AUCcC`0tP`%JK zfmD=L%9{0^u+ql-7#~+bNEp(xo9KDFpiX5hy~*qTinzg}<^KKIvduo5ohYbrAktV@ z=2aiIWetD$3+Zz|-~{(AH=d-dxqsxZ#Vp^tW)b{7^TeT#Tjd&HgiY4pih+!aCIt)F zTHP`4F0SncnouW%CjMpKs6f9aVB&yg`Ri3Ln|mgcchJG3j#KX;U1Ik`?Pk3enPVn8 zI+_3wU~{Ep>;WIoMhO~V70t`=rqbSU|h8$)k{2*}p#-hJK!s}9;|_Dlnv z^_Fa5ysbsy8pEqa6aO+6!M**n0Hngh-%cx zLe7siE9jMIP7!7J8ze_URHHl@OWvm92LTYL)JEK|ogp3z>S#6XVd0r0i$2ddtPYMa zf{J#|OAolx{7ZZ*r|PozLGX^vdCYQ}ZF^h>%~o)n;pPWJSK`H4{N8fd9T6Es;aYVo zAJFj64uV<^2aXwx{I*DT$FjR35WlxCjHtlI8E_0<`GxRP-ua(%53h5NFpXA+t(kNpCavZS>%xjfz7@otJNoZmb0)$|5zjb6Clwz>^8*M7E%i=5S- zJ4CkF5-k|{ARArvWG$cq%`4INDCotch@y<$m2AjBR2My&b>znndz)7PuCERte;|K} zALf)0L~GuU)*fJBG_2?!OI2l^V#@KMiZW=4g|jQ8~0*w32dB_i0r#H z-9Ndk7n{de8r0%Jm|P<2XK-?HYkvGhAT$%648`kBd z#xN#-`eCYG;oUK^EP&dcF+c>@-V!R%Y!$}H!lK@ErxG` z@R_>`N^NAce9$F4H&<6peafad%BIrZWp6;%rO~x|wz-4Mb?}2c0Czh=# z$M4Z_SVW#>`K0w=oG4F%+>^lgr$QTZ>H^zbU4rJC`(zfckYWPX*F5^FlHBhY zYxd5?zV86!6ZjU1IQCnoMqX}s;yK3o&KoatEAF))?t7I0fhp>eon#-iVCy4($!E9^ zNk?(tsW^WNa1|BW`T>2ZrKxnZ6r>C=C&y`v4Va((8!|>TKNlDBp-^`zDE@7ilN_t( z#LEYb&CS04jmwPJGeAh)6~(czdTldBq;7b_XIX%&%V6VT-uaeevCl38KQ-T#Bm+?k zs6aZy!xwh(HcYSY%SfF3*&0!Z1o8I$5czs%hI0L~c>!*gm_i^S6E_*`S6Nd&CT4v0 zAAk8Ev*p9}+2#4MZ;IEn(TP*A2(7>>hrFpD^NXu5aDT3hd3bvaI{+lQ=utxO4~ed+ zDT-*Bc`50`X@8K~mvp{zF2vthYjhapTqbWJ2qM%a#_Md--V+0zlPzIIQ=e|B+`7#d zfO(VGuc?V{=uPb#9%$Q0=HQ5xvJ_VXk<*lkDG2I-?i6^q5w|`2d&HHUe!K}u(5%1w zXEJyp^sAk^x(Xk!0L?BD>f<@aa(S6vN1xotFHRmfPz9XnGR>u0xoimhM{MqxDU23L z_svPXNBKlyxS&4gv78zI_N7bFYCR99r$gz0vQlJr=(@J3p3s#)WS5?iF3KFk^$y4c zKv(g&BVkS@bKt`QW$@<=Rf5;!f0s+#1nJu{Knq)sztt?$!+A6-_DpY|F84H`wu3|) zLW1?ZT**)|3D!DIZf>irEaqBU;Q%@E#KLo9#bba?EivA(U0z~&UnrjQn3oeM%7$+< zrk5Qwjt+?SC1aVAZ05QqscGh)KPJ+HIiH);&yfJce4^D7OHA|Wjt;AHww4`BwhjhN z$pJRtr4iillkcuH9ZZ{QsLau}ho1?A?lWX3YrtYu3$)9|1p50O>bk9jJAd#59<`%A zOv7ptywRVwlGFx^_<|$@daLyo)~ret0bJJ5K~fgDETqbq%T(=Xb{L5+?1 zqC$%v*==zI_+(wCjY8y=4Tv(EytQx&$m3_ls!?6MGqyWWzt*Od6LcANR28-y9C6jFAv5? z#p@uXFH!;}2c9>9$5s7#=E9F8(i?m?fwHrIzr$N^K5um7`0 z+h&#=pY+1eWP994{t_qPkdiVo%ZrTN=Hgz>QK?Fa_xLc*Q}wp<^|ENhozR;?#RH93 z6N4{rIn23(heknm61Y^hKgXQ}m^{&-F|>>zXsK$%$+;I!^TcH_8QY<0k+nRJp zZ~Cgl;=&?GFoAjkee%9qLHDkkPk0p&_TFE5Is5Q-Y?Nk2>Kk)6*s(MY6znqszGgR7r$iSS z4Q7tI?L>i`=QfEC*+%jEc|9Imn)A;ng9`be7cg>$E}|*1B=@~v9JX)?+}O9g7tK85 z-fL4~V8Kmai0yXH!bdm^p^ z$$~W%gm4ZVm@QFink#|3nUnHHRj9`N`v=;GIF6t^?Gni05wm*X+i$m;4%$EvJQJ*e z)yG;I;KYi$t+}334M~50!G>e;{YTQijr3#5(!b^={JxcYa5Dqp=l!!fWRD}Pa0 z2#&1`HmPqa9d2#2eB(M9mO*fs6M8i|8$gzVgN*&BRI`#ZDyP=x@4Yd_n{N%0sn$%zfo;ki6@0aQo-9K7N#{cCWxx7Z zU~H5}UIt})nhuomJ5yVSus4|!J8FjQ^%l#_T%OVbrR#&YF# zrksA>Q4fDKNY^4{cPjPsGzNO>@tPC$dVkW5T~YSC9~=@)aD&+u8IwHbs)E(c=KOaD zgNOI$X!#rY0c3c)1QnqD)rO8?NI?UGTHr_*SM5RmYkq$ETWo3BW==9;l^wsdl$x1x z+XoLG=}Zs`$Y=&7p@;^9XU|`Aseq1c^E40H#Rq%4vJG$V+0(nQiZvGIw|HXbdYB9* zLeGQ3n&)gp?D)97TN^uY`#?@^ZriS<`!?MN{q@C+I1P0?O`F%P4MPM&-JtGZ(ME7x z#8JYv-vJ`FiGr0@C}d@(z{ec8V_9sFs=)Ic*u;NSJcLNdPx@kt$JT&EJ2l8H+E#hD zt@2qsaCY|v_(hmCm}8o4G5ugMBD^f9I9rW`8 z5Mx{zo!kunt@cSNkDnVdn@dn@tLuF_T8lc=*+Rs!)DX z)Z&`Oi}kQ1@Y)cW+ zJG)qNN44VX%UvP?1W1S@!+Jn*jmG^L82q(piTa%UWf`T-?^bztjmDdv-YEb{FiH}M z*$4922Gp>_9?eqyri$EFljV)E+%3WPtY2ptP}6dLm&t-Z43O)>Oyc^MIqJ*|^F8+XE&-S`w%?t9ONbDc@pW8od9CgcSN%t$LEUpDa{zo77)6|orBE&1CE^$Z$6QS^_>hQQK&LV-E2^zBRqcg?}T z#bKH7x{|ub#0ef+=keXF&b_Wa!B4J_5J&l@$-^Rx7xrKqXP@&O2oFnhMF!n}1RH|T znrK{z%j|89=L`KT{K`t?uBM8SUs0ua5NalB|KUudbKJO7MV$`{)O$?@FXYqZ8Z@~5 zb7A&h?wTeY!!$FEU!?PP;*o?$I)-(xjKMMo1xApdGlKkl*rv^&f-P4rafa`1mP9UX zKs0WO{StnAB&NVlQRLI`2jC??=^ZGUKs(Pcc}i>= zb#sePzbNmSO4=%Rw?>d+N51I1AM)n#(07%W_kP|ebaQyC{@#4aoeNR{a$BI13&=&G zbO0!4%reh~b!>lN!RnQw zAv=)X7X}52>r;V#59kjz+3_wS+%ar&l@fXjk|3Y2M6=2QyH#IqVgPhC^%JIR*B`i| z7o=x?l@1l_2-+SV_z$Fb>kL$6l;}}>5sN${@TM@q?+%Yl!@r2^pW{X>E6bB8(=x|b zbV%HrA-m>uzdI6bmBo#tU_C;g^`wU9Rj5FIBqotj{p#mvDH`?vvzj)qfbRdMaQ5cK zPR{NgsO~=%7hHm4&JIkTeGN+`S?6<}JG%x1Zn}5x-kpAC+M={AerAxak1Ra=;h%p9 z3OuafW{-n@Ck zDg&zD9TPxxyhF~ru&}T@JUo1kSs@`IBErIwpkSW@!!11r4*ze}#6wCST|Fe=mRDE( z7<|4A48-Muw%qz|q50aA&4b2kY_---3-iZv>92sp|AXTDUg2oRv~xVdh$VVt9ajg5`2?&v#uo@?}{uY~ycOVrd*=?v>LiK>fBOE*|qr%&h%0QV!q%!!o_y!Y3LK973WQ+>1~Er; zc`Tbu(BWzkcfvc+z1O%)RqPsE{QS=S=~9Z}m&dEEqvwNet7>Wn&Az#CxVu_oJHQ@w zo`Rx+REEg)qwQL=eoM0&J(rq@z+itBlV7jEOSbiRr5yuWQG5Q#M1r@ z841^!hyU@JO$zeT`e$qJ?oaoEf`T+!v%stcu2*%gwV+3|%F4~#g3#qg7>^uL=Dr-oh(qG0IhG;KeF%6c zk?ev?gN6d;sMeQX--bIqhU?R^h&ydBbZtyF(9+P*T)Zg69JBiO#VBYt)#~Tz<>f`` zGi)V{{PT?(m{7s#AV}rZk$LSZ-~`X*e$w8g>qY=S2S?U&W`GNr$H$K!i{DVw&@e^% z6C4ppeM7@vA9C4b{cAzDuG3IZh_XxxNB?mD{r%l0>Zl(D-E78H?Ct$UK7=6rYW z+AVe`;xGsw507eBS1_o7+Y2gx$CU<*`C|ozw```(dc0%HS;0XkB;q77SHz}we=dR} zJtJeo7`8fGfblyWAWwyI<=gwSA+)_Vo-mC6-ni{%({g5bEB@0x9n=Xq;0Qep21Qe| zpI{$v51RSGin6nbB-j;$ z`CM3D#yB#7-V-q9lha-K@jf$tJd%>XVi_b{rhKPwoQ{tOu=UY!8kV(|?nd|d^H*4Z z8OqG!a7Sxph>5D-Gspn2A%$Rc+IsJ4>x7JV`czeJbKibUM*vJZIye~ffyc+kgSBIp z^%QIw>bqRUP6JNFXJ(oq^dR+4em(pRqIegeME7UOV`CX;IphM_{x&TSnvP@}-vYK) zAMo6pW!;fm9?TTf@U*pK(AvM=R#>HVe-}8Ic{rr5bQ5y#-aX8#(~;ZBE4Xz1)mW+C z+}xbTaqi5_Oq~EdBclPfPx|iBCC>A-?8oi2@}rK43kwUl%2P8s;6(E&Vk%4n_SXGq z@P@FOyqX%%ICfdqT9J_RG^MV4bwl~nf2}-~LwfE@P(0U|#L@Y)XYJ}@4~54O=52=+ zV39*M9)0L*JiVXMEp&Sj008NX3bV5P!$WD8VDPl|oe*nlYmDA$A8VeJN03xiRo7ag z6l)M{^DWezs%mP8vcJ&q&XD$K{3A2YgQa46of@r_x7EyhI(JEtgvu;7*u%BSngTOm zF4NP~xR#`dh)$^9>9E|tihCt5ga6sv)8n}!uu3=ty>i}j{_%I{uT`<{)#Q;R<>j&N zM>-}ZCRi692pl~=e(-<2_c$g;W5W9^ZgzGS2mh}p@!s9K%gfu{-R&_*{`C>o{_R^u z!+&+O&S`u&_TuL&9CGW~B&mQsCzydf7DXcH|5uk{ltv0Q(he&Y8`lFCettZx7_PGH z{IA>RJ$7lF3qP#5#=x+col$lB=n8NH()UlFHwf732LaROQJ=(+3$%rWh2n>qpFVv` zmvqk!vTcLNy3Kz1uldV+y#6zmJ7INaK;Cs%)~L=w@9ER0Nd0&3-`5I>If)4T*Zm%c zoFA`31Gj1a;3l(=mfX>L&0&SdPM?JNf7mDpL}-SaJeQOBjnO{v^73*mxr6+xKJ$Y) ziiC~ATcH5zfBc8h|20G2#I+=2mnzGSj%Y?6E-uk-IpF0ka0FJO9P8{y3WHC`7~8)_ zsaUS8sF5b#7h4Ag5|27^QHf_pQ6k#Nhg}IgxE6GV&rFGSNyX&ffmIs2J4Z>lcH>4; zdb$ZhPc!o&SWp9wHIn{FIZ;tj2?ox8UVnmH#80=lq7H13X6A^x^z`)j zgak!JMZ|yHEFA4G`l+U72T7c9#5SA52xF~&JL=pCu(HixRhGl~Dg+u(j0YOP+}RhO z|J#1JVVu$*fISYP%tvSvzGc$|b+B~kt(45Q8ZC%wTud_V?&-lDMKQt zq&L`mi0;wW(P?jQhf13&ivPyrgTR~&fYCnQ-y)OAXf03<&B4(I#x#aSLP=S9l5|Z@ zPL5H)go=tP`|o`?(dOs+gwfQTN%xJm0Y-_V(Nj}X3IK6KLJ0maB_$<&{iGo~r+pg? z+*rbMN&PP>Gg*MvO)Qa`MJ#fG`@k|Q#$`=feYdKud+z=_gWczMOYi7VS0*UVmyUV$|q+;tHDjp9FdP7 z%F0SOPb}bYYJc(lX}R)z0AL`?eQ7>1>+iF&u)r85K+pbmOUOc&)#6}#5rH~f)7xYk z1}lj2!j;SH4?mwayaaZrDI3+=4S~~;T08eT@0VbhhK7c&UcLHvHUhr;OAan{CD5`- zj{-)<9c0SGQ-m=KaPEv{`}+0k>32+MBMENhV3OEn{dZ5NxY~?QV%lq@M5l;M+6*q! zsw8?rn;6Z=yW&jrJE!g4ic#F^GTziCV zU+P$6UYnz6>OGypWAf3aavXbxr2DN?Fv5V6HL#wSfq{XI4L0{J z4D^KQa(LU#O$W{i?bd@%q`3Q(^EOBlux&WF752X7zE z-$F}VP3W?EaC1RG;(wkv2egBc)#B6b@sd=x=OkHEPUkXKsodwj@agvWR0^>7ukrCI zrf9r2+(M=r7XY*uvDo=K9y%ise4n^M;D8Yi30EGUZpwe8bL)R@(f6UM8CU;%T^HSdXDtv?n(Y_h0cv4% zk{$i`65UOgi1upyU4Y0$TxQZvzGG@?3IvA|A)tF~fX&aIi%Om^!m0{w3Eb~^s~@EG z0k#J&Ys8VAMqcy(#Fvi@vj%Y<>^! zhAvhxZuF+!9xm034I0Y5G{0J&n=Xu`yY*$dDd0nLvRZ~_j&{iERO0U`4`jccKK0z< z+UU?gUShJNDY{Q!ENg)O-d%~Cz40|li_3V{tApPr38`{WASY>SYlA=l78uy=`5;9dxIYlnK=@t3Z&5s#xfZfr9;b04D!up&f+BFprq?wO$~qW?(S~7l;=|J(`ZPYLq`fQWvAyV z*DP~HQNzR9%1-s)>@wT7zArVFr{4JE4u#0p3`DXVJ3D=ErthDWfFcpWGLwS5D!)^( z{m5X+k{_PW?vO=zkGo#6mCDs^$JJh_Pp^hW#ayA@C)cC!nyIS6!7qDCHZ=>!XBvva zp~nL&{V{q-AT(`fRJVuI)a1D$%@8Mmk{P5uC4Cg+w{oJ%?Xc1&|8?~Z5q;h&rA^tmc z!0!g#RYcZ{@_;~NrvfyL))pSU7u?@0t0EgW{mhRV&5{y>sRDq@H24t25ztm6=E;%R*Y;EBnkBR)9Lzb1grMa}v4?OSjB z)Hn(j(4!M4Pu>S?*V4kGx~2vTBTfS`W=jdI+cBV#{@DJkmee`@7P8(FP+YS#V6%D8 z^zDWEJwIO@(?Q#$fK~EZ410f!o15iu^d+z0*zob&zZ}~)NGDE$M7mcfdWNF2phzmjaKlaeD^^~zRkBbm znL_KJ3R18(QQZLynzU{hb)5X+hCqzj7*Gd{>z%5qzU*(!o0^_1vxoPkNu;TBbAAU* z7%l>g3a;O_^@9aSY&tG3cp$lg=xDN@l$8PlLBy`Dih1CW^awThWfCMdU=s-#H#H~F zGkH#bex>+gm6;Iv=W)P4qhJ}-TKSJ3%Q>5qNpGjTD@0PS?esKX@Dn?JdfV6^4@Trz zaS|X2z+2s?h4b!Ks>ZMj2J$N6jXvLZ^a%)5EhZbzvIe&UcnRC7Z0yIFQ7ea(!) zKEN7o@0_rHzI(U$ko|E|+w$U~UP}c8euavv*nS!m)^R>@aS=HE$r>PRBW^MjwzZTu z>M=~lv1iMROrSZD@n+COfZc{%3C-J)wg!mHDK_Cml9>m*$&&i6L<<0$H$ zJySQ&VXr@)v_#@M({HhQ*vo<0wP5YWBG*ZPLtT~@X7}K}Zawy#p>bt8%M=5Vj|OHq z&FH1;y-fUoh)G(n8JIBqg26Nlo4;4eJAMSOAd7p&DCyXDsp3jOl9>kUdmqC?l;!u1XedMPL9B7OB}2r`cof4t|rMO6OY$w)viL{?qxmkxiiP4Fr=D z6aiFU4?QFBN)7VjV4ooZ7-WLd#$s;Wam{p|%}Yn@Z6ZxugSj!7B?m^yk1u>j8kD{D zh#7O4C}*Dmj)(Ui77v@A6;`<>-(elQ&%5UFF}D3I;3bZ-PJTfKfl6ChO;Wc8ciL5C z55xS8%n47gEw zSo&>xvA)`P%LIMhp!>*|&l4*og?)JXveBp{z&&=TTju#itej|?=#>>uxJOf$%GD}IaqEQrr~+OJZyx>fS<=;~B;^U{VL?*7$}oD$xbYmX(G`bFlY z9Y+P@a28ApsHvCD+NEu4%AErPT0tLDQj9kKVPaxROe(Lf;8HQDFe&ji?GY!DNPf2g zn*_z+40&2Vv6LDp)QLjMT=4d8a;FY|$Z(^S{lrl-rZxj@7EJr^4r$e^=g%iSBcFOO z>|J^&-X{f125cY9xK(odjAcU1PV~q=++~ex!gm;gPnKTF)@yk|P}UuPh~7Q(bA}?H zx$<;-8XswtE62%q4q@w^jmKUltXgz=*cVpMpz?-Y0IGpY(=A z(CjtG0fCcQHMh?NH19W)rY#r9eMR?4>tS^Aowm~lmg*XaTa`*!OyyMN2AUPNp zzqzgGMNV~_Rqa;?!^mY~(Xs5?0NOIL6dOsFz+Kb&d-2vZEKSR_moKper+c%n${=dl zMe@}FQ%+1nT2|T!a9cWs>K_Aso~jk|KVr~YDe8rt9Kj~+=|Lb*larH^hz7Z==HuBg zMl;D^aw|c{&(AOJ;r6qFj??9=t)iEzFyvNHP!RiTJ+CeOV9mF5!@AX^HsZz=$K`n! z%aks@JR;Ya#m%0J_>CfxTDR;+q-1Rzk~*F_Nt+(K>l%ksaqtI)Hr*r!N1 z5Rbo7mLGN^WD-y>=etx{FvWT{CEF@KmYrW{8i)<#o_6Db`*8j#iMeFFlD3JOU3^Kr}l z8T1NvqF`flJq?p^{%{m`f;|w2goK0?s3asBQbcU`HYN?q#(s2kz>R9Sd3a9iXs&qx z^|zGG7d*VxcmD{t=j9Tgdx_-TPTDFaG5*<2uv6I?=GaUez15&D4# zkKefdc&n2mfN1lD190ryV5PoGP$Kp1&S3jiMDpRT1y^+fO)~JCSo{j9-LY?1fhyQ% z8M{OibW~VDX?$demxU)KM|)%5ex*LD^E|;-1X>JWZ=km zeX?_{FVNcxy%&cEush>ObRsBF?V)o6ty^X#MKxrbLid#(|BJ%L~3xu7{Y zEH&RhERxzKAq?geJl8N=Pu&tSl&0oUWGc4e;a);&y(B&|GLrE6^)oVgX>rkUqN}+* zkM;UDNcT65jh{D;+`fH#*CElQcFzTfad}5O#4bZ##Cmvp_umWn^#A*yxy1OtZ_nCC zWjl8@Hj?9TmuXOyf?m8xOFI)1*IVuyd ziVC+d*>lVnX^h2+^_7Y+l6zfucewUn5wr+~wLR*#^d1V5shz)_-Jj3qvpQ3Gk5N%6@&ZFc%W}vaxYv7VCv54o=Q=odU(JGx(j7 z!oq9pNsKw&r4Bz7(E%XYUt@9ZDRM}pGo0wka@z5|r@#jHP(wJ=a0F^olT=gi`JZ4x zr*T#Gsz{r1ragO3`>c76Gnj6{ag*l9WLqBZKZ$q=8+f40fLB=L1cB*wVr!W-}ZeZcCT1onbJ z%F)&yg}CO@#qUqx2^X)gt$=eH!EmE+gRzIQfwzisa(gpY6$H;etMMd%YZr!~*25}hV8cZY7VUd1#z~+6{xb`V= zb%f5#Q_rMnu+W9GeCUS8hs z;X^tpd6=lCf*p3eIo)ZkVr!OC9sl>HLc&1BCz-);Mb3dZpi z|KX%*q)DeL{6HcCE*bXT($bP>6dwm1TftnlRCPVb3LWVz)O!{ol?ID&!+hxiC6-Ow zD@EYeLg5N}`t&p6KHk5VvGL&cWgy`(kZX3`+4&c|uuen(>;|i0W*N zgNy6t%CE^tVXOMuTGP;Bp~yyk=rypGed!hkd@B6+y`8VU-ZXdHB) zq1B~1A>LL*ZN?BQf_M7%_QtjY87Ht(2jK$VgeQk5A*Y0FfO4EPCkF=)sdVuJqkfyI zp#9kclHRNLc1xVS_2~t?=D*p2lYX^p=tGv677g45pPB;w#purmziy#zp7W(C_vTGC zM-ZBVPbu|KY`WB;x6n?A7j-P!XKe{K^z}r=C{ZW5#8y4Bc~FJM@lpE8&#&I&p;s)A zr`li#R?@Piwb}Fwht$oZRy731xQ&DZBT$z7w+SBeyy%jKJP!0AaXfxPXgk8r{;g4D zAT9qGjqQGTeYgqrtusFB3qIt%h%tN5d|-s)%k4V~y-YhjpY&2AlQdnn5ItPcB(Y+N zo!~?Yhhb0FAa&=Og}fLOa~%Cydo)mRMxvH?6gQUNZ0b0=9WWlhF2Bb99a3h_p)h@q z$vpmwRRK{^?Zj5Bn?5+)3OAF)WciRZa2T=nhCbkG;T?!?t1q0=_&i6f38N1^_47u3K8ycirPs=#xTD>9}Qvr z)7?2TQ$C1B{l%?Nv^E#BUOV}cSdTeozrVmuMs46yiarmwSq`?imzk!#Jv9xqGa%J$xU88mvX1UIMN2UWGP|sjcTOeknP)dXBG{Q`T7ZL^tF>Y5c67N&b<)?j>=_lp7oHfG>CSgU#SXDfdBV>_d?W8(y~ zOq7+Dj%RnEA|R~A_8n8q?K?0rG?Z)EEkLQzC07q-XA692&ykJ` zhZYUL8?~^&b7@wF)$Mi1LRh7ND@5S?bWr2M;ZF~0|JArdvh19ks_HY&dCM=kb=gyrIUVD-%75tj( zAN!j*Zs^~$gOS$7vU`La@|NdCJ|`*14bh^0r01~(kTMB{ufVzwQMw9k0X*>(4_ej@ zoo6u;PxP|cd)Pi&8Kt*Xp38F;Y5YcjLuZol6E;Z6RB^-X09Jx}M7_kBGp)-N$Uw!K z{S?hY8Tq}ku$|rg*F^HO8|8>;kTu&6i8)WSC5O;`|M~5`83s&3QP{C`39BA22BH}@ zZfA(N1-{4LjY=71dyw;wNyhGNv?s{_r%4??cMt|u!gzXT-B=VQ$ zdCMTUNzc}X-I95VU8WV&Dik%V6Ansv7OGg+sN(QkQ$aM{r#A19ulTuBr1#pSA^P~E zW6@{vQX+<#Cc_!bF$NtJd5%(%J0o{+ev5k<$@_Dm*KUY~$csVLEh{U7UUq2Do*Om8 zXOA8GfglMK4t92SRPOXK=~QI7@KIV?ikIyEg9l<_Vx1H-<#_z$$tO>qlsJwBCnny))fg|Qk&Zjf%gc+4DJdz* z!;=Pl79AbEOI!ze#TU1i7`v$*ExfCsRl5JG_0 zgN|w&Gmz&wmQ%HiIOPeGg2=Re0g8zyd3er)Mc?1fDDBVOUgqJ_5U&=@aj()A8xdGc z`toHzW}y_m3Onj#&jPdvdZ~kA&Xyw|!_|Cr&O|oG0AH?5SNK3*x9%&};tC@VXvqW| zWD<2ep2cd)^*pHIBCF@mi$(w9>>p20PnrUCb@g9ZoDFkA+S|8nbNl}27-G2Q+B07( z?`8b?NK}e}I8P|y|~A^L?QS!E+LVfQn}#b;-X(@d&Aqi+=PtW3jrpOM(%jB zdCL|QisDuJg?@SaMcc z{oUPe*a66yV3|4=qwHzgSXJKLJbPWPz_@Pj)mITWL$&Tzo-)K3zOQ1U!{@7`RQHC% z|NPQh9x@9yRuFDT^W@358JH>zgNKLR79(gD?6gl7nPe0a$iB^{x)t7DVSIW#N={R~ zOTa5hd5b8s37tQ{vG`a8#rW- zbcoe=r0b-=U%-zi`rBp(Yk*rV21~*N1Dg;o(w;01W#5XGseM91T>x=kz;)!Qo<4mV z5lTR!p%5j@33F^YV`F25RExYX2-0Sh%POzcX{M_WI5dfqTXOQyLz0q`-o1N=z)sKc zn-$)_GjxkosA{aPU+-w!Xd#H^f|Pjt{us|$*&d5@-J%wEx2tJ3h;8{7t0b!uC`Ij@ z6ACtN@0hPE+Vez@)Qub80NQv=+HP$@? zBO}nqwXHLNltcs>zsV};T^bhF2{#NA++=UE>9VGcK={E!l#q}R-uu~hy;i27S^dTW zUZ5ZLuSlLVAAo}R(Q;Us>KYm>kVOZKotd3|?B~~ul8W1L$HPFQ6cQAKs1=uAV7X+X zxmB6^mlKpRxt#~q)Xd#CywqKNI(d8_mwe&UNMr#TvM=`Sfphjl05U$<5LzPDZ6Dx* z;Ckz;s2nNSxSRF7EASX}LAYI!A~uTh^7K;l^z^HPZ0n1FcGdXBR_9%;=Z{N{_7o7& zJe@pqa!d>x3(E>(*L5*}4|vWgAO(%nV!ADz?d_RS)sb-ei-v|@ub6({zPTQoif!zu zY^-iWL7*;b9xS@%=Hmd=t*xy%hSB22&(~<08r9yxn`{qi*1c{ zbxT0&M~)mBmk7GcAqxXqOG~S}yBks@Hn0EV$H5h}SaKn|#pkcZmzeSH*zz95ssJ4r zrZtaVvFh4QA?w~kWXFj7c#O}Q+qG+-kDLPdZ*Mw(1bqxt>cHH zEd;9d{+yvFR_v_Ve;7L|*yo*pVBj>GtnL}$zByvIsjB7zz{v(9pqB*#kDW2yTQmo9 z6`W}a5|XcD3|Sy1MxR8Aru&X{Mi!O$y*k&F7tq(8XQ2_W=d=9J#%jCAj~}C#YbC$K zy%0>Ps=B(vWIL4P&}#=+S-o)uDYtfRd5;xhwe-~zgA2~i&hfmO+8El6llB+2`+hlo zGn#xSngvC?6v!!6c!?;{H8nCMgP&h3)m++J{yrXtewW^oQNQ!;9|70b*SA7 zN?m`vs!`?lSR#v_qoqID!E29avZMzpQ zR4K3L#<)dWYwHYuVW+s}gWxy1}(4G^fvQvQZ&HE5CzZ zzg(BE8BEY}-YuuHIL~|W=QrntO*w;_XOr^EV`HV(%%-rM$o*&EKiFMRUM?Gqj857Q zPgiVqbMb^(gR@c(ha18)ylgt;#-@*MV5oAhsc&rbg4mE^bsbB4@J!Qrhw-)(EU66+ z8~5(#%&H~v<-k(WY+NljIirh5;-QAtA75;C+rk=awVd^x!{XlZG~ z$YiyW%~#*g0j-ad9)y+_A}${hnq}RRQEZb9T!H{?ZNvFrT}D*V0HpZ^u8+{{K7Vae zz`1HUB6JC*g|}f9`nz+C_m+D^#GN{xra~eCcQ4>CRezh`x`$?h^lC8YRaVF?I@U#3 zb}<3Fpoi+_+*DA=ngFN9kvTF1nzd)^%m!H32{)29@V=}c(vSZ{^ zl!Y1BI?!+Ydeq+9BZm*aOb8lr2S4t<>whr;57)aPiUf|DJU`*HzM{WWiFAoMbL}vw zDw)l4pjf+YcY&6CT^UxD@QDwnrOxFUwU=C8)*;$xt6An?kvQ zJK8L#4Sk1iEtog?7k{QuG2%kRR?=2Pd$?pE9#+j94q1m!^6YT!~P$#*8zw^nP@C|)Xi{=-0b}t>HX(oFaO&4l$ni9 z5}g8?oyOJ~G`5bJkbYCa4DCGbQ>T7LdrT$U9KN26(kGXW={j>@Kd5v|;kTlLV%(|i z8h}CCC%RKrQxNmsb?~h1cw4fXh{+P!3?h$xN3*FQ$~M@0D{K%Vwk5`OPCzKd;-~2K z8u2ite?|E$YdUQjJU0t@Ra%2)WKFdj3P-Y(Y5$rY@IglSShQ>GcvJrDYdwL6={q1C z7&=!os%YjyWndPnYi@^)1zp=y;0uOFx{8`M2p;QnP1hL z5T0Lce|bFe@bEXkXn?DR&PXUv{G`%CzDvju{a!#FRXRU&Ss~<0Th#fB?MO@yl@#l{ z4ew{K!eLPcvcjPIXt%*?KRBNt{R~m)lDDOJtm1_WZ+Q!P4F}es9|QjgceMQz%z<;s zeo)JP_q{?d7Q}Wd_GAhPD;i6P9MAp<5sl4bBFo*YCdYFRb$-5dyx=3Ve>Ij`d;Fe@ zitmwGH;?ylN@<6L1bCki;9?obbS5D-Hg^17Fo#x+J4NvqLEAd5(mK+qouAHadpU-| zF`TI&4u%NKL};_vg}Lz#tB?^805mKr)bW0iH!q&_NG^UHT@+H={jxAB-btn-S5sR0 z-_@6x)JQn3WE}eR%c%JAaaL1HOfUkaJPrtuz~E5))i4180ec6BNz)w5yWdq!1evkW zmVtb<)@Q&aQN|lD?G`uo(7RXp#(r3ipPyeWVm!5?&@>NwFvO^g?mJYYa8aie>T_11 zC>kKftn>MfyDeV&`fj4E$TRbCbB{+nrUH_^`SdU^W$@j`jT;HYrmu+>_Wy1cPT!=w z@S<%8fro&8VA)yqnm>B@@S(qdy66!ph3R4QAG2qe{Xq?g zvi?3o;631;=g*(lEf-P`evMbSelLYrPxYizeWp4x{ll6*YJ3zl<y%EB&QzIl~oLTKISVZ~sr0!a50^X4o$6C%PtDZd&;R(*Ss-y#3S3%ZuN%ktnF8@6N-v20ktrKw!ZWu z#%jktO9b95KfY3y0fGyS^aMR_d#a!%!NWt|{T1i*S1dzp$u((UD6%O&*k}^UqGaXB zQ1FzNmqBWAWktkmPnuXWAp+(P#(fkx-Y5jU@CLXN^mzuUr1o)$+<^y9#{%m?Ety?d z0LhfZ@Fw)g%o&FhhYuUz$%3{#N=>zB2<7F?lHlSR0j~1Gbe@GaDi4TG%ZCrcz?&IH zyKu4G%ZMoj6n#f~`xN033@*K0t`KvXN^|1PYLrvk+l9Aj z7>+59Oibv84NoTBx|+=cA%Y<#K43{lf;Ue){b)+O_N?*YY{2*KvO|=fXB4MIvD+gL zG<|Tog88$9l=PC%np0`w;6v#;01tOp-?khpIiV%8#~;MMgLJ9$CE9I$xHq0Qs;Wu* z5qd^O{lP1Ery2gT`CuHCN>dN0c}($zY51foLXp7X1`0v$>ubLO;ND{=SnUuohQ2Pb zzB)&YcY@_X%1(q9{QU{PdY@~&?>t~Q%Z@EQ1IyS^@EnF~Fkkuo`%4}iZPb!|IIyn4 zPqjUmqi&X}w)HfIQ~$MW+)IH7gE>Z{zDs{yuydW$-^WuXkv3-syLy-wGD!B+u!T#-15lsf&yPBF?b0dxU6`{3f)<}_PADlX=?c-2$ zw4=Y2xMWgo7dsq9@#Z<+IOjsr$NLb?9a3f>;C~4Z##f#3{Mwz%vwI!cG38U}lpqq> z=w4P{TH5R0hf1Wy3Jsv3WecaQI~)WCev+kdWRbOQWSZEwYb*kP(%tj!Wy7UH;qsxH z1BCg$l*qIF{M^Sgvn;K`l#b=MZ!aW}wq)F@TDEux6g))RVHFRRGn5VEYF(@^zU6h2 zLLS&1IBRPV-+U%g`r5Up!&6y|B-cv&rWY!j5f30lfBvbsu2y4Op`D-H+Yq}gS1FiUJS3Kt&17g(lvB$0Y1kjB4ij001*RgrABZb zm#ftqp87B#VS+$_cDZL!rNP%?JFD!f<@tHXEjsj46UgvE35EVleNyoX!mh*a;E^hDkN#3PqNb+o{K}!y@mYs&BEzkCS(+cPAUCT zIP`TZv;PPxFtOc|H z0Km}rK@U#MY*#=a1-3c2Q~^og`KcRssei$?xqI*)7A4d&I7Iqp+ff^UXkIEN?vwIk z6)ztDD53D@3C}aTL(8@X8i)|xr4><~c_{P~D{$7_kETq)l(KG1Ql2?6v^TWgL4Tb0 zgFZyBJlmX?mf+^0;gJBC>KVFP$us<-qIg~v<>;Oir&bbHnUhBRtr)L-^B)Fr%@A8J zHu%W+Gdoez3+e-h13rW{S7AzUSlWR)VM_8^wbl(HSrVJi;Td0G| z46}$}Ebr#c5hoH07MAM z-#d0dd){~Ju&sF9hX&Xa&rclvncO87%^B$141XMfK zHSw8xD*4z>vQL~QRE0Y;iUxV zmjFu#-8QMQSHX}#gju;EDh*?)&_d~@K1PaUk}hhcwkt*oPr}D~fxtQXaSuGXlpp;i z)8!W8q($hT25x#}Ig>Hr!xhDN7A3=)Yop`i=6%}w%E~jD7*B`#2e;l1rZBy`7Sb6g zw(px&%c4{>I~!W(>s`dT=c{F8gCQvK2fjlEQVlzu`SE6sQ%Si={Vq2P(Wp;#b+Q?B zMJ@EI=wj04GdNpsaPW-xs%xHh*?$HsaZS|LcILNOYT<=-#qjwt`R6~0Z9S}K7XHlx zI#w^wcHL=?vlralmM{{kxzKG6dyyO!rPtcg51p!qk+2IJ`ZQCs=T(uqeUtSbB2ji} zmmkv^OCDkC-m`95VEAvoY{s5U=^xQ-2hq}7s86F_F$tBPhhyEA>*o(L+>moRF9k_` zoWn3=Eet9vWVNopzO2#i1WN#8onEQ#;7qfI3%w3XG)>ihemf{LLBuL^liRfkc7+JD zKeaE7lv%KPcLoSwT~!ssJm+HI9?BIgm!Y_Fh19uIPikbiy1To(x)T0ACnqPQe{AzY zIz!^Nqsno&Kzz-_*v}122F#YK@|Q0{$-g#g@+vmg5TF$W^8i>R^;s+f#3AOHFu+)y z7-4@Dn>wN7*0F;cDoZjz;;EVC+F^Rk!j{kDzXk;bkyGHpUXHF%$_T~!KW7*-+!2CS zBrnoQr5?YXB}(_ut^YtZ+1#nA}D`vlpHAMmz+)Q-Ik&D=Q1Y-*?q2A0Rk@ z|3=Xn@48@4a=*!j3=xPJ(15A{WBO3s%vWTVmzVn?v*NNQIf+5OkH&Eu?W8`yI}$pC z|5dkrHSV&(sJsY6(C%bb&5NqTnIB9X@-VH~W@k^JV3EnXWkw^q73tjHzQ zb1XvO7yZ7&LI)E6L5TXjTHzTXwALD z^4=*h%#7-rve{`JUQjPx~P3G!6a-vsX3(J7-wbcwsAM38A<+I15nSQ1M~8(r(@i1E;g5Cb695^no2{$xv7z z96N=-XlTY!36chHH#D40AuLNlvliI>#FQ(p5h(K)FWMA1O#c2T=94hj@Y^=wLtU+{ zIrs)TBJnqokve%l00vyY-DhlRZN(`rL;WV0c6qwMAuQ|+NQ-I*{S@CB?JrwV5b2{o za3E<^VvBT~9bQUi{{)p2DZ2#u)<^BowSGLadLicg$Byy%Nk3Qpe$=WMX_`3M2aiJTRB!66G~q*w0g2HOvQ zEiy6)F<{UGaIf349}!|t&sAbjI6Hg!*CY!os|0k0VtqjXQEfOi!jUNIo)cZCDd8J8 z>IN>}q!IK$?S1utHM^98rA|^-K8*tk*sBoPU@=)iSx1k>_xnp+Y%nFRm>426mR}Z& z#1^wZS4i>~VZFF!O~BHy$(K_yDciIuJGa~${*i!Auee8Oun=cMHbRA5vdsK-;$G!7 zaHN4oNXg0>Kd?SO1!OpGh+wkL+!!J0X3l-JAc>f3Ml=h}Zm=fM=2>$H-Z>CcQXm`X zP|^VhyD`ba$jFE}ZEYXF;2%vXW52HbiSucPO@57tZ8yiD_zw!@q;FS+kL3O%6*7X% z3he{tJW6JHU*vKGI^}|2qbn$H#jbn;Cm`zED;&CWhav@#onG^XNEy)?VrXN>1|Pj3 zj$~9DK*ivDCHl^zpTA|4Ozp+{vO505dS`(-u_`kFHvTgmicQI+@W1(+4*R~e5w|Vt z%CiU?UNicWwt>o3VDX9~84=tYC!4M=CR$rvRfR?P8r2YKEBmU7P}yK(f$*W2z-RWy zfgyi=JkwNbGnukaW<%jF;#4|*JZ0iIp9UWYPFE8ucGc-gM7*>s27+UFkPX2>lA=DXyu2q2uo>$l?-+q18fV^6qyjhWZZ#( z!Fr>Sn1NiUfTj*vw{vZi&xVM-Y?E)PSK?KI#+skuxDP@VfewWQM1c?t2GjP)i$hi$ zw*((_s&Fs>wpPg4M%8zTVjJEP_!wQb5C&yV_X`{calcC@_%-gCS1a2XR|p=p>ekzG t|115s*wCB$1+(y`p$IL(1Upe*sK1r$GP! literal 0 HcmV?d00001 diff --git a/docs/src/doc/docs/index.md b/docs/src/doc/docs/index.md index d1fe1af4b0..fe91c62032 100644 --- a/docs/src/doc/docs/index.md +++ b/docs/src/doc/docs/index.md @@ -1,21 +1,21 @@ -# Dokka [![official JetBrains project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![TeamCity (build status)](https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:Kotlin_Dokka_DokkaAntMavenGradle)/statusIcon)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_Dokka_DokkaAntMavenGradle&branch_KotlinTools_Dokka=%3Cdefault%3E&tab=buildTypeStatusDiv) +# Dokka -Dokka is an API documentation engine for Kotlin that performs the same function as the Javadoc tool for Java, +`Dokka` is an API documentation engine for `Kotlin` that performs the same function as the `Javadoc` tool for `Java`, but more modern and highly pluggable. -Just like Kotlin itself, Dokka supports mixed-language Kotlin/Java projects. It understands -[KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in Kotlin source files as well -as Javadoc comments in Java files, and can generate documentation in multiple formats including its -own HTML format, Java's Javadoc lookalike and Markdown. +Just like `Kotlin` itself, `Dokka` supports mixed-language `Kotlin`/`Java` projects. It understands +[KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in `Kotlin` source files as well +as `Javadoc` comments in `Java` files, and can generate documentation in multiple formats including its +own `HTML` format, Java's `Javadoc` lookalike and Markdown. -Some libraries that use Dokka for API reference docs: +Some libraries that use `Dokka` for API reference docs: * [kotlinx.coroutines](https://kotlin.github.io/kotlinx.coroutines/) * [kotlinx.serialization](https://kotlin.github.io/kotlinx.serialization/) * [Ktor](https://api.ktor.io/) * [Spring Framework](https://docs.spring.io/spring-framework/docs/current/kdoc-api/) -Dokka provides support for the following build systems: +`Dokka` provides support for the following build systems: * [Gradle](user_guide/applying/gradle.md) (preffered) * [Maven](user_guide/applying/maven.md) diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index 9655b65642..63943c9544 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -20,6 +20,12 @@ theme: social: - type: 'github' link: 'https://github.com/Kotlin/dokka' + features: + - navigation.expand + - navigation.tabs + - navigation.instant + - navigation.indexes + - navigation.top # Extensions markdown_extensions: @@ -37,6 +43,8 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.magiclink - pymdownx.smartsymbols + - attr_list + - md_in_html - pymdownx.superfences: custom_fences: - name: mermaid @@ -47,9 +55,14 @@ markdown_extensions: #dev_addr: 127.0.0.1:3001 nav: - - Home: index.md + - Home: + - index.md + - User guides: user_guide/introduction.md + - Developer guides: developer_guide/introduction.md + - Community: community/slack.md + - FAQ: faq.md - User guides: - - Introduction: user_guide/introduction.md + - user_guide/introduction.md - Applying Dokka: - Gradle: user_guide/applying/gradle.md - Maven: user_guide/applying/maven.md @@ -60,28 +73,25 @@ nav: - Versioning plugin: user_guide/plugins/versioning-plugin.md - Android plugin: user_guide/plugins/android-plugin.md - Developer guides: - - Introduction: developer_guide/introduction.md + - developer_guide/introduction.md - Workflow: developer_guide/workflow.md - - Architecture: - - Overview: developer_guide/architecture/architecture_overview.md + - Internals: + - Architecture: developer_guide/architecture/architecture_overview.md - Data model: - Documentables: developer_guide/architecture/data_model/documentables.md - Page & Content: developer_guide/architecture/data_model/page_content.md - Extra properties: developer_guide/architecture/data_model/extra.md - Extension points: - - Introduction: developer_guide/architecture/extension_points/introduction.md + - developer_guide/architecture/extension_points/introduction.md - Core extension points: developer_guide/architecture/extension_points/core_extensions.md - Base extensions: developer_guide/architecture/extension_points/base_extensions.md - Plugin development: - - Introduction: developer_guide/plugin-development/introduction.md + - Plugin development: developer_guide/plugin-development/introduction.md - Simple plugin tutorial: developer_guide/plugin-development/simple-plugin-guide.md - Community: - Slack: community/slack.md - Community plugins: community/plugins-list.md - FAQ: faq.md -extra_javascript: - - expand_navigation.js - extra_css: - dokka_colors.css diff --git a/examples/plugin/hide-internal-api/LICENSE b/examples/plugin/hide-internal-api/LICENSE new file mode 100644 index 0000000000..5735cde7ca --- /dev/null +++ b/examples/plugin/hide-internal-api/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014-2020 JetBrains s.r.o. and Dokka Plugin Template project contributors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/examples/plugin/hide-internal-api/README.md b/examples/plugin/hide-internal-api/README.md new file mode 100644 index 0000000000..59d27c79d0 --- /dev/null +++ b/examples/plugin/hide-internal-api/README.md @@ -0,0 +1 @@ +This plugin uses [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). diff --git a/examples/plugin/hide-internal-api/build.gradle.kts b/examples/plugin/hide-internal-api/build.gradle.kts new file mode 100644 index 0000000000..beb25aade2 --- /dev/null +++ b/examples/plugin/hide-internal-api/build.gradle.kts @@ -0,0 +1,115 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import java.net.URI + +plugins { + kotlin("jvm") version "1.6.21" + id("org.jetbrains.dokka") version "1.6.21" + `maven-publish` + signing +} + +group = "org.example" +version = "1.4-SNAPSHOT" + +repositories { + mavenCentral() + jcenter() +} + +val dokkaVersion: String by project +dependencies { + implementation(kotlin("stdlib")) + compileOnly("org.jetbrains.dokka:dokka-core:$dokkaVersion") + implementation("org.jetbrains.dokka:dokka-base:$dokkaVersion") + + testImplementation(kotlin("test-junit")) + testImplementation("org.jetbrains.dokka:dokka-test-api:$dokkaVersion") + testImplementation("org.jetbrains.dokka:dokka-base-test-utils:$dokkaVersion") +} + +val dokkaOutputDir = "$buildDir/dokka" + +tasks { + withType { + kotlinOptions.jvmTarget = "1.8" + } + dokkaHtml { + outputDirectory.set(file(dokkaOutputDir)) + } +} + +val javadocJar by tasks.registering(Jar::class) { + dependsOn(tasks.dokkaHtml) + archiveClassifier.set("javadoc") + from(dokkaOutputDir) +} + +java { + withSourcesJar() +} + +publishing { + publications { + val dokkaTemplatePlugin by creating(MavenPublication::class) { + artifactId = project.name + from(components["java"]) + artifact(javadocJar.get()) + + pom { + name.set("Dokka template plugin") + description.set("This is a plugin template for Dokka") + url.set("https://github.com/Kotlin/dokka-plugin-template/") + + licenses { + license { + name.set("The Apache Software License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("repo") + } + } + + developers { + developer { + id.set("JetBrains") + name.set("JetBrains Team") + organization.set("JetBrains") + organizationUrl.set("http://www.jetbrains.com") + } + } + + scm { + connection.set("scm:git:git://github.com/Kotlin/dokka-plugin-template.git") + url.set("https://github.com/Kotlin/dokka-plugin-template/tree/master") + } + } + } + signPublicationsIfKeyPresent(dokkaTemplatePlugin) + } + + repositories { + maven { + url = URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/") + credentials { + username = System.getenv("SONATYPE_USER") + password = System.getenv("SONATYPE_PASSWORD") + } + } + } +} + +fun Project.signPublicationsIfKeyPresent(publication: MavenPublication) { + val signingKeyId: String? = System.getenv("SIGN_KEY_ID") + val signingKey: String? = System.getenv("SIGN_KEY") + val signingKeyPassphrase: String? = System.getenv("SIGN_KEY_PASSPHRASE") + + if (!signingKey.isNullOrBlank()) { + extensions.configure("signing") { + if (signingKeyId?.isNotBlank() == true) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingKeyPassphrase) + } else { + useInMemoryPgpKeys(signingKey, signingKeyPassphrase) + } + sign(publication) + } + } +} diff --git a/examples/plugin/hide-internal-api/gradle.properties b/examples/plugin/hide-internal-api/gradle.properties new file mode 100644 index 0000000000..c19e865370 --- /dev/null +++ b/examples/plugin/hide-internal-api/gradle.properties @@ -0,0 +1 @@ +dokkaVersion=1.6.21 diff --git a/examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.jar b/examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

          K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.properties b/examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..e750102e09 --- /dev/null +++ b/examples/plugin/hide-internal-api/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/plugin/hide-internal-api/gradlew b/examples/plugin/hide-internal-api/gradlew new file mode 100755 index 0000000000..4f906e0c81 --- /dev/null +++ b/examples/plugin/hide-internal-api/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/plugin/hide-internal-api/gradlew.bat b/examples/plugin/hide-internal-api/gradlew.bat new file mode 100644 index 0000000000..ac1b06f938 --- /dev/null +++ b/examples/plugin/hide-internal-api/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/plugin/hide-internal-api/settings.gradle.kts b/examples/plugin/hide-internal-api/settings.gradle.kts new file mode 100644 index 0000000000..359689cd26 --- /dev/null +++ b/examples/plugin/hide-internal-api/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "hide-internal-api" diff --git a/examples/plugin/hide-internal-api/src/main/kotlin/org/example/dokka/plugin/HideInternalApiPlugin.kt b/examples/plugin/hide-internal-api/src/main/kotlin/org/example/dokka/plugin/HideInternalApiPlugin.kt new file mode 100644 index 0000000000..e0edac4bbf --- /dev/null +++ b/examples/plugin/hide-internal-api/src/main/kotlin/org/example/dokka/plugin/HideInternalApiPlugin.kt @@ -0,0 +1,34 @@ +package org.example.dokka.plugin + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.transformers.documentables.SuppressedByConditionDocumentableFilterTransformer +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin + +class HideInternalApiPlugin : DokkaPlugin() { + val myFilterExtension by extending { + plugin().preMergeDocumentableTransformer providing ::HideInternalApiTransformer + } +} + +class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(context) { + + override fun shouldBeSuppressed(d: Documentable): Boolean { + val annotations: List = + (d as? WithExtraProperties<*>) + ?.extra + ?.allOfType() + ?.flatMap { it.directAnnotations.values.flatten() } + ?: emptyList() + + return annotations.any { isInternalAnnotation(it) } + } + + private fun isInternalAnnotation(annotation: Annotations.Annotation): Boolean { + return annotation.dri.packageName == "org.jetbrains.dokka.internal.test" + && annotation.dri.classNames == "Internal" + } +} diff --git a/examples/plugin/hide-internal-api/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/examples/plugin/hide-internal-api/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 0000000000..a179d0c92a --- /dev/null +++ b/examples/plugin/hide-internal-api/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.example.dokka.plugin.HideInternalApiPlugin diff --git a/examples/plugin/hide-internal-api/src/test/kotlin/org/example/dokka/plugin/HideInternalApiPluginTest.kt b/examples/plugin/hide-internal-api/src/test/kotlin/org/example/dokka/plugin/HideInternalApiPluginTest.kt new file mode 100644 index 0000000000..f98208d02d --- /dev/null +++ b/examples/plugin/hide-internal-api/src/test/kotlin/org/example/dokka/plugin/HideInternalApiPluginTest.kt @@ -0,0 +1,44 @@ +package org.example.dokka.plugin + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.Test +import kotlin.test.assertEquals + +class HideInternalApiPluginTest : BaseAbstractTest() { + @Test + fun `should hide annotated functions`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + val hideInternalPlugin = HideInternalApiPlugin() + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package org.jetbrains.dokka.internal.test + | + |annotation class Internal + | + |fun shouldBeVisible() {} + | + |@Internal + |fun shouldBeExcludedFromDocumentation() {} + """.trimMargin(), + configuration = configuration, + pluginOverrides = listOf(hideInternalPlugin) + ) { + preMergeDocumentablesTransformationStage = { modules -> + val testModule = modules.single { it.name == "root" } + val testPackage = testModule.packages.single { it.name == "org.jetbrains.dokka.internal.test" } + + val packageFunctions = testPackage.functions + assertEquals(1, packageFunctions.size) + assertEquals("shouldBeVisible", packageFunctions[0].name) + } + } + } +} From 094f388ac7657b75e98128f2bc109ce2a1e52068 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Tue, 7 Jun 2022 20:35:32 +0200 Subject: [PATCH 4/9] Proofread developer guides --- docs/src/doc/docs/community/plugins-list.md | 52 +++++++++---- docs/src/doc/docs/community/slack.md | 4 +- .../architecture/architecture_overview.md | 29 ++++--- .../architecture/data_model/documentables.md | 56 +++++++++++--- .../architecture/data_model/extra.md | 8 +- .../architecture/data_model/page_content.md | 15 ++-- .../extension_points/base_extensions.md | 7 +- .../extension_points/core_extensions.md | 17 ++-- .../extension_points/introduction.md | 8 +- .../doc/docs/developer_guide/introduction.md | 4 +- .../plugin-development/introduction.md | 16 ++-- ...gin-guide.md => sample-plugin-tutorial.md} | 73 ++++++++++-------- docs/src/doc/docs/developer_guide/workflow.md | 11 +-- docs/src/doc/docs/dokka_colors.css | 2 +- docs/src/doc/docs/images/mermaid_demo.png | Bin 0 -> 91709 bytes docs/src/doc/docs/index.md | 25 ++++-- docs/src/doc/mkdocs.yml | 14 ++-- .../dokka/templates/includes/header.ftl | 4 +- .../templates/includes/page_metadata.ftl | 2 +- 19 files changed, 222 insertions(+), 125 deletions(-) rename docs/src/doc/docs/developer_guide/plugin-development/{simple-plugin-guide.md => sample-plugin-tutorial.md} (77%) create mode 100644 docs/src/doc/docs/images/mermaid_demo.png diff --git a/docs/src/doc/docs/community/plugins-list.md b/docs/src/doc/docs/community/plugins-list.md index dc88ade3cb..8a01a6845d 100644 --- a/docs/src/doc/docs/community/plugins-list.md +++ b/docs/src/doc/docs/community/plugins-list.md @@ -1,6 +1,12 @@ # Dokka community plugins -TODO add a link on how to apply plugins +On this page you can find `Dokka` plugins which are supported by both `Dokka` maintainers and community members. + +If you want to add your plugin to this list, get in touch with maintainers via [Slack](../community/slack.md) +or `GitHub`. + +If you want to learn how to develop plugins for `Dokka`, see +[Plugin development](../developer_guide/plugin-development/introduction.md) section. ## Output Formats @@ -8,7 +14,7 @@ TODO add a link on how to apply plugins Javadoc plugin adds a `Javadoc` output format that looks like Java's `Javadoc`, but it's for the most part a lookalike, so you may experience problems if you try to use it with a tool that expects native -`Javadocs` generated by `Java`. +`Javadoc` documentation generated by `Java`. `Javadoc` plugin does not support multiplatform projects and does not have a multi-module task. @@ -26,7 +32,7 @@ a lookalike, so you may experience problems if you try to use it with a tool tha ### GFM (Alpha) -`GFM` plugins adds an ability to generate documentation in `GitHub flavoured Markdown` format. Supports both +`GFM` plugins adds the ability to generate documentation in `GitHub flavoured Markdown` format. Supports both multimodule and multiplatform projects, and is shipped together with `Dokka`, so you can start using it right away with one of the following tasks: @@ -40,26 +46,26 @@ ___ //[dokka-debug-kts](#gfm)/[org.jetbrains.dokka.test](#gfm)/[MyClass](#gfm) -# MyClass +#### MyClass [jvm] class [MyClass](#gfm) KDoc that describes this class -## Constructors +##### Constructors | | | |---|---| | [MyClass](#gfm) | [jvm]
          fun [MyClass](#gfm)() | -## Functions +##### Functions | Name | Summary | |------------------|---| | [function](#gfm) | [jvm]
          fun [function](#gfm)(): [String](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html)
          KDoc comment on top of this function | -## Properties +##### Properties | Name | Summary | |---|------------------------------------------------------------------------------------------------------------------------------------------------| @@ -74,7 +80,7 @@ ___ ### Jekyll (Alpha) -`Jekyll` plugins adds an ability to generate documentation in `Jekyll flavoured Markdown` format. Supports both +`Jekyll` plugins adds the ability to generate documentation in `Jekyll flavoured Markdown` format. Supports both multi-module and multiplatform projects, and is shipped together with `Dokka`, so you can start using it right away with one of the following tasks: @@ -91,18 +97,18 @@ right away with one of the following tasks: ### Mathjax -[MathJax](https://docs.mathjax.org/) allows you to include mathematics in your web pages. `Dokka` `MathJax` plugin -adds an ability to render mathematics from source code comments. +[MathJax](https://docs.mathjax.org/) allows you to include mathematics in your web pages. `MathJax` plugin +adds the ability to render mathematics from source code comments. -If `MathJax` plugin encounters `@usesMathJax` `KDoc` tag, it adds `MathJax.js` 2.7.6 with `config=TeX-AMS_SVG` +If `MathJax` plugin encounters `@usesMathJax` `KDoc` tag, it adds `MathJax.js` (ver. 2) with `config=TeX-AMS_SVG` to generated `HTML` pages. Usage example: ```kotlin /** - * @usesMathJax - * * Some math \(\sqrt{3x-1}+(1+x)^2\) + * + * @usesMathJax */ class Foo {} ``` @@ -118,6 +124,26 @@ Which results in: [Mermaid JS](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code. `Mermaid` plugin allows rendering such diagrams and visualizations found in source code documentation. +Usage example: +```kotlin +/** + * See the graph for more details: + * \```mermaid + * graph LR + * A[Christmas] -->|Get money| B(Go shopping) + * B --> C{Let me think} + * C -->|One| D[Laptop] + * C -->|Two| E[iPhone] + * C -->|Three| F[fa:fa-car Car] + * \``` + */ +class CompositeSubscription +``` + +Which results in: + +![Mermaid demo](../images/mermaid_demo.png){ width="700" } + For more information and examples, see [Html Mermaid Dokka plugin](https://github.com/glureau/dokka-mermaid) repository on GitHub. diff --git a/docs/src/doc/docs/community/slack.md b/docs/src/doc/docs/community/slack.md index 98716523f9..290d4a180e 100644 --- a/docs/src/doc/docs/community/slack.md +++ b/docs/src/doc/docs/community/slack.md @@ -1,7 +1,7 @@ # Slack channel -Dokka has a dedicated `#dokka` channel in the Kotlin Community Slack, where you can ask questions and chat -about using, customizing or contributing to Dokka. +`Dokka` has a dedicated `#dokka` channel in the `Kotlin Community Slack`, where you can ask questions and chat +about using, customizing or contributing to `Dokka`. [Follow the instructions](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up) to get an invite or [connect directly](https://kotlinlang.slack.com). diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md index 17bd7568b8..54ca9d331d 100644 --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -1,21 +1,23 @@ # Architecture overview -Normally, you would think that a tool like Dokka simply parses some programming language sources and generates +Normally, you would think that a tool like `Dokka` simply parses some programming language sources and generates `HTML` pages for whatever it sees along the way, with little to no abstractions. That would be the simplest and shortest way to implement a documentation engine. -However, it was clear that Dokka may need to generate documentation from various sources (not only `Kotlin`), that users +However, it was clear that `Dokka` may need to generate documentation from various sources (not only `Kotlin`), that users might request additional output formats (like `Markdown`), that users might need additional features like supporting -custom KDoc tags or rendering `mermaid.js` diagrams - all these things would require changing a lot of code inside -Dokka itself if all solutions were hardcoded. +custom `KDoc` tags or rendering `mermaid.js` diagrams - all these things would require changing a lot of code inside +`Dokka` itself if all solutions were hardcoded. -For this reason, Dokka was built from the ground up to be extensible and customizable. You can think of the general -flow of generating documentation with Dokka as mapping one intermediate representation / abstraction into another. -You, as a Dokka developer or a plugin writer, can use extension points and introduce selective changes to the -model on any level without touching everything else. +For this reason, `Dokka` was built from the ground up to be extensible and customizable. You can think of the general +flow of generating documentation with `Dokka` as mapping one intermediate representation / abstraction into another. +You, as a `Dokka` developer or a plugin writer, can use extension points and introduce selective changes to the +model on one particular level without touching the rest. ## Overview of data model +Below you can find the general flow and transformation of Dokka's models. + ```mermaid flowchart TD Input --> Documentables --> Documentables --> Pages --> Pages --> Output @@ -25,9 +27,13 @@ flowchart TD * `Documentables` - unified data model that represents any parsed sources as a tree. Examples: class/function/package/property * `Pages` - universal model that represents pages (e.g a function/property page) and its content - (lists, text, code blocks) that the users needs to see -* `Output` - specific output format like `HTML`/`Markdown`/`Javadoc`/etc. This is a mapping of content to + (lists, text, code blocks) that the users needs to see. Not to be confused with `.html` pages. Goes hand in hand + with so-called Content Model. +* `Output` - specific output format like `HTML`/`Markdown`/`Javadoc`/etc. This is a mapping of pages/content model to some human-readable and visual representation. For instance: + * `PageNode` is mapped as + * `.html` file for `HTML` format + * `.md` file for `Markdown` format * `ContentList` is mapped as * `

        • ` / `
            ` for `HTML` format * `1.` / `*` for `Markdown` format @@ -63,7 +69,7 @@ class MyPlugin : DokkaPlugin() { signatureProvider with KotlinSignatureProvider() } - // register our own extension in another plugin and override its default + // register our own extension in base plugin and override its default val dokkaBasePlugin by lazy { plugin() } val multimoduleLocationProvider by extending { (dokkaBasePlugin.locationProviderFactory @@ -74,6 +80,7 @@ class MyPlugin : DokkaPlugin() { // use a registered extention, pretty much dependency injection class MyExtension(val context: DokkaContext) { + val signatureProvider: SignatureProvider = context.plugin().querySingle { signatureProvider } fun doSomething() { diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md index 5fd8db5be5..6c66f9be98 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md @@ -1,7 +1,7 @@ -# Documentables +# Documentables Model -Documentables represent data that is parsed from some sources. Think of this data model as of something that could be -seen or produced by a compiler frontend, and it's not far off from the truth. +Documentables represent data that is parsed from sources. Think of this data model as of something that could be +seen or produced by a compiler frontend, it's not far off from the truth. By default, documentables are parsed from `Descriptor` (for `Kotlin`) and [Psi](https://plugins.jetbrains.com/docs/intellij/psi.html) @@ -67,7 +67,7 @@ data class DClass( ___ -There are three non-documentable classes important for this model: +There are three non-documentable classes that important for this model: * `DRI` * `SourceSetDependent` @@ -75,7 +75,7 @@ There are three non-documentable classes important for this model: ### DRI -`DRI` stans for _Dokka Resource Identifier_ - a unique value that identifies specific `Documentable`. +`DRI` stans for _Dokka Resource Identifier_ - a unique value that identifies a specific `Documentable`. All references and relations between documentables (other than direct ownership) are described using `DRI`. For example, `DFunction` with a parameter of type `Foo` has only `Foo`'s `DRI`, not the actual reference @@ -83,7 +83,7 @@ to `Foo`'s `Documentable` object. #### Example -For an example of how a `DRI` can look like, let's look at `limitedParallelism` function from `kotlinx.coroutines`: +For an example of how a `DRI` can look like, let's take the `limitedParallelism` function from `kotlinx.coroutines`: ```kotlin package kotlinx.coroutines @@ -152,22 +152,54 @@ ___ ## Documentation model -Documentation model is used along `Documentable` model to store data obtained by parsing +Documentation model is used alongside Documentables to store data obtained by parsing code comments (such as `KDoc`/`Javadoc`). ### DocTag -`DocTag` describes a specific documentation syntax element, universal across source languages. For instance, -DocTag `B` is the same for `**bold**` in `Kotlin` and `bold` in `Java`. +`DocTag` describes a specific documentation syntax element. -However, some `DocTag` elements are specific to a certain language, there are many such example for `Java` -because it allows HTML tags inside `Javadoc` comments, some of which are not possible with `Markdown`. +It's universal across source languages. For instance, DocTag `B` is the same for `**bold**` in `Kotlin` and +`bold` in `Java`. + +However, some `DocTag` elements are specific to a certain language, there are many such examples for `Java` +because it allows HTML tags inside `Javadoc` comments, some of which are simply not possible to reproduce with `Markdown`. `DocTag` elements can be deeply nested with other `DocTag` children elements. +Examples: + +```kotlin +data class H1( + override val children: List = emptyList(), + override val params: Map = emptyMap() +) : DocTag() + +data class H2( + override val children: List = emptyList(), + override val params: Map = emptyMap() +) : DocTag() + +data class Strikethrough( + override val children: List = emptyList(), + override val params: Map = emptyMap() +) : DocTag() + +data class Strong( + override val children: List = emptyList(), + override val params: Map = emptyMap() +) : DocTag() + +data class CodeBlock( + override val children: List = emptyList(), + override val params: Map = emptyMap() +) : Code() + +``` + ### TagWrapper -`TagWrapper` described a whole comment description or a specific comment tag. +`TagWrapper` describes the whole comment description or a specific comment tag. For example: `@see` / `@author` / `@return`. Since each such section may contain formatted text inside of it, each `TagWrapper` has `DocTag` children. diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md index 69025d7957..5dda7ef394 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md @@ -2,7 +2,10 @@ ## Introduction -`ExtraProperty` classes are used both by [Documentable](documentables.md) and [Content](page_content.md) models. +`ExtraProperty` classes are used both by [Documentable](documentables.md) and [Content](page_content.md#content-model) +models. + +Source code for `ExtraProperty`: ```kotlin interface ExtraProperty { @@ -21,6 +24,7 @@ when declaring new extras: ```kotlin data class CustomExtra( + [any data relevant to your extra], [any data relevant to your extra] ): ExtraProperty { override val key: CustomExtra.Key = CustomExtra @@ -51,7 +55,7 @@ which can only be stored in a specific `Documentable`. ## Usage example -In following example we will create `DFunction`-only property, store it and then retrieve its value: +In following example we will create a `DFunction`-only property, store it and then retrieve its value: ```kotlin data class CustomExtra(val customExtraValue: String) : ExtraProperty { diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md index a07e90658e..86fe49acab 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md @@ -9,7 +9,7 @@ Page model represents the structure of documentation pages to be generated. Duri is processed separately, so one page corresponds to exactly one output file. Page model is independent of the final output format, in other words it's universal. Which extension the pages -should be created as (`.html`, `.md`, etc) and how is up to the `Renderer` part. +should be created as (`.html`, `.md`, etc) and how is up to the `Renderer`. Subclasses of `PageNode` represent different kinds of rendered pages, such as `ModulePage`, `PackagePage`, `ClasslikePage`, `MemberPage` (properties, functions), etc. @@ -28,7 +28,7 @@ flowchart TD firstPackageClasslike --> firstPackageClasslikeSecondMember[MemberPageNode - Property] ``` -Almost all pages are `ContentPage` - it's the type of `Page` that has `Content` on it. +Almost all pages are derivatives of `ContentPage` - it's the type of `Page` that has `Content` on it. ## Content Model @@ -44,7 +44,7 @@ to wrap all children with some style. // real example of composing content using `DocumentableContentBuilder` DSL orderedList { item { - text("The following table is nested in a list:") + text("This list contains a nested table:") table { header { text("Col1") @@ -61,16 +61,17 @@ orderedList { } ``` -It is then responsibility of `Renderer` (i.e specific output format) to render it the way it wants. For instance, -`HtmlRenderer` might render `ContentCodeBlock` as `text`, but `CommonmarkRenderer` might render it using -backticks. +It is then responsibility of `Renderer` (i.e specific output format) to render it the way it wants. + +For instance, `HtmlRenderer` might render `ContentCodeBlock` as `text`, but `CommonmarkRenderer` might +render it using backticks. ___ ### DCI Each node is identified by unique `DCI`, which stands for _Dokka Content Identifier_. `DCI` aggregates `DRI`s of all -`Documentables` that make up specific `ContentNode`. +`Documentables` that make up a specific `ContentNode`. ```kotlin data class DCI(val dri: Set, val kind: Kind) diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md index 32bb578a1f..16a52fab42 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md @@ -1,10 +1,11 @@ # Base extensions -`DokkaBase` is a base plugin which defines a number of default implementations for `CoreExtensions` as well as declares -its own, more high-level, extension points to be used from other plugins and output formats. +`DokkaBase` class is a base plugin which defines a number of default implementations for `CoreExtensions` as well as +declares its own, more high-level extension points to be used from other plugins and output formats. It's very convenient to use extension points and defaults defined in `DokkaBase` if you have an idea for a simple -plugin that only needs to provide a few extensions or change a single place and have everything else be the default. +plugin that only needs to provide a few extensions or change a single extension point and have everything else be the +default. `DokkaBase` is used extensively for Dokka's own output formats such as `HTML`, `Markdown`, `Mathjax` and others. diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md index 28f1564d6d..e540c79662 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md @@ -21,11 +21,13 @@ object CoreExtensions { } ``` +On this page we'll go over each extension point individually. + ## PreGenerationChecker -`PreGenerationChecker` can be used to run some check to check for constraints. +`PreGenerationChecker` can be used to run some checks and constraints. -For instance, `Javadoc` plugin does not support generating documentation for multi-platform project, so it uses +For instance, `Javadoc` plugin does not support generating documentation for multi-platform projects, so it uses `PreGenerationChecker` to check for multi-platform source sets and fails if it finds any. ## Generation @@ -70,7 +72,7 @@ flowchart TD `SourceToDocumentableTranslator` translates sources into documentable model. `Kotlin` and `Java` sources are supported by default, but you can analyze any language as long as you can map -it to `Documentables` model. +it to the [Documentable](../data_model/documentables.md) model. For reference, see @@ -81,17 +83,18 @@ For reference, see This extension point actually comes from `DokkaBase` and is not a core extension point, but it's used in `SingleModuleGeneration` nonetheless. If you are implementing your own plugin without relying on `DokkaBase`, -you can either have something similar or rely on [DocumentableTransformer](#documentabletransformer). +you can either introduce a similar extension point or rely on [DocumentableTransformer](#documentabletransformer) which +will be discussed below. `PreMergeDocumentableTransformer` allows applying any transformation to [Documentables model](../data_model/documentables.md) before different source sets are merged. -Useful if you want to filter/map existing documentables. For instance, if you want to only include members annotated -as `@PublicAPI`, you most likely need an implementation of `PreMergeDocumentableTransformer`. +Useful if you want to filter/map existing documentables. For instance, 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 overriden and takes care of the rest. +requires one function to be overridden. The rest is taken care of. #### DocumentableMerger diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md index 1751ecefce..28fd0019cf 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md @@ -1,4 +1,4 @@ -# Overview +# Introduction to extension points In this section you can learn how to create new extension points, how to use and configure existing ones and how to query for extensions when generating documentation. @@ -29,7 +29,7 @@ See [Extending from extension points](#extending-from-extension-points) for exam You can use extension points to provide your own implementation(s) in order to customize plugin's behaviour. -If you want to provide a default implementation of your plugin's extension point, you can do that within the same class: +You can do that within the same class as the extension point itself: ```kotlin open class MyPlugin : DokkaPlugin() { @@ -47,6 +47,8 @@ class DefaultSampleExtension : SampleExtensionPointInterface { } ``` +___ + If you want to extend someone else's plugin (including `DokkaBase`), you can use plugin querying API to do that. In the example below we will extend `MyPlugin` that was created above with our own implementation of `SampleExtensionPointInterface`. @@ -81,7 +83,7 @@ You can read more on what you can do with `context` in [Obtaining extension inst ### Override -By extending an extension point, you are registering an additional extension. This behaviour is expected for some +By extending an extension point, you are registering an _additional_ extension. This behaviour is expected for some extension points, for instance documentable transformers, since all transformers do their own transformations and all of them will be invoked before proceeding. diff --git a/docs/src/doc/docs/developer_guide/introduction.md b/docs/src/doc/docs/developer_guide/introduction.md index a15a0f2540..feb601fe90 100644 --- a/docs/src/doc/docs/developer_guide/introduction.md +++ b/docs/src/doc/docs/developer_guide/introduction.md @@ -4,13 +4,13 @@ The purpose of `Developer guides` section is to get you acquainted with Dokka's your own plugins or contributing features and fixes to Dokka itself. If you want to start hacking on Dokka right away, the only thing you need to be aware of is the -[general workflow](workflow.md), it will teach you how to build, debug and test Dokka. +[general workflow](workflow.md), it will teach you how to build, debug and test Dokka locally. If you want to get into plugin development quick, see [Introduction to plugin development](plugin-development/introduction.md). If you have time to spare and want to know more about Dokka's internals, its architecture and capabilities, follow -[Architecture overview](architecture/architecture_overview.md) and subsequent sections. +[Architecture overview](architecture/architecture_overview.md) and subsequent sections inside `Internals`. Having read through all the developer guides, you'll have a pretty good unrestanding of Dokka and how to develop for it. diff --git a/docs/src/doc/docs/developer_guide/plugin-development/introduction.md b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md index d1314fcfad..fbfb32ac7a 100644 --- a/docs/src/doc/docs/developer_guide/plugin-development/introduction.md +++ b/docs/src/doc/docs/developer_guide/plugin-development/introduction.md @@ -1,15 +1,15 @@ -# Plugin Development +# Introduction to plugin development In order to have an easier time developing plugins, it's a good idea to go through -[Dokka's internals](../architecture/architecture_overview.md) to learn more about -[extensions](../architecture/extension_points/introduction.md) and -[data model](../architecture/data_model/documentables.md). +[Dokka's internals](../architecture/architecture_overview.md) first to learn more about its +[data model](../architecture/data_model/documentables.md) and +[extensions](../architecture/extension_points/introduction.md). ## Setup ### Template -The easiest way to start is to use a convenient [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). +The easiest way to start is to use the convenient [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). It has pre-configured dependencies, publishing and signing of your artifacts. ### Manual @@ -38,7 +38,7 @@ All instances are automatically loaded during Dokka setup using `java.util.Servi Dokka provides a set of entry points for which you can create your own implementations. If you are not sure which extension point to use, have a look at [core extensions](../architecture/extension_points/core_extensions.md) and -[base extensions](../architecture/extension_points/base_extensions.md) - it might give you a general direction. +[base extensions](../architecture/extension_points/base_extensions.md). You can learn how to declare extension points and use extensions in [Introduction to Extension points](../architecture/extension_points/introduction.md). @@ -48,8 +48,8 @@ versions of Dokka. ## Example -You can follow [a simple plugin guide](simple-plugin-guide.md) which covers creation of a simple plugin: hide members -annotated with `@Internal` annotation, i.e exclude them from generated documentation. +You can follow the [sample plugin tutorial](sample-plugin-tutorial.md) which covers creation of a simple plugin: hide members +annotated with your own `@Internal` annotation, that is exclude these members from generated documentation. Fore more practical examples, have a look at sources of [community plugins](../../community/plugins-list.md). diff --git a/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md similarity index 77% rename from docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md rename to docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md index 098b11c861..f99344017c 100644 --- a/docs/src/doc/docs/developer_guide/plugin-development/simple-plugin-guide.md +++ b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md @@ -1,9 +1,9 @@ -# Simple plugin guide +# Sample plugin tutorial We'll go over creating a simple plugin that covers a very common use case: generate documentation for everything except for members annotated with a custom `@Internal` annotation - they should be hidden. -Plugin functionality will be tested on a simple project with the following code: +The plugin will be tested with the following code: ```kotlin package org.jetbrains.dokka.internal.test @@ -18,18 +18,18 @@ fun shouldBeExcludedFromDocumentation() {} Expected behavior: function `shouldBeExcludedFromDocumentation` should not be visible in generated documentation. -Full plugin code can be found in Dokka's examples under +Full source code of this tutorial can be found in Dokka's examples under [hide-internal-api](https://github.com/Kotlin/dokka/examples/plugin/hide-internal-api). ## Preparing the project -We'll begin by using [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). Press the -green `Use this template` button and open this project in an IDE of your choice (IntelliJ IDEA is recommended). +We'll begin by using [Dokka plugin template](https://github.com/Kotlin/dokka-plugin-template). Press the +`Use this template` button and +[open this project in IntelliJ IDEA](https://www.jetbrains.com/idea/guide/tutorials/working-with-gradle/opening-a-gradle-project/). -We can start by changing `template` package and renaming the pre-made `MyAwesomeDokkaPlugin` class to something -of our own. +First, let's rename the pre-made `template` package and `MyAwesomeDokkaPlugin` class to something of our own. -For instance, `org.example.dokka.plugin.ExposePublicApiPlugin`: +For instance, package can be renamed to `org.example.dokka.plugin` and the class to `HideInternalApiPlugin`: ```kotlin package org.example.dokka.plugin @@ -47,17 +47,20 @@ After you do that, make sure to update the path to this class in org.example.dokka.plugin.HideInternalApiPlugin ``` -At this point you can also change project name in `settings.gradle.kts` and `groupId` in `build.gradle.kts`. +At this point you can also change project name in `settings.gradle.kts` (to `hide-internal-api` in our case) +and `groupId` in `build.gradle.kts`. ## Extending Dokka -After preparing the project we can start extending Dokka with our own extension. +After preparing the project we can begin extending Dokka with our own extension. -Having read through [Core extensions](../architecture/extension_points/core_extensions.md) it's clear that we need -a `PreMergeDocumentableTransformer` in order to filter out undesired documentables. Moreover, it mentioned a convenient -abstract transformer `SuppressedByConditionDocumentableFilterTransformer`, so we can begin by implementing it. +Having read through [Core extensions](../architecture/extension_points/core_extensions.md), it's clear that we need +a `PreMergeDocumentableTransformer` extension in order to filter out undesired documentables. -Create a new class, place it next to your plugin and implement the abstract class method. You should end up with this: +Moreover, the article mentioned a convenient abstract transformer `SuppressedByConditionDocumentableFilterTransformer` +which is perfect for our use case, so we can try to implement it. + +Create a new class, place it next to your plugin and implement the abstract method. You should end up with this: ```kotlin package org.example.dokka.plugin @@ -70,17 +73,18 @@ import org.jetbrains.dokka.plugability.DokkaPlugin class HideInternalApiPlugin : DokkaPlugin() {} class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(context) { + override fun shouldBeSuppressed(d: Documentable): Boolean { return false } } ``` -Now we somehow need to find all annotations applied to `d: Documentable` and see if our annotation is present. However, -it's not very clear how to do that. What usually helps is stopping in debugger and having a look at what fields +Now we somehow need to find all annotations applied to `d: Documentable` and see if our `@Internal` annotation is present. +However, it's not very clear how to do that. What usually helps is stopping in debugger and having a look at what fields and values a given `Documentable` has. -To do that, we'll need to register our extension point first, then we can publish our plugin and set a breakpoint. +To do that, we'll need to register our extension point first, then we can publish our plugin and set the breakpoint. Having read through [Introduction to extensions](../architecture/extension_points/introduction.md), we now know how to register our extensions: @@ -93,7 +97,7 @@ class HideInternalApiPlugin : DokkaPlugin() { } ``` -At this point we're ready to debug our plugin locally. +At this point we're ready to debug our plugin locally, it should already work, but do nothing. ## Debugging @@ -106,7 +110,7 @@ First, let's begin by publishing our plugin to `mavenLocal()`. ./gradlew publishToMavenLocal ``` -This will publish your plugin under the package, project name and version that you've specified in your +This will publish your plugin under the groupId, artifactId and version that you've specified in your `build.gradle.kts`. In our case it's `org.example:hide-internal-api:1.0-SNAPSHOT`. Open a debug project of your choosing and add our plugin to dependencies: @@ -117,7 +121,7 @@ dependencies { } ``` -Next, in that project let's run `dokkaHtml` with debug enabled +Next, in that project let's run `dokkaHtml` with debug enabled: ```bash ./gradlew clean dokkaHtml -Dorg.gradle.debug=true --no-daemon @@ -157,11 +161,11 @@ private fun isInternalAnnotation(annotation: Annotations.Annotation): Boolean { } ``` -Seems like we're done with writing our plugin and can begin testing it. +Seems like we're done with writing our plugin and can begin testing it manually. ## Manual testing -At this point you should be looking a plugin that looks roughly like this: +At this point, the implementation of your plugin should look roughly like this: ```kotlin package org.example.dokka.plugin @@ -200,8 +204,8 @@ class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionD } ``` -Bump plugin version in `gradle.build.kts`, publish it to maven local, run `dokkaHtml` and see if it works! It should. -You should **NOT** be able to see `shouldBeExcludedFromDocumentation` function in generated documentation. +Bump plugin version in `gradle.build.kts`, publish it to maven local, open the debug project and run `dokkaHtml`. +It should work, you should **not** be able to see `shouldBeExcludedFromDocumentation` function in generated documentation. Manual testing is cool and all, but wouldn't it be better if we could somehow write unit tests for it? Indeed! @@ -210,13 +214,13 @@ Manual testing is cool and all, but wouldn't it be better if we could somehow wr You might've noticed that plugin template comes with a pre-made test class. Feel free to move it to another package and rename it. -We are mostly interested in a single test case - non-annotated functions should be visible, and annotated ones should -be hidden. +We are mostly interested in a single test case - functions annotated with `@Internal` should be hidden, while all other +public functions should be visible. Plugin API comes with a set of convenient test utilities that are used to test Dokka itself, so it covers a wide range of use cases. When in doubt, see Dokka's tests for reference. -Below you will find a complete unit tests that passes, and the main takeaways below that. +Below you will find a complete unit test that passes, and the main takeaways below that. ```kotlin package org.example.dokka.plugin @@ -265,18 +269,21 @@ class HideInternalApiPluginTest : BaseAbstractTest() { } ``` -Note that the package of the test code (inside `testInline` function) is the same as the package that we have -hardcoded in our plugin. Make sure to change that if you are following along, otherwise it will fail. +Note that the package of the tested code (inside `testInline` function) is the same as the package that we have +hardcoded in our plugin. Make sure to change that if you are following along but for your own annotation and package, +otherwise it will fail. Things to note and remember: 1. Your test class should extend `BaseAbstractTest`, which contains base utility methods for testing. -2. You can configure Dokka to your liking, enable some specific settings, configure source sets, etc +2. You can configure Dokka to your liking, enable some specific settings, configure source sets, etc. All done via + `dokkaConfiguration` DSL. 3. `testInline` function is the main entry point for unit tests -4. You can pass plugins to be used in tests, notice `pluginOverrides` parameter +4. You can pass plugins to be used in a test, notice `pluginOverrides` parameter 5. You can test Dokka on different stages of generating documentation, the main ones being documentables model generation, pages generation and output generation. Since we implemented our plugin to work during - `PreMergeDocumentableTransformer` stage, we can test it on the same level. + `PreMergeDocumentableTransformer` stage, we can test it on the same level (that is + `preMergeDocumentablesTransformationStage`). 6. You will need to write asserts using the model of whatever stage you choose. For documentables transformation stage - it's documentables, for page generation stage you would have pages, and for output you can have `.html` pages + it's documentables, for page generation stage you would have pages, and for output you can have `.html` files that you will need to parse with JSoup (there are also utilities for that). diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md index ef13ddaaaf..d5c92f8201 100644 --- a/docs/src/doc/docs/developer_guide/workflow.md +++ b/docs/src/doc/docs/developer_guide/workflow.md @@ -1,3 +1,5 @@ +# Workflow + Whether you're contributing a feature/fix to Dokka itself or developing a separate plugin, there's 3 things you'll be doing: @@ -10,9 +12,6 @@ We'll go over each step individually in this section. Examples below will be specific to Gradle and [Gradle’s Kotlin DSL](https://docs.gradle.org/current/userguide/kotlin_dsl.html), but you can apply the same principles and run/test/debug with CLI/Maven runners and build configurations if you wish. -If you have any specific questions which are not covered in this section, feel free to reach out to devs out through -[Slack](../community/slack.md). - ## Building Dokka Building Dokka is pretty straightforward, with one small caveat: when you run `./gradlew build`, it will run @@ -34,8 +33,8 @@ If you see messages like `API check failed for project ..` during `build` phase, changed/added/removed some public API. If the change was intentional, run `./gradlew apiDump` - it will re-generate `.api` files with signatures, -and you should be able to `build` Dokka with no errors. Maintainers will review API changes, so please make sure -it's intentional and rational. +and you should be able to `build` Dokka with no errors. These updated files need to be committed as well. Maintainers +will review API changes thoroughly, so please make sure it's intentional and rational. ## Using/testing locally built Dokka @@ -60,6 +59,8 @@ plugins { } ``` +After completing these steps, you should be able to build documentation using your own version of Dokka. + ## Debugging Dokka Dokka is essentially a gradle plugin, so you can debug it the same way you would any other gradle plugin. diff --git a/docs/src/doc/docs/dokka_colors.css b/docs/src/doc/docs/dokka_colors.css index 7816457f72..69a2435914 100644 --- a/docs/src/doc/docs/dokka_colors.css +++ b/docs/src/doc/docs/dokka_colors.css @@ -1,3 +1,3 @@ .md-header, .md-tabs { - background-color: #bc0fd4; + background-color: #27282c } diff --git a/docs/src/doc/docs/images/mermaid_demo.png b/docs/src/doc/docs/images/mermaid_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..0d0e27b60520f3d28d03a0af818ff727be396a8d GIT binary patch literal 91709 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 W-?;djqvu&-{jm)97(Cfx
            - <#-- This can be handled by a versioning plugin --> + <#-- This can be handled by the versioning plugin --> <@version/>
            @@ -21,4 +21,4 @@
            - \ No newline at end of file + diff --git a/plugins/base/src/main/resources/dokka/templates/includes/page_metadata.ftl b/plugins/base/src/main/resources/dokka/templates/includes/page_metadata.ftl index f897c104dc..7cab45828b 100644 --- a/plugins/base/src/main/resources/dokka/templates/includes/page_metadata.ftl +++ b/plugins/base/src/main/resources/dokka/templates/includes/page_metadata.ftl @@ -3,4 +3,4 @@ <@template_cmd name="pathToRoot"> - \ No newline at end of file + From 56465c15711635d831b7a98e019906f189bf523d Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Mon, 20 Jun 2022 17:06:06 +0200 Subject: [PATCH 5/9] Update referenced version to 1.7.0 --- docs/src/doc/docs/community/plugins-list.md | 2 +- docs/src/doc/docs/developer_guide/workflow.md | 4 ++-- examples/plugin/hide-internal-api/build.gradle.kts | 4 ++-- examples/plugin/hide-internal-api/gradle.properties | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/doc/docs/community/plugins-list.md b/docs/src/doc/docs/community/plugins-list.md index 8a01a6845d..ed8993fc5b 100644 --- a/docs/src/doc/docs/community/plugins-list.md +++ b/docs/src/doc/docs/community/plugins-list.md @@ -155,6 +155,6 @@ For instance, `fun foo(bar: Bar): Baz` will be rendered as `public final Baz foo `Kotlin as Java` plugin is published to maven central as a [separate artifact](https://mvnrepository.com/artifact/org.jetbrains.dokka/kotlin-as-java-plugin): -`org.jetbrains.dokka:kotlin-as-java-plugin:1.6.21`. +`org.jetbrains.dokka:kotlin-as-java-plugin:1.7.0`. [Plugin source code on GitHub](https://github.com/Kotlin/dokka/tree/master/plugins/kotlin-as-java) diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md index d5c92f8201..d3de262f92 100644 --- a/docs/src/doc/docs/developer_guide/workflow.md +++ b/docs/src/doc/docs/developer_guide/workflow.md @@ -42,7 +42,7 @@ Having built Dokka locally, you can publish it to `mavenLocal()`. This will allo project as well as debug code remotely. 1. Change `dokka_version` in `gradle.properties` to something that you will use later on as the dependency version. - For instance, you can set it to something like `1.6.21-my-fix-SNAPSHOT`. This version will be propagated to plugins + For instance, you can set it to something like `1.7.0-my-fix-SNAPSHOT`. This version will be propagated to plugins that reside inside Dokka's project (such as `mathjax`, `kotlin-as-java`, etc). 2. Publish it to maven local (`./gradlew publishToMavenLocal`). Corresponding artifacts should appear in `~/.m2` 3. In the project you want to generate documentation for or debug on, add maven local as a plugin/dependency @@ -55,7 +55,7 @@ repositories { 4. Update your dokka dependency to the version you've just published: ```kotlin plugins { - id("org.jetbrains.dokka") version "1.6.21-my-fix-SNAPSHOT" + id("org.jetbrains.dokka") version "1.7.0-my-fix-SNAPSHOT" } ``` diff --git a/examples/plugin/hide-internal-api/build.gradle.kts b/examples/plugin/hide-internal-api/build.gradle.kts index beb25aade2..2a0cf624d4 100644 --- a/examples/plugin/hide-internal-api/build.gradle.kts +++ b/examples/plugin/hide-internal-api/build.gradle.kts @@ -2,8 +2,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.net.URI plugins { - kotlin("jvm") version "1.6.21" - id("org.jetbrains.dokka") version "1.6.21" + kotlin("jvm") version "1.7.0" + id("org.jetbrains.dokka") version "1.7.0" `maven-publish` signing } diff --git a/examples/plugin/hide-internal-api/gradle.properties b/examples/plugin/hide-internal-api/gradle.properties index c19e865370..e9bfc47c83 100644 --- a/examples/plugin/hide-internal-api/gradle.properties +++ b/examples/plugin/hide-internal-api/gradle.properties @@ -1 +1 @@ -dokkaVersion=1.6.21 +dokkaVersion=1.7.0 From a45b83c10a0fc7e648198cbf902cc05f829d7e57 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Mon, 20 Jun 2022 18:38:23 +0200 Subject: [PATCH 6/9] Address code review points --- buildSrc/src/main/kotlin/org/jetbrains/publication.kt | 2 +- .../architecture/data_model/documentables.md | 5 ++++- .../developer_guide/architecture/data_model/extra.md | 6 ++++-- .../architecture/data_model/page_content.md | 2 ++ .../architecture/extension_points/core_extensions.md | 11 +++++++---- .../plugin-development/sample-plugin-tutorial.md | 3 ++- docs/src/doc/docs/index.md | 9 +++++---- docs/src/doc/docs/user_guide/applying/gradle.md | 5 +++-- docs/src/doc/mkdocs.yml | 2 +- 9 files changed, 29 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/publication.kt b/buildSrc/src/main/kotlin/org/jetbrains/publication.kt index 10353b97e5..eb45e255f2 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/publication.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/publication.kt @@ -100,7 +100,7 @@ fun Project.configureSonatypePublicationIfNecessary(vararg publications: String) fun MavenPublication.configurePom(projectName: String) { pom { name.set(projectName) - description.set("Dokka is a documentation engine for Kotlin and Java, performing the same function as Javadoc for Java") + description.set("Dokka is an API documentation engine for Kotlin and Java, performing the same function as Javadoc for Java") url.set("https://github.com/Kotlin/dokka") licenses { diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md index 6c66f9be98..f688e21b8c 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md @@ -12,6 +12,8 @@ and [Psi](https://plugins.jetbrains.com/docs/intellij/psi.html) Upon creation, it's a collection of trees, each with `DModule` as root. +Here's an example of how an arbitrary `Documentable` tree might look like: + ```mermaid flowchart TD DModule --> firstPackage[DPackage] @@ -128,7 +130,8 @@ kotlinx.coroutines/MainCoroutineDispatcher/limitedParallelism/#kotlin.Int/Pointi ### SourceSetDependent `SourceSetDependent` helps handling multiplatform data by associating platform-specific data (declared with either -`expect` or `actual` modifier) with particular source sets. +`expect` or `actual` modifier) with particular +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets). This comes in handy if `expect`/`actual` declarations differ. For instance, the default value for `actual` might differ from that declared in `expect`, or code comments written for `expect` might be different from what's written diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md index 5dda7ef394..ea98dd3274 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md @@ -32,8 +32,10 @@ data class CustomExtra( } ``` -Merge strategy for extras is invoked only if merged objects have different values for same Extra. -If you don't expect it to happen, you can omit implementing `mergeStrategyFor` function. +Merge strategy (`mergeStrategyFor` method) for extras is invoked during +[merging](../extension_points/core_extensions.md#documentablemerger) if documentables from different +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) each +have their own `Extra`. ## PropertyContainer diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md index 86fe49acab..65a1b5c102 100644 --- a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md @@ -16,6 +16,8 @@ Subclasses of `PageNode` represent different kinds of rendered pages, such as `M The Page Model is a tree structure, with `RootPageNode` at the root. +Here's an example of how an arbitrary `Page` tree might look like: + ```mermaid flowchart TD RootPageNode --> firstPackage[PackagePageNode] diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md index e540c79662..198fe09320 100644 --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md @@ -28,7 +28,8 @@ On this page we'll go over each extension point individually. `PreGenerationChecker` can be used to run some checks and constraints. For instance, `Javadoc` plugin does not support generating documentation for multi-platform projects, so it uses -`PreGenerationChecker` to check for multi-platform source sets and fails if it finds any. +`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 @@ -87,7 +88,8 @@ you can either introduce a similar extension point or rely on [DocumentableTrans will be discussed below. `PreMergeDocumentableTransformer` allows applying any transformation to -[Documentables model](../data_model/documentables.md) before different source sets are merged. +[Documentables model](../data_model/documentables.md) before different +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) are merged. Useful if you want to filter/map existing documentables. For instance, if you want to exclude members annotated with `@Internal`, you most likely need an implementation of `PreMergeDocumentableTransformer`. @@ -105,8 +107,9 @@ requires one function to be overridden. The rest is taken care of. `DocumentableTransformer` performs the same function as `PreMergeDocumentableTransformer`, but after merging source sets. -Notable example is `InheritorsExtractorTransformer`, it extracts inherited classes data across source sets -and creates an inheritance map. +Notable example is `InheritorsExtractorTransformer`, it extracts inherited classes data across +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) and creates an inheritance +map. #### DocumentableToPageTranslator diff --git a/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md index f99344017c..0de85e7577 100644 --- a/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md +++ b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md @@ -276,7 +276,8 @@ otherwise it will fail. Things to note and remember: 1. Your test class should extend `BaseAbstractTest`, which contains base utility methods for testing. -2. You can configure Dokka to your liking, enable some specific settings, configure source sets, etc. All done via +2. You can configure Dokka to your liking, enable some specific settings, configure + [source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets), etc. All done via `dokkaConfiguration` DSL. 3. `testInline` function is the main entry point for unit tests 4. You can pass plugins to be used in a test, notice `pluginOverrides` parameter diff --git a/docs/src/doc/docs/index.md b/docs/src/doc/docs/index.md index 15cf534875..238fb405a5 100644 --- a/docs/src/doc/docs/index.md +++ b/docs/src/doc/docs/index.md @@ -5,8 +5,9 @@ but it's modern and highly pluggable. Just like `Kotlin` itself, `Dokka` supports mixed-language projects (`Kotlin`/`Java`). It understands [KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in `Kotlin` source files as well -as `Javadoc` comments in `Java` files, and can generate documentation in multiple formats including its -own `HTML` format, Java's `Javadoc` lookalike and `Markdown`. +as [Javadoc comments](https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html#format) in `Java` +files, and can generate documentation in multiple formats including its own `HTML` format, Java's `Javadoc` lookalike +and `Markdown`. Some libraries that use `Dokka` for API reference docs: @@ -27,8 +28,8 @@ ___ `Dokka` is also very pluggable and comes with convenient plugin and extension point API. -You can write a plugin to support [mermaid.js](https://mermaid-js.github.io/mermaid/#/) diagrams, -[mathjax](https://www.mathjax.org/) formulas or even write custom processing of your own tags and annotations. +You can write a plugin to support [mermaid.js](community/plugins-list.md#mermaid) diagrams, +[mathjax](community/plugins-list.md#mathjax) formulas or even write custom processing of your own tags and annotations. For more info, see: diff --git a/docs/src/doc/docs/user_guide/applying/gradle.md b/docs/src/doc/docs/user_guide/applying/gradle.md index fd4e53bb17..67aa975747 100644 --- a/docs/src/doc/docs/user_guide/applying/gradle.md +++ b/docs/src/doc/docs/user_guide/applying/gradle.md @@ -1,8 +1,9 @@ # Using the Gradle plugin !!! important -If you are upgrading from 0.10.x to a current release of Dokka, please have a look at our -[migration guide](https://github.com/Kotlin/dokka/blob/master/runners/gradle-plugin/MIGRATION.md) + + If you are upgrading from 0.10.x to a current release of Dokka, please have a look at our + [migration guide](https://github.com/Kotlin/dokka/blob/master/runners/gradle-plugin/MIGRATION.md) ### Supported versions Dokka should work on gradle newer than 5.6 diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index 9d04c2b269..d8aba61883 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -1,7 +1,7 @@ site_name: Dokka documentation # Meta tags (placed in header) -site_description: Dokka is a documentation engine for Kotlin, performing the same function as the Javadoc tool for Java +site_description: Dokka is an API documentation engine for Kotlin, performing the same function as the Javadoc tool for Java site_author: JetBrains site_url: https://github.com/Kotlin/dokka From 3405005ccbc1fbd7e853a5a9906c7619f91308b1 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Tue, 21 Jun 2022 15:14:41 +0200 Subject: [PATCH 7/9] Add more explanations and comments to diagrams --- .../architecture/architecture_overview.md | 47 +++++++++++++------ .../architecture/data_model/documentables.md | 18 ++++++- .../architecture/data_model/page_content.md | 5 +- .../extension_points/core_extensions.md | 6 ++- .../doc/docs/user_guide/applying/gradle.md | 15 +++--- .../src/doc/docs/user_guide/applying/maven.md | 4 +- 6 files changed, 65 insertions(+), 30 deletions(-) diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md index 54ca9d331d..1722d7c786 100644 --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -2,33 +2,36 @@ Normally, you would think that a tool like `Dokka` simply parses some programming language sources and generates `HTML` pages for whatever it sees along the way, with little to no abstractions. That would be the simplest and -shortest way to implement a documentation engine. +shortest way to implement an API documentation engine. However, it was clear that `Dokka` may need to generate documentation from various sources (not only `Kotlin`), that users might request additional output formats (like `Markdown`), that users might need additional features like supporting custom `KDoc` tags or rendering `mermaid.js` diagrams - all these things would require changing a lot of code inside `Dokka` itself if all solutions were hardcoded. -For this reason, `Dokka` was built from the ground up to be extensible and customizable. You can think of the general -flow of generating documentation with `Dokka` as mapping one intermediate representation / abstraction into another. -You, as a `Dokka` developer or a plugin writer, can use extension points and introduce selective changes to the -model on one particular level without touching the rest. +For this reason, `Dokka` was built from the ground up to be easily extensible and customizable by adding several layers +of abstractions to the data model, and by providing pluggable extension points, giving one the ability to introduce +selective changes on a single level. ## Overview of data model -Below you can find the general flow and transformation of Dokka's models. +Generating API documentation begins with `Input` source files (`.kts`, `.java`, etc) and ends with some `Output` files +(`.html`/`.md` pages, etc). However, to allow for extensibility and customization, several input and output independent +abstractions have been added to the data model. + +Below you can find the general pipeline of processing data gathered from sources and the explanation for each stage. ```mermaid flowchart TD - Input --> Documentables --> Documentables --> Pages --> Pages --> Output + Input --> Documentables --> Pages --> Output ``` * `Input` - generalization of sources, by default `Kotlin`/`Java` sources, but could be virtually anything -* `Documentables` - unified data model that represents any parsed sources as a tree. - Examples: class/function/package/property -* `Pages` - universal model that represents pages (e.g a function/property page) and its content +* `Documentables` - unified data model that represents _any_ parsed sources as a tree, independent of the source + language. Examples of a `Documentable`: class, function, package, property, etc +* `Pages` - universal model that represents output pages (e.g a function/property page) and the content it's composed of (lists, text, code blocks) that the users needs to see. Not to be confused with `.html` pages. Goes hand in hand - with so-called Content Model. + with so-called `Content` model. * `Output` - specific output format like `HTML`/`Markdown`/`Javadoc`/etc. This is a mapping of pages/content model to some human-readable and visual representation. For instance: * `PageNode` is mapped as @@ -41,10 +44,21 @@ flowchart TD * `` or `
            ` with some CSS styles in `HTML` format
                     * Text wrapped in triple backticks for `Markdown` format
             
            +    
            +You, as a `Dokka` developer or a plugin writer, can use extension points and introduce selective changes to the
            +model on one particular level without touching everything else. 
             
            -For a deeper dive into Dokka's data model with more examples and details,
            +For instance, if you wanted to make some annotation/function/class invisible in the final documentation, you would only
            +need to modify the `Documentables` model by filtering it out. If you wanted to display all overloaded methods on the same
            +page instead of on separate ones, you would only need to modify the `Page` model by merging multiple pages into one, 
            +and so on.
            +
            +For a deeper dive into Dokka's model with more examples and details,
             see sections about [Documentables](data_model/documentables.md) and [Page/Content](data_model/page_content.md)
             
            +For an overview of existing extension points that let you transform Dokka's models, see 
            +[Core extension points](extension_points/core_extensions.md) and [Base extensions](extension_points/base_extensions.md).
            +
             ## Overview of extension points
             
             An extension point usually represents some pluggable interface that performs an action during one of the stages of
            @@ -54,9 +68,6 @@ extension point.
             You can create extension points, provide your own implementations (extensions) and configure them. All of
             this is possible with Dokka's plugin/extension point API.
             
            -For a deeper dive into extensions and extension points with more examples and details, see
            -[Introduction to Extensions](extension_points/introduction.md).
            -
             Here's a sneak peek of the DSL:
             
             ```kotlin
            @@ -97,6 +108,12 @@ class KotlinSignatureProvider : SignatureProvider {
             }
             ```
             
            +For a deeper dive into extensions and extension points with more examples and details, see
            +[Introduction to Extensions](extension_points/introduction.md).
            +
            +For an overview of existing extension points, see [Core extension points](extension_points/core_extensions.md) and
            +[Base extensions](extension_points/base_extensions.md).
            +
             ## Historical context
             
             This is a second iteration of Dokka that was built from scratch.
            diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md
            index f688e21b8c..5264553d98 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/documentables.md
            @@ -12,7 +12,23 @@ and [Psi](https://plugins.jetbrains.com/docs/intellij/psi.html)
             
             Upon creation, it's a collection of trees, each with `DModule` as root.
             
            -Here's an example of how an arbitrary `Documentable` tree might look like:
            +Take some arbitrary `Kotlin` source code that is located within the same module:
            +
            +```kotlin
            +// Package 1
            +class Clazz(val property: String) {
            +    fun function(parameter: String) {}
            +}
            +
            +fun topLevelFunction() {}
            +
            +// Package 2
            +enum class Enum { }
            +
            +val topLevelProperty: String
            +```
            +
            +This would be represented roughly as the following `Documentable` tree:
             
             ```mermaid
             flowchart TD
            diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            index 65a1b5c102..242c41f113 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            @@ -16,7 +16,8 @@ Subclasses of `PageNode` represent different kinds of rendered pages, such as `M
             
             The Page Model is a tree structure, with `RootPageNode` at the root.
             
            -Here's an example of how an arbitrary `Page` tree might look like:
            +Here's an example of how an arbitrary `Page` tree might look like for a module with 3 packages, one of which contains
            +a top level function, top level property and a class, inside which there's a function and a property:
             
             ```mermaid
             flowchart TD
            @@ -28,6 +29,8 @@ flowchart TD
                 firstPackage ---> firstPackageClasslike[ClasslikePageNode - Class]
                 firstPackageClasslike --> firstPackageClasslikeFirstMember[MemberPageNode - Function]
                 firstPackageClasslike --> firstPackageClasslikeSecondMember[MemberPageNode - Property]
            +    secondPackage --> etcOne[...]
            +    thirdPackage --> etcTwo[...]
             ```
             
             Almost all pages are derivatives of `ContentPage` - it's the type of `Page` that has `Content` on it.
            diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            index 198fe09320..7fc7386556 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            @@ -50,11 +50,14 @@ modules and so on.
             ### SingleModuleGeneration stages
             
             When developing a feature or a plugin, it's more convenient to think that you are generating documentation for single
            -module projects, believeing that Dokka will somehow take care of the rest in multimodule environment.
            +module projects, believing that Dokka will somehow take care of the rest in multimodule environment.
             
             `SingleModuleGeneration` is at heart of generating documentation and utilizes other core extension points, so
             it's worth going over its stages. 
             
            +Below you can see the transformations of [Dokka's models](../architecture_overview.md#overview-of-data-model) and 
            +extension interfaces responsible for each one. Notice how `Documentables` and `Pages` are transformed multiple times.
            +
             ```mermaid
             flowchart TD
                 Input -- SourceToDocumentableTranslator --> 
            @@ -67,7 +70,6 @@ flowchart TD
                 Output
             ```
             
            -
             #### SourceToDocumentableTranslator
             
             `SourceToDocumentableTranslator` translates sources into documentable model. 
            diff --git a/docs/src/doc/docs/user_guide/applying/gradle.md b/docs/src/doc/docs/user_guide/applying/gradle.md
            index 67aa975747..3d0c8fa1a9 100644
            --- a/docs/src/doc/docs/user_guide/applying/gradle.md
            +++ b/docs/src/doc/docs/user_guide/applying/gradle.md
            @@ -1,7 +1,6 @@
             # Using the Gradle plugin
             
             !!! important
            -
                 If you are upgrading from 0.10.x to a current release of Dokka, please have a look at our
                 [migration guide](https://github.com/Kotlin/dokka/blob/master/runners/gradle-plugin/MIGRATION.md)
             
            @@ -62,10 +61,10 @@ dokkaHtml {
             ```
             
             !!! note
            -Dokka extracts the information about sourcesets from the Kotlin Gradle plugin.
            -Therefore, if you are using Dokka in a [precompiled script plugin](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins),
            -you will have to add a depencency to the Kotlin Gradle Plugin as well
            -(`implementation(kotlin("gradle-plugin", ""))` resp. `implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:")`).
            +    Dokka extracts the information about sourcesets from the Kotlin Gradle plugin.
            +    Therefore, if you are using Dokka in a [precompiled script plugin](https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins),
            +    you will have to add a depencency to the Kotlin Gradle Plugin as well
            +    (`implementation(kotlin("gradle-plugin", ""))` resp. `implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:")`).
             
             ## Configuration options
             
            @@ -263,7 +262,7 @@ tasks.withType().configureEach {
             ```
             
             !!! note
            -If you want to share the configuration between source sets, you can use Gradle's `configureEach`
            +    If you want to share the configuration between source sets, you can use Gradle's `configureEach`
             
             ## Applying plugins
             Dokka plugin creates Gradle configuration for each output format in the form of `dokka${format}Plugin` (or `dokka${format}PartialPlugin` for multi-module tasks) :
            @@ -285,7 +284,7 @@ val customDokkaTask by creating(DokkaTask::class) {
             ```
             
             !!! important
            -Please note that `dokkaJavadoc` task will properly document only single `jvm` source set
            +    Please note that `dokkaJavadoc` task will properly document only single `jvm` source set
             
             To generate the documentation, use the appropriate `dokka${format}` Gradle task:
             
            @@ -324,7 +323,7 @@ pluginsMapConfiguration.set(mapOf("" to """
            Date: Tue, 21 Jun 2022 15:33:31 +0200
            Subject: [PATCH 8/9] Correct typos and an incorrect example
            
            ---
             .../architecture/architecture_overview.md            |  2 +-
             .../architecture/data_model/page_content.md          | 12 ++++++++----
             2 files changed, 9 insertions(+), 5 deletions(-)
            
            diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            index 1722d7c786..d789ea8e33 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            @@ -45,7 +45,7 @@ flowchart TD
                     * Text wrapped in triple backticks for `Markdown` format
             
                 
            -You, as a `Dokka` developer or a plugin writer, can use extension points and introduce selective changes to the
            +You, as a `Dokka` developer or a plugin writer, can use extension points to introduce selective changes to the
             model on one particular level without touching everything else. 
             
             For instance, if you wanted to make some annotation/function/class invisible in the final documentation, you would only
            diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            index 242c41f113..54ded235cb 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/page_content.md
            @@ -56,13 +56,17 @@ orderedList {
                             text("Col2")
                         }
                         row {
            -                group(styles = setOf(TextStyle.Bold)) {
            -                    text("Text1")
            -                    text("Text2")
            -                }
            +                text("Text1")
            +                text("Text2")
                         }
                     }
                 }
            +    item {
            +        group(styles = setOf(TextStyle.Bold)) {
            +            text("This is bald")
            +            text("This is also bald")
            +        }
            +    }
             }
             ```
             
            
            From 9a1864bc359dc958c84f353772a2b8942fb13dfa Mon Sep 17 00:00:00 2001
            From: IgnatBeresnev 
            Date: Thu, 7 Jul 2022 22:14:41 +0200
            Subject: [PATCH 9/9] Proofread one last time and correct documentables
             flowchart
            
            ---
             .../architecture/architecture_overview.md     |  8 ++---
             .../architecture/data_model/extra.md          |  2 +-
             .../extension_points/core_extensions.md       | 19 +++++++-----
             .../extension_points/introduction.md          | 18 ++++++-----
             .../sample-plugin-tutorial.md                 | 30 ++++++++++---------
             docs/src/doc/docs/developer_guide/workflow.md |  4 +--
             docs/src/doc/docs/faq.md                      |  2 +-
             7 files changed, 45 insertions(+), 38 deletions(-)
            
            diff --git a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            index d789ea8e33..fb11f32a99 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/architecture_overview.md
            @@ -10,7 +10,7 @@ custom `KDoc` tags or rendering `mermaid.js` diagrams - all these things would r
             `Dokka` itself if all solutions were hardcoded.
             
             For this reason, `Dokka` was built from the ground up to be easily extensible and customizable by adding several layers
            -of abstractions to the data model, and by providing pluggable extension points, giving one the ability to introduce
            +of abstractions to the data model, and by providing pluggable extension points, giving you the ability to introduce
             selective changes on a single level.
             
             ## Overview of data model
            @@ -49,9 +49,9 @@ You, as a `Dokka` developer or a plugin writer, can use extension points to intr
             model on one particular level without touching everything else. 
             
             For instance, if you wanted to make some annotation/function/class invisible in the final documentation, you would only
            -need to modify the `Documentables` model by filtering it out. If you wanted to display all overloaded methods on the same
            -page instead of on separate ones, you would only need to modify the `Page` model by merging multiple pages into one, 
            -and so on.
            +need to modify the `Documentables` model by filtering undesirable members out. If you wanted to display all overloaded
            +methods on the same page instead of on separate ones, you would only need to modify the `Page` model by merging multiple
            +pages into one, and so on.
             
             For a deeper dive into Dokka's model with more examples and details,
             see sections about [Documentables](data_model/documentables.md) and [Page/Content](data_model/page_content.md)
            diff --git a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md
            index ea98dd3274..0abbc70e0d 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/data_model/extra.md
            @@ -35,7 +35,7 @@ data class CustomExtra(
             Merge strategy (`mergeStrategyFor` method) for extras is invoked during
             [merging](../extension_points/core_extensions.md#documentablemerger) if documentables from different 
             [source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) each
            -have their own `Extra`. 
            +have their own `Extra` of the same type. 
             
             ## PropertyContainer
             
            diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            index 7fc7386556..381a95962f 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md
            @@ -60,14 +60,17 @@ extension interfaces responsible for each one. Notice how `Documentables` and `P
             
             ```mermaid
             flowchart TD
            -    Input -- SourceToDocumentableTranslator --> 
            -    docFirst[Documentables] -- PreMergeDocumentableTransformer --> 
            -    docSecond[Documentables] -- DocumentableMerger --> 
            -    docThird[Documentables] -- DocumentableTransformer --> 
            -    docFourth[Documentables] -- DocumentableToPageTranslator --> 
            -    Pages -- PageTransformer --> 
            -    Pages -- Renderer --> 
            -    Output
            +    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
             ```
             
             #### SourceToDocumentableTranslator
            diff --git a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md
            index 28fd0019cf..877d14e967 100644
            --- a/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md
            +++ b/docs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md
            @@ -56,11 +56,11 @@ In the example below we will extend `MyPlugin` that was created above with our o
             ```kotlin
             class MyExtendedPlugin : DokkaPlugin() {
                 val mySampleExtensionImplementation by extending {
            -        plugin().sampleExtensionPoint with MyOwnSampleExtensionImplementation()
            +        plugin().sampleExtensionPoint with SampleExtensionImpl()
                 }
             }
             
            -class MyOwnSampleExtensionImplementation : SampleExtensionPointInterface {
            +class SampleExtensionImpl : SampleExtensionPointInterface {
                 override fun doSomething(input: Input): List = listOf()
             }
             
            @@ -73,7 +73,7 @@ If you need to have access to `DokkaContext` in order to create an extension, yo
             ```kotlin
             val defaultSampleExtension by extending {
                 sampleExtensionPoint providing { context ->
            -        // can use context to query other extensions    
            +        // can use context to query other extensions or get configuration 
                     DefaultSampleExtension() 
                 }
             }
            @@ -84,7 +84,7 @@ You can read more on what you can do with `context` in [Obtaining extension inst
             ### Override
             
             By extending an extension point, you are registering an _additional_ extension. This behaviour is expected for some
            -extension points, for instance documentable transformers, since all transformers do their own transformations and all
            +extension points, for instance `Documentable` transformers, since all transformers do their own transformations and all
             of them will be invoked before proceeding.
             
             However, a plugin can expect only a single registered extension for an extension point. In this case, you can `override`
            @@ -96,12 +96,14 @@ class MyExtendedPlugin : DokkaPlugin() {
             
                 val mySampleExtensionImplementation by extending {
                     (myPlugin.sampleExtensionPoint
            -                with MyOwnSampleExtensionImplementation()
            +                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 `order`:
            @@ -111,7 +113,7 @@ class MyExtendedPlugin : DokkaPlugin() {
                 private val myPlugin by lazy { plugin() }
             
                 val mySampleExtensionImplementation by extending {
            -        myPlugin.sampleExtensionPoint with MyOwnSampleExtensionImplementation() order {
            +        myPlugin.sampleExtensionPoint with SampleExtensionImpl() order {
                         before(myPlugin.firstExtension)
                         after(myPlugin.thirdExtension)
                     }
            @@ -121,14 +123,14 @@ class MyExtendedPlugin : DokkaPlugin() {
             
             ### Conditional apply
             
            -If you want your extension to be registered only if some condition is true, you can use `applyIf`:
            +If you want your extension to be registered only if some condition is `true`, you can use `applyIf`:
             
             ```kotlin
             class MyExtendedPlugin : DokkaPlugin() {
                 private val myPlugin by lazy { plugin() }
                 
                 val mySampleExtensionImplementation by extending {
            -        myPlugin.sampleExtensionPoint with MyOwnSampleExtensionImplementation() applyIf {
            +        myPlugin.sampleExtensionPoint with SampleExtensionImpl() applyIf {
                         Random.Default.nextBoolean()
                     }
                 }
            diff --git a/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md
            index 0de85e7577..fdea0207e4 100644
            --- a/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md
            +++ b/docs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md
            @@ -110,10 +110,10 @@ First, let's begin by publishing our plugin to `mavenLocal()`.
             ./gradlew publishToMavenLocal
             ```
             
            -This will publish your plugin under the groupId, artifactId and version that you've specified in your
            +This will publish your plugin under the `groupId`, `artifactId` and `version` that you've specified in your
             `build.gradle.kts`. In our case it's `org.example:hide-internal-api:1.0-SNAPSHOT`.
             
            -Open a debug project of your choosing and add our plugin to dependencies:
            +Open a debug project of your choosing that has Dokka configured, and add our plugin to dependencies:
             
             ```kotlin
             dependencies {
            @@ -140,11 +140,12 @@ place of `d: Documentable` (observe the changing `name` property).
             Looking at what's inside the object, you might notice it has 3 values in `extra`, one of which is `Annotations`.
             Sounds like something we need!
             
            -Having poked around, you come up with the following monstrosity of a code for determining if a given documentable has
            +Having poked around, we come up with the following monstrosity of a code for determining if a given documentable has
             `@Internal` annotation (it can of course be refactored.. later):
             
             ```kotlin
             override fun shouldBeSuppressed(d: Documentable): Boolean {
            +   
                 val annotations: List =
                     (d as? WithExtraProperties<*>)
                         ?.extra
            @@ -156,8 +157,8 @@ override fun shouldBeSuppressed(d: Documentable): Boolean {
             }
             
             private fun isInternalAnnotation(annotation: Annotations.Annotation): Boolean {
            -    return annotation.dri.packageName == "org.jetbrains.dokka.expose.test"
            -            && annotation.dri.classNames == "Internal"
            +   return annotation.dri.packageName == "org.jetbrains.dokka.internal.test"
            +           && annotation.dri.classNames == "Internal"
             }
             ```
             
            @@ -204,8 +205,9 @@ class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionD
             }
             ```
             
            -Bump plugin version in `gradle.build.kts`, publish it to maven local, open the debug project and run `dokkaHtml`. 
            -It should work, you should **not** be able to see `shouldBeExcludedFromDocumentation` function in generated documentation.
            +Bump plugin version in `gradle.build.kts`, publish it to maven local, open the debug project and run `dokkaHtml` 
            +(without debug this time). It should work, you should **not** be able to see `shouldBeExcludedFromDocumentation`
            +function in generated documentation.
             
             Manual testing is cool and all, but wouldn't it be better if we could somehow write unit tests for it? Indeed!
             
            @@ -230,6 +232,7 @@ import org.junit.Test
             import kotlin.test.assertEquals
             
             class HideInternalApiPluginTest : BaseAbstractTest() {
            +   
                 @Test
                 fun `should hide annotated functions`() {
                     val configuration = dokkaConfiguration {
            @@ -270,8 +273,7 @@ class HideInternalApiPluginTest : BaseAbstractTest() {
             ```
             
             Note that the package of the tested code (inside `testInline` function) is the same as the package that we have
            -hardcoded in our plugin. Make sure to change that if you are following along but for your own annotation and package,
            -otherwise it will fail.
            +hardcoded in our plugin. Make sure to change that to your own if you are following along, otherwise it will fail.
             
             Things to note and remember:
             
            @@ -281,10 +283,10 @@ Things to note and remember:
                `dokkaConfiguration` DSL.
             3. `testInline` function is the main entry point for unit tests
             4. You can pass plugins to be used in a test, notice `pluginOverrides` parameter
            -5. You can test Dokka on different stages of generating documentation, the main ones being documentables model
            -   generation, pages generation and output generation. Since we implemented our plugin to work during
            +5. You can write asserts for different stages of generating documentation, the main ones being `Documentables` model
            +   generation, `Pages` generation and `Output` generation. Since we implemented our plugin to work during
                `PreMergeDocumentableTransformer` stage, we can test it on the same level (that is
                `preMergeDocumentablesTransformationStage`).
            -6. You will need to write asserts using the model of whatever stage you choose. For documentables transformation stage 
            -   it's documentables, for page generation stage you would have pages, and for output you can have `.html` files
            -   that you will need to parse with JSoup (there are also utilities for that).
            +6. You will need to write asserts using the model of whatever stage you choose. For `Documentable` transformation stage 
            +   it's `Documentable`, for `Page` generation stage you would have `Page` model, and for `Output` you can have `.html`
            +   files that you will need to parse with `JSoup` (there are also utilities for that).
            diff --git a/docs/src/doc/docs/developer_guide/workflow.md b/docs/src/doc/docs/developer_guide/workflow.md
            index d3de262f92..d5ebdd80d7 100644
            --- a/docs/src/doc/docs/developer_guide/workflow.md
            +++ b/docs/src/doc/docs/developer_guide/workflow.md
            @@ -3,7 +3,7 @@
             Whether you're contributing a feature/fix to Dokka itself or developing a separate plugin, there's 3 things
             you'll be doing:
             
            -1. Building Dokka
            +1. Building Dokka / Plugins
             2. Using/Testing locally built Dokka in a (debug) project
             3. Debugging Dokka / Plugin code
             
            @@ -96,5 +96,5 @@ wish to debug a plugin which resides in a separate project.
                 If you previously ran Dokka with daemons and you are already encountering problems with it, try killing
                 gradle daemons. For instance, via `pkill -f gradle.*daemon`
             
            -In case you're experiencing need to debug some other part of the build - consult the official Gradle
            +In case you need to debug some other part of the build - consult the official Gradle
             tutorials on [Troubleshooting Builds](https://docs.gradle.org/current/userguide/troubleshooting.html).
            diff --git a/docs/src/doc/docs/faq.md b/docs/src/doc/docs/faq.md
            index ba4a7acf15..ef728ca055 100644
            --- a/docs/src/doc/docs/faq.md
            +++ b/docs/src/doc/docs/faq.md
            @@ -1,2 +1,2 @@
             # FAQ
            -If you encounter any problems, please see the [FAQ](https://github.com/Kotlin/dokka/wiki/faq).
            +If you encounter any problems, please see [FAQ](https://github.com/Kotlin/dokka/wiki/faq).