aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/Model
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/kotlin/Model')
-rw-r--r--core/src/main/kotlin/Model/Content.kt33
-rw-r--r--core/src/main/kotlin/Model/DescriptorSignatureProvider.kt7
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt67
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt5
-rw-r--r--core/src/main/kotlin/Model/ElementSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Model/PackageDocs.kt69
6 files changed, 173 insertions, 17 deletions
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
index 1f5bbc83..7c776bdb 100644
--- a/core/src/main/kotlin/Model/Content.kt
+++ b/core/src/main/kotlin/Model/Content.kt
@@ -9,7 +9,7 @@ object ContentEmpty : ContentNode {
}
open class ContentBlock() : ContentNode {
- val children = arrayListOf<ContentNode>()
+ open val children = arrayListOf<ContentNode>()
fun append(node: ContentNode) {
children.add(node)
@@ -27,6 +27,34 @@ open class ContentBlock() : ContentNode {
get() = children.sumBy { it.textLength }
}
+class NodeRenderContent(
+ val node: DocumentationNode,
+ val mode: LanguageService.RenderMode
+): ContentNode {
+ override val textLength: Int
+ get() = 0 //TODO: Clarify?
+}
+
+class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() {
+ private var computed = false
+ override val children: ArrayList<ContentNode>
+ get() {
+ if (!computed) {
+ computed = true
+ fillChildren(this)
+ }
+ return super.children
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode() + 31 * fillChildren.hashCode()
+ }
+}
+
enum class IdentifierKind {
TypeName,
ParameterName,
@@ -120,6 +148,9 @@ class ContentExternalLink(val href : String) : ContentBlock() {
children.hashCode() * 31 + href.hashCode()
}
+data class ContentBookmark(val name: String): ContentBlock()
+data class ContentLocalLink(val href: String) : ContentBlock()
+
class ContentUnorderedList() : ContentBlock()
class ContentOrderedList() : ContentBlock()
class ContentListItem() : ContentBlock()
diff --git a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt b/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
deleted file mode 100644
index 85584e3c..00000000
--- a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.jetbrains.dokka.Model
-
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-interface DescriptorSignatureProvider {
- fun signature(forDesc: DeclarationDescriptor): String
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index da85cabf..a3388031 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -48,6 +48,7 @@ enum class NodeKind {
Signature,
ExternalLink,
+ QualifiedName,
Platform,
AllTypes,
@@ -62,7 +63,7 @@ enum class NodeKind {
companion object {
val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
- val memberLike = setOf(Function, Property, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
+ val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
}
}
@@ -85,6 +86,8 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Member).map { it.to }
val inheritedMembers: List<DocumentationNode>
get() = references(RefKind.InheritedMember).map { it.to }
+ val allInheritedMembers: List<DocumentationNode>
+ get() = recursiveInheritedMembers()
val inheritedCompanionObjectMembers: List<DocumentationNode>
get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
val extensions: List<DocumentationNode>
@@ -103,6 +106,28 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Deprecation).singleOrNull()?.to
val platforms: List<String>
get() = references(RefKind.Platform).map { it.to.name }
+ val externalType: DocumentationNode?
+ get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
+
+ val supertypes: List<DocumentationNode>
+ get() = details(NodeKind.Supertype)
+
+ val superclassType: DocumentationNode?
+ get() = when (kind) {
+ NodeKind.Supertype -> {
+ (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType
+ }
+ NodeKind.Interface -> null
+ in NodeKind.classLike -> supertypes.firstOrNull {
+ (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) }
+ }
+ else -> null
+ }
+
+ val superclassTypeSequence: Sequence<DocumentationNode>
+ get() = generateSequence(superclassType) {
+ it.superclassType
+ }
// TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
@@ -123,7 +148,6 @@ open class DocumentationNode(val name: String,
}
(content as MutableContent).body()
}
-
fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind }
fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind }
fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
@@ -154,16 +178,17 @@ val DocumentationNode.path: List<DocumentationNode>
return parent.path + this
}
-fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
- val existingNode = members(NodeKind.Package).firstOrNull { it.name == packageName }
+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,
packageContent.getOrElse(packageName) { Content.Empty },
NodeKind.Package)
- append(newNode, RefKind.Member)
+
refGraph.register(packageName, newNode)
+ module?.append(newNode, RefKind.Member)
return newNode
}
@@ -173,7 +198,8 @@ fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Member -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Owner -> child.addReferenceTo(this, RefKind.Member)
- else -> { /* Do not add any links back for other types */ }
+ else -> { /* Do not add any links back for other types */
+ }
}
}
@@ -186,8 +212,37 @@ fun DocumentationNode.appendTextNode(text: String,
fun DocumentationNode.qualifiedName(): String {
if (kind == NodeKind.Type) {
return qualifiedNameFromType()
+ } else if (kind == NodeKind.Package) {
+ return name
}
return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
}
fun DocumentationNode.simpleName() = name.substringAfterLast('.')
+
+private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
+ val allInheritedMembers = mutableListOf<DocumentationNode>()
+ recursiveInheritedMembers(allInheritedMembers)
+ return allInheritedMembers
+}
+
+private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
+ allInheritedMembers.addAll(inheritedMembers)
+ System.out.println(allInheritedMembers.size)
+ inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) ->
+ node.recursiveInheritedMembers(allInheritedMembers)
+ }
+}
+
+private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean {
+ return when(node.kind) {
+ NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class
+ NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception
+ else -> false
+ }
+}
+
+fun DocumentationNode.classNodeNameWithOuterClass(): String {
+ assert(kind in NodeKind.classLike)
+ return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index a968f400..89ec1b3e 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,7 +18,8 @@ enum class RefKind {
HiddenAnnotation,
Deprecation,
TopLevelPage,
- Platform
+ Platform,
+ ExternalType
}
data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
@@ -61,7 +62,7 @@ class NodeReferenceGraph() {
fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? {
val result = nodeMap[signature]
if (result == null) {
- logger.warn("Can't find node by signature $signature")
+ logger.warn("Can't find node by signature `$signature`")
}
return result
}
diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
new file mode 100644
index 00000000..e8fdde6e
--- /dev/null
+++ b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+
+interface ElementSignatureProvider {
+ fun signature(forDesc: DeclarationDescriptor): String
+ fun signature(forPsi: PsiElement): String
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt
index 1f6bdcb9..5b628914 100644
--- a/core/src/main/kotlin/Model/PackageDocs.kt
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -2,16 +2,25 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiFileFactory
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.LocalTimeCounter
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import java.io.File
@Singleton
class PackageDocs
@Inject constructor(val linkResolver: DeclarationLinkResolver?,
- val logger: DokkaLogger)
+ val logger: DokkaLogger,
+ val environment: KotlinCoreEnvironment,
+ val refGraph: NodeReferenceGraph,
+ val elementSignatureProvider: ElementSignatureProvider)
{
val moduleContent: MutableContent = MutableContent()
private val _packageContent: MutableMap<String, MutableContent> = hashMapOf()
@@ -40,6 +49,64 @@ class PackageDocs
}
}
+ private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) {
+ val javadocText = text
+ .replace("*/", "*&#47;")
+ .removeSurrounding("<html>", "</html>", true).trim()
+ .removeSurrounding("<body>", "</body>", true)
+ .lineSequence()
+ .map { "* $it" }
+ .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/")
+ parseJavadoc(javadocText, packageName, file)
+ }
+
+ private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence {
+ if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) {
+ return subSequence(prefix.length, length - suffix.length)
+ }
+ return subSequence(0, length)
+ }
+
+
+ private fun parseJavadoc(text: String, packageName: String, file: File) {
+
+ val psiFileFactory = PsiFileFactory.getInstance(environment.project)
+ val psiFile = psiFileFactory.createFileFromText(
+ file.nameWithoutExtension + ".java",
+ JavaFileType.INSTANCE,
+ "package $packageName; $text\npublic class C {}",
+ LocalTimeCounter.currentTime(),
+ false,
+ true
+ )
+
+ val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!!
+ val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!)
+ findOrCreatePackageContent(packageName).apply {
+ val content = parser.parseDocumentation(psiClass).content
+ children.addAll(content.children)
+ content.sections.forEach {
+ addSection(it.tag, it.subjectName).children.addAll(it.children)
+ }
+ }
+ }
+
+
+ fun parseJava(fileName: String, packageName: String) {
+ val file = File(fileName)
+ if (file.exists()) {
+ val text = file.readText()
+
+ val trimmedText = text.trim()
+
+ if (trimmedText.startsWith("/**")) {
+ parseJavadoc(text, packageName, file)
+ } else if (trimmedText.toLowerCase().startsWith("<html>")) {
+ parseHtmlAsJavadoc(trimmedText, packageName, file)
+ }
+ }
+ }
+
private fun findTargetContent(heading: String): MutableContent {
if (heading.startsWith("Module") || heading.startsWith("module")) {
return moduleContent