aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/kotlin')
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt31
-rw-r--r--core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt4
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt4
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt1
-rw-r--r--core/src/main/kotlin/Generation/configurationImpl.kt6
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt45
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt67
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt20
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt26
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt5
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt20
-rw-r--r--core/src/main/kotlin/Model/Content.kt9
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt23
-rw-r--r--core/src/main/kotlin/Model/SourceLinks.kt38
-rw-r--r--core/src/main/kotlin/javadoc/docbase.kt49
-rw-r--r--core/src/main/kotlin/javadoc/tags.kt2
16 files changed, 225 insertions, 125 deletions
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
index f26829b0..603fd0e4 100644
--- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -269,29 +269,32 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl
return ResolverForProjectImpl(
debugName = "Dokka",
projectContext = projectContext,
- modules = listOf(module, library),
- modulesContent = modulesContent,
+ modules = listOf(library, module),
+ modulesContent = {
+ when (it) {
+ library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
+ module -> ModuleContent(it, emptyList(), sourcesScope)
+ else -> throw IllegalArgumentException("Unexpected module info")
+ }
+ },
modulePlatforms = { JvmPlatform.multiTargetPlatform },
moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
resolverForModuleFactoryByPlatform = { JvmAnalyzerFacade },
- platformParameters = { targetPlaftorm ->
- JvmPlatformParameters(
- { content ->
- JvmPackagePartProvider(
- configuration.languageVersionSettings,
- content.moduleContentScope
- ).apply {
- this.addRoots(javaRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
+ platformParameters = {
+ JvmPlatformParameters ({ content ->
+ JvmPackagePartProvider(
+ configuration.languageVersionSettings,
+ content.moduleContentScope)
+ .apply {
+ addRoots(javaRoots, messageCollector)
}
- }
- ) { it ->
+ }, {
val file = (it as JavaClassImpl).psi.containingFile.virtualFile
if (file in sourcesScope)
module
else
library
-
- }
+ })
},
targetEnvironment = CompilerEnvironment,
builtIns = builtIns
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
index ffd95da2..319d85b1 100644
--- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
+++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
@@ -20,10 +20,10 @@ import com.intellij.openapi.vfs.VirtualFileFilter
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.messages.MessageBus
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
-import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
-import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
import org.jetbrains.kotlin.cli.common.config.ContentRoot
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
+import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
+import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
import org.picocontainer.PicoContainer
import java.io.File
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 46c7f1af..04810498 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -205,6 +205,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ is NodeRenderContent -> {
+ val node = content.node
+ appendContent(languageService.render(node, content.mode))
+ }
is ContentNodeLink -> {
val node = content.node
val linkTo = if (node != null) locationHref(location, node, generator) else "#"
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 9173dfbb..90d7cfcc 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 61f3f902..46174198 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -11,9 +11,9 @@ data class SourceLinkDefinitionImpl(override val path: String,
companion object {
fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
val (path, urlAndLine) = srcLink.split('=')
- return SourceLinkDefinitionImpl(File(path).absolutePath,
+ return SourceLinkDefinitionImpl(File(path).canonicalPath,
urlAndLine.substringBefore("#"),
- urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
+ urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" })
}
}
}
@@ -69,7 +69,7 @@ class PassConfigurationImpl (
private val defaultLinks = run {
val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
if (!noJdkLink)
- links += DokkaConfiguration.ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build()
+ links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://docs.oracle.com/javase/$jdkVersion/docs/api/").build()
if (!noStdlibLink)
links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build()
diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
index 1fe4d180..98d56856 100644
--- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -6,6 +6,7 @@ import com.intellij.psi.*
import com.intellij.psi.impl.JavaConstantExpressionEvaluator
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
@@ -19,11 +20,19 @@ fun getSignature(element: PsiElement?) = when(element) {
is PsiClass -> element.qualifiedName
is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name
is PsiMethod ->
- element.containingClass!!.qualifiedName + "$" + element.name + "(" +
- element.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")"
+ methodSignature(element)
+ is PsiParameter -> {
+ val method = (element.parent.parent as PsiMethod)
+ methodSignature(method)
+ }
else -> null
}
+private fun methodSignature(method: PsiMethod): String {
+ return method.containingClass!!.qualifiedName + "$" + method.name + "(" +
+ method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")"
+}
+
private fun PsiType.typeSignature(): String = when(this) {
is PsiArrayType -> "Array((${componentType.typeSignature()}))"
is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize()
@@ -100,9 +109,11 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun nodeForElement(element: PsiNamedElement,
kind: NodeKind,
- name: String = element.name ?: "<anonymous>"): DocumentationNode {
+ name: String = element.name ?: "<anonymous>",
+ register: Boolean = false): DocumentationNode {
val (docComment, deprecatedContent) = docParser.parseDocumentation(element)
val node = DocumentationNode(name, docComment, kind)
+ if (register) register(element, node)
if (element is PsiModifierListOwner) {
node.appendModifiers(element)
val modifierList = element.modifierList
@@ -171,13 +182,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun PsiClass.build(): DocumentationNode {
val kind = when {
+ isAnnotationType -> NodeKind.AnnotationClass
isInterface -> NodeKind.Interface
isEnum -> NodeKind.Enum
- isAnnotationType -> NodeKind.AnnotationClass
isException() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForElement(this, kind)
+ val node = nodeForElement(this, kind, register = isAnnotationType)
superTypes.filter { !ignoreSupertype(it) }.forEach {
node.appendType(it, NodeKind.Supertype)
val superClass = it.resolve()
@@ -219,11 +230,11 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) {
val modifierList = field.modifierList ?: return
val initializer = field.initializer ?: return
- if (field.type is PsiPrimitiveType &&
- modifierList.hasExplicitModifier(PsiModifier.FINAL) &&
+ if (modifierList.hasExplicitModifier(PsiModifier.FINAL) &&
modifierList.hasExplicitModifier(PsiModifier.STATIC)) {
val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false)
val text = when(value) {
+ null -> return // No value found
is String ->
"\"" + StringUtil.escapeStringCharacters(value) + "\""
else -> value.toString()
@@ -301,8 +312,26 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
return node
}
+ private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode {
+ val existing = refGraph.lookup(getSignature(psiClass)!!)
+ if (existing != null) return existing
+ val new = psiClass.build()
+ val packageNode = findOrCreatePackageNode(null, (psiClass.parent as PsiJavaFile).packageName, emptyMap(), refGraph)
+ packageNode.append(new, RefKind.Member)
+ return new
+ }
+
fun PsiAnnotation.build(): DocumentationNode {
- val node = DocumentationNode(nameReferenceElement?.text ?: "<?>", Content.Empty, NodeKind.Annotation)
+
+ val original = when (this) {
+ is KtLightAbstractAnnotation -> clsDelegate
+ else -> this
+ }
+ val node = DocumentationNode(qualifiedName?.substringAfterLast(".") ?: "<?>", Content.Empty, NodeKind.Annotation)
+ val psiClass = original.nameReferenceElement?.resolve() as? PsiClass
+ if (psiClass != null && psiClass.isAnnotationType) {
+ node.append(lookupOrBuildClass(psiClass), RefKind.Link)
+ }
parameterList.attributes.forEach {
val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter)
val value = it.value
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
index 9f9ea017..70af73f9 100644
--- a/core/src/main/kotlin/Java/JavadocParser.kt
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -1,11 +1,9 @@
package org.jetbrains.dokka
import com.intellij.psi.*
-import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl
import com.intellij.psi.impl.source.tree.JavaDocElementType
import com.intellij.psi.javadoc.*
import com.intellij.psi.util.PsiTreeUtil
-import com.intellij.util.IncorrectOperationException
import com.intellij.util.containers.isNullOrEmpty
import org.jetbrains.kotlin.utils.keysToMap
import org.jsoup.Jsoup
@@ -33,30 +31,29 @@ class JavadocParser(
private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) {
append(LazyContentBlock {
- val node = refGraph.lookupOrWarn(signature, logger)?.let(selector)
- if (node != null) {
- it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
- it.symbol(":")
- it.text(" ")
- }
+ val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList()
+ listOf(ContentBlock().apply {
+ append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
+ symbol(":")
+ text(" ")
+ })
})
}
override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
- val docComment = (element as? PsiDocCommentOwner)?.docComment
- if (docComment == null) return JavadocParseResult.Empty
+ val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return JavadocParseResult.Empty
val result = MutableContent()
var deprecatedContent: Content? = null
+
+ val nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
+ val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph }
val firstParagraph = ContentParagraph()
- firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
- val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph }
- firstParagraph.children.removeAll(paragraphs)
- if (!firstParagraph.isEmpty()) {
+ if (firstParagraphContents.isNotEmpty()) {
+ firstParagraphContents.forEach { firstParagraph.append(it) }
result.append(firstParagraph)
}
- paragraphs.forEach {
- result.append(it)
- }
+
+ result.appendAll(nodes.drop(firstParagraphContents.size))
if (element is PsiMethod) {
val tagsByName = element.searchInheritedTags()
@@ -67,14 +64,16 @@ class JavadocParser(
when (tagName) {
"param" -> {
section.appendTypeElement(signature) {
- it.details.find { it.kind == NodeKind.Parameter }?.detailOrNull(NodeKind.Type)
+ it.details
+ .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() }
+ ?.detailOrNull(NodeKind.Type)
}
}
"return" -> {
section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
}
}
- section.convertJavadocElements(tag.contentElements(), context)
+ section.appendAll(convertJavadocElements(tag.contentElements(), context))
}
}
}
@@ -84,7 +83,7 @@ class JavadocParser(
"see" -> result.convertSeeTag(tag)
"deprecated" -> {
deprecatedContent = Content().apply {
- convertJavadocElements(tag.contentElements(), element)
+ appendAll(convertJavadocElements(tag.contentElements(), element))
}
}
in tagsToInherit -> {}
@@ -92,7 +91,7 @@ class JavadocParser(
val subjectName = tag.getSubjectName()
val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
- section.convertJavadocElements(tag.contentElements(), element)
+ section.appendAll(convertJavadocElements(tag.contentElements(), element))
}
}
}
@@ -133,13 +132,17 @@ class JavadocParser(
return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements
}
- private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement) {
+ private fun convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<ContentNode> {
val doc = Jsoup.parse(expandAllForElements(elements, element))
- doc.body().childNodes().forEach {
- convertHtmlNode(it)?.let { append(it) }
+ return doc.body().childNodes().mapNotNull {
+ convertHtmlNode(it)
}
}
+ private fun ContentBlock.appendAll(nodes: List<ContentNode>) {
+ nodes.forEach { append(it) }
+ }
+
private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String {
val htmlBuilder = StringBuilder()
elements.forEach {
@@ -152,28 +155,30 @@ class JavadocParser(
return htmlBuilder.toString().trim()
}
- private fun convertHtmlNode(node: Node): ContentNode? {
+ private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? {
if (node is TextNode) {
- return ContentText(node.text())
+ val text = if (insidePre) node.wholeText else node.text()
+ return ContentText(text)
} else if (node is Element) {
- val childBlock = createBlock(node)
+ val childBlock = createBlock(node, insidePre)
+
node.childNodes().forEach {
- val child = convertHtmlNode(it)
+ val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode)
if (child != null) {
childBlock.append(child)
}
}
- return (childBlock)
+ return childBlock
}
return null
}
- private fun createBlock(element: Element): ContentBlock = when (element.tagName()) {
+ private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) {
"p" -> ContentParagraph()
"b", "strong" -> ContentStrong()
"i", "em" -> ContentEmphasis()
"s", "del" -> ContentStrikethrough()
- "code" -> ContentCode()
+ "code" -> if (insidePre) ContentBlock() else ContentCode()
"pre" -> ContentBlockCode()
"ul" -> ContentUnorderedList()
"ol" -> ContentOrderedList()
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
index d0650d45..ddd8a32a 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -19,6 +19,8 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -38,10 +40,10 @@ class DescriptorDocumentationParser
val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
)
{
- fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content =
- parseDocumentationAndDetails(descriptor, inline).first
+ fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content =
+ parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first
- fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
+ fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) {
return parseJavadoc(descriptor)
}
@@ -62,7 +64,10 @@ class DescriptorDocumentationParser
?.resolveToDescriptorIfAny()
?: descriptor
- var kdocText = kdoc.getContent()
+ var kdocText = if (isDefaultNoArgConstructor) {
+ getConstructorTagContent(descriptor) ?: kdoc.getContent()
+ } else kdoc.getContent()
+
// workaround for code fence parsing problem in IJ markdown parser
if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
kdocText += "\n"
@@ -90,6 +95,13 @@ class DescriptorDocumentationParser
return content to { node -> }
}
+ private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? {
+ return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag(
+ KDocKnownTag.CONSTRUCTOR
+ )?.getContent()
+ }
+
+
private fun DeclarationDescriptor.isSuppressWarning() : Boolean {
val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
return if (suppressAnnotation != null) {
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 205b44bd..e3f7c35b 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -2,7 +2,6 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
-import com.intellij.psi.PsiField
import com.intellij.psi.PsiJavaFile
import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
@@ -24,7 +23,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtParameter
-import org.jetbrains.kotlin.psi.KtVariableDeclaration
+import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.*
@@ -298,7 +297,11 @@ class DocumentationBuilder
fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
appendInline(descriptor, psi)
- KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ KtTokens.MODIFIER_KEYWORDS_ARRAY.filter {
+ it !in knownModifiers
+ }.sortedBy {
+ MODIFIERS_ORDER.indexOf(it)
+ }.forEach {
if (psi.hasModifier(it)) {
appendTextNode(it.value, NodeKind.Modifier)
}
@@ -772,13 +775,7 @@ class DocumentationBuilder
}
if (isConst) {
- val psi = sourcePsi()
- val valueText = when (psi) {
- is KtVariableDeclaration -> psi.initializer?.text
- is PsiField -> psi.initializer?.text
- else -> null
- }
- valueText?.let { node.appendTextNode(it, NodeKind.Value) }
+ this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) }
}
@@ -903,8 +900,8 @@ class DocumentationBuilder
return node
}
- fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value?.let { value ->
- when (value) {
+ fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value ->
+ val text = when (value) {
is String ->
"\"" + StringUtil.escapeStringCharacters(value) + "\""
is EnumEntrySyntheticClassDescriptor ->
@@ -917,10 +914,9 @@ class DocumentationBuilder
value.toString()
}
}
- else -> value.toString()
- }.let { valueString ->
- DocumentationNode(valueString, Content.Empty, NodeKind.Value)
+ else -> "$value"
}
+ DocumentationNode(text, Content.Empty, NodeKind.Value)
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
index be6dd2e1..6e58f766 100644
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -6,9 +6,11 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiNamedElement
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPropertyAccessor
@@ -59,8 +61,9 @@ class KotlinAsJavaDocumentationParser
return JavadocParseResult.Empty
}
}
+ val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass
val descriptor = resolutionFacade.resolveToDescriptor(origin)
- val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter)
+ val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor)
return JavadocParseResult(content, null)
}
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index ba67d93d..daa09cbf 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -11,7 +11,7 @@ class KotlinLanguageService : CommonLanguageService() {
}
private val fullOnlyModifiers =
- setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
+ setOf("public", "protected", "private", "internal", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -166,8 +166,18 @@ class KotlinLanguageService : CommonLanguageService() {
keyword("dynamic")
return
}
+
+ val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier)
+
if (node.isFunctionalType()) {
- renderFunctionalType(node, renderMode)
+ if (nullabilityModifier != null) {
+ symbol("(")
+ renderFunctionalType(node, renderMode)
+ symbol(")")
+ symbol(nullabilityModifier.name)
+ } else {
+ renderFunctionalType(node, renderMode)
+ }
return
}
if (renderMode == RenderMode.FULL) {
@@ -185,8 +195,8 @@ class KotlinLanguageService : CommonLanguageService() {
}
symbol(">")
}
- val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull()
- if (nullabilityModifier != null) {
+
+ nullabilityModifier ?.apply {
symbol(nullabilityModifier.name)
}
}
@@ -201,7 +211,7 @@ class KotlinLanguageService : CommonLanguageService() {
"final", "public", "var", "expect", "actual", "external" -> {
}
else -> {
- if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
+ if (showModifierInSummary(node) || renderMode == RenderMode.FULL) {
super.renderModifier(block, node, renderMode, nowrap)
}
}
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
index 77451d41..8312b2e2 100644
--- a/core/src/main/kotlin/Model/Content.kt
+++ b/core/src/main/kotlin/Model/Content.kt
@@ -35,13 +35,13 @@ class NodeRenderContent(
get() = 0 //TODO: Clarify?
}
-class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() {
+class LazyContentBlock(private val fillChildren: () -> List<ContentNode>) : ContentBlock() {
private var computed = false
override val children: ArrayList<ContentNode>
get() {
if (!computed) {
computed = true
- fillChildren(this)
+ children.addAll(fillChildren())
}
return super.children
}
@@ -115,6 +115,7 @@ class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: Cont
abstract class ContentNodeLink() : ContentBlock() {
abstract val node: DocumentationNode?
+ abstract val text: String?
}
object ContentHardLineBreak : ContentNode {
@@ -128,6 +129,8 @@ class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLi
override fun hashCode(): Int =
children.hashCode() * 31 + node.name.hashCode()
+
+ override val text: String? = null
}
class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() {
@@ -138,6 +141,8 @@ class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> Documentatio
override fun hashCode(): Int =
children.hashCode() * 31 + linkText.hashCode()
+
+ override val text: String? = linkText
}
class ContentExternalLink(val href : String) : ContentBlock() {
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index 617816b3..1ff14ff1 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -203,17 +203,20 @@ val DocumentationNode.path: List<DocumentationNode>
}
fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
- val existingNode = refGraph.lookup(packageName)
- if (existingNode != null) {
- return existingNode
- }
- val newNode = DocumentationNode(packageName,
+ val node = refGraph.lookup(packageName) ?: run {
+ val newNode = DocumentationNode(
+ packageName,
packageContent.getOrElse(packageName) { Content.Empty },
- NodeKind.Package)
+ NodeKind.Package
+ )
- refGraph.register(packageName, newNode)
- module?.append(newNode, RefKind.Member)
- return newNode
+ refGraph.register(packageName, newNode)
+ newNode
+ }
+ if (module != null && node !in module.members) {
+ module.append(node, RefKind.Member)
+ }
+ return node
}
fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
@@ -240,7 +243,7 @@ fun DocumentationNode.qualifiedName(): String {
} else if (kind == NodeKind.Package) {
return name
}
- return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
+ return path.dropWhile { it.kind == NodeKind.Module }.map { it.name }.filter { it.isNotEmpty() }.joinToString(".")
}
fun DocumentationNode.simpleName() = name.substringAfterLast('.')
diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt
index 2c75cfda..99ee362e 100644
--- a/core/src/main/kotlin/Model/SourceLinks.kt
+++ b/core/src/main/kotlin/Model/SourceLinks.kt
@@ -10,20 +10,20 @@ import java.io.File
fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) {
val path = psi?.containingFile?.virtualFile?.path ?: return
+ val canonicalPath = File(path).canonicalPath
val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi
- val absPath = File(path).absolutePath
- val linkDef = sourceLinks.firstOrNull { absPath.startsWith(it.path) }
- if (linkDef != null) {
- var url = linkDef.url + path.substring(linkDef.path.length)
- if (linkDef.lineSuffix != null) {
+ val pair = determineSourceLinkDefinition(canonicalPath, sourceLinks)
+ if (pair != null) {
+ val (sourceLinkDefinition, sourceLinkCanonicalPath) = pair
+ var url = determineUrl(canonicalPath, sourceLinkDefinition, sourceLinkCanonicalPath)
+ if (sourceLinkDefinition.lineSuffix != null) {
val line = target?.lineNumber()
if (line != null) {
- url += linkDef.lineSuffix + line.toString()
+ url += sourceLinkDefinition.lineSuffix + line.toString()
}
}
- append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl),
- RefKind.Detail);
+ append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), RefKind.Detail)
}
if (target != null) {
@@ -31,6 +31,28 @@ fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<Sourc
}
}
+private fun determineSourceLinkDefinition(
+ canonicalPath: String,
+ sourceLinks: List<SourceLinkDefinition>
+): Pair<SourceLinkDefinition, String>? {
+ return sourceLinks
+ .asSequence()
+ .map { it to File(it.path).canonicalPath }
+ .firstOrNull { (_, sourceLinkCanonicalPath) ->
+ canonicalPath.startsWith(sourceLinkCanonicalPath)
+ }
+}
+
+private fun determineUrl(
+ canonicalPath: String,
+ sourceLinkDefinition: SourceLinkDefinition,
+ sourceLinkCanonicalPath: String
+): String {
+ val relativePath = canonicalPath.substring(sourceLinkCanonicalPath.length)
+ val relativeUrl = relativePath.replace('\\', '/').removePrefix("/")
+ return "${sourceLinkDefinition.url.removeSuffix("/")}/$relativeUrl"
+}
+
private fun PsiElement.sourcePosition(): String {
val path = containingFile.virtualFile.path
val lineNumber = lineNumber()
diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt
index 2a14c6ff..118b134a 100644
--- a/core/src/main/kotlin/javadoc/docbase.kt
+++ b/core/src/main/kotlin/javadoc/docbase.kt
@@ -2,7 +2,7 @@ package org.jetbrains.dokka.javadoc
import com.sun.javadoc.*
import org.jetbrains.dokka.*
-import java.lang.reflect.Modifier
+import java.lang.reflect.Modifier.*
import java.util.*
import kotlin.reflect.KClass
@@ -75,9 +75,7 @@ open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node
node.deprecation?.let {
val content = it.content.asText()
- if (content != null) {
- result.add(TagImpl(this, "deprecated", content))
- }
+ result.add(TagImpl(this, "deprecated", content ?: ""))
}
return result.toTypedArray()
@@ -116,20 +114,27 @@ class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc {
- override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node) // TODO ?????
+ override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node.links.find { it.kind == NodeKind.AnnotationClass } ?: node) // TODO ?????
override fun isSynthesized(): Boolean = false
override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO
}
open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc {
- override fun isPublic(): Boolean = true
+ override fun isPublic(): Boolean = node.hasModifier("public") || node.hasModifier("internal")
override fun isPackagePrivate(): Boolean = false
override fun isStatic(): Boolean = node.hasModifier("static")
- override fun modifierSpecifier(): Int = Modifier.PUBLIC + if (isStatic) Modifier.STATIC else 0
+ override fun modifierSpecifier(): Int = visibilityModifier or (if (isStatic) STATIC else 0)
+ private val visibilityModifier
+ get() = when {
+ isPublic() -> PUBLIC
+ isPrivate() -> PRIVATE
+ isProtected() -> PROTECTED
+ else -> 0
+ }
override fun qualifiedName(): String? = node.qualifiedName()
override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray()
override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim()
- override fun isProtected(): Boolean = false
+ override fun isProtected(): Boolean = node.hasModifier("protected")
override fun isFinal(): Boolean = node.hasModifier("final")
@@ -165,7 +170,7 @@ open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationN
return null
}
- override fun isPrivate(): Boolean = false
+ override fun isPrivate(): Boolean = node.hasModifier("private")
override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true
}
@@ -173,7 +178,7 @@ open class TypeAdapter(override val module: ModuleNodeAdapter, override val node
private val javaLanguageService = JavaLanguageService()
override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType()
- override fun typeName(): String = javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()
+ override fun typeName(): String = (javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()) + dimension()
override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName()
override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("")
@@ -275,7 +280,7 @@ class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter {
- override fun typeName(): String? = JavaLanguageService().renderType(node.detail(NodeKind.Type))
+ override fun typeName(): String? = type()?.typeName()
override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type))
override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
}
@@ -317,7 +322,7 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio
.map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) }
.toTypedArray()
- override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO
+ override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).last().hasModifier("vararg")
override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" }
@@ -406,6 +411,9 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod
return classNode.simpleName()
}
+ override fun qualifiedName(): String? {
+ return super.qualifiedName()
+ }
override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray()
override fun constructors(): Array<out ConstructorDoc> = constructors(true)
override fun importedPackages(): Array<out PackageDoc> = emptyArray()
@@ -514,12 +522,13 @@ class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorR
}
private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) =
- (node.details(kind)
- .filter(DocumentationNode::hasNonEmptyContent)
- .map { ParamTagAdapter(module, this, it.name, true, it.content.children) }
+ (node.details(kind)
+ .filter(DocumentationNode::hasNonEmptyContent)
+ .map { ParamTagAdapter(module, this, it.name, true, it.content.children) }
- + node.content.sections
- .filter(sectionFilter)
- .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) })
-
- .toTypedArray() \ No newline at end of file
+ + node.content.sections
+ .filter(sectionFilter)
+ .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) }
+ )
+ .distinctBy { it.parameterName }
+ .toTypedArray() \ No newline at end of file
diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt
index 05d98b71..99c9bfff 100644
--- a/core/src/main/kotlin/javadoc/tags.kt
+++ b/core/src/main/kotlin/javadoc/tags.kt
@@ -71,7 +71,7 @@ class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: Conte
override fun referencedPackage(): PackageDoc? = null
override fun referencedClass(): ClassDoc? = method.containingClass()
override fun referencedClassName(): String = method.containingClass()?.name() ?: ""
- override fun label(): String = "${method.containingClass()?.name()}.${method.name()}"
+ override fun label(): String = content.text ?: "${method.containingClass()?.name()}.${method.name()}"
override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO