aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/signatures
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/base/src/main/kotlin/signatures')
-rw-r--r--plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt141
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt382
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt48
-rw-r--r--plugins/base/src/main/kotlin/signatures/SignatureProvider.kt8
4 files changed, 579 insertions, 0 deletions
diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
new file mode 100644
index 00000000..689f6db5
--- /dev/null
+++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
@@ -0,0 +1,141 @@
+package org.jetbrains.dokka.base.signatures
+
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+
+interface JvmSignatureUtils {
+
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable)
+
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable)
+
+ fun <T : Documentable> WithExtraProperties<T>.modifiers(): SourceSetDependent<Set<ExtraModifiers>>
+
+ fun Collection<ExtraModifiers>.toSignatureString(): String =
+ joinToString("") { it.name.toLowerCase() + " " }
+
+ fun <T : Documentable> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> =
+ extra[Annotations]?.content ?: emptyMap()
+
+ private fun PageContentBuilder.DocumentableContentBuilder.annotations(
+ d: Documentable,
+ ignored: Set<Annotations.Annotation>,
+ styles: Set<Style>,
+ operation: PageContentBuilder.DocumentableContentBuilder.(Annotations.Annotation) -> Unit
+ ): Unit = when (d) {
+ is DFunction -> d.annotations()
+ is DProperty -> d.annotations()
+ is DClass -> d.annotations()
+ is DInterface -> d.annotations()
+ is DObject -> d.annotations()
+ is DEnum -> d.annotations()
+ is DAnnotation -> d.annotations()
+ is DTypeParameter -> d.annotations()
+ is DEnumEntry -> d.annotations()
+ is DTypeAlias -> d.annotations()
+ is DParameter -> d.annotations()
+ else -> null
+ }?.let {
+ it.entries.forEach {
+ it.value.filter { it !in ignored && it.mustBeDocumented }.takeIf { it.isNotEmpty() }?.let { annotations ->
+ group(sourceSets = setOf(it.key), styles = styles, kind = ContentKind.Annotations) {
+ annotations.forEach {
+ operation(it)
+ }
+ }
+ }
+ }
+ } ?: Unit
+
+ fun PageContentBuilder.DocumentableContentBuilder.toSignatureString(
+ a: Annotations.Annotation,
+ renderAtStrategy: AtStrategy,
+ listBrackets: Pair<Char, Char>,
+ classExtension: String
+ ) {
+
+ when (renderAtStrategy) {
+ is All, is OnlyOnce -> text("@")
+ is Never -> Unit
+ }
+ link(a.dri.classNames!!, a.dri)
+ text("(")
+ a.params.entries.forEachIndexed { i, it ->
+ group(styles = setOf(TextStyle.BreakableAfter)) {
+ text(it.key + " = ")
+ when (renderAtStrategy) {
+ is All -> All
+ is Never, is OnlyOnce -> Never
+ }.let { strategy ->
+ valueToSignature(it.value, strategy, listBrackets, classExtension)
+ }
+ if (i != a.params.entries.size - 1) text(", ")
+ }
+ }
+ text(")")
+ }
+
+ private fun PageContentBuilder.DocumentableContentBuilder.valueToSignature(
+ a: AnnotationParameterValue,
+ renderAtStrategy: AtStrategy,
+ listBrackets: Pair<Char, Char>,
+ classExtension: String
+ ): Unit = when (a) {
+ is AnnotationValue -> toSignatureString(a.annotation, renderAtStrategy, listBrackets, classExtension)
+ is ArrayValue -> {
+ text(listBrackets.first.toString())
+ a.value.forEachIndexed { i, it ->
+ group(styles = setOf(TextStyle.BreakableAfter)) {
+ valueToSignature(it, renderAtStrategy, listBrackets, classExtension)
+ if (i != a.value.size - 1) text(", ")
+ }
+ }
+ text(listBrackets.second.toString())
+ }
+ is EnumValue -> link(a.enumName, a.enumDri)
+ is ClassValue -> link(a.className + classExtension, a.classDRI)
+ is StringValue -> group(styles = setOf(TextStyle.Breakable)) { text(a.value) }
+ }
+
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsBlockWithIgnored(
+ d: Documentable,
+ ignored: Set<Annotations.Annotation>,
+ renderAtStrategy: AtStrategy,
+ listBrackets: Pair<Char, Char>,
+ classExtension: String
+ ) {
+ annotations(d, ignored, setOf(TextStyle.Block)) {
+ group {
+ toSignatureString(it, renderAtStrategy, listBrackets, classExtension)
+ }
+ }
+ }
+
+ fun PageContentBuilder.DocumentableContentBuilder.annotationsInlineWithIgnored(
+ d: Documentable,
+ ignored: Set<Annotations.Annotation>,
+ renderAtStrategy: AtStrategy,
+ listBrackets: Pair<Char, Char>,
+ classExtension: String
+ ) {
+ annotations(d, ignored, setOf(TextStyle.Span)) {
+ toSignatureString(it, renderAtStrategy, listBrackets, classExtension)
+ text(Typography.nbsp.toString())
+ }
+ }
+
+ fun <T : Documentable> WithExtraProperties<T>.stylesIfDeprecated(sourceSetData: DokkaSourceSet): Set<TextStyle> =
+ if (extra[Annotations]?.content?.get(sourceSetData)?.any {
+ it.dri == DRI("kotlin", "Deprecated")
+ || it.dri == DRI("java.lang", "Deprecated")
+ } == true) setOf(TextStyle.Strikethrough) else emptySet()
+}
+
+sealed class AtStrategy
+object All : AtStrategy()
+object OnlyOnce : AtStrategy()
+object Never : AtStrategy()
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
new file mode 100644
index 00000000..37e0ea83
--- /dev/null
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
@@ -0,0 +1,382 @@
+package org.jetbrains.dokka.base.signatures
+
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.dri
+import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull
+import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.links.*
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.Nullable
+import org.jetbrains.dokka.model.TypeConstructor
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.ContentKind
+import org.jetbrains.dokka.pages.ContentNode
+import org.jetbrains.dokka.pages.TextStyle
+import org.jetbrains.dokka.utilities.DokkaLogger
+import kotlin.text.Typography.nbsp
+
+class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider,
+ JvmSignatureUtils by KotlinSignatureUtils {
+ private val contentBuilder = PageContentBuilder(ctcc, this, logger)
+
+ private val ignoredVisibilities = setOf(JavaVisibility.Public, KotlinVisibility.Public)
+ private val ignoredModifiers = setOf(JavaModifier.Final, KotlinModifier.Final)
+ private val ignoredExtraModifiers = setOf(
+ ExtraModifiers.KotlinOnlyModifiers.TailRec,
+ ExtraModifiers.KotlinOnlyModifiers.External
+ )
+ private val platformSpecificModifiers: Map<ExtraModifiers, Set<Platform>> = mapOf(
+ ExtraModifiers.KotlinOnlyModifiers.External to setOf(Platform.js)
+ )
+
+ override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) {
+ is DFunction -> functionSignature(documentable)
+ is DProperty -> propertySignature(documentable)
+ is DClasslike -> classlikeSignature(documentable)
+ is DTypeParameter -> signature(documentable)
+ is DEnumEntry -> signature(documentable)
+ is DTypeAlias -> signature(documentable)
+ else -> throw NotImplementedError(
+ "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}"
+ )
+ }
+
+ private fun <T> PageContentBuilder.DocumentableContentBuilder.processExtraModifiers(t: T)
+ where T : Documentable, T : WithExtraProperties<T> {
+ sourceSetDependentText(
+ t.modifiers()
+ .mapValues { entry ->
+ entry.value.filter {
+ it !in ignoredExtraModifiers || entry.key.analysisPlatform in (platformSpecificModifiers[it]
+ ?: emptySet())
+ }
+ }
+ ) {
+ it.toSignatureString()
+ }
+ }
+
+ private fun signature(e: DEnumEntry): List<ContentNode> =
+ e.sourceSets.map {
+ contentBuilder.contentFor(
+ e,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace, TextStyle.Block) + e.stylesIfDeprecated(it),
+ sourceSets = setOf(it)
+ ) {
+ group(styles = setOf(TextStyle.Block)) {
+ annotationsBlock(e)
+ link(e.name, e.dri, styles = emptySet())
+ e.extra[ConstructorValues]?.let { constructorValues ->
+ constructorValues.values[it]
+ text(constructorValues.values[it]?.joinToString(prefix = "(", postfix = ")") ?: "")
+ }
+ }
+ }
+ }
+
+ private fun actualTypealiasedSignature(c: DClasslike, sourceSet: DokkaSourceSet, aliasedType: Bound) =
+ contentBuilder.contentFor(
+ c,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet)
+ ?: emptySet()),
+ sourceSets = setOf(sourceSet)
+ ) {
+ text("actual typealias ")
+ link(c.name.orEmpty(), c.dri)
+ text(" = ")
+ signatureForProjection(aliasedType)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <T : DClasslike> classlikeSignature(c: T): List<ContentNode> =
+ c.sourceSets.map { sourceSetData ->
+ (c as? WithExtraProperties<out DClasslike>)?.extra?.get(ActualTypealias)?.underlyingType?.get(sourceSetData)
+ ?.let {
+ actualTypealiasedSignature(c, sourceSetData, it)
+ } ?: regularSignature(c, sourceSetData)
+ }
+
+
+ private fun regularSignature(c: DClasslike, sourceSet: DokkaSourceSet) =
+ contentBuilder.contentFor(
+ c,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet)
+ ?: emptySet()),
+ sourceSets = setOf(sourceSet)
+ ) {
+ annotationsBlock(c)
+ text(c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "")
+ if (c is DClass) {
+ text(
+ if (c.modifier[sourceSet] !in ignoredModifiers)
+ when {
+ c.extra[AdditionalModifiers]?.content?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> ""
+ c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} "
+ else -> c.modifier[sourceSet]?.name?.let { "$it " } ?: ""
+ }
+ else
+ ""
+ )
+ }
+ if (c is DInterface) {
+ c.extra[AdditionalModifiers]?.content?.let { additionalModifiers ->
+ sourceSetDependentText(additionalModifiers, setOf(sourceSet)) { extraModifiers ->
+ if (ExtraModifiers.KotlinOnlyModifiers.Fun in extraModifiers) "fun "
+ else ""
+ }
+ }
+ }
+ when (c) {
+ is DClass -> {
+ processExtraModifiers(c)
+ text("class ")
+ }
+ is DInterface -> {
+ processExtraModifiers(c)
+ text("interface ")
+ }
+ is DEnum -> {
+ processExtraModifiers(c)
+ text("enum ")
+ }
+ is DObject -> {
+ processExtraModifiers(c)
+ text("object ")
+ }
+ is DAnnotation -> {
+ processExtraModifiers(c)
+ text("annotation class ")
+ }
+ }
+ link(c.name!!, c.dri)
+ if (c is WithGenerics) {
+ list(c.generics, prefix = "<", suffix = "> ") {
+ +buildSignature(it)
+ }
+ }
+ if (c is WithConstructors) {
+ val pConstructor = c.constructors.singleOrNull { it.extra[PrimaryConstructorExtra] != null }
+ if (pConstructor?.sourceSets?.contains(sourceSet) == true) {
+ if (pConstructor.annotations().values.any { it.isNotEmpty() }) {
+ text(nbsp.toString())
+ annotationsInline(pConstructor)
+ text("constructor")
+ }
+ list(
+ pConstructor.parameters,
+ "(",
+ ")",
+ ",",
+ pConstructor.sourceSets.toSet()
+ ) {
+ annotationsInline(it)
+ text(it.name ?: "", styles = mainStyles.plus(TextStyle.Bold))
+ text(": ")
+ signatureForProjection(it.type)
+ }
+ }
+ }
+ if (c is WithSupertypes) {
+ c.supertypes.filter { it.key == sourceSet }.map { (s, dris) ->
+ list(dris, prefix = " : ", sourceSets = setOf(s)) {
+ link(it.dri.sureClassNames, it.dri, sourceSets = setOf(s))
+ }
+ }
+ }
+ }
+
+ private fun propertySignature(p: DProperty) =
+ p.sourceSets.map {
+ contentBuilder.contentFor(
+ p,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace) + p.stylesIfDeprecated(it),
+ sourceSets = setOf(it)
+ ) {
+ annotationsBlock(p)
+ text(p.visibility[it].takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "")
+ text(
+ p.modifier[it].takeIf { it !in ignoredModifiers }?.let {
+ if (it is JavaModifier.Empty) KotlinModifier.Open else it
+ }?.name?.let { "$it " } ?: ""
+ )
+ text(p.modifiers()[it]?.toSignatureString() ?: "")
+ p.setter?.let { text("var ") } ?: text("val ")
+ list(p.generics, prefix = "<", suffix = "> ") {
+ +buildSignature(it)
+ }
+ p.receiver?.also {
+ signatureForProjection(it.type)
+ text(".")
+ }
+ link(p.name, p.dri)
+ text(": ")
+ signatureForProjection(p.type)
+ }
+ }
+
+ private fun functionSignature(f: DFunction) =
+ f.sourceSets.map {
+ contentBuilder.contentFor(
+ f,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace) + f.stylesIfDeprecated(it),
+ sourceSets = setOf(it)
+ ) {
+ annotationsBlock(f)
+ text(f.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "")
+ text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.let {
+ if (it is JavaModifier.Empty) KotlinModifier.Open else it
+ }?.name?.let { "$it " } ?: ""
+ )
+ text(f.modifiers()[it]?.toSignatureString() ?: "")
+ text("fun ")
+ list(f.generics, prefix = "<", suffix = "> ") {
+ +buildSignature(it)
+ }
+ f.receiver?.also {
+ signatureForProjection(it.type)
+ text(".")
+ }
+ link(f.name, f.dri)
+ text("(")
+ list(f.parameters) {
+ annotationsInline(it)
+ processExtraModifiers(it)
+ text(it.name!!)
+ text(": ")
+ signatureForProjection(it.type)
+ }
+ text(")")
+ if (f.documentReturnType()) {
+ text(": ")
+ signatureForProjection(f.type)
+ }
+ }
+ }
+
+ private fun DFunction.documentReturnType() = when {
+ this.isConstructor -> false
+ this.type is TypeConstructor && (this.type as TypeConstructor).dri == DriOfUnit -> false
+ this.type is Void -> false
+ else -> true
+ }
+
+ private fun signature(t: DTypeAlias) =
+ t.sourceSets.map {
+ contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) {
+ t.underlyingType.entries.groupBy({ it.value }, { it.key }).map { (type, platforms) ->
+ +contentBuilder.contentFor(
+ t,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace),
+ sourceSets = platforms.toSet()
+ ) {
+ text(t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "")
+ processExtraModifiers(t)
+ text("typealias ")
+ signatureForProjection(t.type)
+ text(" = ")
+ signatureForTypealiasTarget(t, type)
+ }
+ }
+ }
+ }
+
+ private fun signature(t: DTypeParameter) =
+ t.sourceSets.map {
+ contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) {
+ link(t.name, t.dri.withTargetToDeclaration())
+ list(t.bounds, prefix = " : ") {
+ signatureForProjection(it)
+ }
+ }
+ }
+
+ private fun PageContentBuilder.DocumentableContentBuilder.signatureForTypealiasTarget(
+ typeAlias: DTypeAlias, bound: Bound
+ ) {
+ signatureForProjection(
+ p = bound,
+ showFullyQualifiedName =
+ bound.driOrNull?.packageName != typeAlias.dri.packageName &&
+ bound.driOrNull?.packageName != "kotlin"
+ )
+ }
+
+ private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(
+ p: Projection, showFullyQualifiedName: Boolean = false
+ ): Unit =
+ when (p) {
+ is OtherParameter -> link(p.name, p.declarationDRI)
+
+ is TypeConstructor -> if (p.function)
+ +funType(mainDRI.single(), mainSourcesetData, p)
+ else
+ group(styles = emptySet()) {
+ val linkText = if (showFullyQualifiedName && p.dri.packageName != null) {
+ "${p.dri.packageName}.${p.dri.classNames.orEmpty()}"
+ } else p.dri.classNames.orEmpty()
+
+ link(linkText, p.dri)
+ list(p.projections, prefix = "<", suffix = ">") {
+ signatureForProjection(it, showFullyQualifiedName)
+ }
+ }
+
+ is Variance -> group(styles = emptySet()) {
+ text(p.kind.toString() + " ")
+ signatureForProjection(p.inner, showFullyQualifiedName)
+ }
+
+ is Star -> text("*")
+
+ is Nullable -> group(styles = emptySet()) {
+ signatureForProjection(p.inner, showFullyQualifiedName)
+ text("?")
+ }
+
+ is JavaObject -> link("Any", DriOfAny)
+ is Void -> link("Unit", DriOfUnit)
+ is PrimitiveJavaType -> signatureForProjection(p.translateToKotlin(), showFullyQualifiedName)
+ is Dynamic -> text("dynamic")
+ is UnresolvedBound -> text(p.name)
+ }
+
+ private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: TypeConstructor) =
+ contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) {
+ if (type.extension) {
+ signatureForProjection(type.projections.first())
+ text(".")
+ }
+
+ val args = if (type.extension)
+ type.projections.drop(1)
+ else
+ type.projections
+
+ text("(")
+ args.subList(0, args.size - 1).forEachIndexed { i, arg ->
+ signatureForProjection(arg)
+ if (i < args.size - 2) text(", ")
+ }
+ text(") -> ")
+ signatureForProjection(args.last())
+ }
+}
+
+private fun PrimitiveJavaType.translateToKotlin() = TypeConstructor(
+ dri = dri,
+ projections = emptyList()
+)
+
+val TypeConstructor.function
+ get() = modifier == FunctionModifiers.FUNCTION || modifier == FunctionModifiers.EXTENSION
+
+val TypeConstructor.extension
+ get() = modifier == FunctionModifiers.EXTENSION
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
new file mode 100644
index 00000000..0a10875a
--- /dev/null
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
@@ -0,0 +1,48 @@
+package org.jetbrains.dokka.base.signatures
+
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.DriOfAny
+import org.jetbrains.dokka.links.DriOfUnit
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+object KotlinSignatureUtils : JvmSignatureUtils {
+
+ private val strategy = OnlyOnce
+ private val listBrackets = Pair('[', ']')
+ private val classExtension = "::class"
+ private val ignoredAnnotations = setOf(
+ Annotations.Annotation(DRI("kotlin", "SinceKotlin"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin", "Deprecated"), emptyMap())
+ )
+
+
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable) =
+ annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
+
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable) =
+ annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
+
+ override fun <T : Documentable> WithExtraProperties<T>.modifiers() =
+ extra[AdditionalModifiers]?.content?.entries?.map {
+ it.key to it.value.filterIsInstance<ExtraModifiers.KotlinOnlyModifiers>().toSet()
+ }?.toMap() ?: emptyMap()
+
+
+ val PrimitiveJavaType.dri: DRI get() = DRI("kotlin", name.capitalize())
+
+ val Bound.driOrNull: DRI?
+ get() {
+ return when (this) {
+ is OtherParameter -> this.declarationDRI
+ is TypeConstructor -> this.dri
+ is Nullable -> this.inner.driOrNull
+ is PrimitiveJavaType -> this.dri
+ is Void -> DriOfUnit
+ is JavaObject -> DriOfAny
+ is Dynamic -> null
+ is UnresolvedBound -> null
+ }
+ }
+}
diff --git a/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt
new file mode 100644
index 00000000..e1933fb8
--- /dev/null
+++ b/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt
@@ -0,0 +1,8 @@
+package org.jetbrains.dokka.base.signatures
+
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.pages.ContentNode
+
+interface SignatureProvider {
+ fun signature(documentable: Documentable): List<ContentNode>
+}