aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle4
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt267
-rw-r--r--core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt30
-rw-r--r--core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt30
-rw-r--r--core/src/main/kotlin/Analysis/JavaResolveExtension.kt131
-rw-r--r--core/src/main/kotlin/DokkaBootstrapImpl.kt30
-rw-r--r--core/src/main/kotlin/Formats/AnalysisComponents.kt45
-rw-r--r--core/src/main/kotlin/Formats/FormatDescriptor.kt48
-rw-r--r--core/src/main/kotlin/Formats/GFMFormatService.kt46
-rw-r--r--core/src/main/kotlin/Formats/HtmlFormatService.kt23
-rw-r--r--core/src/main/kotlin/Formats/HtmlTemplateService.kt8
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt117
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt91
-rw-r--r--core/src/main/kotlin/Formats/JekyllFormatService.kt26
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt67
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt41
-rw-r--r--core/src/main/kotlin/Formats/MarkdownFormatService.kt14
-rw-r--r--core/src/main/kotlin/Formats/OutlineService.kt2
-rw-r--r--core/src/main/kotlin/Formats/PackageListService.kt10
-rw-r--r--core/src/main/kotlin/Formats/StandardFormats.kt44
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt381
-rw-r--r--core/src/main/kotlin/Formats/YamlOutlineService.kt8
-rw-r--r--core/src/main/kotlin/Generation/ConsoleGenerator.kt42
-rw-r--r--core/src/main/kotlin/Generation/DocumentationMerger.kt180
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt99
-rw-r--r--core/src/main/kotlin/Generation/FileGenerator.kt32
-rw-r--r--core/src/main/kotlin/Generation/Generator.kt8
-rw-r--r--core/src/main/kotlin/Generation/configurationImpl.kt75
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt59
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt340
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt13
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt486
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt171
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt22
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt2
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt25
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt201
-rw-r--r--core/src/main/kotlin/Languages/CommonLanguageService.kt84
-rw-r--r--core/src/main/kotlin/Languages/NewJavaLanguageService.kt197
-rw-r--r--core/src/main/kotlin/Locations/FoldersLocationService.kt30
-rw-r--r--core/src/main/kotlin/Locations/Location.kt61
-rw-r--r--core/src/main/kotlin/Locations/LocationService.kt78
-rw-r--r--core/src/main/kotlin/Locations/SingleFolderLocationService.kt20
-rw-r--r--core/src/main/kotlin/Model/Content.kt33
-rw-r--r--core/src/main/kotlin/Model/DescriptorSignatureProvider.kt7
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt86
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt67
-rw-r--r--core/src/main/kotlin/Model/ElementSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Model/PackageDocs.kt69
-rw-r--r--core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt2
-rw-r--r--core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt4
-rw-r--r--core/src/main/kotlin/Utilities/DokkaModules.kt86
-rw-r--r--core/src/main/kotlin/Utilities/Html.kt4
-rw-r--r--core/src/main/kotlin/Utilities/ServiceLocator.kt18
-rw-r--r--core/src/main/kotlin/Utilities/Uri.kt40
-rw-r--r--core/src/main/kotlin/javadoc/dokka-adapters.kt32
-rw-r--r--core/src/main/resources/dokka/format/java-layout-html.properties2
-rw-r--r--core/src/main/resources/dokka/format/javadoc.properties3
-rw-r--r--core/src/main/resources/dokka/generator/default.properties2
-rw-r--r--core/src/main/resources/dokka/generator/javadoc.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties2
-rw-r--r--core/src/main/resources/dokka/language/java.properties1
-rw-r--r--core/src/main/resources/dokka/language/kotlin.properties1
-rw-r--r--core/src/main/resources/dokka/outline/yaml.properties1
-rw-r--r--core/src/test/kotlin/TestAPI.kt253
-rw-r--r--core/src/test/kotlin/format/FileGeneratorTestCase.kt35
-rw-r--r--core/src/test/kotlin/format/GFMFormatTest.kt31
-rw-r--r--core/src/test/kotlin/format/HtmlFormatTest.kt168
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt56
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt80
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt56
-rw-r--r--core/src/test/kotlin/format/MarkdownFormatTest.kt453
-rw-r--r--core/src/test/kotlin/format/PackageDocsTest.kt45
-rw-r--r--core/src/test/kotlin/issues/IssuesTest.kt16
-rw-r--r--core/src/test/kotlin/javadoc/JavadocTest.kt75
-rw-r--r--core/src/test/kotlin/model/ClassTest.kt123
-rw-r--r--core/src/test/kotlin/model/CommentTest.kt36
-rw-r--r--core/src/test/kotlin/model/FunctionTest.kt67
-rw-r--r--core/src/test/kotlin/model/JavaTest.kt40
-rw-r--r--core/src/test/kotlin/model/KotlinAsJavaTest.kt15
-rw-r--r--core/src/test/kotlin/model/LinkTest.kt22
-rw-r--r--core/src/test/kotlin/model/PackageTest.kt57
-rw-r--r--core/src/test/kotlin/model/PropertyTest.kt66
-rw-r--r--core/src/test/kotlin/model/TypeAliasTest.kt18
-rw-r--r--core/testdata/format/JavaSupertype.html10
-rw-r--r--core/testdata/format/accessor.md2
-rw-r--r--core/testdata/format/annotatedTypeParameter.md4
-rw-r--r--core/testdata/format/annotationClass.md4
-rw-r--r--core/testdata/format/annotationClass.package.md4
-rw-r--r--core/testdata/format/annotationParams.md2
-rw-r--r--core/testdata/format/annotations.md8
-rw-r--r--core/testdata/format/arrayAverage.md6
-rw-r--r--core/testdata/format/backtickInCodeBlock.md2
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.html2
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.md2
-rw-r--r--core/testdata/format/bracket.html2
-rw-r--r--core/testdata/format/brokenLink.html2
-rw-r--r--core/testdata/format/classWithCompanionObject.html8
-rw-r--r--core/testdata/format/classWithCompanionObject.md8
-rw-r--r--core/testdata/format/codeBlock.html44
-rw-r--r--core/testdata/format/codeBlock.md14
-rw-r--r--core/testdata/format/codeBlockNoHtmlEscape.md2
-rw-r--r--core/testdata/format/codeSpan.html2
-rw-r--r--core/testdata/format/companionImplements.md8
-rw-r--r--core/testdata/format/companionObjectExtension.md6
-rw-r--r--core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html12
-rw-r--r--core/testdata/format/deprecated.class.html38
-rw-r--r--core/testdata/format/deprecated.package.html8
-rw-r--r--core/testdata/format/dynamicExtension.md4
-rw-r--r--core/testdata/format/dynamicType.md2
-rw-r--r--core/testdata/format/emptyDescription.md2
-rw-r--r--core/testdata/format/entity.html4
-rw-r--r--core/testdata/format/enumClass.md6
-rw-r--r--core/testdata/format/enumClass.value.md2
-rw-r--r--core/testdata/format/enumRef.kt4
-rw-r--r--core/testdata/format/enumRef.md8
-rw-r--r--core/testdata/format/exceptionClass.md4
-rw-r--r--core/testdata/format/exceptionClass.package.md4
-rw-r--r--core/testdata/format/exclInCodeBlock.md2
-rw-r--r--core/testdata/format/extensionFunctionParameter.md4
-rw-r--r--core/testdata/format/extensionScope.md4
-rw-r--r--core/testdata/format/extensionWithDocumentedReceiver.md2
-rw-r--r--core/testdata/format/extensions.class.md6
-rw-r--r--core/testdata/format/extensions.package.md4
-rw-r--r--core/testdata/format/externalReferenceLink.md2
-rw-r--r--core/testdata/format/functionWithDefaultParameter.md2
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.html90
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.md36
-rw-r--r--core/testdata/format/genericInheritedExtensions.md10
-rw-r--r--core/testdata/format/gfm/listInTableCell.md6
-rw-r--r--core/testdata/format/gfm/sample.md8
-rw-r--r--core/testdata/format/htmlEscaping.html4
-rw-r--r--core/testdata/format/inapplicableExtensionFunctions.md8
-rw-r--r--core/testdata/format/indentedCodeBlock.html2
-rw-r--r--core/testdata/format/indentedCodeBlock.md2
-rw-r--r--core/testdata/format/inheritedCompanionObjectProperties.md16
-rw-r--r--core/testdata/format/inheritedExtensions.md10
-rw-r--r--core/testdata/format/inheritedLink.1.kt10
-rw-r--r--core/testdata/format/inheritedLink.kt11
-rw-r--r--core/testdata/format/inheritedLink.md17
-rw-r--r--core/testdata/format/inheritedMembers.md14
-rw-r--r--core/testdata/format/javaCodeInParam.md4
-rw-r--r--core/testdata/format/javaCodeLiteralTags.md4
-rw-r--r--core/testdata/format/javaDeprecated.html4
-rw-r--r--core/testdata/format/javaLinkTag.html10
-rw-r--r--core/testdata/format/javaLinkTagWithLabel.html10
-rw-r--r--core/testdata/format/javaSeeTag.html8
-rw-r--r--core/testdata/format/javaSpaceInAuthor.md4
-rw-r--r--core/testdata/format/javadocHtml.md4
-rw-r--r--core/testdata/format/javadocOrderedList.md4
-rw-r--r--core/testdata/format/jdkLinks.md4
-rw-r--r--core/testdata/format/linkWithLabel.html10
-rw-r--r--core/testdata/format/linkWithStarProjection.html4
-rw-r--r--core/testdata/format/linksInEmphasis.md14
-rw-r--r--core/testdata/format/linksInHeaders.md28
-rw-r--r--core/testdata/format/linksInStrong.md14
-rw-r--r--core/testdata/format/markdownInLinks.html2
-rw-r--r--core/testdata/format/memberExtension.md6
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md2
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.md6
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.package.md8
-rw-r--r--core/testdata/format/multiplatform/implied/foo.md16
-rw-r--r--core/testdata/format/multiplatform/merge/multiplatform.package.md4
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/foo.md16
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/foo.md8
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md4
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md4
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md4
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md4
-rw-r--r--core/testdata/format/multiplatform/simple/multiplatform.package.md6
-rw-r--r--core/testdata/format/multipleTypeParameterConstraints.md13
-rw-r--r--core/testdata/format/nestedLists.md30
-rw-r--r--core/testdata/format/newlineInTableCell.package.md4
-rw-r--r--core/testdata/format/notPublishedTypeAliasAutoExpansion.md6
-rw-r--r--core/testdata/format/nullability.md6
-rw-r--r--core/testdata/format/operatorOverloading.md4
-rw-r--r--core/testdata/format/orderedList.html4
-rw-r--r--core/testdata/format/overloads.html4
-rw-r--r--core/testdata/format/overloadsWithDescription.html4
-rw-r--r--core/testdata/format/overloadsWithDifferentDescriptions.html2
-rw-r--r--core/testdata/format/overridingFunction.md4
-rw-r--r--core/testdata/format/paramTag.md2
-rw-r--r--core/testdata/format/parameterAnchor.html6
-rw-r--r--core/testdata/format/parenthesis.html2
-rw-r--r--core/testdata/format/propertyVar.md2
-rw-r--r--core/testdata/format/qualifiedNameLink.md2
-rw-r--r--core/testdata/format/receiverParameterTypeBound.md6
-rw-r--r--core/testdata/format/receiverReference.md4
-rw-r--r--core/testdata/format/referenceLink.md10
-rw-r--r--core/testdata/format/reifiedTypeParameter.md2
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md4
-rw-r--r--core/testdata/format/returnWithLink.html4
-rw-r--r--core/testdata/format/sampleByFQName.md7
-rw-r--r--core/testdata/format/sampleByShortName.md7
-rw-r--r--core/testdata/format/see.html31
-rw-r--r--core/testdata/format/shadowedExtensionFunctions.md10
-rw-r--r--core/testdata/format/sinceKotlin.html4
-rw-r--r--core/testdata/format/sinceKotlin.md4
-rw-r--r--core/testdata/format/sinceKotlin.package.md4
-rw-r--r--core/testdata/format/sinceKotlinWide.package.md6
-rw-r--r--core/testdata/format/starProjection.md4
-rw-r--r--core/testdata/format/summarizeSignatures.md10
-rw-r--r--core/testdata/format/summarizeSignaturesProperty.md10
-rw-r--r--core/testdata/format/suspendParam.md2
-rw-r--r--core/testdata/format/suspendParam.package.md4
-rw-r--r--core/testdata/format/throwsTag.md2
-rw-r--r--core/testdata/format/tokensInEmphasis.md10
-rw-r--r--core/testdata/format/tokensInHeaders.md26
-rw-r--r--core/testdata/format/tokensInStrong.md12
-rw-r--r--core/testdata/format/tripleBackticks.html2
-rw-r--r--core/testdata/format/typeAliases.md73
-rw-r--r--core/testdata/format/typeAliases.package.md30
-rw-r--r--core/testdata/format/typeLink.html6
-rw-r--r--core/testdata/format/typeParameterBounds.md4
-rw-r--r--core/testdata/format/typeParameterReference.md6
-rw-r--r--core/testdata/format/typeParameterVariance.md4
-rw-r--r--core/testdata/format/typeProjectionVariance.md4
-rw-r--r--core/testdata/format/uninterpretedEmphasisCharacters.html2
-rw-r--r--core/testdata/format/unorderedLists.md24
-rw-r--r--core/testdata/format/varargsFunction.md2
-rw-r--r--core/testdata/format/website-html/dataTags/multiplatform.package.html18
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html6
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html8
-rw-r--r--core/testdata/format/website-html/dropImport.html2
-rw-r--r--core/testdata/format/website-html/newLinesInImportList.html2
-rw-r--r--core/testdata/format/website-html/newLinesInSamples.html2
-rw-r--r--core/testdata/format/website-html/overloadGroup.html2
-rw-r--r--core/testdata/format/website-html/returnTag.html8
-rw-r--r--core/testdata/format/website-html/sample.html2
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.html2
235 files changed, 4901 insertions, 2264 deletions
diff --git a/core/build.gradle b/core/build.gradle
index a8f0f275..a4538471 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -29,10 +29,12 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version"
compile "teamcity:kotlin-ide-common:$bundled_kotlin_compiler_version"
- compile "teamcity:markdown:$bundled_kotlin_compiler_version"
+ compile "org.jetbrains:markdown:$markdownVersion"
compile intellijCoreAnalysis()
+ compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8'
+
//tools.jar
def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") })
compileOnly toolsJar
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
index 56249ac4..fe9ec2fa 100644
--- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -1,5 +1,6 @@
package org.jetbrains.dokka
+import com.google.common.collect.ImmutableMap
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.CoreModuleManager
import com.intellij.mock.MockComponentManager
@@ -17,6 +18,12 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.analyzer.common.CommonAnalyzerFacade
+import org.jetbrains.kotlin.builtins.DefaultBuiltIns
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
+import org.jetbrains.kotlin.caches.project.LibraryModuleInfo
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
@@ -27,23 +34,28 @@ import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.container.getService
+import org.jetbrains.kotlin.container.tryGetService
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.PackagePartProvider
+import org.jetbrains.kotlin.idea.caches.resolve.JsAnalyzerFacade
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.js.config.JSConfigurationKeys
+import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.name.Name
-import org.jetbrains.kotlin.platform.JvmBuiltIns
-import org.jetbrains.kotlin.psi.KtDeclaration
-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.psi.*
+import org.jetbrains.kotlin.resolve.*
+import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
+import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import java.io.File
/**
@@ -54,7 +66,7 @@ import java.io.File
* $messageCollector: required by compiler infrastructure and will receive all compiler messages
* $body: optional and can be used to configure environment without creating local variable
*/
-class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
+class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPlatform: Platform) : Disposable {
val configuration = CompilerConfiguration()
init {
@@ -63,7 +75,12 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
fun createCoreEnvironment(): KotlinCoreEnvironment {
System.setProperty("idea.io.use.fallback", "true")
- val environment = KotlinCoreEnvironment.createForProduction(this, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+
+ val configFiles = when (analysisPlatform) {
+ Platform.jvm, Platform.common -> EnvironmentConfigFiles.JVM_CONFIG_FILES
+ Platform.js -> EnvironmentConfigFiles.JS_CONFIG_FILES
+ }
+ val environment = KotlinCoreEnvironment.createForProduction(this, configuration, configFiles)
val projectComponentManager = environment.project as MockComponentManager
val projectFileIndex = CoreProjectFileIndex(environment.project,
@@ -87,19 +104,33 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
return environment
}
- fun createSourceModuleSearchScope(project: Project, sourceFiles: List<KtFile>): GlobalSearchScope {
- // TODO: Fix when going to implement dokka for JS
- return TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
- }
+ fun createSourceModuleSearchScope(project: Project, sourceFiles: List<KtFile>): GlobalSearchScope =
+ when (analysisPlatform) {
+ Platform.jvm -> TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles)
+ Platform.js, Platform.common -> GlobalSearchScope.filesScope(project, sourceFiles.map { it.virtualFile }.toSet())
+ }
- fun createResolutionFacade(environment: KotlinCoreEnvironment): DokkaResolutionFacade {
+ fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair<DokkaResolutionFacade, DokkaResolutionFacade> {
val projectContext = ProjectContext(environment.project)
val sourceFiles = environment.getSourceFiles()
- val library = object : ModuleInfo {
+ val targetPlatform = when (analysisPlatform) {
+ Platform.js -> JsPlatform
+ Platform.common -> TargetPlatform.Common
+ Platform.jvm -> JvmPlatform
+ }
+
+ val library = object : LibraryModuleInfo {
+ override val platform: TargetPlatform
+ get() = targetPlatform
+
+ override fun getLibraryRoots(): Collection<String> {
+ return classpath.map { it.absolutePath }
+ }
+
override val name: Name = Name.special("<library>")
override fun dependencies(): List<ModuleInfo> = listOf(this)
}
@@ -109,56 +140,126 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
}
val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles)
+ val modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo> = {
+ when (it) {
+ library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
+ module -> ModuleContent(it, emptyList(), GlobalSearchScope.allScope(environment.project))
+ else -> throw IllegalArgumentException("Unexpected module info")
+ }
+ }
- val builtIns = JvmBuiltIns(projectContext.storageManager)
+ var builtIns: JvmBuiltIns? = null
+ val resolverForProject = when (analysisPlatform) {
+ Platform.jvm -> {
+ builtIns = JvmBuiltIns(projectContext.storageManager)
+ createJvmResolverForProject(projectContext, module, library, modulesContent, sourcesScope, builtIns)
+ }
+ Platform.js -> createJsResolverForProject(projectContext, module, library, modulesContent)
+ Platform.common -> createCommonResolverForProject(projectContext, module, library, modulesContent, environment)
- val javaRoots = classpath
- .mapNotNull {
- val rootFile = when {
- it.extension == "jar" ->
- StandardFileSystems.jar().findFileByPath("${it.absolutePath}${URLUtil.JAR_SEPARATOR}")
- else ->
- StandardFileSystems.local().findFileByPath(it.absolutePath)
- }
+ }
+ val resolverForLibrary = resolverForProject.resolverForModule(library) // Required before module to initialize library properly
+ val resolverForModule = resolverForProject.resolverForModule(module)
+ val libraryModuleDescriptor = resolverForProject.descriptorForModule(library)
+ val moduleDescriptor = resolverForProject.descriptorForModule(module)
+ builtIns?.initialize(moduleDescriptor, true)
+ val libraryResolutionFacade = DokkaResolutionFacade(environment.project, libraryModuleDescriptor, resolverForLibrary)
+ val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule)
+ val projectComponentManager = environment.project as MockComponentManager
+ projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created))
- rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
- }
+ return created to libraryResolutionFacade
+ }
- val resolverForProject = ResolverForProjectImpl(
- "Dokka",
- projectContext,
- listOf(library, module),
- { JvmAnalyzerFacade },
- {
- when (it) {
- library -> ModuleContent(emptyList(), GlobalSearchScope.notScope(sourcesScope))
- module -> ModuleContent(sourceFiles, sourcesScope)
- else -> throw IllegalArgumentException("Unexpected module info")
- }
- },
- JvmPlatformParameters {
- val file = (it as JavaClassImpl).psi.containingFile.virtualFile
- if (file in sourcesScope)
- module
- else
- library
- },
- CompilerEnvironment,
- packagePartProviderFactory = { info, content ->
- JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
- addRoots(javaRoots)
- }
- },
- builtIns = builtIns,
- modulePlatforms = { JvmPlatform.multiTargetPlatform }
+ private fun createCommonResolverForProject(
+ projectContext: ProjectContext,
+ module: ModuleInfo,
+ library: LibraryModuleInfo,
+ modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>,
+ environment: KotlinCoreEnvironment
+ ): ResolverForProjectImpl<ModuleInfo> {
+ return ResolverForProjectImpl(
+ debugName = "Dokka",
+ projectContext = projectContext,
+ modules = listOf(module, library),
+ modulesContent = modulesContent,
+ modulePlatforms = { MultiTargetPlatform.Common },
+ moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
+ resolverForModuleFactoryByPlatform = { CommonAnalyzerFacade },
+ platformParameters = object : PlatformAnalysisParameters {},
+ targetEnvironment = CompilerEnvironment,
+ packagePartProviderFactory = { content ->
+ environment.createPackagePartProvider(content.moduleContentScope)
+ },
+ builtIns = DefaultBuiltIns.Instance
)
+ }
- resolverForProject.resolverForModule(library) // Required before module to initialize library properly
- val resolverForModule = resolverForProject.resolverForModule(module)
- val moduleDescriptor = resolverForProject.descriptorForModule(module)
- builtIns.initialize(moduleDescriptor, true)
- return DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule)
+ private fun createJsResolverForProject(
+ projectContext: ProjectContext,
+ module: ModuleInfo,
+ library: LibraryModuleInfo,
+ modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>
+ ): ResolverForProjectImpl<ModuleInfo> {
+ return ResolverForProjectImpl(
+ debugName = "Dokka",
+ projectContext = projectContext,
+ modules = listOf(module, library),
+ modulesContent = modulesContent,
+ modulePlatforms = { JsPlatform.multiTargetPlatform },
+ moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
+ resolverForModuleFactoryByPlatform = { JsAnalyzerFacade },
+ platformParameters = object : PlatformAnalysisParameters {},
+ targetEnvironment = CompilerEnvironment,
+ packagePartProviderFactory = { PackagePartProvider.Empty },
+ builtIns = JsPlatform.builtIns
+ )
+ }
+
+ private fun createJvmResolverForProject(
+ projectContext: ProjectContext,
+ module: ModuleInfo,
+ library: LibraryModuleInfo,
+ modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>,
+ sourcesScope: GlobalSearchScope,
+ builtIns: KotlinBuiltIns
+ ): ResolverForProjectImpl<ModuleInfo> {
+ val javaRoots = classpath
+ .mapNotNull {
+ val rootFile = when {
+ it.extension == "jar" ->
+ StandardFileSystems.jar().findFileByPath("${it.absolutePath}${URLUtil.JAR_SEPARATOR}")
+ else ->
+ StandardFileSystems.local().findFileByPath(it.absolutePath)
+ }
+
+ rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
+ }
+
+ return ResolverForProjectImpl(
+ debugName = "Dokka",
+ projectContext = projectContext,
+ modules = listOf(module, library),
+ modulesContent = modulesContent,
+ modulePlatforms = { JvmPlatform.multiTargetPlatform },
+ moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
+ resolverForModuleFactoryByPlatform = { JvmAnalyzerFacade },
+ platformParameters = JvmPlatformParameters {
+ val file = (it as JavaClassImpl).psi.containingFile.virtualFile
+ if (file in sourcesScope)
+ module
+ else
+ library
+ },
+ targetEnvironment = CompilerEnvironment,
+ packagePartProviderFactory = { content ->
+ JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
+ addRoots(javaRoots)
+ }
+ },
+ builtIns = builtIns
+ )
}
fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) {
@@ -178,6 +279,10 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
* $paths: collection of files to add
*/
fun addClasspath(paths: List<File>) {
+ if (analysisPlatform == Platform.js) {
+ configuration.addAll(JSConfigurationKeys.LIBRARIES, paths.map { it.absolutePath })
+ }
+ configuration.addAll(JSConfigurationKeys.LIBRARIES, paths.map { it.absolutePath })
configuration.addJvmClasspathRoots(paths)
}
@@ -186,6 +291,10 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
* $path: path to add
*/
fun addClasspath(path: File) {
+ if (analysisPlatform == Platform.js) {
+ configuration.add(JSConfigurationKeys.LIBRARIES, path.absolutePath)
+ }
+ configuration.add(JSConfigurationKeys.LIBRARIES, path.absolutePath)
configuration.addJvmClasspathRoot(path)
}
@@ -232,8 +341,12 @@ fun contentRootFromPath(path: String): ContentRoot {
class DokkaResolutionFacade(override val project: Project,
override val moduleDescriptor: ModuleDescriptor,
val resolverForModule: ResolverForModule) : ResolutionFacade {
+ override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
+ throw UnsupportedOperationException()
+ }
+
override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
- return null
+ return resolverForModule.componentProvider.tryGetService(serviceClass)
}
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
@@ -247,10 +360,42 @@ class DokkaResolutionFacade(override val project: Project,
val resolveSession: ResolveSession get() = getFrontendService(ResolveSession::class.java)
override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext {
- throw UnsupportedOperationException()
- }
+ if (element is KtDeclaration) {
+ val descriptor = resolveToDescriptor(element)
+ return object : BindingContext {
+ override fun <K : Any?, V : Any?> getKeys(p0: WritableSlice<K, V>?): Collection<K> {
+ throw UnsupportedOperationException()
+ }
- override fun analyzeFullyAndGetResult(elements: Collection<KtElement>): AnalysisResult {
+ override fun getType(p0: KtExpression): KotlinType? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <K : Any?, V : Any?> get(slice: ReadOnlySlice<K, V>?, key: K): V? {
+ if (key != element) {
+ throw UnsupportedOperationException()
+ }
+ return when {
+ slice == BindingContext.DECLARATION_TO_DESCRIPTOR -> descriptor as V
+ slice == BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER && (element as KtParameter).hasValOrVar() -> descriptor as V
+ else -> null
+ }
+ }
+
+ override fun getDiagnostics(): Diagnostics {
+ throw UnsupportedOperationException()
+ }
+
+ override fun addOwnDataTo(p0: BindingTrace, p1: Boolean) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <K : Any?, V : Any?> getSliceContents(p0: ReadOnlySlice<K, V>): ImmutableMap<K, V> {
+ throw UnsupportedOperationException()
+ }
+
+ }
+ }
throw UnsupportedOperationException()
}
diff --git a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt
new file mode 100644
index 00000000..31b8ffc7
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiFile
+import org.jetbrains.kotlin.analyzer.ModuleInfo
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.TargetPlatform
+import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
+
+
+class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService {
+ override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
+ return resolutionFacade
+ }
+
+ override fun getResolutionFacadeByFile(file: PsiFile, platform: TargetPlatform): ResolutionFacade {
+ return resolutionFacade
+ }
+
+ override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? {
+ return resolutionFacade
+ }
+
+ override fun getSuppressionCache(): KotlinSuppressCache {
+ throw UnsupportedOperationException()
+ }
+
+}
+
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
index 2f2f94b3..4f6a7c76 100644
--- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
+++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
@@ -228,24 +228,26 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont
}
private val moduleSourceOrderEntry = object : ModuleSourceOrderEntry {
- override fun getFiles(p0: OrderRootType?): Array<out VirtualFile> {
+ override fun getFiles(p0: OrderRootType): Array<VirtualFile> {
throw UnsupportedOperationException()
}
- override fun getPresentableName(): String {
+ override fun getUrls(p0: OrderRootType): Array<String> {
throw UnsupportedOperationException()
}
- override fun getUrls(p0: OrderRootType?): Array<out String> {
+ override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R {
throw UnsupportedOperationException()
}
- override fun getOwnerModule(): Module = module
- override fun <R : Any?> accept(p0: RootPolicy<R>?, p1: R?): R {
+ override fun getPresentableName(): String {
throw UnsupportedOperationException()
}
+ override fun getOwnerModule(): Module = module
+
+
override fun isValid(): Boolean {
throw UnsupportedOperationException()
}
@@ -262,29 +264,29 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont
}
private val sdkOrderEntry = object : JdkOrderEntry {
- override fun getJdkName(): String? {
+ override fun getFiles(p0: OrderRootType): Array<VirtualFile> {
throw UnsupportedOperationException()
}
- override fun getJdk(): Sdk = sdk
-
- override fun getFiles(p0: OrderRootType?): Array<out VirtualFile> {
+ override fun getUrls(p0: OrderRootType): Array<String> {
throw UnsupportedOperationException()
}
- override fun getPresentableName(): String {
+ override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R {
throw UnsupportedOperationException()
}
- override fun getUrls(p0: OrderRootType?): Array<out String> {
+ override fun getJdkName(): String? {
throw UnsupportedOperationException()
}
- override fun getOwnerModule(): Module {
+ override fun getJdk(): Sdk = sdk
+
+ override fun getPresentableName(): String {
throw UnsupportedOperationException()
}
- override fun <R : Any?> accept(p0: RootPolicy<R>?, p1: R?): R {
+ override fun getOwnerModule(): Module {
throw UnsupportedOperationException()
}
@@ -358,7 +360,7 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont
override fun getRootModel(p0: Module): ModuleRootModel = this@MyModuleRootManager
})
- override fun <T : Any?> getModuleExtension(p0: Class<T>?): T {
+ override fun <T : Any?> getModuleExtension(p0: Class<T>): T {
throw UnsupportedOperationException()
}
diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
new file mode 100644
index 00000000..4dc6b366
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("JavaResolutionUtils")
+
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import org.jetbrains.kotlin.asJava.classes.KtLightClass
+import org.jetbrains.kotlin.asJava.unwrapped
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.*
+import org.jetbrains.kotlin.load.java.structure.impl.*
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
+import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+
+// TODO: Remove that file
+
+@JvmOverloads
+fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ val method = originalElement as? PsiMethod ?: return null
+ if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null
+ val resolver = method.getJavaDescriptorResolver(resolutionFacade)
+ return when {
+ method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method))
+ else -> resolver?.resolveMethod(JavaMethodImpl(method))
+ }
+}
+
+@JvmOverloads
+fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? {
+ val psiClass = originalElement as? PsiClass ?: return null
+ return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass))
+}
+
+@JvmOverloads
+fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? {
+ val field = originalElement as? PsiField ?: return null
+ return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field))
+}
+
+@JvmOverloads
+fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ return when (this) {
+ is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade)
+ is PsiClass -> getJavaClassDescriptor(resolutionFacade)
+ is PsiMethod -> getJavaMethodDescriptor(resolutionFacade)
+ is PsiField -> getJavaFieldDescriptor(resolutionFacade)
+ else -> null
+ }
+}
+
+@JvmOverloads
+fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ val callable = unwrapped
+ return when (callable) {
+ is PsiMember -> getJavaMemberDescriptor(resolutionFacade)
+ is KtDeclaration -> {
+ val descriptor = resolutionFacade.resolveToDescriptor(callable)
+ if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor
+ }
+ else -> null
+ }
+}
+
+private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? {
+ return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java)
+}
+
+private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? {
+ return getContainingScope(method)
+ ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES)
+ ?.filterIsInstance<DeclarationDescriptorWithSource>()
+ ?.findByJavaElement(method)
+}
+
+private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? {
+ return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor)
+}
+
+private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? {
+ return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field)
+}
+
+private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? {
+ val containingClass = resolveClass(member.containingClass)
+ return if (member.isStatic)
+ containingClass?.staticScope
+ else
+ containingClass?.defaultType?.memberScope
+}
+
+private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElement(javaElement: JavaElement): T? {
+ return firstOrNull { member ->
+ val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement
+ when {
+ memberJavaElement == javaElement ->
+ true
+ memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> ->
+ memberJavaElement.psi.isEquivalentTo(javaElement.psi)
+ else ->
+ false
+ }
+ }
+}
+
+fun PsiElement.javaResolutionFacade() =
+ KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform)!!
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index 126a0175..ccafcd12 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -2,7 +2,6 @@ package org.jetbrains.dokka
import org.jetbrains.dokka.DokkaConfiguration.PackageOptions
import ru.yole.jkid.deserialization.deserialize
-import java.io.File
import java.util.function.BiConsumer
@@ -44,34 +43,7 @@ class DokkaBootstrapImpl : DokkaBootstrap {
= configure(DokkaProxyLogger(logger), deserialize<DokkaConfigurationImpl>(serializedConfigurationJSON))
fun configure(logger: DokkaLogger, configuration: DokkaConfiguration) = with(configuration) {
- generator = DokkaGenerator(
- logger,
- classpath,
- sourceRoots,
- samples,
- includes,
- moduleName,
- DocumentationOptions(
- outputDir,
- format,
- includeNonPublic,
- includeRootPackage,
- reportUndocumented,
- skipEmptyPackages,
- skipDeprecated,
- jdkVersion,
- generateIndexPages,
- sourceLinks,
- impliedPlatforms,
- perPackageOptions,
- externalDocumentationLinks,
- noStdlibLink,
- languageVersion,
- apiVersion,
- cacheRoot,
- suppressedFiles.map { File(it) }
- )
- )
+ generator = DokkaGenerator(configuration, logger)
}
override fun generate() = generator.generate()
diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt
new file mode 100644
index 00000000..d78d4a0c
--- /dev/null
+++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider
+import org.jetbrains.dokka.KotlinElementSignatureProvider
+import org.jetbrains.dokka.ElementSignatureProvider
+import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
+import org.jetbrains.dokka.Samples.SampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.toType
+import kotlin.reflect.KClass
+
+
+interface DefaultAnalysisComponentServices {
+ val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder>
+ val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder>
+ val sampleProcessingService: KClass<out SampleProcessingService>
+ val elementSignatureProvider: KClass<out ElementSignatureProvider>
+}
+
+interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices {
+ override fun configureAnalysis(binder: Binder): Unit = with(binder) {
+ bind<ElementSignatureProvider>() toType elementSignatureProvider
+ bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass
+ bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass
+ bind<SampleProcessingService>() toType sampleProcessingService
+ }
+}
+
+
+object KotlinAsJava : DefaultAnalysisComponentServices {
+ override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
+ override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
+ override val sampleProcessingService = DefaultSampleProcessingService::class
+ override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class
+}
+
+
+object KotlinAsKotlin : DefaultAnalysisComponentServices {
+ override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
+ override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
+ override val sampleProcessingService = DefaultSampleProcessingService::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt
index da0156a7..4bac8aa0 100644
--- a/core/src/main/kotlin/Formats/FormatDescriptor.kt
+++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt
@@ -1,17 +1,43 @@
package org.jetbrains.dokka.Formats
+import com.google.inject.Binder
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.Samples.SampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.lazyBind
+import org.jetbrains.dokka.Utilities.toOptional
+import org.jetbrains.dokka.Utilities.toType
import kotlin.reflect.KClass
-interface FormatDescriptor {
- val formatServiceClass: KClass<out FormatService>?
- val outlineServiceClass: KClass<out OutlineFormatService>?
- val generatorServiceClass: KClass<out Generator>
- val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder>
- val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder>
- val sampleProcessingService: KClass<out SampleProcessingService>
- val packageListServiceClass: KClass<out PackageListService>?
- val descriptorSignatureProvider: KClass<out DescriptorSignatureProvider>
+
+interface FormatDescriptorAnalysisComponent {
+ fun configureAnalysis(binder: Binder)
+}
+
+interface FormatDescriptorOutputComponent {
+ fun configureOutput(binder: Binder)
}
+
+interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent
+
+
+abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor {
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType NodeLocationAwareGenerator::class
+ bind<NodeLocationAwareGenerator>() toType generatorServiceClass
+ bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847
+
+ bind<LanguageService>() toType languageServiceClass
+
+ lazyBind<OutlineFormatService>() toOptional (outlineServiceClass)
+ lazyBind<FormatService>() toOptional formatServiceClass
+ lazyBind<PackageListService>() toOptional packageListServiceClass
+ }
+
+ abstract val formatServiceClass: KClass<out FormatService>?
+ abstract val outlineServiceClass: KClass<out OutlineFormatService>?
+ abstract val generatorServiceClass: KClass<out FileGenerator>
+ abstract val packageListServiceClass: KClass<out PackageListService>?
+
+ open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt
index f741561c..036ec856 100644
--- a/core/src/main/kotlin/Formats/GFMFormatService.kt
+++ b/core/src/main/kotlin/Formats/GFMFormatService.kt
@@ -4,14 +4,14 @@ import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.Utilities.impliedPlatformsName
-open class GFMOutputBuilder(to: StringBuilder,
- location: Location,
- locationService: LocationService,
- languageService: LanguageService,
- extension: String,
- impliedPlatforms: List<String>)
- : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
-{
+open class GFMOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>
+) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
override fun appendTable(vararg columns: String, body: () -> Unit) {
to.appendln(columns.joinToString(" | ", "| ", " |"))
to.appendln("|" + "---|".repeat(columns.size))
@@ -21,8 +21,7 @@ open class GFMOutputBuilder(to: StringBuilder,
override fun appendUnorderedList(body: () -> Unit) {
if (inTableCell) {
wrapInTag("ul", body)
- }
- else {
+ } else {
super.appendUnorderedList(body)
}
}
@@ -30,8 +29,7 @@ open class GFMOutputBuilder(to: StringBuilder,
override fun appendOrderedList(body: () -> Unit) {
if (inTableCell) {
wrapInTag("ol", body)
- }
- else {
+ } else {
super.appendOrderedList(body)
}
}
@@ -39,23 +37,25 @@ open class GFMOutputBuilder(to: StringBuilder,
override fun appendListItem(body: () -> Unit) {
if (inTableCell) {
wrapInTag("li", body)
- }
- else {
+ } else {
super.appendListItem(body)
}
}
}
-open class GFMFormatService(locationService: LocationService,
- signatureGenerator: LanguageService,
- linkExtension: String,
- impliedPlatforms: List<String>)
-: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) {
+open class GFMFormatService(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ linkExtension: String,
+ impliedPlatforms: List<String>
+) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) {
- @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>) : this(locationService, signatureGenerator, "md", impliedPlatforms)
+ @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>
+ ) : this(generator, signatureGenerator, "md", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- GFMOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt
index 5e05f51a..0ad946be 100644
--- a/core/src/main/kotlin/Formats/HtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -4,17 +4,15 @@ import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.Utilities.impliedPlatformsName
import java.io.File
-import java.nio.file.Path
-import java.nio.file.Paths
open class HtmlOutputBuilder(to: StringBuilder,
location: Location,
- locationService: LocationService,
+ generator: NodeLocationAwareGenerator,
languageService: LanguageService,
extension: String,
impliedPlatforms: List<String>,
val templateService: HtmlTemplateService)
- : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
{
override fun appendText(text: String) {
to.append(text.htmlEscape())
@@ -81,7 +79,7 @@ open class HtmlOutputBuilder(to: StringBuilder,
}
override fun appendNodes(nodes: Iterable<DocumentationNode>) {
- templateService.appendHeader(to, getPageTitle(nodes), locationService.calcPathToRoot(location))
+ templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location))
super.appendNodes(nodes)
templateService.appendFooter(to)
}
@@ -95,21 +93,21 @@ open class HtmlOutputBuilder(to: StringBuilder,
}
}
-open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService,
+open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator,
signatureGenerator: LanguageService,
val templateService: HtmlTemplateService,
@Named(impliedPlatformsName) val impliedPlatforms: List<String>)
-: StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService {
+: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService {
override fun enumerateSupportFiles(callback: (String, String) -> Unit) {
callback("/dokka/styles/style.css", "style.css")
}
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, templateService)
+ HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
- templateService.appendHeader(to, "Module Contents", locationService.calcPathToRoot(location))
+ templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location))
super.appendOutline(location, to, nodes)
templateService.appendFooter(to)
}
@@ -123,7 +121,7 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi
link.append(languageService.render(node, LanguageService.RenderMode.FULL))
val tempBuilder = StringBuilder()
createOutputBuilder(tempBuilder, location).appendContent(link)
- to.appendln("<a href=\"${location.path}\">${tempBuilder.toString()}</a><br/>")
+ to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>")
}
override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
@@ -133,11 +131,6 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi
}
}
-private fun LocationService.calcPathToRoot(location: Location): Path {
- val path = Paths.get(location.path)
- return path.parent?.relativize(Paths.get(root.path + '/')) ?: path
-}
-
fun getPageTitle(nodes: Iterable<DocumentationNode>): String? {
val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) }
return breakdownByLocation.keys.singleOrNull()
diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
index 010bc702..a65a7b18 100644
--- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt
+++ b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
@@ -1,9 +1,9 @@
package org.jetbrains.dokka
-import java.nio.file.Path
+import java.io.File
interface HtmlTemplateService {
- fun appendHeader(to: StringBuilder, title: String?, basePath: Path)
+ fun appendHeader(to: StringBuilder, title: String?, basePath: File)
fun appendFooter(to: StringBuilder)
companion object {
@@ -16,7 +16,7 @@ interface HtmlTemplateService {
to.appendln("</BODY>")
to.appendln("</HTML>")
}
- override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) {
+ override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {
to.appendln("<HTML>")
to.appendln("<HEAD>")
to.appendln("<meta charset=\"UTF-8\">")
@@ -24,7 +24,7 @@ interface HtmlTemplateService {
to.appendln("<title>$title</title>")
}
if (css != null) {
- val cssPath = basePath.resolve(css)
+ val cssPath = basePath.resolve(css).toUnixString()
to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">")
}
to.appendln("</HEAD>")
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
new file mode 100644
index 00000000..09bb2602
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
@@ -0,0 +1,117 @@
+package org.jetbrains.dokka.Formats
+
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
+import org.jetbrains.kotlin.types.KotlinType
+
+class JavaLayoutHtmlPackageListService: PackageListService {
+
+ private fun StringBuilder.appendParam(name: String, value: String) {
+ append(DOKKA_PARAM_PREFIX)
+ append(name)
+ append(":")
+ appendln(value)
+ }
+
+ override fun formatPackageList(module: DocumentationModule): String {
+ val packages = module.members(NodeKind.Package).map { it.name }
+
+ return buildString {
+ appendParam("format", "java-layout-html")
+ appendParam("mode", "kotlin")
+ for (p in packages) {
+ appendln(p)
+ }
+ }
+ }
+
+}
+
+class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ private fun getContainerPath(symbol: DeclarationDescriptor): String? {
+ return when (symbol) {
+ is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/"
+ is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html"
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor =
+ generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first()
+
+ private fun ClassifierDescriptor.nameWithOuter(): String =
+ generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor }
+ .toList().asReversed().joinToString(".") { it.name.asString() }
+
+ private fun getPagePath(symbol: DeclarationDescriptor): String? {
+ return when (symbol) {
+ is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html"
+ is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded()
+ is ClassifierDescriptor -> getContainerPath(symbol) + "#"
+ is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded()
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.signatureForAnchor(): String? {
+
+ fun ReceiverParameterDescriptor.extractReceiverName(): String {
+ var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!!
+ if (receiverClass.isCompanionObject()) {
+ receiverClass = receiverClass.containingDeclaration!!
+ } else if (receiverClass is TypeParameterDescriptor) {
+ val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor
+ if (upperBoundClass != null) {
+ receiverClass = upperBoundClass
+ }
+ }
+
+ return receiverClass.name.asString()
+ }
+
+ fun KotlinType.qualifiedNameForSignature(): String {
+ val desc = constructor.declarationDescriptor
+ return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>"
+ }
+
+ fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) {
+ if (desc.containingDeclaration.isCompanionObject()) {
+ append("Companion.")
+ }
+ desc.extensionReceiverParameter?.let {
+ append("(")
+ append(it.extractReceiverName())
+ append(").")
+ }
+ }
+
+ return when(this) {
+ is EnumEntrySyntheticClassDescriptor -> buildString {
+ append("ENUM_VALUE:")
+ append(name.asString())
+ }
+ is FunctionDescriptor -> buildString {
+ appendReceiverAndCompanion(this@signatureForAnchor)
+ append(name.asString())
+ valueParameters.joinTo(this, prefix = "(", postfix = ")") {
+ it.type.qualifiedNameForSignature()
+ }
+ }
+ is PropertyDescriptor -> buildString {
+ appendReceiverAndCompanion(this@signatureForAnchor)
+ append(name.asString())
+ append(":")
+ append(returnType?.qualifiedNameForSignature())
+ }
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded()
+
+ override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol)
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
new file mode 100644
index 00000000..885cdf6c
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
@@ -0,0 +1,91 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import com.google.inject.Inject
+import kotlinx.html.li
+import kotlinx.html.stream.appendHTML
+import kotlinx.html.ul
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.toType
+import java.io.File
+
+
+class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponent {
+ override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
+ override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
+ override val sampleProcessingService = DefaultSampleProcessingService::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType generatorServiceClass
+ }
+
+ val formatServiceClass = JavaLayoutHtmlFormatService::class
+ val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class
+}
+
+
+class JavaLayoutHtmlFormatService : FormatService {
+ override val extension: String
+ get() = TODO("not implemented")
+
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder {
+ TODO("not implemented")
+ }
+}
+
+class JavaLayoutHtmlFormatOutputBuilder : FormattedOutputBuilder {
+ override fun appendNodes(nodes: Iterable<DocumentationNode>) {
+
+ }
+}
+
+
+class JavaLayoutHtmlFormatNavListBuilder @Inject constructor() : OutlineFormatService {
+ override fun getOutlineFileName(location: Location): File {
+ TODO()
+ }
+
+ override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
+ with(to.appendHTML()) {
+ //a(href = )
+ li {
+ when {
+ node.kind == NodeKind.Package -> appendOutline(location, to, node.members)
+ }
+ }
+ }
+ }
+
+ override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
+ with(to.appendHTML()) {
+ ul { body() }
+ }
+ }
+
+}
+
+class JavaLayoutHtmlFormatGenerator @Inject constructor(
+ private val outlineFormatService: OutlineFormatService
+) : Generator {
+ override fun buildPages(nodes: Iterable<DocumentationNode>) {
+
+ }
+
+ override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
+ for (node in nodes) {
+ if (node.kind == NodeKind.Module) {
+ //outlineFormatService.formatOutline()
+ }
+ }
+ }
+
+ override fun buildSupportFiles() {}
+
+ override fun buildPackageList(nodes: Iterable<DocumentationNode>) {
+
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt
index 6b97bbe0..a948dfa9 100644
--- a/core/src/main/kotlin/Formats/JekyllFormatService.kt
+++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt
@@ -6,12 +6,11 @@ import org.jetbrains.dokka.Utilities.impliedPlatformsName
open class JekyllOutputBuilder(to: StringBuilder,
location: Location,
- locationService: LocationService,
+ generator: NodeLocationAwareGenerator,
languageService: LanguageService,
extension: String,
impliedPlatforms: List<String>)
- : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
-{
+ : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
override fun appendNodes(nodes: Iterable<DocumentationNode>) {
to.appendln("---")
appendFrontMatter(nodes, to)
@@ -26,17 +25,20 @@ open class JekyllOutputBuilder(to: StringBuilder,
}
-open class JekyllFormatService(locationService: LocationService,
- signatureGenerator: LanguageService,
- linkExtension: String,
- impliedPlatforms: List<String>)
-: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) {
+open class JekyllFormatService(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ linkExtension: String,
+ impliedPlatforms: List<String>
+) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) {
- @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "html", impliedPlatforms)
+ @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>
+ ) : this(generator, signatureGenerator, "html", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
index 08349980..a98002d4 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -6,14 +6,14 @@ import org.jetbrains.dokka.Utilities.impliedPlatformsName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
-open class KotlinWebsiteOutputBuilder(to: StringBuilder,
- location: Location,
- locationService: LocationService,
- languageService: LanguageService,
- extension: String,
- impliedPlatforms: List<String>)
- : JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
-{
+open class KotlinWebsiteOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>
+) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
private var needHardLineBreaks = false
private var insideDiv = 0
@@ -70,8 +70,7 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
override fun appendHeader(level: Int, body: () -> Unit) {
if (insideDiv > 0) {
wrapInTag("p", body, newlineAfterClose = true)
- }
- else {
+ } else {
super.appendHeader(level, body)
}
}
@@ -79,8 +78,7 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
override fun appendLine() {
if (insideDiv > 0) {
to.appendln("<br/>")
- }
- else {
+ } else {
super.appendLine()
}
}
@@ -135,13 +133,14 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
to.append("<br/>")
}
+
override fun appendIndentedSoftLineBreak() {
if (needHardLineBreaks) {
to.append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;")
}
}
- private fun identifierClassName(kind: IdentifierKind) = when(kind) {
+ private fun identifierClassName(kind: IdentifierKind) = when (kind) {
IdentifierKind.ParameterName -> "parameterName"
IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
else -> "identifier"
@@ -172,28 +171,29 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder,
}
}
-class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>,
- logger: DokkaLogger)
- : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms)
-{
+class KotlinWebsiteFormatService @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+ logger: DokkaLogger
+) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) {
init {
logger.warn("Format kotlin-website deprecated and will be removed in next release")
}
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
}
-class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder,
- location: Location,
- locationService: LocationService,
- languageService: LanguageService,
- extension: String,
- impliedPlatforms: List<String>)
- : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) {
+class KotlinWebsiteRunnableSamplesOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>
+) : KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
div(to, "sample", markdown = true) {
@@ -207,17 +207,18 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder,
}
}
-class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>,
- logger: DokkaLogger)
- : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) {
+class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+ logger: DokkaLogger
+) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) {
init {
logger.warn("Format kotlin-website-samples deprecated and will be removed in next release")
}
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ KotlinWebsiteRunnableSamplesOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
index 378401f3..16bec5a6 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -4,23 +4,25 @@ import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.Utilities.impliedPlatformsName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
-import java.nio.file.Path
+import java.io.File
-private object EmptyHtmlTemplateService : HtmlTemplateService {
+object EmptyHtmlTemplateService : HtmlTemplateService {
override fun appendFooter(to: StringBuilder) {}
- override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) {}
+ override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {}
}
-open class KotlinWebsiteHtmlOutputBuilder(to: StringBuilder,
- location: Location,
- locationService: LocationService,
- languageService: LanguageService,
- extension: String,
- impliedPlatforms: List<String>)
- : HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, EmptyHtmlTemplateService) {
+open class KotlinWebsiteHtmlOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>,
+ templateService: HtmlTemplateService
+) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) {
private var needHardLineBreaks = false
private var insideDiv = 0
@@ -167,16 +169,25 @@ open class KotlinWebsiteHtmlOutputBuilder(to: StringBuilder,
appendContent(section)
}
}
+
+ override fun appendAsBlockWithPlatforms(platforms: Set<String>, block: () -> Unit) {
+ if (platforms.isNotEmpty())
+ wrap("<div${calculateDataAttributes(platforms)}>", "</div>", block)
+ else
+ block()
+ }
}
-class KotlinWebsiteHtmlFormatService @Inject constructor(locationService: LocationService,
- signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>)
- : HtmlFormatService(locationService, signatureGenerator, EmptyHtmlTemplateService, impliedPlatforms) {
+class KotlinWebsiteHtmlFormatService @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+ templateService: HtmlTemplateService
+) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) {
override fun enumerateSupportFiles(callback: (String, String) -> Unit) {}
override fun createOutputBuilder(to: StringBuilder, location: Location) =
- KotlinWebsiteHtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
}
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
index a7c18a28..4265394f 100644
--- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -21,11 +21,11 @@ private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator()
open class MarkdownOutputBuilder(to: StringBuilder,
location: Location,
- locationService: LocationService,
+ generator: NodeLocationAwareGenerator,
languageService: LanguageService,
extension: String,
impliedPlatforms: List<String>)
- : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
{
private val listStack = ArrayDeque<ListState>()
protected var inTableCell = false
@@ -225,15 +225,15 @@ open class MarkdownOutputBuilder(to: StringBuilder,
}
}
-open class MarkdownFormatService(locationService: LocationService,
+open class MarkdownFormatService(generator: NodeLocationAwareGenerator,
signatureGenerator: LanguageService,
linkExtension: String,
val impliedPlatforms: List<String>)
-: StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) {
- @Inject constructor(locationService: LocationService,
+: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) {
+ @Inject constructor(generator: NodeLocationAwareGenerator,
signatureGenerator: LanguageService,
- @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms)
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms)
override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
- MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+ MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
}
diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt
index 3c31ba57..958e93af 100644
--- a/core/src/main/kotlin/Formats/OutlineService.kt
+++ b/core/src/main/kotlin/Formats/OutlineService.kt
@@ -16,7 +16,7 @@ interface OutlineFormatService {
for (node in nodes) {
appendOutlineHeader(location, node, to)
if (node.members.any()) {
- val sortedMembers = node.members.sortedBy { it.name }
+ val sortedMembers = node.members.sortedBy { it.name.toLowerCase() }
appendOutlineLevel(to) {
appendOutline(location, to, sortedMembers)
}
diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt
index e7f4e952..7b68098e 100644
--- a/core/src/main/kotlin/Formats/PackageListService.kt
+++ b/core/src/main/kotlin/Formats/PackageListService.kt
@@ -7,10 +7,10 @@ interface PackageListService {
fun formatPackageList(module: DocumentationModule): String
}
-class DefaultPackageListService @Inject constructor(locationService: FileLocationService,
- val formatService: FormatService) : PackageListService {
-
- val locationService: FileLocationService = locationService.withExtension(formatService.linkExtension)
+class DefaultPackageListService @Inject constructor(
+ val generator: NodeLocationAwareGenerator,
+ val formatService: FormatService
+) : PackageListService {
override fun formatPackageList(module: DocumentationModule): String {
val packages = mutableSetOf<String>()
@@ -26,7 +26,7 @@ class DefaultPackageListService @Inject constructor(locationService: FileLocatio
}
NodeKind.Signature -> {
if (relocated)
- nonStandardLocations[node.name] = locationService.relativePathToLocation(module, node.owner!!)
+ nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!)
}
NodeKind.ExternalClass -> {
node.members.forEach { visit(it, relocated = true) }
diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt
index f3d638c6..dd67ac97 100644
--- a/core/src/main/kotlin/Formats/StandardFormats.kt
+++ b/core/src/main/kotlin/Formats/StandardFormats.kt
@@ -1,41 +1,36 @@
package org.jetbrains.dokka.Formats
+import com.google.inject.Binder
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Kotlin.KotlinAsJavaDescriptorSignatureProvider
-import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService
-import org.jetbrains.dokka.Samples.SampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
import kotlin.reflect.KClass
-abstract class KotlinFormatDescriptorBase : FormatDescriptor {
- override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
- override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
-
+abstract class KotlinFormatDescriptorBase
+ : FileGeneratorBasedFormatDescriptor(),
+ DefaultAnalysisComponent,
+ DefaultAnalysisComponentServices by KotlinAsKotlin {
override val generatorServiceClass = FileGenerator::class
override val outlineServiceClass: KClass<out OutlineFormatService>? = null
- override val sampleProcessingService: KClass<out SampleProcessingService> = DefaultSampleProcessingService::class
override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class
- override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class
-}
-
-class HtmlFormatDescriptor : KotlinFormatDescriptorBase() {
- override val formatServiceClass = HtmlFormatService::class
- override val outlineServiceClass = HtmlFormatService::class
}
-class HtmlAsJavaFormatDescriptor : FormatDescriptor {
+abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent {
override val formatServiceClass = HtmlFormatService::class
override val outlineServiceClass = HtmlFormatService::class
override val generatorServiceClass = FileGenerator::class
- override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
- override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
- override val sampleProcessingService: KClass<out SampleProcessingService> = DefaultSampleProcessingService::class
- override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class
- override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::class
+ override val packageListServiceClass = DefaultPackageListService::class
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ super.configureOutput(binder)
+ bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") }
+ }
}
+class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin
+
+class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava
+
class KotlinWebsiteFormatDescriptor : KotlinFormatDescriptorBase() {
override val formatServiceClass = KotlinWebsiteFormatService::class
override val outlineServiceClass = YamlOutlineService::class
@@ -51,6 +46,11 @@ class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() {
override val formatServiceClass = KotlinWebsiteHtmlFormatService::class
override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class
override val outlineServiceClass = YamlOutlineService::class
+
+ override fun configureOutput(binder: Binder) = with(binder) {
+ super.configureOutput(binder)
+ bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService)
+ }
}
class JekyllFormatDescriptor : KotlinFormatDescriptorBase() {
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 5167a102..53172563 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -7,11 +7,13 @@ data class FormatLink(val text: String, val href: String)
abstract class StructuredOutputBuilder(val to: StringBuilder,
val location: Location,
- val locationService: LocationService,
+ val generator: NodeLocationAwareGenerator,
val languageService: LanguageService,
val extension: String,
val impliedPlatforms: List<String>) : FormattedOutputBuilder {
+ protected fun DocumentationNode.location() = generator.location(this)
+
protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
to.append(prefix)
body()
@@ -81,6 +83,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ open fun appendAsBlockWithPlatforms(platforms: Set<String>, block: () -> Unit) {
+ block()
+ }
+
open fun appendSymbol(text: String) {
appendText(text)
}
@@ -190,30 +196,28 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- open fun link(from: DocumentationNode,
- to: DocumentationNode,
- name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink = link(from, to, extension, name)
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink = link(from, to, extension, name)
- open fun link(from: DocumentationNode,
- to: DocumentationNode,
- extension: String,
- name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
- if (to.owner?.kind == NodeKind.GroupNode)
- return link(from, to.owner!!, extension, name)
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ extension: String,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink =
+ FormatLink(name(to), from.location().relativePathTo(to.location()))
- if (from.owner?.kind == NodeKind.GroupNode)
- return link(from.owner!!, to, extension, name)
-
- return FormatLink(name(to), locationService.relativePathToLocation(from, to))
- }
fun locationHref(from: Location, to: DocumentationNode): String {
val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to
if (topLevelPage != null) {
val signature = to.detailOrNull(NodeKind.Signature)
- return from.relativePathTo(locationService.location(topLevelPage), signature?.name ?: to.name)
+ return from.relativePathTo(topLevelPage.location(), signature?.name ?: to.name)
}
- return from.relativePathTo(locationService.location(to))
+ return from.relativePathTo(to.location())
}
private fun DocumentationNode.isModuleOrPackage(): Boolean =
@@ -296,7 +300,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) {
- val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content }
+ val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node ->
+ when (node.kind) {
+ NodeKind.GroupNode -> node.origins.first().content
+ else -> node.content
+ }
+ }
if (breakdownBySummary.size == 1) {
formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode)
@@ -314,29 +323,90 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) {
for ((index, item) in items.withIndex()) {
if (index > 0) appendLine()
- val rendered = languageService.render(item)
- item.detailOrNull(NodeKind.Signature)?.let {
- if (item.kind !in NodeKind.classLike || !isSingleNode)
- appendAnchor(it.name)
- }
- appendAsSignature(rendered) {
- appendCode { appendContent(rendered) }
- item.appendSourceLink()
+
+ if (item.kind == NodeKind.GroupNode) {
+ renderGroupNode(item, isSingleNode)
+ } else {
+ renderSimpleNode(item, isSingleNode)
}
- item.appendOverrides()
- item.appendDeprecation()
- item.appendPlatforms()
+
}
// All items have exactly the same documentation, so we can use any item to render it
val item = items.first()
+ // TODO: remove this block cause there is no one node with OverloadGroupNote detail
item.details(NodeKind.OverloadGroupNote).forEach {
appendContent(it.content)
}
- appendContent(item.content.summary)
+ if (item.kind == NodeKind.GroupNode) {
+ for (origin in item.origins) {
+ if (origin.content.isEmpty()) continue
+ appendParagraph {
+ appendStrong { to.append("Platform and version requirements:") }
+ to.append(" " + origin.actualPlatforms)
+ appendContent(origin.summary)
+ }
+ }
+ } else {
+ if (!item.content.isEmpty()) {
+ appendStrong { to.append("Platform and version requirements:") }
+ to.append(" " + item.actualPlatforms)
+ appendContent(item.content.summary)
+ }
+ }
+
item.appendDescription()
}
+
+ fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean) {
+ // TODO: use summarizesignatures
+ val rendered = languageService.render(item)
+ item.detailOrNull(NodeKind.Signature)?.let {
+ if (item.kind !in NodeKind.classLike || !isSingleNode)
+ appendAnchor(it.name)
+ }
+ appendAsSignature(rendered) {
+ appendCode { appendContent(rendered) }
+ item.appendSourceLink()
+ }
+ item.appendOverrides()
+ item.appendDeprecation()
+ item.appendPlatforms()
+ }
+
+ fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean) {
+ // TODO: use summarizesignatures
+ val groupBySignature = item.origins.groupBy {
+ languageService.render(it)
+ }
+
+ for ((sign, nodes) in groupBySignature) {
+ val first = nodes.first()
+ first.detailOrNull(NodeKind.Signature)?.let {
+ if (item.kind !in NodeKind.classLike || !isSingleNode)
+ appendAnchor(it.name)
+ }
+
+ appendAsSignature(sign) {
+ appendCode { appendContent(sign) }
+ }
+ first.appendOverrides()
+ first.appendDeprecation()
+
+
+ appendParagraph {
+ appendStrong { to.append("Platform and version requirements:") }
+ to.append(" " + nodes
+ .flatMap { it.actualPlatforms }
+ .distinct()
+ .joinToString()
+ )
+ }
+
+ }
+ }
+
private fun DocumentationNode.appendSourceLink() {
val sourceUrl = details(NodeKind.SourceUrl).firstOrNull()
if (sourceUrl != null) {
@@ -349,7 +419,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
overrides.forEach {
appendParagraph {
to.append("Overrides ")
- val location = locationService.relativePathToLocation(this, it)
+ val location = location().relativePathTo(it.location())
+
appendLink(FormatLink(it.owner!!.name + "." + it.name, location))
}
}
@@ -360,30 +431,30 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull()
val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull()
appendLine()
- if (deprecationValue != null) {
- appendStrong { to.append("Deprecated:") }
- appendText(" " + deprecationValue.name.removeSurrounding("\""))
- appendLine()
- appendLine()
- } else if (deprecation?.content != Content.Empty) {
- appendStrong { to.append("Deprecated:") }
- to.append(" ")
- appendContent(deprecation!!.content)
- } else {
- appendStrong { to.append("Deprecated") }
- appendLine()
- appendLine()
+ when {
+ deprecationValue != null -> {
+ appendStrong { to.append("Deprecated:") }
+ appendText(" " + deprecationValue.name.removeSurrounding("\""))
+ appendLine()
+ appendLine()
+ }
+ deprecation?.content != Content.Empty -> {
+ appendStrong { to.append("Deprecated:") }
+ to.append(" ")
+ appendContent(deprecation!!.content)
+ }
+ else -> {
+ appendStrong { to.append("Deprecated") }
+ appendLine()
+ appendLine()
+ }
}
}
}
private fun DocumentationNode.appendPlatforms() {
- val platforms = if (isModuleOrPackage())
- platformsToShow.toSet() + platformsOfItems(members)
- else
- platformsToShow
-
- if(platforms.isEmpty()) return
+ val platforms = actualPlatforms
+ if (platforms.isEmpty()) return
appendParagraph {
appendStrong { to.append("Platform and version requirements:") }
@@ -391,16 +462,39 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ val DocumentationNode.actualPlatforms: Collection<String>
+ get() = if (isModuleOrPackage())
+ platformsToShow.toSet() + platformsOfItems(members)
+ else
+ platformsToShow
+
+
+ protected fun mergeVersions(otherKotlinVersion: String, kotlinVersions: List<String>): String {
+ val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct()
+
+ val minVersion = allKotlinVersions.min()!!
+ val resultVersion: String = when {
+ allKotlinVersions.size == 1 -> allKotlinVersions.single()
+ minVersion.endsWith("+") -> minVersion
+ else -> "$minVersion+"
+ }
+
+ return resultVersion
+ }
+
protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> {
val platforms = items.asSequence().map {
when (it.kind) {
- NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members)
+ NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members)
+ NodeKind.GroupNode -> platformsOfItems(it.origins)
else -> it.platformsToShow.toSet()
}
}
fun String.isKotlinVersion() = this.startsWith("Kotlin")
+ if (platforms.count() == 0) return emptySet()
+
// Calculating common platforms for items
return platforms.reduce { result, platformsOfItem ->
val otherKotlinVersion = result.find { it.isKotlinVersion() }
@@ -408,19 +502,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
// When no Kotlin version specified, it means that version is 1.0
if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
- val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct()
+ result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions)
+ } else {
+ result.intersect(platformsOfItem)
+ }
+ }
+ }
- val minVersion = allKotlinVersions.min()!!
- val resultVersion = when {
- allKotlinVersions.size == 1 -> allKotlinVersions.single()
- minVersion.endsWith("+") -> minVersion
- else -> minVersion + "+"
- }
+ protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+ val platforms = items.asSequence().map {
+ when (it.kind) {
+ NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+ else -> it.platformsToShow.toSet()
+ }
+ }
+
+ fun String.isKotlinVersion() = this.startsWith("Kotlin")
+
+ if (platforms.count() == 0) return emptySet()
+
+ // Calculating common platforms for items
+ return platforms.reduce { result, platformsOfItem ->
+ val otherKotlinVersion = result.find { it.isKotlinVersion() }
+ val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
- result.intersect(otherPlatforms) + resultVersion
+ // When no Kotlin version specified, it means that version is 1.0
+ if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
+ result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions)
} else {
- result.intersect(platformsOfItem)
+ result.union(otherPlatforms)
}
+ }.let {
+ if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it
}
}
@@ -456,6 +569,15 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) :
+ PageBuilder(listOf(node), noHeader) {
+
+ override fun build() {
+ super.build()
+ SectionsBuilder(node).build()
+ }
+ }
+
inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) {
override fun build() {
@@ -466,39 +588,40 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendLine()
appendHeader { appendText(node.name) }
- fun DocumentationNode.priority(): Int = when (kind) {
- NodeKind.TypeAlias -> 1
- NodeKind.Class -> 2
- else -> 3
- }
+ renderGroupNode(node, true)
- for (member in node.members.sortedBy(DocumentationNode::priority)) {
+ for (origin in node.origins) {
+ if (origin.content.isEmpty()) continue
+ appendStrong { to.append("Platform and version requirements:") }
+ to.append(" " + origin.actualPlatforms)
+ appendContent(origin.content.summary)
+ }
- appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
- formatSubNodeOfGroup(member)
- }
+ SectionsBuilder(node).build()
+ }
+ }
+ private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+ val platforms = items.flatMapTo(mutableSetOf<String>()) {
+ when (it.kind) {
+ NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+ else -> it.platforms
}
}
- fun formatSubNodeOfGroup(member: DocumentationNode) {
- SingleNodePageBuilder(member, true).build()
- }
+ return platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it }
}
- inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
- : PageBuilder(listOf(node), noHeader) {
+ inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) {
override fun build() {
- super.build()
-
if (node.kind == NodeKind.ExternalClass) {
appendSection("Extensions for ${node.name}", node.members)
return
}
fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> {
- return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) }
+ return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) }
}
fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> {
@@ -506,20 +629,19 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true)
- appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
+ appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike /*&& it.kind != NodeKind.TypeAlias*/ && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass))
appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception))
- appendSection("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias))
appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass))
- appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true)
- appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true)
- appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true)
+ appendSection("Enum Values", node.membersOrGroupMembers(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true)
+ appendSection("Constructors", node.membersOrGroupMembers(NodeKind.Constructor), omitSamePlatforms = true)
+ appendSection("Properties", node.membersOrGroupMembers(NodeKind.Property), omitSamePlatforms = true)
appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property))
- appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true)
+ appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true)
appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function))
- appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
+ appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property))
- appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
+ appendSection("Companion Object Functions", node.membersOrGroupMembers(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function))
appendSection("Other members", node.members.filter {
it.kind !in setOf(
@@ -554,7 +676,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
if (node.kind == NodeKind.Module) {
appendHeader(3) { to.append("Index") }
node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes ->
- appendLink(link(node, allTypes, { "All Types" }))
+ appendLink(link(node, allTypes) { "All Types" })
}
}
}
@@ -567,7 +689,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendHeader(3) { appendText(caption) }
- val children = if (sortMembers) members.sortedBy { it.name } else members
+ val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members
val membersMap = children.groupBy { link(node, it) }
@@ -590,11 +712,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
appendTableCell {
- val breakdownBySummary = members.groupBy { it.summary }
- for ((summary, items) in breakdownBySummary) {
- appendSummarySignatures(items)
- appendContent(summary)
- }
+ appendSummarySignatures(members, platformsBasedOnMembers, omitSamePlatforms)
}
}
}
@@ -603,6 +721,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
+ if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) {
+ return unionPlatformsOfItems(items)
+ }
+
val platforms = platformsOfItems(items)
if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
return platforms
@@ -610,27 +732,62 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
return emptySet()
}
- private fun appendSummarySignatures(items: List<DocumentationNode>) {
- val summarySignature = languageService.summarizeSignatures(items)
- if (summarySignature != null) {
- appendAsSignature(summarySignature) {
- summarySignature.appendSignature()
+ private fun appendSummarySignatures(items: List<DocumentationNode>, platformsBasedOnMembers: Boolean, omitSamePlatforms: Boolean) {
+ val groupBySummary = items.groupBy { it.summary }
+
+ for ((summary, node) in groupBySummary) {
+ val nodesToAppend = if (node.all { it.kind == NodeKind.GroupNode }) {
+ node.flatMap { it.origins }
+ } else {
+ node
}
- return
- }
- val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
- renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
- appendAsSignature(it) {
- it.appendSignature()
+
+ val summarySignature = languageService.summarizeSignatures(nodesToAppend)
+ if (summarySignature != null) {
+ appendSignatures(summarySignature, items, platformsBasedOnMembers, omitSamePlatforms)
+ } else {
+ val groupBySignature = nodesToAppend.groupBy {
+ languageService.render(it, RenderMode.SUMMARY)
+ }
+ for ((sign, members) in groupBySignature) {
+ appendSignatures(sign, members, platformsBasedOnMembers, omitSamePlatforms)
+ }
+ }
+
+ val platforms = platformsOfItems(node)
+ appendAsBlockWithPlatforms(platforms) {
+ appendContent(summary)
+ appendSoftLineBreak()
}
- appendLine()
}
- appendAsSignature(renderedSignatures.last()) {
- renderedSignatures.last().appendSignature()
+ }
+
+ private fun appendSignatures(signature: ContentNode, items: List<DocumentationNode>, platformsBasedOnMembers: Boolean, omitSamePlatforms: Boolean) {
+ val elementPlatforms = platformsOfItems(items, omitSamePlatforms)
+ val platforms = if (platformsBasedOnMembers)
+ items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+ else
+ elementPlatforms
+
+ appendAsBlockWithPlatforms(platforms) {
+ appendPlatforms(platforms)
+ appendAsSignature(signature) {
+ signature.appendSignature()
+ }
+ appendSoftLineBreak()
}
}
}
+ private fun DocumentationNode.isClassLikeGroupNode(): Boolean {
+ if (kind != NodeKind.GroupNode) {
+ return false
+ }
+
+ return origins.all { it.kind in NodeKind.classLike }
+ }
+
+
inner class AllTypesNodeBuilder(val node: DocumentationNode)
: PageBuilder(listOf(node)) {
@@ -654,7 +811,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
appendTableCell {
- appendContent(type.summary)
+ val summary = if (type.isClassLikeGroupNode()) {
+ type.origins.first().summary
+ } else {
+ type.summary
+ }
+
+ appendContent(summary)
}
}
}
@@ -674,9 +837,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
-abstract class StructuredFormatService(locationService: LocationService,
+abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator,
val languageService: LanguageService,
override val extension: String,
override final val linkExtension: String = extension) : FormatService {
- val locationService: LocationService = locationService.withExtension(linkExtension)
+
}
diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt
index 7968824c..c36f98eb 100644
--- a/core/src/main/kotlin/Formats/YamlOutlineService.kt
+++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt
@@ -3,15 +3,17 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import java.io.File
-class YamlOutlineService @Inject constructor(val locationService: LocationService,
- val languageService: LanguageService) : OutlineFormatService {
+class YamlOutlineService @Inject constructor(
+ val generator: NodeLocationAwareGenerator,
+ val languageService: LanguageService
+) : OutlineFormatService {
override fun getOutlineFileName(location: Location): File = File("${location.path}.yml")
var outlineLevel = 0
override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
val indent = " ".repeat(outlineLevel)
to.appendln("$indent- title: ${languageService.renderName(node)}")
- to.appendln("$indent url: ${locationService.location(node).path}")
+ to.appendln("$indent url: ${generator.location(node).path}")
}
override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
diff --git a/core/src/main/kotlin/Generation/ConsoleGenerator.kt b/core/src/main/kotlin/Generation/ConsoleGenerator.kt
deleted file mode 100644
index 301f86b9..00000000
--- a/core/src/main/kotlin/Generation/ConsoleGenerator.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.jetbrains.dokka
-
-class ConsoleGenerator(val signatureGenerator: LanguageService, val locationService: LocationService) {
- val IndentStep = " "
-
- fun generate(node: DocumentationNode, indent: String = "") {
- println("@${locationService.location(node).path}")
- generateHeader(node, indent)
- //generateDetails(node, indent)
- generateMembers(node, indent)
- generateLinks(node, indent)
- }
-
- fun generateHeader(node: DocumentationNode, indent: String = "") {
- println(indent + signatureGenerator.render(node))
- val docString = node.content.toString()
- if (!docString.isEmpty())
- println("$indent\"${docString.replace("\n", "\n$indent")}\"")
- println()
- }
-
- fun generateMembers(node: DocumentationNode, indent: String = "") {
- val items = node.members.sortedBy { it.name }
- for (child in items)
- generate(child, indent + IndentStep)
- }
-
- fun generateDetails(node: DocumentationNode, indent: String = "") {
- val items = node.details
- for (child in items)
- generate(child, indent + " ")
- }
-
- fun generateLinks(node: DocumentationNode, indent: String = "") {
- val items = node.links
- if (items.isEmpty())
- return
- println("$indent Links")
- for (child in items)
- generate(child, indent + " ")
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/DocumentationMerger.kt b/core/src/main/kotlin/Generation/DocumentationMerger.kt
new file mode 100644
index 00000000..0394dce2
--- /dev/null
+++ b/core/src/main/kotlin/Generation/DocumentationMerger.kt
@@ -0,0 +1,180 @@
+package org.jetbrains.dokka.Generation
+
+import org.jetbrains.dokka.*
+
+class DocumentationMerger(
+ private val documentationModules: List<DocumentationModule>
+) {
+ private val producedNodeRefGraph: NodeReferenceGraph = NodeReferenceGraph()
+ private val signatureMap: Map<DocumentationNode, String>
+ private val oldToNewNodeMap: MutableMap<DocumentationNode, DocumentationNode> = mutableMapOf()
+
+ init {
+ if (documentationModules.groupBy { it.name }.size > 1) {
+ throw IllegalArgumentException("Modules should have similar names")
+ }
+
+ signatureMap = documentationModules
+ .flatMap { it.nodeRefGraph.nodeMapView.entries }
+ .associate { (k, v) -> v to k }
+
+
+ documentationModules.map { it.nodeRefGraph }
+ .flatMap { it.references }
+ .forEach { producedNodeRefGraph.addReference(it) }
+ }
+
+ private fun mergePackageReferences(
+ from: DocumentationNode,
+ packages: List<DocumentationReference>
+ ): List<DocumentationReference> {
+ val packagesByName = packages
+ .map { it.to }
+ .groupBy { it.name }
+
+ val resultReferences = mutableListOf<DocumentationReference>()
+ for ((name, listOfPackages) in packagesByName) {
+ val producedPackage = mergePackagesWithEqualNames(name, from, listOfPackages)
+ updatePendingReferences()
+
+ resultReferences.add(
+ DocumentationReference(from, producedPackage, RefKind.Member)
+ )
+ }
+
+ return resultReferences
+ }
+
+ private fun mergePackagesWithEqualNames(
+ name: String,
+ from: DocumentationNode,
+ packages: List<DocumentationNode>
+ ): DocumentationNode {
+ val mergedPackage = DocumentationNode(name, Content.Empty, NodeKind.Package)
+
+ for (contentToAppend in packages.map { it.content }.distinct()) {
+ mergedPackage.updateContent {
+ for (otherChild in contentToAppend.children) {
+ children.add(otherChild)
+ }
+ }
+ }
+
+ for (node in packages) {
+ oldToNewNodeMap[node] = mergedPackage
+ }
+
+ val references = packages.flatMap { it.allReferences() }
+ val mergedReferences = mergeReferences(mergedPackage, references)
+ for (ref in mergedReferences) {
+ if (ref.kind == RefKind.Owner) {
+ continue
+ }
+ mergedPackage.addReference(ref)
+ }
+
+ from.append(mergedPackage, RefKind.Member)
+
+ return mergedPackage
+ }
+
+ private fun mergeMemberReferences(
+ from: DocumentationNode,
+ refs: List<DocumentationReference>
+ ): List<DocumentationReference> {
+ val membersBySignature: Map<String, List<DocumentationNode>> = refs.map { it.to }
+ .groupBy { signatureMap[it]!! }
+
+ val mergedMembers: MutableList<DocumentationReference> = mutableListOf()
+ for ((signature, members) in membersBySignature) {
+ val newNode = mergeMembersWithEqualSignature(signature, members)
+
+ producedNodeRefGraph.register(signature, newNode)
+ updatePendingReferences()
+ from.append(newNode, RefKind.Member)
+
+ mergedMembers.add(DocumentationReference(from, newNode, RefKind.Member))
+ }
+
+ return mergedMembers
+ }
+
+ private fun mergeMembersWithEqualSignature(
+ signature: String,
+ nodes: List<DocumentationNode>
+ ): DocumentationNode {
+ val singleNode = nodes.singleOrNull()
+ if (singleNode != null) {
+ singleNode.dropReferences { it.kind == RefKind.Owner }
+ return singleNode
+ }
+
+ val groupNode = DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode)
+ groupNode.appendTextNode(signature, NodeKind.Signature, RefKind.Detail)
+
+ for (node in nodes) {
+ node.dropReferences { it.kind == RefKind.Owner }
+ groupNode.append(node, RefKind.Origin)
+
+ oldToNewNodeMap[node] = groupNode
+ }
+
+ // if nodes are classes, nested members should be also merged and
+ // inserted at the same level with class
+ if (nodes.all { it.kind == NodeKind.Class }) {
+ val members = nodes.flatMap { it.allReferences() }.filter { it.kind == RefKind.Member }
+ val mergedMembers = mergeMemberReferences(groupNode, members)
+
+ for (ref in mergedMembers) {
+ if (ref.kind == RefKind.Owner) {
+ continue
+ }
+
+ groupNode.append(ref.to, RefKind.Member)
+ }
+ }
+
+ return groupNode
+ }
+
+
+ private fun mergeReferences(
+ from: DocumentationNode,
+ refs: List<DocumentationReference>
+ ): List<DocumentationReference> {
+ val (refsToPackages, otherRefs) = refs.partition { it.to.kind == NodeKind.Package }
+ val mergedPackages = mergePackageReferences(from, refsToPackages)
+
+ val (refsToMembers, refsNotToMembers) = otherRefs.partition { it.kind == RefKind.Member }
+ val mergedMembers = mergeMemberReferences(from, refsToMembers)
+
+ return mergedPackages + mergedMembers + refsNotToMembers
+ }
+
+ fun merge(): DocumentationModule {
+ val mergedDocumentationModule = DocumentationModule(
+ name = documentationModules.first().name,
+ nodeRefGraph = producedNodeRefGraph
+ )
+
+ val refs = documentationModules.flatMap {
+ it.allReferences()
+ }
+ mergeReferences(mergedDocumentationModule, refs)
+
+ return mergedDocumentationModule
+ }
+
+ private fun updatePendingReferences() {
+ for (ref in producedNodeRefGraph.references) {
+ ref.lazyNodeFrom.update()
+ ref.lazyNodeTo.update()
+ }
+ }
+
+ private fun NodeResolver.update() {
+ if (this is NodeResolver.Exact && exactNode in oldToNewNodeMap) {
+ exactNode = oldToNewNodeMap[exactNode]!!
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 09e5cedf..1193657e 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -7,9 +7,10 @@ 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.DokkaConfiguration.SourceRoot
+import org.jetbrains.dokka.Generation.DocumentationMerger
import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
import org.jetbrains.dokka.Utilities.DokkaOutputModule
+import org.jetbrains.dokka.Utilities.DokkaRunModule
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
@@ -24,57 +25,64 @@ import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import kotlin.system.measureTimeMillis
-class DokkaGenerator(val logger: DokkaLogger,
- val classpath: List<String>,
- val sources: List<SourceRoot>,
- val samples: List<String>,
- val includes: List<String>,
- val moduleName: String,
- val options: DocumentationOptions) {
+class DokkaGenerator(val dokkaConfiguration: DokkaConfiguration,
+ val logger: DokkaLogger) {
- private val documentationModule = DocumentationModule(moduleName)
+ private val documentationModules: MutableList<DocumentationModule> = mutableListOf()
+ private val globalInjector = Guice.createInjector(DokkaRunModule(dokkaConfiguration))
- fun generate() {
- val sourcesGroupedByPlatform = sources.groupBy { it.platforms.firstOrNull() }
- for ((platform, roots) in sourcesGroupedByPlatform) {
- appendSourceModule(platform, roots)
+
+ fun generate() = with(dokkaConfiguration) {
+
+
+ for (pass in passesConfigurations) {
+ val documentationModule = DocumentationModule(pass.moduleName)
+ appendSourceModule(pass, documentationModule)
+ documentationModules.add(documentationModule)
}
- documentationModule.prepareForGeneration(options)
+
+ val totalDocumentationModule = DocumentationMerger(documentationModules).merge()
+ totalDocumentationModule.prepareForGeneration(dokkaConfiguration)
val timeBuild = measureTimeMillis {
logger.info("Generating pages... ")
- val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger))
- outputInjector.getInstance(Generator::class.java).buildAll(documentationModule)
+ val outputInjector = globalInjector.createChildInjector(DokkaOutputModule(dokkaConfiguration, logger))
+ val instance = outputInjector.getInstance(Generator::class.java)
+ instance.buildAll(totalDocumentationModule)
}
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)
+ private fun appendSourceModule(
+ passConfiguration: DokkaConfiguration.PassConfiguration,
+ documentationModule: DocumentationModule
+ ) = with(passConfiguration) {
+
+ val sourcePaths = passConfiguration.sourceRoots.map { it.path }
+ val environment = createAnalysisEnvironment(sourcePaths, passConfiguration)
logger.info("Module: $moduleName")
- logger.info("Output: ${File(options.outputDir)}")
+ logger.info("Output: ${File(dokkaConfiguration.outputDir)}")
logger.info("Sources: ${sourcePaths.joinToString()}")
logger.info("Classpath: ${environment.classpath.joinToString()}")
logger.info("Analysing sources and libraries... ")
val startAnalyse = System.currentTimeMillis()
- val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty()
+ val defaultPlatformAsList = passConfiguration.targets
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?.platforms ?: defaultPlatformAsList
+ return /*sourceRoot?.platforms ?: */defaultPlatformAsList
}
}
- val injector = Guice.createInjector(
- DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger))
+ val injector = globalInjector.createChildInjector(
+ DokkaAnalysisModule(environment, dokkaConfiguration, defaultPlatformsProvider, documentationModule.nodeRefGraph, passConfiguration, logger))
- buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes)
+ buildDocumentationModule(injector, documentationModule, { isNotSample(it, passConfiguration.samples) }, includes)
val timeAnalyse = System.currentTimeMillis() - startAnalyse
logger.info("done in ${timeAnalyse / 1000} secs")
@@ -82,26 +90,31 @@ class DokkaGenerator(val logger: DokkaLogger,
Disposer.dispose(environment)
}
- fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment {
- val environment = AnalysisEnvironment(DokkaMessageCollector(logger))
+ fun createAnalysisEnvironment(
+ sourcePaths: List<String>,
+ passConfiguration: DokkaConfiguration.PassConfiguration
+ ): AnalysisEnvironment {
+ val environment = AnalysisEnvironment(DokkaMessageCollector(logger), passConfiguration.analysisPlatform)
environment.apply {
- addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
+ if (analysisPlatform == Platform.jvm) {
+ addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
+ }
// addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath())
- for (element in this@DokkaGenerator.classpath) {
+ for (element in passConfiguration.classpath) {
addClasspath(File(element))
}
addSources(sourcePaths)
- addSources(this@DokkaGenerator.samples)
+ addSources(passConfiguration.samples)
- loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
+ loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion)
}
return environment
}
- fun isNotSample(file: PsiFile): Boolean {
+ private fun isNotSample(file: PsiFile, samples: List<String>): Boolean {
val sourceFile = File(file.virtualFile!!.path)
return samples.none { sample ->
val canonicalSample = File(sample).canonicalPath
@@ -140,9 +153,7 @@ fun buildDocumentationModule(injector: Injector,
val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java)
analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles)
- val fragments = fragmentFiles
- .map { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) }
- .filterNotNull()
+ val fragments = fragmentFiles.mapNotNull { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) }
.distinct()
val packageDocs = injector.getInstance(PackageDocs::class.java)
@@ -157,9 +168,13 @@ fun buildDocumentationModule(injector: Injector,
}
}
+ parseJavaPackageDocs(packageDocs, coreEnvironment)
+
with(injector.getInstance(DocumentationBuilder::class.java)) {
documentationModule.appendFragments(fragments, packageDocs.packageContent,
injector.getInstance(PackageDocumentationBuilder::class.java))
+
+ propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
}
val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
@@ -168,6 +183,18 @@ fun buildDocumentationModule(injector: Injector,
}
}
+fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) {
+ val contentRoots = coreEnvironment.configuration.get(JVMConfigurationKeys.CONTENT_ROOTS)
+ ?.filterIsInstance<JavaSourceRoot>()
+ ?.map { it.file }
+ ?: listOf()
+ contentRoots.forEach { root ->
+ root.walkTopDown().filter { it.name == "overview.html" }.forEach {
+ packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", "."))
+ }
+ }
+}
+
fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> {
val sourceRoots = configuration.get(JVMConfigurationKeys.CONTENT_ROOTS)
@@ -189,4 +216,4 @@ fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> {
}
}
return result
-}
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt
index e055c537..eb6800b3 100644
--- a/core/src/main/kotlin/Generation/FileGenerator.kt
+++ b/core/src/main/kotlin/Generation/FileGenerator.kt
@@ -1,28 +1,40 @@
package org.jetbrains.dokka
import com.google.inject.Inject
+import com.google.inject.name.Named
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStreamWriter
-class FileGenerator @Inject constructor(val locationService: FileLocationService) : Generator {
+class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator {
@set:Inject(optional = true) var outlineService: OutlineFormatService? = null
@set:Inject(optional = true) lateinit var formatService: FormatService
- @set:Inject(optional = true) lateinit var options: DocumentationOptions
+ @set:Inject(optional = true) lateinit var dokkaConfiguration: DokkaConfiguration
@set:Inject(optional = true) var packageListService: PackageListService? = null
+ override fun location(node: DocumentationNode): FileLocation {
+ return FileLocation(fileForNode(node, formatService.linkExtension))
+ }
+
+ private fun fileForNode(node: DocumentationNode, extension: String = ""): File {
+ return File(root, relativePathToNode(node)).appendExtension(extension)
+ }
+
+ fun locationWithoutExtension(node: DocumentationNode): FileLocation {
+ return FileLocation(fileForNode(node))
+ }
+
override fun buildPages(nodes: Iterable<DocumentationNode>) {
- val specificLocationService = locationService.withExtension(formatService.extension)
- for ((location, items) in nodes.groupBy { specificLocationService.location(it) }) {
- val file = location.file
+ for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) {
+
file.parentFile?.mkdirsOrFail()
try {
FileOutputStream(file).use {
OutputStreamWriter(it, Charsets.UTF_8).use {
- it.write(formatService.format(location, items))
+ it.write(formatService.format(location(items.first()), items))
}
}
} catch (e: Throwable) {
@@ -34,7 +46,7 @@ class FileGenerator @Inject constructor(val locationService: FileLocationService
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
val outlineService = this.outlineService ?: return
- for ((location, items) in nodes.groupBy { locationService.location(it) }) {
+ for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) {
val file = outlineService.getOutlineFileName(location)
file.parentFile?.mkdirsOrFail()
FileOutputStream(file).use {
@@ -47,7 +59,7 @@ class FileGenerator @Inject constructor(val locationService: FileLocationService
override fun buildSupportFiles() {
formatService.enumerateSupportFiles { resource, targetPath ->
- FileOutputStream(locationService.location(listOf(targetPath), false).file).use {
+ FileOutputStream(File(root, relativePathToNode(listOf(targetPath), false))).use {
javaClass.getResourceAsStream(resource).copyTo(it)
}
}
@@ -58,10 +70,10 @@ class FileGenerator @Inject constructor(val locationService: FileLocationService
for (module in nodes) {
- val moduleRoot = locationService.location(module).file.parentFile
+ val moduleRoot = location(module).file.parentFile
val packageListFile = File(moduleRoot, "package-list")
- packageListFile.writeText("\$dokka.format:${options.outputFormat}\n" +
+ packageListFile.writeText("\$dokka.format:${dokkaConfiguration.format}\n" +
packageListService!!.formatPackageList(module as DocumentationModule))
}
diff --git a/core/src/main/kotlin/Generation/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt
index 76a5f350..23286e29 100644
--- a/core/src/main/kotlin/Generation/Generator.kt
+++ b/core/src/main/kotlin/Generation/Generator.kt
@@ -1,5 +1,7 @@
package org.jetbrains.dokka
+import java.io.File
+
interface Generator {
fun buildPages(nodes: Iterable<DocumentationNode>)
fun buildOutlines(nodes: Iterable<DocumentationNode>)
@@ -19,3 +21,9 @@ fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)
fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node))
fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node))
+
+
+interface NodeLocationAwareGenerator: Generator {
+ fun location(node: DocumentationNode): Location
+ val root: File
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 34d4154e..2aa0e0d7 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -18,14 +18,11 @@ data class SourceLinkDefinitionImpl(override val path: String,
}
}
-class SourceRootImpl(path: String, override val platforms: List<String> = emptyList()) : SourceRoot {
+class SourceRootImpl(path: String) : SourceRoot {
override val path: String = File(path).absolutePath
companion object {
- fun parseSourceRoot(sourceRoot: String): SourceRoot {
- val components = sourceRoot.split("::", limit = 2)
- return SourceRootImpl(components.last(), if (components.size == 1) listOf() else components[0].split(','))
- }
+ fun parseSourceRoot(sourceRoot: String): SourceRoot = SourceRootImpl(sourceRoot)
}
}
@@ -36,27 +33,47 @@ data class PackageOptionsImpl(override val prefix: String,
override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
data class DokkaConfigurationImpl(
- override val moduleName: String,
- override val classpath: List<String>,
- override val sourceRoots: List<SourceRootImpl>,
- override val samples: List<String>,
- override val includes: List<String>,
- override val outputDir: String,
- override val format: String,
- override val includeNonPublic: Boolean,
- override val includeRootPackage: Boolean,
- override val reportUndocumented: Boolean,
- override val skipEmptyPackages: Boolean,
- override val skipDeprecated: Boolean,
- override val jdkVersion: Int,
- override val generateIndexPages: Boolean,
- override val sourceLinks: List<SourceLinkDefinitionImpl>,
- override val impliedPlatforms: List<String>,
- override val perPackageOptions: List<PackageOptionsImpl>,
- override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
- override val noStdlibLink: Boolean,
- override val cacheRoot: String?,
- override val suppressedFiles: List<String>,
- override val languageVersion: String?,
- override val apiVersion: String?
-) : DokkaConfiguration \ No newline at end of file
+ override val outputDir: String = "",
+ override val format: String = "html",
+ override val generateIndexPages: Boolean = false,
+ override val cacheRoot: String? = null,
+ override val impliedPlatforms: List<String> = listOf(),
+ override val passesConfigurations: List<DokkaConfiguration.PassConfiguration> = listOf()
+) : DokkaConfiguration
+
+class PassConfigurationImpl (
+ override val classpath: List<String> = listOf(),
+ override val moduleName: String = "",
+ override val sourceRoots: List<SourceRoot> = listOf(),
+ override val samples: List<String> = listOf(),
+ override val includes: List<String> = listOf(),
+ override val includeNonPublic: Boolean = false,
+ override val includeRootPackage: Boolean = false,
+ override val reportUndocumented: Boolean = false,
+ override val skipEmptyPackages: Boolean = false,
+ override val skipDeprecated: Boolean = false,
+ override val jdkVersion: Int = 6,
+ override val sourceLinks: List<SourceLinkDefinition> = listOf(),
+ override val perPackageOptions: List<DokkaConfiguration.PackageOptions> = listOf(),
+ externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> = listOf(),
+ override val languageVersion: String? = null,
+ override val apiVersion: String? = null,
+ override val noStdlibLink: Boolean = false,
+ override val noJdkLink: Boolean = false,
+ override val suppressedFiles: List<String> = listOf(),
+ override val collectInheritedExtensionsFromLibraries: Boolean = false,
+ override val analysisPlatform: Platform = Platform.DEFAULT,
+ override val targets: List<String> = listOf()
+): DokkaConfiguration.PassConfiguration {
+ private val defaultLinks = run {
+ val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
+ if (!noJdkLink)
+ links += DokkaConfiguration.ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build()
+
+ if (!noStdlibLink)
+ links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build()
+ links
+ }
+ override val externalDocumentationLinks = defaultLinks + externalDocumentationLinks
+}
+
diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
index cf2b0514..1fe4d180 100644
--- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -1,7 +1,9 @@
package org.jetbrains.dokka
import com.google.inject.Inject
+import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
@@ -11,9 +13,9 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner
-import java.io.File
fun getSignature(element: PsiElement?) = when(element) {
+ is PsiPackage -> element.qualifiedName
is PsiClass -> element.qualifiedName
is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name
is PsiMethod ->
@@ -41,18 +43,24 @@ interface JavaDocumentationBuilder {
}
class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
- private val options: DocumentationOptions
+ private val passConfiguration: DokkaConfiguration.PassConfiguration
private val refGraph: NodeReferenceGraph
private val docParser: JavaDocumentationParser
- @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) {
- this.options = options
+ @Inject constructor(
+ passConfiguration: DokkaConfiguration.PassConfiguration,
+ refGraph: NodeReferenceGraph,
+ logger: DokkaLogger,
+ signatureProvider: ElementSignatureProvider,
+ externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+ ) {
+ this.passConfiguration = passConfiguration
this.refGraph = refGraph
- this.docParser = JavadocParser(refGraph, logger)
+ this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver)
}
- constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) {
- this.options = options
+ constructor(passConfiguration: DokkaConfiguration.PassConfiguration, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) {
+ this.passConfiguration = passConfiguration
this.refGraph = refGraph
this.docParser = docParser
}
@@ -61,7 +69,7 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
if (skipFile(file) || file.classes.all { skipElement(it) }) {
return
}
- val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap(), refGraph)
+ val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph)
appendClasses(packageNode, file.classes)
}
@@ -132,21 +140,23 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
}
}
- private fun skipFile(javaFile: PsiJavaFile): Boolean = options.effectivePackageOptions(javaFile.packageName).suppress
+ private fun skipFile(javaFile: PsiJavaFile): Boolean = passConfiguration.effectivePackageOptions(javaFile.packageName).suppress
private fun skipElement(element: Any) =
skipElementByVisibility(element) ||
hasSuppressDocTag(element) ||
skipElementBySuppressedFiles(element)
- private fun skipElementByVisibility(element: Any): Boolean = element is PsiModifierListOwner &&
- !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) &&
- (element.hasModifierProperty(PsiModifier.PRIVATE) ||
- element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
- element.isInternal())
+ private fun skipElementByVisibility(element: Any): Boolean =
+ element is PsiModifierListOwner &&
+ element !is PsiParameter &&
+ !(passConfiguration.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) &&
+ (element.hasModifierProperty(PsiModifier.PRIVATE) ||
+ element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
+ element.isInternal())
private fun skipElementBySuppressedFiles(element: Any): Boolean =
- element is PsiElement && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles
+ element is PsiElement && element.containingFile.virtualFile.path in passConfiguration.suppressedFiles
private fun PsiElement.isInternal(): Boolean {
val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false
@@ -200,11 +210,28 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun PsiField.build(): DocumentationNode {
val node = nodeForElement(this, nodeKind())
node.appendType(type)
- node.appendModifiers(this)
+
+ node.appendConstantValueIfAny(this)
register(this, node)
return node
}
+ private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) {
+ val modifierList = field.modifierList ?: return
+ val initializer = field.initializer ?: return
+ if (field.type is PsiPrimitiveType &&
+ modifierList.hasExplicitModifier(PsiModifier.FINAL) &&
+ modifierList.hasExplicitModifier(PsiModifier.STATIC)) {
+ val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false)
+ val text = when(value) {
+ is String ->
+ "\"" + StringUtil.escapeStringCharacters(value) + "\""
+ else -> value.toString()
+ }
+ append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail)
+ }
+ }
+
private fun PsiField.nodeKind(): NodeKind = when {
this is PsiEnumConstant -> NodeKind.EnumItem
else -> NodeKind.Field
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
index c25f5813..9f9ea017 100644
--- a/core/src/main/kotlin/Java/JavadocParser.kt
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -1,14 +1,18 @@
package org.jetbrains.dokka
import com.intellij.psi.*
-import com.intellij.psi.javadoc.PsiDocTag
-import com.intellij.psi.javadoc.PsiDocTagValue
-import com.intellij.psi.javadoc.PsiDocToken
-import com.intellij.psi.javadoc.PsiInlineDocTag
+import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl
+import com.intellij.psi.impl.source.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.IncorrectOperationException
+import com.intellij.util.containers.isNullOrEmpty
+import org.jetbrains.kotlin.utils.keysToMap
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
+import java.net.URI
data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) {
companion object {
@@ -20,70 +24,151 @@ interface JavaDocumentationParser {
fun parseDocumentation(element: PsiNamedElement): JavadocParseResult
}
-class JavadocParser(private val refGraph: NodeReferenceGraph,
- private val logger: DokkaLogger) : JavaDocumentationParser {
+class JavadocParser(
+ private val refGraph: NodeReferenceGraph,
+ private val logger: DokkaLogger,
+ private val signatureProvider: ElementSignatureProvider,
+ private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+) : JavaDocumentationParser {
+
+ private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) {
+ append(LazyContentBlock {
+ val node = refGraph.lookupOrWarn(signature, logger)?.let(selector)
+ if (node != null) {
+ it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
+ it.symbol(":")
+ it.text(" ")
+ }
+ })
+ }
+
override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
val docComment = (element as? PsiDocCommentOwner)?.docComment
if (docComment == null) return JavadocParseResult.Empty
val result = MutableContent()
var deprecatedContent: Content? = null
- val para = ContentParagraph()
- result.append(para)
- para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() })
+ val firstParagraph = ContentParagraph()
+ firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
+ val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph }
+ firstParagraph.children.removeAll(paragraphs)
+ if (!firstParagraph.isEmpty()) {
+ result.append(firstParagraph)
+ }
+ paragraphs.forEach {
+ result.append(it)
+ }
+
+ if (element is PsiMethod) {
+ val tagsByName = element.searchInheritedTags()
+ for ((tagName, tags) in tagsByName) {
+ for ((tag, context) in tags) {
+ val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName())
+ val signature = signatureProvider.signature(element)
+ when (tagName) {
+ "param" -> {
+ section.appendTypeElement(signature) {
+ it.details.find { it.kind == NodeKind.Parameter }?.detailOrNull(NodeKind.Type)
+ }
+ }
+ "return" -> {
+ section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
+ }
+ }
+ section.convertJavadocElements(tag.contentElements(), context)
+ }
+ }
+ }
+
docComment.tags.forEach { tag ->
- when(tag.name) {
+ when (tag.name) {
"see" -> result.convertSeeTag(tag)
"deprecated" -> {
- deprecatedContent = Content()
- deprecatedContent!!.convertJavadocElements(tag.contentElements())
+ deprecatedContent = Content().apply {
+ convertJavadocElements(tag.contentElements(), element)
+ }
}
+ in tagsToInherit -> {}
else -> {
val subjectName = tag.getSubjectName()
val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
- section.convertJavadocElements(tag.contentElements())
+ section.convertJavadocElements(tag.contentElements(), element)
}
}
}
return JavadocParseResult(result, deprecatedContent)
}
+ private val tagsToInherit = setOf("param", "return", "throws")
+
+ private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement)
+
+ private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> {
+
+ val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() }
+
+ fun recursiveSearch(methods: Array<PsiMethod>) {
+ for (method in methods) {
+ recursiveSearch(method.findSuperMethods())
+ }
+ for (method in methods) {
+ for (tag in method.docComment?.tags.orEmpty()) {
+ if (tag.name in tagsToInherit) {
+ output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method)
+ }
+ }
+ }
+ }
+
+ recursiveSearch(arrayOf(this))
+ return output.mapValues { it.value.values }
+ }
+
+
private fun PsiDocTag.contentElements(): Iterable<PsiElement> {
val tagValueElements = children
- .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME }
- .dropWhile { it is PsiWhiteSpace }
- .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
+ .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME }
+ .dropWhile { it is PsiWhiteSpace }
+ .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements
}
- private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) {
+ private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement) {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ doc.body().childNodes().forEach {
+ convertHtmlNode(it)?.let { append(it) }
+ }
+ }
+
+ private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String {
val htmlBuilder = StringBuilder()
elements.forEach {
if (it is PsiInlineDocTag) {
- htmlBuilder.append(convertInlineDocTag(it))
+ htmlBuilder.append(convertInlineDocTag(it, element))
} else {
htmlBuilder.append(it.text)
}
}
- val doc = Jsoup.parse(htmlBuilder.toString().trim())
- doc.body().childNodes().forEach {
- convertHtmlNode(it)
- }
+ return htmlBuilder.toString().trim()
}
- private fun ContentBlock.convertHtmlNode(node: Node) {
+ private fun convertHtmlNode(node: Node): ContentNode? {
if (node is TextNode) {
- append(ContentText(node.text()))
+ return ContentText(node.text())
} else if (node is Element) {
val childBlock = createBlock(node)
node.childNodes().forEach {
- childBlock.convertHtmlNode(it)
+ val child = convertHtmlNode(it)
+ if (child != null) {
+ childBlock.append(child)
+ }
}
- append(childBlock)
+ return (childBlock)
}
+ return null
}
- private fun createBlock(element: Element): ContentBlock = when(element.tagName()) {
+ private fun createBlock(element: Element): ContentBlock = when (element.tagName()) {
"p" -> ContentParagraph()
"b", "strong" -> ContentStrong()
"i", "em" -> ContentEmphasis()
@@ -99,45 +184,73 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
}
private fun createLink(element: Element): ContentBlock {
- val docref = element.attr("docref")
- if (docref != null) {
- return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)})
- }
- val href = element.attr("href")
- if (href != null) {
- return ContentExternalLink(href)
- } else {
- return ContentBlock()
+ return when {
+ element.hasAttr("docref") -> {
+ val docref = element.attr("docref")
+ ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) })
+ }
+ element.hasAttr("href") -> {
+ val href = element.attr("href")
+
+ val uri = try {
+ URI(href)
+ } catch (_: Exception) {
+ null
+ }
+
+ if (uri?.isAbsolute == false) {
+ ContentLocalLink(href)
+ } else {
+ ContentExternalLink(href)
+ }
+ }
+ element.hasAttr("name") -> {
+ ContentBookmark(element.attr("name"))
+ }
+ else -> ContentBlock()
}
}
private fun MutableContent.convertSeeTag(tag: PsiDocTag) {
- val linkElement = tag.linkElement()
- if (linkElement == null) {
- return
- }
+ val linkElement = tag.linkElement() ?: return
val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null)
- val linkSignature = resolveLink(linkElement)
+
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
val text = ContentText(linkElement.text)
- if (linkSignature != null) {
- val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)})
- linkNode.append(text)
- seeSection.append(linkNode)
- } else {
- seeSection.append(text)
+
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ val node = when {
+ externalLink != null -> {
+ val linkNode = ContentExternalLink(externalLink)
+ linkNode.append(text)
+ linkNode
+ }
+ linkSignature != null -> {
+ val linkNode =
+ ContentNodeLazyLink(
+ (tag.valueElement ?: linkElement).text,
+ { -> refGraph.lookupOrWarn(linkSignature, logger) }
+ )
+ linkNode.append(text)
+ linkNode
+ }
+ else -> text
}
+ seeSection.append(node)
}
- private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) {
+ private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) {
"link", "linkplain" -> {
- val valueElement = tag.linkElement()
- val linkSignature = resolveLink(valueElement)
- if (linkSignature != null) {
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ if (externalLink != null || linkSignature != null) {
val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text
- val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>"
+ val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\""
+ val link = "<a $linkTarget>${labelText.htmlEscape()}</a>"
if (tag.name == "link") "<code>$link</code>" else link
- }
- else if (valueElement != null) {
+ } else if (valueElement != null) {
valueElement.text
} else {
""
@@ -149,16 +262,45 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
val escaped = text.toString().trimStart().htmlEscape()
if (tag.name == "code") "<code>$escaped</code>" else escaped
}
+ "inheritDoc" -> {
+ val result = (element as? PsiMethod)?.let {
+ // @{inheritDoc} is only allowed on functions
+ val parent = tag.parent
+ when (parent) {
+ is PsiDocComment -> element.findSuperDocCommentOrWarn()
+ is PsiDocTag -> element.findSuperDocTagOrWarn(parent)
+ else -> null
+ }
+ }
+ result ?: tag.text
+ }
else -> tag.text
}
+ private fun PsiDocTag.referenceElement(): PsiElement? =
+ linkElement()?.let {
+ if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) {
+ PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java)
+ } else {
+ it
+ }
+ }
+
private fun PsiDocTag.linkElement(): PsiElement? =
- valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
+ valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
+
+ private fun resolveExternalLink(valueElement: PsiElement?): String? {
+ val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return externalDocumentationLinkResolver.buildExternalDocumentationLink(target)
+ }
+ return null
+ }
- private fun resolveLink(valueElement: PsiElement?): String? {
+ private fun resolveInternalLink(valueElement: PsiElement?): String? {
val target = valueElement?.reference?.resolve()
if (target != null) {
- return getSignature(target)
+ return signatureProvider.signature(target)
}
return null
}
@@ -169,4 +311,88 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
}
return null
}
+
+ private fun PsiMethod.findSuperDocCommentOrWarn(): String {
+ val method = findFirstSuperMethodWithDocumentation(this)
+ if (method != null) {
+ val descriptionElements = method.docComment?.descriptionElements?.dropWhile {
+ it.text.trim().isEmpty()
+ } ?: return ""
+
+ return expandAllForElements(descriptionElements, method)
+ }
+ logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}")
+ return ""
+ }
+
+
+ private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this)
+
+ if (result != null) {
+ val (method, tag) = result
+
+ val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() }
+
+ val expandedString = expandAllForElements(contentElements, method)
+
+ return expandedString
+ }
+ logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}")
+ return ""
+ }
+
+ private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? {
+ val superMethods = current.findSuperMethods()
+ for (method in superMethods) {
+ val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() }
+ if (!docs.isNullOrEmpty()) {
+ return method
+ }
+ }
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentation(method)
+ if (result != null) {
+ return result
+ }
+ }
+
+ return null
+ }
+
+ private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair<PsiMethod, PsiDocTag>? {
+ val superMethods = current.findSuperMethods()
+ val mappedFilteredTags = superMethods.map {
+ it to it.docComment?.tags?.filter { it.name == elementToExpand.name }
+ }
+
+ for ((method, tags) in mappedFilteredTags) {
+ tags ?: continue
+ for (tag in tags) {
+ val (tagSubject, elementSubject) = when (tag.name) {
+ "throws" -> {
+ // match class names only for throws, ignore possibly fully qualified path
+ // TODO: Always match exactly here
+ tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last()
+ }
+ else -> {
+ tag.getSubjectName() to elementToExpand.getSubjectName()
+ }
+ }
+
+ if (tagSubject == elementSubject) {
+ return method to tag
+ }
+ }
+ }
+
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method)
+ if (result != null) {
+ return result
+ }
+ }
+ return null
+ }
+
}
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
index ffef399d..c3a84e57 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -1,21 +1,18 @@
package org.jetbrains.dokka
import com.google.inject.Inject
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
class DeclarationLinkResolver
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
val logger: DokkaLogger,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver,
- val descriptorSignatureProvider: DescriptorSignatureProvider) {
+ val elementSignatureProvider: ElementSignatureProvider) {
fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? {
@@ -34,14 +31,14 @@ class DeclarationLinkResolver
if (externalHref != null) {
return ContentExternalLink(externalHref)
}
- val signature = descriptorSignatureProvider.signature(symbol)
+ val signature = elementSignatureProvider.signature(symbol)
val referencedAt = fromDescriptor.signatureWithSourceLocation()
return ContentNodeLazyLink(href, { ->
val target = refGraph.lookup(signature)
if (target == null) {
- logger.warn("Can't find node by signature $signature, referenced at $referencedAt")
+ logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt")
}
target
})
@@ -66,7 +63,7 @@ class DeclarationLinkResolver
if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
return symbol.overriddenDescriptors.firstOrNull()
}
- if (symbol is TypeAliasDescriptor && !symbol.isDocumented(options)) {
+ if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) {
return symbol.classDescriptor
}
return symbol
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
index eb8c12d0..d0650d45 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -8,14 +8,17 @@ import org.intellij.markdown.parser.LinkMap
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -25,12 +28,15 @@ import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
class DescriptorDocumentationParser
- @Inject constructor(val options: DocumentationOptions,
+ @Inject constructor(val options: DokkaConfiguration.PassConfiguration,
val logger: DokkaLogger,
val linkResolver: DeclarationLinkResolver,
val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
- val sampleService: SampleProcessingService)
+ val sampleService: SampleProcessingService,
+ val signatureProvider: KotlinElementSignatureProvider,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+)
{
fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content =
parseDocumentationAndDetails(descriptor, inline).first
@@ -49,6 +55,13 @@ class DescriptorDocumentationParser
}
return Content.Empty to { node -> }
}
+
+ val contextDescriptor =
+ (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration)
+ ?.takeIf { it != descriptor.original.sourcePsi() }
+ ?.resolveToDescriptorIfAny()
+ ?: descriptor
+
var kdocText = kdoc.getContent()
// workaround for code fence parsing problem in IJ markdown parser
if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
@@ -56,20 +69,20 @@ class DescriptorDocumentationParser
}
val tree = parseMarkdown(kdocText)
val linkMap = LinkMap.buildLinkMap(tree.node, kdocText)
- val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(descriptor, href) }), inline)
+ val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }), inline)
if (kdoc is KDocSection) {
val tags = kdoc.getTags()
tags.forEach {
when (it.knownTag) {
KDocKnownTag.SAMPLE ->
- content.append(sampleService.resolveSample(descriptor, it.getSubjectName(), it))
+ content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it))
KDocKnownTag.SEE ->
- content.addTagToSeeAlso(descriptor, it)
+ content.addTagToSeeAlso(contextDescriptor, it)
else -> {
val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName())
val sectionContent = it.getContent()
val markdownNode = parseMarkdown(sectionContent)
- buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(descriptor, href) }))
+ buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }))
}
}
}
@@ -81,7 +94,7 @@ class DescriptorDocumentationParser
val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
return if (suppressAnnotation != null) {
@Suppress("UNCHECKED_CAST")
- (suppressAnnotation.argumentValue("names") as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
+ (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
} else containingDeclaration?.isSuppressWarning() ?: false
}
@@ -119,7 +132,12 @@ 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, logger).parseDocumentation(psi as PsiNamedElement)
+ val parseResult = JavadocParser(
+ refGraph,
+ logger,
+ signatureProvider,
+ externalDocumentationLinkResolver
+ ).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 61bf50d6..e5bc32ab 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -2,8 +2,9 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiField
import com.intellij.psi.PsiJavaFile
-import org.jetbrains.dokka.DokkaConfiguration.*
+import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
@@ -11,74 +12,31 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtVariableDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
-import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
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 java.io.File
-import java.nio.file.Path
-import java.nio.file.Paths
+import org.jetbrains.kotlin.util.supertypesWithAny
import com.google.inject.name.Named as GuiceNamed
-class DocumentationOptions(val outputDir: String,
- val outputFormat: String,
- includeNonPublic: Boolean = false,
- val includeRootPackage: Boolean = false,
- reportUndocumented: Boolean = true,
- val skipEmptyPackages: Boolean = true,
- skipDeprecated: Boolean = false,
- jdkVersion: Int = 6,
- val generateIndexPages: Boolean = true,
- val sourceLinks: List<SourceLinkDefinition> = emptyList(),
- val impliedPlatforms: List<String> = emptyList(),
- // Sorted by pattern length
- perPackageOptions: List<PackageOptions> = emptyList(),
- externalDocumentationLinks: List<ExternalDocumentationLink> = emptyList(),
- noStdlibLink: Boolean,
- val languageVersion: String?,
- val apiVersion: String?,
- cacheRoot: String? = null,
- val suppressedFiles: List<File> = emptyList()) {
- init {
- if (perPackageOptions.any { it.prefix == "" })
- throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
- }
-
- val perPackageOptions = perPackageOptions.sortedByDescending { it.prefix.length }
- val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated)
-
- fun effectivePackageOptions(pack: String): PackageOptions = perPackageOptions.firstOrNull { pack == it.prefix || pack.startsWith(it.prefix + ".") } ?: rootPackageOptions
- fun effectivePackageOptions(pack: FqName): PackageOptions = effectivePackageOptions(pack.asString())
-
- val defaultLinks = run {
- val links = mutableListOf(ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build())
- if (!noStdlibLink)
- links += ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build()
- links
- }
-
- val externalDocumentationLinks = defaultLinks + externalDocumentationLinks
-
- val cacheRoot: Path? = when {
- cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka")
- cacheRoot != null -> Paths.get(cacheRoot)
- else -> null
- }
-}
-
private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor,
extensionReceiverDescriptor: DeclarationDescriptor,
allFqNames: Collection<FqName>): Boolean {
@@ -101,10 +59,14 @@ interface DefaultPlatformsProvider {
fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String>
}
+val ignoredSupertypes = setOf(
+ "kotlin.Annotation", "kotlin.Enum", "kotlin.Any"
+)
+
class DocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val descriptorDocumentationParser: DescriptorDocumentationParser,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val refGraph: NodeReferenceGraph,
val platformNodeRegistry: PlatformNodeRegistry,
val logger: DokkaLogger,
@@ -132,8 +94,20 @@ class DocumentationBuilder
refGraph.register(descriptor.signature(), node)
}
- fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
- val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+ fun <T> nodeForDescriptor(
+ descriptor: T,
+ kind: NodeKind,
+ external: Boolean = false
+ ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ val (doc, callback) =
+ if (external) {
+ Content.Empty to { node -> }
+ } else {
+ descriptorDocumentationParser.parseDocumentationAndDetails(
+ descriptor,
+ kind == NodeKind.Parameter
+ )
+ }
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
node.appendSignature(descriptor)
callback(node)
@@ -167,27 +141,20 @@ class DocumentationBuilder
appendTextNode(modifier, NodeKind.Modifier)
}
- fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType) {
+ fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) {
val unwrappedType = superType.unwrap()
if (unwrappedType is AbbreviatedType) {
- appendSupertype(descriptor, unwrappedType.abbreviation)
- } else if (!ignoreSupertype(unwrappedType)) {
+ appendSupertype(descriptor, unwrappedType.abbreviation, backref)
+ } else {
appendType(unwrappedType, NodeKind.Supertype)
val superclass = unwrappedType.constructor.declarationDescriptor
- link(superclass, descriptor, RefKind.Inheritor)
+ if (backref) {
+ link(superclass, descriptor, RefKind.Inheritor)
+ }
link(descriptor, superclass, RefKind.Superclass)
}
}
- private fun ignoreSupertype(superType: KotlinType): Boolean {
- val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
- if (superClass != null) {
- val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
- return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
- }
- return false
- }
-
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
if (projection.isStarProjection) {
appendTextNode("*", NodeKind.Type)
@@ -225,19 +192,39 @@ class DocumentationBuilder
if (prefix != "") {
node.appendTextNode(prefix, NodeKind.Modifier)
}
- if (kotlinType.isMarkedNullable) {
+ if (kotlinType.isNullabilityFlexible()) {
+ node.appendTextNode("!", NodeKind.NullabilityModifier)
+ } else if (kotlinType.isMarkedNullable) {
node.appendTextNode("?", NodeKind.NullabilityModifier)
}
if (classifierDescriptor != null) {
- val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ val externalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
if (externalLink != null) {
- node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ val targetNode =
+ refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+ node.append(targetNode, RefKind.ExternalType)
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
} else {
- link(node, classifierDescriptor,
- if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+ link(
+ node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+ )
+ }
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ node.append(
+ DocumentationNode(
+ classifierDescriptor.fqNameUnsafe.asString(),
+ Content.Empty,
+ NodeKind.QualifiedName
+ ), RefKind.Detail
+ )
}
}
+
append(node, RefKind.Detail)
node.appendAnnotations(kotlinType)
for (typeArgument in kotlinType.arguments) {
@@ -271,6 +258,17 @@ class DocumentationBuilder
}
}
+ fun DocumentationNode.appendExternalLink(externalLink: String) {
+ append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+
+ fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+ val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+ if (target != null) {
+ appendExternalLink(target)
+ }
+ }
+
fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
val kotlinVersion = annotation
.detail(NodeKind.Parameter)
@@ -300,7 +298,7 @@ class DocumentationBuilder
fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation
fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
- appendSourceLink(sourceElement.getPsi(), options.sourceLinks)
+ appendSourceLink(sourceElement.getPsi(), passConfiguration.sourceLinks)
}
fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) {
@@ -308,7 +306,7 @@ class DocumentationBuilder
}
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? {
- if (!descriptor.isGenerated() && descriptor.isDocumented(options)) {
+ if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) {
val node = descriptor.build()
append(node, kind)
return node
@@ -335,7 +333,7 @@ class DocumentationBuilder
fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) {
- if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return
+ if (descriptor.isGenerated() || !descriptor.isDocumented(passConfiguration)) return
val existingNode = refGraph.lookup(descriptor.signature())
if (existingNode != null) {
@@ -407,25 +405,54 @@ class DocumentationBuilder
val allFqNames = fragments.map { it.fqName }.distinct()
for (packageName in allFqNames) {
- if (packageName.isRoot && !options.includeRootPackage) continue
+ if (packageName.isRoot && !passConfiguration.includeRootPackage) continue
val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() }
- if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue
+ if (passConfiguration.skipEmptyPackages && declarations.none { it.isDocumented(passConfiguration) }) continue
logger.info(" package $packageName: ${declarations.count()} declarations")
- val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
+ val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode,
declarations, allFqNames)
}
- propagateExtensionFunctionsToSubclasses(fragments)
}
- private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
- val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
- val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
- val classHierarchy = buildClassHierarchy(allClasses)
+ fun propagateExtensionFunctionsToSubclasses(
+ fragments: Collection<PackageFragmentDescriptor>,
+ resolutionFacade: DokkaResolutionFacade
+ ) {
+
+ val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+ // Wide-collect all view descriptors
+ val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+ packages
+ .flatMap { pkg ->
+ moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+ }.map { fqName ->
+ moduleDescriptor.getPackage(fqName)
+ }.takeUnless { it.isEmpty() }
+ }.flatten()
+
+ val allDescriptors =
+ if (passConfiguration.collectInheritedExtensionsFromLibraries) {
+ allPackageViewDescriptors.map { it.memberScope }
+ } else {
+ fragments.asSequence().map { it.getMemberScope() }
+ }.flatMap {
+ it.getDescriptorsFiltered(
+ DescriptorKindFilter.CALLABLES
+ ).asSequence()
+ }
+
+
+ val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
+ val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>()
- val allExtensionFunctions = allDescriptors
+ val classHierarchy = buildClassHierarchy(documentingClasses)
+
+ val allExtensionFunctions =
+ allDescriptors
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.extensionReceiverParameter != null }
val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
@@ -433,15 +460,31 @@ class DocumentationBuilder
for (extensionFunction in allExtensionFunctions) {
if (extensionFunction.dispatchReceiverParameter != null) continue
val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
- ?.filter { fn -> fn.canShadow(extensionFunction) }
+ ?.filter { fn -> fn.canShadow(extensionFunction) }
?: emptyList()
if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
- val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
- val subclasses = classHierarchy[classDescriptor] ?: continue
- subclasses.forEach { subclass ->
+ val subclasses =
+ classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+ if (subclasses.isEmpty()) continue
+ subclasses.values.flatten().forEach { subclass ->
if (subclass.isExtensionApplicable(extensionFunction) &&
- possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+ possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+ val hasExternalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+ extensionFunction
+ ) != null
+ if (hasExternalLink) {
+ val containerDesc =
+ extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+ if (containerDesc != null) {
+ val container = refGraph.lookup(containerDesc.signature())
+ ?: containerDesc.buildExternal()
+ container.append(extensionFunction.buildExternal(), RefKind.Member)
+ }
+ }
+
refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
}
}
@@ -449,12 +492,9 @@ class DocumentationBuilder
}
private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
- val receiverType = extensionFunction.extensionReceiverParameter!!.type
- if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
- val receiverClass = receiverType.constructor.declarationDescriptor
- return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
- }
- return defaultType.isSubtypeOf(receiverType)
+ val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+ val classType = defaultType.toFuzzyType(declaredTypeParameters)
+ return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
}
private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -493,34 +533,60 @@ class DocumentationBuilder
}
fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
- is ClassDescriptor -> build()
+ is ClassifierDescriptor -> build()
is ConstructorDescriptor -> build()
is PropertyDescriptor -> build()
is FunctionDescriptor -> build()
- is TypeParameterDescriptor -> build()
is ValueParameterDescriptor -> build()
is ReceiverParameterDescriptor -> build()
- is TypeAliasDescriptor -> build()
else -> throw IllegalStateException("Descriptor $this is not known")
}
- fun TypeAliasDescriptor.build(): DocumentationNode {
+ fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+ val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+ is FunctionDescriptor -> build(true)
+ is PropertyDescriptor -> build(true)
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+
+ fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
+ is ClassDescriptor -> build(external)
+ is TypeAliasDescriptor -> build(external)
+ is TypeParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode {
val node = nodeForDescriptor(this, NodeKind.TypeAlias)
- node.appendAnnotations(this)
+ if (!external) {
+ node.appendAnnotations(this)
+ }
node.appendModifiers(this)
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
-
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
register(this, node)
return node
}
- fun ClassDescriptor.build(): DocumentationNode {
+ fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
val kind = when {
kind == ClassKind.OBJECT -> NodeKind.Object
kind == ClassKind.INTERFACE -> NodeKind.Interface
@@ -530,21 +596,25 @@ class DocumentationBuilder
isSubclassOfThrowable() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForDescriptor(this, kind)
- typeConstructor.supertypes.forEach {
- node.appendSupertype(this, it)
+ val node = nodeForDescriptor(this, kind, external)
+ register(this, node)
+ supertypesWithAnyPrecise().forEach {
+ node.appendSupertype(this, it, !external)
}
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
}
- for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
- node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ if (!external) {
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
}
- node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- register(this, node)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
return node
}
@@ -570,7 +640,7 @@ class DocumentationBuilder
.mapTo(result) { ClassMember(it, extraModifier = "static") }
val companionObjectDescriptor = companionObjectDescriptor
- if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(options)) {
+ if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(passConfiguration)) {
val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()
val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() }
descriptorsToDocument.mapTo(result) {
@@ -611,12 +681,12 @@ class DocumentationBuilder
return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
}
- fun FunctionDescriptor.build(): DocumentationNode {
+ fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
if (ErrorUtils.containsErrorType(this)) {
logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
}
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+ val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -624,8 +694,12 @@ class DocumentationBuilder
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ } else {
+ node.appendExternalLink(this)
+ }
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -646,32 +720,53 @@ class DocumentationBuilder
}
}
- fun PropertyDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+ fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+ val node = nodeForDescriptor(
+ this,
+ if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+ external
+ )
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- if (isVar) {
- node.appendTextNode("var", NodeKind.Modifier)
- }
- getter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ if (!external) {
+ node.appendSourceLink(source)
+ if (isVar) {
+ node.appendTextNode("var", NodeKind.Modifier)
}
- }
- setter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+ if (isConst) {
+ val psi = sourcePsi()
+ val valueText = when (psi) {
+ is KtVariableDeclaration -> psi.initializer?.text
+ is PsiField -> psi.initializer?.text
+ else -> null
+ }
+ valueText?.let { node.appendTextNode(it, NodeKind.Value) }
+ }
+
+
+ getter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ }
+ }
+ setter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+ }
}
+ node.appendDefaultPlatforms(this)
+ }
+ if (external) {
+ node.appendExternalLink(this)
}
overriddenDescriptors.forEach {
addOverrideLink(it, this)
}
- node.appendDefaultPlatforms(this)
register(this, node)
return node
@@ -779,21 +874,55 @@ class DocumentationBuilder
"\"" + StringUtil.escapeStringCharacters(value) + "\""
is EnumEntrySyntheticClassDescriptor ->
value.containingDeclaration.name.asString() + "." + value.name.asString()
+ is Pair<*, *> -> {
+ val (classId, name) = value
+ if (classId is ClassId && name is Name) {
+ classId.shortClassName.asString() + "." + name.asString()
+ } else {
+ value.toString()
+ }
+ }
else -> value.toString()
}.let { valueString ->
DocumentationNode(valueString, Content.Empty, NodeKind.Value)
}
}
-}
-val visibleToDocumentation = setOf(Visibilities.PROTECTED, Visibilities.PUBLIC)
-fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean {
- return (options.effectivePackageOptions(fqNameSafe).includeNonPublic
+ fun DocumentationNode.getParentForPackageMember(
+ descriptor: DeclarationDescriptor,
+ externalClassNodes: MutableMap<FqName, DocumentationNode>,
+ allFqNames: Collection<FqName>,
+ packageName: FqName
+ ): DocumentationNode {
+ if (descriptor is CallableMemberDescriptor) {
+ val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
+ if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) &&
+ !ErrorUtils.isError(extensionClassDescriptor)) {
+ val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
+ return externalClassNodes.getOrPut(fqName) {
+ val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass)
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor)
+ if (externalLink != null) {
+ newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ append(newNode, RefKind.Member)
+ refGraph.register("${packageName.asString()}:${extensionClassDescriptor.signature()}", newNode)
+ newNode
+ }
+ }
+ }
+ return this
+ }
+
+}
+
+fun DeclarationDescriptor.isDocumented(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean {
+ return (passConfiguration.effectivePackageOptions(fqNameSafe).includeNonPublic
|| this !is MemberDescriptor
- || this.visibility in visibleToDocumentation)
- && !isDocumentationSuppressed(options)
- && (!options.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
+ || this.visibility.isPublicAPI)
+ && !isDocumentationSuppressed(passConfiguration)
+ && (!passConfiguration.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
}
private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION
@@ -807,8 +936,13 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
declarations.forEach { descriptor ->
with(documentationBuilder) {
- if (descriptor.isDocumented(options)) {
- val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames)
+ if (descriptor.isDocumented(passConfiguration)) {
+ val parent = packageNode.getParentForPackageMember(
+ descriptor,
+ externalClassNodes,
+ allFqNames,
+ packageName
+ )
parent.appendOrUpdateMember(descriptor)
}
}
@@ -819,20 +953,15 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
class KotlinJavaDocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val documentationBuilder: DocumentationBuilder,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val logger: DokkaLogger) : JavaDocumentationBuilder {
override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
val classDescriptors = file.classes.map {
- val javaDescriptorResolver = resolutionFacade.getFrontendService(JavaDescriptorResolver::class.java)
-
- javaDescriptorResolver.resolveClass(JavaClassImpl(it)) ?: run {
- logger.warn("Cannot find descriptor for Java class ${it.qualifiedName}")
- null
- }
+ it.getJavaClassDescriptor(resolutionFacade)
}
- if (classDescriptors.any { it != null && it.isDocumented(options) }) {
- val packageNode = module.findOrCreatePackageNode(file.packageName, packageContent, documentationBuilder.refGraph)
+ if (classDescriptors.any { it != null && it.isDocumented(passConfiguration) }) {
+ val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph)
for (descriptor in classDescriptors.filterNotNull()) {
with(documentationBuilder) {
@@ -861,13 +990,13 @@ fun AnnotationDescriptor.mustBeDocumented(): Boolean {
return annotationClass.isDocumentedAnnotation()
}
-fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOptions): Boolean {
+fun DeclarationDescriptor.isDocumentationSuppressed(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean {
- if (options.effectivePackageOptions(fqNameSafe).suppress) return true
+ if (passConfiguration.effectivePackageOptions(fqNameSafe).suppress) return true
val path = this.findPsi()?.containingFile?.virtualFile?.path
if (path != null) {
- if (File(path).absoluteFile in options.suppressedFiles) return true
+ if (path in passConfiguration.suppressedFiles) return true
}
val doc = findKDoc()
@@ -877,30 +1006,12 @@ fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOption
}
fun DeclarationDescriptor.sourcePsi() =
- ((original as DeclarationDescriptorWithSource).source as? PsiSourceElement)?.psi
+ ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any {
DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated"
} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated())
-fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
- externalClassNodes: MutableMap<FqName, DocumentationNode>,
- allFqNames: Collection<FqName>): DocumentationNode {
- if (descriptor is CallableMemberDescriptor) {
- val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
- if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) &&
- !ErrorUtils.isError(extensionClassDescriptor)) {
- val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
- return externalClassNodes.getOrPut(fqName, {
- val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass)
- append(newNode, RefKind.Member)
- newNode
- })
- }
- }
- return this
-}
-
fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
val extensionReceiver = extensionReceiverParameter
if (extensionReceiver != null) {
@@ -923,7 +1034,7 @@ fun DeclarationDescriptor.signature(): String {
is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString()
is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature()
- is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature()
+ is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() + ":" + returnType?.signature()
is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name
is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name
is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name
@@ -984,8 +1095,8 @@ fun DeclarationDescriptor.sourceLocation(): String? {
return null
}
-fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
- if (options.generateIndexPages) {
+fun DocumentationModule.prepareForGeneration(configuration: DokkaConfiguration) {
+ if (configuration.generateIndexPages) {
generateAllTypesNode()
}
nodeRefGraph.resolveReferences()
@@ -993,8 +1104,10 @@ fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
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 }
+ .flatMap { it.members.filter {
+ it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass
+ || (it.kind == NodeKind.GroupNode && it.origins.all { it.kind in NodeKind.classLike }) } }
+ .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() }
val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
for (typeNode in allTypes) {
@@ -1003,3 +1116,18 @@ fun DocumentationNode.generateAllTypesNode() {
append(allTypesNode, RefKind.Member)
}
+
+fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> {
+ if (KotlinBuiltIns.isAny(this)) {
+ return emptyList()
+ }
+ return typeConstructor.supertypesWithAny()
+}
+
+fun PassConfiguration.effectivePackageOptions(pack: String): DokkaConfiguration.PackageOptions {
+ val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated)
+ return perPackageOptions.firstOrNull { pack == it.prefix || pack.startsWith(it.prefix + ".") } ?: rootPackageOptions
+}
+
+fun PassConfiguration.effectivePackageOptions(pack: FqName): DokkaConfiguration.PackageOptions = effectivePackageOptions(pack.asString())
+
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
index 108cee78..9d986aee 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -2,13 +2,16 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
+import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.util.io.*
+import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor
+import org.jetbrains.dokka.Formats.FormatDescriptor
+import org.jetbrains.dokka.Utilities.ServiceLocator
+import org.jetbrains.dokka.Utilities.lookup
import org.jetbrains.kotlin.descriptors.*
-import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -19,26 +22,48 @@ import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
import java.nio.file.Path
+import java.nio.file.Paths
import java.security.MessageDigest
+import javax.inject.Named
+import kotlin.reflect.full.findAnnotation
fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) }
+typealias PackageFqNameToLocation = MutableMap<FqName, PackageListProvider.ExternalDocumentationRoot>
+
@Singleton
-class ExternalDocumentationLinkResolver @Inject constructor(
- val options: DocumentationOptions,
- val logger: DokkaLogger
+class PackageListProvider @Inject constructor(
+ val configuration: DokkaConfiguration,
+ val logger: DokkaLogger
) {
+ val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>()
- val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
- val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+ val cacheDir: Path? = when {
+ configuration.cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka")
+ configuration.cacheRoot != null -> Paths.get(configuration.cacheRoot)
+ else -> null
+ }?.resolve("packageListCache")?.apply { createDirectories() }
- class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) {
- override fun toString(): String = rootUrl.toString()
+ val cachedProtocols = setOf("http", "https", "ftp")
+
+ init {
+ for (conf in configuration.passesConfigurations) {
+ for (link in conf.externalDocumentationLinks) {
+ if (link in storage) {
+ continue
+ }
+
+ try {
+ loadPackageList(link)
+ } catch (e: Exception) {
+ throw RuntimeException("Exception while loading package-list from $link", e)
+ }
+ }
+
+ }
}
- val cacheDir: Path? = options.cacheRoot?.resolve("packageListCache")?.apply { createDirectories() }
- val cachedProtocols = setOf("http", "https", "ftp")
fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
val connection = this.openConnection()
@@ -114,48 +139,99 @@ class ExternalDocumentationLinkResolver @Inject constructor(
val (params, packages) =
packageListStream
- .bufferedReader()
- .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
+ .bufferedReader()
+ .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } }
val paramsMap = params.asSequence()
- .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
- .groupBy({ (key, _) -> key }, { (_, value) -> value })
+ .map { it.removePrefix(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX).split(":", limit = 2) }
+ .groupBy({ (key, _) -> key }, { (_, value) -> value })
val format = paramsMap["format"]?.singleOrNull() ?: "javadoc"
val locations = paramsMap["location"].orEmpty()
- .map { it.split("\u001f", limit = 2) }
- .map { (key, value) -> key to value }
- .toMap()
+ .map { it.split("\u001f", limit = 2) }
+ .map { (key, value) -> key to value }
+ .toMap()
- val resolver = if (format == "javadoc") {
- InboundExternalLinkResolutionService.Javadoc()
- } else {
- val linkExtension = paramsMap["linkExtension"]?.singleOrNull() ?:
- throw RuntimeException("Failed to parse package list from $packageListUrl")
- InboundExternalLinkResolutionService.Dokka(linkExtension)
- }
+
+ val defaultResolverDesc = ExternalDocumentationLinkResolver.services["dokka-default"]!!
+ val resolverDesc = ExternalDocumentationLinkResolver.services[format]
+ ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver }
+ ?: defaultResolverDesc.also {
+ logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default")
+ }
+
+
+ val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin
+
+ val constructors = resolverClass.constructors
+
+ val constructor = constructors.singleOrNull()
+ ?: constructors.first { it.findAnnotation<Inject>() != null }
+ val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService
val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations)
- packages.map { FqName(it) }.forEach { packageFqNameToLocation[it] = rootInfo }
+ val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
+ storage[link] = packageFqNameToLocation
+
+ val fqNames = packages.map { FqName(it) }
+ for(name in fqNames) {
+ packageFqNameToLocation[name] = rootInfo
+ }
}
+
+
+ class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) {
+ override fun toString(): String = rootUrl.toString()
+ }
+
+ companion object {
+ private val formatsWithDefaultResolver =
+ ServiceLocator
+ .allServices("format")
+ .filter {
+ val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor
+ desc?.generatorServiceClass == FileGenerator::class
+ }.map { it.name }
+ .toSet()
+
+ }
+
+}
+
+class ExternalDocumentationLinkResolver @Inject constructor(
+ val configuration: DokkaConfiguration,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
+ @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade,
+ val logger: DokkaLogger,
+ val packageListProvider: PackageListProvider
+) {
+
+ val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+ val packageFqNameToLocation = mutableMapOf<FqName, PackageListProvider.ExternalDocumentationRoot>()
+
init {
- options.externalDocumentationLinks.forEach {
- try {
- loadPackageList(it)
- } catch (e: Exception) {
- throw RuntimeException("Exception while loading package-list from $it", e)
- }
+ val fqNameToLocationMaps = passConfiguration.externalDocumentationLinks
+ .mapNotNull { packageListProvider.storage[it] }
+
+ for(map in fqNameToLocationMaps) {
+ packageFqNameToLocation.putAll(map)
+ }
+ }
+
+ fun buildExternalDocumentationLink(element: PsiElement): String? {
+ return element.extractDescriptor(libraryResolutionFacade)?.let {
+ buildExternalDocumentationLink(it)
}
}
fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
val packageFqName: FqName =
when (symbol) {
- is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
is PackageFragmentDescriptor -> symbol.fqName
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
else -> return null
}
@@ -169,6 +245,7 @@ class ExternalDocumentationLinkResolver @Inject constructor(
companion object {
const val DOKKA_PARAM_PREFIX = "\$dokka."
+ val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name }
}
}
@@ -176,15 +253,17 @@ class ExternalDocumentationLinkResolver @Inject constructor(
interface InboundExternalLinkResolutionService {
fun getPath(symbol: DeclarationDescriptor): String?
- class Javadoc : InboundExternalLinkResolutionService {
+ class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
override fun getPath(symbol: DeclarationDescriptor): String? {
- if (symbol is JavaClassDescriptor) {
+ if (symbol is EnumEntrySyntheticClassDescriptor) {
+ return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() }
+ } else if (symbol is JavaClassDescriptor) {
return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html"
} else if (symbol is JavaCallableMemberDescriptor) {
val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
val containingClassLink = getPath(containingClass)
if (containingClassLink != null) {
- if (symbol is JavaMethodDescriptor) {
+ if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) {
val psi = symbol.sourcePsi() as? PsiMethod
if (psi != null) {
val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
@@ -200,7 +279,9 @@ interface InboundExternalLinkResolutionService {
}
}
- class Dokka(val extension: String) : InboundExternalLinkResolutionService {
+ class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver")
+
override fun getPath(symbol: DeclarationDescriptor): String? {
val leafElement = when (symbol) {
is CallableDescriptor, is TypeAliasDescriptor -> true
@@ -211,13 +292,11 @@ interface InboundExternalLinkResolutionService {
else return "$path/index.$extension"
}
- fun getPathWithoutExtension(symbol: DeclarationDescriptor): String {
- if (symbol.containingDeclaration == null)
- return identifierToFilename(symbol.name.asString())
- else if (symbol is PackageFragmentDescriptor) {
- return symbol.fqName.asString()
- } else {
- return getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString())
+ private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String {
+ return when {
+ symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString())
+ symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString())
+ else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString())
}
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
deleted file mode 100644
index a3be658e..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.getSignature
-import org.jetbrains.dokka.sourcePsi
-import org.jetbrains.kotlin.asJava.toLightElements
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.psi.KtElement
-
-class KotlinAsJavaDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String {
- val sourcePsi = forDesc.sourcePsi()
- val javaLikePsi = if (sourcePsi is KtElement) {
- sourcePsi.toLightElements().firstOrNull()
- } else {
- sourcePsi
- }
-
- return getSignature(javaLikePsi) ?:
- "not implemented for $forDesc with psi: $sourcePsi"
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
index c7ed8292..be6dd2e1 100644
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -28,7 +28,7 @@ class KotlinAsJavaDocumentationBuilder
return
}
- val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.options,
+ val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration,
documentationBuilder.refGraph,
kotlinAsJavaDocumentationParser)
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
new file mode 100644
index 00000000..20ea179e
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.asJava.toLightElements
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.psi.KtElement
+
+class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider {
+
+ private fun PsiElement.javaLikePsi() = when {
+ this is KtElement -> toLightElements().firstOrNull()
+ else -> this
+ }
+
+ override fun signature(forPsi: PsiElement): String {
+ return getSignature(forPsi.javaLikePsi()) ?:
+ "not implemented for $forPsi"
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String {
+ val sourcePsi = forDesc.sourcePsi()
+ return getSignature(sourcePsi?.javaLikePsi()) ?:
+ "not implemented for $forDesc with psi: $sourcePsi"
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
deleted file mode 100644
index 7ecd0389..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.signature
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-class KotlinDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
new file mode 100644
index 00000000..bcac0182
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMember
+import com.intellij.psi.PsiPackage
+import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.BindingContext
+import javax.inject.Inject
+
+class KotlinElementSignatureProvider @Inject constructor(
+ val resolutionFacade: DokkaResolutionFacade
+) : ElementSignatureProvider {
+ override fun signature(forPsi: PsiElement): String {
+ return forPsi.extractDescriptor(resolutionFacade)
+ ?.let { signature(it) }
+ ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" }
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
+}
+
+
+fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? {
+ val forPsi = this
+
+ return when (forPsi) {
+ is KtLightElement<*, *> -> return (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade)
+ is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName))
+ is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade)
+ else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi]
+ }
+}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index f33c8c96..5b565464 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -5,8 +5,13 @@ import org.jetbrains.dokka.LanguageService.RenderMode
/**
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
-class KotlinLanguageService : LanguageService {
- private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
+class KotlinLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return node.name !in fullOnlyModifiers
+ }
+
+ private val fullOnlyModifiers =
+ setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -17,11 +22,12 @@ class KotlinLanguageService : LanguageService {
NodeKind.EnumItem,
NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
+ NodeKind.Parameter -> renderParameter(node, renderMode)
NodeKind.TypeParameter -> renderTypeParameter(node, renderMode)
NodeKind.Type,
NodeKind.UpperBound -> renderType(node, renderMode)
- NodeKind.Modifier -> renderModifier(node)
+ NodeKind.Modifier -> renderModifier(this, node, renderMode)
NodeKind.Constructor,
NodeKind.Function,
NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
@@ -32,12 +38,6 @@ class KotlinLanguageService : LanguageService {
}
}
- override fun renderName(node: DocumentationNode): String {
- return when (node.kind) {
- NodeKind.Constructor -> node.owner!!.name
- else -> node.name
- }
- }
override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
if (nodes.size < 2) return null
@@ -46,10 +46,17 @@ class KotlinLanguageService : LanguageService {
return content {
val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first()
if (functionWithTypeParameter.kind == NodeKind.Function) {
- renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
- }
- else {
- renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ renderFunction(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
+ } else {
+ renderProperty(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
}
}
}
@@ -70,26 +77,27 @@ class KotlinLanguageService : LanguageService {
companion object {
private val arrayClasses = setOf(
- "kotlin.Array",
- "kotlin.BooleanArray",
- "kotlin.ByteArray",
- "kotlin.CharArray",
- "kotlin.ShortArray",
- "kotlin.IntArray",
- "kotlin.LongArray",
- "kotlin.FloatArray",
- "kotlin.DoubleArray"
+ "kotlin.Array",
+ "kotlin.BooleanArray",
+ "kotlin.ByteArray",
+ "kotlin.CharArray",
+ "kotlin.ShortArray",
+ "kotlin.IntArray",
+ "kotlin.LongArray",
+ "kotlin.FloatArray",
+ "kotlin.DoubleArray"
)
private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
private val iterableClasses = setOf(
- "kotlin.Collection",
- "kotlin.Sequence",
- "kotlin.Iterable",
- "kotlin.Map",
- "kotlin.String",
- "kotlin.CharSequence") + arrayOrListClasses
+ "kotlin.Collection",
+ "kotlin.Sequence",
+ "kotlin.Iterable",
+ "kotlin.Map",
+ "kotlin.String",
+ "kotlin.CharSequence"
+ ) + arrayOrListClasses
}
private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
@@ -102,53 +110,21 @@ class KotlinLanguageService : LanguageService {
fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
}
- private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper {
override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
to.text("<$typeParameterName>")
}
}
- private fun ContentBlock.renderPackage(node: DocumentationNode) {
- keyword("package")
- text(" ")
- identifier(node.name)
- }
-
- private fun <T> ContentBlock.renderList(nodes: List<T>, separator: String = ", ",
- noWrap: Boolean = false, renderItem: (T) -> Unit) {
- if (nodes.none())
- return
- renderItem(nodes.first())
- nodes.drop(1).forEach {
- if (noWrap) {
- symbol(separator.removeSuffix(" "))
- nbsp()
- } else {
- symbol(separator)
- }
- renderItem(it)
- }
- }
-
- private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode)->Unit) {
- val to = node.links.firstOrNull()
- if (to == null)
- body(node)
- else
- link(to) {
- body(node)
- }
- }
-
private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) {
node.references(RefKind.HiddenAnnotation).map { it.to }
- .find { it.name == "ParameterName" }?.let {
- val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
- identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
- symbol(":")
- nbsp()
- }
+ .find { it.name == "ParameterName" }?.let {
+ val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
+ identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
+ symbol(":")
+ nbsp()
+ }
}
private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
@@ -198,7 +174,9 @@ class KotlinLanguageService : LanguageService {
renderAnnotationsForNode(node)
}
renderModifiersForNode(node, renderMode, true)
- renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
+ renderLinked(this, node) {
+ identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
+ }
val typeArguments = node.details(NodeKind.Type)
if (typeArguments.isNotEmpty()) {
symbol("<")
@@ -213,16 +191,18 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
+ override fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean
+ ) {
when (node.name) {
- "final", "public", "var" -> {}
+ "final", "public", "var", "expect", "actual" -> {
+ }
else -> {
- keyword(node.name)
- if (nowrap) {
- nbsp()
- }
- else {
- text(" ")
+ if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
+ super.renderModifier(block, node, renderMode, nowrap)
}
}
}
@@ -238,11 +218,12 @@ class KotlinLanguageService : LanguageService {
nbsp()
symbol(":")
nbsp()
- renderList(constraints, noWrap=true) {
+ renderList(constraints, noWrap = true) {
renderType(it, renderMode)
}
}
}
+
private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
@@ -274,9 +255,12 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
- val parametersWithMultipleConstraints = node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
+ val parametersWithMultipleConstraints =
+ node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
val parametersWithConstraints = parametersWithMultipleConstraints
- .flatMap { parameter -> parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } }
+ .flatMap { parameter ->
+ parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint }
+ }
if (parametersWithMultipleConstraints.isNotEmpty()) {
keyword(" where ")
renderList(parametersWithConstraints) {
@@ -290,7 +274,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
- val supertypes = node.details(NodeKind.Supertype)
+ val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
if (supertypes.any()) {
nbsp()
symbol(":")
@@ -302,20 +286,6 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifiersForNode(node: DocumentationNode,
- renderMode: RenderMode,
- nowrap: Boolean = false) {
- val modifiers = node.details(NodeKind.Modifier)
- for (it in modifiers) {
- if (node.kind == org.jetbrains.dokka.NodeKind.Interface && it.name == "abstract")
- continue
- if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) {
- continue
- }
- renderModifier(it, nowrap)
- }
- }
-
private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
node.annotations.forEach {
renderAnnotation(it)
@@ -365,9 +335,11 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderFunction(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderFunction(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -385,7 +357,7 @@ class KotlinLanguageService : LanguageService {
renderReceiver(node, renderMode, signatureMapper)
- if (node.kind != org.jetbrains.dokka.NodeKind.Constructor)
+ if (node.kind != NodeKind.Constructor)
identifierOrDeprecated(node)
symbol("(")
@@ -401,14 +373,17 @@ class KotlinLanguageService : LanguageService {
symbol(")")
symbol(": ")
renderType(node.detail(NodeKind.Type), renderMode)
- }
- else {
+ } else {
symbol(")")
}
renderExtraTypeParameterConstraints(node, renderMode)
}
- private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) {
+ private fun ContentBlock.renderReceiver(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper?
+ ) {
val receiver = node.details(NodeKind.Receiver).singleOrNull()
if (receiver != null) {
if (signatureMapper != null) {
@@ -428,17 +403,19 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun needReturnType(node: DocumentationNode) = when(node.kind) {
+ private fun needReturnType(node: DocumentationNode) = when (node.kind) {
NodeKind.Constructor -> false
else -> !node.isUnitReturnType()
}
fun DocumentationNode.isUnitReturnType(): Boolean =
- detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
+ detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
- private fun ContentBlock.renderProperty(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderProperty(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -462,7 +439,7 @@ class KotlinLanguageService : LanguageService {
}
fun DocumentationNode.getPropertyKeyword() =
- if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
+ if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
if (node.deprecation != null) {
@@ -475,4 +452,12 @@ class KotlinLanguageService : LanguageService {
}
}
-fun DocumentationNode.qualifiedNameFromType() = (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() ?: name
+fun DocumentationNode.qualifiedNameFromType(): String {
+ return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
+ ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName()
+ ?: name
+}
+
+
+val DocumentationNode.typeDeclarationClass
+ get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)
diff --git a/core/src/main/kotlin/Languages/CommonLanguageService.kt b/core/src/main/kotlin/Languages/CommonLanguageService.kt
new file mode 100644
index 00000000..ddc95d32
--- /dev/null
+++ b/core/src/main/kotlin/Languages/CommonLanguageService.kt
@@ -0,0 +1,84 @@
+package org.jetbrains.dokka
+
+
+abstract class CommonLanguageService : LanguageService {
+
+ protected fun ContentBlock.renderPackage(node: DocumentationNode) {
+ keyword("package")
+ nbsp()
+ identifier(node.name)
+ }
+
+ override fun renderName(node: DocumentationNode): String {
+ return when (node.kind) {
+ NodeKind.Constructor -> node.owner!!.name
+ else -> node.name
+ }
+ }
+
+ open fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: LanguageService.RenderMode,
+ nowrap: Boolean = false
+ ) = with(block) {
+ keyword(node.name)
+ if (nowrap) {
+ nbsp()
+ } else {
+ text(" ")
+ }
+ }
+
+ protected fun renderLinked(
+ block: ContentBlock,
+ node: DocumentationNode,
+ body: ContentBlock.(DocumentationNode) -> Unit
+ ) = with(block) {
+ val to = node.links.firstOrNull()
+ if (to == null)
+ body(node)
+ else
+ link(to) {
+ this.body(node)
+ }
+ }
+
+ protected fun <T> ContentBlock.renderList(
+ nodes: List<T>, separator: String = ", ",
+ noWrap: Boolean = false, renderItem: (T) -> Unit
+ ) {
+ if (nodes.none())
+ return
+ renderItem(nodes.first())
+ nodes.drop(1).forEach {
+ if (noWrap) {
+ symbol(separator.removeSuffix(" "))
+ nbsp()
+ } else {
+ symbol(separator)
+ }
+ renderItem(it)
+ }
+ }
+
+ abstract fun showModifierInSummary(node: DocumentationNode): Boolean
+
+ protected fun ContentBlock.renderModifiersForNode(
+ node: DocumentationNode,
+ renderMode: LanguageService.RenderMode,
+ nowrap: Boolean = false
+ ) {
+ val modifiers = node.details(NodeKind.Modifier)
+ for (it in modifiers) {
+ if (node.kind == NodeKind.Interface && it.name == "abstract")
+ continue
+ if (renderMode == LanguageService.RenderMode.SUMMARY && !showModifierInSummary(it)) {
+ continue
+ }
+ renderModifier(this, it, renderMode, nowrap)
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
new file mode 100644
index 00000000..992cd090
--- /dev/null
+++ b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
@@ -0,0 +1,197 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.LanguageService.RenderMode
+
+/**
+ * Implements [LanguageService] and provides rendering of symbols in Java language
+ */
+class NewJavaLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return true
+ }
+
+ override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
+ return content {
+ (when (node.kind) {
+ NodeKind.Package -> renderPackage(node)
+ in NodeKind.classLike -> renderClass(node, renderMode)
+
+ NodeKind.Modifier -> renderModifier(this, node, renderMode)
+ NodeKind.TypeParameter -> renderTypeParameter(node)
+ NodeKind.Type,
+ NodeKind.UpperBound -> renderType(node)
+ NodeKind.Parameter -> renderParameter(node)
+ NodeKind.Constructor,
+ NodeKind.Function -> renderFunction(node)
+ NodeKind.Property -> renderProperty(node)
+ else -> "${node.kind}: ${node.name}"
+ })
+ }
+ }
+
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
+
+
+ override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) {
+ when (node.name) {
+ "open", "internal" -> {
+ }
+ else -> super.renderModifier(block, node, renderMode, nowrap)
+ }
+ }
+
+ fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et }
+ ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type)
+
+ else -> null
+ }
+
+ fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ 1
+ else -> 0
+ }
+
+ fun ContentBlock.renderType(node: DocumentationNode) {
+ when (node.name) {
+ "Unit" -> identifier("void")
+ "Int" -> identifier("int")
+ "Long" -> identifier("long")
+ "Double" -> identifier("double")
+ "Float" -> identifier("float")
+ "Char" -> identifier("char")
+ "Boolean" -> identifier("bool")
+ // TODO: render arrays
+ else -> renderLinked(this, node) {
+ identifier(node.name)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderTypeParameter(node: DocumentationNode) {
+ val constraints = node.details(NodeKind.UpperBound)
+ if (constraints.none())
+ identifier(node.name)
+ else {
+ identifier(node.name)
+ text(" ")
+ keyword("extends")
+ text(" ")
+ constraints.forEach { renderType(node) }
+ }
+ }
+
+ private fun ContentBlock.renderParameter(node: DocumentationNode) {
+ renderType(node.detail(NodeKind.Type))
+ text(" ")
+ identifier(node.name)
+ }
+
+ private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) {
+ val typeParameters = node.details(NodeKind.TypeParameter)
+ if (typeParameters.any()) {
+ symbol("<")
+ renderList(typeParameters, noWrap = true) {
+ renderTypeParameter(it)
+ }
+ symbol(">")
+ text(" ")
+ }
+ }
+
+// private fun renderModifiersForNode(node: DocumentationNode): String {
+// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" }
+// if (modifiers.none())
+// return ""
+// return modifiers.joinToString(" ", postfix = " ")
+// }
+
+ private fun ContentBlock.renderClassKind(node: DocumentationNode) {
+ when (node.kind) {
+ NodeKind.Interface -> {
+ keyword("interface")
+ }
+ NodeKind.EnumItem -> {
+ keyword("enum value")
+ }
+ NodeKind.Enum -> {
+ keyword("enum")
+ }
+ NodeKind.Class, NodeKind.Exception, NodeKind.Object -> {
+ keyword("class")
+ }
+ else -> throw IllegalArgumentException("Node $node is not a class-like object")
+ }
+ text(" ")
+ }
+
+ private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode)
+ renderClassKind(node)
+
+ identifier(node.name)
+ renderTypeParametersForNode(node)
+ }
+
+ private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) {
+ renderList(nodes) {
+ renderParameter(it)
+ }
+ }
+
+ private fun ContentBlock.renderFunction(node: DocumentationNode) {
+ when (node.kind) {
+ NodeKind.Constructor -> identifier(node.owner?.name ?: "")
+ NodeKind.Function -> {
+ renderTypeParametersForNode(node)
+ renderType(node.detail(NodeKind.Type))
+ text(" ")
+ identifier(node.name)
+
+ }
+ else -> throw IllegalArgumentException("Node $node is not a function-like object")
+ }
+
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ symbol("(")
+ if (receiver != null)
+ renderParameters(listOf(receiver) + node.details(NodeKind.Parameter))
+ else
+ renderParameters(node.details(NodeKind.Parameter))
+
+ symbol(")")
+ }
+
+ private fun ContentBlock.renderProperty(node: DocumentationNode) {
+
+ when (node.kind) {
+ NodeKind.Property -> {
+ keyword("val")
+ text(" ")
+ }
+ else -> throw IllegalArgumentException("Node $node is not a property")
+ }
+ renderTypeParametersForNode(node)
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ if (receiver != null) {
+ renderType(receiver.detail(NodeKind.Type))
+ symbol(".")
+ }
+
+ identifier(node.name)
+ symbol(":")
+ text(" ")
+ renderType(node.detail(NodeKind.Type))
+
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Locations/FoldersLocationService.kt b/core/src/main/kotlin/Locations/FoldersLocationService.kt
deleted file mode 100644
index 83e1cf6a..00000000
--- a/core/src/main/kotlin/Locations/FoldersLocationService.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.jetbrains.dokka
-
-import com.google.inject.Inject
-import com.google.inject.name.Named
-import java.io.File
-
-class FoldersLocationService @Inject constructor(@Named("outputDir") val rootFile: File, val extension: String) : FileLocationService {
- constructor(root: String): this(File(root), "")
-
- override val root: Location
- get() = FileLocation(rootFile)
-
- override fun withExtension(newExtension: String): FileLocationService {
- return if (extension.isEmpty()) FoldersLocationService(rootFile, newExtension) else this
- }
-
- override fun location(qualifiedName: List<String>, hasMembers: Boolean): FileLocation {
- return FileLocation(File(rootFile, relativePathToNode(qualifiedName, hasMembers)).appendExtension(extension))
- }
-}
-
-fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String {
- val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() }
- return if (!hasMembers) {
- // leaf node, use file in owner's folder
- parts.joinToString("/")
- } else {
- parts.joinToString("/") + (if (parts.none()) "" else "/") + "index"
- }
-}
diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt
new file mode 100644
index 00000000..4cb0ac39
--- /dev/null
+++ b/core/src/main/kotlin/Locations/Location.kt
@@ -0,0 +1,61 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+interface Location {
+ val path: String get
+ fun relativePathTo(other: Location, anchor: String? = null): String
+}
+
+/**
+ * Represents locations in the documentation in the form of [path](File).
+ *
+ * $file: [File] for this location
+ * $path: [String] representing path of this location
+ */
+data class FileLocation(val file: File) : Location {
+ override val path: String
+ get() = file.path
+
+ override fun relativePathTo(other: Location, anchor: String?): String {
+ if (other !is FileLocation) {
+ throw IllegalArgumentException("$other is not a FileLocation")
+ }
+ if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) {
+ return "./${file.name}"
+ }
+ val ownerFolder = file.parentFile!!
+ val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/')
+ return if (anchor == null) relativePath else relativePath + "#" + anchor
+ }
+}
+
+fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String {
+ val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() }
+ return if (!hasMembers) {
+ // leaf node, use file in owner's folder
+ parts.joinToString("/")
+ } else {
+ parts.joinToString("/") + (if (parts.none()) "" else "/") + "index"
+ }
+}
+
+
+fun relativePathToNode(node: DocumentationNode) = relativePathToNode(node.path.map { it.name }, node.members.any())
+
+fun identifierToFilename(path: String): String {
+ val escaped = path.replace('<', '-').replace('>', '-')
+ val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
+ return if (lowercase == "index") "--index--" else lowercase
+}
+
+fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String {
+ return location(owner).relativePathTo(location(node), null)
+}
+
+fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File {
+ val file = File(from.path).parentFile
+ return root.relativeTo(file)
+}
+
+fun File.toUnixString() = toString().replace(File.separatorChar, '/')
diff --git a/core/src/main/kotlin/Locations/LocationService.kt b/core/src/main/kotlin/Locations/LocationService.kt
deleted file mode 100644
index a51ef8d4..00000000
--- a/core/src/main/kotlin/Locations/LocationService.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.jetbrains.dokka
-
-import java.io.File
-
-interface Location {
- val path: String get
- fun relativePathTo(other: Location, anchor: String? = null): String
-}
-
-/**
- * Represents locations in the documentation in the form of [path](File).
- *
- * Locations are provided by [LocationService.location] function.
- *
- * $file: [File] for this location
- * $path: [String] representing path of this location
- */
-data class FileLocation(val file: File): Location {
- override val path : String
- get() = file.path
-
- override fun relativePathTo(other: Location, anchor: String?): String {
- if (other !is FileLocation) {
- throw IllegalArgumentException("$other is not a FileLocation")
- }
- if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) {
- return "./${file.name}"
- }
- val ownerFolder = file.parentFile!!
- val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/')
- return if (anchor == null) relativePath else relativePath + "#" + anchor
- }
-}
-
-/**
- * Provides means of retrieving locations for [DocumentationNode](documentation nodes)
- *
- * `LocationService` determines where documentation for particular node should be generated
- *
- * * [FoldersLocationService] – represent packages and types as folders, members as files in those folders.
- * * [SingleFolderLocationService] – all documentation is generated into single folder using fully qualified names
- * for file names.
- */
-interface LocationService {
- fun withExtension(newExtension: String) = this
-
- fun location(node: DocumentationNode): Location = location(node.path.map { it.name }, node.members.any())
-
- /**
- * Calculates a location corresponding to the specified [qualifiedName].
- * @param hasMembers if true, the node for which the location is calculated has member nodes.
- */
- fun location(qualifiedName: List<String>, hasMembers: Boolean): Location
-
- val root: Location
-}
-
-
-interface FileLocationService: LocationService {
- override fun withExtension(newExtension: String): FileLocationService = this
-
- override fun location(node: DocumentationNode): FileLocation = location(node.path.map { it.name }, node.members.any())
- override fun location(qualifiedName: List<String>, hasMembers: Boolean): FileLocation
-}
-
-
-fun identifierToFilename(path: String): String {
- val escaped = path.replace('<', '-').replace('>', '-')
- val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
- return if (lowercase == "index") "--index--" else lowercase
-}
-
-/**
- * Returns relative location between two nodes. Used for relative links in documentation.
- */
-fun LocationService.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String {
- return location(owner).relativePathTo(location(node), null)
-}
diff --git a/core/src/main/kotlin/Locations/SingleFolderLocationService.kt b/core/src/main/kotlin/Locations/SingleFolderLocationService.kt
deleted file mode 100644
index 1b4fdc28..00000000
--- a/core/src/main/kotlin/Locations/SingleFolderLocationService.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.jetbrains.dokka
-
-import com.google.inject.Inject
-import com.google.inject.name.Named
-import java.io.File
-
-class SingleFolderLocationService @Inject constructor(@Named("outputDir") val rootFile: File, val extension: String) : FileLocationService {
- constructor(root: String): this(File(root), "")
-
- override fun withExtension(newExtension: String): FileLocationService =
- SingleFolderLocationService(rootFile, newExtension)
-
- override fun location(qualifiedName: List<String>, hasMembers: Boolean): FileLocation {
- val filename = qualifiedName.map { identifierToFilename(it) }.joinToString("-")
- return FileLocation(File(rootFile, filename).appendExtension(extension))
- }
-
- override val root: Location
- get() = FileLocation(rootFile)
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
index 1f5bbc83..7c776bdb 100644
--- a/core/src/main/kotlin/Model/Content.kt
+++ b/core/src/main/kotlin/Model/Content.kt
@@ -9,7 +9,7 @@ object ContentEmpty : ContentNode {
}
open class ContentBlock() : ContentNode {
- val children = arrayListOf<ContentNode>()
+ open val children = arrayListOf<ContentNode>()
fun append(node: ContentNode) {
children.add(node)
@@ -27,6 +27,34 @@ open class ContentBlock() : ContentNode {
get() = children.sumBy { it.textLength }
}
+class NodeRenderContent(
+ val node: DocumentationNode,
+ val mode: LanguageService.RenderMode
+): ContentNode {
+ override val textLength: Int
+ get() = 0 //TODO: Clarify?
+}
+
+class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() {
+ private var computed = false
+ override val children: ArrayList<ContentNode>
+ get() {
+ if (!computed) {
+ computed = true
+ fillChildren(this)
+ }
+ return super.children
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode() + 31 * fillChildren.hashCode()
+ }
+}
+
enum class IdentifierKind {
TypeName,
ParameterName,
@@ -120,6 +148,9 @@ class ContentExternalLink(val href : String) : ContentBlock() {
children.hashCode() * 31 + href.hashCode()
}
+data class ContentBookmark(val name: String): ContentBlock()
+data class ContentLocalLink(val href: String) : ContentBlock()
+
class ContentUnorderedList() : ContentBlock()
class ContentOrderedList() : ContentBlock()
class ContentListItem() : ContentBlock()
diff --git a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt b/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
deleted file mode 100644
index 85584e3c..00000000
--- a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.jetbrains.dokka.Model
-
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-interface DescriptorSignatureProvider {
- fun signature(forDesc: DeclarationDescriptor): String
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index da85cabf..f2b3a937 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,
+ QualifiedName,
Platform,
AllTypes,
@@ -62,7 +63,7 @@ enum class NodeKind {
companion object {
val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
- val memberLike = setOf(Function, Property, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
+ val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
}
}
@@ -75,7 +76,11 @@ open class DocumentationNode(val name: String,
var content: Content = content
private set
- val summary: ContentNode get() = content.summary
+ val summary: ContentNode get() = when (kind) {
+ NodeKind.GroupNode -> this.origins.first().summary
+ else -> content.summary
+ }
+
val owner: DocumentationNode?
get() = references(RefKind.Owner).singleOrNull()?.to
@@ -83,8 +88,13 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Detail).map { it.to }
val members: List<DocumentationNode>
get() = references(RefKind.Member).map { it.to }
+ val origins: List<DocumentationNode>
+ get() = references(RefKind.Origin).map { it.to }
+
val inheritedMembers: List<DocumentationNode>
get() = references(RefKind.InheritedMember).map { it.to }
+ val allInheritedMembers: List<DocumentationNode>
+ get() = recursiveInheritedMembers()
val inheritedCompanionObjectMembers: List<DocumentationNode>
get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
val extensions: List<DocumentationNode>
@@ -103,12 +113,38 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Deprecation).singleOrNull()?.to
val platforms: List<String>
get() = references(RefKind.Platform).map { it.to.name }
+ val externalType: DocumentationNode?
+ get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
+
+ val supertypes: List<DocumentationNode>
+ get() = details(NodeKind.Supertype)
+
+ val superclassType: DocumentationNode?
+ get() = when (kind) {
+ NodeKind.Supertype -> {
+ (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType
+ }
+ NodeKind.Interface -> null
+ in NodeKind.classLike -> supertypes.firstOrNull {
+ (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) }
+ }
+ else -> null
+ }
+
+ val superclassTypeSequence: Sequence<DocumentationNode>
+ get() = generateSequence(superclassType) {
+ it.superclassType
+ }
// 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 addReference(reference: DocumentationReference) {
+ references.add(reference)
+ }
+
fun dropReferences(predicate: (DocumentationReference) -> Boolean) {
references.removeAll(predicate)
}
@@ -123,7 +159,6 @@ open class DocumentationNode(val name: String,
}
(content as MutableContent).body()
}
-
fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind }
fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind }
fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
@@ -135,6 +170,7 @@ open class DocumentationNode(val name: String,
fun member(kind: NodeKind): DocumentationNode = members.filter { it.kind == kind }.single()
fun link(kind: NodeKind): DocumentationNode = links.filter { it.kind == kind }.single()
+
fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind }
fun allReferences(): Set<DocumentationReference> = references
@@ -143,9 +179,9 @@ open class DocumentationNode(val name: String,
}
}
-class DocumentationModule(name: String, content: Content = Content.Empty)
+class DocumentationModule(name: String, content: Content = Content.Empty, val nodeRefGraph: NodeReferenceGraph = NodeReferenceGraph())
: DocumentationNode(name, content, NodeKind.Module) {
- val nodeRefGraph = NodeReferenceGraph()
+
}
val DocumentationNode.path: List<DocumentationNode>
@@ -154,16 +190,17 @@ val DocumentationNode.path: List<DocumentationNode>
return parent.path + this
}
-fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
- val existingNode = members(NodeKind.Package).firstOrNull { it.name == packageName }
+fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
+ val existingNode = refGraph.lookup(packageName)
if (existingNode != null) {
return existingNode
}
val newNode = DocumentationNode(packageName,
packageContent.getOrElse(packageName) { Content.Empty },
NodeKind.Package)
- append(newNode, RefKind.Member)
+
refGraph.register(packageName, newNode)
+ module?.append(newNode, RefKind.Member)
return newNode
}
@@ -173,7 +210,9 @@ fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Member -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Owner -> child.addReferenceTo(this, RefKind.Member)
- else -> { /* Do not add any links back for other types */ }
+ RefKind.Origin -> child.addReferenceTo(this, RefKind.Owner)
+ else -> { /* Do not add any links back for other types */
+ }
}
}
@@ -186,8 +225,37 @@ fun DocumentationNode.appendTextNode(text: String,
fun DocumentationNode.qualifiedName(): String {
if (kind == NodeKind.Type) {
return qualifiedNameFromType()
+ } else if (kind == NodeKind.Package) {
+ return name
}
return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
}
fun DocumentationNode.simpleName() = name.substringAfterLast('.')
+
+private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
+ val allInheritedMembers = mutableListOf<DocumentationNode>()
+ recursiveInheritedMembers(allInheritedMembers)
+ return allInheritedMembers
+}
+
+private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
+ allInheritedMembers.addAll(inheritedMembers)
+ System.out.println(allInheritedMembers.size)
+ inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) ->
+ node.recursiveInheritedMembers(allInheritedMembers)
+ }
+}
+
+private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean {
+ return when(node.kind) {
+ NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class
+ NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception
+ else -> false
+ }
+}
+
+fun DocumentationNode.classNodeNameWithOuterClass(): String {
+ assert(kind in NodeKind.classLike)
+ return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index a968f400..e10796d2 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,42 +18,83 @@ enum class RefKind {
HiddenAnnotation,
Deprecation,
TopLevelPage,
- Platform
+ Platform,
+ ExternalType,
+ Origin
}
data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
}
-class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?,
- val lazyNodeTo: () -> DocumentationNode?,
+sealed class NodeResolver {
+ abstract fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode?
+ class BySignature(var signature: String) : NodeResolver() {
+ override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? {
+ return nodeRephGraph.lookup(signature)
+ }
+ }
+
+ class Exact(var exactNode: DocumentationNode) : NodeResolver() {
+ override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? {
+ return exactNode
+ }
+ }
+}
+
+class PendingDocumentationReference(val lazyNodeFrom: NodeResolver,
+ val lazyNodeTo: NodeResolver,
val kind: RefKind) {
- fun resolve() {
- val fromNode = lazyNodeFrom()
- val toNode = lazyNodeTo()
+ fun resolve(nodeRephGraph: NodeReferenceGraph) {
+ val fromNode = lazyNodeFrom.resolve(nodeRephGraph)
+ val toNode = lazyNodeTo.resolve(nodeRephGraph)
if (fromNode != null && toNode != null) {
fromNode.addReferenceTo(toNode, kind)
}
}
}
-class NodeReferenceGraph() {
+class NodeReferenceGraph {
private val nodeMap = hashMapOf<String, DocumentationNode>()
+ val nodeMapView: Map<String, DocumentationNode>
+ get() = HashMap(nodeMap)
+
val references = arrayListOf<PendingDocumentationReference>()
fun register(signature: String, node: DocumentationNode) {
- nodeMap.put(signature, node)
+ nodeMap[signature] = node
}
fun link(fromNode: DocumentationNode, toSignature: String, kind: RefKind) {
- references.add(PendingDocumentationReference({ -> fromNode}, { -> nodeMap[toSignature]}, kind))
+ references.add(
+ PendingDocumentationReference(
+ NodeResolver.Exact(fromNode),
+ NodeResolver.BySignature(toSignature),
+ kind
+ ))
}
fun link(fromSignature: String, toNode: DocumentationNode, kind: RefKind) {
- references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> toNode}, kind))
+ references.add(
+ PendingDocumentationReference(
+ NodeResolver.BySignature(fromSignature),
+ NodeResolver.Exact(toNode),
+ kind
+ )
+ )
}
fun link(fromSignature: String, toSignature: String, kind: RefKind) {
- references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind))
+ references.add(
+ PendingDocumentationReference(
+ NodeResolver.BySignature(fromSignature),
+ NodeResolver.BySignature(toSignature),
+ kind
+ )
+ )
+ }
+
+ fun addReference(reference: PendingDocumentationReference) {
+ references.add(reference)
}
fun lookup(signature: String) = nodeMap[signature]
@@ -61,13 +102,13 @@ class NodeReferenceGraph() {
fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? {
val result = nodeMap[signature]
if (result == null) {
- logger.warn("Can't find node by signature $signature")
+ logger.warn("Can't find node by signature `$signature`")
}
return result
}
fun resolveReferences() {
- references.forEach { it.resolve() }
+ references.forEach { it.resolve(this) }
}
}
diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
new file mode 100644
index 00000000..e8fdde6e
--- /dev/null
+++ b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+
+interface ElementSignatureProvider {
+ fun signature(forDesc: DeclarationDescriptor): String
+ fun signature(forPsi: PsiElement): String
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt
index 1f6bdcb9..5b628914 100644
--- a/core/src/main/kotlin/Model/PackageDocs.kt
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -2,16 +2,25 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiFileFactory
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.LocalTimeCounter
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import java.io.File
@Singleton
class PackageDocs
@Inject constructor(val linkResolver: DeclarationLinkResolver?,
- val logger: DokkaLogger)
+ val logger: DokkaLogger,
+ val environment: KotlinCoreEnvironment,
+ val refGraph: NodeReferenceGraph,
+ val elementSignatureProvider: ElementSignatureProvider)
{
val moduleContent: MutableContent = MutableContent()
private val _packageContent: MutableMap<String, MutableContent> = hashMapOf()
@@ -40,6 +49,64 @@ class PackageDocs
}
}
+ private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) {
+ val javadocText = text
+ .replace("*/", "*&#47;")
+ .removeSurrounding("<html>", "</html>", true).trim()
+ .removeSurrounding("<body>", "</body>", true)
+ .lineSequence()
+ .map { "* $it" }
+ .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/")
+ parseJavadoc(javadocText, packageName, file)
+ }
+
+ private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence {
+ if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) {
+ return subSequence(prefix.length, length - suffix.length)
+ }
+ return subSequence(0, length)
+ }
+
+
+ private fun parseJavadoc(text: String, packageName: String, file: File) {
+
+ val psiFileFactory = PsiFileFactory.getInstance(environment.project)
+ val psiFile = psiFileFactory.createFileFromText(
+ file.nameWithoutExtension + ".java",
+ JavaFileType.INSTANCE,
+ "package $packageName; $text\npublic class C {}",
+ LocalTimeCounter.currentTime(),
+ false,
+ true
+ )
+
+ val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!!
+ val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!)
+ findOrCreatePackageContent(packageName).apply {
+ val content = parser.parseDocumentation(psiClass).content
+ children.addAll(content.children)
+ content.sections.forEach {
+ addSection(it.tag, it.subjectName).children.addAll(it.children)
+ }
+ }
+ }
+
+
+ fun parseJava(fileName: String, packageName: String) {
+ val file = File(fileName)
+ if (file.exists()) {
+ val text = file.readText()
+
+ val trimmedText = text.trim()
+
+ if (trimmedText.startsWith("/**")) {
+ parseJavadoc(text, packageName, file)
+ } else if (trimmedText.toLowerCase().startsWith("<html>")) {
+ parseHtmlAsJavadoc(trimmedText, packageName, file)
+ }
+ }
+ }
+
private fun findTargetContent(heading: String): MutableContent {
if (heading.startsWith("Module") || heading.startsWith("module")) {
return moduleContent
diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
index 116a5c02..f3f45c3f 100644
--- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
+++ b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
open class DefaultSampleProcessingService
-@Inject constructor(val options: DocumentationOptions,
+@Inject constructor(val configuration: DokkaConfiguration,
val logger: DokkaLogger,
val resolutionFacade: DokkaResolutionFacade)
: SampleProcessingService {
diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
index f928b44f..4525e9d9 100644
--- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
+++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
@@ -18,10 +18,10 @@ import java.io.StringWriter
open class KotlinWebsiteSampleProcessingService
-@Inject constructor(options: DocumentationOptions,
+@Inject constructor(dokkaConfiguration: DokkaConfiguration,
logger: DokkaLogger,
resolutionFacade: DokkaResolutionFacade)
- : DefaultSampleProcessingService(options, logger, resolutionFacade) {
+ : DefaultSampleProcessingService(dokkaConfiguration, logger, resolutionFacade) {
private class SampleBuilder : KtTreeVisitorVoid() {
val builder = StringBuilder()
diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
index dfb114ec..c2e652b6 100644
--- a/core/src/main/kotlin/Utilities/DokkaModules.kt
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -1,90 +1,63 @@
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.*
+import com.google.inject.binder.AnnotatedBindingBuilder
import com.google.inject.name.Names
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Formats.FormatDescriptor
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import java.io.File
+import kotlin.reflect.KClass
const val impliedPlatformsName = "impliedPlatforms"
+class DokkaRunModule(val configuration: DokkaConfiguration) : Module {
+ override fun configure(binder: Binder) {
+ binder.bind<DokkaConfiguration>().toInstance(configuration)
+ binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(configuration.impliedPlatforms)
+
+ binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(configuration.outputDir))
+ }
+
+}
+
class DokkaAnalysisModule(val environment: AnalysisEnvironment,
- val options: DocumentationOptions,
+ val configuration: DokkaConfiguration,
val defaultPlatformsProvider: DefaultPlatformsProvider,
val nodeReferenceGraph: NodeReferenceGraph,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val logger: DokkaLogger) : Module {
override fun configure(binder: Binder) {
binder.bind<DokkaLogger>().toInstance(logger)
- val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
- binder.bind<DescriptorSignatureProvider>().to(descriptor.descriptorSignatureProvider.java)
- 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)
+ val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment)
binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade)
+ binder.bind<DokkaResolutionFacade>().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade)
- binder.bind<DocumentationOptions>().toInstance(options)
+ binder.bind<DokkaConfiguration.PassConfiguration>().toInstance(passConfiguration)
binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider)
binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph)
+
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format)
+ descriptor.configureAnalysis(binder)
}
}
object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>()
-class DokkaOutputModule(val options: DocumentationOptions,
+class DokkaOutputModule(val configuration: DokkaConfiguration,
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")
- binder.bindNameAnnotated<FileLocationService, SingleFolderLocationService>("singleFolder")
- binder.bindNameAnnotated<LocationService, FoldersLocationService>("folders")
- binder.bindNameAnnotated<FileLocationService, FoldersLocationService>("folders")
-
- // defaults
- binder.bind(LocationService::class.java).to(FoldersLocationService::class.java)
- binder.bind(FileLocationService::class.java).to(FoldersLocationService::class.java)
-
- binder.registerCategory<OutlineFormatService>("outline")
- binder.registerCategory<FormatService>("format")
- binder.registerCategory<Generator>("generator")
-
- val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
-
- descriptor.outlineServiceClass?.let { clazz ->
- binder.bind(OutlineFormatService::class.java).to(clazz.java)
- }
- descriptor.formatServiceClass?.let { clazz ->
- binder.bind(FormatService::class.java).to(clazz.java)
- }
-
- binder.bind<Generator>().to(descriptor.generatorServiceClass.java)
+ binder.bind<DokkaLogger>().toInstance(logger)
- descriptor.packageListServiceClass?.let { binder.bind<PackageListService>().to(it.java) }
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format)
- binder.bind<DocumentationOptions>().toInstance(options)
- binder.bind<DokkaLogger>().toInstance(logger)
- binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms)
+ descriptor.configureOutput(binder)
}
}
@@ -100,4 +73,11 @@ private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotat
}
-inline fun <reified T: Any> Binder.bind() = bind(T::class.java)
+inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java)
+
+inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) }
+
+inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) =
+ kClass?.let { value toType it }
+
+inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java)
diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/Utilities/Html.kt
index a5a93d9e..de1ce1a5 100644
--- a/core/src/main/kotlin/Utilities/Html.kt
+++ b/core/src/main/kotlin/Utilities/Html.kt
@@ -1,8 +1,12 @@
package org.jetbrains.dokka
+import java.net.URLEncoder
+
/**
* Replaces symbols reserved in HTML with their respective entities.
* Replaces & with &amp;, < with &lt; and > with &gt;
*/
fun String.htmlEscape(): String = replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8") \ No newline at end of file
diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/Utilities/ServiceLocator.kt
index 71bfd21b..fca08f38 100644
--- a/core/src/main/kotlin/Utilities/ServiceLocator.kt
+++ b/core/src/main/kotlin/Utilities/ServiceLocator.kt
@@ -14,12 +14,21 @@ class ServiceLookupException(message: String) : Exception(message)
object ServiceLocator {
fun <T : Any> lookup(clazz: Class<T>, category: String, implementationName: String): T {
val descriptor = lookupDescriptor(category, implementationName)
+ return lookup(clazz, descriptor)
+ }
+
+ fun <T : Any> lookup(
+ clazz: Class<T>,
+ descriptor: ServiceDescriptor
+ ): T {
val loadedClass = javaClass.classLoader.loadClass(descriptor.className)
val constructor = loadedClass.constructors
- .filter { it.parameterTypes.isEmpty() }
- .firstOrNull() ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
+ .filter { it.parameterTypes.isEmpty() }
+ .firstOrNull()
+ ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
- val implementationRawType: Any = if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor)
+ val implementationRawType: Any =
+ if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor)
if (!clazz.isInstance(implementationRawType)) {
throw ServiceLookupException("Class ${descriptor.className} is not a subtype of ${clazz.name}")
@@ -61,7 +70,7 @@ object ServiceLocator {
"jar" -> {
val file = JarFile(URL(it.file.substringBefore("!")).toFile())
try {
- val jarPath = it.file.substringAfterLast("!").removePrefix("/")
+ val jarPath = it.file.substringAfterLast("!").removeSurrounding("/")
file.entries()
.asSequence()
.filter { entry -> !entry.isDirectory && entry.path == jarPath && entry.extension == "properties" }
@@ -79,6 +88,7 @@ object ServiceLocator {
}
inline fun <reified T : Any> ServiceLocator.lookup(category: String, implementationName: String): T = lookup(T::class.java, category, implementationName)
+inline fun <reified T : Any> ServiceLocator.lookup(desc: ServiceDescriptor): T = lookup(T::class.java, desc)
private val ZipEntry.fileName: String
get() = name.substringAfterLast("/", name)
diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/Utilities/Uri.kt
new file mode 100644
index 00000000..9827c624
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/Uri.kt
@@ -0,0 +1,40 @@
+package org.jetbrains.dokka
+
+import java.net.URI
+
+
+fun URI.relativeTo(uri: URI): URI {
+ // Normalize paths to remove . and .. segments
+ val base = uri.normalize()
+ val child = this.normalize()
+
+ fun StringBuilder.appendRelativePath() {
+ // Split paths into segments
+ var bParts = base.path.split('/').dropLastWhile { it.isEmpty() }
+ val cParts = child.path.split('/').dropLastWhile { it.isEmpty() }
+
+ // Discard trailing segment of base path
+ if (bParts.isNotEmpty() && !base.path.endsWith("/")) {
+ bParts = bParts.dropLast(1)
+ }
+
+ // Compute common prefix
+ val commonPartsSize = bParts.zip(cParts).takeWhile { (basePart, childPart) -> basePart == childPart }.count()
+ bParts.drop(commonPartsSize).joinTo(this, separator = "") { "../" }
+ cParts.drop(commonPartsSize).joinTo(this, separator = "/")
+ }
+
+ return URI.create(buildString {
+ if (base.path != child.path) {
+ appendRelativePath()
+ }
+ child.rawQuery?.let {
+ append("?")
+ append(it)
+ }
+ child.rawFragment?.let {
+ append("#")
+ append(it)
+ }
+ })
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/javadoc/dokka-adapters.kt b/core/src/main/kotlin/javadoc/dokka-adapters.kt
index c98a3801..1329876a 100644
--- a/core/src/main/kotlin/javadoc/dokka-adapters.kt
+++ b/core/src/main/kotlin/javadoc/dokka-adapters.kt
@@ -1,21 +1,22 @@
package org.jetbrains.dokka.javadoc
+import com.google.inject.Binder
import com.google.inject.Inject
import com.sun.tools.doclets.formats.html.HtmlDoclet
import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.DefaultAnalysisComponent
+import org.jetbrains.dokka.Formats.DefaultAnalysisComponentServices
import org.jetbrains.dokka.Formats.FormatDescriptor
-import org.jetbrains.dokka.Kotlin.KotlinAsJavaDescriptorSignatureProvider
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
-import kotlin.reflect.KClass
+import org.jetbrains.dokka.Formats.KotlinAsJava
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.toType
-class JavadocGenerator @Inject constructor(val options: DocumentationOptions, val logger: DokkaLogger) : Generator {
+class JavadocGenerator @Inject constructor(val configuration: DokkaConfiguration, val logger: DokkaLogger) : Generator {
override fun buildPages(nodes: Iterable<DocumentationNode>) {
val module = nodes.single() as DocumentationModule
- DokkaConsoleLogger.report()
- HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), options.outputDir))
+ HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), configuration.outputDir))
}
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
@@ -30,13 +31,12 @@ class JavadocGenerator @Inject constructor(val options: DocumentationOptions, va
}
}
-class JavadocFormatDescriptor : FormatDescriptor {
- override val formatServiceClass = null
- override val outlineServiceClass = null
- override val generatorServiceClass = JavadocGenerator::class
- override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
- override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
- override val sampleProcessingService = DefaultSampleProcessingService::class
- override val packageListServiceClass: KClass<out PackageListService>? = null
- override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::class
+class JavadocFormatDescriptor :
+ FormatDescriptor,
+ DefaultAnalysisComponent,
+ DefaultAnalysisComponentServices by KotlinAsJava {
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType JavadocGenerator::class
+ }
}
diff --git a/core/src/main/resources/dokka/format/java-layout-html.properties b/core/src/main/resources/dokka/format/java-layout-html.properties
new file mode 100644
index 00000000..fbb2bbed
--- /dev/null
+++ b/core/src/main/resources/dokka/format/java-layout-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
+description=Produces Kotlin Style Docs with Javadoc like layout \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/javadoc.properties b/core/src/main/resources/dokka/format/javadoc.properties
index a58317fc..a0d8a945 100644
--- a/core/src/main/resources/dokka/format/javadoc.properties
+++ b/core/src/main/resources/dokka/format/javadoc.properties
@@ -1 +1,2 @@
-class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor \ No newline at end of file
+class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor
+description=Produces Javadoc, with Kotlin declarations as Java view \ No newline at end of file
diff --git a/core/src/main/resources/dokka/generator/default.properties b/core/src/main/resources/dokka/generator/default.properties
deleted file mode 100644
index a4a16200..00000000
--- a/core/src/main/resources/dokka/generator/default.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-class=org.jetbrains.dokka.FileGenerator
-description=Default documentation generator \ No newline at end of file
diff --git a/core/src/main/resources/dokka/generator/javadoc.properties b/core/src/main/resources/dokka/generator/javadoc.properties
deleted file mode 100644
index 4075704f..00000000
--- a/core/src/main/resources/dokka/generator/javadoc.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-class=org.jetbrains.dokka.javadoc.JavadocGenerator
-description=Produces output via JDK javadoc tool \ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties
new file mode 100644
index 00000000..c484a920
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Dokka
+description=Uses Dokka Default resolver \ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
new file mode 100644
index 00000000..3b61eabe
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlInboundLinkResolutionService
+description=Resolver for JavaLayoutHtml \ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties
new file mode 100644
index 00000000..0d5d7d17
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Javadoc
+description=Uses Javadoc Default resolver \ No newline at end of file
diff --git a/core/src/main/resources/dokka/language/java.properties b/core/src/main/resources/dokka/language/java.properties
deleted file mode 100644
index ab42f532..00000000
--- a/core/src/main/resources/dokka/language/java.properties
+++ /dev/null
@@ -1 +0,0 @@
-class=org.jetbrains.dokka.JavaLanguageService \ No newline at end of file
diff --git a/core/src/main/resources/dokka/language/kotlin.properties b/core/src/main/resources/dokka/language/kotlin.properties
deleted file mode 100644
index 16092007..00000000
--- a/core/src/main/resources/dokka/language/kotlin.properties
+++ /dev/null
@@ -1 +0,0 @@
-class=org.jetbrains.dokka.KotlinLanguageService \ No newline at end of file
diff --git a/core/src/main/resources/dokka/outline/yaml.properties b/core/src/main/resources/dokka/outline/yaml.properties
deleted file mode 100644
index 7268af37..00000000
--- a/core/src/main/resources/dokka/outline/yaml.properties
+++ /dev/null
@@ -1 +0,0 @@
-class=org.jetbrains.dokka.YamlOutlineService \ No newline at end of file
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index ff8a5260..8974bd0b 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -6,8 +6,8 @@ import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
+import org.jetbrains.dokka.Utilities.DokkaRunModule
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,49 +15,58 @@ 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.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert
import org.junit.Assert.fail
import java.io.File
+data class ModelConfig(
+ val roots: Array<ContentRoot> = arrayOf(),
+ val withJdk: Boolean = false,
+ val withKotlinRuntime: Boolean = false,
+ val format: String = "html",
+ val includeNonPublic: Boolean = true,
+ val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+ val analysisPlatform: Platform = Platform.DEFAULT,
+ val defaultPlatforms: List<String> = emptyList(),
+ val noStdlibLink: Boolean = true,
+ val collectInheritedExtensionsFromLibraries: Boolean = false
+)
-fun verifyModel(vararg roots: ContentRoot,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+fun verifyModel(modelConfig: ModelConfig,
verifier: (DocumentationModule) -> Unit) {
val documentation = DocumentationModule("test")
- val options = DocumentationOptions(
- "",
- format,
- includeNonPublic = includeNonPublic,
- skipEmptyPackages = false,
- includeRootPackage = true,
- sourceLinks = listOf(),
- perPackageOptions = perPackageOptions,
- generateIndexPages = false,
- noStdlibLink = true,
- cacheRoot = "default",
- languageVersion = null,
- apiVersion = null
+ val passConfiguration = PassConfigurationImpl (
+ includeNonPublic = modelConfig.includeNonPublic,
+ skipEmptyPackages = false,
+ includeRootPackage = true,
+ sourceLinks = listOf(),
+ perPackageOptions = modelConfig.perPackageOptions,
+ noStdlibLink = modelConfig.noStdlibLink,
+ noJdkLink = false,
+ languageVersion = null,
+ apiVersion = null,
+ collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries
+ )
+ val configuration = DokkaConfigurationImpl(
+ outputDir = "",
+ format = modelConfig.format,
+ generateIndexPages = false,
+ cacheRoot = "default",
+ passesConfigurations = listOf(passConfiguration)
)
- appendDocumentation(documentation, *roots,
- withJdk = withJdk,
- withKotlinRuntime = withKotlinRuntime,
- options = options)
- documentation.prepareForGeneration(options)
+ appendDocumentation(documentation, configuration, passConfiguration, modelConfig)
+ documentation.prepareForGeneration(configuration)
verifier(documentation)
}
fun appendDocumentation(documentation: DocumentationModule,
- vararg roots: ContentRoot,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- options: DocumentationOptions,
- defaultPlatforms: List<String> = emptyList()) {
+ dokkaConfiguration: DokkaConfiguration,
+ passConfiguration: DokkaConfiguration.PassConfiguration,
+ modelConfig: ModelConfig
+) {
val messageCollector = object : MessageCollector {
override fun clear() {
@@ -82,64 +91,105 @@ fun appendDocumentation(documentation: DocumentationModule,
override fun hasErrors() = false
}
- val environment = AnalysisEnvironment(messageCollector)
+ val environment = AnalysisEnvironment(messageCollector, modelConfig.analysisPlatform)
environment.apply {
- if (withJdk || withKotlinRuntime) {
- val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class")
- addClasspath(File(stringRoot))
+ if (modelConfig.withJdk || modelConfig.withKotlinRuntime) {
+ addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
}
- if (withKotlinRuntime) {
- val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
- addClasspath(File(kotlinStrictfpRoot))
+ if (modelConfig.withKotlinRuntime) {
+ if (analysisPlatform == Platform.jvm) {
+ val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
+ addClasspath(File(kotlinStrictfpRoot))
+ }
+ // TODO: Fix concrete path to correct gradle path providing
+ if (analysisPlatform == Platform.js) {
+ addClasspath(File("/home/jetbrains/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/181.5281.24/plugins/Kotlin/kotlinc/lib/kotlin-jslib.jar"))
+ addClasspath(File("/home/jetbrains/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/181.5281.24/plugins/Kotlin/kotlinc/lib/kotlin-stdlib-js.jar"))
+ addClasspath(File("/home/jetbrains/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/181.5281.24/plugins/Kotlin/kotlinc/lib/kotlin-stdlib-js-sources.jar"))
+ }
+ if (analysisPlatform == Platform.common) {
+ addClasspath(File("/home/jetbrains/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.51/e4a9d4b13ab19ed1e6531fce6df98e8cfa7f7301/kotlin-stdlib-common-1.2.51.jar"))
+ }
}
- addRoots(roots.toList())
+ addRoots(modelConfig.roots.toList())
- loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
+ loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion)
}
val defaultPlatformsProvider = object : DefaultPlatformsProvider {
- override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms
+ override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = modelConfig.defaultPlatforms
}
- val injector = Guice.createInjector(
- DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger))
+
+ val globalInjector = Guice.createInjector(
+ DokkaRunModule(dokkaConfiguration)
+ )
+
+ val injector = globalInjector.createChildInjector(
+ DokkaAnalysisModule(
+ environment,
+ dokkaConfiguration,
+ defaultPlatformsProvider,
+ documentation.nodeRefGraph,
+ passConfiguration,
+ DokkaConsoleLogger
+ )
+ )
+
buildDocumentationModule(injector, documentation)
Disposer.dispose(environment)
}
-fun verifyModel(source: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- verifier: (DocumentationModule) -> Unit) {
+fun checkSourceExistsAndVerifyModel(source: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ verifier: (DocumentationModule) -> Unit) {
if (!File(source).exists()) {
throw IllegalArgumentException("Can't find test data file $source")
}
- verifyModel(contentRootFromPath(source),
- withJdk = withJdk,
- withKotlinRuntime = withKotlinRuntime,
- format = format,
- includeNonPublic = includeNonPublic,
- verifier = verifier)
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(contentRootFromPath(source)),
+ withJdk = modelConfig.withJdk,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ format = modelConfig.format,
+ includeNonPublic = modelConfig.includeNonPublic,
+ analysisPlatform = modelConfig.analysisPlatform
+ ),
+
+ verifier = verifier
+ )
}
fun verifyPackageMember(source: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
verifier: (DocumentationNode) -> Unit) {
- verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model ->
+ checkSourceExistsAndVerifyModel(
+ source,
+ modelConfig = ModelConfig(
+ withJdk = modelConfig.withJdk,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ analysisPlatform = modelConfig.analysisPlatform
+ )
+ ) { model ->
val pkg = model.members.single()
verifier(pkg.members.single())
}
}
fun verifyJavaModel(source: String,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
verifier: (DocumentationModule) -> Unit) {
val tempDir = FileUtil.createTempDirectory("dokka", "")
try {
val sourceFile = File(source)
FileUtil.copy(sourceFile, File(tempDir, sourceFile.name))
- verifyModel(JavaSourceRoot(tempDir, null), withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier)
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(JavaSourceRoot(tempDir, null)),
+ withJdk = true,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ analysisPlatform = modelConfig.analysisPlatform
+ ),
+ verifier = verifier
+ )
}
finally {
FileUtil.delete(tempDir)
@@ -147,32 +197,42 @@ fun verifyJavaModel(source: String,
}
fun verifyJavaPackageMember(source: String,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
verifier: (DocumentationNode) -> Unit) {
- verifyJavaModel(source, withKotlinRuntime) { model ->
+ verifyJavaModel(source, modelConfig) { model ->
val pkg = model.members.single()
verifier(pkg.members.single())
}
}
-fun verifyOutput(roots: Array<ContentRoot>,
+fun verifyOutput(modelConfig: ModelConfig = ModelConfig(),
outputExtension: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
- verifyModel(
- *roots,
- withJdk = withJdk,
- withKotlinRuntime = withKotlinRuntime,
- format = format,
- includeNonPublic = includeNonPublic
- ) {
- verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
+ verifyModel(modelConfig) {
+ verifyModelOutput(it, outputExtension, modelConfig.roots.first().path, outputGenerator)
}
}
+fun verifyOutput(path: String,
+ outputExtension: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+ verifyOutput(
+ ModelConfig(
+ roots = arrayOf(contentRootFromPath(path)) + modelConfig.roots,
+ withJdk = modelConfig.withJdk,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ format = modelConfig.format,
+ includeNonPublic = modelConfig.includeNonPublic,
+ analysisPlatform = modelConfig.analysisPlatform,
+ noStdlibLink = modelConfig.noStdlibLink,
+ collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries
+ ),
+ outputExtension,
+ outputGenerator
+ )
+}
+
fun verifyModelOutput(it: DocumentationModule,
outputExtension: String,
sourcePath: String,
@@ -184,29 +244,11 @@ fun verifyModelOutput(it: DocumentationModule,
assertEqualsIgnoringSeparators(expectedFile, output.toString())
}
-fun verifyOutput(path: String,
- outputExtension: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
- verifyOutput(
- arrayOf(contentRootFromPath(path)),
- outputExtension,
- withJdk,
- withKotlinRuntime,
- format,
- includeNonPublic,
- outputGenerator
- )
-}
-
fun verifyJavaOutput(path: String,
outputExtension: String,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
- verifyJavaModel(path, withKotlinRuntime) { model ->
+ verifyJavaModel(path, modelConfig) { model ->
verifyModelOutput(model, outputExtension, path, outputGenerator)
}
}
@@ -257,6 +299,14 @@ fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
is ContentBlock -> {
appendChildren(node)
}
+ is NodeRenderContent -> {
+ append("render(")
+ append(node.node)
+ append(",")
+ append(node.mode)
+ append(")")
+ }
+ is ContentSymbol -> { append(node.text) }
is ContentEmpty -> { /* nothing */ }
else -> throw IllegalStateException("Don't know how to format node $node")
}
@@ -270,21 +320,6 @@ fun ContentNode.toTestString(): String {
}.toString()
}
-class InMemoryLocation(override val path: String): Location {
- override fun relativePathTo(other: Location, anchor: String?): String =
- if (anchor != null) other.path + "#" + anchor else other.path
-}
-
-object InMemoryLocationService: LocationService {
- override fun location(qualifiedName: List<String>, hasMembers: Boolean) =
- InMemoryLocation(relativePathToNode(qualifiedName, hasMembers))
-
- override val root: Location
- get() = InMemoryLocation("")
-}
-
-val tempLocation = InMemoryLocation("")
-
val ContentRoot.path: String
get() = when(this) {
is KotlinSourceRoot -> path
diff --git a/core/src/test/kotlin/format/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt
new file mode 100644
index 00000000..ef9e815d
--- /dev/null
+++ b/core/src/test/kotlin/format/FileGeneratorTestCase.kt
@@ -0,0 +1,35 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+
+
+abstract class FileGeneratorTestCase {
+ abstract val formatService: FormatService
+
+ @get:Rule
+ var folder = TemporaryFolder()
+
+ val fileGenerator = FileGenerator(folder.apply { create() }.root)
+
+ @Before
+ fun bindGenerator() {
+ fileGenerator.formatService = formatService
+ }
+
+ fun buildPagesAndReadInto(nodes: List<DocumentationNode>, sb: StringBuilder) = with(fileGenerator) {
+ buildPages(nodes)
+ val byLocations = nodes.groupBy { location(it) }
+ byLocations.forEach { (loc, _) ->
+ if (byLocations.size > 1) {
+ if (sb.isNotBlank() && !sb.endsWith('\n')) {
+ sb.appendln()
+ }
+ sb.appendln("<!-- File: ${loc.file.relativeTo(root).toUnixString()} -->")
+ }
+ sb.append(loc.file.readText())
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt
index c097c5c8..60de7d29 100644
--- a/core/src/test/kotlin/format/GFMFormatTest.kt
+++ b/core/src/test/kotlin/format/GFMFormatTest.kt
@@ -2,22 +2,35 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.GFMFormatService
import org.jetbrains.dokka.KotlinLanguageService
+import org.jetbrains.dokka.Platform
import org.junit.Test
-class GFMFormatTest {
- private val gfmService = GFMFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
+abstract class BaseGFMFormatTest(val analysisPlatform: Platform) : FileGeneratorTestCase() {
+ override val formatService = GFMFormatService(fileGenerator, KotlinLanguageService(), listOf())
+ private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
- @Test fun sample() {
- verifyGFMNodeByName("sample", "Foo")
+
+ @Test
+ fun sample() {
+ verifyGFMNodeByName("sample", "Foo", defaultModelConfig)
}
- @Test fun listInTableCell() {
- verifyGFMNodeByName("listInTableCell", "Foo")
+ @Test
+ fun listInTableCell() {
+ verifyGFMNodeByName("listInTableCell", "Foo", defaultModelConfig)
}
- private fun verifyGFMNodeByName(fileName: String, name: String) {
- verifyOutput("testdata/format/gfm/$fileName.kt", ".md") { model, output ->
- gfmService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
+ private fun verifyGFMNodeByName(fileName: String, name: String, modelConfig: ModelConfig) {
+ verifyOutput("testdata/format/gfm/$fileName.kt", ".md", modelConfig) { model, output ->
+ buildPagesAndReadInto(
+ model.members.single().members.filter { it.name == name },
+ output
+ )
}
}
}
+
+
+class JsGFMFormatTest : BaseGFMFormatTest(Platform.js)
+class JvmGFMFormatTest : BaseGFMFormatTest(Platform.jvm)
+class CommonGFMFormatTest : BaseGFMFormatTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt
index 01e4559e..20891963 100644
--- a/core/src/test/kotlin/format/HtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/HtmlFormatTest.kt
@@ -3,175 +3,191 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.config.KotlinSourceRoot
+import org.junit.Before
import org.junit.Test
import java.io.File
-class HtmlFormatTest {
- private val htmlService = HtmlFormatService(InMemoryLocationService, KotlinLanguageService(), HtmlTemplateService.default(), listOf())
+abstract class BaseHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() {
+ protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
+ override val formatService = HtmlFormatService(fileGenerator, KotlinLanguageService(), HtmlTemplateService.default(), listOf())
@Test fun classWithCompanionObject() {
- verifyHtmlNode("classWithCompanionObject")
+ verifyHtmlNode("classWithCompanionObject", defaultModelConfig)
}
@Test fun htmlEscaping() {
- verifyHtmlNode("htmlEscaping")
+ verifyHtmlNode("htmlEscaping", defaultModelConfig)
}
@Test fun overloads() {
- verifyHtmlNodes("overloads") { model -> model.members }
+ verifyHtmlNodes("overloads", defaultModelConfig) { model -> model.members }
}
@Test fun overloadsWithDescription() {
- verifyHtmlNode("overloadsWithDescription")
+ verifyHtmlNode("overloadsWithDescription", defaultModelConfig)
}
@Test fun overloadsWithDifferentDescriptions() {
- verifyHtmlNode("overloadsWithDifferentDescriptions")
+ verifyHtmlNode("overloadsWithDifferentDescriptions", defaultModelConfig)
}
@Test fun deprecated() {
- verifyOutput("testdata/format/deprecated.kt", ".package.html") { model, output ->
- htmlService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
+ verifyOutput("testdata/format/deprecated.kt", ".package.html", defaultModelConfig) { model, output ->
+ buildPagesAndReadInto(model.members, output)
}
- verifyOutput("testdata/format/deprecated.kt", ".class.html") { model, output ->
- htmlService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
+ verifyOutput("testdata/format/deprecated.kt", ".class.html", defaultModelConfig) { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
}
}
@Test fun brokenLink() {
- verifyHtmlNode("brokenLink")
+ verifyHtmlNode("brokenLink", defaultModelConfig)
}
@Test fun codeSpan() {
- verifyHtmlNode("codeSpan")
+ verifyHtmlNode("codeSpan", defaultModelConfig)
}
@Test fun parenthesis() {
- verifyHtmlNode("parenthesis")
+ verifyHtmlNode("parenthesis", defaultModelConfig)
}
@Test fun bracket() {
- verifyHtmlNode("bracket")
+ verifyHtmlNode("bracket", defaultModelConfig)
}
@Test fun see() {
- verifyHtmlNode("see")
+ verifyHtmlNode("see", defaultModelConfig)
}
@Test fun tripleBackticks() {
- verifyHtmlNode("tripleBackticks")
+ verifyHtmlNode("tripleBackticks", defaultModelConfig)
}
@Test fun typeLink() {
- verifyHtmlNodes("typeLink") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ verifyHtmlNodes("typeLink", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } }
}
@Test fun parameterAnchor() {
- verifyHtmlNode("parameterAnchor")
- }
-
- @Test fun javaSupertypeLink() {
- verifyJavaHtmlNodes("JavaSupertype") { model ->
- model.members.single().members.single { it.name == "JavaSupertype" }.members.filter { it.name == "Bar" }
- }
+ verifyHtmlNode("parameterAnchor", defaultModelConfig)
}
@Test fun codeBlock() {
- verifyHtmlNode("codeBlock")
- }
-
- @Test fun javaLinkTag() {
- verifyJavaHtmlNode("javaLinkTag")
- }
-
- @Test fun javaLinkTagWithLabel() {
- verifyJavaHtmlNode("javaLinkTagWithLabel")
- }
-
- @Test fun javaSeeTag() {
- verifyJavaHtmlNode("javaSeeTag")
- }
-
- @Test fun javaDeprecated() {
- verifyJavaHtmlNodes("javaDeprecated") { model ->
- model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" }
- }
+ verifyHtmlNode("codeBlock", defaultModelConfig)
}
-
- @Test fun crossLanguageKotlinExtendsJava() {
- verifyOutput(arrayOf(KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt"),
- JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)),
- ".html") { model, output ->
- htmlService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == "Bar" })
- }
- }
-
@Test fun orderedList() {
- verifyHtmlNodes("orderedList") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ verifyHtmlNodes("orderedList", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } }
}
@Test fun linkWithLabel() {
- verifyHtmlNodes("linkWithLabel") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ verifyHtmlNodes("linkWithLabel", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } }
}
@Test fun entity() {
- verifyHtmlNodes("entity") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ verifyHtmlNodes("entity", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } }
}
@Test fun uninterpretedEmphasisCharacters() {
- verifyHtmlNode("uninterpretedEmphasisCharacters")
+ verifyHtmlNode("uninterpretedEmphasisCharacters", defaultModelConfig)
}
@Test fun markdownInLinks() {
- verifyHtmlNode("markdownInLinks")
+ verifyHtmlNode("markdownInLinks", defaultModelConfig)
}
@Test fun returnWithLink() {
- verifyHtmlNode("returnWithLink")
+ verifyHtmlNode("returnWithLink", defaultModelConfig)
}
@Test fun linkWithStarProjection() {
- verifyHtmlNode("linkWithStarProjection", withKotlinRuntime = true)
+ verifyHtmlNode("linkWithStarProjection", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun functionalTypeWithNamedParameters() {
- verifyHtmlNode("functionalTypeWithNamedParameters")
+ verifyHtmlNode("functionalTypeWithNamedParameters", defaultModelConfig)
}
@Test fun sinceKotlin() {
- verifyHtmlNode("sinceKotlin")
+ verifyHtmlNode("sinceKotlin", defaultModelConfig)
}
@Test fun blankLineInsideCodeBlock() {
- verifyHtmlNode("blankLineInsideCodeBlock")
+ verifyHtmlNode("blankLineInsideCodeBlock", defaultModelConfig)
}
@Test fun indentedCodeBlock() {
- verifyHtmlNode("indentedCodeBlock")
+ verifyHtmlNode("indentedCodeBlock", defaultModelConfig)
}
- private fun verifyHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
- verifyHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ private fun verifyHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyHtmlNodes(fileName, modelConfig) { model -> model.members.single().members }
}
private fun verifyHtmlNodes(fileName: String,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
- verifyOutput("testdata/format/$fileName.kt", ".html", withKotlinRuntime = withKotlinRuntime) { model, output ->
- htmlService.createOutputBuilder(output, tempLocation).appendNodes(nodeFilter(model))
+ verifyOutput("testdata/format/$fileName.kt", ".html", modelConfig) { model, output ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+
+ protected fun verifyJavaHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyJavaHtmlNodes(fileName, modelConfig) { model -> model.members.single().members }
+ }
+
+ protected fun verifyJavaHtmlNodes(fileName: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
+ verifyJavaOutput("testdata/format/$fileName.java", ".html", modelConfig) { model, output ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+}
+
+class JSHtmlFormatTest: BaseHtmlFormatTest(Platform.js)
+
+class JVMHtmlFormatTest: BaseHtmlFormatTest(Platform.jvm) {
+ @Test
+ fun javaSeeTag() {
+ verifyJavaHtmlNode("javaSeeTag", defaultModelConfig)
+ }
+
+ @Test fun javaDeprecated() {
+ verifyJavaHtmlNodes("javaDeprecated", defaultModelConfig) { model ->
+ model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" }
+ }
+ }
+
+ @Test fun crossLanguageKotlinExtendsJava() {
+ verifyOutput(
+ ModelConfig(
+ roots = arrayOf(
+ KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt"),
+ JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)
+ ),
+ analysisPlatform = analysisPlatform
+ ), ".html") { model, output ->
+ buildPagesAndReadInto(
+ model.members.single().members.filter { it.name == "Bar" },
+ output
+ )
}
}
- private fun verifyJavaHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
- verifyJavaHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ @Test fun javaLinkTag() {
+ verifyJavaHtmlNode("javaLinkTag", defaultModelConfig)
+ }
+
+ @Test fun javaLinkTagWithLabel() {
+ verifyJavaHtmlNode("javaLinkTagWithLabel", defaultModelConfig)
}
- private fun verifyJavaHtmlNodes(fileName: String,
- withKotlinRuntime: Boolean = false,
- nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
- verifyJavaOutput("testdata/format/$fileName.java", ".html", withKotlinRuntime = withKotlinRuntime) { model, output ->
- htmlService.createOutputBuilder(output, tempLocation).appendNodes(nodeFilter(model))
+ @Test fun javaSupertypeLink() {
+ verifyJavaHtmlNodes("JavaSupertype", defaultModelConfig) { model ->
+ model.members.single().members.single { it.name == "JavaSupertype" }.members.filter { it.name == "Bar" }
}
}
+
}
+class CommonHtmlFormatTest: BaseHtmlFormatTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
index af44b048..4f292e37 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
@@ -5,8 +5,8 @@ import org.junit.Ignore
import org.junit.Test
@Ignore
-class KotlinWebSiteFormatTest {
- private val kwsService = KotlinWebsiteFormatService(InMemoryLocationService, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
+class KotlinWebSiteFormatTest: FileGeneratorTestCase() {
+ override val formatService = KotlinWebsiteFormatService(fileGenerator, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
@Test fun sample() {
verifyKWSNodeByName("sample", "foo")
@@ -29,36 +29,64 @@ class KotlinWebSiteFormatTest {
val path = "dataTagsInGroupNode"
val module = buildMultiplePlatforms(path)
verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output ->
- kwsService.createOutputBuilder(output, tempLocation).appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }))
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }),
+ output
+ )
}
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 })
+ verifyOutput("testdata/format/website/$fileName.kt", ".md", ModelConfig(format = "kotlin-website")) { model, output ->
+ buildPagesAndReadInto(
+ model.members.single().members.filter { it.name == name },
+ output
+ )
}
}
private fun buildMultiplePlatforms(path: String): DocumentationModule {
val module = DocumentationModule("test")
- val options = DocumentationOptions(
- outputDir = "",
- outputFormat = "html",
- generateIndexPages = false,
- noStdlibLink = true,
+ val passConfiguration = PassConfigurationImpl(noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
- 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)
+ val configuration = DokkaConfigurationImpl(
+ outputDir = "",
+ format = "html",
+ generateIndexPages = false,
+ passesConfigurations = listOf(passConfiguration)
+ )
+
+ appendDocumentation(
+ module, configuration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website/$path/jvm.kt")),
+ defaultPlatforms = listOf("JVM")
+ )
+
+ )
+
+
+ appendDocumentation(
+ module, configuration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website/$path/jre7.kt")),
+ defaultPlatforms = listOf("JVM", "JRE7")
+ )
+ )
+ appendDocumentation(
+ module, configuration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website/$path/js.kt")),
+ defaultPlatforms = listOf("JS")
+ )
+ )
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)
+ buildPagesAndReadInto(model.members, output)
}
}
diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
index 433c9c13..1901154f 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
@@ -3,36 +3,36 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
import org.junit.Test
-class KotlinWebSiteHtmlFormatTest {
- private val kwsService = KotlinWebsiteHtmlFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
-
+abstract class BaseKotlinWebSiteHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() {
+ val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
+ override val formatService = KotlinWebsiteHtmlFormatService(fileGenerator, KotlinLanguageService(), listOf(), EmptyHtmlTemplateService)
@Test fun dropImport() {
- verifyKWSNodeByName("dropImport", "foo")
+ verifyKWSNodeByName("dropImport", "foo", defaultModelConfig)
}
@Test fun sample() {
- verifyKWSNodeByName("sample", "foo")
+ verifyKWSNodeByName("sample", "foo", defaultModelConfig)
}
@Test fun sampleWithAsserts() {
- verifyKWSNodeByName("sampleWithAsserts", "a")
+ verifyKWSNodeByName("sampleWithAsserts", "a", defaultModelConfig)
}
@Test fun newLinesInSamples() {
- verifyKWSNodeByName("newLinesInSamples", "foo")
+ verifyKWSNodeByName("newLinesInSamples", "foo", defaultModelConfig)
}
@Test fun newLinesInImportList() {
- verifyKWSNodeByName("newLinesInImportList", "foo")
+ verifyKWSNodeByName("newLinesInImportList", "foo", defaultModelConfig)
}
@Test fun returnTag() {
- verifyKWSNodeByName("returnTag", "indexOf")
+ verifyKWSNodeByName("returnTag", "indexOf", defaultModelConfig)
}
@Test fun overloadGroup() {
- verifyKWSNodeByName("overloadGroup", "magic")
+ verifyKWSNodeByName("overloadGroup", "magic", defaultModelConfig)
}
@Test fun dataTags() {
@@ -44,37 +44,73 @@ class KotlinWebSiteHtmlFormatTest {
val path = "dataTagsInGroupNode"
val module = buildMultiplePlatforms(path)
verifyModelOutput(module, ".html", "testdata/format/website-html/$path/multiplatform.kt") { model, output ->
- kwsService.createOutputBuilder(output, tempLocation).appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }))
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }),
+ output
+ )
}
verifyMultiplatformPackage(module, path)
}
- private fun verifyKWSNodeByName(fileName: String, name: String) {
- verifyOutput("testdata/format/website-html/$fileName.kt", ".html", format = "kotlin-website-html") { model, output ->
- kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
+ private fun verifyKWSNodeByName(fileName: String, name: String, modelConfig: ModelConfig) {
+ verifyOutput(
+ "testdata/format/website-html/$fileName.kt",
+ ".html",
+ ModelConfig(analysisPlatform = modelConfig.analysisPlatform, format = "kotlin-website-html")
+ ) { model, output ->
+ buildPagesAndReadInto(model.members.single().members.filter { it.name == name }, output)
}
}
private fun buildMultiplePlatforms(path: String): DocumentationModule {
val module = DocumentationModule("test")
- val options = DocumentationOptions(
- outputDir = "",
- outputFormat = "kotlin-website-html",
- generateIndexPages = false,
+ val passConfiguration = PassConfigurationImpl(
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
- appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options)
- appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options)
- appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options)
+
+ val dokkaConfiguration = DokkaConfigurationImpl(
+ outputDir = "",
+ format = "kotlin-website-html",
+ generateIndexPages = false,
+ passesConfigurations = listOf(
+ passConfiguration
+ )
+
+ )
+
+ appendDocumentation(
+ module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jvm.kt")),
+ defaultPlatforms = listOf("JVM")
+ )
+ )
+ appendDocumentation(
+ module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jre7.kt")),
+ defaultPlatforms = listOf("JVM", "JRE7")
+ )
+ )
+ appendDocumentation(
+ module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/js.kt")),
+ defaultPlatforms = listOf("JS")
+ )
+ )
+
return module
}
private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
verifyModelOutput(module, ".package.html", "testdata/format/website-html/$path/multiplatform.kt") { model, output ->
- kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
+ buildPagesAndReadInto(model.members, output)
}
}
}
+
+class JsKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.js)
+class JvmKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.jvm)
+class CommonKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
index 44155004..453b1de8 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
@@ -8,32 +8,32 @@ import org.junit.Test
@Ignore
class KotlinWebSiteRunnableSamplesFormatTest {
- private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
-
-
- @Test fun dropImport() {
- verifyKWSNodeByName("dropImport", "foo")
- }
-
- @Test fun sample() {
- verifyKWSNodeByName("sample", "foo")
- }
-
- @Test fun sampleWithAsserts() {
- 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 })
- }
- }
+// private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
+//
+//
+// @Test fun dropImport() {
+// verifyKWSNodeByName("dropImport", "foo")
+// }
+//
+// @Test fun sample() {
+// verifyKWSNodeByName("sample", "foo")
+// }
+//
+// @Test fun sampleWithAsserts() {
+// 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 820af361..29d2d20f 100644
--- a/core/src/test/kotlin/format/MarkdownFormatTest.kt
+++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt
@@ -3,272 +3,215 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
import org.junit.Test
-class MarkdownFormatTest {
- private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
+abstract class BaseMarkdownFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() {
+ override val formatService = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf())
+
+ protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun emptyDescription() {
- verifyMarkdownNode("emptyDescription")
+ verifyMarkdownNode("emptyDescription", defaultModelConfig)
}
@Test fun classWithCompanionObject() {
- verifyMarkdownNode("classWithCompanionObject")
+ verifyMarkdownNode("classWithCompanionObject", defaultModelConfig)
}
@Test fun annotations() {
- verifyMarkdownNode("annotations")
+ verifyMarkdownNode("annotations", defaultModelConfig)
}
@Test fun annotationClass() {
- verifyMarkdownNode("annotationClass", withKotlinRuntime = true)
- verifyMarkdownPackage("annotationClass", withKotlinRuntime = true)
- }
-
- @Test fun exceptionClass() {
- verifyMarkdownNode("exceptionClass", withKotlinRuntime = true)
- verifyMarkdownPackage("exceptionClass", withKotlinRuntime = true)
- }
-
- @Test fun annotationParams() {
- verifyMarkdownNode("annotationParams", withKotlinRuntime = true)
- }
-
- @Test fun extensions() {
- verifyOutput("testdata/format/extensions.kt", ".package.md") { model, output ->
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
- }
- verifyOutput("testdata/format/extensions.kt", ".class.md") { model, output ->
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
- }
+ verifyMarkdownNode("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
+ verifyMarkdownPackage("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun enumClass() {
- verifyOutput("testdata/format/enumClass.kt", ".md") { model, output ->
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members)
+ verifyOutput("testdata/format/enumClass.kt", ".md", defaultModelConfig) { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
}
- verifyOutput("testdata/format/enumClass.kt", ".value.md") { model, output ->
+ verifyOutput("testdata/format/enumClass.kt", ".value.md", defaultModelConfig) { model, output ->
val enumClassNode = model.members.single().members[0]
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(
- enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" })
+ buildPagesAndReadInto(
+ enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" },
+ output
+ )
}
}
@Test fun varargsFunction() {
- verifyMarkdownNode("varargsFunction")
+ verifyMarkdownNode("varargsFunction", defaultModelConfig)
}
@Test fun overridingFunction() {
- verifyMarkdownNodes("overridingFunction") { model->
+ verifyMarkdownNodes("overridingFunction", defaultModelConfig) { model->
val classMembers = model.members.single().members.first { it.name == "D" }.members
classMembers.filter { it.name == "f" }
}
}
@Test fun propertyVar() {
- verifyMarkdownNode("propertyVar")
+ verifyMarkdownNode("propertyVar", defaultModelConfig)
}
@Test fun functionWithDefaultParameter() {
- verifyMarkdownNode("functionWithDefaultParameter")
+ verifyMarkdownNode("functionWithDefaultParameter", defaultModelConfig)
}
@Test fun accessor() {
- verifyMarkdownNodes("accessor") { model ->
+ verifyMarkdownNodes("accessor", defaultModelConfig) { model ->
model.members.single().members.first { it.name == "C" }.members.filter { it.name == "x" }
}
}
@Test fun paramTag() {
- verifyMarkdownNode("paramTag")
+ verifyMarkdownNode("paramTag", defaultModelConfig)
}
@Test fun throwsTag() {
- verifyMarkdownNode("throwsTag")
+ verifyMarkdownNode("throwsTag", defaultModelConfig)
}
@Test fun typeParameterBounds() {
- verifyMarkdownNode("typeParameterBounds")
+ verifyMarkdownNode("typeParameterBounds", defaultModelConfig)
}
@Test fun typeParameterVariance() {
- verifyMarkdownNode("typeParameterVariance")
+ verifyMarkdownNode("typeParameterVariance", defaultModelConfig)
}
@Test fun typeProjectionVariance() {
- verifyMarkdownNode("typeProjectionVariance")
- }
-
- @Test fun javadocHtml() {
- verifyJavaMarkdownNode("javadocHtml")
- }
-
- @Test fun javaCodeLiteralTags() {
- verifyJavaMarkdownNode("javaCodeLiteralTags")
- }
-
- @Test fun javaCodeInParam() {
- verifyJavaMarkdownNode("javaCodeInParam")
- }
-
- @Test fun javaSpaceInAuthor() {
- verifyJavaMarkdownNode("javaSpaceInAuthor")
- }
-
- @Test fun nullability() {
- verifyMarkdownNode("nullability")
- }
-
- @Test fun operatorOverloading() {
- verifyMarkdownNodes("operatorOverloading") { model->
- model.members.single().members.single { it.name == "C" }.members.filter { it.name == "plus" }
- }
- }
-
- @Test fun javadocOrderedList() {
- verifyJavaMarkdownNodes("javadocOrderedList") { model ->
- model.members.single().members.filter { it.name == "Bar" }
- }
+ verifyMarkdownNode("typeProjectionVariance", defaultModelConfig)
}
@Test fun codeBlockNoHtmlEscape() {
- verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic")
+ verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic", defaultModelConfig)
}
@Test fun companionObjectExtension() {
- verifyMarkdownNodeByName("companionObjectExtension", "Foo")
+ verifyMarkdownNodeByName("companionObjectExtension", "Foo", defaultModelConfig)
}
@Test fun starProjection() {
- verifyMarkdownNode("starProjection")
+ verifyMarkdownNode("starProjection", defaultModelConfig)
}
@Test fun extensionFunctionParameter() {
- verifyMarkdownNode("extensionFunctionParameter")
+ verifyMarkdownNode("extensionFunctionParameter", defaultModelConfig)
}
@Test fun summarizeSignatures() {
- verifyMarkdownNodes("summarizeSignatures") { model -> model.members }
- }
-
- @Test fun summarizeSignaturesProperty() {
- verifyMarkdownNodes("summarizeSignaturesProperty") { model -> model.members }
+ verifyMarkdownNodes("summarizeSignatures", defaultModelConfig) { model -> model.members }
}
@Test fun reifiedTypeParameter() {
- verifyMarkdownNode("reifiedTypeParameter", withKotlinRuntime = true)
+ verifyMarkdownNode("reifiedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun annotatedTypeParameter() {
- verifyMarkdownNode("annotatedTypeParameter", withKotlinRuntime = true)
+ verifyMarkdownNode("annotatedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun inheritedMembers() {
- verifyMarkdownNodeByName("inheritedMembers", "Bar")
+ verifyMarkdownNodeByName("inheritedMembers", "Bar", defaultModelConfig)
}
@Test fun inheritedExtensions() {
- verifyMarkdownNodeByName("inheritedExtensions", "Bar")
+ verifyMarkdownNodeByName("inheritedExtensions", "Bar", defaultModelConfig)
}
@Test fun genericInheritedExtensions() {
- verifyMarkdownNodeByName("genericInheritedExtensions", "Bar")
+ verifyMarkdownNodeByName("genericInheritedExtensions", "Bar", defaultModelConfig)
}
@Test fun arrayAverage() {
- verifyMarkdownNodeByName("arrayAverage", "XArray")
+ verifyMarkdownNodeByName("arrayAverage", "XArray", defaultModelConfig)
}
@Test fun multipleTypeParameterConstraints() {
- verifyMarkdownNode("multipleTypeParameterConstraints", withKotlinRuntime = true)
+ verifyMarkdownNode("multipleTypeParameterConstraints", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun inheritedCompanionObjectProperties() {
- verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C")
+ verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C", defaultModelConfig)
}
@Test fun shadowedExtensionFunctions() {
- verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar")
+ verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar", defaultModelConfig)
}
@Test fun inapplicableExtensionFunctions() {
- verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar")
+ verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar", defaultModelConfig)
}
@Test fun receiverParameterTypeBound() {
- verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo")
+ verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo", defaultModelConfig)
}
@Test fun extensionWithDocumentedReceiver() {
- verifyMarkdownNodes("extensionWithDocumentedReceiver") { model ->
+ verifyMarkdownNodes("extensionWithDocumentedReceiver", defaultModelConfig) { model ->
model.members.single().members.single().members.filter { it.name == "fn" }
}
}
- @Test fun jdkLinks() {
- verifyMarkdownNode("jdkLinks", withKotlinRuntime = true)
- }
-
@Test fun codeBlock() {
- verifyMarkdownNode("codeBlock")
+ verifyMarkdownNode("codeBlock", defaultModelConfig)
}
@Test fun exclInCodeBlock() {
- verifyMarkdownNodeByName("exclInCodeBlock", "foo")
+ verifyMarkdownNodeByName("exclInCodeBlock", "foo", defaultModelConfig)
}
@Test fun backtickInCodeBlock() {
- verifyMarkdownNodeByName("backtickInCodeBlock", "foo")
+ verifyMarkdownNodeByName("backtickInCodeBlock", "foo", defaultModelConfig)
}
@Test fun qualifiedNameLink() {
- verifyMarkdownNodeByName("qualifiedNameLink", "foo", withKotlinRuntime = true)
+ verifyMarkdownNodeByName("qualifiedNameLink", "foo",
+ ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
@Test fun functionalTypeWithNamedParameters() {
- verifyMarkdownNode("functionalTypeWithNamedParameters")
+ verifyMarkdownNode("functionalTypeWithNamedParameters", defaultModelConfig)
}
@Test fun typeAliases() {
- verifyMarkdownNode("typeAliases")
- verifyMarkdownPackage("typeAliases")
- }
-
- @Test fun sampleByFQName() {
- verifyMarkdownNode("sampleByFQName")
+ verifyMarkdownNode("typeAliases", defaultModelConfig)
+ verifyMarkdownPackage("typeAliases", defaultModelConfig)
}
@Test fun sampleByShortName() {
- verifyMarkdownNode("sampleByShortName")
+ verifyMarkdownNode("sampleByShortName", defaultModelConfig)
}
@Test fun suspendParam() {
- verifyMarkdownNode("suspendParam")
- verifyMarkdownPackage("suspendParam")
+ verifyMarkdownNode("suspendParam", defaultModelConfig)
+ verifyMarkdownPackage("suspendParam", defaultModelConfig)
}
@Test fun sinceKotlin() {
- verifyMarkdownNode("sinceKotlin")
- verifyMarkdownPackage("sinceKotlin")
+ verifyMarkdownNode("sinceKotlin", defaultModelConfig)
+ verifyMarkdownPackage("sinceKotlin", defaultModelConfig)
}
@Test fun sinceKotlinWide() {
- verifyMarkdownPackage("sinceKotlinWide")
+ verifyMarkdownPackage("sinceKotlinWide", defaultModelConfig)
}
@Test fun dynamicType() {
- verifyMarkdownNode("dynamicType")
+ verifyMarkdownNode("dynamicType", defaultModelConfig)
}
@Test fun dynamicExtension() {
- verifyMarkdownNodes("dynamicExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ verifyMarkdownNodes("dynamicExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } }
}
@Test fun memberExtension() {
- verifyMarkdownNodes("memberExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ verifyMarkdownNodes("memberExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } }
}
@Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() {
- verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver")
+ verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver", defaultModelConfig)
}
@Test fun multiplePlatforms() {
@@ -282,37 +225,52 @@ class MarkdownFormatTest {
@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)
+ buildPagesAndReadInto(model.members.single().members, output)
}
}
@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)
+ buildPagesAndReadInto(model.members.single().members, output)
}
}
@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)
+ val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf("JVM", "JS"))
+ fileGenerator.formatService = service
+ buildPagesAndReadInto(model.members.single().members, output)
}
}
@Test fun packagePlatformsWithExtExtensions() {
val path = "multiplatform/packagePlatformsWithExtExtensions"
val module = DocumentationModule("test")
- val options = DocumentationOptions(
- outputDir = "",
- outputFormat = "html",
- generateIndexPages = false,
+ val passConfiguration = PassConfigurationImpl(
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
- appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), withKotlinRuntime = true, options = options)
+
+ val dokkaConfiguration = DokkaConfigurationImpl(
+ outputDir = "",
+ format = "html",
+ generateIndexPages = false,
+ passesConfigurations = listOf(
+ passConfiguration
+ )
+ )
+
+ appendDocumentation(module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")),
+ defaultPlatforms = listOf("JVM"),
+ withKotlinRuntime = true,
+ analysisPlatform = analysisPlatform
+ )
+ )
verifyMultiplatformIndex(module, path)
verifyMultiplatformPackage(module, path)
}
@@ -328,8 +286,10 @@ class MarkdownFormatTest {
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 }))
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }),
+ output
+ )
}
verifyMultiplatformPackage(module, path)
}
@@ -338,155 +298,179 @@ class MarkdownFormatTest {
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)))
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Class)?.member(NodeKind.Function)),
+ output
+ )
}
}
@Test fun linksInEmphasis() {
- verifyMarkdownNode("linksInEmphasis")
+ verifyMarkdownNode("linksInEmphasis", defaultModelConfig)
}
@Test fun linksInStrong() {
- verifyMarkdownNode("linksInStrong")
+ verifyMarkdownNode("linksInStrong", defaultModelConfig)
}
@Test fun linksInHeaders() {
- verifyMarkdownNode("linksInHeaders")
+ verifyMarkdownNode("linksInHeaders", defaultModelConfig)
}
@Test fun tokensInEmphasis() {
- verifyMarkdownNode("tokensInEmphasis")
+ verifyMarkdownNode("tokensInEmphasis", defaultModelConfig)
}
@Test fun tokensInStrong() {
- verifyMarkdownNode("tokensInStrong")
+ verifyMarkdownNode("tokensInStrong", defaultModelConfig)
}
@Test fun tokensInHeaders() {
- verifyMarkdownNode("tokensInHeaders")
+ verifyMarkdownNode("tokensInHeaders", defaultModelConfig)
}
@Test fun unorderedLists() {
- verifyMarkdownNode("unorderedLists")
+ verifyMarkdownNode("unorderedLists", defaultModelConfig)
}
@Test fun nestedLists() {
- verifyMarkdownNode("nestedLists")
+ verifyMarkdownNode("nestedLists", defaultModelConfig)
}
@Test fun referenceLink() {
- verifyMarkdownNode("referenceLink")
+ verifyMarkdownNode("referenceLink", defaultModelConfig)
}
@Test fun externalReferenceLink() {
- verifyMarkdownNode("externalReferenceLink")
+ verifyMarkdownNode("externalReferenceLink", defaultModelConfig)
}
@Test fun newlineInTableCell() {
- verifyMarkdownPackage("newlineInTableCell")
+ verifyMarkdownPackage("newlineInTableCell", defaultModelConfig)
}
@Test fun indentedCodeBlock() {
- verifyMarkdownNode("indentedCodeBlock")
+ verifyMarkdownNode("indentedCodeBlock", defaultModelConfig)
}
@Test fun receiverReference() {
- verifyMarkdownNode("receiverReference")
+ verifyMarkdownNode("receiverReference", defaultModelConfig)
}
@Test fun extensionScope() {
- verifyMarkdownNodeByName("extensionScope", "test")
+ verifyMarkdownNodeByName("extensionScope", "test", defaultModelConfig)
}
@Test fun typeParameterReference() {
- verifyMarkdownNode("typeParameterReference")
+ verifyMarkdownNode("typeParameterReference", defaultModelConfig)
}
@Test fun notPublishedTypeAliasAutoExpansion() {
- verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", includeNonPublic = false)
+ verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", ModelConfig(
+ analysisPlatform = analysisPlatform,
+ includeNonPublic = false
+ ))
}
@Test fun companionImplements() {
- verifyMarkdownNodeByName("companionImplements", "Foo")
+ verifyMarkdownNodeByName("companionImplements", "Foo", defaultModelConfig)
}
+
private fun buildMultiplePlatforms(path: String): DocumentationModule {
val module = DocumentationModule("test")
- val options = DocumentationOptions(
- outputDir = "",
- outputFormat = "html",
- generateIndexPages = false,
+ val passConfiguration = PassConfigurationImpl(
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
- 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)
+ val dokkaConfiguration = DokkaConfigurationImpl(
+ outputDir = "",
+ format = "html",
+ generateIndexPages = false,
+ passesConfigurations = listOf(
+ passConfiguration
+ )
+
+ )
+ appendDocumentation(
+ module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")),
+ defaultPlatforms = listOf("JVM"),
+ analysisPlatform = Platform.jvm
+ )
+ )
+ appendDocumentation(
+ module, dokkaConfiguration, passConfiguration, ModelConfig(
+ roots = arrayOf(contentRootFromPath("testdata/format/$path/js.kt")),
+ defaultPlatforms = listOf("JS"),
+ analysisPlatform = Platform.js
+ )
+ )
+
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)
+ buildPagesAndReadInto(model.members, output)
}
}
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))
+ val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf())
+ fileGenerator.formatService = service
+ buildPagesAndReadInto(listOf(model), output)
}
}
@Test fun blankLineInsideCodeBlock() {
- verifyMarkdownNode("blankLineInsideCodeBlock")
+ verifyMarkdownNode("blankLineInsideCodeBlock", defaultModelConfig)
}
- 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)
+ protected fun verifyMarkdownPackage(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyOutput("testdata/format/$fileName.kt", ".package.md", modelConfig) { model, output ->
+ buildPagesAndReadInto(model.members, output)
}
}
- private fun verifyMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) {
- verifyMarkdownNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ protected fun verifyMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members }
}
- private fun verifyMarkdownNodes(
+ protected fun verifyMarkdownNodes(
fileName: String,
- withKotlinRuntime: Boolean = false,
- includeNonPublic: Boolean = true,
+ modelConfig: ModelConfig = ModelConfig(),
nodeFilter: (DocumentationModule) -> List<DocumentationNode>
) {
verifyOutput(
"testdata/format/$fileName.kt",
".md",
- withKotlinRuntime = withKotlinRuntime,
- includeNonPublic = includeNonPublic
+ modelConfig
) { model, output ->
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(nodeFilter(model))
+ buildPagesAndReadInto(nodeFilter(model), output)
}
}
- private fun verifyJavaMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) {
- verifyJavaMarkdownNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ protected fun verifyJavaMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyJavaMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members }
}
- private fun verifyJavaMarkdownNodes(fileName: String, withKotlinRuntime: Boolean = false, nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
- verifyJavaOutput("testdata/format/$fileName.java", ".md", withKotlinRuntime = withKotlinRuntime) { model, output ->
- markdownService.createOutputBuilder(output, tempLocation).appendNodes(nodeFilter(model))
+ protected fun verifyJavaMarkdownNodes(fileName: String, modelConfig: ModelConfig = ModelConfig(), nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
+ verifyJavaOutput("testdata/format/$fileName.java", ".md", modelConfig) { model, output ->
+ buildPagesAndReadInto(nodeFilter(model), output)
}
}
- private fun verifyMarkdownNodeByName(
+ protected fun verifyMarkdownNodeByName(
fileName: String,
name: String,
- withKotlinRuntime: Boolean = false,
- includeNonPublic: Boolean = true
+ modelConfig: ModelConfig = ModelConfig()
) {
- verifyMarkdownNodes(fileName, withKotlinRuntime, includeNonPublic) { model->
+ verifyMarkdownNodes(fileName, modelConfig) { model->
val nodesWithName = model.members.single().members.filter { it.name == name }
if (nodesWithName.isEmpty()) {
throw IllegalArgumentException("Found no nodes named $name")
@@ -495,3 +479,116 @@ class MarkdownFormatTest {
}
}
}
+
+class JSMarkdownFormatTest: BaseMarkdownFormatTest(Platform.js)
+
+class JVMMarkdownFormatTest: BaseMarkdownFormatTest(Platform.jvm) {
+
+ @Test
+ fun enumRef() {
+ verifyMarkdownNode("enumRef", defaultModelConfig)
+ }
+
+ @Test
+ fun javaCodeLiteralTags() {
+ verifyJavaMarkdownNode("javaCodeLiteralTags", defaultModelConfig)
+ }
+
+ @Test
+ fun nullability() {
+ verifyMarkdownNode("nullability", defaultModelConfig)
+ }
+
+ @Test
+ fun exceptionClass() {
+ verifyMarkdownNode(
+ "exceptionClass", ModelConfig(
+ analysisPlatform = analysisPlatform,
+ withKotlinRuntime = true
+ )
+ )
+ verifyMarkdownPackage(
+ "exceptionClass", ModelConfig(
+ analysisPlatform = analysisPlatform,
+ withKotlinRuntime = true
+ )
+ )
+ }
+
+ @Test
+ fun operatorOverloading() {
+ verifyMarkdownNodes("operatorOverloading", defaultModelConfig) { model->
+ model.members.single().members.single { it.name == "C" }.members.filter { it.name == "plus" }
+ }
+ }
+
+ @Test
+ fun extensions() {
+ verifyOutput("testdata/format/extensions.kt", ".package.md", defaultModelConfig) { model, output ->
+ buildPagesAndReadInto(model.members, output)
+ }
+ verifyOutput("testdata/format/extensions.kt", ".class.md", defaultModelConfig) { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ }
+
+ @Test
+ fun summarizeSignaturesProperty() {
+ verifyMarkdownNodes("summarizeSignaturesProperty", defaultModelConfig) { model -> model.members }
+ }
+
+ @Test
+ fun javaSpaceInAuthor() {
+ verifyJavaMarkdownNode("javaSpaceInAuthor", defaultModelConfig)
+ }
+
+ @Test
+ fun javaCodeInParam() {
+ verifyJavaMarkdownNode("javaCodeInParam", defaultModelConfig)
+ }
+
+ @Test
+ fun annotationParams() {
+ verifyMarkdownNode("annotationParams", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
+ }
+
+ @Test fun inheritedLink() {
+ val filePath = "testdata/format/inheritedLink"
+ verifyOutput(
+ filePath,
+ ".md",
+ ModelConfig(
+ roots = arrayOf(
+ contentRootFromPath("$filePath.kt"),
+ contentRootFromPath("$filePath.1.kt")
+ ),
+ withJdk = true,
+ withKotlinRuntime = true,
+ includeNonPublic = false,
+ analysisPlatform = analysisPlatform
+
+ )
+ ) { model, output ->
+ buildPagesAndReadInto(model.members.single { it.name == "p2" }.members.single().members, output)
+ }
+ }
+
+ @Test
+ fun javadocOrderedList() {
+ verifyJavaMarkdownNodes("javadocOrderedList", defaultModelConfig) { model ->
+ model.members.single().members.filter { it.name == "Bar" }
+ }
+ }
+
+ @Test
+ fun jdkLinks() {
+ verifyMarkdownNode("jdkLinks", ModelConfig(withKotlinRuntime = true, analysisPlatform = analysisPlatform))
+ }
+
+ @Test
+ fun javadocHtml() {
+ verifyJavaMarkdownNode("javadocHtml", defaultModelConfig)
+ }
+}
+
+class CommonMarkdownFormatTest: BaseMarkdownFormatTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt
index a5547025..b7fff1e2 100644
--- a/core/src/test/kotlin/format/PackageDocsTest.kt
+++ b/core/src/test/kotlin/format/PackageDocsTest.kt
@@ -1,20 +1,46 @@
package org.jetbrains.dokka.tests.format
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.util.Disposer
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doAnswer
import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.mock
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.tests.InMemoryLocationService
import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment
+import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
+import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Before
import org.junit.Test
import java.io.File
-public class PackageDocsTest {
+class PackageDocsTest {
+
+ private lateinit var testDisposable: Disposable
+
+ @Before
+ fun setup() {
+ testDisposable = Disposer.newDisposable()
+ }
+
+ @After
+ fun cleanup() {
+ Disposer.dispose(testDisposable)
+ }
+
+ fun createPackageDocs(linkResolver: DeclarationLinkResolver?): PackageDocs {
+ val environment = KotlinCoreEnvironment.createForTests(testDisposable, CompilerConfiguration.EMPTY, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+ return PackageDocs(linkResolver, DokkaConsoleLogger, environment, mock(), mock())
+ }
+
@Test fun verifyParse() {
- val docs = PackageDocs(null, DokkaConsoleLogger)
+
+ val docs = createPackageDocs(null)
docs.parse("testdata/packagedocs/stdlib.md", emptyList())
val packageContent = docs.packageContent["kotlin"]!!
val block = (packageContent.children.single() as ContentBlock).children.first() as ContentText
@@ -29,7 +55,7 @@ public class PackageDocsTest {
val mockPackageDescriptor = mock<PackageFragmentDescriptor> {}
- val docs = PackageDocs(mockLinkResolver, DokkaConsoleLogger)
+ val docs = createPackageDocs(mockLinkResolver)
docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor))
checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks")
@@ -37,8 +63,17 @@ public class PackageDocsTest {
fun checkMarkdownOutput(docs: PackageDocs, expectedFilePrefix: String) {
+ val generator = FileGenerator(File(""))
+
val out = StringBuilder()
- val outputBuilder = MarkdownOutputBuilder(out, InMemoryLocationService.root, InMemoryLocationService, KotlinLanguageService(), ".md", emptyList())
+ val outputBuilder = MarkdownOutputBuilder(
+ out,
+ FileLocation(generator.root),
+ generator,
+ KotlinLanguageService(),
+ ".md",
+ emptyList()
+ )
fun checkOutput(content: Content, filePostfix: String) {
outputBuilder.appendContent(content)
val expectedFile = File(expectedFilePrefix + filePostfix)
diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt
index 625d7e46..da5acd6e 100644
--- a/core/src/test/kotlin/issues/IssuesTest.kt
+++ b/core/src/test/kotlin/issues/IssuesTest.kt
@@ -2,17 +2,19 @@ package issues
import org.jetbrains.dokka.DocumentationNode
import org.jetbrains.dokka.NodeKind
-import org.jetbrains.dokka.tests.toTestString
-import org.jetbrains.dokka.tests.verifyModel
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.tests.ModelConfig
+import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel
import org.junit.Test
import kotlin.test.assertEquals
-
-class IssuesTest {
+abstract class BaseIssuesTest(val analysisPlatform: Platform) {
+ val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test
fun errorClasses() {
- verifyModel("testdata/issues/errorClasses.kt", withJdk = true, withKotlinRuntime = true) { model ->
+ checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt",
+ modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model ->
val cls = model.members.single().members.single()
fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name
@@ -26,3 +28,7 @@ class IssuesTest {
}
}
}
+
+class JSIssuesTest: BaseIssuesTest(Platform.js)
+class JVMIssuesTest: BaseIssuesTest(Platform.jvm)
+class CommonIssuesTest: BaseIssuesTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt
index 45c45aa4..7976fccc 100644
--- a/core/src/test/kotlin/javadoc/JavadocTest.kt
+++ b/core/src/test/kotlin/javadoc/JavadocTest.kt
@@ -3,14 +3,18 @@ package org.jetbrains.dokka.javadoc
import com.sun.javadoc.Tag
import com.sun.javadoc.Type
import org.jetbrains.dokka.DokkaConsoleLogger
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.tests.ModelConfig
import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
-import org.jetbrains.dokka.tests.verifyModel
+import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel
import org.junit.Assert.*
import org.junit.Test
class JavadocTest {
+ val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm)
+
@Test fun testTypes() {
- verifyJavadoc("testdata/javadoc/types.kt", withJdk = true) { doc ->
+ verifyJavadoc("testdata/javadoc/types.kt", ModelConfig(analysisPlatform = Platform.jvm, withJdk = true)) { doc ->
val classDoc = doc.classNamed("foo.TypesKt")!!
val method = classDoc.methods().find { it.name() == "foo" }!!
@@ -26,7 +30,7 @@ class JavadocTest {
}
@Test fun testObject() {
- verifyJavadoc("testdata/javadoc/obj.kt") { doc ->
+ verifyJavadoc("testdata/javadoc/obj.kt", defaultModelConfig) { doc ->
val classDoc = doc.classNamed("foo.O")
assertNotNull(classDoc)
@@ -39,7 +43,10 @@ class JavadocTest {
}
@Test fun testException() {
- verifyJavadoc("testdata/javadoc/exception.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/exception.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val classDoc = doc.classNamed("foo.MyException")!!
val member = classDoc.methods().find { it.name() == "foo" }
assertEquals(classDoc, member!!.containingClass())
@@ -47,7 +54,10 @@ class JavadocTest {
}
@Test fun testByteArray() {
- verifyJavadoc("testdata/javadoc/bytearr.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/bytearr.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val classDoc = doc.classNamed("foo.ByteArray")!!
assertNotNull(classDoc.asClassDoc())
@@ -57,7 +67,10 @@ class JavadocTest {
}
@Test fun testStringArray() {
- verifyJavadoc("testdata/javadoc/stringarr.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/stringarr.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val classDoc = doc.classNamed("foo.Foo")!!
assertNotNull(classDoc.asClassDoc())
@@ -70,7 +83,10 @@ class JavadocTest {
}
@Test fun testJvmName() {
- verifyJavadoc("testdata/javadoc/jvmname.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/jvmname.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val classDoc = doc.classNamed("foo.Apple")!!
assertNotNull(classDoc.asClassDoc())
@@ -80,7 +96,10 @@ class JavadocTest {
}
@Test fun testLinkWithParam() {
- verifyJavadoc("testdata/javadoc/paramlink.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/paramlink.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val classDoc = doc.classNamed("demo.Apple")!!
assertNotNull(classDoc.asClassDoc())
val tags = classDoc.inlineTags().filterIsInstance<SeeTagAdapter>()
@@ -91,7 +110,10 @@ class JavadocTest {
}
@Test fun testInternalVisibility() {
- verifyJavadoc("testdata/javadoc/internal.kt", withKotlinRuntime = true, includeNonPublic = false) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/internal.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true, includeNonPublic = false)
+ ) { doc ->
val classDoc = doc.classNamed("foo.Person")!!
val constructors = classDoc.constructors()
assertEquals(1, constructors.size)
@@ -100,7 +122,10 @@ class JavadocTest {
}
@Test fun testSuppress() {
- verifyJavadoc("testdata/javadoc/suppress.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/suppress.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
assertNull(doc.classNamed("Some"))
assertNull(doc.classNamed("SomeAgain"))
assertNull(doc.classNamed("Interface"))
@@ -111,7 +136,10 @@ class JavadocTest {
}
@Test fun testTypeAliases() {
- verifyJavadoc("testdata/javadoc/typealiases.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/typealiases.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
assertNull(doc.classNamed("B"))
assertNull(doc.classNamed("D"))
@@ -126,7 +154,10 @@ class JavadocTest {
}
@Test fun testKDocKeywordsOnMethod() {
- verifyJavadoc("testdata/javadoc/kdocKeywordsOnMethod.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/kdocKeywordsOnMethod.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0]
assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text())
assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text())
@@ -136,7 +167,10 @@ class JavadocTest {
@Test
fun testBlankLineInsideCodeBlock() {
- verifyJavadoc("testdata/javadoc/blankLineInsideCodeBlock.kt", withKotlinRuntime = true) { doc ->
+ verifyJavadoc(
+ "testdata/javadoc/blankLineInsideCodeBlock.kt",
+ ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)
+ ) { doc ->
val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0]
val text = method.inlineTags().joinToString(separator = "", transform = Tag::text)
assertEqualsIgnoringSeparators("""
@@ -153,7 +187,7 @@ class JavadocTest {
@Test
fun testCompanionMethodReference() {
- verifyJavadoc("testdata/javadoc/companionMethodReference.kt") { doc ->
+ verifyJavadoc("testdata/javadoc/companionMethodReference.kt", defaultModelConfig) { doc ->
val classDoc = doc.classNamed("foo.TestClass")!!
val tag = classDoc.inlineTags().filterIsInstance<SeeMethodTagAdapter>().first()
assertEquals("TestClass.Companion", tag.referencedClassName())
@@ -162,12 +196,17 @@ class JavadocTest {
}
private fun verifyJavadoc(name: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- includeNonPublic: Boolean = true,
+ modelConfig: ModelConfig = ModelConfig(),
callback: (ModuleNodeAdapter) -> Unit) {
- verifyModel(name, format = "javadoc", withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, includeNonPublic = includeNonPublic) { model ->
+ checkSourceExistsAndVerifyModel(name,
+ ModelConfig(
+ analysisPlatform = Platform.jvm,
+ format = "javadoc",
+ withJdk = modelConfig.withJdk,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ includeNonPublic = modelConfig.includeNonPublic
+ )) { model ->
val doc = ModuleNodeAdapter(model, StandardReporter(DokkaConsoleLogger), "")
callback(doc)
}
diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt
index ea586041..b8e58a62 100644
--- a/core/src/test/kotlin/model/ClassTest.kt
+++ b/core/src/test/kotlin/model/ClassTest.kt
@@ -2,14 +2,17 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.RefKind
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
-class ClassTest {
+abstract class BaseClassTest(val analysisPlatform: Platform) {
+ protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun emptyClass() {
- verifyModel("testdata/classes/emptyClass.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/emptyClass.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(NodeKind.Class, kind)
assertEquals("Klass", name)
@@ -21,7 +24,7 @@ class ClassTest {
}
@Test fun emptyObject() {
- verifyModel("testdata/classes/emptyObject.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/emptyObject.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(NodeKind.Object, kind)
assertEquals("Obj", name)
@@ -33,7 +36,7 @@ class ClassTest {
}
@Test fun classWithConstructor() {
- verifyModel("testdata/classes/classWithConstructor.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/classWithConstructor.kt", defaultModelConfig) { model ->
with (model.members.single().members.single()) {
assertEquals(NodeKind.Class, kind)
assertEquals("Klass", name)
@@ -63,7 +66,7 @@ class ClassTest {
}
@Test fun classWithFunction() {
- verifyModel("testdata/classes/classWithFunction.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/classWithFunction.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(NodeKind.Class, kind)
assertEquals("Klass", name)
@@ -93,7 +96,7 @@ class ClassTest {
}
@Test fun classWithProperty() {
- verifyModel("testdata/classes/classWithProperty.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/classWithProperty.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(NodeKind.Class, kind)
assertEquals("Klass", name)
@@ -123,7 +126,7 @@ class ClassTest {
}
@Test fun classWithCompanionObject() {
- verifyModel("testdata/classes/classWithCompanionObject.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/classWithCompanionObject.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(NodeKind.Class, kind)
assertEquals("Klass", name)
@@ -151,33 +154,25 @@ class ClassTest {
}
}
- @Test fun annotatedClass() {
- verifyPackageMember("testdata/classes/annotatedClass.kt", withKotlinRuntime = true) { cls ->
- assertEquals(1, cls.annotations.count())
- with(cls.annotations[0]) {
- assertEquals("Strictfp", name)
- assertEquals(Content.Empty, content)
- assertEquals(NodeKind.Annotation, kind)
- }
- }
- }
-
@Test fun dataClass() {
- verifyPackageMember("testdata/classes/dataClass.kt") { cls ->
+ verifyPackageMember("testdata/classes/dataClass.kt", defaultModelConfig) { cls ->
val modifiers = cls.details(NodeKind.Modifier).map { it.name }
assertTrue("data" in modifiers)
}
}
@Test fun sealedClass() {
- verifyPackageMember("testdata/classes/sealedClass.kt") { cls ->
+ verifyPackageMember("testdata/classes/sealedClass.kt", defaultModelConfig) { cls ->
val modifiers = cls.details(NodeKind.Modifier).map { it.name }
assertEquals(1, modifiers.count { it == "sealed" })
}
}
@Test fun annotatedClassWithAnnotationParameters() {
- verifyModel("testdata/classes/annotatedClassWithAnnotationParameters.kt") { model ->
+ checkSourceExistsAndVerifyModel(
+ "testdata/classes/annotatedClassWithAnnotationParameters.kt",
+ defaultModelConfig
+ ) { model ->
with(model.members.single().members.single()) {
with(deprecation!!) {
assertEquals("Deprecated", name)
@@ -197,29 +192,8 @@ class ClassTest {
}
}
- @Test fun javaAnnotationClass() {
- verifyModel("testdata/classes/javaAnnotationClass.kt", withJdk = true) { model ->
- with(model.members.single().members.single()) {
- assertEquals(1, annotations.count())
- with(annotations[0]) {
- assertEquals("Retention", name)
- assertEquals(Content.Empty, content)
- assertEquals(NodeKind.Annotation, kind)
- with(details[0]) {
- assertEquals(NodeKind.Parameter, kind)
- assertEquals(1, details.count())
- with(details[0]) {
- assertEquals(NodeKind.Value, kind)
- assertEquals("RetentionPolicy.SOURCE", name)
- }
- }
- }
- }
- }
- }
-
@Test fun notOpenClass() {
- verifyModel("testdata/classes/notOpenClass.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/notOpenClass.kt", defaultModelConfig) { model ->
with(model.members.single().members.first { it.name == "D"}.members.first { it.name == "f" }) {
val modifiers = details(NodeKind.Modifier)
assertEquals(2, modifiers.size)
@@ -232,7 +206,7 @@ class ClassTest {
}
@Test fun indirectOverride() {
- verifyModel("testdata/classes/indirectOverride.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/indirectOverride.kt", defaultModelConfig) { model ->
with(model.members.single().members.first { it.name == "E"}.members.first { it.name == "foo" }) {
val modifiers = details(NodeKind.Modifier)
assertEquals(2, modifiers.size)
@@ -245,7 +219,7 @@ class ClassTest {
}
@Test fun innerClass() {
- verifyPackageMember("testdata/classes/innerClass.kt") { cls ->
+ verifyPackageMember("testdata/classes/innerClass.kt", defaultModelConfig) { cls ->
val innerClass = cls.members.single { it.name == "D" }
val modifiers = innerClass.details(NodeKind.Modifier)
assertEquals(3, modifiers.size)
@@ -254,7 +228,7 @@ class ClassTest {
}
@Test fun companionObjectExtension() {
- verifyModel("testdata/classes/companionObjectExtension.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model ->
val pkg = model.members.single()
val cls = pkg.members.single { it.name == "Foo" }
val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty }
@@ -263,7 +237,7 @@ class ClassTest {
}
@Test fun secondaryConstructor() {
- verifyPackageMember("testdata/classes/secondaryConstructor.kt") { cls ->
+ verifyPackageMember("testdata/classes/secondaryConstructor.kt", defaultModelConfig) { cls ->
val constructors = cls.members(NodeKind.Constructor)
assertEquals(2, constructors.size)
with (constructors.first { it.details(NodeKind.Parameter).size == 1}) {
@@ -274,7 +248,7 @@ class ClassTest {
}
@Test fun sinceKotlin() {
- verifyModel("testdata/classes/sinceKotlin.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/classes/sinceKotlin.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(listOf("Kotlin 1.1"), platforms)
}
@@ -282,7 +256,10 @@ class ClassTest {
}
@Test fun privateCompanionObject() {
- verifyModel("testdata/classes/privateCompanionObject.kt", includeNonPublic = false) { model ->
+ checkSourceExistsAndVerifyModel(
+ "testdata/classes/privateCompanionObject.kt",
+ modelConfig = ModelConfig(analysisPlatform = analysisPlatform, includeNonPublic = false)
+ ) { model ->
with(model.members.single().members.single()) {
assertEquals(0, members(NodeKind.CompanionObjectFunction).size)
assertEquals(0, members(NodeKind.CompanionObjectProperty).size)
@@ -291,3 +268,51 @@ class ClassTest {
}
}
+
+class JSClassTest: BaseClassTest(Platform.js) {}
+
+class JVMClassTest: BaseClassTest(Platform.jvm) {
+ @Test
+ fun annotatedClass() {
+ verifyPackageMember("testdata/classes/annotatedClass.kt", ModelConfig(
+ analysisPlatform = analysisPlatform,
+ withKotlinRuntime = true
+ )
+ ) { cls ->
+ Assert.assertEquals(1, cls.annotations.count())
+ with(cls.annotations[0]) {
+ Assert.assertEquals("Strictfp", name)
+ Assert.assertEquals(Content.Empty, content)
+ Assert.assertEquals(NodeKind.Annotation, kind)
+ }
+ }
+ }
+
+
+ @Test fun javaAnnotationClass() {
+ checkSourceExistsAndVerifyModel(
+ "testdata/classes/javaAnnotationClass.kt",
+ modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true)
+ ) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals(1, annotations.count())
+ with(annotations[0]) {
+ Assert.assertEquals("Retention", name)
+ Assert.assertEquals(Content.Empty, content)
+ Assert.assertEquals(NodeKind.Annotation, kind)
+ with(details[0]) {
+ Assert.assertEquals(NodeKind.Parameter, kind)
+ Assert.assertEquals(1, details.count())
+ with(details[0]) {
+ Assert.assertEquals(NodeKind.Value, kind)
+ Assert.assertEquals("RetentionPolicy.SOURCE", name)
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+class CommonClassTest: BaseClassTest(Platform.common) {} \ No newline at end of file
diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt
index 3752bb8c..08aa3572 100644
--- a/core/src/test/kotlin/model/CommentTest.kt
+++ b/core/src/test/kotlin/model/CommentTest.kt
@@ -4,10 +4,10 @@ import org.junit.Test
import org.junit.Assert.*
import org.jetbrains.dokka.*
-public class CommentTest {
-
+abstract class BaseCommentTest(val analysisPlatform: Platform) {
+ val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun codeBlockComment() {
- verifyModel("testdata/comments/codeBlockComment.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/codeBlockComment.kt", defaultModelConfig) { model ->
with(model.members.single().members.first()) {
assertEqualsIgnoringSeparators("""[code lang=brainfuck]
|
@@ -30,7 +30,7 @@ public class CommentTest {
}
@Test fun emptyDoc() {
- verifyModel("testdata/comments/emptyDoc.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/emptyDoc.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(Content.Empty, content)
}
@@ -38,7 +38,7 @@ public class CommentTest {
}
@Test fun emptyDocButComment() {
- verifyModel("testdata/comments/emptyDocButComment.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/emptyDocButComment.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(Content.Empty, content)
}
@@ -46,7 +46,7 @@ public class CommentTest {
}
@Test fun multilineDoc() {
- verifyModel("testdata/comments/multilineDoc.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/multilineDoc.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("doc1", content.summary.toTestString())
assertEquals("doc2\ndoc3", content.description.toTestString())
@@ -55,7 +55,7 @@ public class CommentTest {
}
@Test fun multilineDocWithComment() {
- verifyModel("testdata/comments/multilineDocWithComment.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/multilineDocWithComment.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("doc1", content.summary.toTestString())
assertEquals("doc2\ndoc3", content.description.toTestString())
@@ -64,7 +64,7 @@ public class CommentTest {
}
@Test fun oneLineDoc() {
- verifyModel("testdata/comments/oneLineDoc.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/oneLineDoc.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("doc", content.summary.toTestString())
}
@@ -72,7 +72,7 @@ public class CommentTest {
}
@Test fun oneLineDocWithComment() {
- verifyModel("testdata/comments/oneLineDocWithComment.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithComment.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("doc", content.summary.toTestString())
}
@@ -80,7 +80,7 @@ public class CommentTest {
}
@Test fun oneLineDocWithEmptyLine() {
- verifyModel("testdata/comments/oneLineDocWithEmptyLine.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithEmptyLine.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("doc", content.summary.toTestString())
}
@@ -88,7 +88,7 @@ public class CommentTest {
}
@Test fun emptySection() {
- verifyModel("testdata/comments/emptySection.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/emptySection.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Summary", content.summary.toTestString())
assertEquals(1, content.sections.count())
@@ -101,7 +101,7 @@ public class CommentTest {
}
@Test fun quotes() {
- verifyModel("testdata/comments/quotes.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/quotes.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("it's \"useful\"", content.summary.toTestString())
}
@@ -109,7 +109,7 @@ public class CommentTest {
}
@Test fun section1() {
- verifyModel("testdata/comments/section1.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/section1.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Summary", content.summary.toTestString())
assertEquals(1, content.sections.count())
@@ -122,7 +122,7 @@ public class CommentTest {
}
@Test fun section2() {
- verifyModel("testdata/comments/section2.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/section2.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Summary", content.summary.toTestString())
assertEquals(2, content.sections.count())
@@ -139,7 +139,7 @@ public class CommentTest {
}
@Test fun multilineSection() {
- verifyModel("testdata/comments/multilineSection.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/multilineSection.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Summary", content.summary.toTestString())
assertEquals(1, content.sections.count())
@@ -153,7 +153,7 @@ line two""", toTestString())
}
@Test fun directive() {
- verifyModel("testdata/comments/directive.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model ->
with(model.members.single().members.first()) {
assertEquals("Summary", content.summary.toTestString())
with (content.description) {
@@ -184,3 +184,7 @@ line two""", toTestString())
}
}
}
+
+class JSCommentTest: BaseCommentTest(Platform.js)
+class JVMCommentTest: BaseCommentTest(Platform.jvm)
+class CommonCommentTest: BaseCommentTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt
index 32910682..47685df2 100644
--- a/core/src/test/kotlin/model/FunctionTest.kt
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -2,14 +2,17 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.Platform
+import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
-import kotlin.test.assertNotNull
-class FunctionTest {
+abstract class BaseFunctionTest(val analysisPlatform: Platform) {
+ protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun function() {
- verifyModel("testdata/functions/function.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/function.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("fn", name)
assertEquals(NodeKind.Function, kind)
@@ -22,7 +25,7 @@ class FunctionTest {
}
@Test fun functionWithReceiver() {
- verifyModel("testdata/functions/functionWithReceiver.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/functionWithReceiver.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("kotlin.String", name)
assertEquals(NodeKind.ExternalClass, kind)
@@ -54,7 +57,7 @@ class FunctionTest {
}
@Test fun genericFunction() {
- verifyModel("testdata/functions/genericFunction.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/genericFunction.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("generic", name)
assertEquals(NodeKind.Function, kind)
@@ -78,7 +81,7 @@ class FunctionTest {
}
}
@Test fun genericFunctionWithConstraints() {
- verifyModel("testdata/functions/genericFunctionWithConstraints.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/genericFunctionWithConstraints.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("generic", name)
assertEquals(NodeKind.Function, kind)
@@ -118,7 +121,7 @@ class FunctionTest {
}
@Test fun functionWithParams() {
- verifyModel("testdata/functions/functionWithParams.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/functionWithParams.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("function", name)
assertEquals(NodeKind.Function, kind)
@@ -143,32 +146,21 @@ Documentation""", content.description.toTestString())
}
}
- @Test fun annotatedFunction() {
- verifyPackageMember("testdata/functions/annotatedFunction.kt", withKotlinRuntime = true) { func ->
- assertEquals(1, func.annotations.count())
- with(func.annotations[0]) {
- assertEquals("Strictfp", name)
- assertEquals(Content.Empty, content)
- assertEquals(NodeKind.Annotation, kind)
- }
- }
- }
-
@Test fun functionWithNotDocumentedAnnotation() {
- verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt") { func ->
+ verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt", defaultModelConfig) { func ->
assertEquals(0, func.annotations.count())
}
}
@Test fun inlineFunction() {
- verifyPackageMember("testdata/functions/inlineFunction.kt") { func ->
+ verifyPackageMember("testdata/functions/inlineFunction.kt", defaultModelConfig) { func ->
val modifiers = func.details(NodeKind.Modifier).map { it.name }
assertTrue("inline" in modifiers)
}
}
@Test fun functionWithAnnotatedParam() {
- verifyModel("testdata/functions/functionWithAnnotatedParam.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/functionWithAnnotatedParam.kt", defaultModelConfig) { model ->
with(model.members.single().members.single { it.name == "function" }) {
with(details(NodeKind.Parameter).first()) {
assertEquals(1, annotations.count())
@@ -183,7 +175,7 @@ Documentation""", content.description.toTestString())
}
@Test fun functionWithNoinlineParam() {
- verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt") { func ->
+ verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt", defaultModelConfig) { func ->
with(func.details(NodeKind.Parameter).first()) {
val modifiers = details(NodeKind.Modifier).map { it.name }
assertTrue("noinline" in modifiers)
@@ -192,7 +184,10 @@ Documentation""", content.description.toTestString())
}
@Test fun annotatedFunctionWithAnnotationParameters() {
- verifyModel("testdata/functions/annotatedFunctionWithAnnotationParameters.kt") { model ->
+ checkSourceExistsAndVerifyModel(
+ "testdata/functions/annotatedFunctionWithAnnotationParameters.kt",
+ defaultModelConfig
+ ) { model ->
with(model.members.single().members.single { it.name == "f" }) {
assertEquals(1, annotations.count())
with(annotations[0]) {
@@ -214,7 +209,7 @@ Documentation""", content.description.toTestString())
}
@Test fun functionWithDefaultParameter() {
- verifyModel("testdata/functions/functionWithDefaultParameter.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/functionWithDefaultParameter.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
with(details.elementAt(3)) {
val value = details(NodeKind.Value)
@@ -228,10 +223,32 @@ Documentation""", content.description.toTestString())
}
@Test fun sinceKotlin() {
- verifyModel("testdata/functions/sinceKotlin.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/functions/sinceKotlin.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(listOf("Kotlin 1.1"), platforms)
}
}
}
}
+
+class JSFunctionTest: BaseFunctionTest(Platform.js)
+
+class JVMFunctionTest: BaseFunctionTest(Platform.jvm) {
+ @Test
+ fun annotatedFunction() {
+ verifyPackageMember("testdata/functions/annotatedFunction.kt", ModelConfig(
+ analysisPlatform = Platform.jvm,
+ withKotlinRuntime = true
+ )) { func ->
+ Assert.assertEquals(1, func.annotations.count())
+ with(func.annotations[0]) {
+ Assert.assertEquals("Strictfp", name)
+ Assert.assertEquals(Content.Empty, content)
+ Assert.assertEquals(NodeKind.Annotation, kind)
+ }
+ }
+ }
+
+}
+
+class CommonFunctionTest: BaseFunctionTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
index e6c22ee4..13bb72ba 100644
--- a/core/src/test/kotlin/model/JavaTest.kt
+++ b/core/src/test/kotlin/model/JavaTest.kt
@@ -1,14 +1,16 @@
package org.jetbrains.dokka.tests
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.RefKind
import org.junit.Assert.*
import org.junit.Ignore
import org.junit.Test
public class JavaTest {
+ private val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm)
@Test fun function() {
- verifyJavaPackageMember("testdata/java/member.java") { cls ->
+ verifyJavaPackageMember("testdata/java/member.java", defaultModelConfig) { cls ->
assertEquals("Test", cls.name)
assertEquals(NodeKind.Class, cls.kind)
with(cls.members(NodeKind.Function).single()) {
@@ -18,12 +20,12 @@ public class JavaTest {
with(content.sections[0]) {
assertEquals("Parameters", tag)
assertEquals("name", subjectName)
- assertEquals("is String parameter", toTestString())
+ assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString())
}
with(content.sections[1]) {
assertEquals("Parameters", tag)
assertEquals("value", subjectName)
- assertEquals("is int parameter", toTestString())
+ assertEquals("render(Type:String,SUMMARY): is int parameter", toTestString())
}
with(content.sections[2]) {
assertEquals("Author", tag)
@@ -45,7 +47,7 @@ public class JavaTest {
}
@Test fun memberWithModifiers() {
- verifyJavaPackageMember("testdata/java/memberWithModifiers.java") { cls ->
+ verifyJavaPackageMember("testdata/java/memberWithModifiers.java", defaultModelConfig) { cls ->
val modifiers = cls.details(NodeKind.Modifier).map { it.name }
assertTrue("abstract" in modifiers)
with(cls.members.single { it.name == "fn" }) {
@@ -58,7 +60,7 @@ public class JavaTest {
}
@Test fun superClass() {
- verifyJavaPackageMember("testdata/java/superClass.java") { cls ->
+ verifyJavaPackageMember("testdata/java/superClass.java", defaultModelConfig) { cls ->
val superTypes = cls.details(NodeKind.Supertype)
assertEquals(2, superTypes.size)
assertEquals("Exception", superTypes[0].name)
@@ -67,7 +69,7 @@ public class JavaTest {
}
@Test fun arrayType() {
- verifyJavaPackageMember("testdata/java/arrayType.java") { cls ->
+ verifyJavaPackageMember("testdata/java/arrayType.java", defaultModelConfig) { cls ->
with(cls.members(NodeKind.Function).single()) {
val type = detail(NodeKind.Type)
assertEquals("Array", type.name)
@@ -81,7 +83,7 @@ public class JavaTest {
}
@Test fun typeParameter() {
- verifyJavaPackageMember("testdata/java/typeParameter.java") { cls ->
+ verifyJavaPackageMember("testdata/java/typeParameter.java", defaultModelConfig) { cls ->
val typeParameters = cls.details(NodeKind.TypeParameter)
with(typeParameters.single()) {
assertEquals("T", name)
@@ -100,7 +102,7 @@ public class JavaTest {
}
@Test fun constructors() {
- verifyJavaPackageMember("testdata/java/constructors.java") { cls ->
+ verifyJavaPackageMember("testdata/java/constructors.java", defaultModelConfig) { cls ->
val constructors = cls.members(NodeKind.Constructor)
assertEquals(2, constructors.size)
with(constructors[0]) {
@@ -110,14 +112,14 @@ public class JavaTest {
}
@Test fun innerClass() {
- verifyJavaPackageMember("testdata/java/InnerClass.java") { cls ->
+ verifyJavaPackageMember("testdata/java/InnerClass.java", defaultModelConfig) { cls ->
val innerClass = cls.members(NodeKind.Class).single()
assertEquals("D", innerClass.name)
}
}
@Test fun varargs() {
- verifyJavaPackageMember("testdata/java/varargs.java") { cls ->
+ verifyJavaPackageMember("testdata/java/varargs.java", defaultModelConfig) { cls ->
val fn = cls.members(NodeKind.Function).single()
val param = fn.detail(NodeKind.Parameter)
assertEquals("vararg", param.details(NodeKind.Modifier).first().name)
@@ -128,7 +130,7 @@ public class JavaTest {
}
@Test fun fields() {
- verifyJavaPackageMember("testdata/java/field.java") { cls ->
+ verifyJavaPackageMember("testdata/java/field.java", defaultModelConfig) { cls ->
val i = cls.members(NodeKind.Property).single { it.name == "i" }
assertEquals("Int", i.detail(NodeKind.Type).name)
assertTrue("var" in i.details(NodeKind.Modifier).map { it.name })
@@ -141,7 +143,7 @@ public class JavaTest {
}
@Test fun staticMethod() {
- verifyJavaPackageMember("testdata/java/staticMethod.java") { cls ->
+ verifyJavaPackageMember("testdata/java/staticMethod.java", defaultModelConfig) { cls ->
val m = cls.members(NodeKind.Function).single { it.name == "foo" }
assertTrue("static" in m.details(NodeKind.Modifier).map { it.name })
}
@@ -154,13 +156,13 @@ public class JavaTest {
* Proposed tag `@exclude` for it, but not supported yet
*/
@Ignore("@suppress not supported in Java!") @Test fun suppressTag() {
- verifyJavaPackageMember("testdata/java/suppressTag.java") { cls ->
+ verifyJavaPackageMember("testdata/java/suppressTag.java", defaultModelConfig) { cls ->
assertEquals(1, cls.members(NodeKind.Function).size)
}
}
@Test fun annotatedAnnotation() {
- verifyJavaPackageMember("testdata/java/annotatedAnnotation.java") { cls ->
+ verifyJavaPackageMember("testdata/java/annotatedAnnotation.java", defaultModelConfig) { cls ->
assertEquals(1, cls.annotations.size)
with(cls.annotations[0]) {
assertEquals(1, details.count())
@@ -177,29 +179,29 @@ public class JavaTest {
}
@Test fun deprecation() {
- verifyJavaPackageMember("testdata/java/deprecation.java") { cls ->
+ verifyJavaPackageMember("testdata/java/deprecation.java", defaultModelConfig) { cls ->
val fn = cls.members(NodeKind.Function).single()
assertEquals("This should no longer be used", fn.deprecation!!.content.toTestString())
}
}
@Test fun javaLangObject() {
- verifyJavaPackageMember("testdata/java/javaLangObject.java") { cls ->
+ verifyJavaPackageMember("testdata/java/javaLangObject.java", defaultModelConfig) { cls ->
val fn = cls.members(NodeKind.Function).single()
assertEquals("Any", fn.detail(NodeKind.Type).name)
}
}
@Test fun enumValues() {
- verifyJavaPackageMember("testdata/java/enumValues.java") { cls ->
+ verifyJavaPackageMember("testdata/java/enumValues.java", defaultModelConfig) { cls ->
val superTypes = cls.details(NodeKind.Supertype)
- assertEquals(0, superTypes.size)
+ assertEquals(1, superTypes.size)
assertEquals(1, cls.members(NodeKind.EnumItem).size)
}
}
@Test fun inheritorLinks() {
- verifyJavaPackageMember("testdata/java/InheritorLinks.java") { cls ->
+ verifyJavaPackageMember("testdata/java/InheritorLinks.java", defaultModelConfig) { cls ->
val fooClass = cls.members.single { it.name == "Foo" }
val inheritors = fooClass.references(RefKind.Inheritor)
assertEquals(1, inheritors.size)
diff --git a/core/src/test/kotlin/model/KotlinAsJavaTest.kt b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
index d24d8bdd..f6f0aa20 100644
--- a/core/src/test/kotlin/model/KotlinAsJavaTest.kt
+++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
@@ -2,6 +2,7 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.DocumentationModule
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.Platform
import org.junit.Test
import org.junit.Assert.assertEquals
@@ -30,11 +31,15 @@ class KotlinAsJavaTest {
}
fun verifyModelAsJava(source: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
+ modelConfig: ModelConfig = ModelConfig(),
verifier: (DocumentationModule) -> Unit) {
- verifyModel(source,
- withJdk = withJdk, withKotlinRuntime = withKotlinRuntime,
+ checkSourceExistsAndVerifyModel(
+ source,
+ modelConfig = ModelConfig(
+ withJdk = modelConfig.withJdk,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
format = "html-as-java",
- verifier = verifier)
+ analysisPlatform = Platform.jvm),
+ verifier = verifier
+ )
}
diff --git a/core/src/test/kotlin/model/LinkTest.kt b/core/src/test/kotlin/model/LinkTest.kt
index 6b72525f..9b2f9f0d 100644
--- a/core/src/test/kotlin/model/LinkTest.kt
+++ b/core/src/test/kotlin/model/LinkTest.kt
@@ -3,12 +3,14 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.ContentBlock
import org.jetbrains.dokka.ContentNodeLazyLink
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.Platform
import org.junit.Assert.assertEquals
import org.junit.Test
-class LinkTest {
+abstract class BaseLinkTest(val analysisPlatform: Platform) {
+ private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun linkToSelf() {
- verifyModel("testdata/links/linkToSelf.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToSelf.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Foo", name)
assertEquals(NodeKind.Class, kind)
@@ -18,7 +20,7 @@ class LinkTest {
}
@Test fun linkToMember() {
- verifyModel("testdata/links/linkToMember.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToMember.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Foo", name)
assertEquals(NodeKind.Class, kind)
@@ -28,7 +30,7 @@ class LinkTest {
}
@Test fun linkToConstantWithUnderscores() {
- verifyModel("testdata/links/linkToConstantWithUnderscores.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToConstantWithUnderscores.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Foo", name)
assertEquals(NodeKind.Class, kind)
@@ -38,7 +40,7 @@ class LinkTest {
}
@Test fun linkToQualifiedMember() {
- verifyModel("testdata/links/linkToQualifiedMember.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToQualifiedMember.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Foo", name)
assertEquals(NodeKind.Class, kind)
@@ -48,7 +50,7 @@ class LinkTest {
}
@Test fun linkToParam() {
- verifyModel("testdata/links/linkToParam.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToParam.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("Foo", name)
assertEquals(NodeKind.Function, kind)
@@ -58,7 +60,7 @@ class LinkTest {
}
@Test fun linkToPackage() {
- verifyModel("testdata/links/linkToPackage.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/links/linkToPackage.kt", defaultModelConfig) { model ->
val packageNode = model.members.single()
with(packageNode) {
assertEquals(this.name, "test.magic")
@@ -72,4 +74,8 @@ class LinkTest {
}
}
-} \ No newline at end of file
+}
+
+class JSLinkTest: BaseLinkTest(Platform.js)
+class JVMLinkTest: BaseLinkTest(Platform.jvm)
+class CommonLinkTest: BaseLinkTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt
index 052f0d28..80a2fd56 100644
--- a/core/src/test/kotlin/model/PackageTest.kt
+++ b/core/src/test/kotlin/model/PackageTest.kt
@@ -1,15 +1,14 @@
package org.jetbrains.dokka.tests
-import org.jetbrains.dokka.Content
-import org.jetbrains.dokka.NodeKind
-import org.jetbrains.dokka.PackageOptionsImpl
+import org.jetbrains.dokka.*
import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.junit.Assert.*
import org.junit.Test
-public class PackageTest {
+abstract class BasePackageTest(val analysisPlatform: Platform) {
+ val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun rootPackage() {
- verifyModel("testdata/packages/rootPackage.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/packages/rootPackage.kt", defaultModelConfig) { model ->
with(model.members.single()) {
assertEquals(NodeKind.Package, kind)
assertEquals("", name)
@@ -22,7 +21,7 @@ public class PackageTest {
}
@Test fun simpleNamePackage() {
- verifyModel("testdata/packages/simpleNamePackage.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/packages/simpleNamePackage.kt", defaultModelConfig) { model ->
with(model.members.single()) {
assertEquals(NodeKind.Package, kind)
assertEquals("simple", name)
@@ -35,7 +34,7 @@ public class PackageTest {
}
@Test fun dottedNamePackage() {
- verifyModel("testdata/packages/dottedNamePackage.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/packages/dottedNamePackage.kt", defaultModelConfig) { model ->
with(model.members.single()) {
assertEquals(NodeKind.Package, kind)
assertEquals("dot.name", name)
@@ -48,8 +47,15 @@ public class PackageTest {
}
@Test fun multipleFiles() {
- verifyModel(KotlinSourceRoot("testdata/packages/dottedNamePackage.kt"),
- KotlinSourceRoot("testdata/packages/simpleNamePackage.kt")) { model ->
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(
+ KotlinSourceRoot("testdata/packages/dottedNamePackage.kt"),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage.kt")
+ ),
+ analysisPlatform = analysisPlatform
+ )
+ ) { model ->
assertEquals(2, model.members.count())
with(model.members.single { it.name == "simple" }) {
assertEquals(NodeKind.Package, kind)
@@ -70,8 +76,15 @@ public class PackageTest {
}
@Test fun multipleFilesSamePackage() {
- verifyModel(KotlinSourceRoot("testdata/packages/simpleNamePackage.kt"),
- KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt")) { model ->
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(
+ KotlinSourceRoot("testdata/packages/simpleNamePackage.kt"),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt")
+ ),
+ analysisPlatform = analysisPlatform
+ )
+ ) { model ->
assertEquals(1, model.members.count())
with(model.members.elementAt(0)) {
assertEquals(NodeKind.Package, kind)
@@ -85,7 +98,12 @@ public class PackageTest {
}
@Test fun classAtPackageLevel() {
- verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt")) { model ->
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt")),
+ analysisPlatform = analysisPlatform
+ )
+ ) { model ->
assertEquals(1, model.members.count())
with(model.members.elementAt(0)) {
assertEquals(NodeKind.Package, kind)
@@ -99,8 +117,15 @@ public class PackageTest {
}
@Test fun suppressAtPackageLevel() {
- verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt"),
- perPackageOptions = listOf(PackageOptionsImpl(prefix = "simple.name", suppress = true))) { model ->
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt")),
+ perPackageOptions = listOf(
+ PackageOptionsImpl(prefix = "simple.name", suppress = true)
+ ),
+ analysisPlatform = analysisPlatform
+ )
+ ) { model ->
assertEquals(1, model.members.count())
with(model.members.elementAt(0)) {
assertEquals(NodeKind.Package, kind)
@@ -113,3 +138,7 @@ public class PackageTest {
}
}
}
+
+class JSPackageTest : BasePackageTest(Platform.js)
+class JVMPackageTest : BasePackageTest(Platform.jvm)
+class CommonPackageTest : BasePackageTest(Platform.common) \ No newline at end of file
diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt
index 0ee0e0f5..a41ab5a3 100644
--- a/core/src/test/kotlin/model/PropertyTest.kt
+++ b/core/src/test/kotlin/model/PropertyTest.kt
@@ -1,15 +1,16 @@
package org.jetbrains.dokka.tests
-import org.jetbrains.dokka.Content
-import org.jetbrains.dokka.NodeKind
-import org.jetbrains.dokka.RefKind
+import org.jetbrains.dokka.*
+import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
-class PropertyTest {
+abstract class BasePropertyTest(val analysisPlatform: Platform) {
+
+ protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform)
@Test fun valueProperty() {
- verifyModel("testdata/properties/valueProperty.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/valueProperty.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("property", name)
assertEquals(NodeKind.Property, kind)
@@ -22,7 +23,7 @@ class PropertyTest {
}
@Test fun variableProperty() {
- verifyModel("testdata/properties/variableProperty.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/variableProperty.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("property", name)
assertEquals(NodeKind.Property, kind)
@@ -35,7 +36,7 @@ class PropertyTest {
}
@Test fun valuePropertyWithGetter() {
- verifyModel("testdata/properties/valuePropertyWithGetter.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/valuePropertyWithGetter.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("property", name)
assertEquals(NodeKind.Property, kind)
@@ -48,7 +49,7 @@ class PropertyTest {
}
@Test fun variablePropertyWithAccessors() {
- verifyModel("testdata/properties/variablePropertyWithAccessors.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/variablePropertyWithAccessors.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals("property", name)
assertEquals(NodeKind.Property, kind)
@@ -64,21 +65,11 @@ class PropertyTest {
}
}
- @Test fun annotatedProperty() {
- verifyModel("testdata/properties/annotatedProperty.kt", withKotlinRuntime = true) { model ->
- with(model.members.single().members.single()) {
- assertEquals(1, annotations.count())
- with(annotations[0]) {
- assertEquals("Volatile", name)
- assertEquals(Content.Empty, content)
- assertEquals(NodeKind.Annotation, kind)
- }
- }
- }
- }
-
@Test fun propertyWithReceiver() {
- verifyModel("testdata/properties/propertyWithReceiver.kt") { model ->
+ checkSourceExistsAndVerifyModel(
+ "testdata/properties/propertyWithReceiver.kt",
+ defaultModelConfig
+ ) { model ->
with(model.members.single().members.single()) {
assertEquals("kotlin.String", name)
assertEquals(NodeKind.ExternalClass, kind)
@@ -91,7 +82,7 @@ class PropertyTest {
}
@Test fun propertyOverride() {
- verifyModel("testdata/properties/propertyOverride.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/propertyOverride.kt", defaultModelConfig) { model ->
with(model.members.single().members.single { it.name == "Bar" }.members.single { it.name == "xyzzy"}) {
assertEquals("xyzzy", name)
val override = references(RefKind.Override).single().to
@@ -102,10 +93,37 @@ class PropertyTest {
}
@Test fun sinceKotlin() {
- verifyModel("testdata/properties/sinceKotlin.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/properties/sinceKotlin.kt", defaultModelConfig) { model ->
with(model.members.single().members.single()) {
assertEquals(listOf("Kotlin 1.1"), platforms)
}
}
}
}
+
+class JSPropertyTest: BasePropertyTest(Platform.js) {}
+
+class JVMPropertyTest : BasePropertyTest(Platform.jvm) {
+ @Test
+ fun annotatedProperty() {
+ checkSourceExistsAndVerifyModel(
+ "testdata/properties/annotatedProperty.kt",
+ modelConfig = ModelConfig(
+ analysisPlatform = analysisPlatform,
+ withKotlinRuntime = true
+ )
+ ) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals(1, annotations.count())
+ with(annotations[0]) {
+ Assert.assertEquals("Volatile", name)
+ Assert.assertEquals(Content.Empty, content)
+ Assert.assertEquals(NodeKind.Annotation, kind)
+ }
+ }
+ }
+ }
+
+}
+
+class CommonPropertyTest: BasePropertyTest(Platform.common) {} \ No newline at end of file
diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt
index c653ac83..a011b44f 100644
--- a/core/src/test/kotlin/model/TypeAliasTest.kt
+++ b/core/src/test/kotlin/model/TypeAliasTest.kt
@@ -8,7 +8,7 @@ import org.junit.Test
class TypeAliasTest {
@Test
fun testSimple() {
- verifyModel("testdata/typealias/simple.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/simple.kt") {
val pkg = it.members.single()
with(pkg.member(NodeKind.TypeAlias)) {
assertEquals(Content.Empty, content)
@@ -20,7 +20,7 @@ class TypeAliasTest {
@Test
fun testInheritanceFromTypeAlias() {
- verifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") {
val pkg = it.members.single()
with(pkg.member(NodeKind.TypeAlias)) {
assertEquals(Content.Empty, content)
@@ -36,7 +36,7 @@ class TypeAliasTest {
@Test
fun testChain() {
- verifyModel("testdata/typealias/chain.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/chain.kt") {
val pkg = it.members.single()
with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) {
assertEquals(Content.Empty, content)
@@ -51,7 +51,7 @@ class TypeAliasTest {
@Test
fun testDocumented() {
- verifyModel("testdata/typealias/documented.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/documented.kt") {
val pkg = it.members.single()
with(pkg.member(NodeKind.TypeAlias)) {
assertEquals("Just typealias", content.summary.toTestString())
@@ -61,7 +61,7 @@ class TypeAliasTest {
@Test
fun testDeprecated() {
- verifyModel("testdata/typealias/deprecated.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/deprecated.kt") {
val pkg = it.members.single()
with(pkg.member(NodeKind.TypeAlias)) {
assertEquals(Content.Empty, content)
@@ -73,7 +73,7 @@ class TypeAliasTest {
@Test
fun testGeneric() {
- verifyModel("testdata/typealias/generic.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/generic.kt") {
val pkg = it.members.single()
with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) {
assertEquals("Any", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name)
@@ -88,7 +88,7 @@ class TypeAliasTest {
@Test
fun testFunctional() {
- verifyModel("testdata/typealias/functional.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/functional.kt") {
val pkg = it.members.single()
with(pkg.member(NodeKind.TypeAlias)) {
assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name)
@@ -105,7 +105,7 @@ class TypeAliasTest {
@Test
fun testAsTypeBoundWithVariance() {
- verifyModel("testdata/typealias/asTypeBoundWithVariance.kt") {
+ checkSourceExistsAndVerifyModel("testdata/typealias/asTypeBoundWithVariance.kt") {
val pkg = it.members.single()
with(pkg.members(NodeKind.Class).find { it.name == "C" }!!) {
val tParam = detail(NodeKind.TypeParameter)
@@ -123,7 +123,7 @@ class TypeAliasTest {
@Test
fun sinceKotlin() {
- verifyModel("testdata/typealias/sinceKotlin.kt") { model ->
+ checkSourceExistsAndVerifyModel("testdata/typealias/sinceKotlin.kt") { model ->
with(model.members.single().members.single()) {
assertEquals(listOf("Kotlin 1.1"), platforms)
}
diff --git a/core/testdata/format/JavaSupertype.html b/core/testdata/format/JavaSupertype.html
index 3e5e273a..27b8e5d0 100644
--- a/core/testdata/format/JavaSupertype.html
+++ b/core/testdata/format/JavaSupertype.html
@@ -4,16 +4,16 @@
<title>JavaSupertype.Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-java-supertype/index">JavaSupertype</a>&nbsp;/&nbsp;<a href="test/-java-supertype/-bar/index">Bar</a><br/>
+<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">JavaSupertype</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
-<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="test/-java-supertype/-foo/index"><span class="identifier">Foo</span></a></code>
+<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-java-supertype/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
@@ -25,10 +25,10 @@
<tbody>
<tr>
<td>
-<p><a href="test/-java-supertype/-bar/return-foo">returnFoo</a></p>
+<p><a href="return-foo.html">returnFoo</a></p>
</td>
<td>
-<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span>&nbsp;<a href="test/-java-supertype/-foo/index"><span class="identifier">Foo</span></a><span class="symbol">)</span><span class="symbol">: </span><a href="test/-java-supertype/-foo/index"><span class="identifier">Foo</span></a></code></td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">Foo</span></a></code></td>
</tr>
</tbody>
</table>
diff --git a/core/testdata/format/accessor.md b/core/testdata/format/accessor.md
index dcdef0f8..190e8538 100644
--- a/core/testdata/format/accessor.md
+++ b/core/testdata/format/accessor.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index) / [x](test/-c/x)
+[test](../index.md) / [C](index.md) / [x](./x.md)
# x
diff --git a/core/testdata/format/annotatedTypeParameter.md b/core/testdata/format/annotatedTypeParameter.md
index aa8b8592..aa622eac 100644
--- a/core/testdata/format/annotatedTypeParameter.md
+++ b/core/testdata/format/annotatedTypeParameter.md
@@ -1,5 +1,5 @@
-[test](test/index) / [containsAll](test/contains-all)
+[test](index.md) / [containsAll](./contains-all.md)
# containsAll
-`fun <E> containsAll(elements: Collection<@UnsafeVariance `[`E`](test/contains-all#E)`>): @UnsafeVariance `[`E`](test/contains-all#E) \ No newline at end of file
+`fun <E> containsAll(elements: Collection<@UnsafeVariance `[`E`](contains-all.md#E)`>): @UnsafeVariance `[`E`](contains-all.md#E) \ No newline at end of file
diff --git a/core/testdata/format/annotationClass.md b/core/testdata/format/annotationClass.md
index 2f4da736..55fda40c 100644
--- a/core/testdata/format/annotationClass.md
+++ b/core/testdata/format/annotationClass.md
@@ -1,4 +1,4 @@
-[test](test/index) / [fancy](test/fancy/index)
+[test](../index.md) / [fancy](./index.md)
# fancy
@@ -6,5 +6,5 @@
### Constructors
-| [&lt;init&gt;](test/fancy/-init-) | `fancy()` |
+| [&lt;init&gt;](-init-.md) | `fancy()` |
diff --git a/core/testdata/format/annotationClass.package.md b/core/testdata/format/annotationClass.package.md
index f52b28b6..c8aff7a3 100644
--- a/core/testdata/format/annotationClass.package.md
+++ b/core/testdata/format/annotationClass.package.md
@@ -1,8 +1,8 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
### Annotations
-| [fancy](test/fancy/index) | `annotation class fancy` |
+| [fancy](fancy/index.md) | `annotation class fancy` |
diff --git a/core/testdata/format/annotationParams.md b/core/testdata/format/annotationParams.md
index 132078ce..cfa3b822 100644
--- a/core/testdata/format/annotationParams.md
+++ b/core/testdata/format/annotationParams.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/annotations.md b/core/testdata/format/annotations.md
index cc27d5af..2e1604d0 100644
--- a/core/testdata/format/annotations.md
+++ b/core/testdata/format/annotations.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -6,13 +6,13 @@
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
### Properties
-| [x](test/-foo/x) | `val x: Int` |
+| [x](x.md) | `val x: Int` |
### Functions
-| [bar](test/-foo/bar) | `fun bar(notInlined: () -> Unit): Unit` |
+| [bar](bar.md) | `fun bar(notInlined: () -> Unit): Unit` |
diff --git a/core/testdata/format/arrayAverage.md b/core/testdata/format/arrayAverage.md
index 7cc31c47..2c6927d6 100644
--- a/core/testdata/format/arrayAverage.md
+++ b/core/testdata/format/arrayAverage.md
@@ -1,4 +1,4 @@
-[test](test/index) / [XArray](test/-x-array/index)
+[test](../index.md) / [XArray](./index.md)
# XArray
@@ -6,9 +6,9 @@
### Constructors
-| [&lt;init&gt;](test/-x-array/-init-) | `XArray()` |
+| [&lt;init&gt;](-init-.md) | `XArray()` |
### Extension Functions
-| [average](test/average) | `fun `[`XArray`](test/-x-array/index)`<out Byte>.average(): Double`<br>`fun `[`XArray`](test/-x-array/index)`<out Double>.average(): Double`<br>`fun `[`XArray`](test/-x-array/index)`<out Float>.average(): Double`<br>`fun `[`XArray`](test/-x-array/index)`<out Int>.average(): Double`<br>`fun `[`XArray`](test/-x-array/index)`<out Long>.average(): Double`<br>`fun `[`XArray`](test/-x-array/index)`<out Short>.average(): Double` |
+| [average](../average.md) | `fun `[`XArray`](./index.md)`<out Byte>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Double>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Float>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Int>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Long>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Short>.average(): Double` |
diff --git a/core/testdata/format/backtickInCodeBlock.md b/core/testdata/format/backtickInCodeBlock.md
index fc244630..830539ac 100644
--- a/core/testdata/format/backtickInCodeBlock.md
+++ b/core/testdata/format/backtickInCodeBlock.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html
index 168dd0dd..f0351d72 100644
--- a/core/testdata/format/blankLineInsideCodeBlock.html
+++ b/core/testdata/format/blankLineInsideCodeBlock.html
@@ -4,7 +4,7 @@
<title>u - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/u">u</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./u.html">u</a><br/>
<br/>
<h1>u</h1>
<a name="$u()"></a>
diff --git a/core/testdata/format/blankLineInsideCodeBlock.md b/core/testdata/format/blankLineInsideCodeBlock.md
index 66f4d65f..956f8954 100644
--- a/core/testdata/format/blankLineInsideCodeBlock.md
+++ b/core/testdata/format/blankLineInsideCodeBlock.md
@@ -1,4 +1,4 @@
-[test](test/index) / [u](test/u)
+[test](index.md) / [u](./u.md)
# u
diff --git a/core/testdata/format/bracket.html b/core/testdata/format/bracket.html
index a76ff885..01aaaf04 100644
--- a/core/testdata/format/bracket.html
+++ b/core/testdata/format/bracket.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/brokenLink.html b/core/testdata/format/brokenLink.html
index d1b8ccb0..c598a73e 100644
--- a/core/testdata/format/brokenLink.html
+++ b/core/testdata/format/brokenLink.html
@@ -4,7 +4,7 @@
<title>f - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f()"></a>
diff --git a/core/testdata/format/classWithCompanionObject.html b/core/testdata/format/classWithCompanionObject.html
index 2954d5a7..88feea5e 100644
--- a/core/testdata/format/classWithCompanionObject.html
+++ b/core/testdata/format/classWithCompanionObject.html
@@ -4,7 +4,7 @@
<title>Klass - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-klass/index">Klass</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Klass</a><br/>
<br/>
<h1>Klass</h1>
<code><span class="keyword">class </span><span class="identifier">Klass</span></code>
@@ -13,7 +13,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-klass/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Klass</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
@@ -25,7 +25,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-klass/x">x</a></p>
+<p><a href="x.html">x</a></p>
</td>
<td>
<code><span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></code></td>
@@ -37,7 +37,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-klass/foo">foo</a></p>
+<p><a href="foo.html">foo</a></p>
</td>
<td>
<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></td>
diff --git a/core/testdata/format/classWithCompanionObject.md b/core/testdata/format/classWithCompanionObject.md
index 45684c0e..40f605be 100644
--- a/core/testdata/format/classWithCompanionObject.md
+++ b/core/testdata/format/classWithCompanionObject.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Klass](test/-klass/index)
+[test](../index.md) / [Klass](./index.md)
# Klass
@@ -6,13 +6,13 @@
### Constructors
-| [&lt;init&gt;](test/-klass/-init-) | `Klass()` |
+| [&lt;init&gt;](-init-.md) | `Klass()` |
### Companion Object Properties
-| [x](test/-klass/x) | `val x: Int` |
+| [x](x.md) | `val x: Int` |
### Companion Object Functions
-| [foo](test/-klass/foo) | `fun foo(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html
index 48c2ffd2..1e07ff09 100644
--- a/core/testdata/format/codeBlock.html
+++ b/core/testdata/format/codeBlock.html
@@ -1,9 +1,11 @@
+<!-- File: test/-throws/index.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
+<title>Throws - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-throws/index">Throws</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Throws</a><br/>
<br/>
<h1>Throws</h1>
<code><span class="keyword">class </span><span class="identifier">Throws</span></code>
@@ -11,12 +13,50 @@
<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/>
+</code></pre>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Throws</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-it-does-some-obfuscated-thing/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>ItDoesSomeObfuscatedThing - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">ItDoesSomeObfuscatedThing</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Check output of</p>
+</td>
+</tr>
+</tbody>
+</table>
</BODY>
</HTML>
diff --git a/core/testdata/format/codeBlock.md b/core/testdata/format/codeBlock.md
index d64fb9fd..d183a8ba 100644
--- a/core/testdata/format/codeBlock.md
+++ b/core/testdata/format/codeBlock.md
@@ -1,4 +1,5 @@
-[test](test/index) / [Throws](test/-throws/index)
+<!-- File: test/-throws/index.md -->
+[test](../index.md) / [Throws](./index.md)
# Throws
@@ -13,7 +14,12 @@ Throws(IOException::class)
fun readFile(name: String): String {...}
```
-[test](test/index) / [ItDoesSomeObfuscatedThing](test/-it-does-some-obfuscated-thing/index)
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Throws()`<br>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. |
+
+<!-- File: test/-it-does-some-obfuscated-thing/index.md -->
+[test](../index.md) / [ItDoesSomeObfuscatedThing](./index.md)
# ItDoesSomeObfuscatedThing
@@ -25,3 +31,7 @@ Check output of
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
```
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `ItDoesSomeObfuscatedThing()`<br>Check output of |
+
diff --git a/core/testdata/format/codeBlockNoHtmlEscape.md b/core/testdata/format/codeBlockNoHtmlEscape.md
index 2622ef38..548dac4f 100644
--- a/core/testdata/format/codeBlockNoHtmlEscape.md
+++ b/core/testdata/format/codeBlockNoHtmlEscape.md
@@ -1,4 +1,4 @@
-[test](test/index) / [hackTheArithmetic](test/hack-the-arithmetic)
+[test](index.md) / [hackTheArithmetic](./hack-the-arithmetic.md)
# hackTheArithmetic
diff --git a/core/testdata/format/codeSpan.html b/core/testdata/format/codeSpan.html
index 33cbdba3..bfe93f36 100644
--- a/core/testdata/format/codeSpan.html
+++ b/core/testdata/format/codeSpan.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/companionImplements.md b/core/testdata/format/companionImplements.md
index 8a93ed6b..aac7b3e6 100644
--- a/core/testdata/format/companionImplements.md
+++ b/core/testdata/format/companionImplements.md
@@ -1,16 +1,16 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
`class Foo`
-Correct ref [Foo.Companion](test/-foo/-companion)
+Correct ref [Foo.Companion](-companion.md)
### Types
-| [Companion](test/-foo/-companion) | `companion object Companion : `[`Bar`](test/-bar) |
+| [Companion](-companion.md) | `companion object Companion : `[`Bar`](../-bar.md) |
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()`<br>Correct ref [Foo.Companion](test/-foo/-companion) |
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>Correct ref [Foo.Companion](-companion.md) |
diff --git a/core/testdata/format/companionObjectExtension.md b/core/testdata/format/companionObjectExtension.md
index e019c184..c0e268e6 100644
--- a/core/testdata/format/companionObjectExtension.md
+++ b/core/testdata/format/companionObjectExtension.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -6,9 +6,9 @@
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
### Companion Object Extension Properties
-| [x](test/x) | `val Foo.Default.x: Int`<br>The default object property. |
+| [x](../x.md) | `val Foo.Default.x: Int`<br>The default object property. |
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
index 42f21d3c..8842969e 100644
--- a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
@@ -4,21 +4,21 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/test/index">test</a>&nbsp;/&nbsp;<a href="test/test/-bar/index">Bar</a><br/>
+<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
-<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="test/test/-foo/index"><span class="identifier">Foo</span></a></code>
-<p>See <a href="test/test/-foo/xyzzy">xyzzy</a></p>
+<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/test/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
-<p>See <a href="test/test/-foo/xyzzy">xyzzy</a></p>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
</td>
</tr>
</tbody>
@@ -28,7 +28,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/test/-foo/xyzzy">xyzzy</a></p>
+<p><a href="../-foo/xyzzy.html">xyzzy</a></p>
</td>
<td>
<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">xyzzy</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/deprecated.class.html b/core/testdata/format/deprecated.class.html
index 66eb9ff1..540060d1 100644
--- a/core/testdata/format/deprecated.class.html
+++ b/core/testdata/format/deprecated.class.html
@@ -1,22 +1,54 @@
+<!-- File: test/-c/index.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
+<title>C - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-c/index">C</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
<br/>
<h1>C</h1>
<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code><br/>
<strong>Deprecated:</strong> This class sucks<br/>
<br/>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f()"></a>
<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
<strong>Deprecated:</strong> This function sucks<br/>
<br/>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/p">p</a><br/>
+</BODY>
+</HTML>
+<!-- File: test/p.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>p - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./p.html">p</a><br/>
<br/>
<h1>p</h1>
<a name="$p"></a>
diff --git a/core/testdata/format/deprecated.package.html b/core/testdata/format/deprecated.package.html
index b85e53bd..3506de61 100644
--- a/core/testdata/format/deprecated.package.html
+++ b/core/testdata/format/deprecated.package.html
@@ -4,7 +4,7 @@
<title>root package - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a><br/>
+<a href="./index.html">test</a><br/>
<br/>
<h2>Package &lt;root&gt;</h2>
<h3>Types</h3>
@@ -12,7 +12,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-c/index">C</a></p>
+<p><a href="-c/index.html">C</a></p>
</td>
<td>
<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td>
@@ -24,7 +24,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/p">p</a></p>
+<p><a href="p.html">p</a></p>
</td>
<td>
<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></td>
@@ -36,7 +36,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/f">f</a></p>
+<p><a href="f.html">f</a></p>
</td>
<td>
<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md
index 2fd928f6..382cf973 100644
--- a/core/testdata/format/dynamicExtension.md
+++ b/core/testdata/format/dynamicExtension.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -6,5 +6,5 @@
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md
index a3d6696b..07a1d103 100644
--- a/core/testdata/format/dynamicType.md
+++ b/core/testdata/format/dynamicType.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/emptyDescription.md b/core/testdata/format/emptyDescription.md
index 8c0fe109..5d56d968 100644
--- a/core/testdata/format/emptyDescription.md
+++ b/core/testdata/format/emptyDescription.md
@@ -1,4 +1,4 @@
-[test](test/index) / [fn](test/fn)
+[test](index.md) / [fn](./fn.md)
# fn
diff --git a/core/testdata/format/entity.html b/core/testdata/format/entity.html
index abe89502..639f2903 100644
--- a/core/testdata/format/entity.html
+++ b/core/testdata/format/entity.html
@@ -4,7 +4,7 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-bar/index">Bar</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
@@ -14,7 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
diff --git a/core/testdata/format/enumClass.md b/core/testdata/format/enumClass.md
index d3a6f186..50cccfbb 100644
--- a/core/testdata/format/enumClass.md
+++ b/core/testdata/format/enumClass.md
@@ -1,4 +1,4 @@
-[test](test/index) / [InlineOption](test/-inline-option/index)
+[test](../index.md) / [InlineOption](./index.md)
# InlineOption
@@ -6,6 +6,6 @@
### Enum Values
-| [ONLY_LOCAL_RETURN](test/-inline-option/-o-n-l-y_-l-o-c-a-l_-r-e-t-u-r-n) | |
-| [LOCAL_CONTINUE_AND_BREAK](test/-inline-option/-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k) | |
+| [ONLY_LOCAL_RETURN](-o-n-l-y_-l-o-c-a-l_-r-e-t-u-r-n.md) | |
+| [LOCAL_CONTINUE_AND_BREAK](-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k.md) | |
diff --git a/core/testdata/format/enumClass.value.md b/core/testdata/format/enumClass.value.md
index feed2fe5..150016cc 100644
--- a/core/testdata/format/enumClass.value.md
+++ b/core/testdata/format/enumClass.value.md
@@ -1,4 +1,4 @@
-[test](test/index) / [InlineOption](test/-inline-option/index) / [LOCAL_CONTINUE_AND_BREAK](test/-inline-option/-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k)
+[test](../index.md) / [InlineOption](index.md) / [LOCAL_CONTINUE_AND_BREAK](./-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k.md)
# LOCAL_CONTINUE_AND_BREAK
diff --git a/core/testdata/format/enumRef.kt b/core/testdata/format/enumRef.kt
new file mode 100644
index 00000000..5c0b2de9
--- /dev/null
+++ b/core/testdata/format/enumRef.kt
@@ -0,0 +1,4 @@
+/**
+ * [java.math.RoundingMode.UP]
+ */
+fun f() {} \ No newline at end of file
diff --git a/core/testdata/format/enumRef.md b/core/testdata/format/enumRef.md
new file mode 100644
index 00000000..8b2a6650
--- /dev/null
+++ b/core/testdata/format/enumRef.md
@@ -0,0 +1,8 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f(): Unit`
+
+[java.math.RoundingMode.UP](http://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html#UP)
+
diff --git a/core/testdata/format/exceptionClass.md b/core/testdata/format/exceptionClass.md
index e3714ecc..1e928bb6 100644
--- a/core/testdata/format/exceptionClass.md
+++ b/core/testdata/format/exceptionClass.md
@@ -1,4 +1,4 @@
-[test](test/index) / [MyException](test/-my-exception/index)
+[test](../index.md) / [MyException](./index.md)
# MyException
@@ -6,5 +6,5 @@
### Constructors
-| [&lt;init&gt;](test/-my-exception/-init-) | `MyException()` |
+| [&lt;init&gt;](-init-.md) | `MyException()` |
diff --git a/core/testdata/format/exceptionClass.package.md b/core/testdata/format/exceptionClass.package.md
index e10478e4..8716df0a 100644
--- a/core/testdata/format/exceptionClass.package.md
+++ b/core/testdata/format/exceptionClass.package.md
@@ -1,8 +1,8 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
### Exceptions
-| [MyException](test/-my-exception/index) | `class MyException : Exception` |
+| [MyException](-my-exception/index.md) | `class MyException : Exception` |
diff --git a/core/testdata/format/exclInCodeBlock.md b/core/testdata/format/exclInCodeBlock.md
index 9c9e424a..d665c415 100644
--- a/core/testdata/format/exclInCodeBlock.md
+++ b/core/testdata/format/exclInCodeBlock.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/extensionFunctionParameter.md b/core/testdata/format/extensionFunctionParameter.md
index 501d731d..e1e85824 100644
--- a/core/testdata/format/extensionFunctionParameter.md
+++ b/core/testdata/format/extensionFunctionParameter.md
@@ -1,5 +1,5 @@
-[test](test/index) / [apply](test/apply)
+[test](index.md) / [apply](./apply.md)
# apply
-`inline fun <T> `[`T`](test/apply#T)`.apply(f: `[`T`](test/apply#T)`.() -> Unit): `[`T`](test/apply#T) \ No newline at end of file
+`inline fun <T> `[`T`](apply.md#T)`.apply(f: `[`T`](apply.md#T)`.() -> Unit): `[`T`](apply.md#T) \ No newline at end of file
diff --git a/core/testdata/format/extensionScope.md b/core/testdata/format/extensionScope.md
index 3515ed84..ea765bd5 100644
--- a/core/testdata/format/extensionScope.md
+++ b/core/testdata/format/extensionScope.md
@@ -1,8 +1,8 @@
-[test](test/index) / [test](test/test)
+[test](index.md) / [test](./test.md)
# test
`fun test(): Unit`
-Correct link: [Foo.ext](test/ext)
+Correct link: [Foo.ext](ext.md)
diff --git a/core/testdata/format/extensionWithDocumentedReceiver.md b/core/testdata/format/extensionWithDocumentedReceiver.md
index 125da37e..0679ac8f 100644
--- a/core/testdata/format/extensionWithDocumentedReceiver.md
+++ b/core/testdata/format/extensionWithDocumentedReceiver.md
@@ -1,4 +1,4 @@
-[test](test/index) / [kotlin.String](test/kotlin.-string/index) / [fn](test/kotlin.-string/fn)
+[test](../index.md) / [kotlin.String](index.md) / [fn](./fn.md)
# fn
diff --git a/core/testdata/format/extensions.class.md b/core/testdata/format/extensions.class.md
index f75b4dc8..b8fa200a 100644
--- a/core/testdata/format/extensions.class.md
+++ b/core/testdata/format/extensions.class.md
@@ -1,7 +1,7 @@
-[test](test/index) / [foo](test/foo/index) / [kotlin.String](test/foo/kotlin.-string/index)
+[test](../../index.md) / [foo](../index.md) / [kotlin.String](./index.md)
### Extensions for kotlin.String
-| [fn](test/foo/kotlin.-string/fn) | `fun String.fn(): Unit`<br>`fun String.fn(x: Int): Unit`<br>Function with receiver |
-| [foobar](test/foo/kotlin.-string/foobar) | `val String.foobar: Int`<br>Property with receiver. |
+| [fn](fn.md) | `fun String.fn(): Unit`<br>`fun String.fn(x: Int): Unit`<br>Function with receiver |
+| [foobar](foobar.md) | `val String.foobar: Int`<br>Property with receiver. |
diff --git a/core/testdata/format/extensions.package.md b/core/testdata/format/extensions.package.md
index 4dc0cfab..ad895116 100644
--- a/core/testdata/format/extensions.package.md
+++ b/core/testdata/format/extensions.package.md
@@ -1,8 +1,8 @@
-[test](test/index) / [foo](test/foo/index)
+[test](../index.md) / [foo](./index.md)
## Package foo
### Extensions for External Classes
-| [kotlin.String](test/foo/kotlin.-string/index) | |
+| [kotlin.String](kotlin.-string/index.md) | |
diff --git a/core/testdata/format/externalReferenceLink.md b/core/testdata/format/externalReferenceLink.md
index 29d64afe..38ffde78 100644
--- a/core/testdata/format/externalReferenceLink.md
+++ b/core/testdata/format/externalReferenceLink.md
@@ -1,4 +1,4 @@
-[test](test/index) / [a](test/a)
+[test](index.md) / [a](./a.md)
# a
diff --git a/core/testdata/format/functionWithDefaultParameter.md b/core/testdata/format/functionWithDefaultParameter.md
index 568c106c..05f7fbe6 100644
--- a/core/testdata/format/functionWithDefaultParameter.md
+++ b/core/testdata/format/functionWithDefaultParameter.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.html b/core/testdata/format/functionalTypeWithNamedParameters.html
index 84a489ff..83d03d8f 100644
--- a/core/testdata/format/functionalTypeWithNamedParameters.html
+++ b/core/testdata/format/functionalTypeWithNamedParameters.html
@@ -1,25 +1,103 @@
+<!-- File: test/-a/index.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
+<title>A - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-a/index">A</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">A</a><br/>
<br/>
<h1>A</h1>
-<code><span class="keyword">class </span><span class="identifier">A</span></code><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-b/index">B</a><br/>
+<code><span class="keyword">class </span><span class="identifier">A</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">A</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-b/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>B - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">B</a><br/>
<br/>
<h1>B</h1>
-<code><span class="keyword">class </span><span class="identifier">B</span></code><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-c/index">C</a><br/>
+<code><span class="keyword">class </span><span class="identifier">B</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">B</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-c/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>C - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
<br/>
<h1>C</h1>
-<code><span class="keyword">class </span><span class="identifier">C</span></code><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<code><span class="keyword">class </span><span class="identifier">C</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f"></a>
-<code><span class="keyword">val </span><span class="identifier">f</span><span class="symbol">: </span><span class="symbol">(</span><span class="identifier">a</span><span class="symbol">:</span>&nbsp;<a href="test/-a/index"><span class="identifier">A</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">b</span><span class="symbol">:</span>&nbsp;<a href="test/-b/index"><span class="identifier">B</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="test/-c/index"><span class="identifier">C</span></a></code><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/accept-function-type-with-named-arguments">acceptFunctionTypeWithNamedArguments</a><br/>
+<code><span class="keyword">val </span><span class="identifier">f</span><span class="symbol">: </span><span class="symbol">(</span><span class="identifier">a</span><span class="symbol">:</span>&nbsp;<a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">b</span><span class="symbol">:</span>&nbsp;<a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="-c/index.html"><span class="identifier">C</span></a></code>
+</BODY>
+</HTML>
+<!-- File: test/accept-function-type-with-named-arguments.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>acceptFunctionTypeWithNamedArguments - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./accept-function-type-with-named-arguments.html">acceptFunctionTypeWithNamedArguments</a><br/>
<br/>
<h1>acceptFunctionTypeWithNamedArguments</h1>
<a name="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))"></a>
-<code><span class="keyword">fun </span><span class="identifier">acceptFunctionTypeWithNamedArguments</span><span class="symbol">(</span><span class="identifier" id="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))/f">f</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">bb</span><span class="symbol">:</span>&nbsp;<a href="test/-b/index"><span class="identifier">B</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">aa</span><span class="symbol">:</span>&nbsp;<a href="test/-a/index"><span class="identifier">A</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="test/-c/index"><span class="identifier">C</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<code><span class="keyword">fun </span><span class="identifier">acceptFunctionTypeWithNamedArguments</span><span class="symbol">(</span><span class="identifier" id="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))/f">f</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">bb</span><span class="symbol">:</span>&nbsp;<a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">aa</span><span class="symbol">:</span>&nbsp;<a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="-c/index.html"><span class="identifier">C</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
</BODY>
</HTML>
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.md b/core/testdata/format/functionalTypeWithNamedParameters.md
index 317fa6af..4e78694c 100644
--- a/core/testdata/format/functionalTypeWithNamedParameters.md
+++ b/core/testdata/format/functionalTypeWithNamedParameters.md
@@ -1,21 +1,45 @@
-[test](test/index) / [A](test/-a/index)
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
# A
-`class A`[test](test/index) / [B](test/-b/index)
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
# B
-`class B`[test](test/index) / [C](test/-c/index)
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
# C
-`class C`[test](test/index) / [f](test/f)
+`class C`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/f.md -->
+[test](index.md) / [f](./f.md)
# f
-`val f: (a: `[`A`](test/-a/index)`, b: `[`B`](test/-b/index)`) -> `[`C`](test/-c/index)[test](test/index) / [acceptFunctionTypeWithNamedArguments](test/accept-function-type-with-named-arguments)
+`val f: (a: `[`A`](-a/index.md)`, b: `[`B`](-b/index.md)`) -> `[`C`](-c/index.md)
+<!-- File: test/accept-function-type-with-named-arguments.md -->
+[test](index.md) / [acceptFunctionTypeWithNamedArguments](./accept-function-type-with-named-arguments.md)
# acceptFunctionTypeWithNamedArguments
-`fun acceptFunctionTypeWithNamedArguments(f: (bb: `[`B`](test/-b/index)`, aa: `[`A`](test/-a/index)`) -> `[`C`](test/-c/index)`): Unit` \ No newline at end of file
+`fun acceptFunctionTypeWithNamedArguments(f: (bb: `[`B`](-b/index.md)`, aa: `[`A`](-a/index.md)`) -> `[`C`](-c/index.md)`): Unit` \ No newline at end of file
diff --git a/core/testdata/format/genericInheritedExtensions.md b/core/testdata/format/genericInheritedExtensions.md
index 163ff0c9..8d0e316f 100644
--- a/core/testdata/format/genericInheritedExtensions.md
+++ b/core/testdata/format/genericInheritedExtensions.md
@@ -1,15 +1,15 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
-`class Bar<T> : `[`Foo`](test/-foo/index)`<`[`T`](test/-bar/index#T)`>`
+`class Bar<T> : `[`Foo`](../-foo/index.md)`<`[`T`](index.md#T)`>`
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()` |
+| [&lt;init&gt;](-init-.md) | `Bar()` |
### Extension Functions
-| [first](test/first) | `fun <T> `[`Foo`](test/-foo/index)`<`[`T`](test/first#T)`>.first(): Unit` |
-| [second](test/second) | `fun <T> `[`Bar`](test/-bar/index)`<`[`T`](test/second#T)`>.second(): Unit` |
+| [first](../first.md) | `fun <T> `[`Foo`](../-foo/index.md)`<`[`T`](../first.md#T)`>.first(): Unit` |
+| [second](../second.md) | `fun <T> `[`Bar`](./index.md)`<`[`T`](../second.md#T)`>.second(): Unit` |
diff --git a/core/testdata/format/gfm/listInTableCell.md b/core/testdata/format/gfm/listInTableCell.md
index 73152ac9..359ad916 100644
--- a/core/testdata/format/gfm/listInTableCell.md
+++ b/core/testdata/format/gfm/listInTableCell.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -8,10 +8,10 @@
| Name | Summary |
|---|---|
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
### Functions
| Name | Summary |
|---|---|
-| [foo](test/-foo/foo) | `fun foo(): Unit`<ol><li>Foo</li><li>Bar</li></ol> |
+| [foo](foo.md) | `fun foo(): Unit`<ol><li>Foo</li><li>Bar</li></ol> |
diff --git a/core/testdata/format/gfm/sample.md b/core/testdata/format/gfm/sample.md
index c2af1c1e..2b082296 100644
--- a/core/testdata/format/gfm/sample.md
+++ b/core/testdata/format/gfm/sample.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -10,11 +10,11 @@ The class Foo.
| Name | Summary |
|---|---|
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()`<br>The class Foo. |
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>The class Foo. |
### Functions
| Name | Summary |
|---|---|
-| [bar](test/-foo/bar) | `fun bar(): Unit`<br>The method bar. |
-| [baz](test/-foo/baz) | `fun baz(): Unit`<br>The method baz. |
+| [bar](bar.md) | `fun bar(): Unit`<br>The method bar. |
+| [baz](baz.md) | `fun baz(): Unit`<br>The method baz. |
diff --git a/core/testdata/format/htmlEscaping.html b/core/testdata/format/htmlEscaping.html
index 17d48161..bd64454d 100644
--- a/core/testdata/format/htmlEscaping.html
+++ b/core/testdata/format/htmlEscaping.html
@@ -4,11 +4,11 @@
<title>x - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/x">x</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./x.html">x</a><br/>
<br/>
<h1>x</h1>
<a name="$x()"></a>
-<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">x</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="test/x#T"><span class="identifier">T</span></a><span class="symbol">?</span></code>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">x</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="x.html#T"><span class="identifier">T</span></a><span class="symbol">?</span></code>
<p>Special characters: &lt; is "less than", &gt; is "greater than", &amp; is "ampersand"</p>
</BODY>
</HTML>
diff --git a/core/testdata/format/inapplicableExtensionFunctions.md b/core/testdata/format/inapplicableExtensionFunctions.md
index 30246faa..08fc2739 100644
--- a/core/testdata/format/inapplicableExtensionFunctions.md
+++ b/core/testdata/format/inapplicableExtensionFunctions.md
@@ -1,14 +1,14 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
-`class Bar : `[`Foo`](test/-foo/index)`<Char>`
+`class Bar : `[`Foo`](../-foo/index.md)`<Char>`
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()` |
+| [&lt;init&gt;](-init-.md) | `Bar()` |
### Extension Functions
-| [xyzzy](test/xyzzy) | `fun `[`Bar`](test/-bar/index)`.xyzzy(): Unit` |
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
diff --git a/core/testdata/format/indentedCodeBlock.html b/core/testdata/format/indentedCodeBlock.html
index 1ccf800a..86c129fb 100644
--- a/core/testdata/format/indentedCodeBlock.html
+++ b/core/testdata/format/indentedCodeBlock.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/indentedCodeBlock.md b/core/testdata/format/indentedCodeBlock.md
index 515bfee3..77b0630a 100644
--- a/core/testdata/format/indentedCodeBlock.md
+++ b/core/testdata/format/indentedCodeBlock.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/inheritedCompanionObjectProperties.md b/core/testdata/format/inheritedCompanionObjectProperties.md
index db764ff0..ab8f0aa5 100644
--- a/core/testdata/format/inheritedCompanionObjectProperties.md
+++ b/core/testdata/format/inheritedCompanionObjectProperties.md
@@ -1,30 +1,30 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
-`class C : `[`A`](test/-a/index)
+`class C : `[`A`](../-a/index.md)
### Types
-| [Companion](test/-c/-companion/index) | `companion object Companion : `[`B`](test/-b/index) |
+| [Companion](-companion/index.md) | `companion object Companion : `[`B`](../-b/index.md) |
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()` |
+| [&lt;init&gt;](-init-.md) | `C()` |
### Functions
-| [xyzzy](test/-c/xyzzy) | `fun xyzzy(): Unit` |
+| [xyzzy](xyzzy.md) | `fun xyzzy(): Unit` |
### Inherited Functions
-| [foo](test/-a/foo) | `fun foo(): Unit` |
+| [foo](../-a/foo.md) | `fun foo(): Unit` |
### Companion Object Functions
-| [shazam](test/-c/shazam) | `fun shazam(): Unit` |
+| [shazam](shazam.md) | `fun shazam(): Unit` |
### Inherited Companion Object Functions
-| [bar](test/-b/bar) | `fun bar(): Unit` |
+| [bar](../-b/bar.md) | `fun bar(): Unit` |
diff --git a/core/testdata/format/inheritedExtensions.md b/core/testdata/format/inheritedExtensions.md
index 611b4f6a..97a73666 100644
--- a/core/testdata/format/inheritedExtensions.md
+++ b/core/testdata/format/inheritedExtensions.md
@@ -1,15 +1,15 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
-`class Bar : `[`Foo`](test/-foo/index)
+`class Bar : `[`Foo`](../-foo/index.md)
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()` |
+| [&lt;init&gt;](-init-.md) | `Bar()` |
### Extension Functions
-| [first](test/first) | `fun `[`Foo`](test/-foo/index)`.first(): Unit` |
-| [second](test/second) | `fun `[`Bar`](test/-bar/index)`.second(): Unit` |
+| [first](../first.md) | `fun `[`Foo`](../-foo/index.md)`.first(): Unit` |
+| [second](../second.md) | `fun `[`Bar`](./index.md)`.second(): Unit` |
diff --git a/core/testdata/format/inheritedLink.1.kt b/core/testdata/format/inheritedLink.1.kt
new file mode 100644
index 00000000..29cc12b4
--- /dev/null
+++ b/core/testdata/format/inheritedLink.1.kt
@@ -0,0 +1,10 @@
+package p1
+
+import java.util.LinkedList
+
+interface Foo {
+
+ /** Says hello - [LinkedList]. */
+ fun sayHello() : String
+
+} \ No newline at end of file
diff --git a/core/testdata/format/inheritedLink.kt b/core/testdata/format/inheritedLink.kt
new file mode 100644
index 00000000..86b8f4e2
--- /dev/null
+++ b/core/testdata/format/inheritedLink.kt
@@ -0,0 +1,11 @@
+package p2
+
+import p1.Foo
+
+class FooBar : Foo {
+
+ override fun sayHello(): String {
+ return "Hello!"
+ }
+
+} \ No newline at end of file
diff --git a/core/testdata/format/inheritedLink.md b/core/testdata/format/inheritedLink.md
new file mode 100644
index 00000000..e5af326c
--- /dev/null
+++ b/core/testdata/format/inheritedLink.md
@@ -0,0 +1,17 @@
+<!-- File: test/p2/-foo-bar/-init-.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [&lt;init&gt;](./-init-.md)
+
+# &lt;init&gt;
+
+`FooBar()`
+<!-- File: test/p2/-foo-bar/say-hello.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [sayHello](./say-hello.md)
+
+# sayHello
+
+`fun sayHello(): String`
+
+Overrides [Foo.sayHello](../../p1/-foo/say-hello.md)
+
+Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
+
diff --git a/core/testdata/format/inheritedMembers.md b/core/testdata/format/inheritedMembers.md
index bed5bd44..334df360 100644
--- a/core/testdata/format/inheritedMembers.md
+++ b/core/testdata/format/inheritedMembers.md
@@ -1,26 +1,26 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
-`class Bar : `[`Foo`](test/-foo/index)
+`class Bar : `[`Foo`](../-foo/index.md)
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()` |
+| [&lt;init&gt;](-init-.md) | `Bar()` |
### Properties
-| [secondProp](test/-bar/second-prop) | `val secondProp: Int` |
+| [secondProp](second-prop.md) | `val secondProp: Int` |
### Inherited Properties
-| [firstProp](test/-foo/first-prop) | `val firstProp: Int` |
+| [firstProp](../-foo/first-prop.md) | `val firstProp: Int` |
### Functions
-| [second](test/-bar/second) | `fun second(): Unit` |
+| [second](second.md) | `fun second(): Unit` |
### Inherited Functions
-| [first](test/-foo/first) | `fun first(): Unit` |
+| [first](../-foo/first.md) | `fun first(): Unit` |
diff --git a/core/testdata/format/javaCodeInParam.md b/core/testdata/format/javaCodeInParam.md
index 696abdc4..319c6d87 100644
--- a/core/testdata/format/javaCodeInParam.md
+++ b/core/testdata/format/javaCodeInParam.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -10,5 +10,5 @@
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()` |
+| [&lt;init&gt;](-init-.md) | `C()` |
diff --git a/core/testdata/format/javaCodeLiteralTags.md b/core/testdata/format/javaCodeLiteralTags.md
index 99dbccfd..b36be04d 100644
--- a/core/testdata/format/javaCodeLiteralTags.md
+++ b/core/testdata/format/javaCodeLiteralTags.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -12,5 +12,5 @@ A&lt;B&gt;C
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
+| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
diff --git a/core/testdata/format/javaDeprecated.html b/core/testdata/format/javaDeprecated.html
index 67c5fd8f..d938fb9d 100644
--- a/core/testdata/format/javaDeprecated.html
+++ b/core/testdata/format/javaDeprecated.html
@@ -4,11 +4,11 @@
<title>Foo.foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-foo/index">Foo</a>&nbsp;/&nbsp;<a href="test/-foo/foo">foo</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="index.html">Foo</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="Foo$foo()"></a>
<code><span class="keyword">open</span> <span class="keyword">fun </span><s><span class="identifier">foo</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
-<strong>Deprecated:</strong> use <code><a href="test/-foo/bar">#bar</a></code> instead
+<strong>Deprecated:</strong> use <code><a href="bar.html">#bar</a></code> instead
</BODY>
</HTML>
diff --git a/core/testdata/format/javaLinkTag.html b/core/testdata/format/javaLinkTag.html
index 035bdd06..f90a58df 100644
--- a/core/testdata/format/javaLinkTag.html
+++ b/core/testdata/format/javaLinkTag.html
@@ -4,21 +4,21 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-foo/index">Foo</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
<br/>
<h1>Foo</h1>
<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
-<p>Call <code><a href="test/-foo/bar">#bar()</a></code> to do the job.</p>
+<p>Call <code><a href="bar.html">#bar()</a></code> to do the job.</p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-foo/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
-<p>Call <code><a href="test/-foo/bar">#bar()</a></code> to do the job.</p>
+<p>Call <code><a href="bar.html">#bar()</a></code> to do the job.</p>
</td>
</tr>
</tbody>
@@ -28,7 +28,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-foo/bar">bar</a></p>
+<p><a href="bar.html">bar</a></p>
</td>
<td>
<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/javaLinkTagWithLabel.html b/core/testdata/format/javaLinkTagWithLabel.html
index 69a15bc7..51917f7a 100644
--- a/core/testdata/format/javaLinkTagWithLabel.html
+++ b/core/testdata/format/javaLinkTagWithLabel.html
@@ -4,21 +4,21 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-foo/index">Foo</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
<br/>
<h1>Foo</h1>
<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
-<p>Call <code><a href="test/-foo/bar">this wonderful method</a></code> to do the job.</p>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-foo/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
-<p>Call <code><a href="test/-foo/bar">this wonderful method</a></code> to do the job.</p>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
</td>
</tr>
</tbody>
@@ -28,7 +28,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-foo/bar">bar</a></p>
+<p><a href="bar.html">bar</a></p>
</td>
<td>
<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/javaSeeTag.html b/core/testdata/format/javaSeeTag.html
index c81eec31..f8866dc2 100644
--- a/core/testdata/format/javaSeeTag.html
+++ b/core/testdata/format/javaSeeTag.html
@@ -4,18 +4,18 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-foo/index">Foo</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
<br/>
<h1>Foo</h1>
<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
<p><strong>See Also</strong><br/>
-<a href="test/-foo/bar">#bar</a></p>
+<a href="bar.html">#bar</a></p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-foo/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
@@ -27,7 +27,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-foo/bar">bar</a></p>
+<p><a href="bar.html">bar</a></p>
</td>
<td>
<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/javaSpaceInAuthor.md b/core/testdata/format/javaSpaceInAuthor.md
index 6d738087..1d2251d0 100644
--- a/core/testdata/format/javaSpaceInAuthor.md
+++ b/core/testdata/format/javaSpaceInAuthor.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -9,5 +9,5 @@ Dmitry Jemerov
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()` |
+| [&lt;init&gt;](-init-.md) | `C()` |
diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md
index 8aba2059..a3c1baff 100644
--- a/core/testdata/format/javadocHtml.md
+++ b/core/testdata/format/javadocHtml.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -18,7 +18,7 @@ Block code
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
+| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
```
Block code<br>```
diff --git a/core/testdata/format/javadocOrderedList.md b/core/testdata/format/javadocOrderedList.md
index f18ee96a..88d5f5b6 100644
--- a/core/testdata/format/javadocOrderedList.md
+++ b/core/testdata/format/javadocOrderedList.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -10,7 +10,7 @@
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>
1. Rinse
2. Repeat
<br> |
diff --git a/core/testdata/format/jdkLinks.md b/core/testdata/format/jdkLinks.md
index 607d63e5..7498171d 100644
--- a/core/testdata/format/jdkLinks.md
+++ b/core/testdata/format/jdkLinks.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -10,5 +10,5 @@ You can print something to [java.lang.System.out](http://docs.oracle.com/javase/
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()`<br>This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) |
+| [&lt;init&gt;](-init-.md) | `C()`<br>This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) |
diff --git a/core/testdata/format/linkWithLabel.html b/core/testdata/format/linkWithLabel.html
index b8fe2228..59bc6ddf 100644
--- a/core/testdata/format/linkWithLabel.html
+++ b/core/testdata/format/linkWithLabel.html
@@ -4,21 +4,21 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-bar/index">Bar</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
-<p>Use <a href="test/-bar/foo">this method</a> for best results.</p>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
-<p>Use <a href="test/-bar/foo">this method</a> for best results.</p>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
</td>
</tr>
</tbody>
@@ -28,7 +28,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-bar/foo">foo</a></p>
+<p><a href="foo.html">foo</a></p>
</td>
<td>
<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></td>
diff --git a/core/testdata/format/linkWithStarProjection.html b/core/testdata/format/linkWithStarProjection.html
index bb9ce43c..e1b6e098 100644
--- a/core/testdata/format/linkWithStarProjection.html
+++ b/core/testdata/format/linkWithStarProjection.html
@@ -4,7 +4,7 @@
<title>KClassLoader - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-k-class-loader/index">KClassLoader</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">KClassLoader</a><br/>
<br/>
<h1>KClassLoader</h1>
<code><span class="keyword">object </span><span class="identifier">KClassLoader</span></code>
@@ -13,7 +13,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-k-class-loader/foo">foo</a></p>
+<p><a href="foo.html">foo</a></p>
</td>
<td>
<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="KClassLoader$foo(kotlin.Enum(()))/c">c</span><span class="symbol">:</span>&nbsp;<span class="identifier">Enum</span><span class="symbol">&lt;</span><span class="identifier">*</span><span class="symbol">&gt;</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
diff --git a/core/testdata/format/linksInEmphasis.md b/core/testdata/format/linksInEmphasis.md
index 9441ef6a..d0ae70c8 100644
--- a/core/testdata/format/linksInEmphasis.md
+++ b/core/testdata/format/linksInEmphasis.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -6,18 +6,18 @@
An emphasised class.
-*This class [Bar](test/-bar/index) is awesome.*
+*This class [Bar](./index.md) is awesome.*
-*Even more awesomer is the function [Bar.foo](test/-bar/foo)*
+*Even more awesomer is the function [Bar.foo](foo.md)*
-*[Bar.hello](test/-bar/hello) is also OK*
+*[Bar.hello](hello.md) is also OK*
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>An emphasised class. |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>An emphasised class. |
### Functions
-| [foo](test/-bar/foo) | `fun foo(): Unit` |
-| [hello](test/-bar/hello) | `fun hello(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
diff --git a/core/testdata/format/linksInHeaders.md b/core/testdata/format/linksInHeaders.md
index 85d51356..1dc7d18b 100644
--- a/core/testdata/format/linksInHeaders.md
+++ b/core/testdata/format/linksInHeaders.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -6,29 +6,29 @@
Some class with really useless documentation.
-# Beer o'clock - time to go to the [Bar](test/-bar/index)
+# Beer o'clock - time to go to the [Bar](./index.md)
## But **is [it](isitbeeroclock.com)** really?
-### [Bar.hello](test/-bar/hello) to the [Bar.world](test/-bar/world)!
+### [Bar.hello](hello.md) to the [Bar.world](world.md)!
-#### *Kotlin is amazing, [Bar.none](test/-bar/none)*
+#### *Kotlin is amazing, [Bar.none](none.md)*
-##### We need to go [Bar.deeper](test/-bar/deeper)
+##### We need to go [Bar.deeper](deeper.md)
-###### End of the [Bar.line](test/-bar/line) - we need to go back!
+###### End of the [Bar.line](line.md) - we need to go back!
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>Some class with really useless documentation. |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Some class with really useless documentation. |
### Functions
-| [deeper](test/-bar/deeper) | `fun deeper(): Unit` |
-| [foo](test/-bar/foo) | `fun foo(): Unit` |
-| [hello](test/-bar/hello) | `fun hello(): Unit` |
-| [kotlin](test/-bar/kotlin) | `fun kotlin(): Unit` |
-| [line](test/-bar/line) | `fun line(): Unit` |
-| [none](test/-bar/none) | `fun none(): Unit` |
-| [world](test/-bar/world) | `fun world(): Unit` |
+| [deeper](deeper.md) | `fun deeper(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+| [kotlin](kotlin.md) | `fun kotlin(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
+| [none](none.md) | `fun none(): Unit` |
+| [world](world.md) | `fun world(): Unit` |
diff --git a/core/testdata/format/linksInStrong.md b/core/testdata/format/linksInStrong.md
index e98d31cc..5b44112d 100644
--- a/core/testdata/format/linksInStrong.md
+++ b/core/testdata/format/linksInStrong.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -6,18 +6,18 @@
A strong class.
-**This class [Bar](test/-bar/index) is awesome.**
+**This class [Bar](./index.md) is awesome.**
-**Even more awesomer is the function [Bar.foo](test/-bar/foo)**
+**Even more awesomer is the function [Bar.foo](foo.md)**
-**[Bar.hello](test/-bar/hello) is also OK**
+**[Bar.hello](hello.md) is also OK**
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>A strong class. |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>A strong class. |
### Functions
-| [foo](test/-bar/foo) | `fun foo(): Unit` |
-| [hello](test/-bar/hello) | `fun hello(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html
index 6d2327be..596cca73 100644
--- a/core/testdata/format/markdownInLinks.html
+++ b/core/testdata/format/markdownInLinks.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md
index b9db4e3d..0ec1fda3 100644
--- a/core/testdata/format/memberExtension.md
+++ b/core/testdata/format/memberExtension.md
@@ -1,10 +1,10 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
-`class Foo : `[`X`](test/-x/index)
+`class Foo : `[`X`](../-x/index.md)
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
index e3d4c070..37e943ad 100644
--- a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
@@ -1,4 +1,4 @@
-[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index) / [magic](test/pack/-some/-some/magic)
+[test](../../../index.md) / [pack](../../index.md) / [Some](../index.md) / [magic](./magic.md)
# magic
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.md b/core/testdata/format/multiplatform/groupNode/multiplatform.md
index c0ef14b1..74d464c9 100644
--- a/core/testdata/format/multiplatform/groupNode/multiplatform.md
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md
@@ -1,4 +1,4 @@
-[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index)
+[test](../../index.md) / [pack](../index.md) / [Some](./index.md)
# Some
@@ -12,9 +12,9 @@
### Constructors
-| [&lt;init&gt;](test/pack/-some/-some/-init-) | `Some()` |
+| [&lt;init&gt;](-some/-init-.md) | `Some()` |
### Functions
-| [magic](test/pack/-some/-some/magic) | `fun magic(): Unit` |
+| [magic](-some/magic.md) | `fun magic(): Unit` |
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
index a9e2e404..5708795e 100644
--- a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
@@ -1,13 +1,13 @@
-[test](test/index) / [pack](test/pack/index)
+[test](../index.md) / [pack](./index.md)
## Package pack
### Types
-| [Some](test/pack/-some/index)<br>(JS) | `class Some` |
-| [SomeCoolJvmClass](test/pack/-some-cool-jvm-class/index)<br>(JVM) | `class SomeCoolJvmClass` |
+| [Some](-some/index.md)<br>(JS) | `class Some` |
+| [SomeCoolJvmClass](-some-cool-jvm-class/index.md)<br>(JVM) | `class SomeCoolJvmClass` |
### Type Aliases
-| [Some](test/pack/-some/index)<br>(JVM) | `typealias Some = SomeCoolJvmClass` |
+| [Some](-some/index.md)<br>(JVM) | `typealias Some = SomeCoolJvmClass` |
diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md
index c615dd8e..fca2aff4 100644
--- a/core/testdata/format/multiplatform/implied/foo.md
+++ b/core/testdata/format/multiplatform/implied/foo.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
# Foo
@@ -8,17 +8,17 @@ This is a foo.
### Constructors
-| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+| [&lt;init&gt;](-init-.md) | `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` |
+| [propJs](prop-js.md)<br>(JS) | `val propJs: String` |
+| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](prop-jvm-and-js.md) | `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` |
+| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| [js](js.md)<br>(JS) | `fun js(): Unit` |
+| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` |
diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md
index 8e463327..ea78b5a3 100644
--- a/core/testdata/format/multiplatform/merge/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo/index)
+[test](../index.md) / [foo](./index.md)
## Package foo
@@ -6,5 +6,5 @@
### Types
-| [Foo](test/foo/-foo/index)<br>(JVM, JS) | `class Foo`<br>This is a foo. |
+| [Foo](-foo/index.md)<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
index 7490c878..7f41b7d1 100644
--- a/core/testdata/format/multiplatform/mergeMembers/foo.md
+++ b/core/testdata/format/multiplatform/mergeMembers/foo.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
# Foo
@@ -10,17 +10,17 @@ This is a foo.
### Constructors
-| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+| [&lt;init&gt;](-init-.md) | `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` |
+| [propJs](prop-js.md)<br>(JS) | `val propJs: String` |
+| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](prop-jvm-and-js.md) | `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` |
+| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| [js](js.md)<br>(JS) | `fun js(): Unit` |
+| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` |
diff --git a/core/testdata/format/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md
index 088ced2c..a20b14cf 100644
--- a/core/testdata/format/multiplatform/omitRedundant/foo.md
+++ b/core/testdata/format/multiplatform/omitRedundant/foo.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index)
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
# Foo
@@ -10,13 +10,13 @@ This is a foo.
### Constructors
-| [&lt;init&gt;](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. |
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
### Properties
-| [propJvm](test/foo/-foo/prop-jvm) | `val propJvm: String` |
+| [propJvm](prop-jvm.md) | `val propJvm: String` |
### Functions
-| [jvm](test/foo/-foo/jvm) | `fun jvm(): Unit` |
+| [jvm](jvm.md) | `fun jvm(): Unit` |
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
index 1dda25d4..6f45342b 100644
--- a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
@@ -1,10 +1,10 @@
-[test](test/index)
+[test](./index.md)
**Platform and version requirements:** JVM, JS
### Packages
-| [foo.bar](test/foo.bar/index)<br>(JVM, JS) | |
+| [foo.bar](foo.bar/index.md)<br>(JVM, JS) | |
### Index
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
index 2921cdd1..4ddfe2e3 100644
--- a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo.bar](test/foo.bar/index)
+[test](../index.md) / [foo.bar](./index.md)
## Package foo.bar
@@ -6,5 +6,5 @@
### Functions
-| [buz](test/foo.bar/buz)<br>(JVM, JS) | `fun buz(): Unit` |
+| [buz](buz.md)<br>(JVM, JS) | `fun buz(): Unit` |
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
index f6e770b2..f4186b6e 100644
--- a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
@@ -1,10 +1,10 @@
-[test](test/index)
+[test](./index.md)
**Platform and version requirements:** JVM
### Packages
-| [some](test/some/index)<br>(JVM) | |
+| [some](some/index.md)<br>(JVM) | |
### Index
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
index 22f778d6..ff480b5a 100644
--- a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
@@ -1,4 +1,4 @@
-[test](test/index) / [some](test/some/index)
+[test](../index.md) / [some](./index.md)
## Package some
@@ -6,5 +6,5 @@
### Extensions for External Classes
-| [kotlin.String](test/some/kotlin.-string/index) | |
+| [kotlin.String](kotlin.-string/index.md) | |
diff --git a/core/testdata/format/multiplatform/simple/multiplatform.package.md b/core/testdata/format/multiplatform/simple/multiplatform.package.md
index 3574942c..fad7e90d 100644
--- a/core/testdata/format/multiplatform/simple/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/simple/multiplatform.package.md
@@ -1,9 +1,9 @@
-[test](test/index) / [foo](test/foo/index)
+[test](../index.md) / [foo](./index.md)
## 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. |
+| [Bar](-bar/index.md)<br>(JS) | `class Bar`<br>This is a bar. |
+| [Foo](-foo/index.md)<br>(JVM) | `class Foo`<br>This is a foo. |
diff --git a/core/testdata/format/multipleTypeParameterConstraints.md b/core/testdata/format/multipleTypeParameterConstraints.md
index 5dd45e4e..78586aca 100644
--- a/core/testdata/format/multipleTypeParameterConstraints.md
+++ b/core/testdata/format/multipleTypeParameterConstraints.md
@@ -1,13 +1,18 @@
-[test](test/index) / [A](test/-a)
+<!-- File: test/-a.md -->
+[test](index.md) / [A](./-a.md)
# A
-`interface A`[test](test/index) / [B](test/-b)
+`interface A`
+<!-- File: test/-b.md -->
+[test](index.md) / [B](./-b.md)
# B
-`interface B`[test](test/index) / [f](test/f)
+`interface B`
+<!-- File: test/f.md -->
+[test](index.md) / [f](./f.md)
# f
-`fun <T> f(): Unit where T : `[`A`](test/-a)`, T : `[`B`](test/-b) \ No newline at end of file
+`fun <T> f(): Unit where T : `[`A`](-a.md)`, T : `[`B`](-b.md) \ No newline at end of file
diff --git a/core/testdata/format/nestedLists.md b/core/testdata/format/nestedLists.md
index dd151944..fb25bc32 100644
--- a/core/testdata/format/nestedLists.md
+++ b/core/testdata/format/nestedLists.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -8,36 +8,36 @@ Usage instructions:
* **Rinse**
1. Alter any rinse options *(optional)*
- * Recommended to [Bar.useSoap](test/-bar/use-soap)
+ * Recommended to [Bar.useSoap](use-soap.md)
* Optionally apply [Bar.elbowGrease](#) for best results
- 2. [Bar.rinse](test/-bar/rinse) to begin rinse
- 1. Thus you should call [Bar.rinse](test/-bar/rinse)
- 2. *Then* call [Bar.repeat](test/-bar/repeat)
+ 2. [Bar.rinse](rinse.md) to begin rinse
+ 1. Thus you should call [Bar.rinse](rinse.md)
+ 2. *Then* call [Bar.repeat](repeat.md)
* Don't forget to use:
* Soap
* Elbow Grease
- 3. Finally, adjust soap usage [Bar.useSoap](test/-bar/use-soap) as needed
- 3. Repeat with [Bar.repeat](test/-bar/repeat)
+ 3. Finally, adjust soap usage [Bar.useSoap](use-soap.md) as needed
+ 3. Repeat with [Bar.repeat](repeat.md)
* **Repeat**
* Will use previously used rinse options
- * [Bar.rinse](test/-bar/rinse) must have been called once before
+ * [Bar.rinse](rinse.md) must have been called once before
* Can be repeated any number of times
* Options include:
- * [Bar.useSoap](test/-bar/use-soap)
- * [Bar.useElbowGrease](test/-bar/use-elbow-grease)
+ * [Bar.useSoap](use-soap.md)
+ * [Bar.useElbowGrease](use-elbow-grease.md)
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>Usage instructions: |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage instructions: |
### Properties
-| [useElbowGrease](test/-bar/use-elbow-grease) | `var useElbowGrease: Boolean` |
-| [useSoap](test/-bar/use-soap) | `var useSoap: Boolean` |
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
### Functions
-| [repeat](test/-bar/repeat) | `fun repeat(): Unit` |
-| [rinse](test/-bar/rinse) | `fun rinse(): Unit` |
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
diff --git a/core/testdata/format/newlineInTableCell.package.md b/core/testdata/format/newlineInTableCell.package.md
index 4b3875ba..53716db3 100644
--- a/core/testdata/format/newlineInTableCell.package.md
+++ b/core/testdata/format/newlineInTableCell.package.md
@@ -1,8 +1,8 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
### Types
-| [A](test/-a/index) | `class A`<br>There is `long long int` story full of new lines |
+| [A](-a/index.md) | `class A`<br>There is `long long int` story full of new lines |
diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
index 9e0f1560..ca95093c 100644
--- a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
+++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
@@ -1,9 +1,9 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
`fun foo(): Unit`
-Correct ref [TA](test/-a/index)
-Correct ref [TB](test/-b/index)
+Correct ref [TA](-a/index.md)
+Correct ref [TB](-b/index.md)
diff --git a/core/testdata/format/nullability.md b/core/testdata/format/nullability.md
index 014eb485..7b81c255 100644
--- a/core/testdata/format/nullability.md
+++ b/core/testdata/format/nullability.md
@@ -1,4 +1,4 @@
-[test](test/index) / [C](test/-c/index)
+[test](../index.md) / [C](./index.md)
# C
@@ -6,9 +6,9 @@
### Constructors
-| [&lt;init&gt;](test/-c/-init-) | `C()` |
+| [&lt;init&gt;](-init-.md) | `C()` |
### Functions
-| [foo](test/-c/foo) | `fun foo(): Comparable<`[`T`](test/-c/index#T)`>?` |
+| [foo](foo.md) | `fun foo(): Comparable<`[`T`](index.md#T)`>?` |
diff --git a/core/testdata/format/operatorOverloading.md b/core/testdata/format/operatorOverloading.md
index 8e8d36eb..0a4c87b6 100644
--- a/core/testdata/format/operatorOverloading.md
+++ b/core/testdata/format/operatorOverloading.md
@@ -1,5 +1,5 @@
-[test](test/index) / [C](test/-c/index) / [plus](test/-c/plus)
+[test](../index.md) / [C](index.md) / [plus](./plus.md)
# plus
-`fun plus(other: `[`C`](test/-c/index)`): `[`C`](test/-c/index) \ No newline at end of file
+`fun plus(other: `[`C`](index.md)`): `[`C`](index.md) \ No newline at end of file
diff --git a/core/testdata/format/orderedList.html b/core/testdata/format/orderedList.html
index 66f37876..6f735bfd 100644
--- a/core/testdata/format/orderedList.html
+++ b/core/testdata/format/orderedList.html
@@ -4,7 +4,7 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-bar/index">Bar</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
@@ -17,7 +17,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
diff --git a/core/testdata/format/overloads.html b/core/testdata/format/overloads.html
index d0f7f37d..feda82e4 100644
--- a/core/testdata/format/overloads.html
+++ b/core/testdata/format/overloads.html
@@ -4,7 +4,7 @@
<title>root package - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a><br/>
+<a href="./index.html">test</a><br/>
<br/>
<h2>Package &lt;root&gt;</h2>
<h3>Functions</h3>
@@ -12,7 +12,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/f">f</a></p>
+<p><a href="f.html">f</a></p>
</td>
<td>
<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
diff --git a/core/testdata/format/overloadsWithDescription.html b/core/testdata/format/overloadsWithDescription.html
index fe98b8fe..16b03f7e 100644
--- a/core/testdata/format/overloadsWithDescription.html
+++ b/core/testdata/format/overloadsWithDescription.html
@@ -4,14 +4,14 @@
<title>f - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f(kotlin.Int)"></a>
<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
<a name="$f(kotlin.String)"></a>
<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
-<p>Performs an action on <a href="test/f#$f(kotlin.Int)/x">x</a>.</p>
+<p>Performs an action on <a href="f.html#$f(kotlin.Int)/x">x</a>.</p>
<p>This is a long description.</p>
<h3>Parameters</h3>
<p><a name="x"></a>
diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.html b/core/testdata/format/overloadsWithDifferentDescriptions.html
index 1f086039..4c4f7f74 100644
--- a/core/testdata/format/overloadsWithDifferentDescriptions.html
+++ b/core/testdata/format/overloadsWithDifferentDescriptions.html
@@ -4,7 +4,7 @@
<title>f - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f(kotlin.Int)"></a>
diff --git a/core/testdata/format/overridingFunction.md b/core/testdata/format/overridingFunction.md
index dcc1c426..d0ec82fa 100644
--- a/core/testdata/format/overridingFunction.md
+++ b/core/testdata/format/overridingFunction.md
@@ -1,8 +1,8 @@
-[test](test/index) / [D](test/-d/index) / [f](test/-d/f)
+[test](../index.md) / [D](index.md) / [f](./f.md)
# f
`fun f(): Unit`
-Overrides [C.f](test/-c/f)
+Overrides [C.f](../-c/f.md)
diff --git a/core/testdata/format/paramTag.md b/core/testdata/format/paramTag.md
index d3e948ae..7cc33d21 100644
--- a/core/testdata/format/paramTag.md
+++ b/core/testdata/format/paramTag.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/parameterAnchor.html b/core/testdata/format/parameterAnchor.html
index 3ffcf595..a4ae0997 100644
--- a/core/testdata/format/parameterAnchor.html
+++ b/core/testdata/format/parameterAnchor.html
@@ -4,12 +4,12 @@
<title>processFiles - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/process-files">processFiles</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./process-files.html">processFiles</a><br/>
<br/>
<h1>processFiles</h1>
<a name="$processFiles(kotlin.Function0((processFiles.T)))"></a>
-<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">processFiles</span><span class="symbol">(</span><span class="identifier" id="$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="test/process-files#T"><span class="identifier">T</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">List</span><span class="symbol">&lt;</span><a href="test/process-files#T"><span class="identifier">T</span></a><span class="symbol">&gt;</span></code>
-<p>Runs <a href="test/process-files#$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</a> for each file and collects its results into single list</p>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">processFiles</span><span class="symbol">(</span><span class="identifier" id="$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">List</span><span class="symbol">&lt;</span><a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">&gt;</span></code>
+<p>Runs <a href="process-files.html#$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</a> for each file and collects its results into single list</p>
<h3>Parameters</h3>
<p><a name="processor"></a>
<code>processor</code> - function to receive context for symbol resolution and file for processing</p>
diff --git a/core/testdata/format/parenthesis.html b/core/testdata/format/parenthesis.html
index 6f3c1d6b..c63154c1 100644
--- a/core/testdata/format/parenthesis.html
+++ b/core/testdata/format/parenthesis.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/propertyVar.md b/core/testdata/format/propertyVar.md
index 8232dff4..887d25a5 100644
--- a/core/testdata/format/propertyVar.md
+++ b/core/testdata/format/propertyVar.md
@@ -1,4 +1,4 @@
-[test](test/index) / [x](test/x)
+[test](index.md) / [x](./x.md)
# x
diff --git a/core/testdata/format/qualifiedNameLink.md b/core/testdata/format/qualifiedNameLink.md
index 590bb435..92fa8f7a 100644
--- a/core/testdata/format/qualifiedNameLink.md
+++ b/core/testdata/format/qualifiedNameLink.md
@@ -1,4 +1,4 @@
-[test](test/index) / [foo](test/foo)
+[test](index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/receiverParameterTypeBound.md b/core/testdata/format/receiverParameterTypeBound.md
index 9c767449..978dc0f8 100644
--- a/core/testdata/format/receiverParameterTypeBound.md
+++ b/core/testdata/format/receiverParameterTypeBound.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -6,9 +6,9 @@
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
### Extension Functions
-| [xyzzy](test/xyzzy) | `fun <T : `[`Foo`](test/-foo/index)`> `[`T`](test/xyzzy#T)`.xyzzy(): Unit` |
+| [xyzzy](../xyzzy.md) | `fun <T : `[`Foo`](./index.md)`> `[`T`](../xyzzy.md#T)`.xyzzy(): Unit` |
diff --git a/core/testdata/format/receiverReference.md b/core/testdata/format/receiverReference.md
index 1584b2b1..bdcce322 100644
--- a/core/testdata/format/receiverReference.md
+++ b/core/testdata/format/receiverReference.md
@@ -1,6 +1,6 @@
-[test](test/index) / [kotlin.String](test/kotlin.-string/index)
+[test](../index.md) / [kotlin.String](./index.md)
### Extensions for kotlin.String
-| [some](test/kotlin.-string/some) | `fun String.some(): Unit`<br>Prints [this](test/kotlin.-string/some/-this-) |
+| [some](some.md) | `fun String.some(): Unit`<br>Prints [this](some/-this-.md) |
diff --git a/core/testdata/format/referenceLink.md b/core/testdata/format/referenceLink.md
index b3d89e0d..ee910cbf 100644
--- a/core/testdata/format/referenceLink.md
+++ b/core/testdata/format/referenceLink.md
@@ -1,14 +1,16 @@
-[test](test/index) / [example](test/example/index) / [a](test/example/a)
+<!-- File: test/example/a.md -->
+[test](../index.md) / [example](index.md) / [a](./a.md)
# a
`fun a(): Unit`
-It is link to [example other func](test/example/some-other-func)
+It is link to [example other func](some-other-func.md)
-Sure, it is [example](test/example/some-other-func)
+Sure, it is [example](some-other-func.md)
-[test](test/index) / [example](test/example/index) / [someOtherFunc](test/example/some-other-func)
+<!-- File: test/example/some-other-func.md -->
+[test](../index.md) / [example](index.md) / [someOtherFunc](./some-other-func.md)
# someOtherFunc
diff --git a/core/testdata/format/reifiedTypeParameter.md b/core/testdata/format/reifiedTypeParameter.md
index 253f2438..40dbed7b 100644
--- a/core/testdata/format/reifiedTypeParameter.md
+++ b/core/testdata/format/reifiedTypeParameter.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
index c9856976..ad632fef 100644
--- a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
@@ -1,6 +1,6 @@
-[test](test/index) / [kotlin.SuspendFunction0](test/kotlin.-suspend-function0/index)
+[test](../index.md) / [kotlin.SuspendFunction0](./index.md)
### Extensions for kotlin.SuspendFunction0
-| [foo](test/kotlin.-suspend-function0/foo) | `fun (suspend () -> Unit).foo(): Unit` |
+| [foo](foo.md) | `fun (suspend () -> Unit).foo(): Unit` |
diff --git a/core/testdata/format/returnWithLink.html b/core/testdata/format/returnWithLink.html
index f6e918dd..fe1d031b 100644
--- a/core/testdata/format/returnWithLink.html
+++ b/core/testdata/format/returnWithLink.html
@@ -4,12 +4,12 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo(kotlin.String)"></a>
<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="$foo(kotlin.String)/s1">s1</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code>
<p><strong>Return</strong><br/>
-Returns <a href="test/foo#$foo(kotlin.String)/s1">s1</a> and does nothing else.</p>
+Returns <a href="foo.html#$foo(kotlin.String)/s1">s1</a> and does nothing else.</p>
</BODY>
</HTML>
diff --git a/core/testdata/format/sampleByFQName.md b/core/testdata/format/sampleByFQName.md
index 06333e13..7093179d 100644
--- a/core/testdata/format/sampleByFQName.md
+++ b/core/testdata/format/sampleByFQName.md
@@ -1,8 +1,11 @@
-[test](test/index) / [test](test/test/index) / [sample](test/test/sample)
+<!-- File: test/test/sample.md -->
+[test](../index.md) / [test](index.md) / [sample](./sample.md)
# sample
-`fun sample(): Unit`[test](test/index) / [test](test/test/index) / [use](test/test/use)
+`fun sample(): Unit`
+<!-- File: test/test/use.md -->
+[test](../index.md) / [test](index.md) / [use](./use.md)
# use
diff --git a/core/testdata/format/sampleByShortName.md b/core/testdata/format/sampleByShortName.md
index 06333e13..7093179d 100644
--- a/core/testdata/format/sampleByShortName.md
+++ b/core/testdata/format/sampleByShortName.md
@@ -1,8 +1,11 @@
-[test](test/index) / [test](test/test/index) / [sample](test/test/sample)
+<!-- File: test/test/sample.md -->
+[test](../index.md) / [test](index.md) / [sample](./sample.md)
# sample
-`fun sample(): Unit`[test](test/index) / [test](test/test/index) / [use](test/test/use)
+`fun sample(): Unit`
+<!-- File: test/test/use.md -->
+[test](../index.md) / [test](index.md) / [use](./use.md)
# use
diff --git a/core/testdata/format/see.html b/core/testdata/format/see.html
index d350d6b7..74773951 100644
--- a/core/testdata/format/see.html
+++ b/core/testdata/format/see.html
@@ -1,22 +1,43 @@
+<!-- File: test/quux.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
+<title>quux - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/quux">quux</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./quux.html">quux</a><br/>
<br/>
<h1>quux</h1>
<a name="$quux()"></a>
<code><span class="keyword">fun </span><span class="identifier">quux</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
<p><strong>See Also</strong><br/>
-<p><a href="test/foo">foo</a></p>
-<p><a href="test/bar">bar</a></p>
+<p><a href="foo.html">foo</a></p>
+<p><a href="bar.html">bar</a></p>
</p>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+</BODY>
+</HTML>
+<!-- File: test/foo.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
-<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><a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/bar">bar</a><br/>
+<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>
+</BODY>
+</HTML>
+<!-- File: test/bar.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>bar - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./bar.html">bar</a><br/>
<br/>
<h1>bar</h1>
<a name="$bar()"></a>
diff --git a/core/testdata/format/shadowedExtensionFunctions.md b/core/testdata/format/shadowedExtensionFunctions.md
index 3b0a9dc9..f900ecb2 100644
--- a/core/testdata/format/shadowedExtensionFunctions.md
+++ b/core/testdata/format/shadowedExtensionFunctions.md
@@ -1,15 +1,15 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
-`class Bar : `[`Foo`](test/-foo/index)
+`class Bar : `[`Foo`](../-foo/index.md)
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()` |
+| [&lt;init&gt;](-init-.md) | `Bar()` |
### Extension Functions
-| [shazam](test/shazam) | `fun `[`Bar`](test/-bar/index)`.shazam(i: Int): Unit`<br>`fun `[`Foo`](test/-foo/index)`.shazam(): Unit` |
-| [xyzzy](test/xyzzy) | `fun `[`Bar`](test/-bar/index)`.xyzzy(): Unit` |
+| [shazam](../shazam.md) | `fun `[`Bar`](./index.md)`.shazam(i: Int): Unit`<br>`fun `[`Foo`](../-foo/index.md)`.shazam(): Unit` |
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html
index ab25a002..32988de2 100644
--- a/core/testdata/format/sinceKotlin.html
+++ b/core/testdata/format/sinceKotlin.html
@@ -4,7 +4,7 @@
<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/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Since1.1</a><br/>
<br/>
<h1>Since1.1</h1>
<code><span class="keyword">class </span><span class="identifier">Since1.1</span></code>
@@ -15,7 +15,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/-since1.1/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code>
diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md
index a1abe5fd..df96db0a 100644
--- a/core/testdata/format/sinceKotlin.md
+++ b/core/testdata/format/sinceKotlin.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Since1.1](test/-since1.1/index)
+[test](../index.md) / [Since1.1](./index.md)
# Since1.1
@@ -10,5 +10,5 @@ Useful
### Constructors
-| [&lt;init&gt;](test/-since1.1/-init-) | `Since1.1()`<br>Useful |
+| [&lt;init&gt;](-init-.md) | `Since1.1()`<br>Useful |
diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md
index c811749c..eabf88d5 100644
--- a/core/testdata/format/sinceKotlin.package.md
+++ b/core/testdata/format/sinceKotlin.package.md
@@ -1,4 +1,4 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
@@ -6,5 +6,5 @@
### Types
-| [Since1.1](test/-since1.1/index)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
diff --git a/core/testdata/format/sinceKotlinWide.package.md b/core/testdata/format/sinceKotlinWide.package.md
index f683b178..58a5045e 100644
--- a/core/testdata/format/sinceKotlinWide.package.md
+++ b/core/testdata/format/sinceKotlinWide.package.md
@@ -1,4 +1,4 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
@@ -6,6 +6,6 @@
### Types
-| [Since1.1](test/-since1.1/index)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
-| [Since1.2](test/-since1.2/index)<br>(Kotlin 1.2) | `class Since1.2`<br>Useful also |
+| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+| [Since1.2](-since1.2/index.md)<br>(Kotlin 1.2) | `class Since1.2`<br>Useful also |
diff --git a/core/testdata/format/starProjection.md b/core/testdata/format/starProjection.md
index 6f796e77..5a53e5b9 100644
--- a/core/testdata/format/starProjection.md
+++ b/core/testdata/format/starProjection.md
@@ -1,6 +1,6 @@
-[test](test/index) / [kotlin.collections.Iterable](test/kotlin.collections.-iterable/index)
+[test](../index.md) / [kotlin.collections.Iterable](./index.md)
### Extensions for kotlin.collections.Iterable
-| [containsFoo](test/kotlin.collections.-iterable/contains-foo) | `fun Iterable<*>.containsFoo(element: Any?): Boolean` |
+| [containsFoo](contains-foo.md) | `fun Iterable<*>.containsFoo(element: Any?): Boolean` |
diff --git a/core/testdata/format/summarizeSignatures.md b/core/testdata/format/summarizeSignatures.md
index c1830fb5..4f494166 100644
--- a/core/testdata/format/summarizeSignatures.md
+++ b/core/testdata/format/summarizeSignatures.md
@@ -1,14 +1,14 @@
-[test](test/index) / [kotlin](test/kotlin/index)
+[test](../index.md) / [kotlin](./index.md)
## Package kotlin
### Types
-| [Array](test/kotlin/-array/index) | `class Array<T>` |
-| [CharArray](test/kotlin/-char-array/index) | `class CharArray` |
-| [IntArray](test/kotlin/-int-array/index) | `class IntArray` |
+| [Array](-array/index.md) | `class Array<T>` |
+| [CharArray](-char-array/index.md) | `class CharArray` |
+| [IntArray](-int-array/index.md) | `class IntArray` |
### Functions
-| [foo](test/kotlin/foo) | `fun <T> any_array<T>.foo(predicate: (`[`T`](test/kotlin/foo#T)`) -> Boolean): Boolean`<br>Returns true if foo. |
+| [foo](foo.md) | `fun <T> any_array<T>.foo(predicate: (`[`T`](foo.md#T)`) -> Boolean): Boolean`<br>Returns true if foo. |
diff --git a/core/testdata/format/summarizeSignaturesProperty.md b/core/testdata/format/summarizeSignaturesProperty.md
index 712aebd4..507ad6a5 100644
--- a/core/testdata/format/summarizeSignaturesProperty.md
+++ b/core/testdata/format/summarizeSignaturesProperty.md
@@ -1,14 +1,14 @@
-[test](test/index) / [kotlin](test/kotlin/index)
+[test](../index.md) / [kotlin](./index.md)
## Package kotlin
### Types
-| [Array](test/kotlin/-array/index) | `class Array<T>` |
-| [CharArray](test/kotlin/-char-array/index) | `class CharArray` |
-| [IntArray](test/kotlin/-int-array/index) | `class IntArray` |
+| [Array](-array/index.md) | `class Array<T>` |
+| [CharArray](-char-array/index.md) | `class CharArray` |
+| [IntArray](-int-array/index.md) | `class IntArray` |
### Properties
-| [foo](test/kotlin/foo) | `val <T> any_array<T>.foo: Int`<br>Returns true if foo. |
+| [foo](foo.md) | `val <T> any_array<T>.foo: Int`<br>Returns true if foo. |
diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md
index cf11f57b..ab116140 100644
--- a/core/testdata/format/suspendParam.md
+++ b/core/testdata/format/suspendParam.md
@@ -1,4 +1,4 @@
-[test](test/index) / [takesSuspendParam](test/takes-suspend-param)
+[test](index.md) / [takesSuspendParam](./takes-suspend-param.md)
# takesSuspendParam
diff --git a/core/testdata/format/suspendParam.package.md b/core/testdata/format/suspendParam.package.md
index 463ba356..92bd7ee7 100644
--- a/core/testdata/format/suspendParam.package.md
+++ b/core/testdata/format/suspendParam.package.md
@@ -1,8 +1,8 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
### Functions
-| [takesSuspendParam](test/takes-suspend-param) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` |
+| [takesSuspendParam](takes-suspend-param.md) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` |
diff --git a/core/testdata/format/throwsTag.md b/core/testdata/format/throwsTag.md
index a476fa1b..70fba512 100644
--- a/core/testdata/format/throwsTag.md
+++ b/core/testdata/format/throwsTag.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/tokensInEmphasis.md b/core/testdata/format/tokensInEmphasis.md
index 79dde6f0..a68861de 100644
--- a/core/testdata/format/tokensInEmphasis.md
+++ b/core/testdata/format/tokensInEmphasis.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -6,15 +6,15 @@
Another emphasised class.
-*This class, [Bar](test/-bar/index) is just meh.*
+*This class, [Bar](./index.md) is just meh.*
-*For a semicolon; [Bar.foo](test/-bar/foo) is for you!.*
+*For a semicolon; [Bar.foo](foo.md) is for you!.*
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>Another emphasised class. |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Another emphasised class. |
### Functions
-| [foo](test/-bar/foo) | `fun foo(): String` |
+| [foo](foo.md) | `fun foo(): String` |
diff --git a/core/testdata/format/tokensInHeaders.md b/core/testdata/format/tokensInHeaders.md
index 46a1e9a5..bd25492e 100644
--- a/core/testdata/format/tokensInHeaders.md
+++ b/core/testdata/format/tokensInHeaders.md
@@ -1,4 +1,4 @@
-[test](test/index) / [The](test/-the/index)
+[test](../index.md) / [The](./index.md)
# The
@@ -6,32 +6,32 @@
Why did the token cross the road?
-# Because it was Beer o'clock @ [The.bar](test/-the/bar)
+# Because it was Beer o'clock @ [The.bar](bar.md)
## But **waz *\[sic\]* [it](isitbeeroclock.com)** really?
-### [The.bar](test/-the/bar) has? [The.foo](test/-the/foo)est drinks ever!
+### [The.bar](bar.md) has? [The.foo](foo.md)est drinks ever!
-#### *[The.kotlinz](test/-the/kotlinz) is [The.bestests](test/-the/bestests), [Bar.none](test/-the/-bar/none)*
+#### *[The.kotlinz](kotlinz.md) is [The.bestests](bestests.md), [Bar.none](-bar/none.md)*
-##### So many lame code "puns" (in) [The.house](test/-the/house)
+##### So many lame code "puns" (in) [The.house](house.md)
###### End of the?? [Bar.line](#)! - we need to go back!
### Types
-| [Bar](test/-the/-bar/index) | `object Bar` |
+| [Bar](-bar/index.md) | `object Bar` |
### Constructors
-| [&lt;init&gt;](test/-the/-init-) | `The()`<br>Why did the token cross the road? |
+| [&lt;init&gt;](-init-.md) | `The()`<br>Why did the token cross the road? |
### Functions
-| [bar](test/-the/bar) | `fun bar(): Unit` |
-| [bestests](test/-the/bestests) | `fun bestests(): Unit` |
-| [foo](test/-the/foo) | `fun foo(): Unit` |
-| [house](test/-the/house) | `fun house(): Unit` |
-| [kotlinz](test/-the/kotlinz) | `fun kotlinz(): Unit` |
-| [line](test/-the/line) | `fun line(): Unit` |
+| [bar](bar.md) | `fun bar(): Unit` |
+| [bestests](bestests.md) | `fun bestests(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [house](house.md) | `fun house(): Unit` |
+| [kotlinz](kotlinz.md) | `fun kotlinz(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
diff --git a/core/testdata/format/tokensInStrong.md b/core/testdata/format/tokensInStrong.md
index e1abe030..2781656c 100644
--- a/core/testdata/format/tokensInStrong.md
+++ b/core/testdata/format/tokensInStrong.md
@@ -1,20 +1,20 @@
-[test](test/index) / [Yasc](test/-yasc/index)
+[test](../index.md) / [Yasc](./index.md)
# Yasc
`class Yasc`
-**YASC: [Yasc](test/-yasc/index) Yet Another Strong Class**
+**YASC: [Yasc](./index.md) Yet Another Strong Class**
-**This class, [Yasc](test/-yasc/index) *is* just meh.**
+**This class, [Yasc](./index.md) *is* just meh.**
-**For a semicolon; [Yasc.foo](test/-yasc/foo) is for you!.**
+**For a semicolon; [Yasc.foo](foo.md) is for you!.**
### Constructors
-| [&lt;init&gt;](test/-yasc/-init-) | `Yasc()`<br>**YASC: [Yasc](test/-yasc/index) Yet Another Strong Class** |
+| [&lt;init&gt;](-init-.md) | `Yasc()`<br>**YASC: [Yasc](./index.md) Yet Another Strong Class** |
### Functions
-| [foo](test/-yasc/foo) | `fun foo(): String` |
+| [foo](foo.md) | `fun foo(): String` |
diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html
index bac3385c..dacd0567 100644
--- a/core/testdata/format/tripleBackticks.html
+++ b/core/testdata/format/tripleBackticks.html
@@ -4,7 +4,7 @@
<title>f - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/f">f</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f()"></a>
diff --git a/core/testdata/format/typeAliases.md b/core/testdata/format/typeAliases.md
index 2a813c32..218c4848 100644
--- a/core/testdata/format/typeAliases.md
+++ b/core/testdata/format/typeAliases.md
@@ -1,63 +1,104 @@
-[test](test/index) / [A](test/-a/index)
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
# A
-`class A`[test](test/index) / [B](test/-b/index)
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
# B
-`class B`[test](test/index) / [C](test/-c/index)
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
# C
-`class C<T>`[test](test/index) / [D](test/-d)
+`class C<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/-d.md -->
+[test](index.md) / [D](./-d.md)
# D
-`typealias D = `[`A`](test/-a/index)[test](test/index) / [E](test/-e)
+`typealias D = `[`A`](-a/index.md)
+<!-- File: test/-e.md -->
+[test](index.md) / [E](./-e.md)
# E
-`typealias E = `[`D`](test/-d)[test](test/index) / [F](test/-f)
+`typealias E = `[`D`](-d.md)
+<!-- File: test/-f.md -->
+[test](index.md) / [F](./-f.md)
# F
-`typealias F = (`[`A`](test/-a/index)`) -> `[`B`](test/-b/index)[test](test/index) / [G](test/-g)
+`typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md)
+<!-- File: test/-g.md -->
+[test](index.md) / [G](./-g.md)
# G
-`typealias G = `[`C`](test/-c/index)`<`[`A`](test/-a/index)`>`[test](test/index) / [H](test/-h)
+`typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-h.md -->
+[test](index.md) / [H](./-h.md)
# H
-`typealias H<T> = `[`C`](test/-c/index)`<`[`T`](test/-h#T)`>`[test](test/index) / [I](test/-i)
+`typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>`
+<!-- File: test/-i.md -->
+[test](index.md) / [I](./-i.md)
# I
-`typealias I<T> = `[`H`](test/-h)`<`[`T`](test/-i#T)`>`[test](test/index) / [J](test/-j)
+`typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>`
+<!-- File: test/-j.md -->
+[test](index.md) / [J](./-j.md)
# J
-`typealias J = `[`H`](test/-h)`<`[`A`](test/-a/index)`>`[test](test/index) / [K](test/-k)
+`typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-k.md -->
+[test](index.md) / [K](./-k.md)
# K
-`typealias K = `[`H`](test/-h)`<`[`J`](test/-j)`>`[test](test/index) / [L](test/-l)
+`typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>`
+<!-- File: test/-l.md -->
+[test](index.md) / [L](./-l.md)
# L
-`typealias L = (`[`K`](test/-k)`, `[`B`](test/-b/index)`) -> `[`J`](test/-j)[test](test/index) / [M](test/-m)
+`typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md)
+<!-- File: test/-m.md -->
+[test](index.md) / [M](./-m.md)
# M
-`typealias M = `[`A`](test/-a/index)
+`typealias M = `[`A`](-a/index.md)
Documented
-[test](test/index) / [N](test/-n)
+<!-- File: test/-n.md -->
+[test](index.md) / [N](./-n.md)
# N
-`typealias ~~N~~ = `[`A`](test/-a/index)
+`typealias ~~N~~ = `[`A`](-a/index.md)
**Deprecated:** !!!
diff --git a/core/testdata/format/typeAliases.package.md b/core/testdata/format/typeAliases.package.md
index 9407588b..199e91c2 100644
--- a/core/testdata/format/typeAliases.package.md
+++ b/core/testdata/format/typeAliases.package.md
@@ -1,24 +1,24 @@
-[test](test/index)
+[test](./index.md)
## Package &lt;root&gt;
### Types
-| [A](test/-a/index) | `class A` |
-| [B](test/-b/index) | `class B` |
-| [C](test/-c/index) | `class C<T>` |
+| [A](-a/index.md) | `class A` |
+| [B](-b/index.md) | `class B` |
+| [C](-c/index.md) | `class C<T>` |
### Type Aliases
-| [D](test/-d) | `typealias D = `[`A`](test/-a/index) |
-| [E](test/-e) | `typealias E = `[`D`](test/-d) |
-| [F](test/-f) | `typealias F = (`[`A`](test/-a/index)`) -> `[`B`](test/-b/index) |
-| [G](test/-g) | `typealias G = `[`C`](test/-c/index)`<`[`A`](test/-a/index)`>` |
-| [H](test/-h) | `typealias H<T> = `[`C`](test/-c/index)`<`[`T`](test/-h#T)`>` |
-| [I](test/-i) | `typealias I<T> = `[`H`](test/-h)`<`[`T`](test/-i#T)`>` |
-| [J](test/-j) | `typealias J = `[`H`](test/-h)`<`[`A`](test/-a/index)`>` |
-| [K](test/-k) | `typealias K = `[`H`](test/-h)`<`[`J`](test/-j)`>` |
-| [L](test/-l) | `typealias L = (`[`K`](test/-k)`, `[`B`](test/-b/index)`) -> `[`J`](test/-j) |
-| [M](test/-m) | `typealias M = `[`A`](test/-a/index)<br>Documented |
-| [N](test/-n) | `typealias ~~N~~ = `[`A`](test/-a/index) |
+| [D](-d.md) | `typealias D = `[`A`](-a/index.md) |
+| [E](-e.md) | `typealias E = `[`D`](-d.md) |
+| [F](-f.md) | `typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md) |
+| [G](-g.md) | `typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>` |
+| [H](-h.md) | `typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>` |
+| [I](-i.md) | `typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>` |
+| [J](-j.md) | `typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>` |
+| [K](-k.md) | `typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>` |
+| [L](-l.md) | `typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md) |
+| [M](-m.md) | `typealias M = `[`A`](-a/index.md)<br>Documented |
+| [N](-n.md) | `typealias ~~N~~ = `[`A`](-a/index.md) |
diff --git a/core/testdata/format/typeLink.html b/core/testdata/format/typeLink.html
index 2e51863f..30af8a93 100644
--- a/core/testdata/format/typeLink.html
+++ b/core/testdata/format/typeLink.html
@@ -4,16 +4,16 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/-bar/index">Bar</a><br/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
-<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="test/-foo/index"><span class="identifier">Foo</span></a></code>
+<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="test/-bar/-init-">&lt;init&gt;</a></p>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
</td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
diff --git a/core/testdata/format/typeParameterBounds.md b/core/testdata/format/typeParameterBounds.md
index 8f369ed6..cf03b3a7 100644
--- a/core/testdata/format/typeParameterBounds.md
+++ b/core/testdata/format/typeParameterBounds.md
@@ -1,8 +1,8 @@
-[test](test/index) / [generic](test/generic)
+[test](index.md) / [generic](./generic.md)
# generic
-`fun <T : `[`R`](test/generic#R)`, R> generic(): Unit`
+`fun <T : `[`R`](generic.md#R)`, R> generic(): Unit`
generic function
diff --git a/core/testdata/format/typeParameterReference.md b/core/testdata/format/typeParameterReference.md
index ea49d48a..5001d321 100644
--- a/core/testdata/format/typeParameterReference.md
+++ b/core/testdata/format/typeParameterReference.md
@@ -1,8 +1,8 @@
-[test](test/index) / [tt](test/tt)
+[test](index.md) / [tt](./tt.md)
# tt
-`fun <T> `[`T`](test/tt#T)`.tt(): Unit`
+`fun <T> `[`T`](tt.md#T)`.tt(): Unit`
-Correct ref to [T](test/tt#T)
+Correct ref to [T](tt.md#T)
diff --git a/core/testdata/format/typeParameterVariance.md b/core/testdata/format/typeParameterVariance.md
index 01f5562c..b0615d43 100644
--- a/core/testdata/format/typeParameterVariance.md
+++ b/core/testdata/format/typeParameterVariance.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Foo](test/-foo/index)
+[test](../index.md) / [Foo](./index.md)
# Foo
@@ -10,5 +10,5 @@
### Constructors
-| [&lt;init&gt;](test/-foo/-init-) | `Foo()` |
+| [&lt;init&gt;](-init-.md) | `Foo()` |
diff --git a/core/testdata/format/typeProjectionVariance.md b/core/testdata/format/typeProjectionVariance.md
index 072b9fc7..d3a55c58 100644
--- a/core/testdata/format/typeProjectionVariance.md
+++ b/core/testdata/format/typeProjectionVariance.md
@@ -1,6 +1,6 @@
-[test](test/index) / [kotlin.Array](test/kotlin.-array/index)
+[test](../index.md) / [kotlin.Array](./index.md)
### Extensions for kotlin.Array
-| [foo](test/kotlin.-array/foo) | `fun <T> Array<out `[`T`](test/kotlin.-array/foo#T)`>.foo(): Unit` |
+| [foo](foo.md) | `fun <T> Array<out `[`T`](foo.md#T)`>.foo(): Unit` |
diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.html b/core/testdata/format/uninterpretedEmphasisCharacters.html
index ced7d0a5..dd338f72 100644
--- a/core/testdata/format/uninterpretedEmphasisCharacters.html
+++ b/core/testdata/format/uninterpretedEmphasisCharacters.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="test/index">test</a>&nbsp;/&nbsp;<a href="test/foo">foo</a><br/>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
<br/>
<h1>foo</h1>
<a name="$foo()"></a>
diff --git a/core/testdata/format/unorderedLists.md b/core/testdata/format/unorderedLists.md
index a6b00b34..52ad9a71 100644
--- a/core/testdata/format/unorderedLists.md
+++ b/core/testdata/format/unorderedLists.md
@@ -1,4 +1,4 @@
-[test](test/index) / [Bar](test/-bar/index)
+[test](../index.md) / [Bar](./index.md)
# Bar
@@ -11,9 +11,9 @@ Usage summary:
Usage instructions:
-* [Bar.rinse](test/-bar/rinse) to rinse
+* [Bar.rinse](rinse.md) to rinse
* Alter any rinse options *(optional)*
-* To repeat; [Bar.repeat](test/-bar/repeat)
+* To repeat; [Bar.repeat](repeat.md)
* Can reconfigure options:
* Soap
* Elbow Grease
@@ -21,27 +21,27 @@ Usage instructions:
Rinse options:
-* [Bar.useSoap](test/-bar/use-soap)
+* [Bar.useSoap](use-soap.md)
* *recommended*
-* [Bar.useElbowGrease](test/-bar/use-elbow-grease)
+* [Bar.useElbowGrease](use-elbow-grease.md)
* *warning: requires effort*
-* [Bar.useBleach](test/-bar/use-bleach)
+* [Bar.useBleach](use-bleach.md)
* **use with caution**
### Constructors
-| [&lt;init&gt;](test/-bar/-init-) | `Bar()`<br>Usage summary: |
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage summary: |
### Properties
-| [useBleach](test/-bar/use-bleach) | `var useBleach: Boolean` |
-| [useElbowGrease](test/-bar/use-elbow-grease) | `var useElbowGrease: Boolean` |
-| [useSoap](test/-bar/use-soap) | `var useSoap: Boolean` |
+| [useBleach](use-bleach.md) | `var useBleach: Boolean` |
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
### Functions
-| [repeat](test/-bar/repeat) | `fun repeat(): Unit` |
-| [rinse](test/-bar/rinse) | `fun rinse(): Unit` |
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
diff --git a/core/testdata/format/varargsFunction.md b/core/testdata/format/varargsFunction.md
index 85ac9c92..550202cc 100644
--- a/core/testdata/format/varargsFunction.md
+++ b/core/testdata/format/varargsFunction.md
@@ -1,4 +1,4 @@
-[test](test/index) / [f](test/f)
+[test](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/website-html/dataTags/multiplatform.package.html b/core/testdata/format/website-html/dataTags/multiplatform.package.html
index 15e1fc12..35453ab1 100644
--- a/core/testdata/format/website-html/dataTags/multiplatform.package.html
+++ b/core/testdata/format/website-html/dataTags/multiplatform.package.html
@@ -1,10 +1,10 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">foo</a></div>
+<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">foo</a></div>
<h2>Package foo</h2>
<h3>Functions</h3>
<table class="api-docs-table">
<tbody>
<tr data-platform="JVM" data-jre-version="JRE7"><td>
-<p><a href="test/foo/jre7">jre7</a></p>
+<p><a href="jre7.html">jre7</a></p>
</td>
<td>
@@ -12,7 +12,7 @@
</td>
</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td>
-<p><a href="test/foo/jre7-new">jre7New</a></p>
+<p><a href="jre7-new.html">jre7New</a></p>
</td>
<td>
@@ -20,7 +20,7 @@
</td>
</tr><tr data-platform="JS"><td>
-<p><a href="test/foo/js">js</a></p>
+<p><a href="js.html">js</a></p>
</td>
<td>
@@ -28,7 +28,7 @@
</td>
</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td>
-<p><a href="test/foo/js-new">jsNew</a></p>
+<p><a href="js-new.html">jsNew</a></p>
</td>
<td>
@@ -36,7 +36,7 @@
</td>
</tr><tr data-platform="JVM"><td>
-<p><a href="test/foo/jvm">jvm</a></p>
+<p><a href="jvm.html">jvm</a></p>
</td>
<td>
@@ -44,7 +44,7 @@
</td>
</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td>
-<p><a href="test/foo/jvm-new">jvmNew</a></p>
+<p><a href="jvm-new.html">jvmNew</a></p>
</td>
<td>
@@ -52,7 +52,7 @@
</td>
</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td>
-<p><a href="test/foo/shared">shared</a></p>
+<p><a href="shared.html">shared</a></p>
</td>
<td>
@@ -60,7 +60,7 @@
</td>
</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td>
-<p><a href="test/foo/shared-new">sharedNew</a></p>
+<p><a href="shared-new.html">sharedNew</a></p>
</td>
<td>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
index 35773561..3d34fc7e 100644
--- a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
@@ -1,4 +1,4 @@
-<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>
+<div class='api-docs-breadcrumbs'><a href="../../index.html">test</a> / <a href="../index.html">pack</a> / <a href="./index.html">Some</a></div>
<h1>Some</h1>
<div class="overload-group" data-platform="JVM"><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>
<p><strong>Platform and version requirements:</strong> JVM</p>
@@ -10,7 +10,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/pack/-some/-some/-init-">&lt;init&gt;</a></p>
+<p><a href="-some/-init-.html">&lt;init&gt;</a></p>
</td>
<td>
@@ -25,7 +25,7 @@
<tbody>
<tr>
<td>
-<p><a href="test/pack/-some/-some/magic">magic</a></p>
+<p><a href="-some/magic.html">magic</a></p>
</td>
<td>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
index 15ae2a5b..c8926a28 100644
--- a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
@@ -1,10 +1,10 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div>
+<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">pack</a></div>
<h2>Package pack</h2>
<h3>Types</h3>
<table class="api-docs-table">
<tbody>
<tr data-platform="JS"><td>
-<p><a href="test/pack/-some/index">Some</a></p>
+<p><a href="-some/index.html">Some</a></p>
</td>
<td>
@@ -12,7 +12,7 @@
</td>
</tr><tr data-platform="JVM"><td>
-<p><a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a></p>
+<p><a href="-some-cool-jvm-class/index.html">SomeCoolJvmClass</a></p>
</td>
<td>
@@ -25,7 +25,7 @@
<table class="api-docs-table">
<tbody>
<tr data-platform="JVM"><td>
-<p><a href="test/pack/-some/index">Some</a></p>
+<p><a href="-some/index.html">Some</a></p>
</td>
<td>
diff --git a/core/testdata/format/website-html/dropImport.html b/core/testdata/format/website-html/dropImport.html
index 97ff4a68..e0fcb12b 100644
--- a/core/testdata/format/website-html/dropImport.html
+++ b/core/testdata/format/website-html/dropImport.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
<h1>foo</h1>
<a name="$foo()"></a>
<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>
diff --git a/core/testdata/format/website-html/newLinesInImportList.html b/core/testdata/format/website-html/newLinesInImportList.html
index 334ed11c..b5a07325 100644
--- a/core/testdata/format/website-html/newLinesInImportList.html
+++ b/core/testdata/format/website-html/newLinesInImportList.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
<h1>foo</h1>
<a name="$foo()"></a>
<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>
diff --git a/core/testdata/format/website-html/newLinesInSamples.html b/core/testdata/format/website-html/newLinesInSamples.html
index ab70140d..50f875da 100644
--- a/core/testdata/format/website-html/newLinesInSamples.html
+++ b/core/testdata/format/website-html/newLinesInSamples.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
<h1>foo</h1>
<a name="$foo()"></a>
<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>
diff --git a/core/testdata/format/website-html/overloadGroup.html b/core/testdata/format/website-html/overloadGroup.html
index 3ea7de94..aaba9c96 100644
--- a/core/testdata/format/website-html/overloadGroup.html
+++ b/core/testdata/format/website-html/overloadGroup.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/magic">magic</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./magic.html">magic</a></div>
<h1>magic</h1>
<div class="overload-group"><a name="$magic(kotlin.String)"></a>
<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
diff --git a/core/testdata/format/website-html/returnTag.html b/core/testdata/format/website-html/returnTag.html
index d4190fa1..7724eaa7 100644
--- a/core/testdata/format/website-html/returnTag.html
+++ b/core/testdata/format/website-html/returnTag.html
@@ -1,9 +1,9 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/index-of">indexOf</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./index-of.html">indexOf</a></div>
<h1>indexOf</h1>
<a name="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)"></a>
-<div class="signature"><code><span class="keyword">fun </span><a href="test/-foo/index"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
-<p>Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.</p>
+<div class="signature"><code><span class="keyword">fun </span><a href="-foo/index.html"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.</p>
<h3>Parameters</h3>
<p><a name="ignoreCase"></a>
<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.</p>
-<p><strong>Returns</strong> An index of the first occurrence of <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.</p>
+<p><strong>Returns</strong> An index of the first occurrence of <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.</p>
diff --git a/core/testdata/format/website-html/sample.html b/core/testdata/format/website-html/sample.html
index d709edc4..1fb26e41 100644
--- a/core/testdata/format/website-html/sample.html
+++ b/core/testdata/format/website-html/sample.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
<h1>foo</h1>
<div class="overload-group"><a name="$foo()"></a>
<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">Int</span></code></div>
diff --git a/core/testdata/format/website-html/sampleWithAsserts.html b/core/testdata/format/website-html/sampleWithAsserts.html
index cbb67dbf..2b2a9ac5 100644
--- a/core/testdata/format/website-html/sampleWithAsserts.html
+++ b/core/testdata/format/website-html/sampleWithAsserts.html
@@ -1,4 +1,4 @@
-<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/a">a</a></div>
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./a.html">a</a></div>
<h1>a</h1>
<a name="$a()"></a>
<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div>