diff options
author | Eugene Petrenko <eugene.petrenko@gmail.com> | 2018-09-11 14:03:29 +0300 |
---|---|---|
committer | Eugene Petrenko <eugene.petrenko@gmail.com> | 2018-09-11 14:03:29 +0300 |
commit | a54070f5db04d05b48c369b211efe044fbc02a8d (patch) | |
tree | aac5889906f49f68c39e1416ea39d4d19bad008e /core/src/main/kotlin/Formats | |
parent | e29f1c966056b0f7ee31dd9bd7d55f9e526fa7fe (diff) | |
parent | 0df19264ccae3d294946caf634ee15eea0c4fe4a (diff) | |
download | dokka-a54070f5db04d05b48c369b211efe044fbc02a8d.tar.gz dokka-a54070f5db04d05b48c369b211efe044fbc02a8d.tar.bz2 dokka-a54070f5db04d05b48c369b211efe044fbc02a8d.zip |
Merge remote-tracking branch 'zub/dev-multiplatf' into kotlin-website-jonnyzzz
Diffstat (limited to 'core/src/main/kotlin/Formats')
16 files changed, 711 insertions, 260 deletions
diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt new file mode 100644 index 00000000..d78d4a0c --- /dev/null +++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Binder +import org.jetbrains.dokka.* +import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider +import org.jetbrains.dokka.KotlinElementSignatureProvider +import org.jetbrains.dokka.ElementSignatureProvider +import org.jetbrains.dokka.Samples.DefaultSampleProcessingService +import org.jetbrains.dokka.Samples.SampleProcessingService +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.toType +import kotlin.reflect.KClass + + +interface DefaultAnalysisComponentServices { + val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder> + val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder> + val sampleProcessingService: KClass<out SampleProcessingService> + val elementSignatureProvider: KClass<out ElementSignatureProvider> +} + +interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { + override fun configureAnalysis(binder: Binder): Unit = with(binder) { + bind<ElementSignatureProvider>() toType elementSignatureProvider + bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass + bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass + bind<SampleProcessingService>() toType sampleProcessingService + } +} + + +object KotlinAsJava : DefaultAnalysisComponentServices { + override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class + override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class + override val sampleProcessingService = DefaultSampleProcessingService::class + override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class +} + + +object KotlinAsKotlin : DefaultAnalysisComponentServices { + override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class + override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class + override val sampleProcessingService = DefaultSampleProcessingService::class + override val elementSignatureProvider = KotlinElementSignatureProvider::class +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt index da0156a7..4bac8aa0 100644 --- a/core/src/main/kotlin/Formats/FormatDescriptor.kt +++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt @@ -1,17 +1,43 @@ package org.jetbrains.dokka.Formats +import com.google.inject.Binder import org.jetbrains.dokka.* -import org.jetbrains.dokka.Model.DescriptorSignatureProvider -import org.jetbrains.dokka.Samples.SampleProcessingService +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.lazyBind +import org.jetbrains.dokka.Utilities.toOptional +import org.jetbrains.dokka.Utilities.toType import kotlin.reflect.KClass -interface FormatDescriptor { - val formatServiceClass: KClass<out FormatService>? - val outlineServiceClass: KClass<out OutlineFormatService>? - val generatorServiceClass: KClass<out Generator> - val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder> - val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder> - val sampleProcessingService: KClass<out SampleProcessingService> - val packageListServiceClass: KClass<out PackageListService>? - val descriptorSignatureProvider: KClass<out DescriptorSignatureProvider> + +interface FormatDescriptorAnalysisComponent { + fun configureAnalysis(binder: Binder) +} + +interface FormatDescriptorOutputComponent { + fun configureOutput(binder: Binder) } + +interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent + + +abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor { + + override fun configureOutput(binder: Binder): Unit = with(binder) { + bind<Generator>() toType NodeLocationAwareGenerator::class + bind<NodeLocationAwareGenerator>() toType generatorServiceClass + bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847 + + bind<LanguageService>() toType languageServiceClass + + lazyBind<OutlineFormatService>() toOptional (outlineServiceClass) + lazyBind<FormatService>() toOptional formatServiceClass + lazyBind<PackageListService>() toOptional packageListServiceClass + } + + abstract val formatServiceClass: KClass<out FormatService>? + abstract val outlineServiceClass: KClass<out OutlineFormatService>? + abstract val generatorServiceClass: KClass<out FileGenerator> + abstract val packageListServiceClass: KClass<out PackageListService>? + + open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt index f741561c..036ec856 100644 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ b/core/src/main/kotlin/Formats/GFMFormatService.kt @@ -4,14 +4,14 @@ import com.google.inject.Inject import com.google.inject.name.Named import org.jetbrains.dokka.Utilities.impliedPlatformsName -open class GFMOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) -{ +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)) @@ -21,8 +21,7 @@ open class GFMOutputBuilder(to: StringBuilder, override fun appendUnorderedList(body: () -> Unit) { if (inTableCell) { wrapInTag("ul", body) - } - else { + } else { super.appendUnorderedList(body) } } @@ -30,8 +29,7 @@ open class GFMOutputBuilder(to: StringBuilder, override fun appendOrderedList(body: () -> Unit) { if (inTableCell) { wrapInTag("ol", body) - } - else { + } else { super.appendOrderedList(body) } } @@ -39,23 +37,25 @@ open class GFMOutputBuilder(to: StringBuilder, override fun appendListItem(body: () -> Unit) { if (inTableCell) { wrapInTag("li", body) - } - else { + } else { super.appendListItem(body) } } } -open class GFMFormatService(locationService: LocationService, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String>) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { +open class GFMFormatService( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + linkExtension: String, + impliedPlatforms: List<String> +) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>) : this(locationService, signatureGenerator, "md", 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, locationService, languageService, extension, impliedPlatforms) + 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 index 5e05f51a..0ad946be 100644 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt @@ -4,17 +4,15 @@ import com.google.inject.Inject import com.google.inject.name.Named import org.jetbrains.dokka.Utilities.impliedPlatformsName import java.io.File -import java.nio.file.Path -import java.nio.file.Paths open class HtmlOutputBuilder(to: StringBuilder, location: Location, - locationService: LocationService, + generator: NodeLocationAwareGenerator, languageService: LanguageService, extension: String, impliedPlatforms: List<String>, val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) + : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { override fun appendText(text: String) { to.append(text.htmlEscape()) @@ -81,7 +79,7 @@ open class HtmlOutputBuilder(to: StringBuilder, } override fun appendNodes(nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, getPageTitle(nodes), locationService.calcPathToRoot(location)) + templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location)) super.appendNodes(nodes) templateService.appendFooter(to) } @@ -95,21 +93,21 @@ open class HtmlOutputBuilder(to: StringBuilder, } } -open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService, +open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator, signatureGenerator: LanguageService, val templateService: HtmlTemplateService, @Named(impliedPlatformsName) val impliedPlatforms: List<String>) -: StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService { +: 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, locationService, languageService, extension, impliedPlatforms, templateService) + HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, "Module Contents", locationService.calcPathToRoot(location)) + templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location)) super.appendOutline(location, to, nodes) templateService.appendFooter(to) } @@ -123,7 +121,7 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi link.append(languageService.render(node, LanguageService.RenderMode.FULL)) val tempBuilder = StringBuilder() createOutputBuilder(tempBuilder, location).appendContent(link) - to.appendln("<a href=\"${location.path}\">${tempBuilder.toString()}</a><br/>") + to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>") } override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { @@ -133,11 +131,6 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi } } -private fun LocationService.calcPathToRoot(location: Location): Path { - val path = Paths.get(location.path) - return path.parent?.relativize(Paths.get(root.path + '/')) ?: path -} - fun getPageTitle(nodes: Iterable<DocumentationNode>): String? { val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } return breakdownByLocation.keys.singleOrNull() diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt index 010bc702..a65a7b18 100644 --- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt +++ b/core/src/main/kotlin/Formats/HtmlTemplateService.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka -import java.nio.file.Path +import java.io.File interface HtmlTemplateService { - fun appendHeader(to: StringBuilder, title: String?, basePath: Path) + fun appendHeader(to: StringBuilder, title: String?, basePath: File) fun appendFooter(to: StringBuilder) companion object { @@ -16,7 +16,7 @@ interface HtmlTemplateService { to.appendln("</BODY>") to.appendln("</HTML>") } - override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) { + override fun appendHeader(to: StringBuilder, title: String?, basePath: File) { to.appendln("<HTML>") to.appendln("<HEAD>") to.appendln("<meta charset=\"UTF-8\">") @@ -24,7 +24,7 @@ interface HtmlTemplateService { to.appendln("<title>$title</title>") } if (css != null) { - val cssPath = basePath.resolve(css) + val cssPath = basePath.resolve(css).toUnixString() to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">") } to.appendln("</HEAD>") diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt new file mode 100644 index 00000000..09bb2602 --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt @@ -0,0 +1,117 @@ +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 new file mode 100644 index 00000000..885cdf6c --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt @@ -0,0 +1,91 @@ +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 index 6b97bbe0..a948dfa9 100644 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt @@ -6,12 +6,11 @@ import org.jetbrains.dokka.Utilities.impliedPlatformsName open class JekyllOutputBuilder(to: StringBuilder, location: Location, - locationService: LocationService, + generator: NodeLocationAwareGenerator, languageService: LanguageService, extension: String, impliedPlatforms: List<String>) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) -{ + : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { override fun appendNodes(nodes: Iterable<DocumentationNode>) { to.appendln("---") appendFrontMatter(nodes, to) @@ -26,17 +25,20 @@ open class JekyllOutputBuilder(to: StringBuilder, } -open class JekyllFormatService(locationService: LocationService, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String>) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { +open class JekyllFormatService( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + linkExtension: String, + impliedPlatforms: List<String> +) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "html", 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, locationService, languageService, extension, impliedPlatforms) + JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt index 08349980..a98002d4 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -6,14 +6,14 @@ import org.jetbrains.dokka.Utilities.impliedPlatformsName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty -open class KotlinWebsiteOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) -{ +open class KotlinWebsiteOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String> +) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { private var needHardLineBreaks = false private var insideDiv = 0 @@ -70,8 +70,7 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, override fun appendHeader(level: Int, body: () -> Unit) { if (insideDiv > 0) { wrapInTag("p", body, newlineAfterClose = true) - } - else { + } else { super.appendHeader(level, body) } } @@ -79,8 +78,7 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, override fun appendLine() { if (insideDiv > 0) { to.appendln("<br/>") - } - else { + } else { super.appendLine() } } @@ -135,13 +133,14 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, to.append("<br/>") } + override fun appendIndentedSoftLineBreak() { if (needHardLineBreaks) { to.append("<br/> ") } } - private fun identifierClassName(kind: IdentifierKind) = when(kind) { + private fun identifierClassName(kind: IdentifierKind) = when (kind) { IdentifierKind.ParameterName -> "parameterName" IdentifierKind.SummarizedTypeName -> "summarizedTypeName" else -> "identifier" @@ -172,28 +171,29 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, } } -class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>, - logger: DokkaLogger) - : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) -{ +class KotlinWebsiteFormatService @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>, + logger: DokkaLogger +) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { init { logger.warn("Format kotlin-website deprecated and will be removed in next release") } override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) + KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) } -class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { +class KotlinWebsiteRunnableSamplesOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String> +) : KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { div(to, "sample", markdown = true) { @@ -207,17 +207,18 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, } } -class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>, - logger: DokkaLogger) - : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) { +class KotlinWebsiteRunnableSamplesFormatService @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>, + logger: DokkaLogger +) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { init { logger.warn("Format kotlin-website-samples deprecated and will be removed in next release") } override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) + KotlinWebsiteRunnableSamplesOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt index 378401f3..16bec5a6 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt @@ -4,23 +4,25 @@ import com.google.inject.Inject import com.google.inject.name.Named import org.jetbrains.dokka.Utilities.impliedPlatformsName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty -import java.nio.file.Path +import java.io.File -private object EmptyHtmlTemplateService : HtmlTemplateService { +object EmptyHtmlTemplateService : HtmlTemplateService { override fun appendFooter(to: StringBuilder) {} - override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) {} + override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {} } -open class KotlinWebsiteHtmlOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, EmptyHtmlTemplateService) { +open class KotlinWebsiteHtmlOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>, + templateService: HtmlTemplateService +) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) { private var needHardLineBreaks = false private var insideDiv = 0 @@ -167,16 +169,25 @@ open class KotlinWebsiteHtmlOutputBuilder(to: StringBuilder, appendContent(section) } } + + override fun appendAsBlockWithPlatforms(platforms: Set<String>, block: () -> Unit) { + if (platforms.isNotEmpty()) + wrap("<div${calculateDataAttributes(platforms)}>", "</div>", block) + else + block() + } } -class KotlinWebsiteHtmlFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>) - : HtmlFormatService(locationService, signatureGenerator, EmptyHtmlTemplateService, impliedPlatforms) { +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, locationService, languageService, extension, impliedPlatforms) + KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) } diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt index a7c18a28..4265394f 100644 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt @@ -21,11 +21,11 @@ private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() open class MarkdownOutputBuilder(to: StringBuilder, location: Location, - locationService: LocationService, + generator: NodeLocationAwareGenerator, languageService: LanguageService, extension: String, impliedPlatforms: List<String>) - : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) + : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { private val listStack = ArrayDeque<ListState>() protected var inTableCell = false @@ -225,15 +225,15 @@ open class MarkdownOutputBuilder(to: StringBuilder, } } -open class MarkdownFormatService(locationService: LocationService, +open class MarkdownFormatService(generator: NodeLocationAwareGenerator, signatureGenerator: LanguageService, linkExtension: String, val impliedPlatforms: List<String>) -: StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) { - @Inject constructor(locationService: LocationService, +: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) { + @Inject constructor(generator: NodeLocationAwareGenerator, signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms) + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) + 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 index 3c31ba57..958e93af 100644 --- a/core/src/main/kotlin/Formats/OutlineService.kt +++ b/core/src/main/kotlin/Formats/OutlineService.kt @@ -16,7 +16,7 @@ interface OutlineFormatService { for (node in nodes) { appendOutlineHeader(location, node, to) if (node.members.any()) { - val sortedMembers = node.members.sortedBy { it.name } + val sortedMembers = node.members.sortedBy { it.name.toLowerCase() } appendOutlineLevel(to) { appendOutline(location, to, sortedMembers) } diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt index e7f4e952..7b68098e 100644 --- a/core/src/main/kotlin/Formats/PackageListService.kt +++ b/core/src/main/kotlin/Formats/PackageListService.kt @@ -7,10 +7,10 @@ interface PackageListService { fun formatPackageList(module: DocumentationModule): String } -class DefaultPackageListService @Inject constructor(locationService: FileLocationService, - val formatService: FormatService) : PackageListService { - - val locationService: FileLocationService = locationService.withExtension(formatService.linkExtension) +class DefaultPackageListService @Inject constructor( + val generator: NodeLocationAwareGenerator, + val formatService: FormatService +) : PackageListService { override fun formatPackageList(module: DocumentationModule): String { val packages = mutableSetOf<String>() @@ -26,7 +26,7 @@ class DefaultPackageListService @Inject constructor(locationService: FileLocatio } NodeKind.Signature -> { if (relocated) - nonStandardLocations[node.name] = locationService.relativePathToLocation(module, node.owner!!) + nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!) } NodeKind.ExternalClass -> { node.members.forEach { visit(it, relocated = true) } diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt index f3d638c6..dd67ac97 100644 --- a/core/src/main/kotlin/Formats/StandardFormats.kt +++ b/core/src/main/kotlin/Formats/StandardFormats.kt @@ -1,41 +1,36 @@ package org.jetbrains.dokka.Formats +import com.google.inject.Binder import org.jetbrains.dokka.* -import org.jetbrains.dokka.Kotlin.KotlinAsJavaDescriptorSignatureProvider -import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider -import org.jetbrains.dokka.Model.DescriptorSignatureProvider -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService -import org.jetbrains.dokka.Samples.SampleProcessingService +import org.jetbrains.dokka.Utilities.bind import kotlin.reflect.KClass -abstract class KotlinFormatDescriptorBase : FormatDescriptor { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - +abstract class KotlinFormatDescriptorBase + : FileGeneratorBasedFormatDescriptor(), + DefaultAnalysisComponent, + DefaultAnalysisComponentServices by KotlinAsKotlin { override val generatorServiceClass = FileGenerator::class override val outlineServiceClass: KClass<out OutlineFormatService>? = null - override val sampleProcessingService: KClass<out SampleProcessingService> = DefaultSampleProcessingService::class override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class - override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class -} - -class HtmlFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class } -class HtmlAsJavaFormatDescriptor : FormatDescriptor { +abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent { override val formatServiceClass = HtmlFormatService::class override val outlineServiceClass = HtmlFormatService::class override val generatorServiceClass = FileGenerator::class - override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class - override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class - override val sampleProcessingService: KClass<out SampleProcessingService> = DefaultSampleProcessingService::class - override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class - override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::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 KotlinWebsiteFormatDescriptor : KotlinFormatDescriptorBase() { override val formatServiceClass = KotlinWebsiteFormatService::class override val outlineServiceClass = YamlOutlineService::class @@ -51,6 +46,11 @@ 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() { diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index 5167a102..53172563 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -7,11 +7,13 @@ data class FormatLink(val text: String, val href: String) abstract class StructuredOutputBuilder(val to: StringBuilder, val location: Location, - val locationService: LocationService, + val generator: NodeLocationAwareGenerator, val languageService: LanguageService, val extension: String, val impliedPlatforms: List<String>) : FormattedOutputBuilder { + protected fun DocumentationNode.location() = generator.location(this) + protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { to.append(prefix) body() @@ -81,6 +83,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + open fun appendAsBlockWithPlatforms(platforms: Set<String>, block: () -> Unit) { + block() + } + open fun appendSymbol(text: String) { appendText(text) } @@ -190,30 +196,28 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } - 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, + 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 { - if (to.owner?.kind == NodeKind.GroupNode) - return link(from, to.owner!!, extension, name) + open fun link( + from: DocumentationNode, + to: DocumentationNode, + extension: String, + name: (DocumentationNode) -> String = DocumentationNode::name + ): FormatLink = + FormatLink(name(to), from.location().relativePathTo(to.location())) - if (from.owner?.kind == NodeKind.GroupNode) - return link(from.owner!!, to, extension, name) - - return FormatLink(name(to), locationService.relativePathToLocation(from, to)) - } fun locationHref(from: Location, to: DocumentationNode): String { val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to if (topLevelPage != null) { val signature = to.detailOrNull(NodeKind.Signature) - return from.relativePathTo(locationService.location(topLevelPage), signature?.name ?: to.name) + return from.relativePathTo(topLevelPage.location(), signature?.name ?: to.name) } - return from.relativePathTo(locationService.location(to)) + return from.relativePathTo(to.location()) } private fun DocumentationNode.isModuleOrPackage(): Boolean = @@ -296,7 +300,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) { - val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content } + val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> + when (node.kind) { + NodeKind.GroupNode -> node.origins.first().content + else -> node.content + } + } if (breakdownBySummary.size == 1) { formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode) @@ -314,29 +323,90 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) { for ((index, item) in items.withIndex()) { if (index > 0) appendLine() - val rendered = languageService.render(item) - item.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - appendAsSignature(rendered) { - appendCode { appendContent(rendered) } - item.appendSourceLink() + + if (item.kind == NodeKind.GroupNode) { + renderGroupNode(item, isSingleNode) + } else { + renderSimpleNode(item, isSingleNode) } - item.appendOverrides() - item.appendDeprecation() - item.appendPlatforms() + } // 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) } - appendContent(item.content.summary) + if (item.kind == NodeKind.GroupNode) { + for (origin in item.origins) { + if (origin.content.isEmpty()) continue + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + origin.actualPlatforms) + appendContent(origin.summary) + } + } + } else { + if (!item.content.isEmpty()) { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + item.actualPlatforms) + appendContent(item.content.summary) + } + } + item.appendDescription() } + + fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean) { + // TODO: use summarizesignatures + val rendered = languageService.render(item) + item.detailOrNull(NodeKind.Signature)?.let { + if (item.kind !in NodeKind.classLike || !isSingleNode) + appendAnchor(it.name) + } + appendAsSignature(rendered) { + appendCode { appendContent(rendered) } + item.appendSourceLink() + } + item.appendOverrides() + item.appendDeprecation() + item.appendPlatforms() + } + + fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean) { + // TODO: use summarizesignatures + val groupBySignature = item.origins.groupBy { + languageService.render(it) + } + + for ((sign, nodes) in groupBySignature) { + val first = nodes.first() + first.detailOrNull(NodeKind.Signature)?.let { + if (item.kind !in NodeKind.classLike || !isSingleNode) + appendAnchor(it.name) + } + + appendAsSignature(sign) { + appendCode { appendContent(sign) } + } + first.appendOverrides() + first.appendDeprecation() + + + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + nodes + .flatMap { it.actualPlatforms } + .distinct() + .joinToString() + ) + } + + } + } + private fun DocumentationNode.appendSourceLink() { val sourceUrl = details(NodeKind.SourceUrl).firstOrNull() if (sourceUrl != null) { @@ -349,7 +419,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, overrides.forEach { appendParagraph { to.append("Overrides ") - val location = locationService.relativePathToLocation(this, it) + val location = location().relativePathTo(it.location()) + appendLink(FormatLink(it.owner!!.name + "." + it.name, location)) } } @@ -360,30 +431,30 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull() val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull() appendLine() - if (deprecationValue != null) { - appendStrong { to.append("Deprecated:") } - appendText(" " + deprecationValue.name.removeSurrounding("\"")) - appendLine() - appendLine() - } else if (deprecation?.content != Content.Empty) { - appendStrong { to.append("Deprecated:") } - to.append(" ") - appendContent(deprecation!!.content) - } else { - appendStrong { to.append("Deprecated") } - appendLine() - 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() + } } } } private fun DocumentationNode.appendPlatforms() { - val platforms = if (isModuleOrPackage()) - platformsToShow.toSet() + platformsOfItems(members) - else - platformsToShow - - if(platforms.isEmpty()) return + val platforms = actualPlatforms + if (platforms.isEmpty()) return appendParagraph { appendStrong { to.append("Platform and version requirements:") } @@ -391,16 +462,39 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + val DocumentationNode.actualPlatforms: Collection<String> + get() = if (isModuleOrPackage()) + platformsToShow.toSet() + platformsOfItems(members) + else + platformsToShow + + + protected fun mergeVersions(otherKotlinVersion: String, kotlinVersions: List<String>): String { + val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct() + + val minVersion = allKotlinVersions.min()!! + val resultVersion: String = when { + allKotlinVersions.size == 1 -> allKotlinVersions.single() + minVersion.endsWith("+") -> minVersion + else -> "$minVersion+" + } + + return resultVersion + } + protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { val platforms = items.asSequence().map { when (it.kind) { - NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members) + 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() } @@ -408,19 +502,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, // When no Kotlin version specified, it means that version is 1.0 if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { - val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct() + result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions) + } else { + result.intersect(platformsOfItem) + } + } + } - val minVersion = allKotlinVersions.min()!! - val resultVersion = when { - allKotlinVersions.size == 1 -> allKotlinVersions.single() - minVersion.endsWith("+") -> minVersion - else -> minVersion + "+" - } + 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() } - result.intersect(otherPlatforms) + resultVersion + // 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.intersect(platformsOfItem) + result.union(otherPlatforms) } + }.let { + if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it } } @@ -456,6 +569,15 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + 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() { @@ -466,39 +588,40 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendLine() appendHeader { appendText(node.name) } - fun DocumentationNode.priority(): Int = when (kind) { - NodeKind.TypeAlias -> 1 - NodeKind.Class -> 2 - else -> 3 - } + renderGroupNode(node, true) - for (member in node.members.sortedBy(DocumentationNode::priority)) { + for (origin in node.origins) { + if (origin.content.isEmpty()) continue + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + origin.actualPlatforms) + appendContent(origin.content.summary) + } - appendAsOverloadGroup(to, platformsOfItems(listOf(member))) { - formatSubNodeOfGroup(member) - } + 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 } } - fun formatSubNodeOfGroup(member: DocumentationNode) { - SingleNodePageBuilder(member, true).build() - } + return platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it } } - inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) - : PageBuilder(listOf(node), noHeader) { + inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) { override fun build() { - super.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).flatMap { it.members.filter(predicate) } + return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) } } fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { @@ -506,20 +629,19 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } 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("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("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias)) appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) - appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true) - appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true) + 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.members(NodeKind.Function), omitSamePlatforms = true) + appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true) appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) + appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) + 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( @@ -554,7 +676,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, if (node.kind == NodeKind.Module) { appendHeader(3) { to.append("Index") } node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes -> - appendLink(link(node, allTypes, { "All Types" })) + appendLink(link(node, allTypes) { "All Types" }) } } } @@ -567,7 +689,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendHeader(3) { appendText(caption) } - val children = if (sortMembers) members.sortedBy { it.name } else members + val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members val membersMap = children.groupBy { link(node, it) } @@ -590,11 +712,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } appendTableCell { - val breakdownBySummary = members.groupBy { it.summary } - for ((summary, items) in breakdownBySummary) { - appendSummarySignatures(items) - appendContent(summary) - } + appendSummarySignatures(members, platformsBasedOnMembers, omitSamePlatforms) } } } @@ -603,6 +721,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } 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 @@ -610,27 +732,62 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, return emptySet() } - private fun appendSummarySignatures(items: List<DocumentationNode>) { - val summarySignature = languageService.summarizeSignatures(items) - if (summarySignature != null) { - appendAsSignature(summarySignature) { - summarySignature.appendSignature() + private fun appendSummarySignatures(items: List<DocumentationNode>, platformsBasedOnMembers: Boolean, omitSamePlatforms: Boolean) { + val groupBySummary = items.groupBy { it.summary } + + for ((summary, node) in groupBySummary) { + val nodesToAppend = if (node.all { it.kind == NodeKind.GroupNode }) { + node.flatMap { it.origins } + } else { + node } - return - } - val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) } - renderedSignatures.subList(0, renderedSignatures.size - 1).forEach { - appendAsSignature(it) { - it.appendSignature() + + val summarySignature = languageService.summarizeSignatures(nodesToAppend) + if (summarySignature != null) { + appendSignatures(summarySignature, items, platformsBasedOnMembers, omitSamePlatforms) + } else { + val groupBySignature = nodesToAppend.groupBy { + languageService.render(it, RenderMode.SUMMARY) + } + for ((sign, members) in groupBySignature) { + appendSignatures(sign, members, platformsBasedOnMembers, omitSamePlatforms) + } + } + + val platforms = platformsOfItems(node) + appendAsBlockWithPlatforms(platforms) { + appendContent(summary) + appendSoftLineBreak() } - appendLine() } - appendAsSignature(renderedSignatures.last()) { - renderedSignatures.last().appendSignature() + } + + private fun appendSignatures(signature: ContentNode, items: List<DocumentationNode>, platformsBasedOnMembers: Boolean, omitSamePlatforms: Boolean) { + val elementPlatforms = platformsOfItems(items, omitSamePlatforms) + val platforms = if (platformsBasedOnMembers) + items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms + else + elementPlatforms + + appendAsBlockWithPlatforms(platforms) { + appendPlatforms(platforms) + appendAsSignature(signature) { + signature.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)) { @@ -654,7 +811,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } appendTableCell { - appendContent(type.summary) + val summary = if (type.isClassLikeGroupNode()) { + type.origins.first().summary + } else { + type.summary + } + + appendContent(summary) } } } @@ -674,9 +837,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } -abstract class StructuredFormatService(locationService: LocationService, +abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator, val languageService: LanguageService, override val extension: String, override final val linkExtension: String = extension) : FormatService { - val locationService: LocationService = locationService.withExtension(linkExtension) + } diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt index 7968824c..c36f98eb 100644 --- a/core/src/main/kotlin/Formats/YamlOutlineService.kt +++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt @@ -3,15 +3,17 @@ package org.jetbrains.dokka import com.google.inject.Inject import java.io.File -class YamlOutlineService @Inject constructor(val locationService: LocationService, - val languageService: LanguageService) : OutlineFormatService { +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: ${locationService.location(node).path}") + to.appendln("$indent url: ${generator.location(node).path}") } override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { |