diff options
Diffstat (limited to 'mkdocs/src/doc/docs/developer_guide/plugin-development')
-rw-r--r-- | mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md | 72 | ||||
-rw-r--r-- | mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md | 297 |
2 files changed, 0 insertions, 369 deletions
diff --git a/mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md b/mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md deleted file mode 100644 index ad980e49..00000000 --- a/mkdocs/src/doc/docs/developer_guide/plugin-development/introduction.md +++ /dev/null @@ -1,72 +0,0 @@ -# Introduction to plugin development - -Dokka was built from the ground up to be easily extensible and highly customizable, which allows the community to -implement plugins for missing or very specific features that are not provided out of the box. - -Dokka plugins range anywhere from supporting other programming language sources to exotic output formats. You can add -support for your own KDoc tags or annotations, teach Dokka how to render different DSLs that are found in KDoc -descriptions, visually redesign Dokka's pages to be seamlessly integrated into your company's website, integrate -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). - -## Setup - -### 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 - -At a bare minimum, Dokka requires `Kotlin Gradle Plugin` and `dokka-core` dependencies: - -```kotlin -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - - -plugins { - kotlin("jvm") version "<kotlin_version>" -} - -dependencies { - compileOnly("org.jetbrains.dokka:dokka-core:<dokka_version>") -} - -tasks.withType<KotlinCompile>().configureEach { - compilerOptions.jvmTarget.set(JvmTarget.JVM_1_8) -} -``` - -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`. - -## 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). - -You can learn how to declare extension points and use extensions in -[Introduction to Extension points](../architecture/extension_points/introduction.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. - -## 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. - -Fore 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). diff --git a/mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md b/mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md deleted file mode 100644 index 3869d79b..00000000 --- a/mkdocs/src/doc/docs/developer_guide/plugin-development/sample-plugin-tutorial.md +++ /dev/null @@ -1,297 +0,0 @@ -# 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. - -The plugin will be tested with the following code: - -```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 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 -`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/). - -First, let's rename the pre-made `template` package and `MyAwesomeDokkaPlugin` class to something of our own. - -For instance, package can be renamed to `org.example.dokka.plugin` and the class to `HideInternalApiPlugin`: - -```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` (to `hide-internal-api` in our case) -and `groupId` in `build.gradle.kts`. - -## Extending Dokka - -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` extension in order to filter out undesired documentables. - -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 - -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 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 `@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 the 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<DokkaBase>().preMergeDocumentableTransformer providing ::HideInternalApiTransformer - } -} -``` - -At this point we're ready to debug our plugin locally, it should already work, but do nothing. - -## Debugging - -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 `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 that has Dokka configured, 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, 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<Annotations.Annotation> = - (d as? WithExtraProperties<*>) - ?.extra - ?.allOfType<Annotations>() - ?.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" -} -``` - -Seems like we're done with writing our plugin and can begin testing it manually. - -## Manual testing - -At this point, the implementation of your plugin should look 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<DokkaBase>().preMergeDocumentableTransformer providing ::HideInternalApiTransformer - } -} - -class HideInternalApiTransformer(context: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(context) { - - override fun shouldBeSuppressed(d: Documentable): Boolean { - val annotations: List<Annotations.Annotation> = - (d as? WithExtraProperties<*>) - ?.extra - ?.allOfType<Annotations>() - ?.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, 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! - -## 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 - 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 test 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 tested code (inside `testInline` function) is the same as the package that we have -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: - -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](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 -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 `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). - -___ - -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). |