aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/base/src')
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt3
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt35
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt17
-rw-r--r--plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt3
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt39
-rw-r--r--plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt15
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/utils.kt22
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt14
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt2
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt190
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt4
-rw-r--r--plugins/base/src/main/resources/dokka/styles/style.css33
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt139
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt395
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt (renamed from plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt)46
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt109
-rw-r--r--plugins/base/src/test/kotlin/utils/contentUtils.kt4
17 files changed, 987 insertions, 83 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 945fff38..f5c3854c 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -95,6 +95,7 @@ open class HtmlRenderer(
childrenCallback()
}
node.hasStyle(ContentStyle.KDocTag) -> span("kdoc-tag") { childrenCallback() }
+ node.hasStyle(ContentStyle.Footnote) -> div("footnote") { childrenCallback() }
node.hasStyle(TextStyle.BreakableAfter) -> {
span { childrenCallback() }
wbr { }
@@ -124,10 +125,12 @@ open class HtmlRenderer(
node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { //TODO this can be removed
childrenCallback()
}
+ node.dci.kind == ContentKind.Deprecation -> div("deprecation-content") { childrenCallback() }
node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.Quotation) -> blockQuote(additionalClasses) { childrenCallback() }
node.hasStyle(TextStyle.FloatingRight) -> span("clearfix") { span("floating-right") { childrenCallback() } }
+ node.hasStyle(TextStyle.Strikethrough) -> strike { childrenCallback() }
node.isAnchorable -> buildAnchor(
node.anchor!!,
node.anchorLabel!!,
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
index 958488ef..ecce70e8 100644
--- a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt
@@ -1,6 +1,8 @@
package org.jetbrains.dokka.base.renderers.html
import org.jetbrains.dokka.base.renderers.sourceSets
+import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
import org.jetbrains.dokka.base.transformers.documentables.isException
import org.jetbrains.dokka.base.translators.documentables.DocumentableLanguage
import org.jetbrains.dokka.base.translators.documentables.documentableLanguage
@@ -17,6 +19,7 @@ abstract class NavigationDataProvider {
dri = page.dri.first(),
sourceSets = page.sourceSets(),
icon = chooseNavigationIcon(page),
+ styles = chooseStyles(page),
children = page.navigableChildren()
)
@@ -31,8 +34,8 @@ abstract class NavigationDataProvider {
name
}
- private fun chooseNavigationIcon(contentPage: ContentPage): NavigationNodeIcon? {
- return if (contentPage is WithDocumentables) {
+ private fun chooseNavigationIcon(contentPage: ContentPage): NavigationNodeIcon? =
+ if (contentPage is WithDocumentables) {
val documentable = contentPage.documentables.firstOrNull()
val isJava = documentable?.hasAnyJavaSources() ?: false
@@ -61,27 +64,41 @@ abstract class NavigationDataProvider {
} else {
null
}
- }
private fun Documentable.hasAnyJavaSources(): Boolean {
val withSources = this as? WithSources ?: return false
return this.sourceSets.any { withSources.documentableLanguage(it) == DocumentableLanguage.JAVA }
}
- private fun DClass.isAbstract(): Boolean {
- return modifier.values.all { it is KotlinModifier.Abstract || it is JavaModifier.Abstract }
+ private fun DClass.isAbstract() =
+ modifier.values.all { it is KotlinModifier.Abstract || it is JavaModifier.Abstract }
+
+ private fun chooseStyles(page: ContentPage): Set<Style> =
+ if (page.containsOnlyDeprecatedDocumentables()) setOf(TextStyle.Strikethrough) else emptySet()
+
+ private fun ContentPage.containsOnlyDeprecatedDocumentables(): Boolean {
+ if (this !is WithDocumentables) {
+ return false
+ }
+ return this.documentables.isNotEmpty() && this.documentables.all { it.isDeprecatedForAllSourceSets() }
+ }
+
+ private fun Documentable.isDeprecatedForAllSourceSets(): Boolean {
+ val sourceSetAnnotations = this.annotations()
+ return sourceSetAnnotations.isNotEmpty() && sourceSetAnnotations.all { (_, annotations) ->
+ annotations.any { it.isDeprecated() }
+ }
}
- private fun ContentPage.navigableChildren(): List<NavigationNode> {
- return if (this is ClasslikePage) {
- return this.navigableChildren()
+ private fun ContentPage.navigableChildren() =
+ if (this is ClasslikePage) {
+ this.navigableChildren()
} else {
children
.filterIsInstance<ContentPage>()
.map { visit(it) }
.sortedBy { it.name.toLowerCase() }
}
- }
private fun ClasslikePage.navigableChildren(): List<NavigationNode> {
// Classlikes should only have other classlikes as navigable children
diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
index 87808add..fc17983d 100644
--- a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
@@ -57,17 +57,27 @@ class NavigationPage(
span("nav-link-grid") {
span("nav-link-child ${node.icon?.style()}")
span("nav-link-child") {
- buildBreakableText(node.name)
+ nodeText(node)
}
}
} else {
- buildBreakableText(node.name)
+ nodeText(node)
}
}
}
node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) }
}
}
+
+ private fun FlowContent.nodeText(node: NavigationNode) {
+ if (node.styles.contains(TextStyle.Strikethrough)) {
+ strike {
+ buildBreakableText(node.name)
+ }
+ } else {
+ buildBreakableText(node.name)
+ }
+ }
}
data class NavigationNode(
@@ -75,6 +85,7 @@ data class NavigationNode(
val dri: DRI,
val sourceSets: Set<DisplaySourceSet>,
val icon: NavigationNodeIcon?,
+ val styles: Set<Style> = emptySet(),
override val children: List<NavigationNode>
) : WithChildren<NavigationNode>
@@ -108,4 +119,4 @@ fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode) =
NavigationPage(root.transform(block), moduleName, context)
fun NavigationNode.transform(block: (NavigationNode) -> NavigationNode) =
- run(block).let { NavigationNode(it.name, it.dri, it.sourceSets, it.icon, it.children.map(block)) }
+ run(block).let { NavigationNode(it.name, it.dri, it.sourceSets, it.icon, it.styles, it.children.map(block)) }
diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
index dc5a9543..7ed7ff3f 100644
--- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
+++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
@@ -20,6 +20,9 @@ interface JvmSignatureUtils {
fun Collection<ExtraModifiers>.toSignatureString(): String =
joinToString("") { it.name.toLowerCase() + " " }
+ @Suppress("UNCHECKED_CAST")
+ fun Documentable.annotations() = (this as? WithExtraProperties<Documentable>)?.annotations() ?: emptyMap()
+
fun <T : AnnotationTarget> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> =
extra[Annotations]?.directAnnotations ?: emptyMap()
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
index 24ed0765..2692928b 100644
--- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
@@ -72,12 +72,12 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
contentBuilder.contentFor(
e,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + e.stylesIfDeprecated(it),
+ setOf(TextStyle.Monospace),
sourceSets = setOf(it)
) {
group(styles = setOf(TextStyle.Block)) {
annotationsBlock(e)
- link(e.name, e.dri, styles = emptySet())
+ link(e.name, e.dri, styles = mainStyles + e.stylesIfDeprecated(it))
}
}
}
@@ -91,12 +91,12 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
return contentBuilder.contentFor(
c,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + deprecationStyles,
+ setOf(TextStyle.Monospace),
sourceSets = setOf(sourceSet)
) {
keyword("actual ")
keyword("typealias ")
- link(c.name.orEmpty(), c.dri)
+ link(c.name.orEmpty(), c.dri, styles = mainStyles + deprecationStyles)
operator(" = ")
signatureForProjection(aliasedType)
}
@@ -142,7 +142,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
return contentBuilder.contentFor(
c,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + deprecationStyles,
+ setOf(TextStyle.Monospace),
sourceSets = setOf(sourceSet)
) {
annotationsBlock(c)
@@ -183,7 +183,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
keyword("annotation class ")
}
}
- link(c.name!!, c.dri)
+ link(c.name!!, c.dri, styles = mainStyles + deprecationStyles)
if (c is WithGenerics) {
list(c.generics, prefix = "<", suffix = ">",
separatorStyles = mainStyles + TokenStyle.Punctuation,
@@ -251,7 +251,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
contentBuilder.contentFor(
p,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + p.stylesIfDeprecated(sourceSet),
+ setOf(TextStyle.Monospace),
sourceSets = setOf(sourceSet)
) {
annotationsBlock(p)
@@ -272,7 +272,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
signatureForProjection(it.type)
punctuation(".")
}
- link(p.name, p.dri)
+ link(p.name, p.dri, styles = mainStyles + p.stylesIfDeprecated(sourceSet))
operator(": ")
signatureForProjection(p.type)
defaultValueAssign(p, sourceSet)
@@ -298,7 +298,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
contentBuilder.contentFor(
f,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + f.stylesIfDeprecated(sourceSet),
+ setOf(TextStyle.Monospace),
sourceSets = setOf(sourceSet)
) {
annotationsBlock(f)
@@ -320,7 +320,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
signatureForProjection(it.type)
punctuation(".")
}
- link(f.name, f.dri, styles = mainStyles + TokenStyle.Function)
+ link(f.name, f.dri, styles = mainStyles + TokenStyle.Function + f.stylesIfDeprecated(sourceSet))
// for a function, opening and closing parentheses must be present
// anyway, even if it has no parameters, resulting in `fun test(): R`
@@ -357,14 +357,16 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
+contentBuilder.contentFor(
t,
ContentKind.Symbol,
- setOf(TextStyle.Monospace) + t.stylesIfDeprecated(it),
+ setOf(TextStyle.Monospace),
sourceSets = platforms.toSet()
) {
annotationsBlock(t)
t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") }
processExtraModifiers(t)
keyword("typealias ")
- signatureForProjection(t.type)
+ group(styles = mainStyles + t.stylesIfDeprecated(it)) {
+ signatureForProjection(t.type)
+ }
operator(" = ")
signatureForTypealiasTarget(t, type)
}
@@ -374,10 +376,15 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
private fun signature(t: DTypeParameter) =
t.sourceSets.map {
- contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) {
- signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration()))
- list(t.nontrivialBounds, prefix = " : ",
- surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { bound ->
+ contentBuilder.contentFor(t, sourceSets = setOf(it)) {
+ group(styles = mainStyles + t.stylesIfDeprecated(it)) {
+ signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration()))
+ }
+ list(
+ elements = t.nontrivialBounds,
+ prefix = " : ",
+ surroundingCharactersStyle = mainStyles + TokenStyle.Operator
+ ) { bound ->
signatureForProjection(bound)
}
}
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
index 381fadf1..ae5275a5 100644
--- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt
@@ -1,6 +1,8 @@
package org.jetbrains.dokka.base.signatures
import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer
+import org.jetbrains.dokka.pages.ContentKind
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.DriOfAny
import org.jetbrains.dokka.links.DriOfUnit
@@ -10,12 +12,21 @@ import org.jetbrains.dokka.model.properties.WithExtraProperties
object KotlinSignatureUtils : JvmSignatureUtils {
+ private const val classExtension = "::class"
private val strategy = OnlyOnce
private val listBrackets = Pair('[', ']')
- private val classExtension = "::class"
private val ignoredAnnotations = setOf(
+ /**
+ * Rendered separately, see [SinceKotlinTransformer]
+ */
Annotations.Annotation(DRI("kotlin", "SinceKotlin"), emptyMap()),
- Annotations.Annotation(DRI("kotlin", "Deprecated"), emptyMap())
+
+ /**
+ * Rendered separately as its own block, see usage of [ContentKind.Deprecation]
+ */
+ Annotations.Annotation(DRI("kotlin", "Deprecated"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin", "DeprecatedSinceKotlin"), emptyMap()),
+ Annotations.Annotation(DRI("java.lang", "Deprecated"), emptyMap()), // could be used as well for interop
)
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt
index 2a5fbc11..079cebea 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt
@@ -5,16 +5,26 @@ import org.jetbrains.dokka.model.Documentable
import org.jetbrains.dokka.model.ExceptionInSupertypes
import org.jetbrains.dokka.model.properties.WithExtraProperties
-fun <T> T.isDeprecated() where T : WithExtraProperties<out Documentable> =
- deprecatedAnnotation != null
+val <T : WithExtraProperties<out Documentable>> T.isException: Boolean
+ get() = extra[ExceptionInSupertypes] != null
+
val <T> T.deprecatedAnnotation where T : WithExtraProperties<out Documentable>
get() = extra[Annotations]?.let { annotations ->
annotations.directAnnotations.values.flatten().firstOrNull {
- it.dri.toString() == "kotlin/Deprecated///PointingToDeclaration/" ||
- it.dri.toString() == "java.lang/Deprecated///PointingToDeclaration/"
+ it.isDeprecated()
}
}
-val <T : WithExtraProperties<out Documentable>> T.isException: Boolean
- get() = extra[ExceptionInSupertypes] != null \ No newline at end of file
+/**
+ * @return true if [T] has [kotlin.Deprecated] or [java.lang.Deprecated]
+ * annotation for **any** source set
+ */
+fun <T> T.isDeprecated() where T : WithExtraProperties<out Documentable> = deprecatedAnnotation != null
+
+/**
+ * @return true for [kotlin.Deprecated] and [java.lang.Deprecated]
+ */
+fun Annotations.Annotation.isDeprecated() =
+ (this.dri.packageName == "kotlin" && this.dri.classNames == "Deprecated") ||
+ (this.dri.packageName == "java.lang" && this.dri.classNames == "Deprecated")
diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt
index 003d68cf..87ff14a4 100644
--- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt
+++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt
@@ -1,8 +1,11 @@
package org.jetbrains.dokka.base.transformers.pages.merger
import org.jetbrains.dokka.base.renderers.sourceSets
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
import org.jetbrains.dokka.model.DisplaySourceSet
+import org.jetbrains.dokka.model.Documentable
import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.utilities.DokkaLogger
@@ -12,7 +15,12 @@ import org.jetbrains.dokka.utilities.DokkaLogger
*/
class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStrategy {
override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> {
- val members = pages.filterIsInstance<MemberPageNode>().takeIf { it.isNotEmpty() } ?: return pages
+ val members = pages
+ .filterIsInstance<MemberPageNode>()
+ .takeIf { it.isNotEmpty() }
+ ?.sortedBy { it.containsDeprecatedDocumentables() } // non-deprecated first
+ ?: return pages
+
val name = pages.first().name.also {
if (pages.any { page -> page.name != it }) { // Is this even possible?
logger.error("Page names for $it do not match!")
@@ -33,6 +41,10 @@ class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStra
return (pages - members) + listOf(merged)
}
+ @Suppress("UNCHECKED_CAST")
+ private fun MemberPageNode.containsDeprecatedDocumentables() =
+ this.documentables.any { (it as? WithExtraProperties<Documentable>)?.isDeprecated() == true }
+
private fun List<MemberPageNode>.allSourceSets(): Set<DisplaySourceSet> =
fold(emptySet()) { acc, e -> acc + e.sourceSets() }
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index f08b2056..de31e448 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -452,6 +452,8 @@ open class DefaultPageCreator(
val platforms = d.sourceSets.toSet()
return contentBuilder.contentFor(d, styles = setOf(TextStyle.Block)) {
+ deprecatedSectionContent(d, platforms)
+
val descriptions = d.descriptions
if (descriptions.any { it.value.root.children.isNotEmpty() }) {
platforms.forEach { platform ->
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt
new file mode 100644
index 00000000..73c36d8d
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt
@@ -0,0 +1,190 @@
+package org.jetbrains.dokka.base.translators.documentables
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.pages.ContentKind
+import org.jetbrains.dokka.pages.ContentStyle
+import org.jetbrains.dokka.pages.TextStyle
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder
+
+/**
+ * Main header for [Deprecated] section
+ */
+private const val DEPRECATED_HEADER_LEVEL = 3
+
+/**
+ * Header for a direct parameter of [Deprecated] annotation,
+ * such as [Deprecated.message] and [Deprecated.replaceWith]
+ */
+private const val DIRECT_PARAM_HEADER_LEVEL = 4
+
+internal fun PageContentBuilder.DocumentableContentBuilder.deprecatedSectionContent(
+ documentable: Documentable,
+ platforms: Set<DokkaConfiguration.DokkaSourceSet>
+) {
+ val allAnnotations = documentable.annotations()
+ if (allAnnotations.isEmpty()) {
+ return
+ }
+
+ platforms.forEach { platform ->
+ val platformAnnotations = allAnnotations[platform] ?: emptyList()
+ val deprecatedPlatformAnnotations = platformAnnotations.filter { it.isDeprecated() }
+
+ if (deprecatedPlatformAnnotations.isNotEmpty()) {
+ group(kind = ContentKind.Deprecation, sourceSets = setOf(platform), styles = emptySet()) {
+ val kotlinAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "kotlin" }
+ val javaAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "java.lang" }
+
+ // If both annotations are present, priority is given to Kotlin's annotation since it
+ // contains more useful information, and Java's annotation is probably there
+ // for interop with Java callers, so it should be OK to ignore it
+ if (kotlinAnnotation != null) {
+ createKotlinDeprecatedSectionContent(kotlinAnnotation, platformAnnotations)
+ } else if (javaAnnotation != null) {
+ createJavaDeprecatedSectionContent(javaAnnotation)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @see [DeprecatedSinceKotlin]
+ */
+private fun findDeprecatedSinceKotlinAnnotation(annotations: List<Annotations.Annotation>): Annotations.Annotation? {
+ return annotations.firstOrNull {
+ it.dri.packageName == "kotlin" && it.dri.classNames == "DeprecatedSinceKotlin"
+ }
+}
+
+/**
+ * Section with details for Kotlin's [kotlin.Deprecated] annotation
+ */
+private fun DocumentableContentBuilder.createKotlinDeprecatedSectionContent(
+ deprecatedAnnotation: Annotations.Annotation,
+ allAnnotations: List<Annotations.Annotation>
+) {
+ val deprecatedSinceKotlinAnnotation = findDeprecatedSinceKotlinAnnotation(allAnnotations)
+ header(
+ level = DEPRECATED_HEADER_LEVEL,
+ text = createKotlinDeprecatedHeaderText(deprecatedAnnotation, deprecatedSinceKotlinAnnotation)
+ )
+
+ deprecatedSinceKotlinAnnotation?.let {
+ createDeprecatedSinceKotlinFootnoteContent(it)
+ }
+
+ deprecatedAnnotation.takeStringParam("message")?.let {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text(it)
+ }
+ }
+
+ createReplaceWithSectionContent(deprecatedAnnotation)
+}
+
+private fun createKotlinDeprecatedHeaderText(
+ kotlinDeprecatedAnnotation: Annotations.Annotation,
+ deprecatedSinceKotlinAnnotation: Annotations.Annotation?
+): String {
+ if (deprecatedSinceKotlinAnnotation != null) {
+ // In this case there's no single level, it's dynamic based on api version,
+ // so there should be a footnote with levels and their respective versions
+ return "Deprecated"
+ }
+
+ val deprecationLevel = kotlinDeprecatedAnnotation.params["level"]?.let { (it as? EnumValue)?.enumName }
+ return when (deprecationLevel) {
+ "DeprecationLevel.ERROR" -> "Deprecated (with error)"
+ "DeprecationLevel.HIDDEN" -> "Deprecated (hidden)"
+ else -> "Deprecated"
+ }
+}
+
+/**
+ * Footnote for [DeprecatedSinceKotlin] annotation used in stdlib
+ *
+ * Notice that values are empty by default, so it's not guaranteed that all three will be set
+ */
+private fun DocumentableContentBuilder.createDeprecatedSinceKotlinFootnoteContent(
+ deprecatedSinceKotlinAnnotation: Annotations.Annotation
+) {
+ group(styles = setOf(ContentStyle.Footnote)) {
+ deprecatedSinceKotlinAnnotation.takeStringParam("warningSince")?.let {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("Warning since $it")
+ }
+ }
+ deprecatedSinceKotlinAnnotation.takeStringParam("errorSince")?.let {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("Error since $it")
+ }
+ }
+ deprecatedSinceKotlinAnnotation.takeStringParam("hiddenSince")?.let {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("Hidden since $it")
+ }
+ }
+ }
+}
+
+/**
+ * Section for [ReplaceWith] parameter of [kotlin.Deprecated] annotation
+ */
+private fun DocumentableContentBuilder.createReplaceWithSectionContent(kotlinDeprecatedAnnotation: Annotations.Annotation) {
+ val replaceWithAnnotation = (kotlinDeprecatedAnnotation.params["replaceWith"] as? AnnotationValue)?.annotation
+ ?: return
+
+ header(
+ level = DIRECT_PARAM_HEADER_LEVEL,
+ text = "Replace with"
+ )
+
+ // Signature: vararg val imports: String
+ val imports = (replaceWithAnnotation.params["imports"] as? ArrayValue)
+ ?.value
+ ?.mapNotNull { (it as? StringValue)?.value }
+ ?: emptyList()
+
+ if (imports.isNotEmpty()) {
+ codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) {
+ imports.forEach {
+ text("import $it")
+ breakLine()
+ }
+ }
+ }
+
+ replaceWithAnnotation.takeStringParam("expression")?.removeSurrounding("`")?.let {
+ codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) {
+ text(it)
+ }
+ }
+}
+
+/**
+ * Section with details for Java's [java.lang.Deprecated] annotation
+ */
+private fun DocumentableContentBuilder.createJavaDeprecatedSectionContent(
+ deprecatedAnnotation: Annotations.Annotation,
+) {
+ val isForRemoval = deprecatedAnnotation.takeBooleanParam("forRemoval", default = false)
+ header(
+ level = DEPRECATED_HEADER_LEVEL,
+ text = if (isForRemoval) "Deprecated (for removal)" else "Deprecated"
+ )
+ deprecatedAnnotation.takeStringParam("since")?.let {
+ group(styles = setOf(ContentStyle.Footnote)) {
+ text("Since version $it")
+ }
+ }
+}
+
+private fun Annotations.Annotation.takeBooleanParam(name: String, default: Boolean): Boolean =
+ (this.params[name] as? BooleanValue)?.value ?: default
+
+private fun Annotations.Annotation.takeStringParam(name: String): String? =
+ (this.params[name] as? StringValue)?.takeIf { it.value.isNotEmpty() }?.value
diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
index 02b2e0d9..000c955f 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
@@ -139,6 +139,10 @@ open class PageContentBuilder(
contents += createText(text, kind, sourceSets, styles, extra)
}
+ fun breakLine(sourceSets: Set<DokkaSourceSet> = mainSourcesetData) {
+ contents += ContentBreakLine(sourceSets.toDisplaySourceSets())
+ }
+
fun buildSignature(d: Documentable) = signatureProvider.signature(d)
fun table(
diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css
index 8e379c75..0d1085c1 100644
--- a/plugins/base/src/main/resources/dokka/styles/style.css
+++ b/plugins/base/src/main/resources/dokka/styles/style.css
@@ -743,6 +743,39 @@ small {
font-size: 11px;
}
+.deprecation-content {
+ margin: 20px 10px;
+ border:1px solid var(--border-color);
+ padding: 13px 15px 16px 15px;
+}
+
+.deprecation-content > h3 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.deprecation-content > h4 {
+ font-size: 16px;
+ margin-top: 15px;
+ margin-bottom: 0;
+}
+
+.deprecation-content code.block {
+ padding: 5px 10px;
+ display: inline-block;
+}
+
+.deprecation-content .footnote {
+ margin-left: 25px;
+ font-size: 13px;
+ font-weight: bold;
+ display: block;
+}
+
+.deprecation-content .footnote > p {
+ margin: 0;
+}
+
.platform-tag {
display: flex;
flex-direction: row;
diff --git a/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt b/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
new file mode 100644
index 00000000..961ce5f5
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
@@ -0,0 +1,139 @@
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.ContentStyle
+import org.junit.jupiter.api.Test
+import utils.pWrapped
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class JavaDeprecatedTest : BaseAbstractTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ @Suppress("UNCHECKED_CAST")
+ fun `should assert util functions for deprecation`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |@Deprecated(forRemoval = true)
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ documentablesTransformationStage = { module ->
+ val deprecatedClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" }
+
+ val isDeprecated = (deprecatedClass as WithExtraProperties<out Documentable>).isDeprecated()
+ assertTrue(isDeprecated)
+
+ val deprecatedAnnotation = (deprecatedClass as WithExtraProperties<out Documentable>).deprecatedAnnotation
+ checkNotNull(deprecatedAnnotation)
+
+ assertTrue(deprecatedAnnotation.isDeprecated())
+ assertEquals("java.lang", deprecatedAnnotation.dri.packageName)
+ assertEquals("Deprecated", deprecatedAnnotation.dri.classNames)
+ }
+ }
+ }
+
+ @Test
+ fun `should change deprecated header if marked for removal`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(forRemoval = true)
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val deprecatedJavaClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" } as ContentPage
+
+ deprecatedJavaClass.content.assertNode {
+ group {
+ header(1) { +"DeprecatedJavaClass" }
+ platformHinted {
+ skipAllNotMatching()
+ group {
+ header(3) {
+ +"Deprecated (for removal)"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should add footnote for 'since' param`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(since = "11")
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val deprecatedJavaClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" } as ContentPage
+
+ deprecatedJavaClass.content.assertNode {
+ group {
+ header(1) { +"DeprecatedJavaClass" }
+ platformHinted {
+ skipAllNotMatching()
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ group {
+ check { assertEquals(ContentStyle.Footnote, this.style.firstOrNull()) }
+ +"Since version 11"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt b/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt
new file mode 100644
index 00000000..8b311893
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt
@@ -0,0 +1,395 @@
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation
+import org.jetbrains.dokka.pages.ContentStyle
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.pWrapped
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+
+class KotlinDeprecatedTest : BaseAbstractTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ @Suppress("UNCHECKED_CAST")
+ fun `should assert util functions for deprecation`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/KotlinFile.kt
+ |package kotlin
+ |
+ |@Deprecated(
+ | message = "Fancy message"
+ |)
+ |fun simpleFunction() {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ documentablesTransformationStage = { module ->
+ val deprecatedFunction = module.children
+ .single { it.name == "kotlin" }.children
+ .single { it.name == "simpleFunction" }
+
+ val isDeprecated = (deprecatedFunction as WithExtraProperties<out Documentable>).isDeprecated()
+ assertTrue(isDeprecated)
+
+ val deprecatedAnnotation = (deprecatedFunction as WithExtraProperties<out Documentable>).deprecatedAnnotation
+ checkNotNull(deprecatedAnnotation)
+
+ assertTrue(deprecatedAnnotation.isDeprecated())
+ assertEquals("kotlin", deprecatedAnnotation.dri.packageName)
+ assertEquals("Deprecated", deprecatedAnnotation.dri.classNames)
+ }
+ }
+ }
+
+ @Test
+ fun `should change header if deprecation level is not default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package kotlin
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Reason for deprecation bla bla",
+ | level = DeprecationLevel.ERROR
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "kotlin" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated (with error)"
+ }
+ p {
+ +"Reason for deprecation bla bla"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should display repalceWith param with imports as code blocks`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package kotlin
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Reason for deprecation bla bla",
+ | replaceWith = ReplaceWith(
+ | "newShinyFunction(typedParam, someLiteral, SomeNewType())",
+ | imports = [
+ | "com.example.dokka.debug.newShinyFunction",
+ | "com.example.dokka.debug.SomeOldType",
+ | "com.example.dokka.debug.SomeNewType",
+ | ]
+ | ),
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "kotlin" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ p {
+ +"Reason for deprecation bla bla"
+ }
+
+ header(4) {
+ +"Replace with"
+ }
+ codeBlock {
+ +"import com.example.dokka.debug.newShinyFunction"
+ br()
+ +"import com.example.dokka.debug.SomeOldType"
+ br()
+ +"import com.example.dokka.debug.SomeNewType"
+ br()
+ }
+ codeBlock {
+ +"newShinyFunction(typedParam, someLiteral, SomeNewType())"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should add footnote for DeprecatedSinceKotlin annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package kotlin
+ |
+ |/**
+ | * Average function description
+ | */
+ |@DeprecatedSinceKotlin(
+ | warningSince = "1.4",
+ | errorSince = "1.5",
+ | hiddenSince = "1.6"
+ |)
+ |@Deprecated(
+ | message = "Deprecation reason bla bla"
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "kotlin" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ group {
+ check { assertEquals(ContentStyle.Footnote, this.style.firstOrNull()) }
+ p {
+ +"Warning since 1.4"
+ }
+ p {
+ +"Error since 1.5"
+ }
+ p {
+ +"Hidden since 1.6"
+ }
+ }
+ p {
+ +"Deprecation reason bla bla"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should generate deprecation block with all parameters present and long description`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package kotlin
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vel vulputate risus. " +
+ | "Etiam dictum odio vel vulputate auctor.Nulla facilisi. Duis ullamcorper ullamcorper lectus " +
+ | "nec rutrum. Quisque eu risus eu purus bibendum ultricies. Maecenas tincidunt dui in sodales " +
+ | "faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin id sem felis. " +
+ | "Praesent et libero lacinia, egestas libero in, ultrices lectus. Suspendisse eget volutpat " +
+ | "velit. Phasellus laoreet mi eu egestas mattis.",
+ | replaceWith = ReplaceWith(
+ | "newShinyFunction(typedParam, someLiteral, SomeNewType())",
+ | imports = [
+ | "com.example.dokka.debug.newShinyFunction",
+ | "com.example.dokka.debug.SomeOldType",
+ | "com.example.dokka.debug.SomeNewType",
+ | ]
+ | ),
+ | level = DeprecationLevel.ERROR
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "kotlin" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated (with error)"
+ }
+ p {
+ +("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+ "Maecenas vel vulputate risus. Etiam dictum odio vel " +
+ "vulputate auctor.Nulla facilisi. Duis ullamcorper " +
+ "ullamcorper lectus nec rutrum. Quisque eu risus eu " +
+ "purus bibendum ultricies. Maecenas tincidunt dui in sodales faucibus. " +
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+ "Proin id sem felis. Praesent et libero lacinia, egestas " +
+ "libero in, ultrices lectus. Suspendisse eget volutpat velit. " +
+ "Phasellus laoreet mi eu egestas mattis.")
+ }
+ header(4) {
+ +"Replace with"
+ }
+ codeBlock {
+ +"import com.example.dokka.debug.newShinyFunction"
+ br()
+ +"import com.example.dokka.debug.SomeOldType"
+ br()
+ +"import com.example.dokka.debug.SomeNewType"
+ br()
+ }
+ codeBlock {
+ +"newShinyFunction(typedParam, someLiteral, SomeNewType())"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt b/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt
index e0aa1d04..84f5b647 100644
--- a/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt
+++ b/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt
@@ -1,15 +1,13 @@
package content.annotations
-
import matchers.content.*
-import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.pages.ContentPage
import org.junit.jupiter.api.Test
import utils.ParamAttributes
import utils.bareSignature
-
-class DepredatedAndSinceKotlinTest : BaseAbstractTest() {
+class SinceKotlinTest : BaseAbstractTest() {
private val testConfiguration = dokkaConfiguration {
sourceSets {
@@ -21,46 +19,6 @@ class DepredatedAndSinceKotlinTest : BaseAbstractTest() {
}
@Test
- fun `function with deprecated annotation`() {
- testInline(
- """
- |/src/main/kotlin/test/source.kt
- |package test
- |
- |@Deprecated("And some things that should not have been forgotten were lost. History became legend. Legend became myth.")
- |fun ring(abc: String): String {
- | return "My precious " + abc
- |}
- """.trimIndent(), testConfiguration
- ) {
- pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "ring" } as ContentPage
- page.content.assertNode {
- group {
- header(1) { +"ring" }
- }
- divergentGroup {
- divergentInstance {
- divergent {
- bareSignature(
- emptyMap(),
- "",
- "",
- emptySet(),
- "ring",
- "String",
- "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
- )
- }
- }
- }
- }
- }
- }
- }
-
- @Test
fun `function with since kotlin annotation`() {
testInline(
"""
diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt
index 104246cb..13a9e711 100644
--- a/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt
+++ b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt
@@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test
import utils.TestOutputWriterPlugin
import kotlin.test.assertEquals
import utils.navigationHtml
+import kotlin.test.assertNull
class NavigationTest : BaseAbstractTest() {
@@ -19,6 +20,106 @@ class NavigationTest : BaseAbstractTest() {
}
@Test
+ fun `should strike deprecated class link`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/SimpleDeprecatedClass.kt
+ |package com.example
+ |
+ |@Deprecated("reason")
+ |class SimpleDeprecatedClass {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(3, content.size)
+
+ // Navigation menu should be the following:
+ // - root
+ // - com.example
+ // - SimpleDeprecatedClass
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "SimpleDeprecatedClass",
+ address = "root/com.example/-simple-deprecated-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT,
+ isStrikethrough = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should not strike pages where only one of N documentables is deprecated`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/File.kt
+ |package com.example
+ |
+ |/**
+ | * First
+ | */
+ |@Deprecated("reason")
+ |fun functionWithCommonName()
+ |
+ |/**
+ | * Second
+ | */
+ |fun functionWithCommonName()
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(3, content.size)
+
+ // Navigation menu should be the following:
+ // - root
+ // - com.example
+ // - functionWithCommonName
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "functionWithCommonName()",
+ address = "root/com.example/function-with-common-name.html",
+ icon = NavigationNodeIcon.FUNCTION,
+ isStrikethrough = false
+ )
+ }
+ }
+ }
+
+ @Test
fun `should have expandable classlikes`() {
val writerPlugin = TestOutputWriterPlugin()
testInline(
@@ -209,7 +310,7 @@ class NavigationTest : BaseAbstractTest() {
}
private fun Element.assertNavigationLink(
- id: String, text: String, address: String, icon: NavigationNodeIcon? = null
+ id: String, text: String, address: String, icon: NavigationNodeIcon? = null, isStrikethrough: Boolean = false
) {
assertEquals(id, this.id())
@@ -224,5 +325,11 @@ class NavigationTest : BaseAbstractTest() {
assertEquals("nav-link-child", iconStyles[0])
assertEquals(icon.style(), "${iconStyles[1]} ${iconStyles[2]}")
}
+ if (isStrikethrough) {
+ val textInsideStrikethrough = link.selectFirst("strike")?.text()
+ assertEquals(text, textInsideStrikethrough)
+ } else {
+ assertNull(link.selectFirst("strike"))
+ }
}
}
diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt
index 0af253df..08ddf034 100644
--- a/plugins/base/src/test/kotlin/utils/contentUtils.kt
+++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt
@@ -208,7 +208,9 @@ fun ContentMatcherBuilder<*>.typealiasSignature(name: String, expressionTarget:
group {
+"typealias "
group {
- link { +name }
+ group {
+ link { +name }
+ }
skipAllNotMatching()
}
+" = "