diff options
author | Kamil Doległo <kamilok1965@interia.pl> | 2019-10-31 12:10:10 +0100 |
---|---|---|
committer | Kamil Doległo <kamilok1965@interia.pl> | 2019-10-31 12:10:10 +0100 |
commit | 7c30624e3d0868346823b15b5cea5e29a56357f2 (patch) | |
tree | 5cc8175510c907d23f7bdcb5d5617ef03b587965 /core/src/main/kotlin | |
parent | 57830827b9bc13aad7af6ab899f25b278b248ef7 (diff) | |
download | dokka-7c30624e3d0868346823b15b5cea5e29a56357f2.tar.gz dokka-7c30624e3d0868346823b15b5cea5e29a56357f2.tar.bz2 dokka-7c30624e3d0868346823b15b5cea5e29a56357f2.zip |
Remove all unnecessary files
Diffstat (limited to 'core/src/main/kotlin')
28 files changed, 0 insertions, 4097 deletions
diff --git a/core/src/main/kotlin/Formats/FormatService.kt b/core/src/main/kotlin/Formats/FormatService.kt deleted file mode 100644 index 8f4855e3..00000000 --- a/core/src/main/kotlin/Formats/FormatService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.jetbrains.dokka - -/** - * Abstract representation of a formatting service used to output documentation in desired format - * - * Bundled Formatters: - * * [HtmlFormatService] – outputs documentation to HTML format - * * [MarkdownFormatService] – outputs documentation in Markdown format - */ -interface FormatService { - /** Returns extension for output files */ - val extension: String - - /** extension which will be used for internal and external linking */ - val linkExtension: String - get() = extension - - fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder - - fun enumerateSupportFiles(callback: (resource: String, targetPath: String) -> Unit) { - } -} - -interface FormattedOutputBuilder { - /** Appends formatted content */ - fun appendNodes(nodes: Iterable<DocumentationNode>) -} - -/** Format content to [String] using specified [location] */ -fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply { - createOutputBuilder(this, location).appendNodes(nodes) -}.toString() diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt deleted file mode 100644 index 036ec856..00000000 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class GFMOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String> -) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendTable(vararg columns: String, body: () -> Unit) { - to.appendln(columns.joinToString(" | ", "| ", " |")) - to.appendln("|" + "---|".repeat(columns.size)) - body() - } - - override fun appendUnorderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ul", body) - } else { - super.appendUnorderedList(body) - } - } - - override fun appendOrderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ol", body) - } else { - super.appendOrderedList(body) - } - } - - override fun appendListItem(body: () -> Unit) { - if (inTableCell) { - wrapInTag("li", body) - } else { - super.appendListItem(body) - } - } -} - -open class GFMFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt deleted file mode 100644 index d36ea0a2..00000000 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ /dev/null @@ -1,166 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - -open class HtmlOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>, - val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - override fun appendText(text: String) { - to.append(text.htmlEscape()) - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>") - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - val openTags = if (language.isNotBlank()) - "<pre><code class=\"lang-$language\">" - else - "<pre><code>" - wrap(openTags, "</code></pre>", body) - } - - override fun appendHeader(level: Int, body: () -> Unit) = - wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true) - override fun appendParagraph(body: () -> Unit) = - wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true) - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - override fun appendLine() { - to.appendln("<br/>") - } - - override fun appendAnchor(anchor: String) { - to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>") - } - - override fun appendTable(vararg columns: String, body: () -> Unit) = - wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableBody(body: () -> Unit) = - wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableRow(body: () -> Unit) = - wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableCell(body: () -> Unit) = - wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true) - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body) - override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body) - override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) - override fun appendCode(body: () -> Unit) = wrapInTag("code", body) - - override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true) - override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true) - override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true) - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location)) - super.appendNodes(nodes) - templateService.appendFooter(to) - } - - override fun appendNonBreakingSpace() { - to.append(" ") - } - - override fun ensureParagraph() {} -} - -open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - val templateService: HtmlTemplateService, - @Named(impliedPlatformsName) val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) { - callback("/dokka/styles/style.css", "style.css") - } - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) - - override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location)) - super.appendOutline(location, to, nodes) - templateService.appendFooter(to) - } - - override fun getOutlineFileName(location: Location): File { - return File("${location.path}-outline.html") - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val link = ContentNodeDirectLink(node) - link.append(languageService.render(node, LanguageService.RenderMode.FULL)) - val tempBuilder = StringBuilder() - createOutputBuilder(tempBuilder, location).appendContent(link) - to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - to.appendln("<ul>") - body() - to.appendln("</ul>") - } -} - -fun getPageTitle(nodes: Iterable<DocumentationNode>): String? { - val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } - return breakdownByLocation.keys.singleOrNull() -} - -fun formatPageTitle(node: DocumentationNode): String { - val path = node.path - val moduleName = path.first().name - if (path.size == 1) { - return moduleName - } - - val qName = qualifiedNameForPageTitle(node) - return "$qName - $moduleName" -} - -private fun qualifiedNameForPageTitle(node: DocumentationNode): String { - if (node.kind == NodeKind.Package) { - var packageName = node.qualifiedName() - if (packageName.isEmpty()) { - packageName = "root package" - } - return packageName - } - - val path = node.path - var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike } - if (pathFromToplevelMember.isEmpty()) { - pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function } - } - if (pathFromToplevelMember.isNotEmpty()) { - return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".") - } - return node.qualifiedName() -} diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt deleted file mode 100644 index a65a7b18..00000000 --- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface HtmlTemplateService { - fun appendHeader(to: StringBuilder, title: String?, basePath: File) - fun appendFooter(to: StringBuilder) - - companion object { - fun default(css: String? = null): HtmlTemplateService { - return object : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) { - if (!to.endsWith('\n')) { - to.append('\n') - } - to.appendln("</BODY>") - to.appendln("</HTML>") - } - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) { - to.appendln("<HTML>") - to.appendln("<HEAD>") - to.appendln("<meta charset=\"UTF-8\">") - if (title != null) { - to.appendln("<title>$title</title>") - } - if (css != null) { - val cssPath = basePath.resolve(css).toUnixString() - to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">") - } - to.appendln("</HEAD>") - to.appendln("<BODY>") - } - } - } - } -} - - diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt deleted file mode 100644 index 09bb2602..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.jetbrains.dokka.Formats - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject -import org.jetbrains.kotlin.types.KotlinType - -class JavaLayoutHtmlPackageListService: PackageListService { - - private fun StringBuilder.appendParam(name: String, value: String) { - append(DOKKA_PARAM_PREFIX) - append(name) - append(":") - appendln(value) - } - - override fun formatPackageList(module: DocumentationModule): String { - val packages = module.members(NodeKind.Package).map { it.name } - - return buildString { - appendParam("format", "java-layout-html") - appendParam("mode", "kotlin") - for (p in packages) { - appendln(p) - } - } - } - -} - -class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - private fun getContainerPath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/" - is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html" - else -> null - } - } - - private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor = - generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first() - - private fun ClassifierDescriptor.nameWithOuter(): String = - generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor } - .toList().asReversed().joinToString(".") { it.name.asString() } - - private fun getPagePath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html" - is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded() - is ClassifierDescriptor -> getContainerPath(symbol) + "#" - is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded() - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchor(): String? { - - fun ReceiverParameterDescriptor.extractReceiverName(): String { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if (receiverClass.isCompanionObject()) { - receiverClass = receiverClass.containingDeclaration!! - } else if (receiverClass is TypeParameterDescriptor) { - val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor - if (upperBoundClass != null) { - receiverClass = upperBoundClass - } - } - - return receiverClass.name.asString() - } - - fun KotlinType.qualifiedNameForSignature(): String { - val desc = constructor.declarationDescriptor - return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>" - } - - fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) { - if (desc.containingDeclaration.isCompanionObject()) { - append("Companion.") - } - desc.extensionReceiverParameter?.let { - append("(") - append(it.extractReceiverName()) - append(").") - } - } - - return when(this) { - is EnumEntrySyntheticClassDescriptor -> buildString { - append("ENUM_VALUE:") - append(name.asString()) - } - is FunctionDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - valueParameters.joinTo(this, prefix = "(", postfix = ")") { - it.type.qualifiedNameForSignature() - } - } - is PropertyDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - append(":") - append(returnType?.qualifiedNameForSignature()) - } - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded() - - override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt deleted file mode 100644 index 885cdf6c..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import com.google.inject.Inject -import kotlinx.html.li -import kotlinx.html.stream.appendHTML -import kotlinx.html.ul -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import java.io.File - - -class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponent { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType generatorServiceClass - } - - val formatServiceClass = JavaLayoutHtmlFormatService::class - val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class -} - - -class JavaLayoutHtmlFormatService : FormatService { - override val extension: String - get() = TODO("not implemented") - - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder { - TODO("not implemented") - } -} - -class JavaLayoutHtmlFormatOutputBuilder : FormattedOutputBuilder { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - - } -} - - -class JavaLayoutHtmlFormatNavListBuilder @Inject constructor() : OutlineFormatService { - override fun getOutlineFileName(location: Location): File { - TODO() - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - with(to.appendHTML()) { - //a(href = ) - li { - when { - node.kind == NodeKind.Package -> appendOutline(location, to, node.members) - } - } - } - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - with(to.appendHTML()) { - ul { body() } - } - } - -} - -class JavaLayoutHtmlFormatGenerator @Inject constructor( - private val outlineFormatService: OutlineFormatService -) : Generator { - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - if (node.kind == NodeKind.Module) { - //outlineFormatService.formatOutline() - } - } - } - - override fun buildSupportFiles() {} - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt deleted file mode 100644 index a948dfa9..00000000 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class JekyllOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - to.appendln("---") - appendFrontMatter(nodes, to) - to.appendln("---") - to.appendln("") - super.appendNodes(nodes) - } - - protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) { - to.appendln("title: ${getPageTitle(nodes)}") - } -} - - -open class JekyllFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "html", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) - -} diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt deleted file mode 100644 index 3cdea156..00000000 --- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt +++ /dev/null @@ -1,264 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - - -object EmptyHtmlTemplateService : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) {} - - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {} -} - - -open class KotlinWebsiteHtmlOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - val impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) { - private var needHardLineBreaks = false - private var insideDiv = 0 - - override fun appendLine() {} - - override fun appendBreadcrumbs(path: Iterable<FormatLink>) { - if (path.count() > 1) { - to.append("<div class='api-docs-breadcrumbs'>") - super.appendBreadcrumbs(path) - to.append("</div>") - } - } - - override fun appendSinceKotlin(version: String) { - } - - override fun appendSinceKotlinWrapped(version: String) { - } - - override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body) - - protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) { - to.append("<div class=\"$cssClass\"$otherAttributes") - to.append(">") - insideDiv++ - block() - insideDiv-- - to.append("</div>\n") - } - - override fun appendAsSignature(node: ContentNode, block: () -> Unit) { - val contentLength = node.textLength - if (contentLength == 0) return - div(to, "signature") { - needHardLineBreaks = contentLength >= 62 - try { - block() - } finally { - needHardLineBreaks = false - } - } - } - - override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - div(to, "overload-group", calculateDataAttributes(platforms)) { - block() - } - } - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendTable(vararg columns: String, body: () -> Unit) { - //to.appendln("<table class=\"api-docs-table\">") - div(to, "api-declarations-list") { - body() - } - //to.appendln("</table>") - } - - override fun appendTableBody(body: () -> Unit) { - //to.appendln("<tbody>") - body() - //to.appendln("</tbody>") - } - - override fun appendTableRow(body: () -> Unit) { - //to.appendln("<tr>") - body() - //to.appendln("</tr>") - } - - override fun appendTableCell(body: () -> Unit) { -// to.appendln("<td>") - body() -// to.appendln("\n</td>") - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>") - } - - override fun appendSoftLineBreak() { - if (needHardLineBreaks) - to.append("<br/>") - } - - override fun appendIndentedSoftLineBreak() { - if (needHardLineBreaks) { - to.append("<br/> ") - } - } - - private fun identifierClassName(kind: IdentifierKind) = when (kind) { - IdentifierKind.ParameterName -> "parameterName" - IdentifierKind.SummarizedTypeName -> "summarizedTypeName" - else -> "identifier" - } - - private data class PlatformsForElement( - val platformToVersion: Map<String, String> - ) - - private fun calculatePlatforms(platforms: PlatformsData): PlatformsForElement { - //val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)?.removePrefix("Kotlin ") - val jreVersion = platforms.keys.filter(String::isJREVersion).min()?.takeUnless { it.endsWith("6") } - val targetPlatforms = platforms.filterNot { it.key.isJREVersion() } + - listOfNotNull(jreVersion?.let { it to platforms[it]!! }) - - return PlatformsForElement( - targetPlatforms.mapValues { (_, nodes) -> effectiveSinceKotlinForNodes(nodes) } - ) - } - - private fun calculateDataAttributes(platforms: PlatformsData): String { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - val (platformNames, versions) = platformToVersion.toList().unzip() - return "data-platform=\"${platformNames.joinToString()}\" "+ - "data-kotlin-version=\"${versions.joinToString()}\"" - } - - override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { -// if (platforms.isNotEmpty()) -// wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) -// else -// appendTableRow(block) - div(to, "declarations", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - } - - override fun appendPlatforms(platforms: PlatformsData) { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - div(to, "tags") { - div(to, "spacer") {} - platformToVersion.entries.sortedBy { - platformSortWeight(it.key) - }.forEach { (platform, version) -> - div(to, "tags__tag platform tag-value-$platform", - otherAttributes = " data-tag-version=\"$version\"") { - to.append(platform) - } - } - div(to, "tags__tag kotlin-version") { - to.append(mergeVersions(platformToVersion.values.toList())) - } - } - } - - override fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - div(to, "node-page-main", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - - } - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendPlatformsAsText(platforms: PlatformsData) { - appendHeader(5) { - val filtered = platforms.keys.filterNot { it.isJREVersion() }.sortedBy { platformSortWeight(it) } - if (filtered.isNotEmpty()) { - to.append("For ") - filtered.joinTo(to) - } - } - } - - override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { - div(to, "sample", otherAttributes = " data-min-compiler-version=\"1.3\"") { - appendBlockCode(language) { - imports() - wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") { - wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) - } - } - } - } - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - - override fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendText(" ") - appendContent(section) - } - } - - override fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - if (platforms.isNotEmpty()) - wrap("<div ${calculateDataAttributes(platforms)}>", "</div>") { - block(platforms) - } - else - block(platforms) - } - - override fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - div(to, "summary-group", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block(platforms) - } - } - - fun platformSortWeight(name: String) = when(name.toLowerCase()) { - "common" -> 0 - "jvm" -> 1 - "js" -> 3 - "native" -> 4 - else -> 2 // This is hack to support JRE/JUnit and so on - } -} - -class KotlinWebsiteHtmlFormatService @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) {} - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) -} - - -private fun String.isKotlinVersion() = this.startsWith("Kotlin") -private fun String.isJREVersion() = this.startsWith("JRE", ignoreCase=true)
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt deleted file mode 100644 index 216dd2ef..00000000 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ /dev/null @@ -1,250 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.util.* - -enum class ListKind { - Ordered, - Unordered -} - -private class ListState(val kind: ListKind, var size: Int = 1) { - fun getTagAndIncrement() = when (kind) { - ListKind.Ordered -> "${size++}. " - else -> "* " - } -} - -private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() - -open class MarkdownOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - private val listStack = ArrayDeque<ListState>() - protected var inTableCell = false - protected var inCodeBlock = false - private var lastTableCellStart = -1 - private var maxBackticksInCodeBlock = 0 - - private fun appendNewline() { - while (to.endsWith(' ')) { - to.setLength(to.length - 1) - } - to.appendln() - } - - private fun ensureNewline() { - if (inTableCell && listStack.isEmpty()) { - if (to.length != lastTableCellStart && !to.endsWith("<br>")) { - to.append("<br>") - } - } - else { - if (!endsWithNewline()) { - appendNewline() - } - } - } - - private fun endsWithNewline(): Boolean { - var index = to.length - 1 - while (index > 0) { - val c = to[index] - if (c != ' ') { - return c == '\n' - } - index-- - } - return false - } - - override fun ensureParagraph() { - if (!to.endsWith(TWO_LINE_BREAKS)) { - if (!to.endsWith('\n')) { - appendNewline() - } - appendNewline() - } - } - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - private val backTickFindingRegex = """(`+)""".toRegex() - - override fun appendText(text: String) { - if (inCodeBlock) { - to.append(text) - val backTicks = backTickFindingRegex.findAll(text) - val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0 - maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun) - } - else { - if (text == "\n" && inTableCell) { - to.append(" ") - } else { - to.append(text.htmlEscape()) - } - } - } - - override fun appendCode(body: () -> Unit) { - inCodeBlock = true - val codeBlockStart = to.length - maxBackticksInCodeBlock = 0 - - wrapIfNotEmpty("`", "`", body, checkEndsWith = true) - - if (maxBackticksInCodeBlock > 0) { - val extraBackticks = "`".repeat(maxBackticksInCodeBlock) - to.insert(codeBlockStart, extraBackticks) - to.append(extraBackticks) - } - - inCodeBlock = false - } - - override fun appendUnorderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Unordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendOrderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Ordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendListItem(body: () -> Unit) { - ensureNewline() - to.append(listStack.peek()?.getTagAndIncrement()) - body() - ensureNewline() - } - - override fun appendStrong(body: () -> Unit) = wrap("**", "**", body) - override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body) - override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body) - - override fun appendLink(href: String, body: () -> Unit) { - if (inCodeBlock) { - wrap("`[`", "`]($href)`", body) - } - else { - wrap("[", "]($href)", body) - } - } - - override fun appendLine() { - if (inTableCell) { - to.append("<br>") - } - else { - appendNewline() - } - } - - override fun appendAnchor(anchor: String) { - // no anchors in Markdown - } - - override fun appendParagraph(body: () -> Unit) { - when { - inTableCell -> { - ensureNewline() - body() - } - listStack.isNotEmpty() -> { - body() - ensureNewline() - } - else -> { - ensureParagraph() - body() - ensureParagraph() - } - } - } - - override fun appendHeader(level: Int, body: () -> Unit) { - when { - inTableCell -> { - body() - } - else -> { - ensureParagraph() - to.append("${"#".repeat(level)} ") - body() - ensureParagraph() - } - } - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - inCodeBlock = true - ensureParagraph() - to.appendln(if (language.isEmpty()) "```" else "``` $language") - body() - ensureNewline() - to.appendln("```") - appendLine() - inCodeBlock = false - } - - override fun appendTable(vararg columns: String, body: () -> Unit) { - ensureParagraph() - body() - ensureParagraph() - } - - override fun appendTableBody(body: () -> Unit) { - body() - } - - override fun appendTableRow(body: () -> Unit) { - to.append("|") - body() - appendNewline() - } - - override fun appendTableCell(body: () -> Unit) { - to.append(" ") - inTableCell = true - lastTableCellStart = to.length - body() - inTableCell = false - to.append(" |") - } - - override fun appendNonBreakingSpace() { - if (inCodeBlock) { - to.append(" ") - } - else { - to.append(" ") - } - } -} - -open class MarkdownFormatService(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) { - @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt deleted file mode 100644 index 958e93af..00000000 --- a/core/src/main/kotlin/Formats/OutlineService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -/** - * Service for building the outline of the package contents. - */ -interface OutlineFormatService { - fun getOutlineFileName(location: Location): File - - fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) - fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) - - /** Appends formatted outline to [StringBuilder](to) using specified [location] */ - fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - appendOutlineHeader(location, node, to) - if (node.members.any()) { - val sortedMembers = node.members.sortedBy { it.name.toLowerCase() } - appendOutlineLevel(to) { - appendOutline(location, to, sortedMembers) - } - } - } - } - - fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String = - StringBuilder().apply { appendOutline(location, this, nodes) }.toString() -} diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt deleted file mode 100644 index e675d927..00000000 --- a/core/src/main/kotlin/Formats/PackageListService.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - - -interface PackageListService { - fun formatPackageList(module: DocumentationModule): String -} - -class DefaultPackageListService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val formatService: FormatService -) : PackageListService { - - override fun formatPackageList(module: DocumentationModule): String { - val packages = mutableSetOf<String>() - val nonStandardLocations = mutableMapOf<String, String>() - - fun visit(node: DocumentationNode, relocated: Boolean = false) { - val nodeKind = node.kind - - when (nodeKind) { - NodeKind.Package -> { - packages.add(node.qualifiedName()) - node.members.forEach { visit(it) } - } - NodeKind.Signature -> { - if (relocated) - nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!) - } - NodeKind.ExternalClass -> { - node.members.forEach { visit(it, relocated = true) } - } - NodeKind.GroupNode -> { - if (node.members.isNotEmpty()) { - // Only nodes only has single file is need to be relocated - // TypeAliases for example - node.origins - .filter { it.members.isEmpty() } - .forEach { visit(it, relocated = true) } - } - } - else -> { - if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) { - node.details(NodeKind.Signature).forEach { visit(it, relocated) } - node.members.forEach { visit(it, relocated) } - } - } - } - } - - module.members.forEach { visit(it) } - - return buildString { - appendln("\$dokka.linkExtension:${formatService.linkExtension}") - - nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" } - .sorted().joinTo(this, separator = "\n", postfix = "\n") - - packages.sorted().joinTo(this, separator = "\n", postfix = "\n") - } - - } - -} - diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt deleted file mode 100644 index 86f70a37..00000000 --- a/core/src/main/kotlin/Formats/StandardFormats.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import kotlin.reflect.KClass - -abstract class KotlinFormatDescriptorBase - : FileGeneratorBasedFormatDescriptor(), - DefaultAnalysisComponent, - DefaultAnalysisComponentServices by KotlinAsKotlin { - override val generatorServiceClass = FileGenerator::class - override val outlineServiceClass: KClass<out OutlineFormatService>? = null - override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class -} - -abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class - override val generatorServiceClass = FileGenerator::class - override val packageListServiceClass = DefaultPackageListService::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") } - } -} - -class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin - -class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava - -class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = KotlinWebsiteHtmlFormatService::class - override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class - override val outlineServiceClass = YamlOutlineService::class - - override fun configureOutput(binder: Binder) = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService) - } -} - -class JekyllFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = JekyllFormatService::class -} - -class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = MarkdownFormatService::class -} - -class GFMFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = GFMFormatService::class -} diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt deleted file mode 100644 index 76f9366f..00000000 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ /dev/null @@ -1,1014 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode -import org.jetbrains.kotlin.utils.keysToMap -import java.util.* - -data class FormatLink(val text: String, val href: String) - -private data class Summarized( - val data: List<SummarizedBySummary> -) { - - constructor(data: Map<ContentNode, Map<ContentNode, List<DocumentationNode>>>) : this( - data.entries.map { (summary, signatureToMember) -> - SummarizedBySummary( - summary, - signatureToMember.map { (signature, nodes) -> - SummarizedNodes(signature, nodes) - } - ) - } - ) - - data class SummarizedNodes(val content: ContentNode, val nodes: List<DocumentationNode>) { - val platforms = effectivePlatformsForMembers(nodes) - } - data class SummarizedBySummary(val content: ContentNode, val signatures: List<SummarizedNodes>) { - val platforms = effectivePlatformsForMembers(signatures.flatMap { it.nodes }) - val platformsOnSignature = !samePlatforms(signatures.map { it.platforms }) - } - - - fun computePlatformLevel(): PlatformPlacement { - if (data.any { it.platformsOnSignature }) { - return PlatformPlacement.Signature - } - if (samePlatforms(data.map { it.platforms })) { - return PlatformPlacement.Row - } - return PlatformPlacement.Summary - } - val platformPlacement: PlatformPlacement = computePlatformLevel() - val platforms = effectivePlatformsForMembers(data.flatMap { it.signatures.flatMap { it.nodes } }) - - - enum class PlatformPlacement { - Row, Summary, Signature - } -} - -abstract class StructuredOutputBuilder(val to: StringBuilder, - val location: Location, - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - val extension: String, - impliedPlatforms: List<String>) : FormattedOutputBuilder { - - protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { - to.append(prefix) - body() - to.append(suffix) - } - - protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) { - val startLength = to.length - to.append(prefix) - body() - if (checkEndsWith && to.endsWith(suffix)) { - to.setLength(to.length - suffix.length) - } else if (to.length > startLength + prefix.length) { - to.append(suffix) - } else { - to.setLength(startLength) - } - } - - protected fun wrapInTag(tag: String, - body: () -> Unit, - newlineBeforeOpen: Boolean = false, - newlineAfterOpen: Boolean = false, - newlineAfterClose: Boolean = false) { - if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln() - to.append("<$tag>") - if (newlineAfterOpen) to.appendln() - body() - to.append("</$tag>") - if (newlineAfterClose) to.appendln() - } - - protected abstract fun ensureParagraph() - - open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body) - abstract fun appendBlockCode(language: String, body: () -> Unit) - abstract fun appendHeader(level: Int = 1, body: () -> Unit) - abstract fun appendParagraph(body: () -> Unit) - - open fun appendSoftParagraph(body: () -> Unit) { - ensureParagraph() - body() - } - - abstract fun appendLine() - abstract fun appendAnchor(anchor: String) - - abstract fun appendTable(vararg columns: String, body: () -> Unit) - abstract fun appendTableBody(body: () -> Unit) - abstract fun appendTableRow(body: () -> Unit) - abstract fun appendTableCell(body: () -> Unit) - - abstract fun appendText(text: String) - - open fun appendSinceKotlin(version: String) { - appendText("Since: ") - appendCode { appendText(version) } - } - - open fun appendSinceKotlinWrapped(version: String) { - wrap(" (", ")") { - appendSinceKotlin(version) - } - } - - open fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendLine() - appendContent(section) - } - } - - open fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - block(platforms) - } - - open fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - appendAsPlatformDependentBlock(platforms, block) - } - - open fun appendSymbol(text: String) { - appendText(text) - } - - open fun appendKeyword(text: String) { - appendText(text) - } - - open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - appendText(text) - } - - open fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - block() - } - - fun appendEntity(text: String) { - to.append(text) - } - - abstract fun appendLink(href: String, body: () -> Unit) - - open fun appendLink(link: FormatLink) { - appendLink(link.href) { appendText(link.text) } - } - - abstract fun appendStrong(body: () -> Unit) - abstract fun appendStrikethrough(body: () -> Unit) - abstract fun appendEmphasis(body: () -> Unit) - abstract fun appendCode(body: () -> Unit) - abstract fun appendUnorderedList(body: () -> Unit) - abstract fun appendOrderedList(body: () -> Unit) - abstract fun appendListItem(body: () -> Unit) - - abstract fun appendBreadcrumbSeparator() - abstract fun appendNonBreakingSpace() - open fun appendSoftLineBreak() { - } - - open fun appendIndentedSoftLineBreak() { - } - - fun appendContent(content: List<ContentNode>) { - for (contentNode in content) { - appendContent(contentNode) - } - } - - open fun appendContent(content: ContentNode) { - when (content) { - is ContentText -> appendText(content.text) - is ContentSymbol -> appendSymbol(content.text) - is ContentKeyword -> appendKeyword(content.text) - is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature) - is ContentNonBreakingSpace -> appendNonBreakingSpace() - is ContentSoftLineBreak -> appendSoftLineBreak() - is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak() - is ContentEntity -> appendEntity(content.text) - is ContentStrong -> appendStrong { appendContent(content.children) } - is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) } - is ContentCode -> appendCode { appendContent(content.children) } - is ContentEmphasis -> appendEmphasis { appendContent(content.children) } - is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) } - is ContentOrderedList -> appendOrderedList { appendContent(content.children) } - is ContentListItem -> appendListItem { - val child = content.children.singleOrNull() - if (child is ContentParagraph) { - appendContent(child.children) - } else { - appendContent(content.children) - } - } - - is NodeRenderContent -> { - val node = content.node - appendContent(languageService.render(node, content.mode)) - } - is ContentNodeLink -> { - val node = content.node - val linkTo = if (node != null) locationHref(location, node, generator) else "#" - appendLinkIfNotThisPage(linkTo, content) - } - is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content) - - is ContentParagraph -> { - if (!content.isEmpty()) { - appendParagraph { appendContent(content.children) } - } - } - - is ContentBlockSampleCode, is ContentBlockCode -> { - content as ContentBlockCode - fun ContentBlockCode.appendBlockCodeContent() { - children - .dropWhile { it is ContentText && it.text.isBlank() } - .forEach { appendContent(it) } - } - when (content) { - is ContentBlockSampleCode -> - appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent) { content.appendBlockCodeContent() } - is ContentBlockCode -> - appendBlockCode(content.language) { content.appendBlockCodeContent() } - } - } - is ContentHeading -> appendHeader(content.level) { appendContent(content.children) } - is ContentBlock -> appendContent(content.children) - } - } - - private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) { - if (href == ".") { - appendContent(content.children) - } else { - appendLink(href) { appendContent(content.children) } - } - } - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = link(from, to, extension, name) - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - extension: String, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = - FormatLink(name(to), generator.relativePathToLocation(from, to)) - - private fun DocumentationNode.isModuleOrPackage(): Boolean = - kind == NodeKind.Module || kind == NodeKind.Package - - protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) { - block() - } - - protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - block() - } - - protected open fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { - appendTableRow(block) - } - - protected open fun appendPlatformsAsText(platforms: PlatformsData) { - appendPlatforms(platforms) - } - - protected open fun appendPlatforms(platforms: PlatformsData) { - if (platforms.isNotEmpty()) { - appendText(platforms.keys.joinToString(prefix = "(", postfix = ") ")) - } - } - - protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) { - for ((index, item) in path.withIndex()) { - if (index > 0) { - appendBreadcrumbSeparator() - } - appendLink(item) - } - } - - fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> = - sections.filter { it.subjectName != null }.groupBy { it.tag } - - private fun ContentNode.appendSignature() { - if (this is ContentBlock && this.isEmpty()) { - return - } - - val signatureAsCode = ContentCode() - signatureAsCode.append(this) - appendContent(signatureAsCode) - } - - open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { - open fun build() { - val breakdownByLocation = nodes.groupBy { node -> - node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct() - } - - for ((path, nodes) in breakdownByLocation) { - if (!noHeader && path.isNotEmpty()) { - appendBreadcrumbs(path) - appendLine() - appendLine() - } - appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) - } - } - - private fun appendLocation(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - if (singleNode != null && singleNode.isModuleOrPackage()) { - if (singleNode.kind == NodeKind.Package) { - val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name - appendHeader(2) { appendText("Package $packageName") } - } - appendContent(singleNode.content) - } else { - val breakdownByName = nodes.groupBy { node -> node.name } - for ((name, items) in breakdownByName) { - if (!noHeader) - appendHeader { appendText(name) } - appendDocumentation(items, singleNode != null) - } - } - } - - private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) { - val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> - when (node.kind) { - NodeKind.GroupNode -> node.origins.map { it.content } - else -> node.content - } - } - - if (breakdownBySummary.size == 1) { - val node = breakdownBySummary.values.single() - appendAsNodeDescription(effectivePlatformsForMembers(node)) { - formatOverloadGroup(node, isSingleNode) - } - } else { - for ((_, items) in breakdownBySummary) { - appendAsOverloadGroup(to, effectivePlatformsForMembers(items)) { - formatOverloadGroup(items) - } - } - } - } - - private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) { - - val platformsPerGroup = samePlatforms( - items.flatMap { - if (it.kind == NodeKind.GroupNode) { - it.origins.groupBy { origin -> - languageService.render(origin) - }.values.map { origins -> effectivePlatformsForMembers(origins) } - } else { - listOf(effectivePlatformsForNode(it)) - } - } - ) - - if (platformsPerGroup) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(items)) { platforms -> - appendPlatforms(platforms) - } - } - for ((index, item) in items.withIndex()) { - if (index > 0) appendLine() - - if (item.kind == NodeKind.GroupNode) { - renderGroupNode(item, isSingleNode, !platformsPerGroup) - } else { - renderSimpleNode(item, isSingleNode, !platformsPerGroup) - } - - } - // All items have exactly the same documentation, so we can use any item to render it - val item = items.first() - // TODO: remove this block cause there is no one node with OverloadGroupNote detail - item.details(NodeKind.OverloadGroupNote).forEach { - appendContent(it.content) - } - - if (item.kind == NodeKind.GroupNode) { - val groupByContent = item.origins.groupBy { it.content } - if (groupByContent.count { !it.key.isEmpty() } > 1) { - if (groupByContent.size > 1) println("[mult] Found ov diff: ${generator.location(item).path}") - } - for ((content, origins) in groupByContent) { - if (content.isEmpty()) continue - appendAsPlatformDependentBlock(effectivePlatformsForMembers(origins)) { platforms -> - if (groupByContent.count { !it.key.isEmpty() } > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } else { - val platforms = effectivePlatformsForNode(item) - appendAsPlatformDependentBlock(platforms) { - appendContent(item.summary) - item.content.appendDescription() - } - } - } - - - fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(listOf(item))) { platforms -> - // TODO: use summarizesignatures - val rendered = languageService.render(item) - item.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - if (withPlatforms) { - appendPlatforms(platforms) - } - appendAsSignature(rendered) { - appendCode { appendContent(rendered) } - item.appendSourceLink() - } - item.appendOverrides() - item.appendDeprecation() - } - } - - fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - // TODO: use summarizesignatures - val groupBySignature = item.origins.groupBy { - languageService.render(it) - } - - for ((sign, nodes) in groupBySignature) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(nodes)) { platforms -> - val first = nodes.first() - first.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - - if (withPlatforms) { - appendPlatforms(platforms) - } - - appendAsSignature(sign) { - appendCode { appendContent(sign) } - } - first.appendOverrides() - first.appendDeprecation() - } - - } - } - - private fun DocumentationNode.appendSourceLink() { - val sourceUrl = details(NodeKind.SourceUrl).firstOrNull() - if (sourceUrl != null) { - to.append(" ") - appendLink(sourceUrl.name) { to.append("(source)") } - } - } - - private fun DocumentationNode.appendOverrides() { - overrides.forEach { - appendParagraph { - to.append("Overrides ") - val location = generator.relativePathToLocation(this, it) - - appendLink(FormatLink(it.owner!!.name + "." + it.name, location)) - } - } - } - - private fun DocumentationNode.appendDeprecation() { - if (deprecation != null) { - val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull() - val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull() - appendLine() - when { - deprecationValue != null -> { - appendStrong { to.append("Deprecated:") } - appendText(" " + deprecationValue.name.removeSurrounding("\"")) - appendLine() - appendLine() - } - deprecation?.content != Content.Empty -> { - appendStrong { to.append("Deprecated:") } - to.append(" ") - appendContent(deprecation!!.content) - } - else -> { - appendStrong { to.append("Deprecated") } - appendLine() - appendLine() - } - } - } - } - - -// protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members) -// NodeKind.GroupNode -> platformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.intersect(platformsOfItem) -// } -// } -// } -// -// protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.union(otherPlatforms) -// } -// } -// } - -// val DocumentationNode.platformsToShow: List<String> -// get() = platforms - - private fun Content.appendDescription() { - if (description != ContentEmpty) { - appendContent(description) - } - - - getSectionsWithSubjects().forEach { - appendSectionWithSubject(it.key, it.value) - } - - for (section in sections.filter { it.subjectName == null }) { - appendSectionWithTag(section) - } - } - - fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) { - appendHeader(3) { appendText(title) } - subjectSections.forEach { - val subjectName = it.subjectName - if (subjectName != null) { - appendSoftParagraph { - appendAnchor(subjectName) - appendCode { to.append(subjectName) } - to.append(" - ") - appendContent(it) - } - } - } - } - - fun appendOriginsGroupByContent(node: DocumentationNode) { - require(node.kind == NodeKind.GroupNode) - val groupByContent = - node.origins.groupBy { it.content } - .mapValues { (_, origins) -> - effectivePlatformsForMembers(origins) - } - .filterNot { it.key.isEmpty() } - .toList() - .sortedByDescending { it.second.size } - - if (groupByContent.size > 1) println("[mult] Found diff: ${generator.location(node).path}") - for ((content, platforms) in groupByContent) { - appendAsPlatformDependentBlock(platforms) { - if (groupByContent.size > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } - } - - inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) : - PageBuilder(listOf(node), noHeader) { - - override fun build() { - super.build() - SectionsBuilder(node).build() - } - } - - inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { - - override fun build() { - val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } - - appendBreadcrumbs(breakdownByLocation) - appendLine() - appendLine() - appendHeader { appendText(node.name) } - - appendAsNodeDescription(effectivePlatformsForNode(node)) { - renderGroupNode(node, true) - - appendOriginsGroupByContent(node) - } - - SectionsBuilder(node).build() - } - } -// -// private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.flatMapTo(mutableSetOf<String>()) { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platforms -// } -// } -// -// return platforms -// } - - - inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) { - override fun build() { - if (node.kind == NodeKind.ExternalClass) { - appendSection("Extensions for ${node.name}", node.members) - return - } - - fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { - return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) } - } - - fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { - return membersOrGroupMembers { it.kind == kind } - } - - appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) - appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike /*&& it.kind != NodeKind.TypeAlias*/ && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) - appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) - appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) - appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.membersOrGroupMembers(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) - appendSection("Constructors", node.membersOrGroupMembers(NodeKind.Constructor), omitSamePlatforms = true) - appendSection("Properties", node.membersOrGroupMembers(NodeKind.Property), omitSamePlatforms = true) - appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) - appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true) - appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) - appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.membersOrGroupMembers(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) - appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) - appendSection("Other members", node.members.filter { - it.kind !in setOf( - NodeKind.Class, - NodeKind.Interface, - NodeKind.Enum, - NodeKind.Object, - NodeKind.AnnotationClass, - NodeKind.Exception, - NodeKind.TypeAlias, - NodeKind.Constructor, - NodeKind.Property, - NodeKind.Package, - NodeKind.Function, - NodeKind.CompanionObjectProperty, - NodeKind.CompanionObjectFunction, - NodeKind.ExternalClass, - NodeKind.EnumItem, - NodeKind.AllTypes, - NodeKind.GroupNode - ) - }) - - val allExtensions = node.extensions - appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property }) - appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function }) - appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty }) - appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction }) - appendSection("Inheritors", - node.inheritors.filter { it.kind != NodeKind.EnumItem }) - - if (node.kind == NodeKind.Module) { - appendHeader(3) { to.append("Index") } - node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes -> - appendLink(link(node, allTypes) { "All Types" }) - } - } - } - - private fun appendSection(caption: String, members: List<DocumentationNode>, - sortMembers: Boolean = true, - omitSamePlatforms: Boolean = false, - platformsBasedOnMembers: Boolean = false) { - if (members.isEmpty()) return - - appendHeader(3) { appendText(caption) } - - val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members - val membersMap = children.groupBy { link(node, it) } - - - - appendTable("Name", "Summary") { - appendTableBody { - for ((memberLocation, membersList) in membersMap) { - val platforms = effectivePlatformsForMembers(membersList) -// val platforms = if (platformsBasedOnMembers) -// members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - val summarized = computeSummarySignatures(membersList) - - appendIndexRow(platforms) { - appendTableCell { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Row) { - appendPlatforms(platforms) - } - appendHeader(level = 4) { - // appendParagraph { - appendLink(memberLocation) - } - if (node.sinceKotlin != null) { - appendSinceKotlin(node.sinceKotlin.toString()) - } - - if (membersList.singleOrNull()?.sinceKotlin != null){ - appendSinceKotlinWrapped(membersList.single().sinceKotlin.toString()) - } -// } -// if (members.singleOrNull()?.kind != NodeKind.ExternalClass) { -// appendPlatforms(platforms) -// } -// } - } - appendTableCell { - appendSummarySignatures(summarized) - } - } - } - } - } - } - -// -// private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> { -// if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) { -// return unionPlatformsOfItems(items) -// } -// -// val platforms = platformsOfItems(items) -// if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { -// return platforms -// } -// return emptySet() -// } - - - - private fun computeSummarySignatures(items: List<DocumentationNode>): Summarized = - Summarized(items.groupBy { it.summary }.mapValues { (_, nodes) -> - val nodesToAppend = nodes.flatMap { if(it.kind == NodeKind.GroupNode) it.origins else listOf(it) } - - val summarySignature = languageService.summarizeSignatures(nodesToAppend) - if (summarySignature != null) { - mapOf(summarySignature to nodesToAppend) - } else { - nodesToAppend.groupBy { - languageService.render(it, RenderMode.SUMMARY) - } - } - }) - - - private fun appendSummarySignatures( - summarized: Summarized - ) { - for(summary in summarized.data) { - - appendAsSummaryGroup(summary.platforms) { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Summary) { - appendPlatforms(summary.platforms) - } - appendContent(summary.content) - summary.signatures.subList(0, summary.signatures.size - 1).forEach { - appendSignatures( - it, - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - appendLine() - } - appendSignatures( - summary.signatures.last(), - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - } - - } - } - - private fun appendSignatures( - signature: Summarized.SummarizedNodes, - withPlatforms: Boolean - ) { - -// val platforms = if (platformsBasedOnMembers) -// items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - - appendAsPlatformDependentBlock(signature.platforms) { - if (withPlatforms) { - appendPlatforms(signature.platforms) - } - appendAsSignature(signature.content) { - signature.content.appendSignature() - } - appendSoftLineBreak() - } - } - } - - private fun DocumentationNode.isClassLikeGroupNode(): Boolean { - if (kind != NodeKind.GroupNode) { - return false - } - - return origins.all { it.kind in NodeKind.classLike } - } - - - inner class AllTypesNodeBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { - - override fun build() { - appendContent(node.owner!!.summary) - appendHeader(3) { to.append("All Types") } - - appendTable("Name", "Summary") { - appendTableBody { - for (type in node.members) { - val platforms = effectivePlatformsForNode(type) - appendIndexRow(platforms) { - appendPlatforms(platforms) - if (type.kind == NodeKind.ExternalClass) { - val packageName = type.owner?.name - if (packageName != null) { - appendText(" (extensions in package $packageName)") - } - } - appendHeader(level = 5) { - appendLink(link(node, type) { - if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName() - }) - } - - appendContent(type.summary) - } - } - } - } - } - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - when (singleNode?.kind) { - NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() - NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() - null -> PageBuilder(nodes).build() - else -> SingleNodePageBuilder(singleNode).build() - } - } -} - -abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - override val extension: String, - override final val linkExtension: String = extension) : FormatService { - -} - -typealias PlatformsData = Map<String, Set<DocumentationNode>> - -fun memberPlatforms(node: DocumentationNode): PlatformsData { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - - return members.map(::effectivePlatformsForNode).fold(mapOf(), ::mergePlatforms) -} - -fun mergePlatforms(a: PlatformsData, b: PlatformsData): PlatformsData { - val mutable = a.toMutableMap() - b.forEach { (name, declarations) -> - mutable.merge(name, declarations) { a, b -> a.union(b) } - } - return mutable -} - -fun effectivePlatformsForNode(node: DocumentationNode): PlatformsData { - val platforms = node.platforms + memberPlatforms(node).keys - return platforms.keysToMap { setOf(node) } -} - -fun effectivePlatformsForMembers(nodes: Collection<DocumentationNode>): PlatformsData { - return nodes.map { effectivePlatformsForNode(it) }.reduce(::mergePlatforms) -} - -fun mergeVersions(kotlinVersions: List<String>): String { - return kotlinVersions.distinct().min().orEmpty() -} - -fun effectiveSinceKotlinForNode(node: DocumentationNode, baseVersion: String = "1.0"): String { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - val newBase = node.sinceKotlin ?: baseVersion - val memberVersion = if (members.isNotEmpty()) effectiveSinceKotlinForNodes(members, newBase) else newBase - - return node.sinceKotlin ?: memberVersion -} - -fun effectiveSinceKotlinForNodes(nodes: Collection<DocumentationNode>, baseVersion: String = "1.0"): String { - val map = nodes.map { effectiveSinceKotlinForNode(it, baseVersion) } - return mergeVersions(map) -} - -fun samePlatforms(platformsPerNode: Collection<PlatformsData>): Boolean { - - val first = platformsPerNode.firstOrNull()?.keys ?: return true - return platformsPerNode.all { it.keys == first } -} - -fun locationHref( - from: Location, - to: DocumentationNode, - generator: NodeLocationAwareGenerator, - pathOnly: Boolean = false -): String { - val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - val signature = to.detailOrNull(NodeKind.Signature) - return from.relativePathTo( - generator.location(topLevelPage), - (signature?.name ?: to.name).takeUnless { pathOnly } - ) - } - return from.relativePathTo(generator.location(to)) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt deleted file mode 100644 index 3c92d8ff..00000000 --- a/core/src/main/kotlin/Formats/YamlOutlineService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import java.io.File - -class YamlOutlineService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService -) : OutlineFormatService { - override fun getOutlineFileName(location: Location): File = File("${location.path}.yml") - - var outlineLevel = 0 - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent- title: ${languageService.renderName(node)}") - to.appendln("$indent url: ${generator.relativePathToLocation(node.path.first(), node)}") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent content:") - outlineLevel++ - body() - outlineLevel-- - } -} diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt deleted file mode 100644 index ee2c068e..00000000 --- a/core/src/main/kotlin/Generation/FileGenerator.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File -import java.io.IOException -import java.io.PrintWriter -import java.io.StringWriter - -class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator { - - @set:Inject(optional = true) var outlineService: OutlineFormatService? = null - @set:Inject(optional = true) lateinit var formatService: FormatService - @set:Inject(optional = true) lateinit var dokkaConfiguration: DokkaConfiguration - @set:Inject(optional = true) var packageListService: PackageListService? = null - - private val createdFiles = mutableMapOf<File, List<String>>() - - private fun File.writeFileAndAssert(context: String, action: (File) -> Unit) { - //TODO: there is a possible refactoring to drop FileLocation - //TODO: aad File from API, Location#path. - //TODO: turn [Location] into a final class, - //TODO: Use [Location] all over the place without full - //TODO: reference to the real target path, - //TODO: it opens the way to safely track all files created - //TODO: to make sure no files were overwritten by mistake - //TODO: also, the NodeLocationAwareGenerator should be removed - - val writes = createdFiles.getOrDefault(this, listOf()) + context - createdFiles[this] = writes - if (writes.size > 1) { - println("ERROR. An attempt to write ${this.relativeTo(root)} several times!") - return - } - - try { - parentFile?.mkdirsOrFail() - action(this) - } catch (e : Throwable) { - println("Failed to write $this. ${e.message}") - e.printStackTrace() - } - } - - private fun File.mkdirsOrFail() { - if (!mkdirs() && !exists()) { - throw IOException("Failed to create directory $this") - } - } - - override fun location(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node, formatService.linkExtension)) - } - - private fun fileForNode(node: DocumentationNode, extension: String = ""): File { - return File(root, relativePathToNode(node)).appendExtension(extension) - } - - private fun locationWithoutExtension(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node)) - } - - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) { - file.writeFileAndAssert("pages") { it -> - it.writeText(formatService.format(location(items.first()), items)) - } - - buildPages(items.filterNot { it.kind == NodeKind.AllTypes }.flatMap { it.members }) - } - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - val outlineService = this.outlineService ?: return - for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) { - outlineService.getOutlineFileName(location).writeFileAndAssert("outlines") { file -> - file.writeText(outlineService.formatOutline(location, items)) - } - } - } - - override fun buildSupportFiles() { - formatService.enumerateSupportFiles { resource, targetPath -> - File(root, relativePathToNode(listOf(targetPath), false)).writeFileAndAssert("support files") { file -> - file.outputStream().use { - javaClass.getResourceAsStream(resource).copyTo(it) - } - } - } - } - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - if (packageListService == null) return - - for (module in nodes) { - - val moduleRoot = location(module).file.parentFile - val packageListFile = File(moduleRoot, "package-list") - - val text = "\$dokka.format:${dokkaConfiguration.format}\n" + packageListService!!.formatPackageList(module as DocumentationModule) - - packageListFile.writeFileAndAssert("packages-list") { file -> - file.writeText(text) - } - } - } -} diff --git a/core/src/main/kotlin/Generation/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt deleted file mode 100644 index 23286e29..00000000 --- a/core/src/main/kotlin/Generation/Generator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Generator { - fun buildPages(nodes: Iterable<DocumentationNode>) - fun buildOutlines(nodes: Iterable<DocumentationNode>) - fun buildSupportFiles() - fun buildPackageList(nodes: Iterable<DocumentationNode>) -} - -fun Generator.buildAll(nodes: Iterable<DocumentationNode>) { - buildPages(nodes) - buildOutlines(nodes) - buildSupportFiles() - buildPackageList(nodes) -} - -fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)) - -fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node)) - -fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node)) - - -interface NodeLocationAwareGenerator: Generator { - fun location(node: DocumentationNode): Location - val root: File -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt deleted file mode 100644 index 0e60ff26..00000000 --- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt +++ /dev/null @@ -1,192 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.html.entities.EntityConverter -import org.intellij.markdown.parser.LinkMap -import java.util.* - -class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) { - fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel) - fun resolve(href: String): ContentBlock = contentFactory(href) -} - -fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent { - val result = MutableContent() - if (inline) { - buildInlineContentTo(tree, result, linkResolver) - } else { - buildContentTo(tree, result, linkResolver) - } - return result -} - -fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { -// println(tree.toTestString()) - val nodeStack = ArrayDeque<ContentBlock>() - nodeStack.push(target) - - tree.visit { node, processChildren -> - val parent = nodeStack.peek() - - fun appendNodeWithChildren(content: ContentBlock) { - nodeStack.push(content) - processChildren() - parent.append(nodeStack.pop()) - } - - when (node.type) { - MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1)) - MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2)) - MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3)) - MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4)) - MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5)) - MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6)) - MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList()) - MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList()) - MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem()) - MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis()) - MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong()) - MarkdownElementTypes.CODE_SPAN -> { - val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text - if (startDelimiter != null) { - val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) - val codeSpan = ContentCode().apply { append(ContentText(text)) } - parent.append(codeSpan) - } - } - MarkdownElementTypes.CODE_BLOCK, - MarkdownElementTypes.CODE_FENCE -> { - val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" - appendNodeWithChildren(ContentBlockCode(language)) - } - MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph()) - - MarkdownElementTypes.INLINE_LINK -> { - val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) - val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) - if (linkTextNode != null) { - if (destination != null) { - val link = ContentExternalLink(destination.text) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } else { - val link = ContentExternalLink(linkTextNode.getLabelText()) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } - } - } - MarkdownElementTypes.SHORT_REFERENCE_LINK, - MarkdownElementTypes.FULL_REFERENCE_LINK -> { - val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) - if (labelElement != null) { - val linkInfo = linkResolver.getLinkInfo(labelElement.text) - val labelText = labelElement.getLabelText() - val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText) - val linkText = node.child(MarkdownElementTypes.LINK_TEXT) - if (linkText != null) { - renderLinkTextTo(linkText, link, linkResolver) - } else { - link.append(ContentText(labelText)) - } - parent.append(link) - } - } - MarkdownTokenTypes.WHITE_SPACE -> { - // Don't append first space if start of header (it is added during formatting later) - // v - // #### Some Heading - if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { - parent.append(ContentText(node.text)) - } - } - MarkdownTokenTypes.EOL -> { - if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || - // Keep extra blank lines when processing lists (affects Markdown formatting) - (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.CODE_LINE -> { - val content = ContentText(node.text) - if (parent is ContentBlockCode) { - parent.append(content) - } else { - parent.append(ContentBlockCode().apply { append(content) }) - } - } - - 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.EMPH -> { - val parentNodeType = node.parent?.type - if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.COLON, - MarkdownTokenTypes.SINGLE_QUOTE, - MarkdownTokenTypes.DOUBLE_QUOTE, - MarkdownTokenTypes.LT, - MarkdownTokenTypes.GT, - MarkdownTokenTypes.LPAREN, - MarkdownTokenTypes.RPAREN, - MarkdownTokenTypes.LBRACKET, - MarkdownTokenTypes.RBRACKET, - MarkdownTokenTypes.EXCLAMATION_MARK, - MarkdownTokenTypes.BACKTICK, - MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - parent.append(ContentText(node.text)) - } - - MarkdownElementTypes.LINK_DEFINITION -> { - } - - MarkdownTokenTypes.EMAIL_AUTOLINK -> { - parent.append(ContentText(node.text)) // TODO: create new ContentType for email to create mailto: links - } - - else -> { - processChildren() - } - } - } -} - -private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH || it.type == MarkdownTokenTypes.COLON }.joinToString("") { it.text } - -private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode -private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList - -fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) - inlineContent.forEach { - buildContentTo(it, target, linkResolver) - } -} - -fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val linkTextNodes = tree.children.drop(1).dropLast(1) - linkTextNodes.forEach { - buildContentTo(it, target, linkResolver) - } -} diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt deleted file mode 100644 index 88494581..00000000 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink - -class DeclarationLinkResolver - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, - val elementSignatureProvider: ElementSignatureProvider) { - - - fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { - val symbol = try { - val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, - resolutionFacade, fromDescriptor, null, href.split('.').toList()) - findTargetSymbol(symbols) - } catch(e: Exception) { - null - } - - // don't include unresolved links in generated doc - // assume that if an href doesn't contain '/', it's not an attempt to reference an external file - if (symbol != null) { - val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol) - if (externalHref != null) { - return ContentExternalLink(externalHref) - } - val signature = elementSignatureProvider.signature(symbol) - val referencedAt = fromDescriptor.signatureWithSourceLocation() - - return ContentNodeLazyLink(href) { - val target = refGraph.lookup(signature) - - if (target == null) { - logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt. " + - "This is probably caused by invalid configuration of cross-module dependencies") - } - target - } - } - if ("/" in href) { - return ContentExternalLink(href) - } - return null - } - - fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) = - tryResolveContentLink(fromDescriptor, href) ?: run { - logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") - ContentExternalLink("#") - } - - fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { - if (symbols.isEmpty()) { - return null - } - val symbol = symbols.first() - if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - return symbol.overriddenDescriptors.firstOrNull() - } - if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) { - return symbol.classDescriptor - } - return symbol - } - -} diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt deleted file mode 100644 index 793f9589..00000000 --- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt +++ /dev/null @@ -1,305 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.util.io.* -import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Utilities.ServiceLocator -import org.jetbrains.dokka.Utilities.lookup -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.load.java.descriptors.* -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.parents -import java.io.ByteArrayOutputStream -import java.io.PrintWriter -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection -import java.nio.file.Path -import java.nio.file.Paths -import java.security.MessageDigest -import javax.inject.Named -import kotlin.reflect.full.findAnnotation - -fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) } - -typealias PackageFqNameToLocation = MutableMap<FqName, PackageListProvider.ExternalDocumentationRoot> - -@Singleton -class PackageListProvider @Inject constructor( - val configuration: DokkaConfiguration, - val logger: DokkaLogger -) { - val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>() - - val cacheDir: Path? = when { - configuration.cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka") - configuration.cacheRoot != null -> Paths.get(configuration.cacheRoot) - else -> null - }?.resolve("packageListCache")?.apply { createDirectories() } - - val cachedProtocols = setOf("http", "https", "ftp") - - init { - for (conf in configuration.passesConfigurations) { - for (link in conf.externalDocumentationLinks) { - if (link in storage) { - continue - } - - try { - loadPackageList(link) - } catch (e: Exception) { - throw RuntimeException("Exception while loading package-list from $link", e) - } - } - - } - } - - - - fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { - val connection = this.openConnection() - connection.connectTimeout = timeout - connection.readTimeout = timeout - - when (connection) { - is HttpURLConnection -> { - return when (connection.responseCode) { - in 200..299 -> { - connection - } - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP, - HttpURLConnection.HTTP_SEE_OTHER -> { - if (redirectsAllowed > 0) { - val newUrl = connection.getHeaderField("Location") - URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) - } else { - throw RuntimeException("Too many redirects") - } - } - else -> { - throw RuntimeException("Unhandled http code: ${connection.responseCode}") - } - } - } - else -> return connection - } - } - - fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) { - - val packageListUrl = link.packageListUrl - val needsCache = packageListUrl.protocol in cachedProtocols - - val packageListStream = if (cacheDir != null && needsCache) { - val packageListLink = packageListUrl.toExternalForm() - - val digest = MessageDigest.getInstance("SHA-256") - val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString() - val cacheEntry = cacheDir.resolve(hash) - - if (cacheEntry.exists()) { - try { - val connection = packageListUrl.doOpenConnectionToReadContent() - val originModifiedDate = connection.date - val cacheDate = cacheEntry.lastModified().toMillis() - if (originModifiedDate > cacheDate || originModifiedDate == 0L) { - if (originModifiedDate == 0L) - logger.warn("No date header for $packageListUrl, downloading anyway") - else - logger.info("Renewing package-list from $packageListUrl") - connection.getInputStream().copyTo(cacheEntry.outputStream()) - } - } catch (e: Exception) { - logger.error("Failed to update package-list cache for $link") - val baos = ByteArrayOutputStream() - PrintWriter(baos).use { - e.printStackTrace(it) - } - baos.flush() - logger.error(baos.toString()) - } - } else { - logger.info("Downloading package-list from $packageListUrl") - packageListUrl.openStream().copyTo(cacheEntry.outputStream()) - } - cacheEntry.inputStream() - } else { - packageListUrl.doOpenConnectionToReadContent().getInputStream() - } - - val (params, packages) = - packageListStream - .bufferedReader() - .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } } - - val paramsMap = params.asSequence() - .map { it.removePrefix(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX).split(":", limit = 2) } - .groupBy({ (key, _) -> key }, { (_, value) -> value }) - - val format = paramsMap["format"]?.singleOrNull() ?: "javadoc" - - val locations = paramsMap["location"].orEmpty() - .map { it.split("\u001f", limit = 2) } - .map { (key, value) -> key to value } - .toMap() - - - val defaultResolverDesc = ExternalDocumentationLinkResolver.services.getValue("dokka-default") - val resolverDesc = ExternalDocumentationLinkResolver.services[format] - ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver } - ?: defaultResolverDesc.also { - logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default") - } - - - val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin - - val constructors = resolverClass.constructors - - val constructor = constructors.singleOrNull() - ?: constructors.first { it.findAnnotation<Inject>() != null } - val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService - - val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations) - - val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>() - storage[link] = packageFqNameToLocation - - val fqNames = packages.map { FqName(it) } - for(name in fqNames) { - packageFqNameToLocation[name] = rootInfo - } - } - - - - class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) { - override fun toString(): String = rootUrl.toString() - } - - companion object { - private val formatsWithDefaultResolver = - ServiceLocator - .allServices("format") - .filter { - val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor - desc?.generatorServiceClass == FileGenerator::class - }.map { it.name } - .toSet() - - } - -} - -class ExternalDocumentationLinkResolver @Inject constructor( - val configuration: DokkaConfiguration, - val passConfiguration: DokkaConfiguration.PassConfiguration, - @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade, - val logger: DokkaLogger, - val packageListProvider: PackageListProvider -) { - - val formats = mutableMapOf<String, InboundExternalLinkResolutionService>() - val packageFqNameToLocation = mutableMapOf<FqName, PackageListProvider.ExternalDocumentationRoot>() - - init { - val fqNameToLocationMaps = passConfiguration.externalDocumentationLinks - .mapNotNull { packageListProvider.storage[it] } - - for(map in fqNameToLocationMaps) { - packageFqNameToLocation.putAll(map) - } - } - - fun buildExternalDocumentationLink(element: PsiElement): String? { - return element.extractDescriptor(libraryResolutionFacade)?.let { - buildExternalDocumentationLink(it) - } - } - - fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? { - val packageFqName: FqName = - when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName - is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null - else -> return null - } - - val externalLocation = packageFqNameToLocation[packageFqName] ?: return null - - val path = externalLocation.locations[symbol.signature()] ?: - externalLocation.resolver.getPath(symbol) ?: return null - - return URL(externalLocation.rootUrl, path).toExternalForm() - } - - companion object { - const val DOKKA_PARAM_PREFIX = "\$dokka." - val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name } - } -} - - -interface InboundExternalLinkResolutionService { - fun getPath(symbol: DeclarationDescriptor): String? - - class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - override fun getPath(symbol: DeclarationDescriptor): String? { - if (symbol is EnumEntrySyntheticClassDescriptor) { - return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() } - } else if (symbol is JavaClassDescriptor) { - return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html" - } else if (symbol is JavaCallableMemberDescriptor) { - val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null - val containingClassLink = getPath(containingClass) - if (containingClassLink != null) { - if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) { - val psi = symbol.sourcePsi() as? PsiMethod - if (psi != null) { - val params = psi.parameterList.parameters.joinToString { it.type.canonicalText } - return containingClassLink + "#" + symbol.name + "(" + params + ")" - } - } else if (symbol is JavaPropertyDescriptor) { - return "$containingClassLink#${symbol.name}" - } - } - } - // TODO Kotlin javadoc - return null - } - } - - class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver") - - override fun getPath(symbol: DeclarationDescriptor): String? { - val leafElement = when (symbol) { - is CallableDescriptor, is TypeAliasDescriptor -> true - else -> false - } - val path = getPathWithoutExtension(symbol) - if (leafElement) return "$path.$extension" - else return "$path/index.$extension" - } - - private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String { - return when { - symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString()) - symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString()) - else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString()) - } - } - - } -} - diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt deleted file mode 100644 index 70c04da6..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt +++ /dev/null @@ -1,67 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.asJava.elements.KtLightMethod -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtPropertyAccessor - -class KotlinAsJavaDocumentationBuilder - @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder -{ - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) { - val project = documentationBuilder.resolutionFacade.project - val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString()) - if (psiPackage == null) { - documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}") - return - } - - val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder, - kotlinAsJavaDocumentationParser) - - psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach { - javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it)) - } - } - - fun PsiClass.isVisibleInDocumentation(): Boolean { - val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true - - return !origin.hasModifier(KtTokens.INTERNAL_KEYWORD) && !origin.hasModifier(KtTokens.PRIVATE_KEYWORD) - } -} - -class KotlinAsJavaDocumentationParser - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser -{ - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty - val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty - if (origin is KtParameter) { - // LazyDeclarationResolver does not support setter parameters - val grandFather = origin.parent?.parent - if (grandFather is KtPropertyAccessor) { - return JavadocParseResult.Empty - } - } - val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass - val descriptor = resolutionFacade.resolveToDescriptor(origin) - val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor) - return JavadocParseResult(content, null) - } -} diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt deleted file mode 100644 index 20ea179e..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.asJava.toLightElements -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtElement - -class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider { - - private fun PsiElement.javaLikePsi() = when { - this is KtElement -> toLightElements().firstOrNull() - else -> this - } - - override fun signature(forPsi: PsiElement): String { - return getSignature(forPsi.javaLikePsi()) ?: - "not implemented for $forPsi" - } - - override fun signature(forDesc: DeclarationDescriptor): String { - val sourcePsi = forDesc.sourcePsi() - return getSignature(sourcePsi?.javaLikePsi()) ?: - "not implemented for $forDesc with psi: $sourcePsi" - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/JavaLanguageService.kt b/core/src/main/kotlin/Languages/JavaLanguageService.kt deleted file mode 100644 index ad66123b..00000000 --- a/core/src/main/kotlin/Languages/JavaLanguageService.kt +++ /dev/null @@ -1,171 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class JavaLanguageService : LanguageService { - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return ContentText(when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node) - - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - NodeKind.Constructor -> node.owner!!.name - else -> node.name - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - private fun renderPackage(node: DocumentationNode): String { - return "package ${node.name}" - } - - private fun renderModifier(node: DocumentationNode): String { - return when (node.name) { - "open" -> "" - "internal" -> "" - else -> node.name - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?: - DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun renderType(node: DocumentationNode): String { - return when (node.name) { - "Unit" -> "void" - "Int" -> "int" - "Long" -> "long" - "Double" -> "double" - "Float" -> "float" - "Char" -> "char" - "Boolean" -> "bool" - // TODO: render arrays - else -> node.name - } - } - - private fun renderTypeParameter(node: DocumentationNode): String { - val constraints = node.details(NodeKind.UpperBound) - return if (constraints.none()) - node.name - else { - node.name + " extends " + constraints.joinToString { renderType(node) } - } - } - - private fun renderParameter(node: DocumentationNode): String { - return "${renderType(node.detail(NodeKind.Type))} ${node.name}" - } - - private fun renderTypeParametersForNode(node: DocumentationNode): String { - return StringBuilder().apply { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - append("<") - append(typeParameters.joinToString { renderTypeParameter(it) }) - append("> ") - } - }.toString() - } - - private fun renderModifiersForNode(node: DocumentationNode): String { - val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } - if (modifiers.none()) - return "" - return modifiers.joinToString(" ", postfix = " ") - } - - private fun renderClass(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Class -> append("class ") - NodeKind.Interface -> append("interface ") - NodeKind.Enum -> append("enum ") - NodeKind.EnumItem -> append("enum value ") - NodeKind.Object -> append("class ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - append(node.name) - append(renderTypeParametersForNode(node)) - }.toString() - } - - private fun renderFunction(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Constructor -> append(node.owner?.name) - NodeKind.Function -> { - append(renderTypeParametersForNode(node)) - append(renderType(node.detail(NodeKind.Type))) - append(" ") - append(node.name) - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - append("(") - if (receiver != null) - (listOf(receiver) + node.details(NodeKind.Parameter)).joinTo(this) { renderParameter(it) } - else - node.details(NodeKind.Parameter).joinTo(this) { renderParameter(it) } - - append(")") - }.toString() - } - - private fun renderProperty(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Property -> append("val ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - append(renderTypeParametersForNode(node)) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - append(renderType(receiver.detail(NodeKind.Type))) - append(".") - } - - append(node.name) - append(": ") - append(renderType(node.detail(NodeKind.Type))) - }.toString() - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt deleted file mode 100644 index 992cd090..00000000 --- a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class NewJavaLanguageService : CommonLanguageService() { - override fun showModifierInSummary(node: DocumentationNode): Boolean { - return true - } - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - (when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node, renderMode) - - NodeKind.Modifier -> renderModifier(this, node, renderMode) - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - NodeKind.Parameter -> renderParameter(node) - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - - override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) { - when (node.name) { - "open", "internal" -> { - } - else -> super.renderModifier(block, node, renderMode, nowrap) - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } - ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun ContentBlock.renderType(node: DocumentationNode) { - when (node.name) { - "Unit" -> identifier("void") - "Int" -> identifier("int") - "Long" -> identifier("long") - "Double" -> identifier("double") - "Float" -> identifier("float") - "Char" -> identifier("char") - "Boolean" -> identifier("bool") - // TODO: render arrays - else -> renderLinked(this, node) { - identifier(node.name) - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode) { - val constraints = node.details(NodeKind.UpperBound) - if (constraints.none()) - identifier(node.name) - else { - identifier(node.name) - text(" ") - keyword("extends") - text(" ") - constraints.forEach { renderType(node) } - } - } - - private fun ContentBlock.renderParameter(node: DocumentationNode) { - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters, noWrap = true) { - renderTypeParameter(it) - } - symbol(">") - text(" ") - } - } - -// private fun renderModifiersForNode(node: DocumentationNode): String { -// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } -// if (modifiers.none()) -// return "" -// return modifiers.joinToString(" ", postfix = " ") -// } - - private fun ContentBlock.renderClassKind(node: DocumentationNode) { - when (node.kind) { - NodeKind.Interface -> { - keyword("interface") - } - NodeKind.EnumItem -> { - keyword("enum value") - } - NodeKind.Enum -> { - keyword("enum") - } - NodeKind.Class, NodeKind.Exception, NodeKind.Object -> { - keyword("class") - } - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode) - renderClassKind(node) - - identifier(node.name) - renderTypeParametersForNode(node) - } - - private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) { - renderList(nodes) { - renderParameter(it) - } - } - - private fun ContentBlock.renderFunction(node: DocumentationNode) { - when (node.kind) { - NodeKind.Constructor -> identifier(node.owner?.name ?: "") - NodeKind.Function -> { - renderTypeParametersForNode(node) - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - symbol("(") - if (receiver != null) - renderParameters(listOf(receiver) + node.details(NodeKind.Parameter)) - else - renderParameters(node.details(NodeKind.Parameter)) - - symbol(")") - } - - private fun ContentBlock.renderProperty(node: DocumentationNode) { - - when (node.kind) { - NodeKind.Property -> { - keyword("val") - text(" ") - } - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - renderType(receiver.detail(NodeKind.Type)) - symbol(".") - } - - identifier(node.name) - symbol(":") - text(" ") - renderType(node.detail(NodeKind.Type)) - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt deleted file mode 100644 index 2ad6b652..00000000 --- a/core/src/main/kotlin/Locations/Location.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Location { - val path: String get - fun relativePathTo(other: Location, anchor: String? = null): String -} - -/** - * Represents locations in the documentation in the form of [path](File). - * - * $file: [File] for this location - * $path: [String] representing path of this location - */ -data class FileLocation(val file: File) : Location { - override val path: String - get() = file.path - - override fun relativePathTo(other: Location, anchor: String?): String { - if (other !is FileLocation) { - throw IllegalArgumentException("$other is not a FileLocation") - } - if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) { - return "./${file.name}" - } - val ownerFolder = file.parentFile!! - val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/') - return if (anchor == null) relativePath else "$relativePath#$anchor" - } -} - -fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String { - val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() } - return if (!hasMembers) { - // leaf node, use file in owner's folder - parts.joinToString("/") - } else { - parts.joinToString("/") + (if (parts.none()) "" else "/") + "index" - } -} - -fun nodeActualQualifier(node: DocumentationNode): List<DocumentationNode> { - val topLevelPage = node.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - return nodeActualQualifier(topLevelPage) - } - return node.owner?.let { nodeActualQualifier(it) }.orEmpty() + node -} - -fun relativePathToNode(node: DocumentationNode): String { - val qualifier = nodeActualQualifier(node) - return relativePathToNode( - qualifier.map { it.name }, - qualifier.last().members.any() - ) -} - - -private val reservedFilenames = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out") - -fun identifierToFilename(path: String): String { - if (path.isEmpty()) return "--root--" - val escaped = path.replace('<', '-').replace('>', '-') - val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } - return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase -} - -fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String { - return location(owner).relativePathTo(location(node), null) -} - -fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File { - val file = File(from.path).parentFile - return root.relativeTo(file) -} - -fun File.toUnixString() = toString().replace(File.separatorChar, '/') diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt deleted file mode 100644 index 3881ee20..00000000 --- a/core/src/main/kotlin/Model/Content.kt +++ /dev/null @@ -1,290 +0,0 @@ -package org.jetbrains.dokka - -interface ContentNode { - val textLength: Int -} - -object ContentEmpty : ContentNode { - override val textLength: Int get() = 0 -} - -open class ContentBlock() : ContentNode { - open val children = arrayListOf<ContentNode>() - - fun append(node: ContentNode) { - children.add(node) - } - - fun isEmpty() = children.isEmpty() - - override fun equals(other: Any?): Boolean = - other is ContentBlock && javaClass == other.javaClass && children == other.children - - override fun hashCode(): Int = - children.hashCode() - - override val textLength: Int - get() = children.sumBy { it.textLength } -} - -class NodeRenderContent( - val node: DocumentationNode, - val mode: LanguageService.RenderMode -): ContentNode { - override val textLength: Int - get() = 0 //TODO: Clarify? -} - -class LazyContentBlock(private val fillChildren: () -> List<ContentNode>) : ContentBlock() { - private var computed = false - override val children: ArrayList<ContentNode> - get() { - if (!computed) { - computed = true - children.addAll(fillChildren()) - } - return super.children - } - - override fun equals(other: Any?): Boolean { - return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other) - } - - override fun hashCode(): Int { - return super.hashCode() + 31 * fillChildren.hashCode() - } -} - -enum class IdentifierKind { - TypeName, - ParameterName, - AnnotationName, - SummarizedTypeName, - Other -} - -data class ContentText(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentKeyword(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentIdentifier(val text: String, - val kind: IdentifierKind = IdentifierKind.Other, - val signature: String? = null) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentSymbol(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentEntity(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -object ContentNonBreakingSpace: ContentNode { - override val textLength: Int - get() = 1 -} - -object ContentSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -object ContentIndentedSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentParagraph() : ContentBlock() -class ContentEmphasis() : ContentBlock() -class ContentStrong() : ContentBlock() -class ContentStrikethrough() : ContentBlock() -class ContentCode() : ContentBlock() -open class ContentBlockCode(val language: String = "") : ContentBlock() -class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language) - -abstract class ContentNodeLink() : ContentBlock() { - abstract val node: DocumentationNode? - abstract val text: String? -} - -object ContentHardLineBreak : ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name - - override fun hashCode(): Int = - children.hashCode() * 31 + node.name.hashCode() - - override val text: String? = null -} - -class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() { - override val node: DocumentationNode? get() = lazyNode() - - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText - - override fun hashCode(): Int = - children.hashCode() * 31 + linkText.hashCode() - - override val text: String? = linkText -} - -class ContentExternalLink(val href : String) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentExternalLink && href == other.href - - override fun hashCode(): Int = - children.hashCode() * 31 + href.hashCode() -} - -data class ContentBookmark(val name: String): ContentBlock() -data class ContentLocalLink(val href: String) : ContentBlock() - -class ContentUnorderedList() : ContentBlock() -class ContentOrderedList() : ContentBlock() -class ContentListItem() : ContentBlock() - -class ContentHeading(val level: Int) : ContentBlock() - -class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName - - override fun hashCode(): Int = - children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0) -} - -object ContentTags { - const val Description = "Description" - const val SeeAlso = "See Also" - const val Return = "Return" - const val Exceptions = "Exceptions" -} - -fun content(body: ContentBlock.() -> Unit): ContentBlock { - val block = ContentBlock() - block.body() - return block -} - -fun ContentBlock.text(value: String) = append(ContentText(value)) -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, signature: String? = null) { - append(ContentIdentifier(value, kind, signature)) -} - -fun ContentBlock.nbsp() = append(ContentNonBreakingSpace) -fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak) -fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak) -fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak) - -fun ContentBlock.strong(body: ContentBlock.() -> Unit) { - val strong = ContentStrong() - strong.body() - append(strong) -} - -fun ContentBlock.code(body: ContentBlock.() -> Unit) { - val code = ContentCode() - code.body() - append(code) -} - -fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) { - val block = if (to is DocumentationNodes.ExternalLink) - ContentExternalLink(to.name) - else - ContentNodeDirectLink(to) - - block.body() - append(block) -} - -open class Content(): ContentBlock() { - open val sections: List<ContentSection> get() = emptyList() - open val summary: ContentNode get() = ContentEmpty - open val description: ContentNode get() = ContentEmpty - - fun findSectionByTag(tag: String): ContentSection? = - sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) } - - companion object { - val Empty = object: Content() { - override fun toString(): String { - return "EMPTY_CONTENT" - } - } - - fun of(vararg child: ContentNode): Content { - val result = MutableContent() - child.forEach { result.append(it) } - return result - } - } -} - -open class MutableContent() : Content() { - private val sectionList = arrayListOf<ContentSection>() - override val sections: List<ContentSection> - get() = sectionList - - fun addSection(tag: String?, subjectName: String?): ContentSection { - val section = ContentSection(tag ?: "", subjectName) - sectionList.add(section) - return section - } - - override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty - - override val description: ContentNode by lazy { - val descriptionNodes = children.drop(1) - if (descriptionNodes.isEmpty()) { - ContentEmpty - } else { - val result = ContentSection(ContentTags.Description, null) - result.children.addAll(descriptionNodes) - result - } - } - - override fun equals(other: Any?): Boolean { - if (other !is Content) - return false - return sections == other.sections && children == other.children - } - - override fun hashCode(): Int { - return sections.map { it.hashCode() }.sum() - } - - override fun toString(): String { - if (sections.isEmpty()) - return "<empty>" - return (listOf(summary, description) + sections).joinToString() - } -} - -fun javadocSectionDisplayName(sectionName: String?): String? = - when(sectionName) { - "param" -> "Parameters" - "throws", "exception" -> ContentTags.Exceptions - else -> sectionName?.capitalize() - } diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt deleted file mode 100644 index da74495f..00000000 --- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.jetbrains.dokka.Samples - -import com.google.inject.Inject -import com.intellij.psi.PsiElement -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.ResolutionScope - - -open class DefaultSampleProcessingService -@Inject constructor(val configuration: DokkaConfiguration, - val logger: DokkaLogger, - val resolutionFacade: DokkaResolutionFacade) - : SampleProcessingService { - - override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode { - if (functionName == null) { - logger.warn("Missing function name in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) } - } - val bindingContext = BindingContext.EMPTY - val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull() - if (symbol == null) { - logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) } - } - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol) - if (psiElement == null) { - logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) } - } - - val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() - val lines = text.split("\n") - val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 - val finalText = lines.joinToString("\n") { it.drop(indent) } - - return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) } - } - - protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - when (bodyExpression) { - is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") - else -> bodyExpression!!.text - } - } - else -> psiElement.text - } - - protected open fun processImports(psiElement: PsiElement): ContentBlockCode { - val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText(psiFile.importList?.text ?: "")) - } - } else { - return ContentBlockCode("") - } - } - - private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? { - var currentScope = scope - val parts = functionName.split('.') - - var symbol: DeclarationDescriptor? = null - - for (part in parts) { - // short name - val symbolName = Name.identifier(part) - val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL) { it == symbolName }.firstOrNull { it.name == symbolName } - - if (partSymbol == null) { - symbol = null - break - } - @Suppress("IfThenToElvis") - currentScope = if (partSymbol is ClassDescriptor) - partSymbol.defaultType.memberScope - else if (partSymbol is PackageViewDescriptor) - partSymbol.memberScope - else - getKDocLinkResolutionScope(resolutionFacade, partSymbol) - symbol = partSymbol - } - - return symbol - } -} - diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt deleted file mode 100644 index 4525e9d9..00000000 --- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.jetbrains.dokka.Samples - -import com.google.inject.Inject -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.tree.LeafPsiElement -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.allChildren -import org.jetbrains.kotlin.psi.psiUtil.prevLeaf -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.resolve.ImportPath -import java.io.PrintWriter -import java.io.StringWriter - - -open class KotlinWebsiteSampleProcessingService -@Inject constructor(dokkaConfiguration: DokkaConfiguration, - logger: DokkaLogger, - resolutionFacade: DokkaResolutionFacade) - : DefaultSampleProcessingService(dokkaConfiguration, logger, resolutionFacade) { - - private class SampleBuilder : KtTreeVisitorVoid() { - val builder = StringBuilder() - val text: String - get() = builder.toString() - - val errors = mutableListOf<ConvertError>() - - data class ConvertError(val e: Exception, val text: String, val loc: String) - - fun KtValueArgument.extractStringArgumentValue() = - (getArgumentExpression() as KtStringTemplateExpression) - .entries.joinToString("") { it.text } - - - fun convertAssertPrints(expression: KtCallExpression) { - val (argument, commentArgument) = expression.valueArguments - builder.apply { - append("println(") - append(argument.text) - append(") // ") - append(commentArgument.extractStringArgumentValue()) - } - } - - fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { - val (argument) = expression.valueArguments - builder.apply { - expression.valueArguments.getOrNull(1)?.let { - append("// ${it.extractStringArgumentValue()}") - val ws = expression.prevLeaf { it is PsiWhiteSpace } - append(ws?.text ?: "\n") - } - append("println(\"") - append(argument.text) - append(" is \${") - append(argument.text) - append("}\") // $expectedResult") - } - } - - fun convertAssertFails(expression: KtCallExpression) { - val valueArguments = expression.valueArguments - - val funcArgument: KtValueArgument - val message: KtValueArgument? - - if (valueArguments.size == 1) { - message = null - funcArgument = valueArguments.first() - } else { - message = valueArguments.first() - funcArgument = valueArguments.last() - } - - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // ") - if (message != null) { - append(message.extractStringArgumentValue()) - } - append(" will fail") - } - } - - private fun KtValueArgument.extractFunctionalArgumentText(): String { - return if (getArgumentExpression() is KtLambdaExpression) - PsiTreeUtil.findChildOfType(this, KtBlockExpression::class.java)?.text ?: "" - else - text - } - - fun convertAssertFailsWith(expression: KtCallExpression) { - val (funcArgument) = expression.valueArguments - val (exceptionType) = expression.typeArguments - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // will fail with ") - append(exceptionType.text) - } - } - - override fun visitCallExpression(expression: KtCallExpression) { - when (expression.calleeExpression?.text) { - "assertPrints" -> convertAssertPrints(expression) - "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) - "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) - "assertFails" -> convertAssertFails(expression) - "assertFailsWith" -> convertAssertFailsWith(expression) - else -> super.visitCallExpression(expression) - } - } - - private fun reportProblemConvertingElement(element: PsiElement, e: Exception) { - val text = element.text - val document = PsiDocumentManager.getInstance(element.project).getDocument(element.containingFile) - - val lineInfo = if (document != null) { - val lineNumber = document.getLineNumber(element.startOffset) - "$lineNumber, ${element.startOffset - document.getLineStartOffset(lineNumber)}" - } else { - "offset: ${element.startOffset}" - } - errors += ConvertError(e, text, lineInfo) - } - - override fun visitElement(element: PsiElement) { - if (element is LeafPsiElement) - builder.append(element.text) - - element.acceptChildren(object : PsiElementVisitor() { - override fun visitElement(element: PsiElement) { - try { - element.accept(this@SampleBuilder) - } catch (e: Exception) { - try { - reportProblemConvertingElement(element, e) - } finally { - builder.append(element.text) //recover - } - } - } - }) - } - - } - - private fun PsiElement.buildSampleText(): String { - val sampleBuilder = SampleBuilder() - this.accept(sampleBuilder) - - sampleBuilder.errors.forEach { - val sw = StringWriter() - val pw = PrintWriter(sw) - it.e.printStackTrace(pw) - - logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") - } - return sampleBuilder.text - } - - val importsToIgnore = arrayOf("samples.*").map { ImportPath.fromString(it) } - - override fun processImports(psiElement: PsiElement): ContentBlockCode { - val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText("\n")) - psiFile.importList?.let { - it.allChildren.filter { - it !is KtImportDirective || it.importPath !in importsToIgnore - }.forEach { append(ContentText(it.text)) } - } - } - } - return super.processImports(psiElement) - } - - override fun processSampleBody(psiElement: PsiElement) = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - val bodyExpressionText = bodyExpression!!.buildSampleText() - when (bodyExpression) { - is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") - else -> bodyExpressionText - } - } - else -> psiElement.buildSampleText() - } -} - diff --git a/core/src/main/kotlin/Samples/SampleProcessingService.kt b/core/src/main/kotlin/Samples/SampleProcessingService.kt deleted file mode 100644 index 86c917cf..00000000 --- a/core/src/main/kotlin/Samples/SampleProcessingService.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka.Samples - -import org.jetbrains.dokka.ContentNode -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag - -interface SampleProcessingService { - fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode -}
\ No newline at end of file |