diff options
Diffstat (limited to 'src')
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/> " + 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 == "&" || text == """ || text == "<" || text == ">") { + return ContentEntity(text) + } + if (text == "&") { + return ContentEntity("&") + } + 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) |