Skip to content

Commit

Permalink
Stabilize Sample analysis API (#3195)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev authored Nov 21, 2023
1 parent 9ce37af commit 6fbc222
Show file tree
Hide file tree
Showing 35 changed files with 1,216 additions and 407 deletions.
25 changes: 14 additions & 11 deletions dokka-subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
public final class org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
public fun <init> ()V
public final fun getSampleAnalysisEnvironmentCreator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
}

public final class org/jetbrains/dokka/analysis/kotlin/internal/DocumentableLanguage : java/lang/Enum {
Expand Down Expand Up @@ -51,7 +52,6 @@ public final class org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAn
public final fun getInheritanceBuilder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getKotlinToJavaService ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getModuleAndPackageDocumentationReader ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getSampleProviderFactory ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getSyntheticDocumentableDetector ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
}

Expand All @@ -65,21 +65,24 @@ public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/Mod
public abstract fun read (Lorg/jetbrains/dokka/model/DPackage;)Ljava/util/Map;
}

public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider : java/lang/AutoCloseable {
public abstract fun getSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet;
public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector {
public abstract fun isSynthetic (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Z
}

public final class org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public final fun getBody ()Ljava/lang/String;
public final fun getImports ()Ljava/lang/String;
public abstract interface class org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironment {
public abstract fun resolveSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet;
}

public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SampleProviderFactory {
public abstract fun build ()Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider;
public abstract interface class org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator {
public abstract fun use (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector {
public abstract fun isSynthetic (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Z
public final class org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet {
public fun <init> (Ljava/util/List;Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getBody ()Ljava/lang/String;
public final fun getImports ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@

package org.jetbrains.dokka.analysis.kotlin

import org.jetbrains.dokka.analysis.kotlin.sample.SampleAnalysisEnvironmentCreator
import org.jetbrains.dokka.analysis.kotlin.sample.SampleAnalysisEnvironment
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
import org.jetbrains.dokka.plugability.ExtensionPoint
import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement

public class KotlinAnalysisPlugin : DokkaPlugin() {

/*
* This is where stable public API will go.
/**
* An extension for analyzing Kotlin sample functions used in the `@sample` KDoc tag.
*
* No stable public API for now.
* @see SampleAnalysisEnvironment for more details
*/
public val sampleAnalysisEnvironmentCreator: ExtensionPoint<SampleAnalysisEnvironmentCreator> by extensionPoint()

@OptIn(DokkaPluginApiPreview::class)
override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ public class InternalKotlinAnalysisPlugin : DokkaPlugin() {

public val documentableSourceLanguageParser: ExtensionPoint<DocumentableSourceLanguageParser> by extensionPoint()

public val sampleProviderFactory: ExtensionPoint<SampleProviderFactory> by extensionPoint()

@OptIn(DokkaPluginApiPreview::class)
override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.analysis.kotlin.sample

import org.jetbrains.dokka.DokkaConfiguration

/**
* Fully-configured and ready-to-use sample analysis environment.
*
* It's best to limit the scope of use and lifetime of this environment as it takes up
* additional resources which could be freed once the samples have been analyzed.
* Therefore, it's best to use it through the [SampleAnalysisEnvironmentCreator.use] lambda.
*
* For example, if you need to process all samples in an arbitrary project, it's best to do it
* in one iteration and at the same time, so that the environment is created once and lives for
* as little is possible, as opposed to creating it again and again for every individual sample.
*/
public interface SampleAnalysisEnvironment {

/**
* Resolves a Kotlin sample function by its fully qualified name, and returns its import statements and body.
*
* @param sourceSet must be either the source set in which this sample function resides, or the source set
* for which [DokkaConfiguration#samples] or [DokkaConfiguration#sourceRoots]
* have been configured with the sample's sources.
* @param fullyQualifiedLink fully qualified path to the sample function, including all middle packages
* and the name of the function. Only links to Kotlin functions are valid,
* which can reside within a class. The package must be the same as the package
* declared in the sample file. The function must be resolvable by Dokka,
* meaning it must reside either in the main sources of the project or its
* sources must be included in [DokkaConfiguration#samples] or
* [DokkaConfiguration#sourceRoots]. Example: `com.example.pckg.topLevelKotlinFunction`
*
* @return a sample code snippet which includes import statements and the function body,
* or null if the link could not be resolved (examine the logs to find out the reason).
*/
public fun resolveSample(
sourceSet: DokkaConfiguration.DokkaSourceSet,
fullyQualifiedLink: String
): SampleSnippet?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.analysis.kotlin.sample

import org.jetbrains.dokka.analysis.kotlin.KotlinAnalysisPlugin

/**
* Entry point to analyzing Kotlin samples.
*
* Can be acquired via [KotlinAnalysisPlugin.sampleAnalysisEnvironmentCreator].
*/
public interface SampleAnalysisEnvironmentCreator {

/**
* Creates and configures the sample analysis environment for a limited-time use.
*
* Configuring sample analysis environment is a rather expensive operation that takes up additional
* resources since Dokka needs to configure and analyze source roots additional to the main ones.
* It's best to limit the scope of use and the lifetime of the created environment
* so that the resources could be freed as soon as possible.
*
* No specific cleanup is required by the caller - everything is taken care of automatically
* as soon as you exit the [block] block.
*
* Usage example:
* ```kotlin
* // create a short-lived environment and resolve all the needed samples
* val sample = sampleAnalysisEnvironmentCreator.use {
* resolveSample(sampleSourceSet, "org.jetbrains.dokka.sample.functionName")
* }
* // process the samples
* // ...
* ```
*/
public fun <T> use(block: SampleAnalysisEnvironment.() -> T): T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package org.jetbrains.dokka.analysis.kotlin.sample

/**
* Represents a sample code snippet of a Kotlin function. The snippet includes both file
* import directives (all, even unused) and the sample function body.
*
* @property imports list of import statement values, without the `import` prefix.
* Contains no blank lines. Example of a single value: `com.example.pckg.MyClass.function`.
* @property body body of the sample function, without the function name or curly braces, only the inner body.
* Common minimal indent of all lines is trimmed. Leading and trailing line breaks are removed.
* Trailing whitespaces are removed. Example: given the sample function `fun foo() { println("foo") }`,
* the sample body will be `println("foo")`.
*
* @see SampleAnalysisEnvironment for how to acquire it
*/
public class SampleSnippet(
public val imports: List<String>,
public val body: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as SampleSnippet

if (imports != other.imports) return false
if (body != other.body) return false

return true
}

override fun hashCode(): Int {
var result = imports.hashCode()
result = 31 * result + body.hashCode()
return result
}

override fun toString(): String {
return "SampleSnippet(imports=$imports, body='$body')"
}
}
Loading

0 comments on commit 6fbc222

Please sign in to comment.