aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/kotlinc.xml7
-rw-r--r--.idea/runConfigurations/Stdlib.xml2
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt4
-rw-r--r--core/src/main/kotlin/DokkaBootstrapImpl.kt9
-rw-r--r--core/src/main/kotlin/Formats/GFMFormatService.kt21
-rw-r--r--core/src/main/kotlin/Formats/HtmlFormatService.kt9
-rw-r--r--core/src/main/kotlin/Formats/JekyllFormatService.kt18
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt63
-rw-r--r--core/src/main/kotlin/Formats/MarkdownFormatService.kt15
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt203
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt76
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt4
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt7
-rw-r--r--core/src/main/kotlin/Kotlin/ContentBuilder.kt2
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt2
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt2
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt253
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt72
-rw-r--r--core/src/main/kotlin/Markdown/MarkdownProcessor.kt5
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt12
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt23
-rw-r--r--core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt7
-rw-r--r--core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt18
-rw-r--r--core/src/main/kotlin/Utilities/DokkaModules.kt (renamed from core/src/main/kotlin/Utilities/DokkaModule.kt)60
-rw-r--r--core/src/main/resources/dokka/styles/style.css7
-rw-r--r--core/src/test/kotlin/TestAPI.kt56
-rw-r--r--core/src/test/kotlin/format/GFMFormatTest.kt2
-rw-r--r--core/src/test/kotlin/format/HtmlFormatTest.kt10
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt36
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt10
-rw-r--r--core/src/test/kotlin/format/MarkdownFormatTest.kt127
-rw-r--r--core/src/test/kotlin/javadoc/JavadocTest.kt19
-rw-r--r--core/src/test/kotlin/model/ClassTest.kt12
-rw-r--r--core/src/test/kotlin/model/CommentTest.kt7
-rw-r--r--core/src/test/kotlin/model/FunctionTest.kt12
-rw-r--r--core/src/test/kotlin/model/PropertyTest.kt12
-rw-r--r--core/src/test/kotlin/model/TypeAliasTest.kt9
-rw-r--r--core/testdata/classes/sinceKotlin.kt5
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.html18
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.kt12
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.md14
-rw-r--r--core/testdata/format/codeBlock.html6
-rw-r--r--core/testdata/format/dynamicExtension.kt3
-rw-r--r--core/testdata/format/dynamicExtension.md10
-rw-r--r--core/testdata/format/dynamicType.kt2
-rw-r--r--core/testdata/format/dynamicType.md5
-rw-r--r--core/testdata/format/memberExtension.kt8
-rw-r--r--core/testdata/format/memberExtension.md10
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt7
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md8
-rw-r--r--core/testdata/format/multiplatform/groupNode/js.kt8
-rw-r--r--core/testdata/format/multiplatform/groupNode/jvm.kt9
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.md20
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.package.md13
-rw-r--r--core/testdata/format/multiplatform/implied/foo.md24
-rw-r--r--core/testdata/format/multiplatform/implied/js.kt16
-rw-r--r--core/testdata/format/multiplatform/implied/jvm.kt16
-rw-r--r--core/testdata/format/multiplatform/merge/js.kt7
-rw-r--r--core/testdata/format/multiplatform/merge/jvm.kt8
-rw-r--r--core/testdata/format/multiplatform/merge/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/foo.md26
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/js.kt16
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/jvm.kt16
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/foo.md22
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/js.kt2
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/jvm.kt11
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt3
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt3
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt5
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/simple/js.kt7
-rw-r--r--core/testdata/format/multiplatform/simple/jvm.kt8
-rw-r--r--core/testdata/format/multiplatform/simple/multiplatform.package.md9
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt3
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md6
-rw-r--r--core/testdata/format/sinceKotlin.html27
-rw-r--r--core/testdata/format/sinceKotlin.kt5
-rw-r--r--core/testdata/format/sinceKotlin.md14
-rw-r--r--core/testdata/format/sinceKotlin.package.md10
-rw-r--r--core/testdata/format/suspendParam.kt3
-rw-r--r--core/testdata/format/suspendParam.md5
-rw-r--r--core/testdata/format/suspendParam.package.md8
-rw-r--r--core/testdata/format/tripleBackticks.html3
-rw-r--r--core/testdata/format/website-samples/dropImport.md1
-rw-r--r--core/testdata/format/website-samples/newLinesInImportList.kt12
-rw-r--r--core/testdata/format/website-samples/newLinesInImportList.md24
-rw-r--r--core/testdata/format/website-samples/newLinesInSamples.kt19
-rw-r--r--core/testdata/format/website-samples/newLinesInSamples.md31
-rw-r--r--core/testdata/format/website-samples/sample.md1
-rw-r--r--core/testdata/format/website-samples/sampleWithAsserts.md1
-rw-r--r--core/testdata/format/website/dataTags/jre7.kt11
-rw-r--r--core/testdata/format/website/dataTags/js.kt11
-rw-r--r--core/testdata/format/website/dataTags/jvm.kt11
-rw-r--r--core/testdata/format/website/dataTags/multiplatform.package.md71
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/jre7.kt0
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/js.kt8
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/multiplatform.md56
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md43
-rw-r--r--core/testdata/functions/sinceKotlin.kt5
-rw-r--r--core/testdata/javadoc/blankLineInsideCodeBlock.kt12
-rw-r--r--core/testdata/properties/sinceKotlin.kt5
-rw-r--r--core/testdata/typealias/sinceKotlin.kt5
-rw-r--r--gradle.properties4
-rw-r--r--integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt1
-rw-r--r--lib/kotlin-compiler.jarbin22257475 -> 22544590 bytes
-rw-r--r--lib/kotlin-ide-common.jarbin445319 -> 449435 bytes
-rw-r--r--lib/kotlin-script-runtime.jarbin3353 -> 3299 bytes
-rw-r--r--lib/markdown.jarbin379275 -> 375565 bytes
-rw-r--r--runners/ant/src/main/kotlin/ant/dokka.kt24
-rw-r--r--runners/cli/src/main/kotlin/cli/main.kt8
-rw-r--r--runners/gradle-plugin/src/main/kotlin/main.kt1
-rw-r--r--runners/maven-plugin/src/main/kotlin/DokkaMojo.kt9
117 files changed, 1800 insertions, 296 deletions
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 00000000..1c24f9a8
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinCommonCompilerArguments">
+ <option name="languageVersion" value="1.1" />
+ <option name="apiVersion" value="1.1" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/runConfigurations/Stdlib.xml b/.idea/runConfigurations/Stdlib.xml
index 3e0cc995..fc196ae4 100644
--- a/.idea/runConfigurations/Stdlib.xml
+++ b/.idea/runConfigurations/Stdlib.xml
@@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="org.jetbrains.dokka.MainKt" />
<option name="VM_PARAMETERS" value="-Xmx2048m -Xms256m" />
- <option name="PROGRAM_PARAMETERS" value="../kotlin/libraries/stdlib/src ../kotlin/core/builtins ../kotlin/core/reflection.jvm -samples ../kotlin/libraries/stdlib/test -output doc -module stdlib -include ../kotlin/libraries/stdlib/src/Module.md -format kotlin-website -nodeprecated -srcLink ../kotlin=http://github.com/JetBrains/kotlin/blob/master#L" />
+ <option name="PROGRAM_PARAMETERS" value="JVM::../kotlin/libraries/stdlib/src JVM::../kotlin/core/builtins JVM::../kotlin/core/reflection.jvm JVM::../kotlin/core/runtime.jvm JVM,JRE7::../kotlin/libraries/stdlib/jre7/src JVM,JRE8::../kotlin/libraries/stdlib/jre8/src JS::../kotlin/core/builtins JS::../kotlin/dist/kotlin-stdlib-js-sources -samples ../kotlin/libraries/stdlib/test -output ../kotlin-web-site/pages/api/latest/jvm -module stdlib -include ../kotlin/libraries/stdlib/src/Module.md -format kotlin-website-samples -nodeprecated -srcLink ../kotlin=http://github.com/JetBrains/kotlin/blob/master#L -impliedPlatforms JVM,JS" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
index 3f7f616f..15d0a26b 100644
--- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompilerEnvironment
+import org.jetbrains.kotlin.resolve.MultiTargetPlatform
import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
@@ -133,7 +134,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
info, content ->
JvmPackagePartProvider(environment, content.moduleContentScope)
},
- builtIns = builtIns
+ builtIns = { builtIns },
+ modulePlatforms = { MultiTargetPlatform.Specific("JVM") }
)
resolverForProject.resolverForModule(library) // Required before module to initialize library properly
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index eb2b2a65..8038089f 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -10,6 +10,11 @@ fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
}
+fun parseSourceRoot(sourceRoot: String): SourceRoot {
+ val components = sourceRoot.split("::", limit = 2)
+ return SourceRoot(components.last(), if (components.size == 1) listOf() else components[0].split(','))
+}
+
class DokkaBootstrapImpl : DokkaBootstrap {
class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger {
@@ -37,6 +42,7 @@ class DokkaBootstrapImpl : DokkaBootstrap {
outputDir: String,
format: String,
includeNonPublic: Boolean,
+ includeRootPackage: Boolean,
reportUndocumented: Boolean,
skipEmptyPackages: Boolean,
skipDeprecated: Boolean,
@@ -46,7 +52,7 @@ class DokkaBootstrapImpl : DokkaBootstrap {
generator = DokkaGenerator(
DokkaProxyLogger(logger),
classpath,
- sources,
+ sources.map(::parseSourceRoot),
samples,
includes,
moduleName,
@@ -54,6 +60,7 @@ class DokkaBootstrapImpl : DokkaBootstrap {
outputDir,
format,
includeNonPublic,
+ includeRootPackage,
reportUndocumented,
skipEmptyPackages,
skipDeprecated,
diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt
index cfb7fc03..cb31a1d3 100644
--- a/core/src/main/kotlin/Formats/GFMFormatService.kt
+++ b/core/src/main/kotlin/Formats/GFMFormatService.kt
@@ -3,11 +3,12 @@ package org.jetbrains.dokka
import com.google.inject.Inject
open class GFMOutputBuilder(to: StringBuilder,
- location: Location,
- locationService: LocationService,
- languageService: LanguageService,
- extension: String)
- : MarkdownOutputBuilder(to, location, locationService, languageService, extension)
+ location: Location,
+ locationService: LocationService,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>)
+ : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
{
override fun appendTable(vararg columns: String, body: () -> Unit) {
to.appendln(columns.joinToString(" | ", "| ", " |"))
@@ -45,12 +46,14 @@ open class GFMOutputBuilder(to: StringBuilder,
open class GFMFormatService(locationService: LocationService,
signatureGenerator: LanguageService,
- linkExtension: String)
-: MarkdownFormatService(locationService, signatureGenerator, linkExtension) {
+ linkExtension: String,
+ impliedPlatforms: List<String>)
+: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) {
@Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService) : this(locationService, signatureGenerator, "md")
+ signatureGenerator: LanguageService,
+ impliedPlatforms: List<String>) : this(locationService, signatureGenerator, "md", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- GFMOutputBuilder(to, location, locationService, languageService, extension)
+ GFMOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt
index d73e4e62..6819e652 100644
--- a/core/src/main/kotlin/Formats/HtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -2,6 +2,7 @@ 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
import java.nio.file.Path
import java.nio.file.Paths
@@ -11,8 +12,9 @@ open class HtmlOutputBuilder(to: StringBuilder,
locationService: LocationService,
languageService: LanguageService,
extension: String,
+ impliedPlatforms: List<String>,
val templateService: HtmlTemplateService)
- : StructuredOutputBuilder(to, location, locationService, languageService, extension)
+ : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
{
override fun appendText(text: String) {
to.append(text.htmlEscape())
@@ -95,7 +97,8 @@ open class HtmlOutputBuilder(to: StringBuilder,
open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService,
signatureGenerator: LanguageService,
- val templateService: HtmlTemplateService)
+ val templateService: HtmlTemplateService,
+ @Named(impliedPlatformsName) val impliedPlatforms: List<String>)
: StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService {
override fun enumerateSupportFiles(callback: (String, String) -> Unit) {
@@ -103,7 +106,7 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi
}
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- HtmlOutputBuilder(to, location, locationService, languageService, extension, templateService)
+ HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, templateService)
override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
templateService.appendHeader(to, "Module Contents", locationService.calcPathToRoot(location))
diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt
index bab73379..d217bf38 100644
--- a/core/src/main/kotlin/Formats/JekyllFormatService.kt
+++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt
@@ -1,13 +1,16 @@
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,
locationService: LocationService,
languageService: LanguageService,
- extension: String)
- : MarkdownOutputBuilder(to, location, locationService, languageService, extension)
+ extension: String,
+ impliedPlatforms: List<String>)
+ : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
{
override fun appendNodes(nodes: Iterable<DocumentationNode>) {
to.appendln("---")
@@ -25,14 +28,15 @@ open class JekyllOutputBuilder(to: StringBuilder,
open class JekyllFormatService(locationService: LocationService,
signatureGenerator: LanguageService,
- linkExtension: String)
-: MarkdownFormatService(locationService, signatureGenerator, linkExtension) {
+ linkExtension: String,
+ impliedPlatforms: List<String>)
+: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) {
@Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md") {
- }
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- JekyllOutputBuilder(to, location, locationService, languageService, extension)
+ JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
index 007e9353..eac053aa 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -1,14 +1,18 @@
package org.jetbrains.dokka
import com.google.inject.Inject
+import com.google.inject.name.Named
+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)
- : JekyllOutputBuilder(to, location, locationService, languageService, extension)
+ extension: String,
+ impliedPlatforms: List<String>)
+ : JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
{
private var needHardLineBreaks = false
private var insideDiv = 0
@@ -30,8 +34,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
- protected fun div(to: StringBuilder, cssClass: String, markdown: Boolean = false, block: () -> Unit) {
- to.append("<div class=\"$cssClass\"")
+ protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) {
+ to.append("<div class=\"$cssClass\"$otherAttributes")
if (markdown) to.append(" markdown=\"1\"")
to.append(">")
if (!markdown) insideDiv++
@@ -53,8 +57,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
}
}
- override fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) {
- div(to, "overload-group", true) {
+ override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ div(to, "overload-group", calculateDataAttributes(platforms), true) {
ensureParagraph()
block()
ensureParagraph()
@@ -142,14 +146,39 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
else -> "identifier"
}
+
+ fun calculateDataAttributes(platforms: Set<String>): String {
+ fun String.isKotlinVersion() = this.startsWith("Kotlin")
+ fun String.isJREVersion() = this.startsWith("JRE")
+ val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)
+ val jreVersion = platforms.singleOrNull(String::isJREVersion)
+ val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() }
+
+ val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: ""
+ val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: ""
+ val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: ""
+ return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
+ }
+
+ override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ if (platforms.isNotEmpty())
+ wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+ else
+ appendTableRow(block)
+ }
+
+ override fun appendPlatforms(platforms: Set<String>) {
+
+ }
}
class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService)
- : JekyllFormatService(locationService, signatureGenerator, "html")
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>)
+ : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms)
{
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension)
+ KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
}
@@ -157,14 +186,15 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder,
location: Location,
locationService: LocationService,
languageService: LanguageService,
- extension: String)
- : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension) {
+ extension: String,
+ impliedPlatforms: List<String>)
+ : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) {
override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
- div(to, "sample", true) {
+ div(to, "sample", markdown = true) {
appendBlockCode(language) {
imports()
- wrap("\nfun main(args: Array<String>) {", "}") {
+ wrap("\n\nfun main(args: Array<String>) {", "}") {
wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
}
}
@@ -173,9 +203,10 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder,
}
class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService)
- : JekyllFormatService(locationService, signatureGenerator, "html") {
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>)
+ : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) {
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension)
+ KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
index fc63b2f2..b9c9c04f 100644
--- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -1,6 +1,8 @@
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 {
@@ -14,8 +16,9 @@ open class MarkdownOutputBuilder(to: StringBuilder,
location: Location,
locationService: LocationService,
languageService: LanguageService,
- extension: String)
- : StructuredOutputBuilder(to, location, locationService, languageService, extension)
+ extension: String,
+ impliedPlatforms: List<String>)
+ : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
{
private val listKindStack = Stack<ListKind>()
protected var inTableCell = false
@@ -211,11 +214,13 @@ open class MarkdownOutputBuilder(to: StringBuilder,
open class MarkdownFormatService(locationService: LocationService,
signatureGenerator: LanguageService,
- linkExtension: String)
+ linkExtension: String,
+ val impliedPlatforms: List<String>)
: StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) {
@Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md")
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- MarkdownOutputBuilder(to, location, locationService, languageService, extension)
+ MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 7896bcd8..84f91d9c 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -9,7 +9,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val location: Location,
val locationService: LocationService,
val languageService: LanguageService,
- val extension: String) : FormattedOutputBuilder {
+ val extension: String,
+ val impliedPlatforms: List<String>) : FormattedOutputBuilder {
protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
to.append(prefix)
@@ -59,6 +60,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
abstract fun appendText(text: String)
+ open fun appendSinceKotlin(version: String) {
+ appendParagraph {
+ appendText("Available since Kotlin: ")
+ appendCode { appendText(version) }
+ }
+ }
+
open fun appendSymbol(text: String) {
appendText(text)
}
@@ -144,12 +152,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
is ContentBlockSampleCode, is ContentBlockCode -> {
content as ContentBlockCode
fun ContentBlockCode.appendBlockCodeContent() {
- for ((index, contentNode) in this.children.withIndex()) {
- appendContent(contentNode)
- if (index < this.children.size - 1) {
- to.append("\n")
- }
- }
+ children
+ .dropWhile { it is ContentText && it.text.isBlank() }
+ .forEach { appendContent(it) }
}
when (content) {
is ContentBlockSampleCode ->
@@ -179,6 +184,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
to: DocumentationNode,
extension: String,
name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
+ if (to.owner?.kind == NodeKind.GroupNode)
+ return link(from, to.owner!!, extension, name)
+
+ if (from.owner?.kind == NodeKind.GroupNode)
+ return link(from.owner!!, to, extension, name)
+
return FormatLink(name(to), locationService.relativePathToLocation(from, to))
}
@@ -198,15 +209,25 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
block()
}
- protected open fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) {
+ protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
block()
}
+ protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ appendTableRow(block)
+ }
+
+ protected open fun appendPlatforms(platforms: Set<String>) {
+ if (platforms.isNotEmpty()) {
+ appendLine()
+ appendText(platforms.joinToString(prefix = "(", postfix = ")"))
+ }
+ }
+
protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) {
for ((index, item) in path.withIndex()) {
if (index > 0) {
appendBreadcrumbSeparator()
-
}
appendLink(item)
}
@@ -225,16 +246,18 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendContent(signatureAsCode)
}
- open inner class PageBuilder(val nodes: Iterable<DocumentationNode>) {
+ 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) }
+ node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct()
}
for ((path, nodes) in breakdownByLocation) {
- appendBreadcrumbs(path)
- appendLine()
- appendLine()
+ if (!noHeader) {
+ appendBreadcrumbs(path)
+ appendLine()
+ appendLine()
+ }
appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass })
}
}
@@ -246,11 +269,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name
appendHeader(2) { appendText("Package $packageName") }
}
+ singleNode.appendPlatforms()
appendContent(singleNode.content)
} else {
val breakdownByName = nodes.groupBy { node -> node.name }
for ((name, items) in breakdownByName) {
- appendHeader { appendText(name) }
+ if (!noHeader)
+ appendHeader { appendText(name) }
appendDocumentation(items)
}
}
@@ -262,9 +287,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
if (breakdownBySummary.size == 1) {
formatOverloadGroup(breakdownBySummary.values.single())
} else {
- for ((summary, items) in breakdownBySummary) {
+ for ((_, items) in breakdownBySummary) {
ensureParagraph()
- appendAsOverloadGroup(to) {
+ appendAsOverloadGroup(to, platformsOfItems(items)) {
formatOverloadGroup(items)
}
}
@@ -284,12 +309,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
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()
item.details(NodeKind.OverloadGroupNote).forEach {
appendContent(it.content)
}
+
appendContent(item.content.summary)
item.appendDescription()
}
@@ -334,6 +361,37 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ private fun DocumentationNode.appendPlatforms() {
+ val platforms = if (isModuleOrPackage())
+ platformsToShow.toSet() + platformsOfItems(members)
+ else
+ platformsToShow
+
+ if(platforms.isEmpty()) return
+
+ appendParagraph {
+ appendStrong { to.append("Platform and version requirements:") }
+ to.append(" " + platforms.joinToString())
+ }
+ }
+
+ 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)
+ else -> it.platformsToShow.toSet()
+ }
+ }
+ // Calculating common platforms for items
+ return platforms.fold(platforms.first()) {
+ result, platforms ->
+ result.intersect(platforms)
+ }
+ }
+
+ val DocumentationNode.platformsToShow: List<String>
+ get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it }
+
private fun DocumentationNode.appendDescription() {
if (content.description != ContentEmpty) {
appendContent(content.description)
@@ -367,8 +425,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- inner class SingleNodePageBuilder(val node: DocumentationNode)
- : PageBuilder(listOf(node)) {
+ 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) }
+
+ fun DocumentationNode.priority(): Int = when (kind) {
+ NodeKind.TypeAlias -> 1
+ NodeKind.Class -> 2
+ else -> 3
+ }
+
+ for (member in node.members.sortedBy(DocumentationNode::priority)) {
+ ensureParagraph()
+ appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
+ formatSubNodeOfGroup(member)
+ }
+ }
+ }
+
+ fun formatSubNodeOfGroup(member: DocumentationNode) {
+ SingleNodePageBuilder(member, true).build()
+ }
+ }
+
+ inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
+ : PageBuilder(listOf(node), noHeader) {
override fun build() {
super.build()
@@ -378,21 +466,29 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
return
}
- appendSection("Packages", node.members(NodeKind.Package))
- appendSection("Types", node.members.filter { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
- appendSection("Annotations", node.members(NodeKind.AnnotationClass))
- appendSection("Exceptions", node.members(NodeKind.Exception))
- appendSection("Type Aliases", node.members(NodeKind.TypeAlias))
+ fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> {
+ return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) }
+ }
+
+ 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("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias))
appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass))
- appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false)
- appendSection("Constructors", node.members(NodeKind.Constructor))
- appendSection("Properties", node.members(NodeKind.Property))
+ 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("Inherited Properties", node.inheritedMembers(NodeKind.Property))
- appendSection("Functions", node.members(NodeKind.Function))
+ appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true)
appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function))
- appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty))
+ appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property))
- appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction))
+ appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function))
appendSection("Other members", node.members.filter {
it.kind !in setOf(
@@ -411,7 +507,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
NodeKind.CompanionObjectFunction,
NodeKind.ExternalClass,
NodeKind.EnumItem,
- NodeKind.AllTypes
+ NodeKind.AllTypes,
+ NodeKind.GroupNode
)
})
@@ -431,7 +528,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- private fun appendSection(caption: String, members: List<DocumentationNode>, sortMembers: Boolean = true) {
+ 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) }
@@ -439,12 +539,22 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val children = if (sortMembers) members.sortedBy { it.name } else members
val membersMap = children.groupBy { link(node, it) }
+
+
appendTable("Name", "Summary") {
- appendTableBody() {
+ appendTableBody {
for ((memberLocation, members) in membersMap) {
- appendTableRow() {
+ val elementPlatforms = platformsOfItems(members, omitSamePlatforms)
+ val platforms = if (platformsBasedOnMembers)
+ members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+ else
+ elementPlatforms
+ appendIndexRow(platforms) {
appendTableCell {
appendLink(memberLocation)
+ if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
+ appendPlatforms(platforms)
+ }
}
appendTableCell {
val breakdownBySummary = members.groupBy { it.summary }
@@ -459,6 +569,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
+ val platforms = platformsOfItems(items)
+ if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
+ return platforms
+ }
+ return emptySet()
+ }
+
private fun appendSummarySignatures(items: List<DocumentationNode>) {
val summarySignature = languageService.summarizeSignatures(items)
if (summarySignature != null) {
@@ -488,9 +606,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendHeader(3) { to.append("All Types") }
appendTable("Name", "Summary") {
- appendTableBody() {
+ appendTableBody {
for (type in node.members) {
- appendTableRow() {
+ appendTableRow {
appendTableCell {
appendLink(link(node, type) {
if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName()
@@ -514,14 +632,11 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
override fun appendNodes(nodes: Iterable<DocumentationNode>) {
val singleNode = nodes.singleOrNull()
- if (singleNode != null) {
- if (singleNode.kind == NodeKind.AllTypes) {
- AllTypesNodeBuilder(singleNode).build()
- } else {
- SingleNodePageBuilder(singleNode).build()
- }
- } else {
- PageBuilder(nodes).build()
+ when (singleNode?.kind) {
+ NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build()
+ NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build()
+ null -> PageBuilder(nodes).build()
+ else -> SingleNodePageBuilder(singleNode).build()
}
}
}
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 360965d5..6b5df7c9 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -7,7 +7,8 @@ import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
-import org.jetbrains.dokka.Utilities.DokkaModule
+import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
+import org.jetbrains.dokka.Utilities.DokkaOutputModule
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
@@ -15,47 +16,76 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.config.JVMConfigurationKeys
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import kotlin.system.measureTimeMillis
+class SourceRoot(path: String, val defaultPlatforms: List<String> = emptyList()) {
+ val path: String = File(path).absolutePath
+}
+
class DokkaGenerator(val logger: DokkaLogger,
val classpath: List<String>,
- val sources: List<String>,
+ val sources: List<SourceRoot>,
val samples: List<String>,
val includes: List<String>,
val moduleName: String,
val options: DocumentationOptions) {
+
+ private val documentationModule = DocumentationModule(moduleName)
+
fun generate() {
- val environment = createAnalysisEnvironment()
+ val sourcesGroupedByPlatform = sources.groupBy { it.defaultPlatforms.firstOrNull() }
+ for ((platform, roots) in sourcesGroupedByPlatform) {
+ appendSourceModule(platform, roots)
+ }
+ documentationModule.prepareForGeneration(options)
+
+ val timeBuild = measureTimeMillis {
+ logger.info("Generating pages... ")
+ val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger))
+ outputInjector.getInstance(Generator::class.java).buildAll(documentationModule)
+ }
+ logger.info("done in ${timeBuild / 1000} secs")
+ }
+
+ private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) {
+ val sourcePaths = sourceRoots.map { it.path }
+ val environment = createAnalysisEnvironment(sourcePaths)
logger.info("Module: $moduleName")
logger.info("Output: ${File(options.outputDir)}")
- logger.info("Sources: ${environment.sources.joinToString()}")
+ logger.info("Sources: ${sourcePaths.joinToString()}")
logger.info("Classpath: ${environment.classpath.joinToString()}")
logger.info("Analysing sources and libraries... ")
val startAnalyse = System.currentTimeMillis()
- val injector = Guice.createInjector(DokkaModule(environment, options, logger))
+ val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty()
+ val defaultPlatformsProvider = object : DefaultPlatformsProvider {
+ override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> {
+ val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath
+ ?.let { File(it).absolutePath }
+ val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } }
+ return sourceRoot?.defaultPlatforms ?: defaultPlatformAsList
+ }
+ }
+
+ val injector = Guice.createInjector(
+ DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger))
- val documentation = buildDocumentationModule(injector, moduleName, { isSample(it) }, includes)
+ buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes)
val timeAnalyse = System.currentTimeMillis() - startAnalyse
logger.info("done in ${timeAnalyse / 1000} secs")
- val timeBuild = measureTimeMillis {
- logger.info("Generating pages... ")
- injector.getInstance(Generator::class.java).buildAll(documentation)
- }
- logger.info("done in ${timeBuild / 1000} secs")
-
Disposer.dispose(environment)
}
- fun createAnalysisEnvironment(): AnalysisEnvironment {
+ fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment {
val environment = AnalysisEnvironment(DokkaMessageCollector(logger))
environment.apply {
@@ -65,14 +95,14 @@ class DokkaGenerator(val logger: DokkaLogger,
addClasspath(File(element))
}
- addSources(this@DokkaGenerator.sources)
+ addSources(sourcePaths)
addSources(this@DokkaGenerator.samples)
}
return environment
}
- fun isSample(file: PsiFile): Boolean {
+ fun isNotSample(file: PsiFile): Boolean {
val sourceFile = File(file.virtualFile!!.path)
return samples.none { sample ->
val canonicalSample = File(sample).canonicalPath
@@ -100,9 +130,9 @@ class DokkaMessageCollector(val logger: DokkaLogger): MessageCollector {
}
fun buildDocumentationModule(injector: Injector,
- moduleName: String,
+ documentationModule: DocumentationModule,
filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true },
- includes: List<String> = listOf()): DocumentationModule {
+ includes: List<String> = listOf()) {
val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java)
val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter)
@@ -120,7 +150,13 @@ fun buildDocumentationModule(injector: Injector,
for (include in includes) {
packageDocs.parse(include, fragments.firstOrNull())
}
- val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent)
+ if (documentationModule.content.isEmpty()) {
+ documentationModule.updateContent {
+ for (node in packageDocs.moduleContent.children) {
+ append(node)
+ }
+ }
+ }
with(injector.getInstance(DocumentationBuilder::class.java)) {
documentationModule.appendFragments(fragments, packageDocs.packageContent,
@@ -131,10 +167,6 @@ fun buildDocumentationModule(injector: Injector,
with(injector.getInstance(JavaDocumentationBuilder::class.java)) {
javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) }
}
-
- injector.getInstance(NodeReferenceGraph::class.java).resolveReferences()
-
- return documentationModule
}
diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
index 806e9f92..b2f4aeaf 100644
--- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -44,10 +44,10 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
private val refGraph: NodeReferenceGraph
private val docParser: JavaDocumentationParser
- @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph) {
+ @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) {
this.options = options
this.refGraph = refGraph
- this.docParser = JavadocParser(refGraph)
+ this.docParser = JavadocParser(refGraph, logger)
}
constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) {
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
index 0fb98230..af45f150 100644
--- a/core/src/main/kotlin/Java/JavadocParser.kt
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -20,7 +20,8 @@ interface JavaDocumentationParser {
fun parseDocumentation(element: PsiNamedElement): JavadocParseResult
}
-class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser {
+class JavadocParser(private val refGraph: NodeReferenceGraph,
+ private val logger: DokkaLogger) : JavaDocumentationParser {
override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
val docComment = (element as? PsiDocCommentOwner)?.docComment
if (docComment == null) return JavadocParseResult.Empty
@@ -99,7 +100,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio
private fun createLink(element: Element): ContentBlock {
val docref = element.attr("docref")
if (docref != null) {
- return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)})
+ return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)})
}
val href = element.attr("href")
if (href != null) {
@@ -118,7 +119,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio
val linkSignature = resolveLink(linkElement)
val text = ContentText(linkElement.text)
if (linkSignature != null) {
- val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookup(linkSignature)})
+ val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)})
linkNode.append(text)
seeSection.append(linkNode)
} else {
diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
index 53afebaf..9c081d17 100644
--- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
@@ -148,7 +148,7 @@ fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (Stri
private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text }
-private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection
+private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode
fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (String) -> ContentBlock) {
val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree)
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
index 4c46f7d6..71b636bf 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -31,7 +31,7 @@ class DeclarationLinkResolver
if (jdkHref != null) {
return ContentExternalLink(jdkHref)
}
- return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) })
+ return ContentNodeLazyLink(href, { -> refGraph.lookupOrWarn(symbol.signature(), logger) })
}
if ("/" in href) {
return ContentExternalLink(href)
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
index 14d04397..6d7ff7ba 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -116,7 +116,7 @@ class DescriptorDocumentationParser
fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> {
val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
if (psi is PsiDocCommentOwner) {
- val parseResult = JavadocParser(refGraph).parseDocumentation(psi as PsiNamedElement)
+ val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement)
return parseResult.content to { node ->
parseResult.deprecatedContent?.let {
val deprecationNode = DocumentationNode("", it, NodeKind.Modifier)
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index afb95fe6..299ad477 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -28,16 +28,19 @@ import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.supertypes
+import com.google.inject.name.Named as GuiceNamed
data class DocumentationOptions(val outputDir: String,
val outputFormat: String,
val includeNonPublic: Boolean = false,
+ val includeRootPackage: Boolean = false,
val reportUndocumented: Boolean = true,
val skipEmptyPackages: Boolean = true,
val skipDeprecated: Boolean = false,
val jdkVersion: Int = 6,
val generateIndexPages: Boolean = true,
- val sourceLinks: List<SourceLinkDefinition>)
+ val sourceLinks: List<SourceLinkDefinition> = emptyList(),
+ val impliedPlatforms: List<String> = emptyList())
private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor,
extensionReceiverDescriptor: DeclarationDescriptor,
@@ -57,13 +60,19 @@ interface PackageDocumentationBuilder {
allFqNames: Collection<FqName>)
}
+interface DefaultPlatformsProvider {
+ fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String>
+}
+
class DocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val descriptorDocumentationParser: DescriptorDocumentationParser,
val options: DocumentationOptions,
val refGraph: NodeReferenceGraph,
+ val platformNodeRegistry: PlatformNodeRegistry,
val logger: DokkaLogger,
- val linkResolver: DeclarationLinkResolver) {
+ val linkResolver: DeclarationLinkResolver,
+ val defaultPlatformsProvider: DefaultPlatformsProvider) {
val boringBuiltinClasses = setOf(
"kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean",
"kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any")
@@ -156,6 +165,11 @@ class DocumentationBuilder
return appendType(it.abbreviation)
}
+ if (kotlinType.isDynamic()) {
+ append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail)
+ return
+ }
+
val classifierDescriptor = kotlinType.constructor.declarationDescriptor
val name = when (classifierDescriptor) {
is ClassDescriptor -> {
@@ -199,17 +213,35 @@ class DocumentationBuilder
fun DocumentationNode.appendAnnotations(annotated: Annotated) {
annotated.annotations.forEach {
it.build()?.let { annotationNode ->
- val refKind = when {
- it.isDocumented() && annotationNode.isDeprecation() -> RefKind.Deprecation
- it.isDocumented() -> RefKind.Annotation
- it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation
- else -> return@forEach
+ if (annotationNode.isSinceKotlin()) {
+ appendSinceKotlin(annotationNode)
}
- append(annotationNode, refKind)
+ else {
+ val refKind = when {
+ it.isDocumented() ->
+ when {
+ annotationNode.isDeprecation() -> RefKind.Deprecation
+ else -> RefKind.Annotation
+ }
+ it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation
+ else -> return@forEach
+ }
+ append(annotationNode, refKind)
+ }
+
}
}
}
+ fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
+ val kotlinVersion = annotation
+ .detail(NodeKind.Parameter)
+ .detail(NodeKind.Value)
+ .name.removeSurrounding("\"")
+
+ append(platformNodeRegistry["Kotlin " + kotlinVersion], RefKind.Platform)
+ }
+
fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
@@ -219,8 +251,16 @@ class DocumentationBuilder
}
}
+ fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) {
+ for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) {
+ append(platformNodeRegistry[platform], RefKind.Platform)
+ }
+ }
+
fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated"
+ fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation
+
fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
appendSourceLink(sourceElement.getPsi(), options.sourceLinks)
}
@@ -230,11 +270,7 @@ class DocumentationBuilder
}
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? {
- // do not include generated code
- if (descriptor is CallableMemberDescriptor && descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION)
- return null
-
- if (descriptor.isDocumented(options)) {
+ if (!descriptor.isGenerated() && descriptor.isDocumented(options)) {
val node = descriptor.build()
append(node, kind)
return node
@@ -242,21 +278,82 @@ class DocumentationBuilder
return null
}
- fun DocumentationNode.appendMembers(descriptors: Iterable<DeclarationDescriptor>,
- inheritedLinkKind: RefKind = RefKind.InheritedMember): List<DocumentationNode> {
- val nodes = descriptors.map { descriptor ->
- if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
- val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull()
- if (baseDescriptor != null) {
- link(this, baseDescriptor, inheritedLinkKind)
+ fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?:
+ DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply {
+ appendTextNode(signature, NodeKind.Signature, RefKind.Detail)
+ })
+ .also { groupNode ->
+ nodes.forEach { node ->
+ if (node != groupNode) {
+ node.owner?.let { owner ->
+ node.dropReferences { it.to == owner && it.kind == RefKind.Owner }
+ owner.dropReferences { it.to == node && it.kind == RefKind.Member }
+ owner.append(groupNode, RefKind.Member)
+ }
+ groupNode.append(node, RefKind.Member)
+ }
}
- null
- } else {
- val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original
- appendChild(descriptorToUse, RefKind.Member)
+ }
+
+
+ fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) {
+ if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return
+
+ val existingNode = refGraph.lookup(descriptor.signature())
+ if (existingNode != null) {
+ if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor
+ || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) {
+ val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build()))
+ register(descriptor, node)
+ return
+ }
+
+ existingNode.updatePlatforms(descriptor)
+
+ if (descriptor is ClassDescriptor) {
+ val membersToDocument = descriptor.collectMembersToDocument()
+ for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) {
+ if (memberDescriptor is ClassDescriptor) {
+ existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes
+ }
+ else {
+ val existingMemberNode = refGraph.lookup(memberDescriptor.signature())
+ if (existingMemberNode != null) {
+ existingMemberNode.updatePlatforms(memberDescriptor)
+ }
+ else {
+ existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier)
+ }
+ }
+ }
+ }
+ }
+ else {
+ appendChild(descriptor, RefKind.Member)
+ }
+ }
+
+ private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) {
+ for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) {
+ append(platformNodeRegistry[platform], RefKind.Platform)
+ }
+ }
+
+ fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor,
+ inheritedLinkKind: RefKind = RefKind.InheritedMember,
+ extraModifier: String?) {
+ if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
+ val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull()
+ if (baseDescriptor != null) {
+ link(this, baseDescriptor, inheritedLinkKind)
+ }
+ } else {
+ val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original
+ val child = appendChild(descriptorToUse, RefKind.Member)
+ if (extraModifier != null) {
+ child?.appendTextNode("static", NodeKind.Modifier)
}
}
- return nodes.filterNotNull()
}
fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) {
@@ -272,6 +369,7 @@ class DocumentationBuilder
val allFqNames = fragments.map { it.fqName }.distinct()
for (packageName in allFqNames) {
+ if (packageName.isRoot && !options.includeRootPackage) continue
val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() }
if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue
@@ -282,9 +380,6 @@ class DocumentationBuilder
}
propagateExtensionFunctionsToSubclasses(fragments)
- if (options.generateIndexPages) {
- generateAllTypesNode()
- }
}
private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
@@ -297,13 +392,15 @@ class DocumentationBuilder
.filter { it.extensionReceiverParameter != null }
val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
- allExtensionFunctions.forEach { extensionFunction ->
+ for (extensionFunction in allExtensionFunctions) {
+ if (extensionFunction.dispatchReceiverParameter != null) continue
val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
?.filter { fn -> fn.canShadow(extensionFunction) }
?: emptyList()
- val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: return@forEach
- val subclasses = classHierarchy[classDescriptor] ?: return@forEach
+ if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
+ val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
+ val subclasses = classHierarchy[classDescriptor] ?: continue
subclasses.forEach { subclass ->
if (subclass.isExtensionApplicable(extensionFunction) &&
possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
@@ -357,19 +454,6 @@ class DocumentationBuilder
return false
}
- private fun DocumentationNode.generateAllTypesNode() {
- val allTypes = members(NodeKind.Package)
- .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } }
- .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name }
-
- val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
- for (typeNode in allTypes) {
- allTypesNode.addReferenceTo(typeNode, RefKind.Member)
- }
-
- append(allTypesNode, RefKind.Member)
- }
-
fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
is ClassDescriptor -> build()
is ConstructorDescriptor -> build()
@@ -392,6 +476,7 @@ class DocumentationBuilder
node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
register(this, node)
return node
@@ -413,28 +498,48 @@ class DocumentationBuilder
}
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
- val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS)
+ }
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
+ node.appendModifiers(this)
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ register(this, node)
+ return node
+ }
+
+ data class ClassMember(val descriptor: DeclarationDescriptor,
+ val inheritedLinkKind: RefKind = RefKind.InheritedMember,
+ val extraModifier: String? = null)
+
+ fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> {
+ val result = arrayListOf<ClassMember>()
+ if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
+ val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS)
constructors.filter { it.valueParameters.size > 0 }
else
constructors
- node.appendMembers(constructorsToDocument)
- }
- val members = defaultType.memberScope.getContributedDescriptors().filter { it != companionObjectDescriptor }
- node.appendMembers(members)
- node.appendMembers(staticScope.getContributedDescriptors()).forEach {
- it.appendTextNode("static", NodeKind.Modifier)
+ constructorsToDocument.mapTo(result) { ClassMember(it) }
}
+
+ defaultType.memberScope.getContributedDescriptors()
+ .filter { it != companionObjectDescriptor }
+ .mapTo(result) { ClassMember(it) }
+
+ staticScope.getContributedDescriptors()
+ .mapTo(result) { ClassMember(it, extraModifier = "static") }
+
val companionObjectDescriptor = companionObjectDescriptor
if (companionObjectDescriptor != null) {
val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()
val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() }
- node.appendMembers(descriptorsToDocument, RefKind.InheritedCompanionObjectMember)
+ descriptorsToDocument.mapTo(result) {
+ ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember)
+ }
}
- node.appendAnnotations(this)
- node.appendModifiers(this)
- node.appendSourceLink(source)
- register(this, node)
- return node
+ return result
}
fun CallableDescriptor.isInheritedFromAny(): Boolean {
@@ -449,6 +554,7 @@ class DocumentationBuilder
fun ConstructorDescriptor.build(): DocumentationNode {
val node = nodeForDescriptor(this, NodeKind.Constructor)
node.appendInPageChildren(valueParameters, RefKind.Detail)
+ node.appendDefaultPlatforms(this)
register(this, node)
return node
}
@@ -477,6 +583,7 @@ class DocumentationBuilder
node.appendModifiers(this)
node.appendSourceLink(source)
node.appendSignature(this)
+ node.appendDefaultPlatforms(this)
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -523,6 +630,7 @@ class DocumentationBuilder
overriddenDescriptors.forEach {
addOverrideLink(it, this)
}
+ node.appendDefaultPlatforms(this)
register(this, node)
return node
@@ -596,9 +704,10 @@ class DocumentationBuilder
receiverClass = upperBoundClass
}
}
- link(receiverClass,
- containingDeclaration,
- RefKind.Extension)
+
+ if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) {
+ link(receiverClass, containingDeclaration, RefKind.Extension)
+ }
val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver)
node.appendType(type)
@@ -646,6 +755,8 @@ fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean {
(!options.skipDeprecated || !isDeprecated())
}
+private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION
+
class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
packageName: FqName,
@@ -657,7 +768,7 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
with(documentationBuilder) {
if (descriptor.isDocumented(options)) {
val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames)
- parent.appendChild(descriptor, RefKind.Member)
+ parent.appendOrUpdateMember(descriptor)
}
}
}
@@ -819,3 +930,23 @@ fun DeclarationDescriptor.sourceLocation(): String? {
}
return null
}
+
+fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
+ if (options.generateIndexPages) {
+ generateAllTypesNode()
+ }
+ nodeRefGraph.resolveReferences()
+}
+
+fun DocumentationNode.generateAllTypesNode() {
+ val allTypes = members(NodeKind.Package)
+ .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } }
+ .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name }
+
+ val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
+ for (typeNode in allTypes) {
+ allTypesNode.addReferenceTo(typeNode, RefKind.Member)
+ }
+
+ append(allTypesNode, RefKind.Member)
+}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index 75a8a948..f33c8c96 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -151,26 +151,47 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
+ private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
var typeArguments = node.details(NodeKind.Type)
- if (node.name == "Function${typeArguments.count() - 1}") {
- // lambda
- val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" }
- if (isExtension) {
- renderType(typeArguments.first(), renderMode)
- symbol(".")
- typeArguments = typeArguments.drop(1)
- }
- symbol("(")
- renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
- renderFunctionalTypeParameterName(it, renderMode)
- renderType(it, renderMode)
- }
- symbol(")")
- nbsp()
- symbol("->")
- nbsp()
- renderType(typeArguments.last(), renderMode)
+
+ if (node.name.startsWith("Suspend")) {
+ keyword("suspend ")
+ }
+
+ // lambda
+ val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" }
+ if (isExtension) {
+ renderType(typeArguments.first(), renderMode)
+ symbol(".")
+ typeArguments = typeArguments.drop(1)
+ }
+ symbol("(")
+ renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
+ renderFunctionalTypeParameterName(it, renderMode)
+ renderType(it, renderMode)
+ }
+ symbol(")")
+ nbsp()
+ symbol("->")
+ nbsp()
+ renderType(typeArguments.last(), renderMode)
+
+ }
+
+ private fun DocumentationNode.isFunctionalType(): Boolean {
+ val typeArguments = details(NodeKind.Type)
+ val functionalTypeName = "Function${typeArguments.count() - 1}"
+ val suspendFunctionalTypeName = "Suspend$functionalTypeName"
+ return name == functionalTypeName || name == suspendFunctionalTypeName
+ }
+
+ private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
+ if (node.name == "dynamic") {
+ keyword("dynamic")
+ return
+ }
+ if (node.isFunctionalType()) {
+ renderFunctionalType(node, renderMode)
return
}
if (renderMode == RenderMode.FULL) {
@@ -178,7 +199,8 @@ class KotlinLanguageService : LanguageService {
}
renderModifiersForNode(node, renderMode, true)
renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
- if (typeArguments.any()) {
+ val typeArguments = node.details(NodeKind.Type)
+ if (typeArguments.isNotEmpty()) {
symbol("<")
renderList(typeArguments, noWrap = true) {
renderType(it, renderMode)
@@ -392,7 +414,15 @@ class KotlinLanguageService : LanguageService {
if (signatureMapper != null) {
signatureMapper.renderReceiver(receiver, this)
} else {
- renderType(receiver.detail(NodeKind.Type), renderMode)
+ val type = receiver.detail(NodeKind.Type)
+
+ if (type.isFunctionalType()) {
+ symbol("(")
+ renderFunctionalType(type, renderMode)
+ symbol(")")
+ } else {
+ renderType(type, renderMode)
+ }
}
symbol(".")
}
diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt
index 46b72c03..d1d40dd4 100644
--- a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt
+++ b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt
@@ -4,13 +4,14 @@ import org.intellij.markdown.IElementType
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.LeafASTNode
+import org.intellij.markdown.ast.getTextInNode
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.parser.MarkdownParser
class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) {
val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) }
val type: IElementType get() = node.type
- val text: String get() = markdown.substring(node.startOffset, node.endOffset)
+ val text: String get() = node.getTextInNode(markdown).toString()
fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }
override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString()
@@ -30,6 +31,7 @@ fun MarkdownNode.toTestString(): String {
visit { node, visitChildren ->
sb.append(" ".repeat(level * 2))
node.presentTo(sb)
+ sb.appendln()
level++
visitChildren()
level--
@@ -40,7 +42,6 @@ fun MarkdownNode.toTestString(): String {
private fun MarkdownNode.presentTo(sb: StringBuilder) {
sb.append(type.toString())
sb.append(":" + text.replace("\n", "\u23CE"))
- sb.appendln()
}
fun parseMarkdown(markdown: String): MarkdownNode {
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index def0f626..c38a6a9f 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -48,6 +48,7 @@ enum class NodeKind {
Signature,
ExternalLink,
+ Platform,
AllTypes,
@@ -55,7 +56,9 @@ enum class NodeKind {
* A note which is rendered once on a page documenting a group of overloaded functions.
* Needs to be generated equally on all overloads.
*/
- OverloadGroupNote;
+ OverloadGroupNote,
+
+ GroupNode;
companion object {
val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
@@ -97,12 +100,18 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Annotation).map { it.to }
val deprecation: DocumentationNode?
get() = references(RefKind.Deprecation).singleOrNull()?.to
+ val platforms: List<String>
+ get() = references(RefKind.Platform).map { it.to.name }
// TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
references.add(DocumentationReference(this, to, kind))
}
+ fun dropReferences(predicate: (DocumentationReference) -> Boolean) {
+ references.removeAll(predicate)
+ }
+
fun addAllReferencesFrom(other: DocumentationNode) {
references.addAll(other.references)
}
@@ -135,6 +144,7 @@ open class DocumentationNode(val name: String,
class DocumentationModule(name: String, content: Content = Content.Empty)
: DocumentationNode(name, content, NodeKind.Module) {
+ val nodeRefGraph = NodeReferenceGraph()
}
val DocumentationNode.path: List<DocumentationNode>
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index 0165b567..a968f400 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -1,6 +1,5 @@
package org.jetbrains.dokka
-import com.google.inject.Inject
import com.google.inject.Singleton
enum class RefKind {
@@ -18,7 +17,8 @@ enum class RefKind {
Annotation,
HiddenAnnotation,
Deprecation,
- TopLevelPage
+ TopLevelPage,
+ Platform
}
data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
@@ -36,9 +36,7 @@ class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?,
}
}
-@Singleton
-class NodeReferenceGraph
- @Inject constructor(val logger: DokkaLogger) {
+class NodeReferenceGraph() {
private val nodeMap = hashMapOf<String, DocumentationNode>()
val references = arrayListOf<PendingDocumentationReference>()
@@ -58,7 +56,9 @@ class NodeReferenceGraph
references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind))
}
- fun lookup(signature: String): DocumentationNode? {
+ fun lookup(signature: String) = nodeMap[signature]
+
+ fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? {
val result = nodeMap[signature]
if (result == null) {
logger.warn("Can't find node by signature $signature")
@@ -70,3 +70,14 @@ class NodeReferenceGraph
references.forEach { it.resolve() }
}
}
+
+@Singleton
+class PlatformNodeRegistry {
+ private val platformNodes = hashMapOf<String, DocumentationNode>()
+
+ operator fun get(platform: String): DocumentationNode {
+ return platformNodes.getOrPut(platform) {
+ DocumentationNode(platform, Content.Empty, NodeKind.Platform)
+ }
+ }
+}
diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
index 6348c181..116a5c02 100644
--- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
+++ b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
@@ -42,10 +42,9 @@ open class DefaultSampleProcessingService
return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) }
}
- val text = processSampleBody(psiElement)
-
- val lines = text.trimEnd().split("\n".toRegex()).toTypedArray().filterNot(String::isEmpty)
- val indent = lines.map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0
+ 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.map { it.drop(indent) }.joinToString("\n")
return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) }
diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
index baf44904..7ac43184 100644
--- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
+++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
@@ -2,8 +2,10 @@ package org.jetbrains.dokka.Samples
import com.google.inject.Inject
import com.intellij.psi.PsiElement
+import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.dokka.*
import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.resolve.ImportPath
open class KotlinWebsiteSampleProcessingService
@@ -12,7 +14,7 @@ open class KotlinWebsiteSampleProcessingService
resolutionFacade: DokkaResolutionFacade)
: DefaultSampleProcessingService(options, logger, resolutionFacade) {
- private class SampleBuilder() : KtVisitorVoid() {
+ private class SampleBuilder : KtTreeVisitorVoid() {
val builder = StringBuilder()
val text: String
get() = builder.toString()
@@ -48,13 +50,10 @@ open class KotlinWebsiteSampleProcessingService
}
}
- override fun visitElement(element: PsiElement?) {
- if (element != null) {
- if (element.children.isEmpty())
- builder.append(element.text)
- else
- element.acceptChildren(this)
- }
+ override fun visitElement(element: PsiElement) {
+ if (element is LeafPsiElement)
+ builder.append(element.text)
+ super.visitElement(element)
}
}
@@ -70,8 +69,9 @@ open class KotlinWebsiteSampleProcessingService
val psiFile = psiElement.containingFile
if (psiFile is KtFile) {
return ContentBlockCode("kotlin").apply {
+ append(ContentText("\n"))
psiFile.importList?.let {
- it.children.filter {
+ it.allChildren.filter {
it !is KtImportDirective || it.importPath !in importsToIgnore
}.forEach { append(ContentText(it.text)) }
}
diff --git a/core/src/main/kotlin/Utilities/DokkaModule.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
index e1ae829a..5982d7dd 100644
--- a/core/src/main/kotlin/Utilities/DokkaModule.kt
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -3,6 +3,7 @@ package org.jetbrains.dokka.Utilities
import com.google.inject.Binder
import com.google.inject.Module
import com.google.inject.Provider
+import com.google.inject.TypeLiteral
import com.google.inject.name.Names
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Formats.FormatDescriptor
@@ -10,10 +11,47 @@ import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import java.io.File
-class DokkaModule(val environment: AnalysisEnvironment,
- val options: DocumentationOptions,
- val logger: DokkaLogger) : Module {
+const val impliedPlatformsName = "impliedPlatforms"
+
+class DokkaAnalysisModule(val environment: AnalysisEnvironment,
+ val options: DocumentationOptions,
+ val defaultPlatformsProvider: DefaultPlatformsProvider,
+ val nodeReferenceGraph: NodeReferenceGraph,
+ val logger: DokkaLogger) : Module {
+ override fun configure(binder: Binder) {
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
+
+ binder.registerCategory<LanguageService>("language")
+ binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java)
+ binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java)
+ binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java)
+
+ val coreEnvironment = environment.createCoreEnvironment()
+ binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment)
+
+ val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment)
+ binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade)
+
+ binder.bind<DocumentationOptions>().toInstance(options)
+ binder.bind<DokkaLogger>().toInstance(logger)
+
+ binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider)
+
+ binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph)
+ }
+}
+
+object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>()
+
+class DokkaOutputModule(val options: DocumentationOptions,
+ val logger: DokkaLogger) : Module {
override fun configure(binder: Binder) {
+ binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java)
+
+ binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> {
+ override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css")
+ })
+
binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir))
binder.bindNameAnnotated<LocationService, SingleFolderLocationService>("singleFolder")
@@ -24,13 +62,7 @@ class DokkaModule(val environment: AnalysisEnvironment,
// defaults
binder.bind(LocationService::class.java).to(FoldersLocationService::class.java)
binder.bind(FileLocationService::class.java).to(FoldersLocationService::class.java)
- binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java)
-
- binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> {
- override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css")
- })
- binder.registerCategory<LanguageService>("language")
binder.registerCategory<OutlineFormatService>("outline")
binder.registerCategory<FormatService>("format")
binder.registerCategory<Generator>("generator")
@@ -43,20 +75,12 @@ class DokkaModule(val environment: AnalysisEnvironment,
descriptor.formatServiceClass?.let { clazz ->
binder.bind(FormatService::class.java).to(clazz.java)
}
- binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java)
- binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java)
- binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java)
binder.bind<Generator>().to(descriptor.generatorServiceClass.java)
- val coreEnvironment = environment.createCoreEnvironment()
- binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment)
-
- val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment)
- binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade)
-
binder.bind<DocumentationOptions>().toInstance(options)
binder.bind<DokkaLogger>().toInstance(logger)
+ binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms)
}
}
diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css
index 09586237..914be69d 100644
--- a/core/src/main/resources/dokka/styles/style.css
+++ b/core/src/main/resources/dokka/styles/style.css
@@ -1,10 +1,13 @@
-@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700);
body, table {
padding:50px;
- font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
color:#555;
font-weight:300;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 1440px;
}
.keyword {
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index 7197d2c4..19eb29d5 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -5,13 +5,14 @@ import com.intellij.openapi.application.PathManager
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Utilities.DokkaModule
+import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.config.ContentRoot
import org.jetbrains.kotlin.config.KotlinSourceRoot
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.junit.Assert
import org.junit.Assert.fail
import java.io.File
@@ -22,6 +23,30 @@ fun verifyModel(vararg roots: ContentRoot,
format: String = "html",
includeNonPublic: Boolean = true,
verifier: (DocumentationModule) -> Unit) {
+ val documentation = DocumentationModule("test")
+
+ val options = DocumentationOptions("", format,
+ includeNonPublic = includeNonPublic,
+ skipEmptyPackages = false,
+ includeRootPackage = true,
+ sourceLinks = listOf<SourceLinkDefinition>(),
+ generateIndexPages = false)
+
+ appendDocumentation(documentation, *roots,
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ options = options)
+ documentation.prepareForGeneration(options)
+
+ verifier(documentation)
+}
+
+fun appendDocumentation(documentation: DocumentationModule,
+ vararg roots: ContentRoot,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ options: DocumentationOptions,
+ defaultPlatforms: List<String> = emptyList()) {
val messageCollector = object : MessageCollector {
override fun clear() {
@@ -60,14 +85,12 @@ fun verifyModel(vararg roots: ContentRoot,
}
addRoots(roots.toList())
}
- val options = DocumentationOptions("", format,
- includeNonPublic = includeNonPublic,
- skipEmptyPackages = false,
- sourceLinks = listOf<SourceLinkDefinition>(),
- generateIndexPages = false)
- val injector = Guice.createInjector(DokkaModule(environment, options, DokkaConsoleLogger))
- val documentation = buildDocumentationModule(injector, "test")
- verifier(documentation)
+ val defaultPlatformsProvider = object : DefaultPlatformsProvider {
+ override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms
+ }
+ val injector = Guice.createInjector(
+ DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger))
+ buildDocumentationModule(injector, documentation)
Disposer.dispose(environment)
}
@@ -128,19 +151,18 @@ fun verifyOutput(roots: Array<ContentRoot>,
format: String = "html",
outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
verifyModel(*roots, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, format = format) {
- verifyModelOutput(it, outputExtension, outputGenerator, roots.first().path)
+ verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
}
}
-private fun verifyModelOutput(it: DocumentationModule,
- outputExtension: String,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit,
- sourcePath: String) {
+fun verifyModelOutput(it: DocumentationModule,
+ outputExtension: String,
+ sourcePath: String,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
val output = StringBuilder()
outputGenerator(it, output)
val ext = outputExtension.removePrefix(".")
- val path = sourcePath
- val expectedOutput = File(path.replaceAfterLast(".", ext, path + "." + ext)).readText()
+ val expectedOutput = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)).readText()
assertEqualsIgnoringSeparators(expectedOutput, output.toString())
}
@@ -158,7 +180,7 @@ fun verifyJavaOutput(path: String,
withKotlinRuntime: Boolean = false,
outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
verifyJavaModel(path, withKotlinRuntime) { model ->
- verifyModelOutput(model, outputExtension, outputGenerator, path)
+ verifyModelOutput(model, outputExtension, path, outputGenerator)
}
}
diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt
index 5327c9dc..c097c5c8 100644
--- a/core/src/test/kotlin/format/GFMFormatTest.kt
+++ b/core/src/test/kotlin/format/GFMFormatTest.kt
@@ -5,7 +5,7 @@ import org.jetbrains.dokka.KotlinLanguageService
import org.junit.Test
class GFMFormatTest {
- private val gfmService = GFMFormatService(InMemoryLocationService, KotlinLanguageService())
+ private val gfmService = GFMFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
@Test fun sample() {
verifyGFMNodeByName("sample", "Foo")
diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt
index 4b4eff59..cbea3209 100644
--- a/core/src/test/kotlin/format/HtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/HtmlFormatTest.kt
@@ -7,7 +7,7 @@ import org.junit.Test
import java.io.File
class HtmlFormatTest {
- private val htmlService = HtmlFormatService(InMemoryLocationService, KotlinLanguageService(), HtmlTemplateService.default())
+ private val htmlService = HtmlFormatService(InMemoryLocationService, KotlinLanguageService(), HtmlTemplateService.default(), listOf())
@Test fun classWithCompanionObject() {
verifyHtmlNode("classWithCompanionObject")
@@ -138,6 +138,14 @@ class HtmlFormatTest {
verifyHtmlNode("functionalTypeWithNamedParameters")
}
+ @Test fun sinceKotlin() {
+ verifyHtmlNode("sinceKotlin")
+ }
+
+ @Test fun blankLineInsideCodeBlock() {
+ verifyHtmlNode("blankLineInsideCodeBlock")
+ }
+
private fun verifyHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
verifyHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
}
diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
index 3363b0a3..9565263f 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
@@ -1,11 +1,11 @@
package org.jetbrains.dokka.tests
-import org.jetbrains.dokka.KotlinLanguageService
-import org.jetbrains.dokka.KotlinWebsiteFormatService
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import org.junit.Test
class KotlinWebSiteFormatTest {
- private val kwsService = KotlinWebsiteFormatService(InMemoryLocationService, KotlinLanguageService())
+ private val kwsService = KotlinWebsiteFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
@Test fun sample() {
verifyKWSNodeByName("sample", "foo")
@@ -19,9 +19,39 @@ class KotlinWebSiteFormatTest {
verifyKWSNodeByName("overloadGroup", "magic")
}
+ @Test fun dataTags() {
+ val module = buildMultiplePlatforms("dataTags")
+ verifyMultiplatformPackage(module, "dataTags")
+ }
+
+ @Test fun dataTagsInGroupNode() {
+ val path = "dataTagsInGroupNode"
+ val module = buildMultiplePlatforms(path)
+ verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output ->
+ kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.find { it.kind == NodeKind.GroupNode }.singletonOrEmptyList())
+ }
+ verifyMultiplatformPackage(module, path)
+ }
+
private fun verifyKWSNodeByName(fileName: String, name: String) {
verifyOutput("testdata/format/website/$fileName.kt", ".md", format = "kotlin-website") { model, output ->
kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
}
}
+
+ private fun buildMultiplePlatforms(path: String): DocumentationModule {
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions("", "html", generateIndexPages = false)
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options)
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options)
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options)
+ return module
+ }
+
+ private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".package.md", "testdata/format/website/$path/multiplatform.kt") { model, output ->
+ kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
+ }
+ }
+
}
diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
index 742d2908..0d586814 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
@@ -5,7 +5,7 @@ import org.jetbrains.dokka.KotlinWebsiteRunnableSamplesFormatService
import org.junit.Test
class KotlinWebSiteRunnableSamplesFormatTest {
- private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService())
+ private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
@Test fun dropImport() {
@@ -20,6 +20,14 @@ class KotlinWebSiteRunnableSamplesFormatTest {
verifyKWSNodeByName("sampleWithAsserts", "a")
}
+ @Test fun newLinesInSamples() {
+ verifyKWSNodeByName("newLinesInSamples", "foo")
+ }
+
+ @Test fun newLinesInImportList() {
+ verifyKWSNodeByName("newLinesInImportList", "foo")
+ }
+
private fun verifyKWSNodeByName(fileName: String, name: String) {
verifyOutput("testdata/format/website-samples/$fileName.kt", ".md", format = "kotlin-website-samples") { model, output ->
kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt
index 4f32fa53..21298520 100644
--- a/core/src/test/kotlin/format/MarkdownFormatTest.kt
+++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt
@@ -1,14 +1,11 @@
package org.jetbrains.dokka.tests
-import org.jetbrains.dokka.DocumentationModule
-import org.jetbrains.dokka.DocumentationNode
-import org.jetbrains.dokka.KotlinLanguageService
-import org.jetbrains.dokka.MarkdownFormatService
-import org.junit.Ignore
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import org.junit.Test
class MarkdownFormatTest {
- private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService())
+ private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
@Test fun emptyDescription() {
verifyMarkdownNode("emptyDescription")
@@ -244,6 +241,124 @@ class MarkdownFormatTest {
verifyMarkdownNode("sampleByShortName")
}
+
+ @Test fun suspendParam() {
+ verifyMarkdownNode("suspendParam")
+ verifyMarkdownPackage("suspendParam")
+ }
+
+ @Test fun sinceKotlin() {
+ verifyMarkdownNode("sinceKotlin")
+ verifyMarkdownPackage("sinceKotlin")
+ }
+
+ @Test fun dynamicType() {
+ verifyMarkdownNode("dynamicType")
+ }
+
+ @Test fun dynamicExtension() {
+ verifyMarkdownNodes("dynamicExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ }
+
+ @Test fun memberExtension() {
+ verifyMarkdownNodes("memberExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ }
+
+ @Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() {
+ verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver")
+ }
+
+ @Test fun multiplePlatforms() {
+ verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple")
+ }
+
+ @Test fun multiplePlatformsMerge() {
+ verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge")
+ }
+
+ @Test fun multiplePlatformsMergeMembers() {
+ val module = buildMultiplePlatforms("multiplatform/mergeMembers")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output ->
+ markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
+ }
+ }
+
+ @Test fun multiplePlatformsOmitRedundant() {
+ val module = buildMultiplePlatforms("multiplatform/omitRedundant")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output ->
+ markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
+ }
+ }
+
+ @Test fun multiplePlatformsImplied() {
+ val module = buildMultiplePlatforms("multiplatform/implied")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output ->
+ MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf("JVM", "JS"))
+ .createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
+ }
+ }
+
+ @Test fun packagePlatformsWithExtExtensions() {
+ val path = "multiplatform/packagePlatformsWithExtExtensions"
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions("", "html", generateIndexPages = false)
+ appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), withKotlinRuntime = true, options = options)
+ verifyMultiplatformIndex(module, path)
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsPackagePlatformFromMembers() {
+ val path = "multiplatform/packagePlatformsFromMembers"
+ val module = buildMultiplePlatforms(path)
+ verifyMultiplatformIndex(module, path)
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsGroupNode() {
+ val path = "multiplatform/groupNode"
+ val module = buildMultiplePlatforms(path)
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ markdownService.createOutputBuilder(output, tempLocation)
+ .appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }))
+ }
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() {
+ val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode"
+ val module = buildMultiplePlatforms(path)
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ markdownService.createOutputBuilder(output, tempLocation)
+ .appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Class)?.member(NodeKind.Function)))
+ }
+ }
+
+ private fun buildMultiplePlatforms(path: String): DocumentationModule {
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions("", "html", generateIndexPages = false)
+ appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options)
+ appendDocumentation(module, contentRootFromPath("testdata/format/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options)
+ return module
+ }
+
+ private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
+ }
+ }
+
+ private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") {
+ model, output ->
+ MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
+ .createOutputBuilder(output, tempLocation).appendNodes(listOf(model))
+ }
+ }
+
+ @Test fun blankLineInsideCodeBlock() {
+ verifyMarkdownNode("blankLineInsideCodeBlock")
+ }
+
private fun verifyMarkdownPackage(fileName: String, withKotlinRuntime: Boolean = false) {
verifyOutput("testdata/format/$fileName.kt", ".package.md", withKotlinRuntime = withKotlinRuntime) { model, output ->
markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt
index f7f86881..359c5fef 100644
--- a/core/src/test/kotlin/javadoc/JavadocTest.kt
+++ b/core/src/test/kotlin/javadoc/JavadocTest.kt
@@ -1,7 +1,9 @@
package org.jetbrains.dokka.javadoc
+import com.sun.javadoc.Tag
import com.sun.javadoc.Type
import org.jetbrains.dokka.DokkaConsoleLogger
+import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
import org.jetbrains.dokka.tests.verifyModel
import org.junit.Assert.*
import org.junit.Test
@@ -132,6 +134,23 @@ class JavadocTest {
}
}
+ @Test
+ fun testBlankLineInsideCodeBlock() {
+ verifyJavadoc("testdata/javadoc/blankLineInsideCodeBlock.kt", withKotlinRuntime = true) { doc ->
+ val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0]
+ val text = method.inlineTags().joinToString(separator = "", transform = Tag::text)
+ assertEqualsIgnoringSeparators("""
+ <p><code><pre>
+ This is a test
+ of Dokka's code blocks.
+ Here is a blank line.
+
+ The previous line was blank.
+ </pre></code></p>
+ """.trimIndent(), text)
+ }
+ }
+
private fun verifyJavadoc(name: String,
withJdk: Boolean = false,
withKotlinRuntime: Boolean = false,
diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt
index d50a3624..fb225728 100644
--- a/core/src/test/kotlin/model/ClassTest.kt
+++ b/core/src/test/kotlin/model/ClassTest.kt
@@ -3,11 +3,11 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
import org.jetbrains.dokka.RefKind
-import org.junit.Test
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
+import org.junit.Test
-public class ClassTest {
+class ClassTest {
@Test fun emptyClass() {
verifyModel("testdata/classes/emptyClass.kt") { model ->
with(model.members.single().members.single()) {
@@ -272,4 +272,12 @@ public class ClassTest {
}
}
}
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/classes/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
}
diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt
index 25fea48d..3752bb8c 100644
--- a/core/src/test/kotlin/model/CommentTest.kt
+++ b/core/src/test/kotlin/model/CommentTest.kt
@@ -10,14 +10,18 @@ public class CommentTest {
verifyModel("testdata/comments/codeBlockComment.kt") { model ->
with(model.members.single().members.first()) {
assertEqualsIgnoringSeparators("""[code lang=brainfuck]
+ |
|++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+ |
|[/code]
|""".trimMargin(),
content.toTestString())
}
with(model.members.single().members.last()) {
assertEqualsIgnoringSeparators("""[code]
+ |
|a + b - c
+ |
|[/code]
|""".trimMargin(),
content.toTestString())
@@ -153,7 +157,8 @@ line two""", toTestString())
with(model.members.single().members.first()) {
assertEquals("Summary", content.summary.toTestString())
with (content.description) {
- assertEqualsIgnoringSeparators("""[code lang=kotlin]
+ assertEqualsIgnoringSeparators("""
+ |[code lang=kotlin]
|if (true) {
| println(property)
|}
diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt
index 4cced562..ddd33941 100644
--- a/core/src/test/kotlin/model/FunctionTest.kt
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -2,11 +2,11 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
-import org.junit.Test
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
+import org.junit.Test
-public class FunctionTest {
+class FunctionTest {
@Test fun function() {
verifyModel("testdata/functions/function.kt") { model ->
with(model.members.single().members.single()) {
@@ -224,4 +224,12 @@ Documentation""", content.description.toTestString())
}
}
}
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/functions/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
}
diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt
index cdf44c03..0ee0e0f5 100644
--- a/core/src/test/kotlin/model/PropertyTest.kt
+++ b/core/src/test/kotlin/model/PropertyTest.kt
@@ -3,11 +3,11 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
import org.jetbrains.dokka.RefKind
-import org.junit.Test
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
+import org.junit.Test
-public class PropertyTest {
+class PropertyTest {
@Test fun valueProperty() {
verifyModel("testdata/properties/valueProperty.kt") { model ->
with(model.members.single().members.single()) {
@@ -100,4 +100,12 @@ public class PropertyTest {
}
}
}
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/properties/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
}
diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt
index 812fd9dc..c653ac83 100644
--- a/core/src/test/kotlin/model/TypeAliasTest.kt
+++ b/core/src/test/kotlin/model/TypeAliasTest.kt
@@ -120,4 +120,13 @@ class TypeAliasTest {
}
}
}
+
+ @Test
+ fun sinceKotlin() {
+ verifyModel("testdata/typealias/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/core/testdata/classes/sinceKotlin.kt b/core/testdata/classes/sinceKotlin.kt
new file mode 100644
index 00000000..1025cf0d
--- /dev/null
+++ b/core/testdata/classes/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Useful
+ */
+@SinceKotlin("1.1")
+class `Since1.1` \ No newline at end of file
diff --git a/core/testdata/format/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html
new file mode 100644
index 00000000..168dd0dd
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.html
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>u - test</title>
+</HEAD>
+<BODY>
+<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/u">u</a><br/>
+<br/>
+<h1>u</h1>
+<a name="$u()"></a>
+<code><span class="keyword">fun </span><span class="identifier">u</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><pre><code>This is a test
+ of Dokka's code blocks.
+Here is a blank line.
+
+The previous line was blank.
+</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/blankLineInsideCodeBlock.kt b/core/testdata/format/blankLineInsideCodeBlock.kt
new file mode 100644
index 00000000..9430f4d5
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.kt
@@ -0,0 +1,12 @@
+/**
+ * ```
+ * This is a test
+ * of Dokka's code blocks.
+ * Here is a blank line.
+ *
+ * The previous line was blank.
+ * ```
+ */
+fun u() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/blankLineInsideCodeBlock.md b/core/testdata/format/blankLineInsideCodeBlock.md
new file mode 100644
index 00000000..66f4d65f
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.md
@@ -0,0 +1,14 @@
+[test](test/index) / [u](test/u)
+
+# u
+
+`fun u(): Unit`
+
+```
+This is a test
+ of Dokka's code blocks.
+Here is a blank line.
+
+The previous line was blank.
+```
+
diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html
index b3b65dba..48c2ffd2 100644
--- a/core/testdata/format/codeBlock.html
+++ b/core/testdata/format/codeBlock.html
@@ -10,11 +10,13 @@
<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
<p>Example:</p>
<pre><code>Throws(IOException::class)
-fun readFile(name: String): String {...}</code></pre><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-it-does-some-obfuscated-thing/index">ItDoesSomeObfuscatedThing</a><br/>
+fun readFile(name: String): String {...}
+</code></pre><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-it-does-some-obfuscated-thing/index">ItDoesSomeObfuscatedThing</a><br/>
<br/>
<h1>ItDoesSomeObfuscatedThing</h1>
<code><span class="keyword">class </span><span class="identifier">ItDoesSomeObfuscatedThing</span></code>
<p>Check output of</p>
-<pre><code class="lang-brainfuck">++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++.------.--------.&gt;+.&gt;.</code></pre>
+<pre><code class="lang-brainfuck">++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++.------.--------.&gt;+.&gt;.
+</code></pre>
</BODY>
</HTML>
diff --git a/core/testdata/format/dynamicExtension.kt b/core/testdata/format/dynamicExtension.kt
new file mode 100644
index 00000000..5c83bf22
--- /dev/null
+++ b/core/testdata/format/dynamicExtension.kt
@@ -0,0 +1,3 @@
+class Foo
+
+fun dynamic.bar() {}
diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md
new file mode 100644
index 00000000..2fd928f6
--- /dev/null
+++ b/core/testdata/format/dynamicExtension.md
@@ -0,0 +1,10 @@
+[test](test/index) / [Foo](test/-foo/index)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+
diff --git a/core/testdata/format/dynamicType.kt b/core/testdata/format/dynamicType.kt
new file mode 100644
index 00000000..9d557ac0
--- /dev/null
+++ b/core/testdata/format/dynamicType.kt
@@ -0,0 +1,2 @@
+fun foo(): dynamic = ""
+
diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md
new file mode 100644
index 00000000..a3d6696b
--- /dev/null
+++ b/core/testdata/format/dynamicType.md
@@ -0,0 +1,5 @@
+[test](test/index) / [foo](test/foo)
+
+# foo
+
+`fun foo(): dynamic` \ No newline at end of file
diff --git a/core/testdata/format/memberExtension.kt b/core/testdata/format/memberExtension.kt
new file mode 100644
index 00000000..955794d1
--- /dev/null
+++ b/core/testdata/format/memberExtension.kt
@@ -0,0 +1,8 @@
+open class X
+
+class Foo : X
+
+class Bar {
+ fun X.y() = ""
+ fun Foo.x() = ""
+}
diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md
new file mode 100644
index 00000000..b9db4e3d
--- /dev/null
+++ b/core/testdata/format/memberExtension.md
@@ -0,0 +1,10 @@
+[test](test/index) / [Foo](test/-foo/index)
+
+# Foo
+
+`class Foo : `[`X`](test/-x/index)
+
+### Constructors
+
+| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt
new file mode 100644
index 00000000..d7fbf924
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt
@@ -0,0 +1,7 @@
+package pack
+
+class Some {
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt
new file mode 100644
index 00000000..57f36742
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
new file mode 100644
index 00000000..e3d4c070
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
@@ -0,0 +1,8 @@
+[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index) / [magic](test/pack/-some/-some/magic)
+
+# magic
+
+`fun magic(): Unit`
+
+**Platform and version requirements:** JS
+
diff --git a/core/testdata/format/multiplatform/groupNode/js.kt b/core/testdata/format/multiplatform/groupNode/js.kt
new file mode 100644
index 00000000..045f3f0d
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/groupNode/jvm.kt b/core/testdata/format/multiplatform/groupNode/jvm.kt
new file mode 100644
index 00000000..57f36742
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.md b/core/testdata/format/multiplatform/groupNode/multiplatform.md
new file mode 100644
index 00000000..c0ef14b1
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md
@@ -0,0 +1,20 @@
+[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index)
+
+# Some
+
+`typealias Some = SomeCoolJvmClass`
+
+**Platform and version requirements:** JVM
+
+`class Some`
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+| [&lt;init&gt;](test/pack/-some/-some/-init-) | `Some()` |
+
+### Functions
+
+| [magic](test/pack/-some/-some/magic) | `fun magic(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
new file mode 100644
index 00000000..a9e2e404
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
@@ -0,0 +1,13 @@
+[test](test/index) / [pack](test/pack/index)
+
+## Package pack
+
+### Types
+
+| [Some](test/pack/-some/index)<br>(JS) | `class Some` |
+| [SomeCoolJvmClass](test/pack/-some-cool-jvm-class/index)<br>(JVM) | `class SomeCoolJvmClass` |
+
+### Type Aliases
+
+| [Some](test/pack/-some/index)<br>(JVM) | `typealias Some = SomeCoolJvmClass` |
+
diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md
new file mode 100644
index 00000000..c615dd8e
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/foo.md
@@ -0,0 +1,24 @@
+[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+
+# Foo
+
+`class Foo`
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJs](test/foo/-foo/prop-js)<br>(JS) | `val propJs: String` |
+| [propJvm](test/foo/-foo/prop-jvm)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](test/foo/-foo/prop-jvm-and-js) | `val propJvmAndJs: Int` |
+
+### Functions
+
+| [bothJvmAndJs](test/foo/-foo/both-jvm-and-js) | `fun bothJvmAndJs(): Unit` |
+| [js](test/foo/-foo/js)<br>(JS) | `fun js(): Unit` |
+| [jvm](test/foo/-foo/jvm)<br>(JVM) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/implied/js.kt b/core/testdata/format/multiplatform/implied/js.kt
new file mode 100644
index 00000000..dd2de5bc
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/js.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun js() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJs = "abc"
+}
diff --git a/core/testdata/format/multiplatform/implied/jvm.kt b/core/testdata/format/multiplatform/implied/jvm.kt
new file mode 100644
index 00000000..8d73ce25
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/jvm.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun jvm() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/merge/js.kt b/core/testdata/format/multiplatform/merge/js.kt
new file mode 100644
index 00000000..bbf1dd7c
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/js.kt
@@ -0,0 +1,7 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+}
diff --git a/core/testdata/format/multiplatform/merge/jvm.kt b/core/testdata/format/multiplatform/merge/jvm.kt
new file mode 100644
index 00000000..cb77273f
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/jvm.kt
@@ -0,0 +1,8 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+
+}
diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md
new file mode 100644
index 00000000..8e463327
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](test/index) / [foo](test/foo/index)
+
+## Package foo
+
+**Platform and version requirements:** JVM, JS
+
+### Types
+
+| [Foo](test/foo/-foo/index)<br>(JVM, JS) | `class Foo`<br>This is a foo. |
+
diff --git a/core/testdata/format/multiplatform/mergeMembers/foo.md b/core/testdata/format/multiplatform/mergeMembers/foo.md
new file mode 100644
index 00000000..7490c878
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/foo.md
@@ -0,0 +1,26 @@
+[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM, JS
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJs](test/foo/-foo/prop-js)<br>(JS) | `val propJs: String` |
+| [propJvm](test/foo/-foo/prop-jvm)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](test/foo/-foo/prop-jvm-and-js) | `val propJvmAndJs: Int` |
+
+### Functions
+
+| [bothJvmAndJs](test/foo/-foo/both-jvm-and-js) | `fun bothJvmAndJs(): Unit` |
+| [js](test/foo/-foo/js)<br>(JS) | `fun js(): Unit` |
+| [jvm](test/foo/-foo/jvm)<br>(JVM) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/mergeMembers/js.kt b/core/testdata/format/multiplatform/mergeMembers/js.kt
new file mode 100644
index 00000000..dd2de5bc
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/js.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun js() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJs = "abc"
+}
diff --git a/core/testdata/format/multiplatform/mergeMembers/jvm.kt b/core/testdata/format/multiplatform/mergeMembers/jvm.kt
new file mode 100644
index 00000000..8d73ce25
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/jvm.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun jvm() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md
new file mode 100644
index 00000000..088ced2c
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/foo.md
@@ -0,0 +1,22 @@
+[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJvm](test/foo/-foo/prop-jvm) | `val propJvm: String` |
+
+### Functions
+
+| [jvm](test/foo/-foo/jvm) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/omitRedundant/js.kt b/core/testdata/format/multiplatform/omitRedundant/js.kt
new file mode 100644
index 00000000..d1b1429c
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/js.kt
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/core/testdata/format/multiplatform/omitRedundant/jvm.kt b/core/testdata/format/multiplatform/omitRedundant/jvm.kt
new file mode 100644
index 00000000..35e3c08d
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun jvm() {
+ }
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt
new file mode 100644
index 00000000..86d09289
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt
@@ -0,0 +1,3 @@
+package foo.bar
+
+fun buz() {}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt
new file mode 100644
index 00000000..86d09289
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt
@@ -0,0 +1,3 @@
+package foo.bar
+
+fun buz() {}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
new file mode 100644
index 00000000..1dda25d4
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
@@ -0,0 +1,10 @@
+[test](test/index)
+
+**Platform and version requirements:** JVM, JS
+
+### Packages
+
+| [foo.bar](test/foo.bar/index)<br>(JVM, JS) | |
+
+### Index
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
new file mode 100644
index 00000000..2921cdd1
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](test/index) / [foo.bar](test/foo.bar/index)
+
+## Package foo.bar
+
+**Platform and version requirements:** JVM, JS
+
+### Functions
+
+| [buz](test/foo.bar/buz)<br>(JVM, JS) | `fun buz(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt
new file mode 100644
index 00000000..27ab1b32
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt
@@ -0,0 +1,5 @@
+package some
+
+fun String.buz(): Unit {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
new file mode 100644
index 00000000..f6e770b2
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
@@ -0,0 +1,10 @@
+[test](test/index)
+
+**Platform and version requirements:** JVM
+
+### Packages
+
+| [some](test/some/index)<br>(JVM) | |
+
+### Index
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
new file mode 100644
index 00000000..22f778d6
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](test/index) / [some](test/some/index)
+
+## Package some
+
+**Platform and version requirements:** JVM
+
+### Extensions for External Classes
+
+| [kotlin.String](test/some/kotlin.-string/index) | |
+
diff --git a/core/testdata/format/multiplatform/simple/js.kt b/core/testdata/format/multiplatform/simple/js.kt
new file mode 100644
index 00000000..e6d66ffd
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/js.kt
@@ -0,0 +1,7 @@
+package foo
+
+/**
+ * This is a bar.
+ */
+class Bar {
+}
diff --git a/core/testdata/format/multiplatform/simple/jvm.kt b/core/testdata/format/multiplatform/simple/jvm.kt
new file mode 100644
index 00000000..cb77273f
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/jvm.kt
@@ -0,0 +1,8 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+
+}
diff --git a/core/testdata/format/multiplatform/simple/multiplatform.package.md b/core/testdata/format/multiplatform/simple/multiplatform.package.md
new file mode 100644
index 00000000..3574942c
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/multiplatform.package.md
@@ -0,0 +1,9 @@
+[test](test/index) / [foo](test/foo/index)
+
+## Package foo
+
+### Types
+
+| [Bar](test/foo/-bar/index)<br>(JS) | `class Bar`<br>This is a bar. |
+| [Foo](test/foo/-foo/index)<br>(JVM) | `class Foo`<br>This is a foo. |
+
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt
new file mode 100644
index 00000000..84f78dfb
--- /dev/null
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt
@@ -0,0 +1,3 @@
+fun (suspend () -> Unit).foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
new file mode 100644
index 00000000..c9856976
--- /dev/null
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
@@ -0,0 +1,6 @@
+[test](test/index) / [kotlin.SuspendFunction0](test/kotlin.-suspend-function0/index)
+
+### Extensions for kotlin.SuspendFunction0
+
+| [foo](test/kotlin.-suspend-function0/foo) | `fun (suspend () -> Unit).foo(): Unit` |
+
diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html
new file mode 100644
index 00000000..4788fcda
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.html
@@ -0,0 +1,27 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Since1.1 - test</title>
+</HEAD>
+<BODY>
+<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-since1.1/index">Since1.1</a><br/>
+<br/>
+<h1>Since1.1</h1>
+<code><span class="keyword">class </span><span class="identifier">Since1.1</span></code>
+<p><strong>Platform and version requirements:</strong> Kotlin 1.1</p>
+<p>Useful</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<a href="test/-since1.1/-init-">&lt;init&gt;</a></td>
+<td>
+<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Useful</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/sinceKotlin.kt b/core/testdata/format/sinceKotlin.kt
new file mode 100644
index 00000000..1025cf0d
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Useful
+ */
+@SinceKotlin("1.1")
+class `Since1.1` \ No newline at end of file
diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md
new file mode 100644
index 00000000..a1abe5fd
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.md
@@ -0,0 +1,14 @@
+[test](test/index) / [Since1.1](test/-since1.1/index)
+
+# Since1.1
+
+`class Since1.1`
+
+**Platform and version requirements:** Kotlin 1.1
+
+Useful
+
+### Constructors
+
+| [&lt;init&gt;](test/-since1.1/-init-) | `Since1.1()`<br>Useful |
+
diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md
new file mode 100644
index 00000000..c811749c
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.package.md
@@ -0,0 +1,10 @@
+[test](test/index)
+
+## Package &lt;root&gt;
+
+**Platform and version requirements:** Kotlin 1.1
+
+### Types
+
+| [Since1.1](test/-since1.1/index)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+
diff --git a/core/testdata/format/suspendParam.kt b/core/testdata/format/suspendParam.kt
new file mode 100644
index 00000000..ea3f56f9
--- /dev/null
+++ b/core/testdata/format/suspendParam.kt
@@ -0,0 +1,3 @@
+fun takesSuspendParam(func: suspend () -> Unit) {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md
new file mode 100644
index 00000000..cf11f57b
--- /dev/null
+++ b/core/testdata/format/suspendParam.md
@@ -0,0 +1,5 @@
+[test](test/index) / [takesSuspendParam](test/takes-suspend-param)
+
+# takesSuspendParam
+
+`fun takesSuspendParam(func: suspend () -> Unit): Unit` \ No newline at end of file
diff --git a/core/testdata/format/suspendParam.package.md b/core/testdata/format/suspendParam.package.md
new file mode 100644
index 00000000..463ba356
--- /dev/null
+++ b/core/testdata/format/suspendParam.package.md
@@ -0,0 +1,8 @@
+[test](test/index)
+
+## Package &lt;root&gt;
+
+### Functions
+
+| [takesSuspendParam](test/takes-suspend-param) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` |
+
diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html
index 5b3aef82..bac3385c 100644
--- a/core/testdata/format/tripleBackticks.html
+++ b/core/testdata/format/tripleBackticks.html
@@ -10,6 +10,7 @@
<a name="$f()"></a>
<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
<p>Description</p>
-<pre><code>code sample</code></pre>
+<pre><code>code sample
+</code></pre>
</BODY>
</HTML>
diff --git a/core/testdata/format/website-samples/dropImport.md b/core/testdata/format/website-samples/dropImport.md
index 13c8fb79..1e05678b 100644
--- a/core/testdata/format/website-samples/dropImport.md
+++ b/core/testdata/format/website-samples/dropImport.md
@@ -12,6 +12,7 @@ layout: api
``` kotlin
import some.*
+
fun main(args: Array<String>) {
//sampleStart
diff --git a/core/testdata/format/website-samples/newLinesInImportList.kt b/core/testdata/format/website-samples/newLinesInImportList.kt
new file mode 100644
index 00000000..836d9f6f
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInImportList.kt
@@ -0,0 +1,12 @@
+import same.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/newLinesInImportList.md b/core/testdata/format/website-samples/newLinesInImportList.md
new file mode 100644
index 00000000..27d796f8
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInImportList.md
@@ -0,0 +1,24 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+import same.*
+import some.*
+
+fun main(args: Array<String>) {
+//sampleStart
+
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website-samples/newLinesInSamples.kt b/core/testdata/format/website-samples/newLinesInSamples.kt
new file mode 100644
index 00000000..ee49aefc
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInSamples.kt
@@ -0,0 +1,19 @@
+fun groupBySample() {
+ val words = listOf("a", "abc", "ab", "def", "abcd")
+ val byLength = words.groupBy { it.length }
+
+ assertPrints(byLength.keys, "[1, 3, 2, 4]")
+ assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]")
+
+ val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+ // same content as in byLength map, but the map is mutable
+ assertTrue(mutableByLength == byLength)
+}
+
+
+/**
+ * @sample groupBySample
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/newLinesInSamples.md b/core/testdata/format/website-samples/newLinesInSamples.md
new file mode 100644
index 00000000..5344b983
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInSamples.md
@@ -0,0 +1,31 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+
+
+fun main(args: Array<String>) {
+//sampleStart
+val words = listOf("a", "abc", "ab", "def", "abcd")
+val byLength = words.groupBy { it.length }
+
+println(byLength.keys) // [1, 3, 2, 4]
+println(byLength.values) // [[a], [abc, def], [ab], [abcd]]
+
+val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+// same content as in byLength map, but the map is mutable
+println("mutableByLength == byLength is ${mutableByLength == byLength}") // true
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website-samples/sample.md b/core/testdata/format/website-samples/sample.md
index 203f1b02..b29075a7 100644
--- a/core/testdata/format/website-samples/sample.md
+++ b/core/testdata/format/website-samples/sample.md
@@ -18,6 +18,7 @@ applied to each element and returns a map where each group key is associated wit
``` kotlin
+
fun main(args: Array<String>) {
//sampleStart
if (true) {
diff --git a/core/testdata/format/website-samples/sampleWithAsserts.md b/core/testdata/format/website-samples/sampleWithAsserts.md
index 98d7df33..c114468a 100644
--- a/core/testdata/format/website-samples/sampleWithAsserts.md
+++ b/core/testdata/format/website-samples/sampleWithAsserts.md
@@ -12,6 +12,7 @@ layout: api
``` kotlin
+
fun main(args: Array<String>) {
//sampleStart
println(a()) // Hello, Work
diff --git a/core/testdata/format/website/dataTags/jre7.kt b/core/testdata/format/website/dataTags/jre7.kt
new file mode 100644
index 00000000..d21b8d7b
--- /dev/null
+++ b/core/testdata/format/website/dataTags/jre7.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jre7New() {}
+
+fun jre7() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/js.kt b/core/testdata/format/website/dataTags/js.kt
new file mode 100644
index 00000000..b22d7088
--- /dev/null
+++ b/core/testdata/format/website/dataTags/js.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jsNew() {}
+
+fun js() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/jvm.kt b/core/testdata/format/website/dataTags/jvm.kt
new file mode 100644
index 00000000..02d04226
--- /dev/null
+++ b/core/testdata/format/website/dataTags/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jvmNew() {}
+
+fun jvm() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/multiplatform.package.md b/core/testdata/format/website/dataTags/multiplatform.package.md
new file mode 100644
index 00000000..1c7fbf66
--- /dev/null
+++ b/core/testdata/format/website/dataTags/multiplatform.package.md
@@ -0,0 +1,71 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">foo</a></div>
+
+## Package foo
+
+### Functions
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/jre7">jre7</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/jre7-new">jre7New</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS"><td markdown="1">
+<a href="test/foo/js">js</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td markdown="1">
+<a href="test/foo/js-new">jsNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td markdown="1">
+<a href="test/foo/jvm">jvm</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td markdown="1">
+<a href="test/foo/jvm-new">jvmNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/shared">shared</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/shared-new">sharedNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt
diff --git a/core/testdata/format/website/dataTagsInGroupNode/js.kt b/core/testdata/format/website/dataTagsInGroupNode/js.kt
new file mode 100644
index 00000000..045f3f0d
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt
new file mode 100644
index 00000000..57f36742
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md
new file mode 100644
index 00000000..78f6adf2
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md
@@ -0,0 +1,56 @@
+---
+title: pack.Some - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a> / <a href="test/pack/-some/index">Some</a></div>
+
+# Some
+
+<div class="overload-group" data-platform="JVM" markdown="1">
+
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+**Platform and version requirements:** JVM
+
+</div>
+
+<div class="overload-group" data-platform="JS" markdown="1">
+
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/-init-">&lt;init&gt;</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+### Functions
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/magic">magic</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+</div>
diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
new file mode 100644
index 00000000..a6e7d63b
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
@@ -0,0 +1,43 @@
+---
+title: pack - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div>
+
+## Package pack
+
+### Types
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
+
+### Type Aliases
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/functions/sinceKotlin.kt b/core/testdata/functions/sinceKotlin.kt
new file mode 100644
index 00000000..cdcd3357
--- /dev/null
+++ b/core/testdata/functions/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Quite useful [String]
+ */
+@SinceKotlin("1.1")
+fun `availableSince1.1`(): String = "1.1 rulezz" \ No newline at end of file
diff --git a/core/testdata/javadoc/blankLineInsideCodeBlock.kt b/core/testdata/javadoc/blankLineInsideCodeBlock.kt
new file mode 100644
index 00000000..9430f4d5
--- /dev/null
+++ b/core/testdata/javadoc/blankLineInsideCodeBlock.kt
@@ -0,0 +1,12 @@
+/**
+ * ```
+ * This is a test
+ * of Dokka's code blocks.
+ * Here is a blank line.
+ *
+ * The previous line was blank.
+ * ```
+ */
+fun u() {
+
+} \ No newline at end of file
diff --git a/core/testdata/properties/sinceKotlin.kt b/core/testdata/properties/sinceKotlin.kt
new file mode 100644
index 00000000..e96f2349
--- /dev/null
+++ b/core/testdata/properties/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Quite useful [String]
+ */
+@SinceKotlin("1.1")
+val `availableSince1.1`: String = "1.1 rulezz" \ No newline at end of file
diff --git a/core/testdata/typealias/sinceKotlin.kt b/core/testdata/typealias/sinceKotlin.kt
new file mode 100644
index 00000000..5b76f63a
--- /dev/null
+++ b/core/testdata/typealias/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Documentation
+ */
+@SinceKotlin("1.1")
+typealias `Since 1.1` = String \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index d0feb72d..49153ef4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,8 +1,8 @@
-dokka_version=0.9.13
+dokka_version=0.9.14
dokka_publication_channel=dokka
#Kotlin compiler and plugin
-kotlin_version=1.1-M04
+kotlin_version=1.1.0
kotlin_for_gradle_version=1.0.6
ant_version=1.9.6
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt
index 50e8fb8d..845f86a6 100644
--- a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt
@@ -13,6 +13,7 @@ interface DokkaBootstrap {
outputDir: String,
format: String,
includeNonPublic: Boolean,
+ includeRootPackage: Boolean,
reportUndocumented: Boolean,
skipEmptyPackages: Boolean,
skipDeprecated: Boolean,
diff --git a/lib/kotlin-compiler.jar b/lib/kotlin-compiler.jar
index e81a8048..dc82213b 100644
--- a/lib/kotlin-compiler.jar
+++ b/lib/kotlin-compiler.jar
Binary files differ
diff --git a/lib/kotlin-ide-common.jar b/lib/kotlin-ide-common.jar
index c8200fcb..8e14e48c 100644
--- a/lib/kotlin-ide-common.jar
+++ b/lib/kotlin-ide-common.jar
Binary files differ
diff --git a/lib/kotlin-script-runtime.jar b/lib/kotlin-script-runtime.jar
index 96bfa29a..547ed51d 100644
--- a/lib/kotlin-script-runtime.jar
+++ b/lib/kotlin-script-runtime.jar
Binary files differ
diff --git a/lib/markdown.jar b/lib/markdown.jar
index eb2c5ab4..a9011fea 100644
--- a/lib/markdown.jar
+++ b/lib/markdown.jar
Binary files differ
diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt
index 38dc543b..e9cb35a2 100644
--- a/runners/ant/src/main/kotlin/ant/dokka.kt
+++ b/runners/ant/src/main/kotlin/ant/dokka.kt
@@ -5,10 +5,7 @@ import org.apache.tools.ant.Project
import org.apache.tools.ant.Task
import org.apache.tools.ant.types.Path
import org.apache.tools.ant.types.Reference
-import org.jetbrains.dokka.DocumentationOptions
-import org.jetbrains.dokka.DokkaGenerator
-import org.jetbrains.dokka.DokkaLogger
-import org.jetbrains.dokka.SourceLinkDefinition
+import org.jetbrains.dokka.*
import java.io.File
class AntLogger(val task: Task): DokkaLogger {
@@ -19,10 +16,17 @@ class AntLogger(val task: Task): DokkaLogger {
class AntSourceLinkDefinition(var path: String? = null, var url: String? = null, var lineSuffix: String? = null)
-class DokkaAntTask(): Task() {
+class AntSourceRoot(var path: String? = null, var platforms: String? = null)
+
+fun AntSourceRoot.toSourceRoot(): SourceRoot? = path?.let {
+ path -> SourceRoot(path, platforms?.split(',').orEmpty())
+}
+
+class DokkaAntTask: Task() {
var moduleName: String? = null
var outputDir: String? = null
var outputFormat: String = "html"
+ var impliedPlatforms: String = ""
var jdkVersion: Int = 6
var skipDeprecated: Boolean = false
@@ -33,6 +37,7 @@ class DokkaAntTask(): Task() {
val includesPath: Path by lazy { Path(getProject()) }
val antSourceLinks: MutableList<AntSourceLinkDefinition> = arrayListOf()
+ val antSourceRoots: MutableList<AntSourceRoot> = arrayListOf()
fun setClasspath(classpath: Path) {
compileClasspath.append(classpath)
@@ -68,8 +73,10 @@ class DokkaAntTask(): Task() {
return def
}
+ fun createSourceRoot(): AntSourceRoot = AntSourceRoot().apply { antSourceRoots.add(this) }
+
override fun execute() {
- if (sourcePath.list().size == 0) {
+ if (sourcePath.list().isEmpty() && antSourceRoots.isEmpty()) {
throw BuildException("At least one source path needs to be specified")
}
if (moduleName == null) {
@@ -87,14 +94,15 @@ class DokkaAntTask(): Task() {
val generator = DokkaGenerator(
AntLogger(this),
compileClasspath.list().toList(),
- sourcePath.list().toList(),
+ sourcePath.list().map { SourceRoot(it) } + antSourceRoots.mapNotNull { it.toSourceRoot() },
samplesPath.list().toList(),
includesPath.list().toList(),
moduleName!!,
DocumentationOptions(outputDir!!, outputFormat,
skipDeprecated = skipDeprecated,
sourceLinks = sourceLinks,
- jdkVersion = jdkVersion)
+ jdkVersion = jdkVersion,
+ impliedPlatforms = impliedPlatforms.split(','))
)
generator.generate()
}
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
index afc67e45..58bca522 100644
--- a/runners/cli/src/main/kotlin/cli/main.kt
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -44,6 +44,9 @@ class DokkaArguments {
@set:Argument(value = "jdkVersion", description = "Version of JDK to use for linking to JDK JavaDoc")
var jdkVersion: Int = 6
+
+ @set:Argument(value = "impliedPlatforms", description = "List of implied platforms (comma-separated)")
+ var impliedPlatforms: String = ""
}
@@ -72,13 +75,14 @@ object MainKt {
arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
arguments.outputFormat,
skipDeprecated = arguments.nodeprecated,
- sourceLinks = sourceLinks
+ sourceLinks = sourceLinks,
+ impliedPlatforms = arguments.impliedPlatforms.split(',')
)
val generator = DokkaGenerator(
DokkaConsoleLogger,
classPath,
- sources,
+ sources.map(::parseSourceRoot),
samples,
includes,
arguments.moduleName,
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
index 7ed83473..3a78080c 100644
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -159,6 +159,7 @@ open class DokkaTask : DefaultTask() {
outputDirectory,
outputFormat,
false,
+ false,
reportNotDocumented,
skipEmptyPackages,
skipDeprecated,
diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
index 899d2dde..c3cf7509 100644
--- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
+++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
@@ -7,11 +7,12 @@ import org.apache.maven.plugin.AbstractMojo
import org.apache.maven.plugins.annotations.*
import org.apache.maven.project.MavenProject
import org.apache.maven.project.MavenProjectHelper
-import org.jetbrains.dokka.DokkaGenerator
-import org.jetbrains.dokka.SourceLinkDefinition
-import org.jetbrains.dokka.DocumentationOptions
import org.codehaus.plexus.archiver.Archiver
import org.codehaus.plexus.archiver.jar.JarArchiver
+import org.jetbrains.dokka.DocumentationOptions
+import org.jetbrains.dokka.DokkaGenerator
+import org.jetbrains.dokka.SourceLinkDefinition
+import org.jetbrains.dokka.SourceRoot
import java.io.File
class SourceLinkMapItem {
@@ -66,7 +67,7 @@ abstract class AbstractDokkaMojo : AbstractMojo() {
val gen = DokkaGenerator(
MavenDokkaLogger(log),
classpath,
- sourceDirectories,
+ sourceDirectories.map { SourceRoot(it) },
samplesDirs,
includeDirs + includes,
moduleName,