aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Analysis/AnalysisEnvironment.kt104
-rw-r--r--src/Analysis/CompilerAPI.kt28
-rw-r--r--src/Formats/FormatService.kt2
-rw-r--r--src/Formats/HtmlFormatService.kt12
-rw-r--r--src/Formats/JekyllFormatService.kt2
-rw-r--r--src/Formats/KotlinWebsiteFormatService.kt35
-rw-r--r--src/Formats/OutlineService.kt4
-rw-r--r--src/Formats/StructuredFormatService.kt200
-rw-r--r--src/Generation/ConsoleGenerator.kt2
-rw-r--r--src/Java/JavaDocumentationBuilder.kt10
-rw-r--r--src/Kotlin/ContentBuilder.kt62
-rw-r--r--src/Kotlin/DocumentationBuilder.kt209
-rw-r--r--src/Kotlin/KotlinLanguageService.kt248
-rw-r--r--src/Languages/JavaLanguageService.kt18
-rw-r--r--src/Languages/LanguageService.kt12
-rw-r--r--src/Markdown/MarkdownProcessor.kt14
-rw-r--r--src/Model/Content.kt65
-rw-r--r--src/Model/DocumentationModule.kt11
-rw-r--r--src/Model/DocumentationNode.kt14
-rw-r--r--src/Model/DocumentationReference.kt3
-rw-r--r--src/Model/PackageDocs.kt14
-rw-r--r--src/Model/SourceLinks.kt9
-rw-r--r--src/Utilities/GuiceModule.kt6
-rw-r--r--src/Utilities/Path.kt11
-rw-r--r--src/Utilities/ServiceLocator.kt14
-rw-r--r--src/main.kt42
26 files changed, 717 insertions, 434 deletions
diff --git a/src/Analysis/AnalysisEnvironment.kt b/src/Analysis/AnalysisEnvironment.kt
index a2836592..561ac2f0 100644
--- a/src/Analysis/AnalysisEnvironment.kt
+++ b/src/Analysis/AnalysisEnvironment.kt
@@ -4,24 +4,45 @@ import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.CoreModuleManager
import com.intellij.mock.MockComponentManager
import com.intellij.openapi.Disposable
-import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
-import com.intellij.openapi.roots.ContentIterator
-import com.intellij.openapi.roots.OrderEntry
+import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.util.Disposer
-import com.intellij.openapi.vfs.VirtualFile
-import org.jetbrains.jps.model.module.JpsModuleSourceRootType
+import com.intellij.psi.PsiElement
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analyzer.AnalysisResult
+import org.jetbrains.kotlin.analyzer.ModuleContent
+import org.jetbrains.kotlin.analyzer.ModuleInfo
+import org.jetbrains.kotlin.analyzer.ResolverForModule
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.cli.jvm.config.*
-import org.jetbrains.kotlin.config.*
+import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
+import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
+import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
+import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
+import org.jetbrains.kotlin.config.CommonConfigurationKeys
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.config.ContentRoot
+import org.jetbrains.kotlin.config.KotlinSourceRoot
+import org.jetbrains.kotlin.container.get
+import org.jetbrains.kotlin.container.getService
+import org.jetbrains.kotlin.context.ProjectContext
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.idea.caches.resolve.KotlinOutOfBlockCompletionModificationTracker
import org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker
-import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.CompilerEnvironment
+import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
+import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import java.io.File
@@ -45,27 +66,42 @@ public class AnalysisEnvironment(val messageCollector: MessageCollector, body: A
* Executes [processor] when analysis is complete.
* $processor: function to receive compiler environment, module and context for symbol resolution
*/
- public fun withContext<T>(processor: (KotlinCoreEnvironment, ResolutionFacade, ResolveSession) -> T): T {
+ public fun <T> withContext(processor: (KotlinCoreEnvironment, ResolutionFacade, ResolveSession) -> T): T {
val environment = KotlinCoreEnvironment.createForProduction(this, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val projectComponentManager = environment.project as MockComponentManager
val moduleManager = CoreModuleManager(environment.project, this)
CoreApplicationEnvironment.registerComponentInstance(projectComponentManager.picoContainer,
- javaClass<ModuleManager>(), moduleManager)
+ ModuleManager::class.java, moduleManager)
- projectComponentManager.registerService(javaClass<ProjectFileIndex>(),
+ projectComponentManager.registerService(ProjectFileIndex::class.java,
CoreProjectFileIndex())
- projectComponentManager.registerService(javaClass<LibraryModificationTracker>(),
+ projectComponentManager.registerService(LibraryModificationTracker::class.java,
LibraryModificationTracker(environment.project))
- projectComponentManager.registerService(javaClass<KotlinCacheService>(),
+ projectComponentManager.registerService(KotlinCacheService::class.java,
KotlinCacheService(environment.project))
- projectComponentManager.registerService(javaClass<KotlinOutOfBlockCompletionModificationTracker>(),
+ projectComponentManager.registerService(KotlinOutOfBlockCompletionModificationTracker::class.java,
KotlinOutOfBlockCompletionModificationTracker())
+ val projectContext = ProjectContext(environment.project)
val sourceFiles = environment.getSourceFiles()
- val facade = KotlinCacheService.getInstance(environment.project).getResolutionFacade(sourceFiles)
- // TODO get rid of resolveSession once we have all necessary APIs in ResolutionFacade
- val resolveSession = environment.analyze()
+
+ val module = object : ModuleInfo {
+ override val name: Name = Name.special("<module>")
+ override fun dependencies(): List<ModuleInfo> = listOf(this)
+ }
+ val resolverForProject = JvmAnalyzerFacade.setupResolverForProject(
+ "Dokka",
+ projectContext,
+ listOf(module),
+ { ModuleContent(sourceFiles, GlobalSearchScope.allScope(environment.project)) },
+ JvmPlatformParameters { module },
+ CompilerEnvironment
+ )
+
+ val resolverForModule = resolverForProject.resolverForModule(module)
+ val resolveSession = resolverForModule.componentProvider.get<ResolveSession>()
+ val facade = DokkaResolutionFacade(environment.project, resolverForProject.descriptorForModule(module), resolverForModule)
return processor(environment, facade, resolveSession)
}
@@ -125,3 +161,37 @@ public fun contentRootFromPath(path: String): ContentRoot {
val file = File(path)
return if (file.extension == "java") JavaSourceRoot(file) else KotlinSourceRoot(path)
}
+
+
+class DokkaResolutionFacade(override val project: Project,
+ override val moduleDescriptor: ModuleDescriptor,
+ val resolverForModule: ResolverForModule) : ResolutionFacade {
+
+ override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext {
+ throw UnsupportedOperationException()
+ }
+
+ override fun analyzeFullyAndGetResult(elements: Collection<KtElement>): AnalysisResult {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> getFrontendService(serviceClass: Class<T>): T {
+ return resolverForModule.componentProvider.getService(serviceClass)
+ }
+
+ override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> getIdeService(serviceClass: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor {
+ throw UnsupportedOperationException()
+ }
+}
diff --git a/src/Analysis/CompilerAPI.kt b/src/Analysis/CompilerAPI.kt
deleted file mode 100644
index 5ab2037b..00000000
--- a/src/Analysis/CompilerAPI.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.jetbrains.dokka
-
-import com.intellij.psi.search.GlobalSearchScope
-import org.jetbrains.kotlin.analyzer.ModuleContent
-import org.jetbrains.kotlin.analyzer.ModuleInfo
-import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
-import org.jetbrains.kotlin.context.ProjectContext
-import org.jetbrains.kotlin.name.Name
-import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
-import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
-import org.jetbrains.kotlin.resolve.lazy.ResolveSession
-
-fun KotlinCoreEnvironment.analyze(): ResolveSession {
- val projectContext = ProjectContext(project)
- val sourceFiles = getSourceFiles()
-
- val module = object : ModuleInfo {
- override val name: Name = Name.special("<module>")
- override fun dependencies(): List<ModuleInfo> = listOf(this)
- }
- val resolverForProject = JvmAnalyzerFacade.setupResolverForProject(
- projectContext,
- listOf(module),
- { ModuleContent(sourceFiles, GlobalSearchScope.allScope(project)) },
- JvmPlatformParameters { module }
- )
- return resolverForProject.resolverForModule(module).lazyResolveSession
-}
diff --git a/src/Formats/FormatService.kt b/src/Formats/FormatService.kt
index 93470a4c..7e66a6b7 100644
--- a/src/Formats/FormatService.kt
+++ b/src/Formats/FormatService.kt
@@ -17,4 +17,4 @@ public interface FormatService {
}
/** Format content to [String] using specified [location] */
-fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder { appendNodes(location, this, nodes) }.toString()
+fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply { appendNodes(location, this, nodes) }.toString()
diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt
index 5dcd432b..e810ef7f 100644
--- a/src/Formats/HtmlFormatService.kt
+++ b/src/Formats/HtmlFormatService.kt
@@ -114,13 +114,13 @@ public open class HtmlFormatService @Inject constructor(@Named("folders") locati
override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
templateService.appendHeader(to, getPageTitle(nodes))
- super<StructuredFormatService>.appendNodes(location, to, nodes)
+ super.appendNodes(location, to, nodes)
templateService.appendFooter(to)
}
override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
templateService.appendHeader(to, "Module Contents")
- super<OutlineFormatService>.appendOutline(location, to, nodes)
+ super.appendOutline(location, to, nodes)
templateService.appendFooter(to)
}
@@ -146,16 +146,16 @@ public open class HtmlFormatService @Inject constructor(@Named("folders") locati
fun getPageTitle(nodes: Iterable<DocumentationNode>): String? {
val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) }
- return breakdownByLocation.keySet().singleOrNull()
+ return breakdownByLocation.keys.singleOrNull()
}
fun formatPageTitle(node: DocumentationNode): String {
val path = node.path
- if (path.size() == 1) {
+ if (path.size == 1) {
return path.first().name
}
- val qualifiedName = path.drop(1).map { it.name }.filter { it.length() > 0 }.join(".")
- if (qualifiedName.length() == 0 && path.size() == 2) {
+ val qualifiedName = node.qualifiedName()
+ if (qualifiedName.length == 0 && path.size == 2) {
return path.first().name + " / root package"
}
return path.first().name + " / " + qualifiedName
diff --git a/src/Formats/JekyllFormatService.kt b/src/Formats/JekyllFormatService.kt
index 113f229f..75684ac2 100644
--- a/src/Formats/JekyllFormatService.kt
+++ b/src/Formats/JekyllFormatService.kt
@@ -11,7 +11,7 @@ public open class JekyllFormatService @Inject constructor(locationService: Locat
appendFrontMatter(nodes, to)
to.appendln("---")
to.appendln("")
- super<MarkdownFormatService>.appendNodes(location, to, nodes)
+ super.appendNodes(location, to, nodes)
}
protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) {
diff --git a/src/Formats/KotlinWebsiteFormatService.kt b/src/Formats/KotlinWebsiteFormatService.kt
index 8fbebaae..25491cb3 100644
--- a/src/Formats/KotlinWebsiteFormatService.kt
+++ b/src/Formats/KotlinWebsiteFormatService.kt
@@ -5,6 +5,7 @@ import com.google.inject.Inject
public class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService,
signatureGenerator: LanguageService)
: JekyllFormatService(locationService, signatureGenerator) {
+ private var needHardLineBreaks = false
override fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) {
super.appendFrontMatter(nodes, to)
@@ -23,16 +24,27 @@ public class KotlinWebsiteFormatService @Inject constructor(locationService: Loc
return ""
}
- override public fun formatCode(code: String): String = if (code.length() > 0) "<code>$code</code>" else ""
+ override public fun formatCode(code: String): String = if (code.length > 0) "<code>$code</code>" else ""
override fun formatStrikethrough(text: String): String = "<s>$text</s>"
- override fun appendAsSignature(to: StringBuilder, block: () -> Unit) {
- val oldLength = to.length()
- block()
- if (to.length() > oldLength) {
- to.append("<br/>") // since we've used HTML to format the signature, add an HTML line break following it
+ override fun appendAsSignature(to: StringBuilder, node: ContentNode, block: () -> Unit) {
+ val contentLength = node.textLength
+ if (contentLength == 0) return
+ to.append("<div class=\"signature\">")
+ needHardLineBreaks = contentLength >= 62
+ try {
+ block()
+ } finally {
+ needHardLineBreaks = false
}
+ to.append("</div>")
+ }
+
+ override fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) {
+ to.append("<div class=\"overload-group\">\n")
+ block()
+ to.append("</div>\n")
}
override fun formatLink(text: String, href: String): String {
@@ -91,8 +103,19 @@ public class KotlinWebsiteFormatService @Inject constructor(locationService: Loc
return "<span class=\"${identifierClassName(kind)}\">${formatText(text)}</span>"
}
+ override fun formatSoftLineBreak(): String = if (needHardLineBreaks)
+ "<br/>"
+ else
+ ""
+
+ override fun formatIndentedSoftLineBreak(): String = if (needHardLineBreaks)
+ "<br/>&nbsp;&nbsp;&nbsp;&nbsp;"
+ else
+ ""
+
private fun identifierClassName(kind: IdentifierKind) = when(kind) {
IdentifierKind.ParameterName -> "parameterName"
+ IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
else -> "identifier"
}
}
diff --git a/src/Formats/OutlineService.kt b/src/Formats/OutlineService.kt
index 6c7e882e..6626cf51 100644
--- a/src/Formats/OutlineService.kt
+++ b/src/Formats/OutlineService.kt
@@ -16,7 +16,7 @@ public interface OutlineFormatService {
for (node in nodes) {
appendOutlineHeader(location, node, to)
if (node.members.any()) {
- val sortedMembers = node.members.sortBy { it.name }
+ val sortedMembers = node.members.sortedBy { it.name }
appendOutlineLevel(to) {
appendOutline(location, to, sortedMembers)
}
@@ -25,5 +25,5 @@ public interface OutlineFormatService {
}
fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String =
- StringBuilder { appendOutline(location, this, nodes) }.toString()
+ StringBuilder().apply { appendOutline(location, this, nodes) }.toString()
}
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index 1c2e5338..41ffc61d 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -1,62 +1,66 @@
package org.jetbrains.dokka
-import java.util.LinkedHashMap
import org.jetbrains.dokka.LanguageService.RenderMode
+import java.util.*
-public data class FormatLink(val text: String, val href: String)
+data class FormatLink(val text: String, val href: String)
enum class ListKind {
Ordered,
Unordered
}
-public abstract class StructuredFormatService(locationService: LocationService,
+abstract class StructuredFormatService(locationService: LocationService,
val languageService: LanguageService,
override val extension: String) : FormatService {
val locationService: LocationService = locationService.withExtension(extension)
- abstract public fun appendBlockCode(to: StringBuilder, line: String, language: String)
- abstract public fun appendHeader(to: StringBuilder, text: String, level: Int = 1)
- abstract public fun appendParagraph(to: StringBuilder, text: String)
- abstract public fun appendLine(to: StringBuilder, text: String)
- public abstract fun appendLine(to: StringBuilder)
- public abstract fun appendAnchor(to: StringBuilder, anchor: String)
-
- public abstract fun appendTable(to: StringBuilder, body: () -> Unit)
- public abstract fun appendTableHeader(to: StringBuilder, body: () -> Unit)
- public abstract fun appendTableBody(to: StringBuilder, body: () -> Unit)
- public abstract fun appendTableRow(to: StringBuilder, body: () -> Unit)
- public abstract fun appendTableCell(to: StringBuilder, body: () -> Unit)
-
- public abstract fun formatText(text: String): String
- public abstract fun formatSymbol(text: String): String
- public abstract fun formatKeyword(text: String): String
- public abstract fun formatIdentifier(text: String, kind: IdentifierKind): String
- public fun formatEntity(text: String): String = text
- public abstract fun formatLink(text: String, href: String): String
- public open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.href)
- 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 formatUnorderedList(text: String): String
- public abstract fun formatOrderedList(text: String): String
- public abstract fun formatListItem(text: String, kind: ListKind): String
- public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
- public abstract fun formatNonBreakingSpace(): String
+ abstract fun appendBlockCode(to: StringBuilder, line: String, language: String)
+ abstract fun appendHeader(to: StringBuilder, text: String, level: Int = 1)
+ abstract fun appendParagraph(to: StringBuilder, text: String)
+ abstract fun appendLine(to: StringBuilder, text: String)
+ abstract fun appendLine(to: StringBuilder)
+ abstract fun appendAnchor(to: StringBuilder, anchor: String)
+
+ abstract fun appendTable(to: StringBuilder, body: () -> Unit)
+ abstract fun appendTableHeader(to: StringBuilder, body: () -> Unit)
+ abstract fun appendTableBody(to: StringBuilder, body: () -> Unit)
+ abstract fun appendTableRow(to: StringBuilder, body: () -> Unit)
+ abstract fun appendTableCell(to: StringBuilder, body: () -> Unit)
+
+ abstract fun formatText(text: String): String
+ abstract fun formatSymbol(text: String): String
+ abstract fun formatKeyword(text: String): String
+ abstract fun formatIdentifier(text: String, kind: IdentifierKind): String
+ fun formatEntity(text: String): String = text
+ abstract fun formatLink(text: String, href: String): String
+ open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.href)
+ abstract fun formatStrong(text: String): String
+ abstract fun formatStrikethrough(text: String): String
+ abstract fun formatEmphasis(text: String): String
+ abstract fun formatCode(code: String): String
+ abstract fun formatUnorderedList(text: String): String
+ abstract fun formatOrderedList(text: String): String
+ abstract fun formatListItem(text: String, kind: ListKind): String
+ abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
+ abstract fun formatNonBreakingSpace(): String
+ open fun formatSoftLineBreak(): String = ""
+ open fun formatIndentedSoftLineBreak(): String = ""
open fun formatText(location: Location, nodes: Iterable<ContentNode>, listKind: ListKind = ListKind.Unordered): String {
- return nodes.map { formatText(location, it, listKind) }.join("")
+ return nodes.map { formatText(location, it, listKind) }.joinToString("")
}
open fun formatText(location: Location, content: ContentNode, listKind: ListKind = ListKind.Unordered): String {
- return StringBuilder {
+ return StringBuilder().apply {
when (content) {
is ContentText -> append(formatText(content.text))
is ContentSymbol -> append(formatSymbol(content.text))
is ContentKeyword -> append(formatKeyword(content.text))
is ContentIdentifier -> append(formatIdentifier(content.text, content.kind))
is ContentNonBreakingSpace -> append(formatNonBreakingSpace())
+ is ContentSoftLineBreak -> append(formatSoftLineBreak())
+ is ContentIndentedSoftLineBreak -> append(formatIndentedSoftLineBreak())
is ContentEntity -> append(formatEntity(content.text))
is ContentStrong -> append(formatStrong(formatText(location, content.children)))
is ContentStrikethrough -> append(formatStrikethrough(formatText(location, content.children)))
@@ -92,9 +96,9 @@ public abstract class StructuredFormatService(locationService: LocationService,
}.toString()
}
- open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension)
+ open fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension)
- open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink {
+ open fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink {
return FormatLink(to.name, locationService.relativePathToLocation(from, to))
}
@@ -110,41 +114,47 @@ public abstract class StructuredFormatService(locationService: LocationService,
val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content }
for ((summary, items) in breakdownBySummary) {
- items.forEach {
- appendAsSignature(to) {
- to.append(formatCode(formatText(location, languageService.render(it))))
- it.appendSourceLink(to)
+ appendAsOverloadGroup(to) {
+ items.forEach {
+ val rendered = languageService.render(it)
+ appendAsSignature(to, rendered) {
+ to.append(formatCode(formatText(location, rendered)))
+ it.appendSourceLink(to)
+ }
+ it.appendOverrides(to)
+ it.appendDeprecation(location, to)
}
- it.appendOverrides(to)
- it.appendDeprecation(location, to)
- }
- // All items have exactly the same documentation, so we can use any item to render it
- val item = items.first()
- item.details(DocumentationNode.Kind.OverloadGroupNote).forEach {
- to.append(formatText(location, it.content))
+ // All items have exactly the same documentation, so we can use any item to render it
+ val item = items.first()
+ item.details(DocumentationNode.Kind.OverloadGroupNote).forEach {
+ to.append(formatText(location, it.content))
+ }
+ to.append(formatText(location, item.content.summary))
+ appendDescription(location, to, item)
+ appendLine(to)
+ appendLine(to)
}
- to.append(formatText(location, item.content.summary))
- appendDescription(location, to, item)
- appendLine(to)
- appendLine(to)
}
}
private fun DocumentationNode.isModuleOrPackage(): Boolean =
kind == DocumentationNode.Kind.Module || kind == DocumentationNode.Kind.Package
- protected open fun appendAsSignature(to: StringBuilder, block: () -> Unit) {
+ protected open fun appendAsSignature(to: StringBuilder, node: ContentNode, block: () -> Unit) {
+ block()
+ }
+
+ protected open fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) {
block()
}
fun appendDescription(location: Location, to: StringBuilder, node: DocumentationNode) {
if (node.content.description != ContentEmpty) {
- appendHeader(to, ContentTags.Description, 3)
appendLine(to, formatText(location, node.content.description))
appendLine(to)
}
node.content.getSectionsWithSubjects().forEach {
- appendSectionWithSubject(it.getKey(), location, it.getValue(), to)
+ appendSectionWithSubject(it.key, location, it.value, to)
}
for (section in node.content.sections.filter { it.subjectName == null }) {
@@ -221,11 +231,11 @@ public abstract class StructuredFormatService(locationService: LocationService,
}
}
- private fun StructuredFormatService.appendSection(location: Location, caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
+ private fun appendSection(location: Location, caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
if (nodes.any()) {
appendHeader(to, caption, 3)
- val children = nodes.sortBy { it.name }
+ val children = nodes.sortedBy { it.name }
val membersMap = children.groupBy { link(node, it) }
appendTable(to) {
@@ -238,24 +248,7 @@ public abstract class StructuredFormatService(locationService: LocationService,
appendTableCell(to) {
val breakdownBySummary = members.groupBy { formatText(location, it.summary) }
for ((summary, items) in breakdownBySummary) {
- val signatureTexts = items map { signature ->
- val signatureText = languageService.render(signature, RenderMode.SUMMARY)
- if (signatureText is ContentBlock && signatureText.isEmpty()) {
- ""
- } else {
- val signatureAsCode = ContentCode()
- signatureAsCode.append(signatureText)
- formatText(location, signatureAsCode)
- }
- }
- signatureTexts.subList(0, signatureTexts.size()-1).forEach {
- appendAsSignature(to) {
- appendLine(to, it)
- }
- }
- appendAsSignature(to) {
- to.append(signatureTexts.last())
- }
+ appendSummarySignatures(items, location, to)
if (!summary.isEmpty()) {
to.append(summary)
}
@@ -268,6 +261,35 @@ public abstract class StructuredFormatService(locationService: LocationService,
}
}
+ private fun appendSummarySignatures(items: List<DocumentationNode>, location: Location, to: StringBuilder) {
+ val summarySignature = languageService.summarizeSignatures(items)
+ if (summarySignature != null) {
+ appendAsSignature(to, summarySignature) {
+ appendLine(to, summarySignature.signatureToText(location))
+ }
+ return
+ }
+ val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
+ renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
+ appendAsSignature(to, it) {
+ appendLine(to, it.signatureToText(location))
+ }
+ }
+ appendAsSignature(to, renderedSignatures.last()) {
+ to.append(renderedSignatures.last().signatureToText(location))
+ }
+ }
+
+ private fun ContentNode.signatureToText(location: Location): String {
+ return if (this is ContentBlock && this.isEmpty()) {
+ ""
+ } else {
+ val signatureAsCode = ContentCode()
+ signatureAsCode.append(this)
+ formatText(location, signatureAsCode)
+ }
+ }
+
override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
val breakdownByLocation = nodes.groupBy { node ->
formatBreadcrumbs(node.path.filterNot { it.name.isEmpty() }.map { link(node, it) })
@@ -295,12 +317,14 @@ public abstract class StructuredFormatService(locationService: LocationService,
DocumentationNode.Kind.AnnotationClass)
}, node, to)
appendSection(location, "Extensions for External Classes", node.members(DocumentationNode.Kind.ExternalClass), node, to)
+ appendSection(location, "Enum Values", node.members(DocumentationNode.Kind.EnumItem), node, to)
appendSection(location, "Constructors", node.members(DocumentationNode.Kind.Constructor), node, to)
appendSection(location, "Properties", node.members(DocumentationNode.Kind.Property), node, to)
+ appendSection(location, "Inherited Properties", node.inheritedMembers(DocumentationNode.Kind.Property), node, to)
appendSection(location, "Functions", node.members(DocumentationNode.Kind.Function), node, to)
+ appendSection(location, "Inherited Functions", node.inheritedMembers(DocumentationNode.Kind.Function), node, to)
appendSection(location, "Companion Object Properties", node.members(DocumentationNode.Kind.CompanionObjectProperty), node, to)
appendSection(location, "Companion Object Functions", node.members(DocumentationNode.Kind.CompanionObjectFunction), node, to)
- appendSection(location, "Enum Values", node.members(DocumentationNode.Kind.EnumItem), node, to)
appendSection(location, "Other members", node.members.filter {
it.kind !in setOf(
DocumentationNode.Kind.Class,
@@ -318,14 +342,32 @@ public abstract class StructuredFormatService(locationService: LocationService,
DocumentationNode.Kind.EnumItem
)
}, node, to)
- appendSection(location, "Extension Properties", node.extensions.filter { it.kind == DocumentationNode.Kind.Property }, node, to)
- appendSection(location, "Extension Functions", node.extensions.filter { it.kind == DocumentationNode.Kind.Function }, node, to)
- appendSection(location, "Companion Object Extension Properties", node.extensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectProperty }, node, to)
- appendSection(location, "Companion Object Extension Functions", node.extensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectFunction }, node, to)
+
+ val allExtensions = collectAllExtensions(node)
+ appendSection(location, "Extension Properties", allExtensions.filter { it.kind == DocumentationNode.Kind.Property }, node, to)
+ appendSection(location, "Extension Functions", allExtensions.filter { it.kind == DocumentationNode.Kind.Function }, node, to)
+ appendSection(location, "Companion Object Extension Properties", allExtensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectProperty }, node, to)
+ appendSection(location, "Companion Object Extension Functions", allExtensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectFunction }, node, to)
appendSection(location, "Inheritors",
node.inheritors.filter { it.kind != DocumentationNode.Kind.EnumItem }, node, to)
appendSection(location, "Links", node.links, node, to)
}
}
+
+ private fun collectAllExtensions(node: DocumentationNode): Collection<DocumentationNode> {
+ val result = LinkedHashSet<DocumentationNode>()
+ val visited = hashSetOf<DocumentationNode>()
+
+ fun collect(node: DocumentationNode) {
+ if (!visited.add(node)) return
+ result.addAll(node.extensions)
+ node.references(DocumentationReference.Kind.Superclass).forEach { collect(it.to) }
+ }
+
+ collect(node)
+
+ return result
+
+ }
} \ No newline at end of file
diff --git a/src/Generation/ConsoleGenerator.kt b/src/Generation/ConsoleGenerator.kt
index 2f2daa55..803a16e4 100644
--- a/src/Generation/ConsoleGenerator.kt
+++ b/src/Generation/ConsoleGenerator.kt
@@ -20,7 +20,7 @@ public class ConsoleGenerator(val signatureGenerator: LanguageService, val locat
}
public fun generateMembers(node: DocumentationNode, indent: String = "") {
- val items = node.members.sortBy { it.name }
+ val items = node.members.sortedBy { it.name }
for (child in items)
generate(child, indent + IndentStep)
}
diff --git a/src/Java/JavaDocumentationBuilder.kt b/src/Java/JavaDocumentationBuilder.kt
index 9717c1a6..88c13d38 100644
--- a/src/Java/JavaDocumentationBuilder.kt
+++ b/src/Java/JavaDocumentationBuilder.kt
@@ -229,9 +229,9 @@ public class JavaDocumentationBuilder(private val options: DocumentationOptions,
else -> false
}
- fun DocumentationNode.appendChildren<T>(elements: Array<T>,
- kind: DocumentationReference.Kind = DocumentationReference.Kind.Member,
- buildFn: T.() -> DocumentationNode) {
+ fun <T : Any> DocumentationNode.appendChildren(elements: Array<T>,
+ kind: DocumentationReference.Kind = DocumentationReference.Kind.Member,
+ buildFn: T.() -> DocumentationNode) {
elements.forEach {
if (!skipElement(it)) {
append(it.buildFn(), kind)
@@ -248,10 +248,10 @@ public class JavaDocumentationBuilder(private val options: DocumentationOptions,
private fun hasSuppressTag(element: Any) =
element is PsiDocCommentOwner && element.docComment?.let { it.findTagByName("suppress") != null } ?: false
- fun DocumentationNode.appendMembers<T>(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
+ fun <T : Any> DocumentationNode.appendMembers(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
appendChildren(elements, DocumentationReference.Kind.Member, buildFn)
- fun DocumentationNode.appendDetails<T>(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
+ fun <T : Any> DocumentationNode.appendDetails(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
appendChildren(elements, DocumentationReference.Kind.Detail, buildFn)
fun PsiClass.build(): DocumentationNode {
diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt
index 9ce81cee..c981eb42 100644
--- a/src/Kotlin/ContentBuilder.kt
+++ b/src/Kotlin/ContentBuilder.kt
@@ -1,14 +1,19 @@
package org.jetbrains.dokka
-import java.util.ArrayDeque
-import org.jetbrains.kotlin.descriptors.*
-import org.jetbrains.kotlin.resolve.*
-import org.jetbrains.kotlin.resolve.scopes.*
-import org.jetbrains.kotlin.name.*
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.html.entities.EntityConverter
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.kdoc.getResolutionScope
-import org.intellij.markdown.*
-import org.jetbrains.kotlin.psi.JetDeclarationWithBody
-import org.jetbrains.kotlin.psi.JetBlockExpression
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtDeclarationWithBody
+import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
+import org.jetbrains.kotlin.resolve.scopes.KtScope
+import org.jetbrains.kotlin.resolve.scopes.utils.asJetScope
+import java.util.*
public fun buildContent(tree: MarkdownNode, linkResolver: (String) -> ContentBlock): MutableContent {
val result = MutableContent()
@@ -43,7 +48,8 @@ public fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver
MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis())
MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong())
MarkdownElementTypes.CODE_SPAN -> appendNodeWithChildren(ContentCode())
- MarkdownElementTypes.CODE_BLOCK -> appendNodeWithChildren(ContentBlockCode())
+ MarkdownElementTypes.CODE_BLOCK,
+ MarkdownElementTypes.CODE_FENCE -> appendNodeWithChildren(ContentBlockCode())
MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph())
MarkdownElementTypes.INLINE_LINK -> {
@@ -84,11 +90,24 @@ public fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver
parent.append(block)
}
- MarkdownTokenTypes.HTML_ENTITY -> {
- parent.append(ContentEntity(node.text))
+ MarkdownTokenTypes.TEXT -> {
+ fun createEntityOrText(text: String): ContentNode {
+ if (text == "&amp;" || text == "&quot;" || text == "&lt;" || text == "&gt;") {
+ return ContentEntity(text)
+ }
+ if (text == "&") {
+ return ContentEntity("&amp;")
+ }
+ val decodedText = EntityConverter.replaceEntities(text, true, true)
+ if (decodedText != text) {
+ return ContentEntity(text)
+ }
+ return ContentText(text)
+ }
+
+ parent.append(createEntityOrText(node.text))
}
- MarkdownTokenTypes.TEXT,
MarkdownTokenTypes.COLON,
MarkdownTokenTypes.DOUBLE_QUOTE,
MarkdownTokenTypes.LT,
@@ -96,7 +115,8 @@ public fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver
MarkdownTokenTypes.LPAREN,
MarkdownTokenTypes.RPAREN,
MarkdownTokenTypes.LBRACKET,
- MarkdownTokenTypes.RBRACKET -> {
+ MarkdownTokenTypes.RBRACKET,
+ MarkdownTokenTypes.CODE_FENCE_CONTENT -> {
parent.append(ContentText(node.text))
}
else -> {
@@ -120,8 +140,8 @@ fun DocumentationBuilder.functionBody(descriptor: DeclarationDescriptor, functio
logger.warn("Missing function name in @sample in ${descriptor.signature()}")
return ContentBlockCode().let() { it.append(ContentText("Missing function name in @sample")); it }
}
- val scope = getResolutionScope(resolutionFacade, descriptor)
- val rootPackage = session.getModuleDescriptor().getPackage(FqName.ROOT)
+ val scope = getResolutionScope(resolutionFacade, descriptor).asJetScope()
+ val rootPackage = resolutionFacade.moduleDescriptor.getPackage(FqName.ROOT)
val rootScope = rootPackage.memberScope
val symbol = resolveInScope(functionName, scope) ?: resolveInScope(functionName, rootScope)
if (symbol == null) {
@@ -135,23 +155,23 @@ fun DocumentationBuilder.functionBody(descriptor: DeclarationDescriptor, functio
}
val text = when (psiElement) {
- is JetDeclarationWithBody -> ContentBlockCode().let() {
+ is KtDeclarationWithBody -> ContentBlockCode().let() {
val bodyExpression = psiElement.bodyExpression
when (bodyExpression) {
- is JetBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
+ is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
else -> bodyExpression!!.text
}
}
else -> psiElement.text
}
- val lines = text.trimEnd().split("\n".toRegex()).toTypedArray().filterNot { it.length() == 0 }
+ val lines = text.trimEnd().split("\n".toRegex()).toTypedArray().filterNot { it.length == 0 }
val indent = lines.map { it.takeWhile { it.isWhitespace() }.count() }.min() ?: 0
- val finalText = lines.map { it.drop(indent) }.join("\n")
+ val finalText = lines.map { it.drop(indent) }.joinToString("\n")
return ContentBlockCode("kotlin").let() { it.append(ContentText(finalText)); it }
}
-private fun DocumentationBuilder.resolveInScope(functionName: String, scope: JetScope): DeclarationDescriptor? {
+private fun DocumentationBuilder.resolveInScope(functionName: String, scope: KtScope): DeclarationDescriptor? {
var currentScope = scope
val parts = functionName.split('.')
@@ -169,7 +189,7 @@ private fun DocumentationBuilder.resolveInScope(functionName: String, scope: Jet
currentScope = if (partSymbol is ClassDescriptor)
partSymbol.defaultType.memberScope
else
- getResolutionScope(resolutionFacade, partSymbol)
+ getResolutionScope(resolutionFacade, partSymbol).asJetScope()
symbol = partSymbol
}
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index 6bfeb360..2d3105ac 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -8,23 +8,28 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
-import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.kdoc.KDocFinder
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
-import org.jetbrains.kotlin.lexer.JetSingleValueToken
+import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.JetParameter
+import org.jetbrains.kotlin.psi.KtModifierListOwner
+import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.ConstantValue
+import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.ErrorUtils
-import org.jetbrains.kotlin.types.JetType
+import org.jetbrains.kotlin.types.KtType
import org.jetbrains.kotlin.types.TypeProjection
-import org.jetbrains.kotlin.types.expressions.OperatorConventions
public data class DocumentationOptions(val includeNonPublic: Boolean = false,
val reportUndocumented: Boolean = true,
@@ -33,8 +38,8 @@ public data class DocumentationOptions(val includeNonPublic: Boolean = false,
val sourceLinks: List<SourceLinkDefinition>)
private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
- val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
- val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
+ val package1 = DescriptorUtils.getParentOfType(descriptor1, PackageFragmentDescriptor::class.java)
+ val package2 = DescriptorUtils.getParentOfType(descriptor2, PackageFragmentDescriptor::class.java)
return package1 != null && package2 != null && package1.fqName == package2.fqName
}
@@ -47,6 +52,10 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
val boringBuiltinClasses = setOf(
"kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean",
"kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any")
+ val knownModifiers = setOf(
+ KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD,
+ KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD,
+ KtTokens.OVERRIDE_KEYWORD)
fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
val kdoc = KDocFinder.findKDoc(descriptor) ?: findStdlibKDoc(descriptor)
@@ -101,9 +110,10 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
deepestDescriptor = deepestDescriptor.overriddenDescriptors.first()
}
if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") {
- val anyClassDescriptors = session.getTopLevelClassDescriptors(FqName.fromSegments(listOf("kotlin", "Any")))
+ val anyClassDescriptors = session.getTopLevelClassDescriptors(FqName.fromSegments(listOf("kotlin", "Any")),
+ NoLookupLocation.UNSORTED)
anyClassDescriptors.forEach {
- val anyMethod = it.getMemberScope(listOf()).getFunctions(descriptor.name).single()
+ val anyMethod = it.getMemberScope(listOf()).getFunctions(descriptor.name, NoLookupLocation.UNSORTED).single()
val kdoc = KDocFinder.findKDoc(anyMethod)
if (kdoc != null) {
return kdoc
@@ -115,7 +125,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any {
- DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.deprecated"
+ DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated"
} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated())
fun DeclarationDescriptor.signature(): String = when(this) {
@@ -142,13 +152,13 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
if (extensionReceiver != null) {
params.add(0, extensionReceiver.type)
}
- return "(" + params.map { it.signature() }.join() + ")"
+ return "(" + params.map { it.signature() }.joinToString() + ")"
}
- fun JetType.signature(): String {
+ fun KtType.signature(): String {
val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>"
val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString()
- if (typeName == "Array" && arguments.size() == 1) {
+ if (typeName == "Array" && arguments.size == 1) {
return "Array<" + arguments.first().type.signature() + ">"
}
return typeName
@@ -203,12 +213,12 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
return symbol
}
- fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, javaClass<KDocTag>()) ?: arrayOf()
+ fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf()
private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) {
val subjectName = seeTag.getSubjectName()
if (subjectName != null) {
- val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null)
+ val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null)
val link = resolveContentLink(descriptor, subjectName)
link.append(ContentText(subjectName))
val para = ContentParagraph()
@@ -217,8 +227,8 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
}
- fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
- refGraph.link(node, descriptor.signature(), DocumentationReference.Kind.Link)
+ fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
+ refGraph.link(node, descriptor.signature(), kind)
}
fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: DocumentationReference.Kind) {
@@ -231,7 +241,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
refGraph.register(descriptor.signature(), node)
}
- fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ fun <T> DocumentationNode(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
val doc = parseDocumentation(descriptor)
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
return node
@@ -255,7 +265,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
modality = Modality.FINAL
}
}
- val modifier = modality.name().toLowerCase()
+ val modifier = modality.name.toLowerCase()
appendTextNode(modifier, DocumentationNode.Kind.Modifier)
}
@@ -269,12 +279,14 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
for (superType in superTypes) {
if (!ignoreSupertype(superType)) {
appendType(superType, DocumentationNode.Kind.Supertype)
- link(superType?.constructor?.declarationDescriptor, descriptor, DocumentationReference.Kind.Inheritor)
+ val superclass = superType?.constructor?.declarationDescriptor
+ link(superclass, descriptor, DocumentationReference.Kind.Inheritor)
+ link(descriptor, superclass, DocumentationReference.Kind.Superclass)
}
}
}
- private fun ignoreSupertype(superType: JetType): Boolean {
+ private fun ignoreSupertype(superType: KtType): Boolean {
val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
if (superClass != null) {
val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
@@ -284,10 +296,15 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
- appendType(projection.type, kind, projection.projectionKind.label)
+ if (projection.isStarProjection) {
+ appendTextNode("*", Kind.Type)
+ }
+ else {
+ appendType(projection.type, kind, projection.projectionKind.label)
+ }
}
- fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
+ fun DocumentationNode.appendType(jetType: KtType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
if (jetType == null)
return
val classifierDescriptor = jetType.constructor.declarationDescriptor
@@ -311,28 +328,53 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
if (jetType.isMarkedNullable) {
node.appendTextNode("?", Kind.NullabilityModifier)
}
- if (classifierDescriptor != null && !classifierDescriptor.isBoringBuiltinClass()) {
- link(node, classifierDescriptor)
+ if (classifierDescriptor != null) {
+ link(node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) DocumentationReference.Kind.HiddenLink else DocumentationReference.Kind.Link)
}
append(node, DocumentationReference.Kind.Detail)
- for (typeArgument in jetType.arguments)
+ node.appendAnnotations(jetType)
+ for (typeArgument in jetType.arguments) {
node.appendProjection(typeArgument)
+ }
}
fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean =
DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses
fun DocumentationNode.appendAnnotations(annotated: Annotated) {
- annotated.annotations.forEach {
+ annotated.annotations.filter { it.isDocumented() }.forEach {
val annotationNode = it.build()
if (annotationNode != null) {
append(annotationNode,
- if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
+ if (annotationNode.isDeprecation()) DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
+ }
+ }
+ }
+
+ private fun AnnotationDescriptor.isDocumented(): Boolean {
+ if (source.getPsi() != null && mustBeDocumented()) return true
+ val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString()
+ return annotationClassName == "kotlin.Extension"
+ }
+
+ fun AnnotationDescriptor.mustBeDocumented(): Boolean {
+ val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false
+ return annotationClass.isDocumentedAnnotation()
+ }
+
+ fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
+ val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
+ KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ if (psi.hasModifier(it)) {
+ appendTextNode(it.value, Kind.Modifier)
}
}
}
+ fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated"
+
fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
appendSourceLink(sourceElement.getPsi(), options.sourceLinks)
}
@@ -364,8 +406,18 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
- fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
- descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
+ fun DocumentationNode.appendMembers(descriptors: Iterable<DeclarationDescriptor>) {
+ descriptors.forEach { descriptor ->
+ if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
+ val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull()
+ if (baseDescriptor != null) {
+ link(this, baseDescriptor, DocumentationReference.Kind.InheritedMember)
+ }
+ }
+ else {
+ appendChild(descriptor, DocumentationReference.Kind.Member)
+ }
+ }
}
fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
@@ -381,7 +433,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor) &&
!ErrorUtils.isError(extensionClassDescriptor)) {
- val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
+ val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
return externalClassNodes.getOrPut(fqName, {
val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
append(newNode, DocumentationReference.Kind.Member)
@@ -436,26 +488,23 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
else -> Kind.Class
}
val node = DocumentationNode(this, kind)
- if (isInner) {
- node.appendTextNode("inner", Kind.Modifier)
- }
node.appendSupertypes(this)
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, DocumentationReference.Kind.Detail)
val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS)
- constructors.filter { it.valueParameters.size() > 0 }
+ constructors.filter { it.valueParameters.size > 0 }
else
constructors
- node.appendChildren(constructorsToDocument, DocumentationReference.Kind.Member)
+ node.appendMembers(constructorsToDocument)
}
val members = defaultType.memberScope.getAllDescriptors().filter { it != companionObjectDescriptor }
- node.appendChildren(members, DocumentationReference.Kind.Member)
+ node.appendMembers(members)
val companionObjectDescriptor = companionObjectDescriptor
if (companionObjectDescriptor != null) {
- node.appendChildren(companionObjectDescriptor.defaultType.memberScope.getAllDescriptors(),
- DocumentationReference.Kind.Member)
+ node.appendMembers(companionObjectDescriptor.defaultType.memberScope.getAllDescriptors())
}
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(source)
register(this, node)
return node
@@ -498,8 +547,8 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
node.appendInPageChildren(valueParameters, DocumentationReference.Kind.Detail)
node.appendType(returnType)
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(source)
- node.appendOperatorOverloadNote(this)
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -520,64 +569,13 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
}
- fun DocumentationNode.appendOperatorOverloadNote(descriptor: FunctionDescriptor) {
- val operatorName = descriptor.getImplementedOperator()
- if (operatorName != null) {
- val content = Content()
- content.append(ContentText("Implements "))
- content.strong {
- text("operator ")
- code {
- text(operatorName)
- }
- }
- val noteNode = DocumentationNode("", content, DocumentationNode.Kind.OverloadGroupNote)
- append(noteNode, DocumentationReference.Kind.Detail)
- }
- }
-
- fun FunctionDescriptor.getImplementedOperator(): String? {
- var arity = valueParameters.size()
- if (containingDeclaration is ClassDescriptor) {
- arity++
- }
- if (extensionReceiverParameter != null) {
- arity++
- }
-
- val token = if (arity == 2) {
- OperatorConventions.BINARY_OPERATION_NAMES.inverse()[name] ?:
- OperatorConventions.ASSIGNMENT_OPERATIONS.inverse()[name] ?:
- OperatorConventions.BOOLEAN_OPERATIONS.inverse()[name]
- } else if (arity == 1) {
- OperatorConventions.UNARY_OPERATION_NAMES.inverse()[name]
- }
- else null
-
- if (token is JetSingleValueToken) {
- return token.value
- }
-
- val name = name.asString()
- if (arity == 2 && name == "contains") {
- return "in"
- }
- if (arity >= 2 && (name == "get" || name == "set")) {
- return "[]"
- }
- if (arity == 2 && name == "equals" && valueParameters.size() == 1 &&
- KotlinBuiltIns.isNullableAny(valueParameters.first().type)) {
- return "=="
- }
- return null
- }
-
fun PropertyDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, if (inCompanionObject()) Kind.CompanionObjectProperty else Kind.Property)
node.appendInPageChildren(typeParameters, DocumentationReference.Kind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendType(returnType)
node.appendAnnotations(this)
+ node.appendModifiers(this)
node.appendSourceLink(source)
if (isVar) {
node.appendTextNode("var", DocumentationNode.Kind.Modifier)
@@ -617,15 +615,9 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
fun ValueParameterDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, Kind.Parameter)
- val varargType = varargElementType
- if (varargType != null) {
- node.appendTextNode("vararg", Kind.Annotation, DocumentationReference.Kind.Annotation)
- node.appendType(varargType)
- } else {
- node.appendType(type)
- }
- if (hasDefaultValue()) {
- val psi = source.getPsi() as? JetParameter
+ node.appendType(varargElementType ?: type)
+ if (declaresDefaultValue()) {
+ val psi = source.getPsi() as? KtParameter
if (psi != null) {
val defaultValueText = psi.defaultValue?.text
if (defaultValueText != null) {
@@ -634,6 +626,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
}
node.appendAnnotations(this)
+ node.appendModifiers(this)
register(this, node)
return node
}
@@ -647,11 +640,14 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
if (prefix != "") {
node.appendTextNode(prefix, Kind.Modifier)
}
+ if (isReified) {
+ node.appendTextNode("reified", Kind.Modifier)
+ }
- val builtIns = KotlinBuiltIns.getInstance()
for (constraint in upperBounds) {
- if (constraint == builtIns.defaultBound)
+ if (KotlinBuiltIns.isDefaultBound(constraint)) {
continue
+ }
node.appendType(constraint, Kind.UpperBound)
}
@@ -683,7 +679,7 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
return null
}
val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, DocumentationNode.Kind.Annotation)
- val arguments = allValueArguments.toList().sortBy { it.first.index }
+ val arguments = allValueArguments.toList().sortedBy { it.first.index }
arguments.forEach {
val valueNode = it.second.toDocumentationNode()
if (valueNode != null) {
@@ -695,6 +691,11 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
return node
}
+ fun CompileTimeConstant<Any?>.build(): DocumentationNode? = when (this) {
+ is TypedCompileTimeConstant -> constantValue.toDocumentationNode()
+ else -> null
+ }
+
fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value?.let { value ->
when (value) {
is String ->
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index 75675c6f..3c4b974f 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -6,7 +6,7 @@ import org.jetbrains.dokka.LanguageService.RenderMode
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
class KotlinLanguageService : LanguageService {
- private val visibilityModifiers = setOf("public", "protected", "private")
+ private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -21,9 +21,9 @@ class KotlinLanguageService : LanguageService {
DocumentationNode.Kind.EnumItem,
DocumentationNode.Kind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
- DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node)
+ DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node, renderMode)
DocumentationNode.Kind.Type,
- DocumentationNode.Kind.UpperBound -> renderType(node)
+ DocumentationNode.Kind.UpperBound -> renderType(node, renderMode)
DocumentationNode.Kind.Modifier -> renderModifier(node)
DocumentationNode.Kind.Constructor,
@@ -43,6 +43,77 @@ class KotlinLanguageService : LanguageService {
}
}
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
+ if (nodes.size < 2) return null
+ val receiverKind = nodes.getReceiverKind() ?: return null
+ val functionWithTypeParameter = nodes.firstOrNull { it.details(DocumentationNode.Kind.TypeParameter).any() } ?: return null
+ return content {
+ val typeParameter = functionWithTypeParameter.details(DocumentationNode.Kind.TypeParameter).first()
+ if (functionWithTypeParameter.kind == DocumentationNode.Kind.Function) {
+ renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ }
+ else {
+ renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ }
+ }
+ }
+
+ private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? {
+ val qNames = map { it.getReceiverQName() }.filterNotNull()
+ if (qNames.size != size)
+ return null
+
+ return ReceiverKind.values.firstOrNull { kind -> qNames.all { it in kind.classes } }
+ }
+
+ private fun DocumentationNode.getReceiverQName(): String? {
+ if (kind != DocumentationNode.Kind.Function && kind != DocumentationNode.Kind.Property) return null
+ val receiver = details(DocumentationNode.Kind.Receiver).singleOrNull() ?: return null
+ val receiverType = receiver.detail(DocumentationNode.Kind.Type)
+ return (receiverType.links.firstOrNull() ?: receiverType.hiddenLinks.firstOrNull())?.qualifiedName()
+ }
+
+ companion object {
+ private val arrayClasses = setOf(
+ "kotlin.Array",
+ "kotlin.BooleanArray",
+ "kotlin.ByteArray",
+ "kotlin.CharArray",
+ "kotlin.ShortArray",
+ "kotlin.IntArray",
+ "kotlin.LongArray",
+ "kotlin.FloatArray",
+ "kotlin.DoubleArray"
+ )
+
+ private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
+
+ private val iterableClasses = setOf(
+ "kotlin.Collection",
+ "kotlin.Sequence",
+ "kotlin.Iterable",
+ "kotlin.Map",
+ "kotlin.String",
+ "kotlin.CharSequence") + arrayOrListClasses
+ }
+
+ private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
+ ARRAY("any_array", arrayClasses),
+ ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses),
+ ITERABLE("any_iterable", iterableClasses),
+ }
+
+ interface SignatureMapper {
+ fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
+ }
+
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
+ to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
+ to.text("<$typeParameterName>")
+ }
+ }
+
private fun ContentBlock.renderPackage(node: DocumentationNode) {
keyword("package")
text(" ")
@@ -75,42 +146,36 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderType(node: DocumentationNode) {
- val typeArguments = node.details(DocumentationNode.Kind.Type)
+ private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
+ var typeArguments = node.details(DocumentationNode.Kind.Type)
if (node.name == "Function${typeArguments.count() - 1}") {
// lambda
- symbol("(")
- renderList(typeArguments.take(typeArguments.size() - 1), noWrap = true) {
- renderType(it)
+ val isExtension = node.annotations.any { it.name == "Extension" }
+ if (isExtension) {
+ renderType(typeArguments.first(), renderMode)
+ symbol(".")
+ typeArguments = typeArguments.drop(1)
}
- symbol(")")
- nbsp()
- symbol("->")
- nbsp()
- renderType(typeArguments.last())
- return
- }
- if (node.name == "ExtensionFunction${typeArguments.count() - 2}") {
- // extension lambda
- renderType(typeArguments.first())
- symbol(".")
symbol("(")
- renderList(typeArguments.drop(1).take(typeArguments.size() - 2), noWrap = true) {
- renderType(it)
+ renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
+ renderType(it, renderMode)
}
symbol(")")
nbsp()
symbol("->")
nbsp()
- renderType(typeArguments.last())
+ renderType(typeArguments.last(), renderMode)
return
}
- renderSingleModifier(node)
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode, true)
renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
if (typeArguments.any()) {
symbol("<")
renderList(typeArguments, noWrap = true) {
- renderType(it)
+ renderType(it, renderMode)
}
symbol(">")
}
@@ -120,18 +185,23 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifier(node: DocumentationNode) {
+ private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
when (node.name) {
- "final", "internal", "var" -> {}
+ "final", "public", "var" -> {}
else -> {
keyword(node.name)
- text(" ")
+ if (nowrap) {
+ nbsp()
+ }
+ else {
+ text(" ")
+ }
}
}
}
- private fun ContentBlock.renderTypeParameter(node: DocumentationNode) {
- renderSingleModifier(node)
+ private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode, true)
identifier(node.name)
@@ -141,26 +211,20 @@ class KotlinLanguageService : LanguageService {
symbol(":")
nbsp()
renderList(constraints, noWrap=true) {
- renderType(it)
+ renderType(it, renderMode)
}
}
}
-
- private fun ContentBlock.renderSingleModifier(node: DocumentationNode) {
- val modifier = node.details(DocumentationNode.Kind.Modifier).singleOrNull()
- if (modifier != null) {
- keyword(modifier.name)
- nbsp()
+ private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
}
- }
-
- private fun ContentBlock.renderParameter(node: DocumentationNode) {
- renderAnnotationsForNode(node)
+ renderModifiersForNode(node, renderMode)
identifier(node.name, IdentifierKind.ParameterName)
symbol(":")
nbsp()
val parameterType = node.detail(DocumentationNode.Kind.Type)
- renderType(parameterType)
+ renderType(parameterType, renderMode)
val valueNode = node.details(DocumentationNode.Kind.Value).firstOrNull()
if (valueNode != null) {
nbsp()
@@ -170,38 +234,41 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) {
+ private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) {
val typeParameters = node.details(DocumentationNode.Kind.TypeParameter)
if (typeParameters.any()) {
symbol("<")
renderList(typeParameters) {
- renderTypeParameter(it)
+ renderTypeParameter(it, renderMode)
}
symbol(">")
}
}
- private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode) {
+ private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
val supertypes = node.details(DocumentationNode.Kind.Supertype)
if (supertypes.any()) {
nbsp()
symbol(":")
nbsp()
renderList(supertypes) {
- renderType(it)
+ indentedSoftLineBreak()
+ renderType(it, renderMode)
}
}
}
- private fun ContentBlock.renderModifiersForNode(node: DocumentationNode, renderMode: RenderMode) {
+ private fun ContentBlock.renderModifiersForNode(node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean = false) {
val modifiers = node.details(DocumentationNode.Kind.Modifier)
for (it in modifiers) {
if (node.kind == org.jetbrains.dokka.DocumentationNode.Kind.Interface && it.name == "abstract")
continue
- if (renderMode == RenderMode.SUMMARY && it.name in visibilityModifiers) {
+ if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) {
continue
}
- renderModifier(it)
+ renderModifier(it, nowrap)
}
}
@@ -212,7 +279,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
- identifier(node.name, IdentifierKind.AnnotationName)
+ identifier("@" + node.name, IdentifierKind.AnnotationName)
val parameters = node.details(DocumentationNode.Kind.Parameter)
if (!parameters.isEmpty()) {
symbol("(")
@@ -225,82 +292,109 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
- DocumentationNode.Kind.Class -> keyword("class ")
+ DocumentationNode.Kind.Class,
+ DocumentationNode.Kind.AnnotationClass,
+ DocumentationNode.Kind.Enum -> keyword("class ")
DocumentationNode.Kind.Interface -> keyword("interface ")
- 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")
}
identifierOrDeprecated(node)
- renderTypeParametersForNode(node)
- renderSupertypesForNode(node)
+ renderTypeParametersForNode(node, renderMode)
+ renderSupertypesForNode(node, renderMode)
}
- private fun ContentBlock.renderFunction(node: DocumentationNode, renderMode: RenderMode) {
+ private fun ContentBlock.renderFunction(node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name)
DocumentationNode.Kind.Function,
DocumentationNode.Kind.CompanionObjectFunction -> keyword("fun ")
else -> throw IllegalArgumentException("Node $node is not a function-like object")
}
- renderTypeParametersForNode(node)
+ renderTypeParametersForNode(node, renderMode)
if (node.details(DocumentationNode.Kind.TypeParameter).any()) {
text(" ")
}
- val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull()
- if (receiver != null) {
- renderType(receiver.detail(DocumentationNode.Kind.Type))
- symbol(".")
- }
+
+ renderReceiver(node, renderMode, signatureMapper)
if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor)
identifierOrDeprecated(node)
symbol("(")
- renderList(node.details(DocumentationNode.Kind.Parameter)) {
- renderParameter(it)
+ val parameters = node.details(DocumentationNode.Kind.Parameter)
+ renderList(parameters) {
+ indentedSoftLineBreak()
+ renderParameter(it, renderMode)
}
- symbol(")")
if (needReturnType(node)) {
+ if (parameters.isNotEmpty()) {
+ softLineBreak()
+ }
+ symbol(")")
symbol(": ")
- renderType(node.detail(DocumentationNode.Kind.Type))
+ renderType(node.detail(DocumentationNode.Kind.Type), renderMode)
+ }
+ else {
+ symbol(")")
+ }
+ }
+
+ private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) {
+ val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull()
+ if (receiver != null) {
+ if (signatureMapper != null) {
+ signatureMapper.renderReceiver(receiver, this)
+ } else {
+ renderType(receiver.detail(DocumentationNode.Kind.Type), renderMode)
+ }
+ symbol(".")
}
}
private fun needReturnType(node: DocumentationNode) = when(node.kind) {
DocumentationNode.Kind.Constructor -> false
- else -> true
+ else -> !node.isUnitReturnType()
}
- private fun ContentBlock.renderProperty(node: DocumentationNode, renderMode: RenderMode) {
+ fun DocumentationNode.isUnitReturnType(): Boolean =
+ detail(DocumentationNode.Kind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
+
+ private fun ContentBlock.renderProperty(node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Property,
DocumentationNode.Kind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ")
else -> throw IllegalArgumentException("Node $node is not a property")
}
- renderTypeParametersForNode(node)
+ renderTypeParametersForNode(node, renderMode)
if (node.details(DocumentationNode.Kind.TypeParameter).any()) {
text(" ")
}
- val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull()
- if (receiver != null) {
- renderType(receiver.detail(DocumentationNode.Kind.Type))
- symbol(".")
- }
+
+ renderReceiver(node, renderMode, signatureMapper)
identifierOrDeprecated(node)
symbol(": ")
- renderType(node.detail(DocumentationNode.Kind.Type))
+ renderType(node.detail(DocumentationNode.Kind.Type), renderMode)
}
fun DocumentationNode.getPropertyKeyword() =
diff --git a/src/Languages/JavaLanguageService.kt b/src/Languages/JavaLanguageService.kt
index 488a2dc4..5750588e 100644
--- a/src/Languages/JavaLanguageService.kt
+++ b/src/Languages/JavaLanguageService.kt
@@ -1,6 +1,6 @@
package org.jetbrains.dokka
-import org.jetbrains.dokka.DocumentationNode.*
+import org.jetbrains.dokka.DocumentationNode.Kind
import org.jetbrains.dokka.LanguageService.RenderMode
/**
@@ -34,6 +34,8 @@ public class JavaLanguageService : LanguageService {
}
}
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
+
private fun renderPackage(node: DocumentationNode): String {
return "package ${node.name}"
}
@@ -77,7 +79,7 @@ public class JavaLanguageService : LanguageService {
return if (constraints.none())
node.name
else {
- node.name + " extends " + constraints.map { renderType(node) }.join()
+ node.name + " extends " + constraints.map { renderType(node) }.joinToString()
}
}
@@ -86,11 +88,11 @@ public class JavaLanguageService : LanguageService {
}
private fun renderTypeParametersForNode(node: DocumentationNode): String {
- return StringBuilder {
+ return StringBuilder().apply {
val typeParameters = node.details(Kind.TypeParameter)
if (typeParameters.any()) {
append("<")
- append(typeParameters.map { renderTypeParameter(it) }.join())
+ append(typeParameters.map { renderTypeParameter(it) }.joinToString())
append("> ")
}
}.toString()
@@ -100,11 +102,11 @@ public class JavaLanguageService : LanguageService {
val modifiers = node.details(Kind.Modifier).map { renderModifier(it) }.filter { it != "" }
if (modifiers.none())
return ""
- return modifiers.join(" ", postfix = " ")
+ return modifiers.joinToString(" ", postfix = " ")
}
private fun renderClass(node: DocumentationNode): String {
- return StringBuilder {
+ return StringBuilder().apply {
when (node.kind) {
Kind.Class -> append("class ")
Kind.Interface -> append("interface ")
@@ -120,7 +122,7 @@ public class JavaLanguageService : LanguageService {
}
private fun renderFunction(node: DocumentationNode): String {
- return StringBuilder {
+ return StringBuilder().apply {
when (node.kind) {
Kind.Constructor -> append(node.owner?.name)
Kind.Function -> {
@@ -144,7 +146,7 @@ public class JavaLanguageService : LanguageService {
}
private fun renderProperty(node: DocumentationNode): String {
- return StringBuilder {
+ return StringBuilder().apply {
when (node.kind) {
Kind.Property -> append("val ")
else -> throw IllegalArgumentException("Node $node is not a property")
diff --git a/src/Languages/LanguageService.kt b/src/Languages/LanguageService.kt
index c587335a..b0f4bbc9 100644
--- a/src/Languages/LanguageService.kt
+++ b/src/Languages/LanguageService.kt
@@ -12,13 +12,19 @@ interface LanguageService {
}
/**
- * Renders a [node](DocumentationNode) as a class, function, property or other signature in a target language.
- * $node: A [DocumentationNode] to render
- * $returns: [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService]
+ * Renders a [node] as a class, function, property or other signature in a target language.
+ * @param node A [DocumentationNode] to render
+ * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService]
*/
fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode
/**
+ * Tries to summarize the signatures of the specified documentation nodes in a compact representation.
+ * Returns the representation if successful, or null if the signatures could not be summarized.
+ */
+ fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode?
+
+ /**
* Renders [node] as a named representation in the target language
*
* For example:
diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt
index 13023a2c..99caddc4 100644
--- a/src/Markdown/MarkdownProcessor.kt
+++ b/src/Markdown/MarkdownProcessor.kt
@@ -1,9 +1,11 @@
package org.jetbrains.dokka
-import org.intellij.markdown.*
-import org.intellij.markdown.ast.*
-import org.intellij.markdown.parser.*
-import org.intellij.markdown.parser.dialects.commonmark.CommonMarkMarkerProcessor
+import org.intellij.markdown.IElementType
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.ast.ASTNode
+import org.intellij.markdown.ast.LeafASTNode
+import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
+import org.intellij.markdown.parser.MarkdownParser
class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) {
val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) }
@@ -11,7 +13,7 @@ class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: S
val text: String get() = markdown.substring(node.startOffset, node.endOffset)
fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }
- override fun toString(): String = StringBuilder { presentTo(this) }.toString()
+ override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString()
}
fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) {
@@ -44,5 +46,5 @@ private fun MarkdownNode.presentTo(sb: StringBuilder) {
fun parseMarkdown(markdown: String): MarkdownNode {
if (markdown.isEmpty())
return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown)
- return MarkdownNode(MarkdownParser(CommonMarkMarkerProcessor.Factory).buildMarkdownTreeFromString(markdown), null, markdown)
+ return MarkdownNode(MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown), null, markdown)
}
diff --git a/src/Model/Content.kt b/src/Model/Content.kt
index 0bb78454..45d42a2d 100644
--- a/src/Model/Content.kt
+++ b/src/Model/Content.kt
@@ -1,10 +1,14 @@
package org.jetbrains.dokka
-public abstract class ContentNode
+public interface ContentNode {
+ val textLength: Int
+}
-public object ContentEmpty : ContentNode()
+public object ContentEmpty : ContentNode {
+ override val textLength: Int get() = 0
+}
-public open class ContentBlock() : ContentNode() {
+public open class ContentBlock() : ContentNode {
val children = arrayListOf<ContentNode>()
fun append(node : ContentNode) {
@@ -18,21 +22,58 @@ public open class ContentBlock() : ContentNode() {
override fun hashCode(): Int =
children.hashCode()
+
+ override val textLength: Int
+ get() = children.sumBy { it.textLength }
}
enum class IdentifierKind {
TypeName,
ParameterName,
AnnotationName,
+ SummarizedTypeName,
Other
}
-public data class ContentText(val text: String) : ContentNode()
-public data class ContentKeyword(val text: String) : ContentNode()
-public data class ContentIdentifier(val text: String, val kind: IdentifierKind = IdentifierKind.Other) : ContentNode()
-public data class ContentSymbol(val text: String) : ContentNode()
-public data class ContentEntity(val text: String) : ContentNode()
-public object ContentNonBreakingSpace: ContentNode()
+public data class ContentText(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+public data class ContentKeyword(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+public data class ContentIdentifier(val text: String, val kind: IdentifierKind = IdentifierKind.Other) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+public data class ContentSymbol(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+public data class ContentEntity(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+public object ContentNonBreakingSpace: ContentNode {
+ override val textLength: Int
+ get() = 1
+}
+
+public object ContentSoftLineBreak: ContentNode {
+ override val textLength: Int
+ get() = 0
+}
+
+public object ContentIndentedSoftLineBreak: ContentNode {
+ override val textLength: Int
+ get() = 0
+}
public class ContentParagraph() : ContentBlock()
public class ContentEmphasis() : ContentBlock()
@@ -101,6 +142,9 @@ fun ContentBlock.keyword(value: String) = append(ContentKeyword(value))
fun ContentBlock.symbol(value: String) = append(ContentSymbol(value))
fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other) = append(ContentIdentifier(value, kind))
fun ContentBlock.nbsp() = append(ContentNonBreakingSpace)
+fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak)
+fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak)
+
fun ContentBlock.strong(body: ContentBlock.() -> Unit) {
val strong = ContentStrong()
strong.body()
@@ -171,9 +215,6 @@ public open class MutableContent() : Content() {
return "<empty>"
return (listOf(summary, description) + sections).joinToString()
}
-
- val isEmpty: Boolean
- get() = sections.none()
}
fun javadocSectionDisplayName(sectionName: String?): String? =
diff --git a/src/Model/DocumentationModule.kt b/src/Model/DocumentationModule.kt
deleted file mode 100644
index e74c544b..00000000
--- a/src/Model/DocumentationModule.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.jetbrains.dokka
-
-public class DocumentationModule(name: String, content: Content = Content.Empty) : DocumentationNode(name, content, DocumentationNode.Kind.Module) {
- fun merge(other: DocumentationModule): DocumentationModule {
- val model = DocumentationModule(name)
- model.addAllReferencesFrom(other)
- model.addAllReferencesFrom(this)
- return model
- }
-}
-
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index c3b9942d..4a77f761 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -1,6 +1,6 @@
package org.jetbrains.dokka
-import java.util.LinkedHashSet
+import java.util.*
public open class DocumentationNode(val name: String,
content: Content,
@@ -19,6 +19,8 @@ public open class DocumentationNode(val name: String,
get() = references(DocumentationReference.Kind.Detail).map { it.to }
public val members: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Member).map { it.to }
+ public val inheritedMembers: List<DocumentationNode>
+ get() = references(DocumentationReference.Kind.InheritedMember).map { it.to }
public val extensions: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Extension).map { it.to }
public val inheritors: List<DocumentationNode>
@@ -27,6 +29,8 @@ public open class DocumentationNode(val name: String,
get() = references(DocumentationReference.Kind.Override).map { it.to }
public val links: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Link).map { it.to }
+ public val hiddenLinks: List<DocumentationNode>
+ get() = references(DocumentationReference.Kind.HiddenLink).map { it.to }
public val annotations: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Annotation).map { it.to }
public val deprecation: DocumentationNode?
@@ -50,6 +54,7 @@ public open class DocumentationNode(val name: String,
public fun details(kind: DocumentationNode.Kind): List<DocumentationNode> = details.filter { it.kind == kind }
public fun members(kind: DocumentationNode.Kind): List<DocumentationNode> = members.filter { it.kind == kind }
+ public fun inheritedMembers(kind: DocumentationNode.Kind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
public fun links(kind: DocumentationNode.Kind): List<DocumentationNode> = links.filter { it.kind == kind }
public fun detail(kind: DocumentationNode.Kind): DocumentationNode = details.filter { it.kind == kind }.single()
@@ -112,6 +117,10 @@ public open class DocumentationNode(val name: String,
}
+public class DocumentationModule(name: String, content: Content = Content.Empty)
+ : DocumentationNode(name, content, DocumentationNode.Kind.Module) {
+}
+
val DocumentationNode.path: List<DocumentationNode>
get() {
val parent = owner ?: return listOf(this)
@@ -137,6 +146,7 @@ fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationRefere
DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
+ else -> { /* Do not add any links back for other types */ }
}
}
@@ -145,3 +155,5 @@ fun DocumentationNode.appendTextNode(text: String,
refKind: DocumentationReference.Kind = DocumentationReference.Kind.Detail) {
append(DocumentationNode(text, Content.Empty, kind), refKind)
}
+
+fun DocumentationNode.qualifiedName() = path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt
index a61ac65f..79cec2f3 100644
--- a/src/Model/DocumentationReference.kt
+++ b/src/Model/DocumentationReference.kt
@@ -4,10 +4,13 @@ public data class DocumentationReference(val from: DocumentationNode, val to: Do
public enum class Kind {
Owner,
Member,
+ InheritedMember,
Detail,
Link,
+ HiddenLink,
Extension,
Inheritor,
+ Superclass,
Override,
Annotation,
Deprecation,
diff --git a/src/Model/PackageDocs.kt b/src/Model/PackageDocs.kt
index b5b34942..99da327b 100644
--- a/src/Model/PackageDocs.kt
+++ b/src/Model/PackageDocs.kt
@@ -5,7 +5,7 @@ import org.intellij.markdown.MarkdownTokenTypes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import java.io.File
-public class PackageDocs(val documentationBuilder: DocumentationBuilder,
+public class PackageDocs(val documentationBuilder: DocumentationBuilder?,
val linkResolveContext: DeclarationDescriptor?,
val logger: DokkaLogger) {
public val moduleContent: MutableContent = MutableContent()
@@ -13,17 +13,17 @@ public class PackageDocs(val documentationBuilder: DocumentationBuilder,
public val packageContent: Map<String, Content>
get() = _packageContent
- fun parse(path: String) {
- val file = File(path)
+ fun parse(fileName: String) {
+ val file = File(fileName)
if (file.exists()) {
val text = file.readText()
val tree = parseMarkdown(text)
var targetContent: MutableContent = moduleContent
tree.children.forEach {
if (it.type == MarkdownElementTypes.ATX_1) {
- val headingText = it.child(MarkdownTokenTypes.TEXT)?.text
+ val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text
if (headingText != null) {
- targetContent = findTargetContent(headingText)
+ targetContent = findTargetContent(headingText.trimStart())
}
} else {
buildContentTo(it, targetContent, { resolveContentLink(it) })
@@ -39,7 +39,7 @@ public class PackageDocs(val documentationBuilder: DocumentationBuilder,
return moduleContent
}
if (heading.startsWith("Package") || heading.startsWith("package")) {
- return findOrCreatePackageContent(heading.substring("package".length()).trim())
+ return findOrCreatePackageContent(heading.substring("package".length).trim())
}
return findOrCreatePackageContent(heading)
}
@@ -48,7 +48,7 @@ public class PackageDocs(val documentationBuilder: DocumentationBuilder,
_packageContent.getOrPut(packageName) { -> MutableContent() }
private fun resolveContentLink(href: String): ContentBlock {
- if (linkResolveContext != null) {
+ if (linkResolveContext != null && documentationBuilder != null) {
return documentationBuilder.resolveContentLink(linkResolveContext, href)
}
return ContentExternalLink("#")
diff --git a/src/Model/SourceLinks.kt b/src/Model/SourceLinks.kt
index 4530518f..956bfe4b 100644
--- a/src/Model/SourceLinks.kt
+++ b/src/Model/SourceLinks.kt
@@ -15,7 +15,7 @@ fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<Sourc
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())
+ var url = linkDef.url + path.substring(linkDef.path.length)
if (linkDef.lineSuffix != null) {
val line = target?.lineNumber()
if (line != null) {
@@ -50,8 +50,7 @@ fun PsiElement.lineNumber(): Int? {
}
fun PsiElement.columnNumber(): Int? {
- val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile)
- // IJ uses 0-based line-numbers; external source browsers use 1-based
- val lineNumber = doc?.getLineNumber(textRange.startOffset)?.plus(1) ?: return null
- return startOffset - doc!!.getLineStartOffset(lineNumber)
+ val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) ?: return null
+ val lineNumber = doc.getLineNumber(textRange.startOffset)
+ return startOffset - doc.getLineStartOffset(lineNumber)
} \ No newline at end of file
diff --git a/src/Utilities/GuiceModule.kt b/src/Utilities/GuiceModule.kt
index 4ce4863d..57bad468 100644
--- a/src/Utilities/GuiceModule.kt
+++ b/src/Utilities/GuiceModule.kt
@@ -45,14 +45,14 @@ class GuiceModule(val config: DokkaGenerator) : Module {
}
-private inline fun <reified T> Binder.registerCategory(category: String) {
+private inline fun <reified T: Any> Binder.registerCategory(category: String) {
ServiceLocator.allServices(category).forEach {
- @suppress("UNCHECKED_CAST")
+ @Suppress("UNCHECKED_CAST")
bind(javaClass<T>()).annotatedWith(Names.named(it.name)).to(javaClass<T>().classLoader.loadClass(it.className) as Class<T>)
}
}
-private inline fun <reified Base, reified T : Base> Binder.bindNameAnnotated(name: String) {
+private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotated(name: String) {
bind(javaClass<Base>()).annotatedWith(Names.named(name)).to(javaClass<T>())
}
diff --git a/src/Utilities/Path.kt b/src/Utilities/Path.kt
index fea22250..36277d9f 100644
--- a/src/Utilities/Path.kt
+++ b/src/Utilities/Path.kt
@@ -1,18 +1,19 @@
package org.jetbrains.dokka
-import java.io.*
+import java.io.File
+import java.io.IOException
fun File.getRelativePath(name: File): File {
val parent = parentFile ?: throw IOException("No common directory")
- val basePath = getCanonicalPath() + File.separator;
- val targetPath = name.getCanonicalPath();
+ val basePath = canonicalPath + File.separator;
+ val targetPath = name.canonicalPath;
if (targetPath.startsWith(basePath)) {
- return File(targetPath.substring(basePath.length()))
+ return File(targetPath.substring(basePath.length))
} else {
return File(".." + File.separator + parent.getRelativePath(name))
}
}
-fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(getPath() + "." + extension)
+fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(path + "." + extension)
diff --git a/src/Utilities/ServiceLocator.kt b/src/Utilities/ServiceLocator.kt
index b3610a53..bc04238f 100644
--- a/src/Utilities/ServiceLocator.kt
+++ b/src/Utilities/ServiceLocator.kt
@@ -2,7 +2,7 @@ package org.jetbrains.dokka.Utilities
import org.jetbrains.dokka.DokkaGenerator
import java.io.File
-import java.util.Properties
+import java.util.*
import java.util.jar.JarFile
import java.util.zip.ZipEntry
@@ -16,7 +16,7 @@ public object ServiceLocator {
val loadedClass = javaClass.classLoader.loadClass(descriptor.className)
val constructor = loadedClass.constructors
.filter { it.parameterTypes.isEmpty() || (it.parameterTypes.size() == 1 && conf.javaClass.isInstance(it.parameterTypes[0])) }
- .sortDescendingBy { it.parameterTypes.size() }
+ .sortedByDescending { it.parameterTypes.size() }
.firstOrNull() ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
val implementationRawType: Any = if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor)
@@ -25,7 +25,7 @@ public object ServiceLocator {
throw ServiceLookupException("Class ${descriptor.className} is not a subtype of ${clazz.name}")
}
- @suppress("UNCHECKED_CAST")
+ @Suppress("UNCHECKED_CAST")
return implementationRawType as T
}
@@ -35,7 +35,7 @@ public object ServiceLocator {
throw ServiceLookupException("Class $className is not a subtype of ${clazz.name}")
}
- @suppress("UNCHECKED_CAST")
+ @Suppress("UNCHECKED_CAST")
val casted = loaded as Class<T>
casted
@@ -89,6 +89,12 @@ public inline fun <reified T : Any> ServiceLocator.lookupOrNull(category: String
null
}
+fun main(args: Array<String>) {
+ ServiceLocator.allServices("format").forEach {
+ println(it)
+ }
+}
+
private val ZipEntry.fileName: String
get() = name.substringAfterLast("/", name)
diff --git a/src/main.kt b/src/main.kt
index 29a22672..a534ffdd 100644
--- a/src/main.kt
+++ b/src/main.kt
@@ -22,46 +22,46 @@ import java.io.File
import kotlin.util.measureTimeMillis
class DokkaArguments {
- Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)")
- ValueDescription("<path>")
+ @set:Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)")
+ @ValueDescription("<path>")
public var src: String = ""
- Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code")
- ValueDescription("<path>=<url>[#lineSuffix]")
+ @set:Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code")
+ @ValueDescription("<path>=<url>[#lineSuffix]")
public var srcLink: String = ""
- Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)")
- ValueDescription("<path>")
+ @set:Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)")
+ @ValueDescription("<path>")
public var include: String = ""
- Argument(value = "samples", description = "Source root for samples")
- ValueDescription("<path>")
+ @set:Argument(value = "samples", description = "Source root for samples")
+ @ValueDescription("<path>")
public var samples: String = ""
- Argument(value = "output", description = "Output directory path")
- ValueDescription("<path>")
+ @set:Argument(value = "output", description = "Output directory path")
+ @ValueDescription("<path>")
public var outputDir: String = "out/doc/"
- Argument(value = "format", description = "Output format (text, html, markdown, jekyll, kotlin-website)")
- ValueDescription("<name>")
+ @set:Argument(value = "format", description = "Output format (text, html, markdown, jekyll, kotlin-website)")
+ @ValueDescription("<name>")
public var outputFormat: String = "html"
- Argument(value = "module", description = "Name of the documentation module")
- ValueDescription("<name>")
+ @set:Argument(value = "module", description = "Name of the documentation module")
+ @ValueDescription("<name>")
public var moduleName: String = ""
- Argument(value = "classpath", description = "Classpath for symbol resolution")
- ValueDescription("<path>")
+ @set:Argument(value = "classpath", description = "Classpath for symbol resolution")
+ @ValueDescription("<path>")
public var classpath: String = ""
- Argument(value = "nodeprecated", description = "Exclude deprecated members from documentation")
+ @set:Argument(value = "nodeprecated", description = "Exclude deprecated members from documentation")
public var nodeprecated: Boolean = false
}
private fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
val (path, urlAndLine) = srcLink.split('=')
- return SourceLinkDefinition(File(path).getAbsolutePath(),
+ return SourceLinkDefinition(File(path).absolutePath,
urlAndLine.substringBefore("#"),
urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
}
@@ -182,7 +182,7 @@ class DokkaGenerator(val logger: DokkaLogger,
}
fun isSample(file: PsiFile): Boolean {
- val sourceFile = File(file.getVirtualFile()!!.getPath())
+ val sourceFile = File(file.virtualFile!!.path)
return samples.none { sample ->
val canonicalSample = File(sample).canonicalPath
val canonicalSource = sourceFile.canonicalPath
@@ -199,7 +199,7 @@ fun buildDocumentationModule(environment: AnalysisEnvironment,
logger: DokkaLogger): DocumentationModule {
val documentation = environment.withContext { environment, resolutionFacade, session ->
val fragmentFiles = environment.getSourceFiles().filter(filesToDocumentFilter)
- val fragments = fragmentFiles.map { session.getPackageFragment(it.getPackageFqName()) }.filterNotNull().distinct()
+ val fragments = fragmentFiles.map { session.getPackageFragment(it.packageFqName) }.filterNotNull().distinct()
val refGraph = NodeReferenceGraph()
val documentationBuilder = DocumentationBuilder(resolutionFacade, session, options, refGraph, logger)
@@ -235,7 +235,7 @@ fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> {
val result = arrayListOf<PsiJavaFile>()
val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file")
sourceRoots.forEach { sourceRoot ->
- sourceRoot.getAbsoluteFile().walkTopDown().forEach {
+ sourceRoot.absoluteFile.walkTopDown().forEach {
val vFile = localFileSystem.findFileByPath(it.path)
if (vFile != null) {
val psiFile = PsiManager.getInstance(project).findFile(vFile)