diff options
27 files changed, 646 insertions, 618 deletions
diff --git a/.github/workflows/gh-pages-deploy-mkdocs.yml b/.github/workflows/gh-pages-deploy-dev-docs.yml index b212f55b..9b4fdb52 100644 --- a/.github/workflows/gh-pages-deploy-mkdocs.yml +++ b/.github/workflows/gh-pages-deploy-dev-docs.yml @@ -1,11 +1,11 @@ -name: Deploy MkDocs to GitHub Pages +name: Deploy developer docs to GitHub Pages on: push: branches: - master paths: - - 'mkdocs/**' + - 'docs-developer/**' release: types: [ published ] @@ -25,7 +25,7 @@ jobs: - uses: gradle/gradle-build-action@v2 with: gradle-home-cache-cleanup: true - - name: Get current dokka version + - name: Get current Dokka version run: echo "DOKKA_VERSION=`./gradlew :properties | grep '^version:.*' | cut -d ' ' -f 2`" >> $GITHUB_ENV working-directory: ./dokka - name: Build docs @@ -35,6 +35,6 @@ jobs: uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dokka/mkdocs/build/mkdocs + publish_dir: ./dokka/docs-developer/build/mkdocs keep_files: true full_commit_message: Publish ${{ env.DOKKA_VERSION }} documentation diff --git a/docs-developer/README.md b/docs-developer/README.md new file mode 100644 index 00000000..d415dbf7 --- /dev/null +++ b/docs-developer/README.md @@ -0,0 +1,43 @@ +# Developer documentation + +This module contains developer documentation which is published to GitHub pages: +[kotlin.github.io/dokka](https://kotlin.github.io/dokka/). + +It is built using the [gradle-mkdocs-plugin](https://github.com/xvik/gradle-mkdocs-plugin). + +## Building + +You can build the documentation locally: + +```Bash +./gradlew :docs-developer:mkdocsBuild +``` + +The output directory is `build/mkdocs`. + +### Docker + +Alternatively, you can use Docker: + +```bash +docker run --rm -it -p 8000:8000 -v ./docs-developer/src/doc:/docs squidfunk/mkdocs-material +``` + +This will build the docs and start a web server under [localhost:8000/Kotlin/dokka](http://localhost:8000/Kotlin/dokka/). + +### Livereload server + +Alternatively, you can run a livereload server that automatically rebuilds documentation on every change: + +```Bash +./gradlew :docs-developer:mkdocsServe +``` + +By default, it is run under [localhost:3001](http://localhost:3001/), but you can change it in +[mkdocs.yml](src/doc/mkdocs.yml) by setting the `dev_addr` option. + +## Publishing + +The documentation is published automatically for all changes in master and for every GitHub release. + +See [gh-pages.yml](../.github/workflows/gh-pages-deploy-dev-docs.yml) workflow configuration for more details. diff --git a/mkdocs/build.gradle.kts b/docs-developer/build.gradle.kts index b3c52a0a..e920c6f8 100644 --- a/mkdocs/build.gradle.kts +++ b/docs-developer/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.dokkaVersionType import org.jetbrains.DokkaVersionType plugins { - id("ru.vyarus.mkdocs") version "2.3.0" + id("ru.vyarus.mkdocs") version "2.4.0" } if (dokkaVersionType != DokkaVersionType.RELEASE) { diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/architecture_overview.md b/docs-developer/src/doc/docs/developer_guide/architecture/architecture_overview.md new file mode 100644 index 00000000..d72dda91 --- /dev/null +++ b/docs-developer/src/doc/docs/developer_guide/architecture/architecture_overview.md @@ -0,0 +1,123 @@ +# 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 +the most straightforward 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](https://mermaid.js.org/) 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 easily extensible and customizable by adding several layers +of abstractions to the data model, and by providing pluggable extension points, giving you the ability to introduce +selective changes on a given level. + +## Overview of data model + +Generating API documentation begins with input source files (`.kt`, `.java`, etc) and ends with some output files +(`.html`/`.md`, 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 --> Pages --> Output +``` + +* `Input` - generalization of sources, by default Kotlin / Java sources, but could be virtually anything +* [`Documentables`](data_model/documentable_model.md) - 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`](data_model/page_content.md) - 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 the so-called [Content model](data_model/page_content.md#content-model). +* `Output` - specific output formats like HTML / Markdown / Javadoc and so on. This is a mapping of the pages/content + model to a human-readable and visual representation. For instance: + * `PageNode` is mapped as + * `.html` file for the HTML format + * `.md` file for the Markdown format + * `ContentList` is mapped as + * `<li>` / `<ul>` for the HTML format + * `1.` / `*` for the Markdown format + * `ContentCodeBlock` is mapped as + * `<code>` or `<pre>` with some CSS styles in the HTML format + * Text wrapped in triple backticks for the Markdown format + + +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 altering everything else. + +For instance, if you wanted to make an annotation / function / class invisible in the final documentation, you would only +need to modify the `Documentables` level by filtering undesirable declarations 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 `Pages` layer 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/documentable_model.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_extension_points.md) and [Base extensions](extension_points/base_plugin.md). + +## Overview of extension points + +An _extension point_ usually represents a pluggable interface that performs an action during one of the stages of +generating documentation. An _extension_ is, therefore, an implementation of the interface which is extending the +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. + +Here's a sneak peek of the DSL: + +```kotlin +// declare your own plugin +class MyPlugin : DokkaPlugin() { + // create an extension point for developers to use + val signatureProvider by extensionPoint<SignatureProvider>() + + // provide a default implementation + val defaultSignatureProvider by extending { + signatureProvider with KotlinSignatureProvider() + } + + // register our own extension in Dokka's Base plugin by overriding its default implementation + val dokkaBasePlugin by lazy { plugin<DokkaBase>() } + val multimoduleLocationProvider by extending { + (dokkaBasePlugin.locationProviderFactory + providing MultimoduleLocationProvider::Factory + override dokkaBasePlugin.locationProvider) + } +} + +class MyExtension(val context: DokkaContext) { + + // use an existing extension + val signatureProvider: SignatureProvider = context.plugin<MyPlugin>().querySingle { signatureProvider } + + fun doSomething() { + signatureProvider.signature(..) + } +} + +interface SignatureProvider { + fun signature(documentable: Documentable): List<ContentNode> +} + +class KotlinSignatureProvider : SignatureProvider { + override fun signature(documentable: Documentable): List<ContentNode> = listOf() +} +``` + +For a deeper dive into extensions and extension points, see [Introduction to Extensions](extension_points/extension_points.md). + +For an overview of existing extension points, see [Core extension points](extension_points/core_extension_points.md) and +[Base extensions](extension_points/base_plugin.md). + +## Historical context + +This is a second iteration of Dokka that was built from scratch. + +If you want to learn more about why Dokka was redesigned 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/mkdocs/src/doc/docs/developer_guide/architecture/data_model/documentables.md b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/documentable_model.md index 5264553d..b30780fc 100644 --- a/mkdocs/src/doc/docs/developer_guide/architecture/data_model/documentables.md +++ b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/documentable_model.md @@ -1,18 +1,22 @@ -# Documentables Model +# Documentable Model -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. +The Documentable model represents the data that is parsed from some programming language sources. Think of this data 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) -(for `Java`) models. Code-wise, you can have a look at following classes: +By default, the documentables are created from: -* `DefaultDescriptorToDocumentableTranslator` - responsible for `Kotlin` -> `Documentable` mapping -* `DefaultPsiToDocumentableTranslator` - responsible for `Java` -> `Documentable` mapping +* Descriptors (Kotlin's K1 compiler) +* Symbols (Kotlin's K2 compiler) +* [PSI](https://plugins.jetbrains.com/docs/intellij/psi.html) (Java's model). -Upon creation, it's a collection of trees, each with `DModule` as root. +Code-wise, you can have a look at following classes: -Take some arbitrary `Kotlin` source code that is located within the same module: +* `DefaultDescriptorToDocumentableTranslator` - responsible for Kotlin -> `Documentable` mapping +* `DefaultPsiToDocumentableTranslator` - responsible for Java -> `Documentable` mapping + +Upon creation, the documentable model represents a collection of trees, each with `DModule` as root. + +Take some arbitrary Kotlin source code that is located within the same module: ```kotlin // Package 1 @@ -28,7 +32,7 @@ enum class Enum { } val topLevelProperty: String ``` -This would be represented roughly as the following `Documentable` tree: +This would be represented roughly as the following Documentable tree: ```mermaid flowchart TD @@ -43,20 +47,23 @@ flowchart TD secondPackage --> secondPackageProperty[DProperty] ``` -At later stages of transformation, all trees are folded into one (by `DocumentableMerger`). +At later stages of transformation, all trees are folded into one by +[DocumentableMerger](../extension_points/core_extension_points.md#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 main building block of the documentable model is the `Documentable` class. It is the base class for all more specific +types. All implementations represent elements of source code with mostly self-explanatory names: `DFunction`, +`DPackage`, `DProperty`, and so on. + +`DClasslike` is the base class for all class-like documentables, such as `DClass`, `DEnum`, `DAnnotation` and others. + +The contents of each documentable normally represent what you would see in the source code. -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). +For example, if you open +`DClass`, you should find that it contains references to functions, properties, companion objects, constructors and so +on. `DEnum` should have references to its entries, and `DPackage` can have references to both classlikes and top-level +functions and properties (Kotlin-specific). Here's an example of a documentable: @@ -85,7 +92,7 @@ data class DClass( ___ -There are three non-documentable classes that important for this model: +There are three non-documentable classes that are important for this model: * `DRI` * `SourceSetDependent` @@ -94,9 +101,9 @@ There are three non-documentable classes that important for this model: ### DRI `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`. +All references and relations between the 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 +For example, `DFunction` with a parameter of type `Foo` only has `Foo`'s `DRI`, but not the actual reference to `Foo`'s `Documentable` object. #### Example @@ -146,11 +153,11 @@ 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 +`expect` or `actual` modifiers) 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 +This comes in handy if the `expect` / `actual` declarations differ. For example, 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`: @@ -171,18 +178,18 @@ ___ ## Documentation model -Documentation model is used alongside Documentables to store data obtained by parsing -code comments (such as `KDoc`/`Javadoc`). +The Documentation model is used alongside documentables to store data obtained by parsing +code comments (such as KDocs / Javadocs). ### DocTag `DocTag` describes a specific documentation syntax element. -It's universal across source languages. For instance, DocTag `B` is the same for `**bold**` in `Kotlin` and -`<b>bold</b>` in `Java`. +It's universal across language sources. For example, the DocTag `B` is the same for `**bold**` in Kotlin and +`<b>bold</b>` 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`. +However, some DocTag elements are specific to one language. There are many such examples for Java, because it allows +HTML tags inside the Javadoc comments, some of which are simply not possible to reproduce with Markdown that KDocs use. `DocTag` elements can be deeply nested with other `DocTag` children elements. @@ -218,10 +225,9 @@ data class CodeBlock( ### TagWrapper -`TagWrapper` describes the whole comment description or a specific comment tag. -For example: `@see` / `@author` / `@return`. +`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. +Since each such section may contain formatted text inside it, each `TagWrapper` has `DocTag` children. ```kotlin /** diff --git a/mkdocs/src/doc/docs/developer_guide/architecture/data_model/extra.md b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/extra.md index 0abbc70e..d7412e36 100644 --- a/mkdocs/src/doc/docs/developer_guide/architecture/data_model/extra.md +++ b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/extra.md @@ -2,24 +2,13 @@ ## Introduction -`ExtraProperty` classes are used both by [Documentable](documentables.md) and [Content](page_content.md#content-model) -models. - -Source code for `ExtraProperty`: - -```kotlin -interface ExtraProperty<in C : Any> { - interface Key<in C : Any, T : Any> { - fun mergeStrategyFor(left: T, right: T): MergeStrategy<C> = MergeStrategy.Fail { - throw NotImplementedError("Property merging for $this is not implemented") - } - } +`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. - val key: Key<C, *> -} -``` +`ExtraProperty` classes are available both in the [Documentable](documentable_model.md) and the [Content](page_content.md#content-model) +models. -To declare a new extra, you need to implement `ExtraProperty` interface. It is advised to use following pattern +To create a new extra, you need to implement the `ExtraProperty` interface. It is advised to use the following pattern when declaring new extras: ```kotlin @@ -32,14 +21,14 @@ 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` of the same type. +Merge strategy (the `mergeStrategyFor` method) for extras is invoked during the +[merging](../extension_points/core_extension_points.md#documentablemerger) of the documentables from different +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets), when the documentables being +merged have their own `Extra` of the same type. ## PropertyContainer -All extras for `ContentNode` and `Documentable` classes are stored in `PropertyContainer<C : Any>` class instances. +All extras for `ContentNode` and `Documentable` classes are stored in the `PropertyContainer<C : Any>` class instances. ```kotlin data class DFunction( @@ -51,26 +40,29 @@ data class DFunction( `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 +The generic class parameter `C` limits the types of properties that can be stored in the container - it must +match the generic `C` class parameter from the `ExtraProperty` interface. This allows creating extra properties which can only be stored in a specific `Documentable`. ## Usage example -In following example we will create a `DFunction`-only property, store it and then retrieve its value: +In following example we will create a `DFunction`-only extra property, store it and then retrieve its value: ```kotlin +// Extra that is applicable only to DFunction data class CustomExtra(val customExtraValue: String) : ExtraProperty<DFunction> { override val key: ExtraProperty.Key<Documentable, *> = CustomExtra companion object: ExtraProperty.Key<Documentable, CustomExtra> } +// Storing it inside the documentable fun DFunction.withCustomExtraProperty(data: String): DFunction { return this.copy( extra = extra + CustomExtra(data) ) } +// Retrieveing it from the documentable fun DFunction.getCustomExtraPropertyValue(): String? { return this.extra[CustomExtra]?.customExtraValue } diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/page_content.md new file mode 100644 index 00000000..eb85200f --- /dev/null +++ b/docs-developer/src/doc/docs/developer_guide/architecture/data_model/page_content.md @@ -0,0 +1,144 @@ +# Page / Content Model + +Even though the `Page` and `Content` models reside on the same level (under `Page`), it is easier to view them as two +different models altogether, even though `Content` is only used in conjunction with and inside the `Page` model only. + +## Page + +The 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. + +The Page model is independent of the final output format. In other words, it's universal. Which file extension the pages +should be created as (`.html`, `.md`, etc), and how, is up to the +[Renderer](../extension_points/core_extension_points.md#renderer) extension. + +Subclasses of the `PageNode` class represent the different kinds of pages, such as `ModulePage`, `PackagePage`, +`ClasslikePage`, `MemberPage` and so on. + +The Page model can be represented as a tree, with `RootPageNode` at the root. + +Here's an example of how an arbitrary project's `Page` tree might look like, if the project consists of a module with +3 packages, one of which contains a top level function, a top level property and a class, inside which there's a function +and a property: + +```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] + secondPackage --> etcOne[...] + thirdPackage --> etcTwo[...] +``` + +Almost all pages are derivatives of `ContentPage` - it's the type of a page that has user-visible content on it. + +## Content Model + +The Content model describes what the pages consist of. It is essentially a set of building blocks that you can put +together to represent some content. It is also output-format independent and universal. + +For an example, have a look at the subclasses of `ContentNode`: `ContentText`, `ContentList`, `ContentTable`, +`ContentCodeBlock`, `ContentHeader` and so on -- all self-explanatory. You can group chunks of content together with +`ContentGroup` - for example, to wrap all children with a style. + +```kotlin +// real example of composing content using the `DocumentableContentBuilder` DSL +orderedList { + item { + text("This list contains a nested table:") + table { + header { + text("Col1") + text("Col2") + } + row { + text("Text1") + text("Text2") + } + } + } + item { + group(styles = setOf(TextStyle.Bold)) { + text("This is bald") + text("This is also bald") + } + } +} +``` + +It is the responsibility of the `Renderer` (i.e a specific output format) to render it in a way the user can process it, +be it visually (html pages) or otherwise (json). + +For instance, `HtmlRenderer` might render `ContentCodeBlock` as `<code>text</code>`, but `CommonmarkRenderer` might +render it using backticks. + +### DCI + +Each node is identified by a unique `DCI`, which stands for _Dokka Content Identifier_. + +`DCI` aggregates `DRI`s of all documentables that are used by the given `ContentNode`. + +```kotlin +data class DCI(val dri: Set<DRI>, 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 be rendered as part of a composite +page, like a single one tab or a block within a class's page. + +For example, on the same page that describes a class you can have multiple sections (== `ContentKind`s). +One to describe functions, one to describe properties, another one to describe the constructors, and so on. + +### Styles + +Each `ContentNode` has a `styles` property in case you want to indicate to the `Renderer` that this content needs to be +rendered in a certain way. + +```kotlin +group(styles = setOf(TextStyle.Paragraph)) { + text("Text1", styles = setOf(TextStyle.Bold)) + text("Text2", styles = setOf(TextStyle.Italic)) +} +``` + +It is responsibility of the `Renderer` (i.e a specific output format) to render it in a way the user can process it. +For instance, `HtmlRenderer` might render `TextStyle.Bold` as `<b>text</b>`, but `CommonmarkRenderer` might render it +as `**text**`. + +There's a number of existing styles that you can use, most of them are supported by the `HtmlRenderer` extension 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 the `Documentable` model are propagated into the `Content` model, and are available +in the `Renderer` extensions. + +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-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md new file mode 100644 index 00000000..88579be7 --- /dev/null +++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/base_plugin.md @@ -0,0 +1,33 @@ +# Base plugin + +`DokkaBase` represents Dokka's _Base_ plugin, which provides a number of sensible default implementations for +`CoreExtensions`, as well as declares its own, more high-level abstractions and extension points to be used from other +plugins and output formats. + +If you want to develop a simple plugin that only changes a few details, it is very convenient to rely on +default implementations and use extension points defined in `DokkaBase`, as it reduces the scope of changes you need to make. + +`DokkaBase` is used extensively in Dokka's own output formats. + +You can learn how to add, use, override and configure extensions and extension points in +[Introduction to Extensions](extension_points.md) - all of that information is applicable to the `DokkaBase` plugin as well. + +## Extension points + +Some notable extension points defined in Dokka's Base plugin. + +### PreMergeDocumentableTransformer + +`PreMergeDocumentableTransformer` is very similar to the +[DocumentableTransformer](core_extension_points.md#documentabletransformer) core extension point, but it is used during +an earlier stage by the [Single module generation](generation_implementations.md#singlemodulegeneration). + +This extension point allows you to apply any transformations to the [Documentables model](../data_model/documentable_model.md) +before the project's [source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) are merged. + +It is useful if you want to filter/map existing documentables. For example, if you want to exclude members annotated with +`@Internal`, you most likely need an implementation of `PreMergeDocumentableTransformer`. + +For simple condition-based filtering of documentables, consider extending +`SuppressedByConditionDocumentableFilterTransformer` - it implements `PreMergeDocumentableTransformer` and only +requires one function to be overridden, whereas the rest is taken care of. diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md new file mode 100644 index 00000000..fc0088c9 --- /dev/null +++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/core_extension_points.md @@ -0,0 +1,103 @@ +# Core extension points + +Core extension points represent the main stages of generating documentation. + +These extension points are plugin and output format independent, meaning it's the very core functionality and as +low-level as can get in Dokka. + +For higher-level extension functions that can be used in different output formats, have a look at the +[Base plugin](base_plugin.md). + +You can find all core extensions in the `CoreExtensions` class: + +```kotlin +object CoreExtensions { + val preGenerationCheck by coreExtensionPoint<PreGenerationChecker>() + val generation by coreExtensionPoint<Generation>() + val sourceToDocumentableTranslator by coreExtensionPoint<SourceToDocumentableTranslator>() + val documentableMerger by coreExtensionPoint<DocumentableMerger>() + val documentableTransformer by coreExtensionPoint<DocumentableTransformer>() + val documentableToPageTranslator by coreExtensionPoint<DocumentableToPageTranslator>() + val pageTransformer by coreExtensionPoint<PageTransformer>() + val renderer by coreExtensionPoint<Renderer>() + val postActions by coreExtensionPoint<PostAction>() +} +``` + +On this page, we'll go over each extension point individually. + +## PreGenerationChecker + +`PreGenerationChecker` can be used to run some checks and constraints. + +For example, Dokka's Javadoc plugin does not support generating documentation for multi-platform projects, so it uses +`PreGenerationChecker` to check for multi-platform +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets), and fails if it finds any. + +## Generation + +`Generation` is responsible for generating documentation as a whole, utilizing higher-level extensions and extension +points where applicable. + +See [Generation implementations](generation_implementations.md) to learn about the default implementations. + +## SourceToDocumentableTranslator + +`SourceToDocumentableTranslator` translates any given sources into the Documentable model. + +Kotlin and Java sources are supported by default by the [Base plugin](base_plugin.md), but you can analyze any language +as long as you can map it to the [Documentable](../data_model/documentable_model.md) model. + +For reference, see + +* `DefaultDescriptorToDocumentableTranslator` for Kotlin sources translation +* `DefaultPsiToDocumentableTranslator` for Java sources translation + +## DocumentableMerger + +`DocumentableMerger` merges all `DModule` instances into one. Only one extension of this type is expected to be +registered. + +## DocumentableTransformer + +`DocumentableTransformer` performs the same function as `PreMergeDocumentableTransformer`, but after merging source +sets. + +Notable example is `InheritorsExtractorTransformer`, it extracts inheritance information from +[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) and creates an inheritance +map. + +## DocumentableToPageTranslator + +`DocumentableToPageTranslator` is responsible for creating pages and their content. See +[Page / Content model](../data_model/page_content.md) page for more information and examples. + +Output formats can either use the same page structure or define their own. + +Only a single extension of this type is expected to be registered. + +## PageTransformer + +`PageTransformer` is useful if you need to add, remove or modify generated pages or their content. + +Using this extension point, plugins like `org.jetbrains.dokka:mathjax-pligin` can add `.js` scripts to the HTML pages. + +If you want all overloaded functions to be rendered on the same page instead of separate ones, +you can use `PageTransformer` to combine the pages into a single one. + +## Renderer + +`Renderer` - defines the rules on how to render pages and their content: which files to create and how to display +the content properly. + +Custom output format plugins should use the `Renderer` extension point. Notable examples are `HtmlRenderer` +and `CommonmarkRenderer`. + +## PostAction + +`PostAction` can be used for when you want to run some actions after the documentation has been generated - for example, +if you want to move some files around or log some informational messages. + +Dokka's [Versioning plugin](https://github.com/Kotlin/dokka/tree/master/plugins/versioning) utilizes `PostAction` +to move generated documentation to the versioned directories. + diff --git a/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md index 877d14e9..97b02a7d 100644 --- a/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/introduction.md +++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/extension_points.md @@ -1,12 +1,12 @@ -# Introduction to extension points +# 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. +In this section you can learn how to create new extension points, how to configure existing ones, and how to query for +registered extensions when generating documentation. ## Declaring extension points -If you are writing a plugin, you can create your own extension point that other developers (or you) can use later on -in some other part of code. +If you are writing a plugin, you can create your own extension points that other developers (or you) can use in other +plugins / parts of code. ```kotlin class MyPlugin : DokkaPlugin() { @@ -21,54 +21,52 @@ 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. +Usually, you would want to provide some default implementations for your extension points. You can do that +within the same plugin class by extending an extension point you've just created. See +[Extending from extension points](#extending-from-extension-points) for examples. ## Extending from extension points -You can use extension points to provide your own implementation(s) in order to customize plugin's behaviour. +You can use extension points to provide your own implementations in order to customize a plugin's behaviour. -You can do that within the same class as the extension point itself: +If you want to provide an implementation for an extension point declared in an external plugin (including `DokkaBase`), +you can use plugin querying API to do that. -```kotlin -open class MyPlugin : DokkaPlugin() { - val sampleExtensionPoint by extensionPoint<SampleExtensionPointInterface>() +The example below shows how to extend `MyPlugin` (that was created above) with an implementation of +`SampleExtensionPointInterface`. - val defaultSampleExtension by extending { - sampleExtensionPoint with DefaultSampleExtension() +```kotlin +class MyExtendedPlugin : DokkaPlugin() { + + val mySampleExtensionImplementation by extending { + plugin<MyPlugin>().sampleExtensionPoint with SampleExtensionImpl() } } -... - -class DefaultSampleExtension : SampleExtensionPointInterface { +class SampleExtensionImpl : SampleExtensionPointInterface { override fun doSomething(input: Input): List<Output> = 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`. +Alternatively, if it is your own plugin, you can do that within the same class as the extension point itself: ```kotlin -class MyExtendedPlugin : DokkaPlugin() { - val mySampleExtensionImplementation by extending { - plugin<MyPlugin>().sampleExtensionPoint with SampleExtensionImpl() +open class MyPlugin : DokkaPlugin() { + val sampleExtensionPoint by extensionPoint<SampleExtensionPointInterface>() + + val defaultSampleExtension by extending { + sampleExtensionPoint with DefaultSampleExtension() } } -class SampleExtensionImpl : SampleExtensionPointInterface { +class DefaultSampleExtension : SampleExtensionPointInterface { override fun doSomething(input: Input): List<Output> = listOf() } - ``` ### Providing -If you need to have access to `DokkaContext` in order to create an extension, you can use `providing` instead. +If you need to have access to `DokkaContext` when creating an extension, you can use the `providing` keyword instead. ```kotlin val defaultSampleExtension by extending { @@ -83,12 +81,12 @@ 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 -of them will be invoked before proceeding. +By extending an extension point, you are registering an _additional_ extension. This behaviour is expected by some +extension points, for example the `Documentable` transformers, because all registered transformer extensions do their own +transformations independently and one after the other. -However, a plugin can expect only a single registered extension for an extension point. In this case, you can `override` -existing registered extensions: +However, a plugin can expect only a single extension to be registered for an extension point. In this case, you can use +the `override` keyword to override the existing registered extension: ```kotlin class MyExtendedPlugin : DokkaPlugin() { @@ -102,11 +100,12 @@ class MyExtendedPlugin : DokkaPlugin() { } ``` -This is also useful if you wish to override some extension from `DokkaBase` to disable or alter it. +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`: +Sometimes, the order in which extensions are invoked matters. This is something you can control as well using the +`order` construct: ```kotlin class MyExtendedPlugin : DokkaPlugin() { @@ -123,7 +122,7 @@ 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 the `applyIf` construct: ```kotlin class MyExtendedPlugin : DokkaPlugin() { @@ -139,17 +138,17 @@ class MyExtendedPlugin : DokkaPlugin() { ## 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. +After an extension point has been [created](#declaring-extension-points) and some extensions have been +[registered](#extending-from-extension-points), you can use `query` and `querySingle` functions to find all or just a +single implementation. ```kotlin class MyExtension(context: DokkaContext) { - // returns all registered extensions for this extension point + // returns all registered extensions for the extension point val allSampleExtensions = context.plugin<MyPlugin>().query { sampleExtensionPoint } - // will throw an exception if more than one extension is found - // use if you expect only a single extension to be registered for this extension point + // will throw an exception if more than one extension is found. + // use if you expect only a single extension to be registered for the extension point val singleSampleExtensions = context.plugin<MyPlugin>().querySingle { sampleExtensionPoint } fun invoke() { @@ -160,4 +159,4 @@ class MyExtension(context: DokkaContext) { } ``` -In order to have access to context you can use [providing](#providing) when registering this as an extension. +In order to have access to `DokkaContext`, you can use the [providing](#providing) keyword when registering an extension. diff --git a/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md new file mode 100644 index 00000000..3d857fec --- /dev/null +++ b/docs-developer/src/doc/docs/developer_guide/architecture/extension_points/generation_implementations.md @@ -0,0 +1,40 @@ +# Generation implementations + +There are two main implementations of the [Generation](core_extension_points.md#generation) core extension point: + +* `SingleModuleGeneration` - generates documentation for a single module, for instance when `dokkaHtml` task is invoked +* `AllModulesPageGeneration` - generates multi-module documentation, for instance when `dokkaHtmlMultiModule` task is + invoked. + +## SingleModuleGeneration + +`SingleModuleGeneration` is at the heart of generating documentation. It utilizes [core](core_extension_points.md) and +[base](base_plugin.md) extensions to build the documentation from start to finish. + +Below you can see the flow of how Dokka's [data model](../architecture_overview.md#overview-of-data-model) is transformed +by various core and base extensions. + +```mermaid +flowchart TD + Input -- SourceToDocumentableTranslator --> doc1[Documentables] + subgraph documentables [ ] + doc1 -- PreMergeDocumentableTransformer --> doc2[Documentables] + doc2 -- DocumentableMerger --> doc3[Documentables] + doc3 -- DocumentableTransformer --> doc4[Documentables] + end + doc4 -- DocumentableToPageTranslator --> page1[Pages] + subgraph ide2 [ ] + page1 -- PageTransformer --> page2[Pages] + end + page2 -- Renderer --> Output +``` + +You can read about what each stage does in [Core extension points](core_extension_points.md) and +[Base plugin](base_plugin.md). + +## AllModulesPageGeneration + +`AllModulesPageGeneration` utilizes the output generated by `SingleModuleGeneration`. + +Under the hood, it just collects all of the pages generated for individual modules, and assembles it all together, +creating navigation links between the modules and so on. diff --git a/mkdocs/src/doc/docs/community/slack.md b/docs-developer/src/doc/docs/developer_guide/community/slack.md index 290d4a18..5e57f2ac 100644 --- a/mkdocs/src/doc/docs/community/slack.md +++ b/docs-developer/src/doc/docs/developer_guide/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/mkdocs/src/doc/docs/developer_guide/introduction.md b/docs-developer/src/doc/docs/developer_guide/introduction.md index feb601fe..627afa3a 100644 --- a/mkdocs/src/doc/docs/developer_guide/introduction.md +++ b/docs-developer/src/doc/docs/developer_guide/introduction.md @@ -1,10 +1,13 @@ # 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. +The purpose of the Developer guides documentation 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 locally. +[general workflow](workflow.md): it will teach you how to build, debug and test Dokka locally. + +[CONTRIBUTING.md](https://github.com/Kotlin/dokka/blob/master/CONTRIBUTING.md) contains information that can be useful +if you want to contribute to Dokka. If you want to get into plugin development quick, see [Introduction to plugin development](plugin-development/introduction.md). @@ -12,8 +15,8 @@ If you want to get into plugin development quick, see 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 inside `Internals`. -Having read through all the developer guides, you'll have a pretty good unrestanding of Dokka and how to develop +Having read through all the developer guides, you'll have a pretty good understanding 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 +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/mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md b/docs-developer/src/doc/docs/developer_guide/plugin-development/introduction.md index ad980e49..1008e336 100644 --- a/mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md +++ b/docs-developer/src/doc/docs/developer_guide/plugin-development/introduction.md @@ -9,9 +9,9 @@ descriptions, visually redesign Dokka's pages to be seamlessly integrated into y it with other tools and so much more. In order to have an easier time developing plugins, it's a good idea to go through -[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). +[Dokka's internals](../architecture/architecture_overview.md) first, to learn more about its +[data model](../architecture/data_model/documentable_model.md) and +[extensions](../architecture/extension_points/extension_points.md). ## Setup @@ -22,7 +22,7 @@ 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: +At a bare minimum, a Dokka plugin requires `dokka-core` as a dependency: ```kotlin import org.jetbrains.kotlin.gradle.dsl.JvmTarget @@ -42,31 +42,32 @@ tasks.withType<KotlinCompile>().configureEach { } ``` -In order to load a plugin into Dokka, your class must extend `DokkaPlugin` class. A fully qualified name of that class +In order to load a plugin into Dokka, your class must extend the `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`. +All instances are automatically loaded during Dokka's configuration step using `java.util.ServiceLoader`. ## Extension points 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). +extension points to use, have a look at [core extensions](../architecture/extension_points/core_extension_points.md) and +[base extensions](../architecture/extension_points/base_plugin.md). -You can learn how to declare extension points and use extensions in -[Introduction to Extension points](../architecture/extension_points/introduction.md). +You can learn how to declare extension points and extensions in +[Introduction to Extension points](../architecture/extension_points/extension_points.md). -In case no suitable extension point exists for your use case, do share the details - it might be added in future -versions of Dokka. +In case no suitable extension point exists for your use case, do share the use case with the +[maintainers](../community/slack.md) — it might be added in a future version of Dokka. ## Example -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. +You can follow the [sample plugin tutorial](sample-plugin-tutorial.md), which covers the creation of a simple plugin +that hides members annotated with your own `@Internal` annotation: that is, it excludes these members from the generated +documentation. -Fore more practical examples, have a look at sources of +For more practical examples, have a look at sources of [community plugins](https://kotlinlang.org/docs/dokka-plugins.html#notable-plugins). ## Help -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). +If you have any further questions, feel free to get in touch with Dokka's maintainers via [Slack](../community/slack.md) +or [GitHub](https://github.com/kotlin/dokka). diff --git a/mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md b/docs-developer/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md index 3869d79b..55d7001f 100644 --- a/mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md +++ b/docs-developer/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md @@ -54,7 +54,7 @@ and `groupId` in `build.gradle.kts`. 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 +Having read through [Core extensions](../architecture/extension_points/core_extension_points.md), it's clear that we need a `PreMergeDocumentableTransformer` extension in order to filter out undesired documentables. Moreover, the article mentioned a convenient abstract transformer `SuppressedByConditionDocumentableFilterTransformer` @@ -86,7 +86,7 @@ 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 the breakpoint. -Having read through [Introduction to extensions](../architecture/extension_points/introduction.md), we now know +Having read through [Introduction to extensions](../architecture/extension_points/extension_points.md), we now know how to register our extensions: ```kotlin diff --git a/mkdocs/src/doc/docs/developer_guide/workflow.md b/docs-developer/src/doc/docs/developer_guide/workflow.md index 388100b2..7db5382d 100644 --- a/mkdocs/src/doc/docs/developer_guide/workflow.md +++ b/docs-developer/src/doc/docs/developer_guide/workflow.md @@ -1,18 +1,18 @@ # Workflow -Whether you're contributing a feature/fix to Dokka itself or developing a separate plugin, there's 3 things -you'll be doing: +Whether you're contributing a feature/fix to Dokka itself or developing a Dokka plugin, there are 3 essential things +you need to know how to do: -1. Building Dokka / Plugins -2. Using/Testing locally built Dokka in a (debug) project -3. Debugging Dokka / Plugin code +1. How to build Dokka or a plugin +2. How to use/test locally built Dokka in a project +3. How to debug Dokka or a plugin in IntelliJ IDEA 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. -## Building Dokka +## Build 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 @@ -28,7 +28,7 @@ Unit tests which are run as part of `build` should not take much time, but you c #### API check failed for project .. -If you see messages like `API check failed for project ..` during `build` phase, it indicates that +If you see a message like `API check failed for project ..` during the `build` phase, it indicates that the [binary compatibility check](https://github.com/Kotlin/binary-compatibility-validator) has failed, meaning you've changed/added/removed some public API. @@ -36,7 +36,7 @@ If the change was intentional, run `./gradlew apiDump` - it will re-generate `.a 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 +## Use / test 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. @@ -44,7 +44,7 @@ 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.8.20-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` +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 @@ -52,7 +52,7 @@ repositories { mavenLocal() } ``` -4. Update your dokka dependency to the version you've just published: +4. Update your Dokka dependency to the version you've just published: ```kotlin plugins { id("org.jetbrains.dokka") version "1.8.20-my-fix-SNAPSHOT" @@ -63,22 +63,22 @@ After completing these steps, you should be able to build documentation using yo ## Debugging Dokka -Dokka is essentially a gradle plugin, so you can debug it the same way you would any other gradle plugin. +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. +wish to debug a Dokka plugin. 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 multimodule/multiplatform examples. + 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:<br/>`./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 +3. Run the 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. <br/>Example: `./gradlew dokkaHtml -Dorg.gradle.debug=true --no-daemon`. diff --git a/mkdocs/src/doc/docs/dokka_colors.css b/docs-developer/src/doc/docs/dokka_colors.css index 69a24359..69a24359 100644 --- a/mkdocs/src/doc/docs/dokka_colors.css +++ b/docs-developer/src/doc/docs/dokka_colors.css diff --git a/mkdocs/src/doc/docs/favicon.svg b/docs-developer/src/doc/docs/favicon.svg index 1fea0877..1fea0877 100755 --- a/mkdocs/src/doc/docs/favicon.svg +++ b/docs-developer/src/doc/docs/favicon.svg diff --git a/mkdocs/src/doc/docs/index.md b/docs-developer/src/doc/docs/index.md index 2cedfe35..2cedfe35 100644 --- a/mkdocs/src/doc/docs/index.md +++ b/docs-developer/src/doc/docs/index.md diff --git a/mkdocs/src/doc/mkdocs.yml b/docs-developer/src/doc/mkdocs.yml index 0b520944..533d32b1 100644 --- a/mkdocs/src/doc/mkdocs.yml +++ b/docs-developer/src/doc/mkdocs.yml @@ -8,7 +8,7 @@ site_url: https://github.com/Kotlin/dokka # Repository (add link to repository on each page) repo_name: dokka repo_url: https://github.com/Kotlin/dokka -edit_uri: edit/master/mkdocs/src/doc/docs/ +edit_uri: edit/master/docs-developer/src/doc/docs/ # Copyright (shown at the footer) copyright: 'Copyright © 2023 JetBrains' @@ -59,7 +59,7 @@ nav: - index.md - User guides: https://kotlinlang.org/docs/dokka-introduction.html - Developer guides: developer_guide/introduction.md - - Community: community/slack.md + - Community: developer_guide/community/slack.md - User guides: - https://kotlinlang.org/docs/dokka-introduction.html - Developer guides: @@ -68,18 +68,19 @@ nav: - Internals: - Architecture: developer_guide/architecture/architecture_overview.md - Data model: - - Documentables: developer_guide/architecture/data_model/documentables.md + - Documentables: developer_guide/architecture/data_model/documentable_model.md - Page & Content: developer_guide/architecture/data_model/page_content.md - Extra properties: developer_guide/architecture/data_model/extra.md - - Extension points: - - Extension points: 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 + - Extensions: + - Extension points: developer_guide/architecture/extension_points/extension_points.md + - Core extension points: developer_guide/architecture/extension_points/core_extension_points.md + - Base extensions: developer_guide/architecture/extension_points/base_plugin.md + - Generation implementations: developer_guide/architecture/extension_points/generation_implementations.md - Plugin development: - Plugin development: developer_guide/plugin-development/introduction.md - Sample plugin tutorial: developer_guide/plugin-development/sample-plugin-tutorial.md - Community: - - Slack: community/slack.md + - Slack: developer_guide/community/slack.md extra_css: - dokka_colors.css diff --git a/mkdocs/README.md b/mkdocs/README.md deleted file mode 100644 index c8d07370..00000000 --- a/mkdocs/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# MkDocs documentation - -This module contains documentation which is published to GitHub pages: -[kotlin.github.io/dokka](https://kotlin.github.io/dokka/). - -It is built using the [gradle-mkdocs-plugin](https://github.com/xvik/gradle-mkdocs-plugin). - -## Building - -You can build the documentation locally: - -```Bash -./gradlew :mkdocs:mkdocsBuild -``` - -The output directory is `build/mkdocs` - -### Livereload server - -Alternatively, you can run a livereload server that automatically rebuilds documentation on every change: - -```Bash -./gradlew :mkdocs:mkdocsServe -``` - -By default, it is run under [127.0.0.1:3001](http://127.0.0.1:3001/), but you can change it in -[mkdocs.yml](src/doc/mkdocs.yml) by setting the `dev_addr` option. - -## Publishing - -The documentation is published automatically for all changes in master and for every GitHub release. - -See [gh-pages.yml](../.github/workflows/gh-pages.yml) workflow configuration for more details. diff --git a/mkdocs/src/doc/docs/developer_guide/architecture/architecture_overview.md b/mkdocs/src/doc/docs/developer_guide/architecture/architecture_overview.md deleted file mode 100644 index fb11f32a..00000000 --- a/mkdocs/src/doc/docs/developer_guide/architecture/architecture_overview.md +++ /dev/null @@ -1,123 +0,0 @@ -# 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 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 easily extensible and customizable by adding several layers -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 - -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 --> 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, 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. -* `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 - * `<li>` / `<ul>` for `HTML` format - * `1.` / `*` for `Markdown` format - * `ContentCodeBlock` is mapped as - * `<code>` or `<pre>` 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 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 -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) - -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 -generating documentation. An extension is therefore an implementation of that interface which is extending the -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. - -Here's a sneak peek of the DSL: - -```kotlin -class MyPlugin : DokkaPlugin() { - // create an extension point for other developers - val signatureProvider by extensionPoint<SignatureProvider>() - - // provide a default implementation - val defaultSignatureProvider by extending { - signatureProvider with KotlinSignatureProvider() - } - - // register our own extension in base plugin and override its default - val dokkaBasePlugin by lazy { plugin<DokkaBase>() } - val multimoduleLocationProvider by extending { - (dokkaBasePlugin.locationProviderFactory - providing MultimoduleLocationProvider::Factory - override dokkaBasePlugin.locationProvider) - } -} - -// use a registered extention, pretty much dependency injection -class MyExtension(val context: DokkaContext) { - - val signatureProvider: SignatureProvider = context.plugin<MyPlugin>().querySingle { signatureProvider } - - fun doSomething() { - signatureProvider.signature(..) - } -} - -interface SignatureProvider { - fun signature(documentable: Documentable): List<ContentNode> -} - -class KotlinSignatureProvider : SignatureProvider { - override fun signature(documentable: Documentable): List<ContentNode> = listOf() -} -``` - -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. - -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/mkdocs/src/doc/docs/developer_guide/architecture/data_model/page_content.md b/mkdocs/src/doc/docs/developer_guide/architecture/data_model/page_content.md deleted file mode 100644 index 54ded235..00000000 --- a/mkdocs/src/doc/docs/developer_guide/architecture/data_model/page_content.md +++ /dev/null @@ -1,140 +0,0 @@ -# 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`. - -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. - -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 - 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] - secondPackage --> etcOne[...] - thirdPackage --> etcTwo[...] -``` - -Almost all pages are derivatives of `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("This list contains a nested table:") - table { - header { - text("Col1") - text("Col2") - } - row { - text("Text1") - text("Text2") - } - } - } - item { - group(styles = setOf(TextStyle.Bold)) { - text("This is bald") - text("This is also bald") - } - } -} -``` - -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 `<code>text</code>`, 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 a specific `ContentNode`. - -```kotlin -data class DCI(val dri: Set<DRI>, 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 `<b>text</b>`, 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/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md b/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md deleted file mode 100644 index 16a52fab..00000000 --- a/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/base_extensions.md +++ /dev/null @@ -1,13 +0,0 @@ -# Base extensions - -`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 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. - -You can learn how to add/use/override/configure extensions and extension points in -[Introduction to Extensions](introduction.md), all the information is applicable to `DokkaBase` plugin as well. diff --git a/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md b/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md deleted file mode 100644 index 77ebc15e..00000000 --- a/mkdocs/src/doc/docs/developer_guide/architecture/extension_points/core_extensions.md +++ /dev/null @@ -1,151 +0,0 @@ -# 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 core extensions in `CoreExtensions` class: -```kotlin -object CoreExtensions { - val preGenerationCheck by coreExtensionPoint<PreGenerationChecker>() - val generation by coreExtensionPoint<Generation>() - val sourceToDocumentableTranslator by coreExtensionPoint<SourceToDocumentableTranslator>() - val documentableMerger by coreExtensionPoint<DocumentableMerger>() - val documentableTransformer by coreExtensionPoint<DocumentableTransformer>() - val documentableToPageTranslator by coreExtensionPoint<DocumentableToPageTranslator>() - val pageTransformer by coreExtensionPoint<PageTransformer>() - val renderer by coreExtensionPoint<Renderer>() - val postActions by coreExtensionPoint<PostAction>() -} -``` - -On this page we'll go over each extension point individually. - -## PreGenerationChecker - -`PreGenerationChecker` can be used to run some checks and constraints. - -For instance, `Javadoc` plugin does not support generating documentation for multi-platform projects, so it uses -`PreGenerationChecker` to check for multi-platform -[source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) and fails if it finds any. - -## Generation - -`Generation` is responsible for generating documentation as a whole, utilizing 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, 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 --> 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 - -`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 the [Documentable](../data_model/documentables.md) 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 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](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`. - -For simple condition-based filtering of documentables consider extending -`SuppressedByConditionDocumentableFilterTransformer` - it implements `PreMergeDocumentableTransformer` and only -requires one function to be overridden. The rest is taken care of. - -#### 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](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) and creates an inheritance -map. - -#### DocumentableToPageTranslator - -`DocumentableToPageTranslator` is responsible for creating pages and their content. See -[Page/Content model](../data_model/page_content.md) 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](https://github.com/Kotlin/dokka/tree/master/plugins/versioning) utilizes `PostAction` in order to move -generated documentation to versioned folders. diff --git a/mkdocs/src/doc/docs/user_guide/output-formats/html.md b/mkdocs/src/doc/docs/user_guide/output-formats/html.md deleted file mode 100644 index e69de29b..00000000 --- a/mkdocs/src/doc/docs/user_guide/output-formats/html.md +++ /dev/null diff --git a/settings.gradle.kts b/settings.gradle.kts index 6e67f58c..2320ca69 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -96,7 +96,7 @@ include( ":integration-tests:cli", ":integration-tests:maven", - ":mkdocs", + ":docs-developer", ) val isCiBuild = System.getenv("GITHUB_ACTIONS") != null || System.getenv("TEAMCITY_VERSION") != null |