aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Formats/HtmlFormatService.kt4
-rw-r--r--src/Formats/MarkdownFormatService.kt9
-rw-r--r--src/Formats/StructuredFormatService.kt29
-rw-r--r--src/Kotlin/DocumentationBuilder.kt101
-rw-r--r--src/Kotlin/KotlinLanguageService.kt54
-rw-r--r--src/Model/Content.kt1
-rw-r--r--src/Model/DocumentationNode.kt10
-rw-r--r--src/Model/DocumentationReference.kt2
-rw-r--r--src/main.kt12
9 files changed, 188 insertions, 34 deletions
diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt
index b23e4a45..f76693dc 100644
--- a/src/Formats/HtmlFormatService.kt
+++ b/src/Formats/HtmlFormatService.kt
@@ -93,6 +93,10 @@ public open class HtmlFormatService(locationService: LocationService,
return "<emph>${text}</emph>"
}
+ override fun formatStrikethrough(text: String): String {
+ return "<s>${text}</s>"
+ }
+
override fun formatCode(code: String): String {
return "<code>${code}</code>"
}
diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt
index 96f64eec..a2e3ce55 100644
--- a/src/Formats/MarkdownFormatService.kt
+++ b/src/Formats/MarkdownFormatService.kt
@@ -46,6 +46,10 @@ public open class MarkdownFormatService(locationService: LocationService,
return "*$text*"
}
+ override fun formatStrikethrough(text: String): String {
+ return "~~$text~~"
+ }
+
override public fun formatLink(text: String, location: Location): String {
return "[$text](${location.path})"
}
@@ -106,14 +110,15 @@ public open class MarkdownFormatService(locationService: LocationService,
}
override fun appendTableRow(to: StringBuilder, body: () -> Unit) {
- to.append("| ")
+ to.append("|")
body()
to.appendln()
}
override fun appendTableCell(to: StringBuilder, body: () -> Unit) {
+ to.append(" ")
body()
- to.append(" | ")
+ to.append(" |")
}
var outlineLevel = 0
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index 2d326854..977d81d0 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -28,6 +28,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi
public abstract fun formatLink(text: String, href: String): String
public open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.location)
public abstract fun formatStrong(text: String): String
+ public abstract fun formatStrikethrough(text: String): String
public abstract fun formatEmphasis(text: String): String
public abstract fun formatCode(code: String): String
public abstract fun formatList(text: String): String
@@ -46,6 +47,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi
is ContentKeyword -> append(formatKeyword(content.text))
is ContentIdentifier -> append(formatIdentifier(content.text))
is ContentStrong -> append(formatStrong(formatText(location, content.children)))
+ is ContentStrikethrough -> append(formatStrikethrough(formatText(location, content.children)))
is ContentCode -> append(formatCode(formatText(location, content.children)))
is ContentEmphasis -> append(formatEmphasis(formatText(location, content.children)))
is ContentList -> append(formatList(formatText(location, content.children)))
@@ -116,6 +118,17 @@ public abstract class StructuredFormatService(val locationService: LocationServi
for ((summary, items) in breakdownBySummary) {
items.forEach {
appendBlockCode(to, formatText(location, languageService.render(it)))
+ val deprecation = it.deprecation
+ if (deprecation != null) {
+ val deprecationParameter = deprecation.details(DocumentationNode.Kind.Parameter).firstOrNull()
+ val deprecationValue = deprecationParameter?.details(DocumentationNode.Kind.Value)?.firstOrNull()
+ if (deprecationValue != null) {
+ to.append(formatStrong("Deprecated: "))
+ appendLine(to, formatText(deprecationValue.name.trim("\"")))
+ } else {
+ appendLine(to, formatStrong("Deprecated"))
+ }
+ }
}
appendLine(to, summary)
appendLine(to)
@@ -178,18 +191,25 @@ public abstract class StructuredFormatService(val locationService: LocationServi
for ((breadcrumbs, items) in breakdownByLocation) {
appendLine(to, breadcrumbs)
appendLine(to)
- appendLocation(location, to, items)
+ appendLocation(location, to, items.filter { it.kind != DocumentationNode.Kind.ExternalClass })
}
for (node in nodes) {
+ if (node.kind == DocumentationNode.Kind.ExternalClass) {
+ appendSection(location, "Extensions for ${node.name}", node.members, node, to)
+ continue
+ }
+
appendSection(location, "Packages", node.members(DocumentationNode.Kind.Package), node, to)
appendSection(location, "Types", node.members.filter {
it.kind in setOf(
DocumentationNode.Kind.Class,
DocumentationNode.Kind.Interface,
DocumentationNode.Kind.Enum,
- DocumentationNode.Kind.Object)
+ DocumentationNode.Kind.Object,
+ DocumentationNode.Kind.AnnotationClass)
}, node, to)
+ appendSection(location, "Extensions for External Classes", node.members(DocumentationNode.Kind.ExternalClass), node, to)
appendSection(location, "Constructors", node.members(DocumentationNode.Kind.Constructor), node, to)
appendSection(location, "Properties", node.members(DocumentationNode.Kind.Property), node, to)
appendSection(location, "Functions", node.members(DocumentationNode.Kind.Function), node, to)
@@ -200,14 +220,17 @@ public abstract class StructuredFormatService(val locationService: LocationServi
it.kind !in setOf(
DocumentationNode.Kind.Class,
DocumentationNode.Kind.Interface,
+ DocumentationNode.Kind.Enum,
DocumentationNode.Kind.Object,
+ DocumentationNode.Kind.AnnotationClass,
DocumentationNode.Kind.Constructor,
DocumentationNode.Kind.Property,
DocumentationNode.Kind.Package,
DocumentationNode.Kind.Function,
DocumentationNode.Kind.PropertyAccessor,
DocumentationNode.Kind.ClassObjectProperty,
- DocumentationNode.Kind.ClassObjectFunction
+ DocumentationNode.Kind.ClassObjectFunction,
+ DocumentationNode.Kind.ExternalClass
)
}, node, to)
appendSection(location, "Extensions", node.extensions, node, to)
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index af8d0ad6..8b6b19d3 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -6,9 +6,21 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.resolve.lazy.*
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.descriptors.annotations.Annotated
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
+import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
+import com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
public data class DocumentationOptions(val includeNonPublic: Boolean = false)
+private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
+ val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
+ val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
+ return package1 != null && package2 != null && package1.fqName == package2.fqName
+}
+
class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
@@ -73,11 +85,20 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
val superTypes = descriptor.getTypeConstructor().getSupertypes()
for (superType in superTypes) {
- if (superType.toString() != "Any")
+ if (!ignoreSupertype(superType))
appendType(superType, DocumentationNode.Kind.Supertype)
}
}
+ private fun ignoreSupertype(superType: JetType): Boolean {
+ val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
+ if (superClass != null) {
+ val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
+ return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
+ }
+ return false
+ }
+
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
val prefix = when (projection.getProjectionKind()) {
Variance.IN_VARIANCE -> "in "
@@ -104,6 +125,16 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendProjection(typeArgument)
}
+ fun DocumentationNode.appendAnnotations(annotated: Annotated) {
+ annotated.getAnnotations().forEach {
+ val annotationNode = it.build()
+ if (annotationNode != null) {
+ append(annotationNode,
+ if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
+ }
+ }
+ }
+
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
// do not include generated code
if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
@@ -120,15 +151,35 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
}
+ fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
+ externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
+ if (descriptor is CallableMemberDescriptor) {
+ val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
+ if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
+ val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
+ return externalClassNodes.getOrPut(fqName, {
+ val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
+ append(newNode, DocumentationReference.Kind.Member)
+ newNode
+ })
+ }
+ }
+ return this
+ }
+
fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
for ((name, parts) in fragments.groupBy { it.fqName }) {
descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
}
for ((packageName, declarations) in descriptors) {
- println(" package $packageName: ${declarations.count()} nodes")
+ println(" package $packageName: ${declarations.count()} declarations")
val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
- packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
+ val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
+ declarations.forEach { descriptor ->
+ val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
+ parent.appendChild(descriptor, DocumentationReference.Kind.Member)
+ }
append(packageNode, DocumentationReference.Kind.Member)
}
}
@@ -153,6 +204,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
ClassKind.CLASS_OBJECT -> Kind.Object
ClassKind.TRAIT -> Kind.Interface
ClassKind.ENUM_CLASS -> Kind.Enum
+ ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
ClassKind.ENUM_ENTRY -> Kind.EnumItem
else -> Kind.Class
}
@@ -168,6 +220,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
DocumentationReference.Kind.Member)
}
+ node.appendAnnotations(this)
register(this, node)
return node
}
@@ -182,6 +235,15 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
private fun DeclarationDescriptor.inClassObject() =
getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
+ fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
+ val extensionReceiver = getExtensionReceiverParameter()
+ if (extensionReceiver != null) {
+ val type = extensionReceiver.getType()
+ return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
+ }
+ return null
+ }
+
fun FunctionDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
@@ -189,6 +251,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
node.appendType(getReturnType())
+ node.appendAnnotations(this)
register(this, node)
return node
@@ -210,6 +273,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendType(getReturnType())
+ node.appendAnnotations(this)
getGetter()?.let {
if (!it.isDefault())
node.appendChild(it, DocumentationReference.Kind.Member)
@@ -226,6 +290,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
fun ValueParameterDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, Kind.Parameter)
node.appendType(getType())
+ node.appendAnnotations(this)
register(this, node)
return node
}
@@ -264,6 +329,36 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
return node
}
+ fun AnnotationDescriptor.build(): DocumentationNode? {
+ val annotationClass = getType().getConstructor().getDeclarationDescriptor()
+ if (ErrorUtils.isError(annotationClass)) {
+ return null
+ }
+ val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
+ val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
+ arguments.forEach {
+ val valueNode = it.second.build()
+ if (valueNode != null) {
+ val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
+ paramNode.append(valueNode, DocumentationReference.Kind.Detail)
+ node.append(paramNode, DocumentationReference.Kind.Detail)
+ }
+ }
+ return node
+ }
+
+ fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
+ val value = getValue()
+ val valueString = when(value) {
+ is String ->
+ "\"" + StringUtil.escapeStringCharacters(value) + "\""
+ is EnumEntrySyntheticClassDescriptor ->
+ value.getContainingDeclaration().getName().asString() + "." + value.getName()
+ else -> value?.toString()
+ }
+ return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
+ }
+
/**
* Generates cross-references for documentation such as extensions for a type, inheritors, etc
*
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index a4016849..40f47639 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -1,28 +1,18 @@
package org.jetbrains.dokka
-import org.jetbrains.dokka.symbol
-import org.jetbrains.dokka.text
-import org.jetbrains.dokka.identifier
-import org.jetbrains.dokka.link
-import org.jetbrains.dokka.keyword
-import org.jetbrains.dokka.LanguageService
-import org.jetbrains.dokka.DocumentationNode
-import org.jetbrains.dokka.ContentNode
-import org.jetbrains.dokka
-import org.jetbrains.dokka.ContentText
-
/**
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
class KotlinLanguageService : LanguageService {
override fun render(node: DocumentationNode): ContentNode {
- return dokka.content {
+ return content {
when (node.kind) {
DocumentationNode.Kind.Package -> renderPackage(node)
DocumentationNode.Kind.Class,
DocumentationNode.Kind.Interface,
DocumentationNode.Kind.Enum,
DocumentationNode.Kind.EnumItem,
+ DocumentationNode.Kind.AnnotationClass,
DocumentationNode.Kind.Object -> renderClass(node)
DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node)
@@ -135,6 +125,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentNode.renderParameter(node: DocumentationNode) {
+ renderAnnotationsForNode(node)
identifier(node.name)
symbol(": ")
val parameterType = node.detail(DocumentationNode.Kind.Type)
@@ -171,24 +162,46 @@ class KotlinLanguageService : LanguageService {
}
}
+ private fun ContentNode.renderAnnotationsForNode(node: DocumentationNode) {
+ node.annotations.forEach {
+ renderAnnotation(it)
+ }
+ }
+
+ private fun ContentNode.renderAnnotation(node: DocumentationNode) {
+ identifier(node.name)
+ val parameters = node.details(DocumentationNode.Kind.Parameter)
+ if (!parameters.isEmpty()) {
+ symbol("(")
+ renderList(parameters) {
+ text(it.detail(DocumentationNode.Kind.Value).name)
+ }
+ symbol(")")
+ }
+ text(" ")
+ }
+
private fun ContentNode.renderClass(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Class -> keyword("class ")
DocumentationNode.Kind.Interface -> keyword("trait ")
DocumentationNode.Kind.Enum -> keyword("enum class ")
+ DocumentationNode.Kind.AnnotationClass -> keyword("annotation class ")
DocumentationNode.Kind.EnumItem -> keyword("enum val ")
DocumentationNode.Kind.Object -> keyword("object ")
else -> throw IllegalArgumentException("Node $node is not a class-like object")
}
- identifier(node.name)
+ identifierOrDeprecated(node)
renderTypeParametersForNode(node)
renderSupertypesForNode(node)
}
private fun ContentNode.renderFunction(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name)
DocumentationNode.Kind.Function,
@@ -203,7 +216,7 @@ class KotlinLanguageService : LanguageService {
}
if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor)
- identifier(node.name)
+ identifierOrDeprecated(node)
symbol("(")
renderList(node.details(DocumentationNode.Kind.Parameter)) {
@@ -218,6 +231,7 @@ class KotlinLanguageService : LanguageService {
private fun ContentNode.renderProperty(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Property,
DocumentationNode.Kind.ClassObjectProperty -> keyword("val ")
@@ -230,8 +244,18 @@ class KotlinLanguageService : LanguageService {
symbol(".")
}
- identifier(node.name)
+ identifierOrDeprecated(node)
symbol(": ")
renderType(node.detail(DocumentationNode.Kind.Type))
}
+
+ fun ContentNode.identifierOrDeprecated(node: DocumentationNode) {
+ if (node.deprecation != null) {
+ val strike = ContentStrikethrough()
+ strike.identifier(node.name)
+ append(strike)
+ } else {
+ identifier(node.name)
+ }
+ }
} \ No newline at end of file
diff --git a/src/Model/Content.kt b/src/Model/Content.kt
index e1c1ef78..8491fd88 100644
--- a/src/Model/Content.kt
+++ b/src/Model/Content.kt
@@ -27,6 +27,7 @@ public class ContentSymbol(val text: String) : ContentNode()
public class ContentParagraph() : ContentBlock()
public class ContentEmphasis() : ContentBlock()
public class ContentStrong() : ContentBlock()
+public class ContentStrikethrough() : ContentBlock()
public class ContentCode() : ContentBlock()
public class ContentBlockCode() : ContentBlock()
public class ContentNodeLink(val node : DocumentationNode) : ContentBlock()
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index 86d2ee04..635d1db9 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -30,6 +30,10 @@ public open class DocumentationNode(val name: String,
get() = references(DocumentationReference.Kind.Inheritor).map { it.to }
public val links: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Link).map { it.to }
+ public val annotations: List<DocumentationNode>
+ get() = references(DocumentationReference.Kind.Annotation).map { it.to }
+ public val deprecation: DocumentationNode?
+ get() = references(DocumentationReference.Kind.Deprecation).singleOrNull()?.to
// TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
public fun addReferenceTo(to: DocumentationNode, kind: DocumentationReference.Kind) {
@@ -62,6 +66,7 @@ public open class DocumentationNode(val name: String,
Class
Interface
Enum
+ AnnotationClass
EnumItem
Object
@@ -85,6 +90,11 @@ public open class DocumentationNode(val name: String,
Modifier
Module
+
+ ExternalClass
+ Annotation
+
+ Value
}
}
diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt
index 1849fe08..bd40f0f5 100644
--- a/src/Model/DocumentationReference.kt
+++ b/src/Model/DocumentationReference.kt
@@ -9,6 +9,8 @@ public data class DocumentationReference(val from: DocumentationNode, val to: Do
Extension
Inheritor
Override
+ Annotation
+ Deprecation
}
}
diff --git a/src/main.kt b/src/main.kt
index f540e3dc..24853be5 100644
--- a/src/main.kt
+++ b/src/main.kt
@@ -98,17 +98,7 @@ public fun main(args: Array<String>) {
}
val documentationModule = DocumentationModule(arguments.moduleName, moduleContent)
-
- val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
- for ((name, parts) in fragments.groupBy { it.fqName }) {
- descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
- }
- for ((packageName, declarations) in descriptors) {
- println(" package $packageName: ${declarations.count()} nodes")
- val packageNode = DocumentationNode(packageName, Content.Empty, DocumentationNode.Kind.Package)
- packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
- documentationModule.append(packageNode, DocumentationReference.Kind.Member)
- }
+ documentationModule.appendFragments(fragments)
documentationBuilder.resolveReferences(documentationModule)
documentationModule
}