aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle10
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt255
-rw-r--r--core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt4
-rw-r--r--core/src/main/kotlin/Analysis/DokkaAnalyzerFacades.kt163
-rw-r--r--core/src/main/kotlin/Analysis/JavaResolveExtension.kt131
-rw-r--r--core/src/main/kotlin/DokkaBootstrapImpl.kt68
-rw-r--r--core/src/main/kotlin/Formats/AnalysisComponents.kt14
-rw-r--r--core/src/main/kotlin/Formats/FormatDescriptor.kt1
-rw-r--r--core/src/main/kotlin/Formats/HtmlFormatService.kt6
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt117
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt3
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt224
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt130
-rw-r--r--core/src/main/kotlin/Formats/MarkdownFormatService.kt24
-rw-r--r--core/src/main/kotlin/Formats/OutlineService.kt2
-rw-r--r--core/src/main/kotlin/Formats/PackageListService.kt11
-rw-r--r--core/src/main/kotlin/Formats/StandardFormats.kt11
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt754
-rw-r--r--core/src/main/kotlin/Formats/YamlOutlineService.kt2
-rw-r--r--core/src/main/kotlin/Generation/DocumentationMerger.kt217
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt109
-rw-r--r--core/src/main/kotlin/Generation/FileGenerator.kt86
-rw-r--r--core/src/main/kotlin/Generation/configurationImpl.kt62
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt100
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt344
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt12
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt36
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt521
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt154
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt22
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt7
-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.kt217
-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/Location.kt19
-rw-r--r--core/src/main/kotlin/Model/Content.kt44
-rw-r--r--core/src/main/kotlin/Model/DescriptorSignatureProvider.kt7
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt117
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt69
-rw-r--r--core/src/main/kotlin/Model/ElementSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Model/PackageDocs.kt72
-rw-r--r--core/src/main/kotlin/Model/SourceLinks.kt38
-rw-r--r--core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt2
-rw-r--r--core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt86
-rw-r--r--core/src/main/kotlin/Utilities/DokkaModules.kt31
-rw-r--r--core/src/main/kotlin/Utilities/Html.kt4
-rw-r--r--core/src/main/kotlin/Utilities/ServiceLocator.kt13
-rw-r--r--core/src/main/kotlin/Utilities/Uri.kt40
-rw-r--r--core/src/main/kotlin/javadoc/docbase.kt159
-rw-r--r--core/src/main/kotlin/javadoc/dokka-adapters.kt9
-rw-r--r--core/src/main/kotlin/javadoc/tags.kt2
-rw-r--r--core/src/main/resources/dokka/format/kotlin-website-samples.properties2
-rw-r--r--core/src/main/resources/dokka/format/kotlin-website.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/test/kotlin/DokkaConfigurationTestImplementations.kt81
-rw-r--r--core/src/test/kotlin/NodeSelect.kt90
-rw-r--r--core/src/test/kotlin/TestAPI.kt314
-rw-r--r--core/src/test/kotlin/format/GFMFormatTest.kt18
-rw-r--r--core/src/test/kotlin/format/HtmlFormatTest.kt162
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt71
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt73
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt35
-rw-r--r--core/src/test/kotlin/format/MarkdownFormatTest.kt443
-rw-r--r--core/src/test/kotlin/format/PackageDocsTest.kt33
-rw-r--r--core/src/test/kotlin/issues/IssuesTest.kt15
-rw-r--r--core/src/test/kotlin/javadoc/JavadocTest.kt195
-rw-r--r--core/src/test/kotlin/model/ClassTest.kt125
-rw-r--r--core/src/test/kotlin/model/CommentTest.kt36
-rw-r--r--core/src/test/kotlin/model/FunctionTest.kt95
-rw-r--r--core/src/test/kotlin/model/JavaTest.kt42
-rw-r--r--core/src/test/kotlin/model/KotlinAsJavaTest.kt37
-rw-r--r--core/src/test/kotlin/model/LinkTest.kt22
-rw-r--r--core/src/test/kotlin/model/PackageTest.kt59
-rw-r--r--core/src/test/kotlin/model/PropertyTest.kt68
-rw-r--r--core/src/test/kotlin/model/SourceLinksErrorTest.kt35
-rw-r--r--core/src/test/kotlin/model/SourceLinksTest.kt75
-rw-r--r--core/src/test/kotlin/model/TypeAliasTest.kt20
-rw-r--r--core/testdata/format/JavaSupertype.html12
-rw-r--r--core/testdata/format/accessor.md2
-rw-r--r--core/testdata/format/annotatedTypeParameter.md2
-rw-r--r--core/testdata/format/annotationClass.md2
-rw-r--r--core/testdata/format/annotationClass.package.md2
-rw-r--r--core/testdata/format/annotationParams.md2
-rw-r--r--core/testdata/format/annotations.md2
-rw-r--r--core/testdata/format/arrayAverage.md2
-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.html11
-rw-r--r--core/testdata/format/classWithCompanionObject.md2
-rw-r--r--core/testdata/format/codeBlock.html14
-rw-r--r--core/testdata/format/codeBlock.md8
-rw-r--r--core/testdata/format/codeBlockNoHtmlEscape.md2
-rw-r--r--core/testdata/format/codeSpan.html2
-rw-r--r--core/testdata/format/companionImplements.md2
-rw-r--r--core/testdata/format/companionObjectExtension.md2
-rw-r--r--core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html6
-rw-r--r--core/testdata/format/deprecated.class.html15
-rw-r--r--core/testdata/format/deprecated.package.html11
-rw-r--r--core/testdata/format/dynamicExtension.md2
-rw-r--r--core/testdata/format/dynamicType.md2
-rw-r--r--core/testdata/format/emptyDescription.md2
-rw-r--r--core/testdata/format/entity.html5
-rw-r--r--core/testdata/format/enumClass.md2
-rw-r--r--core/testdata/format/enumClass.value.md2
-rw-r--r--core/testdata/format/enumRef.md4
-rw-r--r--core/testdata/format/exceptionClass.md2
-rw-r--r--core/testdata/format/exceptionClass.package.md2
-rw-r--r--core/testdata/format/exclInCodeBlock.md2
-rw-r--r--core/testdata/format/extensionFunctionParameter.md2
-rw-r--r--core/testdata/format/extensionScope.md2
-rw-r--r--core/testdata/format/extensionWithDocumentedReceiver.md2
-rw-r--r--core/testdata/format/externalReferenceLink.kt2
-rw-r--r--core/testdata/format/externalReferenceLink.md6
-rw-r--r--core/testdata/format/functionWithDefaultParameter.md2
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.html29
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.md20
-rw-r--r--core/testdata/format/genericInheritedExtensions.md2
-rw-r--r--core/testdata/format/gfm/listInTableCell.md2
-rw-r--r--core/testdata/format/gfm/sample.md2
-rw-r--r--core/testdata/format/htmlEscaping.html2
-rw-r--r--core/testdata/format/inapplicableExtensionFunctions.md2
-rw-r--r--core/testdata/format/indentedCodeBlock.html2
-rw-r--r--core/testdata/format/indentedCodeBlock.md2
-rw-r--r--core/testdata/format/inheritedCompanionObjectProperties.md2
-rw-r--r--core/testdata/format/inheritedExtensions.md2
-rw-r--r--core/testdata/format/inheritedLink.md2
-rw-r--r--core/testdata/format/inheritedMembers.md2
-rw-r--r--core/testdata/format/inlineSuspendFunction.kt6
-rw-r--r--core/testdata/format/inlineSuspendFunction.md8
-rw-r--r--core/testdata/format/javaCodeInParam.java10
-rw-r--r--core/testdata/format/javaCodeInParam.md13
-rw-r--r--core/testdata/format/javaCodeLiteralTags.md4
-rw-r--r--core/testdata/format/javaDeprecated.html2
-rw-r--r--core/testdata/format/javaLinkTag.html8
-rw-r--r--core/testdata/format/javaLinkTagWithLabel.html8
-rw-r--r--core/testdata/format/javaSeeTag.html8
-rw-r--r--core/testdata/format/javaSpaceInAuthor.md2
-rw-r--r--core/testdata/format/javadocHtml.java12
-rw-r--r--core/testdata/format/javadocHtml.md24
-rw-r--r--core/testdata/format/javadocOrderedList.md2
-rw-r--r--core/testdata/format/jdkLinks.md10
-rw-r--r--core/testdata/format/linkWithLabel.html8
-rw-r--r--core/testdata/format/linkWithStarProjection.html5
-rw-r--r--core/testdata/format/linksInEmphasis.md2
-rw-r--r--core/testdata/format/linksInHeaders.md2
-rw-r--r--core/testdata/format/linksInStrong.md2
-rw-r--r--core/testdata/format/markdownInLinks.html4
-rw-r--r--core/testdata/format/markdownInLinks.kt2
-rw-r--r--core/testdata/format/memberExtension.md2
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md7
-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.md18
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/foo.md10
-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.md4
-rw-r--r--core/testdata/format/multipleTypeParameterConstraints.md12
-rw-r--r--core/testdata/format/nestedLists.md2
-rw-r--r--core/testdata/format/newlineInTableCell.package.md2
-rw-r--r--core/testdata/format/notPublishedTypeAliasAutoExpansion.md2
-rw-r--r--core/testdata/format/nullability.md2
-rw-r--r--core/testdata/format/nullableTypeParameterFunction.kt8
-rw-r--r--core/testdata/format/nullableTypeParameterFunction.md18
-rw-r--r--core/testdata/format/operatorOverloading.md2
-rw-r--r--core/testdata/format/orderedList.html5
-rw-r--r--core/testdata/format/overloads.html5
-rw-r--r--core/testdata/format/overloadsWithDescription.html2
-rw-r--r--core/testdata/format/overloadsWithDifferentDescriptions.html2
-rw-r--r--core/testdata/format/overridingFunction.md2
-rw-r--r--core/testdata/format/paramTag.md2
-rw-r--r--core/testdata/format/parameterAnchor.html2
-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.md2
-rw-r--r--core/testdata/format/receiverReference.md2
-rw-r--r--core/testdata/format/reifiedTypeParameter.kt2
-rw-r--r--core/testdata/format/reifiedTypeParameter.md4
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md4
-rw-r--r--core/testdata/format/returnWithLink.html2
-rw-r--r--core/testdata/format/see.html12
-rw-r--r--core/testdata/format/shadowedExtensionFunctions.md2
-rw-r--r--core/testdata/format/sinceKotlin.html6
-rw-r--r--core/testdata/format/sinceKotlin.md6
-rw-r--r--core/testdata/format/sinceKotlin.package.md6
-rw-r--r--core/testdata/format/sinceKotlinWide.package.md8
-rw-r--r--core/testdata/format/starProjection.md2
-rw-r--r--core/testdata/format/suspendInlineFunction.kt6
-rw-r--r--core/testdata/format/suspendInlineFunction.md8
-rw-r--r--core/testdata/format/suspendParam.md2
-rw-r--r--core/testdata/format/suspendParam.package.md2
-rw-r--r--core/testdata/format/throwsTag.md2
-rw-r--r--core/testdata/format/tokensInEmphasis.md2
-rw-r--r--core/testdata/format/tokensInHeaders.md2
-rw-r--r--core/testdata/format/tokensInStrong.md2
-rw-r--r--core/testdata/format/tripleBackticks.html2
-rw-r--r--core/testdata/format/typeAliases.md56
-rw-r--r--core/testdata/format/typeAliases.package.md5
-rw-r--r--core/testdata/format/typeLink.html5
-rw-r--r--core/testdata/format/typeParameterBounds.md2
-rw-r--r--core/testdata/format/typeParameterReference.md2
-rw-r--r--core/testdata/format/typeParameterVariance.md2
-rw-r--r--core/testdata/format/typeProjectionVariance.md2
-rw-r--r--core/testdata/format/uninterpretedEmphasisCharacters.html2
-rw-r--r--core/testdata/format/unorderedLists.md2
-rw-r--r--core/testdata/format/varargsFunction.md2
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.html2
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.kt2
-rw-r--r--core/testdata/functions/inlineFunction.kt2
-rw-r--r--core/testdata/functions/inlineSuspendFunction.kt2
-rw-r--r--core/testdata/functions/suspendFunction.kt2
-rw-r--r--core/testdata/functions/suspendInlineFunction.kt2
-rw-r--r--core/testdata/java/constants.java5
-rw-r--r--core/testdata/javadoc/argumentReference.kt4
-rw-r--r--core/testdata/javadoc/constructorParameters.kt14
-rw-r--r--core/testdata/javadoc/defaultNoArgConstructor.kt12
-rw-r--r--core/testdata/javadoc/deprecated.java28
-rw-r--r--core/testdata/javadoc/functionParameters.java17
-rw-r--r--core/testdata/javadoc/noArgConstructor.kt12
-rw-r--r--core/testdata/javadoc/vararg.kt3
-rw-r--r--core/testdata/javadoc/visibilityModifiers.kt15
-rw-r--r--core/testdata/markdown/spec.txt52
-rw-r--r--core/testdata/packagedocs/referenceLinks.kotlin.md5
-rw-r--r--core/testdata/packagedocs/referenceLinks.md2
-rw-r--r--core/testdata/packagedocs/referenceLinks.module.md4
-rw-r--r--core/testdata/properties/annotatedProperty.kt2
-rw-r--r--core/testdata/sourceLinks/dummy.kt6
238 files changed, 5487 insertions, 2429 deletions
diff --git a/core/build.gradle b/core/build.gradle
index a4538471..697fd726 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -12,8 +12,8 @@ sourceCompatibility = 1.8
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
- languageVersion = "1.2"
- apiVersion = languageVersion
+ languageVersion = language_version
+ apiVersion = language_version
jvmTarget = "1.8"
}
}
@@ -32,6 +32,8 @@ dependencies {
compile "org.jetbrains:markdown:$markdownVersion"
compile intellijCoreAnalysis()
+
+ compile kotlinPluginDependency()
compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8'
@@ -45,6 +47,8 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_version
testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
-
+ implementation "com.google.code.gson:gson:$gson_version"
testCompile ideaRT()
+ testImplementation "org.jetbrains.kotlin:kotlin-stdlib-js:$bundled_kotlin_compiler_version"
+ testImplementation "org.jetbrains.kotlin:kotlin-stdlib-common:$bundled_kotlin_compiler_version"
} \ No newline at end of file
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
index b2e4b490..603fd0e4 100644
--- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -17,9 +17,20 @@ import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.io.URLUtil
+import org.jetbrains.dokka.Analysis.DokkaJsAnalyzerFacade
+import org.jetbrains.dokka.Analysis.DokkaNativeAnalyzerFacade
import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters
+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.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
+import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
@@ -29,21 +40,22 @@ 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.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.*
-import org.jetbrains.kotlin.resolve.BindingContext
-import org.jetbrains.kotlin.resolve.BindingTrace
-import org.jetbrains.kotlin.resolve.CompilerEnvironment
+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.konan.platform.KonanPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.types.KotlinType
@@ -59,7 +71,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 {
@@ -68,11 +80,18 @@ 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.native -> EnvironmentConfigFiles.NATIVE_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,
- environment.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
+ environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
+
val moduleManager = object : CoreModuleManager(environment.project, this) {
override fun getModules(): Array<out Module> = arrayOf(projectFileIndex.module)
@@ -92,19 +111,34 @@ 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, Platform.native -> 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.native -> KonanPlatform
+ 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)
}
@@ -114,64 +148,157 @@ 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.common -> createCommonResolverForProject(projectContext, module, library, modulesContent, environment)
+ Platform.js -> createJsResolverForProject(projectContext, module, library, modulesContent)
+ Platform.native -> createNativeResolverForProject(projectContext, module, library, modulesContent)
- 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))
+
+ return created to libraryResolutionFacade
+ }
+
+ 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 = { _ ->
+ CommonAnalysisParameters { content ->
+ environment.createPackagePartProvider(content.moduleContentScope)
+ }
+ },
+ targetEnvironment = CompilerEnvironment,
+ builtIns = DefaultBuiltIns.Instance
+ )
+ }
- rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
+ 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 = { DokkaJsAnalyzerFacade },
+ platformParameters = { _ -> PlatformAnalysisParameters.Empty },
+ targetEnvironment = CompilerEnvironment,
+ builtIns = JsPlatform.builtIns
+ )
+ }
+
+ private fun createNativeResolverForProject(
+ 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 = { KonanPlatform.multiTargetPlatform },
+ moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
+ resolverForModuleFactoryByPlatform = { DokkaNativeAnalyzerFacade },
+ platformParameters = { _ -> PlatformAnalysisParameters.Empty },
+ targetEnvironment = CompilerEnvironment
+ )
+
+ }
+
+ 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)
}
- val resolverForProject = ResolverForProjectImpl(
- "Dokka",
- projectContext,
- listOf(library, module),
- {
+ rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
+ }
+
+ return ResolverForProjectImpl(
+ debugName = "Dokka",
+ projectContext = projectContext,
+ modules = listOf(library, module),
+ modulesContent = {
when (it) {
library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
module -> ModuleContent(it, emptyList(), sourcesScope)
else -> throw IllegalArgumentException("Unexpected module info")
}
},
- {
- JvmPlatform.multiTargetPlatform
- },
- LanguageSettingsProvider.Default /* TODO: Fix this */,
- { JvmAnalyzerFacade },
-
- JvmPlatformParameters {
- val file = (it as JavaClassImpl).psi.containingFile.virtualFile
- if (file in sourcesScope)
- module
- else
- library
- },
- CompilerEnvironment,
- packagePartProviderFactory = { content ->
- JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
- addRoots(javaRoots)
- }
+ modulePlatforms = { JvmPlatform.multiTargetPlatform },
+ moduleLanguageSettingsProvider = LanguageSettingsProvider.Default /* TODO: Fix this */,
+ resolverForModuleFactoryByPlatform = { JvmAnalyzerFacade },
+ platformParameters = {
+ JvmPlatformParameters ({ content ->
+ JvmPackagePartProvider(
+ configuration.languageVersionSettings,
+ content.moduleContentScope)
+ .apply {
+ addRoots(javaRoots, messageCollector)
+ }
+ }, {
+ val file = (it as JavaClassImpl).psi.containingFile.virtualFile
+ if (file in sourcesScope)
+ module
+ else
+ library
+ })
},
+ targetEnvironment = CompilerEnvironment,
builtIns = builtIns
)
-
- resolverForProject.resolverForModule(library) // Required before module to initialize library properly
- val resolverForModule = resolverForProject.resolverForModule(module)
- val moduleDescriptor = resolverForProject.descriptorForModule(module)
- builtIns.initialize(moduleDescriptor, true)
- val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule)
- val projectComponentManager = environment.project as MockComponentManager
- projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created))
-
- return created
}
fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) {
@@ -191,6 +318,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)
}
@@ -199,6 +330,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)
}
@@ -206,7 +341,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
* List of source roots for this environment.
*/
val sources: List<String>
- get() = configuration.get(JVMConfigurationKeys.CONTENT_ROOTS)
+ get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
?.filterIsInstance<KotlinSourceRoot>()
?.map { it.path } ?: emptyList()
@@ -225,7 +360,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
}
fun addRoots(list: List<ContentRoot>) {
- configuration.addAll(JVMConfigurationKeys.CONTENT_ROOTS, list)
+ configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list)
}
/**
@@ -238,7 +373,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
fun contentRootFromPath(path: String): ContentRoot {
val file = File(path)
- return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path)
+ return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false)
}
@@ -250,7 +385,7 @@ class DokkaResolutionFacade(override val project: Project,
}
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 {
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
index 1ad68643..f5fbf991 100644
--- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
+++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
@@ -20,10 +20,10 @@ import com.intellij.openapi.vfs.VirtualFileFilter
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.messages.MessageBus
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
+import org.jetbrains.kotlin.cli.common.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
-import org.jetbrains.kotlin.config.ContentRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.picocontainer.PicoContainer
import java.io.File
diff --git a/core/src/main/kotlin/Analysis/DokkaAnalyzerFacades.kt b/core/src/main/kotlin/Analysis/DokkaAnalyzerFacades.kt
new file mode 100644
index 00000000..082d3968
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/DokkaAnalyzerFacades.kt
@@ -0,0 +1,163 @@
+package org.jetbrains.dokka.Analysis
+
+import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.caches.project.LibraryModuleInfo
+import org.jetbrains.kotlin.config.LanguageVersionSettings
+import org.jetbrains.kotlin.config.TargetPlatformVersion
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.get
+import org.jetbrains.kotlin.container.useImpl
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.context.ModuleContext
+import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
+import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
+import org.jetbrains.kotlin.frontend.di.configureModule
+import org.jetbrains.kotlin.ide.konan.KOTLIN_NATIVE_CURRENT_ABI_VERSION
+import org.jetbrains.kotlin.ide.konan.createPackageFragmentProvider
+import org.jetbrains.kotlin.incremental.components.LookupTracker
+import org.jetbrains.kotlin.js.resolve.JsPlatform
+import org.jetbrains.kotlin.konan.file.File
+import org.jetbrains.kotlin.konan.library.createKonanLibrary
+import org.jetbrains.kotlin.resolve.*
+import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
+import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
+import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
+import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil
+import org.jetbrains.kotlin.serialization.js.createKotlinJavascriptPackageFragmentProvider
+import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
+
+fun createContainerForLazyResolve(
+ moduleContext: ModuleContext,
+ declarationProviderFactory: DeclarationProviderFactory,
+ bindingTrace: BindingTrace,
+ platform: TargetPlatform,
+ targetPlatformVersion: TargetPlatformVersion,
+ targetEnvironment: TargetEnvironment,
+ languageVersionSettings: LanguageVersionSettings
+): StorageComponentContainer = createContainer("LazyResolve", platform) {
+ configureModule(moduleContext, platform, targetPlatformVersion, bindingTrace)
+
+ useInstance(declarationProviderFactory)
+ useInstance(languageVersionSettings)
+
+ useImpl<AnnotationResolverImpl>()
+ useImpl<CompilerDeserializationConfiguration>()
+ targetEnvironment.configure(this)
+
+ useImpl<ResolveSession>()
+ useImpl<LazyTopDownAnalyzer>()
+}
+
+
+object DokkaJsAnalyzerFacade : ResolverForModuleFactory() {
+ override fun <M : ModuleInfo> createResolverForModule(
+ moduleDescriptor: ModuleDescriptorImpl,
+ moduleContext: ModuleContext,
+ moduleContent: ModuleContent<M>,
+ platformParameters: PlatformAnalysisParameters,
+ targetEnvironment: TargetEnvironment,
+ resolverForProject: ResolverForProject<M>,
+ languageVersionSettings: LanguageVersionSettings,
+ targetPlatformVersion: TargetPlatformVersion
+ ): ResolverForModule {
+ val (moduleInfo, syntheticFiles, moduleContentScope) = moduleContent
+ val project = moduleContext.project
+ val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
+ project,
+ moduleContext.storageManager,
+ syntheticFiles,
+ moduleContentScope,
+ moduleInfo
+ )
+
+ val container = createContainerForLazyResolve(
+ moduleContext,
+ declarationProviderFactory,
+ BindingTraceContext(),
+ JsPlatform,
+ TargetPlatformVersion.NoVersion,
+ targetEnvironment,
+ languageVersionSettings
+ )
+ var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
+
+ if (moduleInfo is LibraryModuleInfo && moduleInfo.platform == JsPlatform) {
+ val providers = moduleInfo.getLibraryRoots()
+ .flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
+ .filter { it.version.isCompatible() }
+ .map { metadata ->
+ val (header, packageFragmentProtos) =
+ KotlinJavascriptSerializationUtil.readModuleAsProto(metadata.body, metadata.version)
+ createKotlinJavascriptPackageFragmentProvider(
+ moduleContext.storageManager, moduleDescriptor, header, packageFragmentProtos, metadata.version,
+ container.get(), LookupTracker.DO_NOTHING
+ )
+ }
+
+ if (providers.isNotEmpty()) {
+ packageFragmentProvider = CompositePackageFragmentProvider(listOf(packageFragmentProvider) + providers)
+ }
+ }
+
+ return ResolverForModule(packageFragmentProvider, container)
+ }
+
+ override val targetPlatform: TargetPlatform
+ get() = JsPlatform
+}
+
+object DokkaNativeAnalyzerFacade : ResolverForModuleFactory() {
+ override val targetPlatform: TargetPlatform
+ get() = KonanPlatform
+
+ override fun <M : ModuleInfo> createResolverForModule(
+ moduleDescriptor: ModuleDescriptorImpl,
+ moduleContext: ModuleContext,
+ moduleContent: ModuleContent<M>,
+ platformParameters: PlatformAnalysisParameters,
+ targetEnvironment: TargetEnvironment,
+ resolverForProject: ResolverForProject<M>,
+ languageVersionSettings: LanguageVersionSettings,
+ targetPlatformVersion: TargetPlatformVersion
+ ): ResolverForModule {
+
+ val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
+ moduleContext.project,
+ moduleContext.storageManager,
+ moduleContent.syntheticFiles,
+ moduleContent.moduleContentScope,
+ moduleContent.moduleInfo
+ )
+
+ val container = createContainerForLazyResolve(
+ moduleContext,
+ declarationProviderFactory,
+ BindingTraceContext(),
+ targetPlatform,
+ TargetPlatformVersion.NoVersion,
+ targetEnvironment,
+ languageVersionSettings
+ )
+
+ val packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
+ val fragmentProviders = mutableListOf(packageFragmentProvider)
+
+ val moduleInfo = moduleContent.moduleInfo
+
+ if (moduleInfo is LibraryModuleInfo) {
+ moduleInfo.getLibraryRoots()
+ .map { createKonanLibrary(File(it), KOTLIN_NATIVE_CURRENT_ABI_VERSION) }
+ .mapTo(fragmentProviders) {
+ it.createPackageFragmentProvider(
+ moduleContext.storageManager,
+ languageVersionSettings,
+ moduleDescriptor
+ )
+ }
+
+ }
+
+ return ResolverForModule(CompositePackageFragmentProvider(fragmentProviders), container)
+ }
+}
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 aeaca8be..b48b62d4 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -1,8 +1,8 @@
package org.jetbrains.dokka
+import com.google.gson.Gson
import org.jetbrains.dokka.DokkaConfiguration.PackageOptions
-import ru.yole.jkid.deserialization.deserialize
-import java.io.File
+
import java.util.function.BiConsumer
@@ -39,40 +39,38 @@ class DokkaBootstrapImpl : DokkaBootstrap {
}
lateinit var generator: DokkaGenerator
+ val gson = Gson()
- override fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String)
- = 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) }.toSet()
- )
- )
+ fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) = with(configuration) {
+
+ fun defaultLinks(config: PassConfigurationImpl): List<ExternalDocumentationLinkImpl> {
+ val links = mutableListOf<ExternalDocumentationLinkImpl>()
+ if (!config.noJdkLink)
+ links += DokkaConfiguration.ExternalDocumentationLink
+ .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/")
+ .build() as ExternalDocumentationLinkImpl
+
+ if (!config.noStdlibLink)
+ links += DokkaConfiguration.ExternalDocumentationLink
+ .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/")
+ .build() as ExternalDocumentationLinkImpl
+ return links
+ }
+
+ val configurationWithLinks =
+ configuration.copy(passesConfigurations =
+ passesConfigurations
+ .map {
+ val links: List<ExternalDocumentationLinkImpl> = it.externalDocumentationLinks + defaultLinks(it)
+ it.copy(externalDocumentationLinks = links)
+ }
+ )
+
+ generator = DokkaGenerator(configurationWithLinks, logger)
}
+ override fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String)
+ = configure(DokkaProxyLogger(logger), gson.fromJson(serializedConfigurationJSON, DokkaConfigurationImpl::class.java))
+
override fun generate() = generator.generate()
-} \ No newline at end of file
+}
diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt
index c4d97dbb..d78d4a0c 100644
--- a/core/src/main/kotlin/Formats/AnalysisComponents.kt
+++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt
@@ -2,9 +2,9 @@ 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.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
@@ -16,12 +16,12 @@ interface DefaultAnalysisComponentServices {
val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder>
val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder>
val sampleProcessingService: KClass<out SampleProcessingService>
- val descriptorSignatureProvider: KClass<out DescriptorSignatureProvider>
+ val elementSignatureProvider: KClass<out ElementSignatureProvider>
}
interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices {
override fun configureAnalysis(binder: Binder): Unit = with(binder) {
- bind<DescriptorSignatureProvider>() toType descriptorSignatureProvider
+ bind<ElementSignatureProvider>() toType elementSignatureProvider
bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass
bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass
bind<SampleProcessingService>() toType sampleProcessingService
@@ -33,7 +33,7 @@ object KotlinAsJava : DefaultAnalysisComponentServices {
override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::class
+ override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class
}
@@ -41,5 +41,5 @@ object KotlinAsKotlin : DefaultAnalysisComponentServices {
override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::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 b497fb0f..4bac8aa0 100644
--- a/core/src/main/kotlin/Formats/FormatDescriptor.kt
+++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt
@@ -25,6 +25,7 @@ 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
diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt
index 0ad946be..d36ea0a2 100644
--- a/core/src/main/kotlin/Formats/HtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -88,9 +88,7 @@ open class HtmlOutputBuilder(to: StringBuilder,
to.append("&nbsp;")
}
- override fun ensureParagraph() {
-
- }
+ override fun ensureParagraph() {}
}
open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator,
@@ -144,7 +142,7 @@ fun formatPageTitle(node: DocumentationNode): String {
}
val qName = qualifiedNameForPageTitle(node)
- return qName + " - " + moduleName
+ return "$qName - $moduleName"
}
private fun qualifiedNameForPageTitle(node: DocumentationNode): String {
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
index f73cd23e..885cdf6c 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
@@ -6,7 +6,6 @@ import kotlinx.html.li
import kotlinx.html.stream.appendHTML
import kotlinx.html.ul
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider
import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
import org.jetbrains.dokka.Utilities.bind
import org.jetbrains.dokka.Utilities.toType
@@ -17,7 +16,7 @@ class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponen
override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
override fun configureOutput(binder: Binder): Unit = with(binder) {
bind<Generator>() toType generatorServiceClass
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
deleted file mode 100644
index a98002d4..00000000
--- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
+++ /dev/null
@@ -1,224 +0,0 @@
-package org.jetbrains.dokka
-
-import com.google.inject.Inject
-import com.google.inject.name.Named
-import org.jetbrains.dokka.Utilities.impliedPlatformsName
-import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
-
-
-open class KotlinWebsiteOutputBuilder(
- to: StringBuilder,
- location: Location,
- generator: NodeLocationAwareGenerator,
- languageService: LanguageService,
- extension: String,
- impliedPlatforms: List<String>
-) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
- private var needHardLineBreaks = false
- private var insideDiv = 0
-
- override fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) {
- super.appendFrontMatter(nodes, to)
- to.appendln("layout: api")
- }
-
- override fun appendBreadcrumbs(path: Iterable<FormatLink>) {
- if (path.count() > 1) {
- to.append("<div class='api-docs-breadcrumbs'>")
- super.appendBreadcrumbs(path)
- to.append("</div>")
- }
- }
-
- override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body)
-
- override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
-
- protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) {
- to.append("<div class=\"$cssClass\"$otherAttributes")
- if (markdown) to.append(" markdown=\"1\"")
- to.append(">")
- if (!markdown) insideDiv++
- block()
- if (!markdown) insideDiv--
- to.append("</div>\n")
- }
-
- override fun appendAsSignature(node: ContentNode, block: () -> Unit) {
- val contentLength = node.textLength
- if (contentLength == 0) return
- div(to, "signature") {
- needHardLineBreaks = contentLength >= 62
- try {
- block()
- } finally {
- needHardLineBreaks = false
- }
- }
- }
-
- override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
- div(to, "overload-group", calculateDataAttributes(platforms), true) {
- ensureParagraph()
- block()
- ensureParagraph()
- }
- }
-
- override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
-
- override fun appendHeader(level: Int, body: () -> Unit) {
- if (insideDiv > 0) {
- wrapInTag("p", body, newlineAfterClose = true)
- } else {
- super.appendHeader(level, body)
- }
- }
-
- override fun appendLine() {
- if (insideDiv > 0) {
- to.appendln("<br/>")
- } else {
- super.appendLine()
- }
- }
-
- override fun appendTable(vararg columns: String, body: () -> Unit) {
- to.appendln("<table class=\"api-docs-table\">")
- body()
- to.appendln("</table>")
- }
-
- override fun appendTableBody(body: () -> Unit) {
- to.appendln("<tbody>")
- body()
- to.appendln("</tbody>")
- }
-
- override fun appendTableRow(body: () -> Unit) {
- to.appendln("<tr>")
- body()
- to.appendln("</tr>")
- }
-
- override fun appendTableCell(body: () -> Unit) {
- to.appendln("<td markdown=\"1\">")
- body()
- to.appendln("\n</td>")
- }
-
- override fun appendBlockCode(language: String, body: () -> Unit) {
- if (language.isNotEmpty()) {
- super.appendBlockCode(language, body)
- } else {
- wrap("<pre markdown=\"1\">", "</pre>", body)
- }
- }
-
- override fun appendSymbol(text: String) {
- to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
- }
-
- override fun appendKeyword(text: String) {
- to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
- }
-
- override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
- val id = signature?.let { " id=\"$it\"" }.orEmpty()
- to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>")
- }
-
- override fun appendSoftLineBreak() {
- if (needHardLineBreaks)
- to.append("<br/>")
-
- }
-
- override fun appendIndentedSoftLineBreak() {
- if (needHardLineBreaks) {
- to.append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;")
- }
- }
-
- private fun identifierClassName(kind: IdentifierKind) = when (kind) {
- IdentifierKind.ParameterName -> "parameterName"
- IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
- else -> "identifier"
- }
-
- fun calculateDataAttributes(platforms: Set<String>): String {
- fun String.isKotlinVersion() = this.startsWith("Kotlin")
- fun String.isJREVersion() = this.startsWith("JRE")
- val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)
- val jreVersion = platforms.singleOrNull(String::isJREVersion)
- val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() }
-
- val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: ""
- val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: ""
- val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: ""
- return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
- }
-
- override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
- if (platforms.isNotEmpty())
- wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
- else
- appendTableRow(block)
- }
-
- override fun appendPlatforms(platforms: Set<String>) {
-
- }
-}
-
-class KotlinWebsiteFormatService @Inject constructor(
- 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, generator, 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) {
- appendBlockCode(language) {
- imports()
- wrap("\n\nfun main(args: Array<String>) {", "}") {
- wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
- }
- }
- }
- }
-}
-
-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, generator, languageService, extension, impliedPlatforms)
-}
-
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
index 6ced75b5..86bc9df9 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -3,7 +3,6 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.Utilities.impliedPlatformsName
-import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
@@ -20,7 +19,7 @@ open class KotlinWebsiteHtmlOutputBuilder(
generator: NodeLocationAwareGenerator,
languageService: LanguageService,
extension: String,
- impliedPlatforms: List<String>,
+ val impliedPlatforms: List<String>,
templateService: HtmlTemplateService
) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) {
private var needHardLineBreaks = false
@@ -60,7 +59,7 @@ open class KotlinWebsiteHtmlOutputBuilder(
}
}
- override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) {
div(to, "overload-group", calculateDataAttributes(platforms)) {
block()
}
@@ -69,27 +68,29 @@ open class KotlinWebsiteHtmlOutputBuilder(
override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
override fun appendTable(vararg columns: String, body: () -> Unit) {
- to.appendln("<table class=\"api-docs-table\">")
- body()
- to.appendln("</table>")
+ //to.appendln("<table class=\"api-docs-table\">")
+ div(to, "api-declarations-list") {
+ body()
+ }
+ //to.appendln("</table>")
}
override fun appendTableBody(body: () -> Unit) {
- to.appendln("<tbody>")
+ //to.appendln("<tbody>")
body()
- to.appendln("</tbody>")
+ //to.appendln("</tbody>")
}
override fun appendTableRow(body: () -> Unit) {
- to.appendln("<tr>")
+ //to.appendln("<tr>")
body()
- to.appendln("</tr>")
+ //to.appendln("</tr>")
}
override fun appendTableCell(body: () -> Unit) {
- to.appendln("<td>")
+// to.appendln("<td>")
body()
- to.appendln("\n</td>")
+// to.appendln("\n</td>")
}
override fun appendSymbol(text: String) {
@@ -122,34 +123,79 @@ open class KotlinWebsiteHtmlOutputBuilder(
else -> "identifier"
}
- fun calculateDataAttributes(platforms: Set<String>): String {
- fun String.isKotlinVersion() = this.startsWith("Kotlin")
- fun String.isJREVersion() = this.startsWith("JRE")
- val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)
- val jreVersion = platforms.singleOrNull(String::isJREVersion)
- val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() }
+ private data class PlatformsForElement(
+ val platformToVersion: Map<String, String>
+ )
+
+ private fun calculatePlatforms(platforms: PlatformsData): PlatformsForElement {
+ //val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)?.removePrefix("Kotlin ")
+ val jreVersion = platforms.keys.filter(String::isJREVersion).min()?.takeUnless { it.endsWith("6") }
+ val targetPlatforms = platforms.filterNot { it.key.isJREVersion() } +
+ listOfNotNull(jreVersion?.let { it to platforms[it]!! })
+
+ return PlatformsForElement(
+ targetPlatforms.mapValues { (_, nodes) -> effectiveSinceKotlinForNodes(nodes) }
+ )
+ }
- val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: ""
- val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: ""
- val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: ""
- return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
+ private fun calculateDataAttributes(platforms: PlatformsData): String {
+ val platformToVersion = calculatePlatforms(platforms).platformToVersion
+ val (platformNames, versions) = platformToVersion.toList().unzip()
+ return "data-platform=\"${platformNames.joinToString()}\" "+
+ "data-kotlin-version=\"${versions.joinToString()}\""
}
- override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
- if (platforms.isNotEmpty())
- wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
- else
- appendTableRow(block)
+ override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) {
+// if (platforms.isNotEmpty())
+// wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+// else
+// appendTableRow(block)
+ div(to, "declarations", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block()
+ }
}
- override fun appendPlatforms(platforms: Set<String>) {}
+ override fun appendPlatforms(platforms: PlatformsData) {
+ val platformToVersion = calculatePlatforms(platforms).platformToVersion
+ div(to, "tags") {
+ div(to, "spacer") {}
+ platformToVersion.entries.sortedBy {
+ platformSortWeight(it.key)
+ }.forEach { (platform, version) ->
+ div(to, "tags__tag platform tag-value-$platform",
+ otherAttributes = " data-tag-version=\"$version\"") {
+ to.append(platform)
+ }
+ }
+ div(to, "tags__tag kotlin-version") {
+ to.append(mergeVersions(platformToVersion.values.toList()))
+ }
+ }
+ }
+
+ override fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) {
+ div(to, "node-page-main", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block()
+ }
+
+ }
override fun appendBreadcrumbSeparator() {
to.append(" / ")
}
+ override fun appendPlatformsAsText(platforms: PlatformsData) {
+ appendHeader(5) {
+ val filtered = platforms.keys.filterNot { it.isJREVersion() }.sortedBy { platformSortWeight(it) }
+ if (filtered.isNotEmpty()) {
+ to.append("For ")
+ filtered.joinTo(to)
+ }
+ }
+ }
+
override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
- div(to, "sample") {
+ div(to, "sample", otherAttributes = " data-min-compiler-version=\"1.3\"") {
appendBlockCode(language) {
imports()
wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") {
@@ -169,6 +215,29 @@ open class KotlinWebsiteHtmlOutputBuilder(
appendContent(section)
}
}
+
+ override fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ if (platforms.isNotEmpty())
+ wrap("<div ${calculateDataAttributes(platforms)}>", "</div>") {
+ block(platforms)
+ }
+ else
+ block(platforms)
+ }
+
+ override fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ div(to, "summary-group", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block(platforms)
+ }
+ }
+
+ fun platformSortWeight(name: String) = when(name.toLowerCase()) {
+ "common" -> 0
+ "jvm" -> 1
+ "js" -> 3
+ "native" -> 4
+ else -> 2 // This is hack to support JRE/JUnit and so on
+ }
}
class KotlinWebsiteHtmlFormatService @Inject constructor(
@@ -184,3 +253,6 @@ class KotlinWebsiteHtmlFormatService @Inject constructor(
KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
}
+
+private fun String.isKotlinVersion() = this.startsWith("Kotlin")
+private fun String.isJREVersion() = this.startsWith("JRE", ignoreCase=true) \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
index 4265394f..71356619 100644
--- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -159,16 +159,20 @@ open class MarkdownOutputBuilder(to: StringBuilder,
}
override fun appendParagraph(body: () -> Unit) {
- if (inTableCell) {
- ensureNewline()
- body()
- } else if (listStack.isNotEmpty()) {
- body()
- ensureNewline()
- } else {
- ensureParagraph()
- body()
- ensureParagraph()
+ when {
+ inTableCell -> {
+ ensureNewline()
+ body()
+ }
+ listStack.isNotEmpty() -> {
+ body()
+ ensureNewline()
+ }
+ else -> {
+ ensureParagraph()
+ body()
+ ensureParagraph()
+ }
}
}
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 7b68098e..e675d927 100644
--- a/core/src/main/kotlin/Formats/PackageListService.kt
+++ b/core/src/main/kotlin/Formats/PackageListService.kt
@@ -32,10 +32,13 @@ class DefaultPackageListService @Inject constructor(
node.members.forEach { visit(it, relocated = true) }
}
NodeKind.GroupNode -> {
- //only children of top-level GN records interesting for us, since link to top-level ones should point to GN
- node.members.forEach { it.members.forEach { visit(it, relocated = true) } }
- //record signature of GN as signature of type alias and class merged to GN, so link to it should point to GN
- node.detailOrNull(NodeKind.Signature)?.let { visit(it, relocated = true) }
+ if (node.members.isNotEmpty()) {
+ // Only nodes only has single file is need to be relocated
+ // TypeAliases for example
+ node.origins
+ .filter { it.members.isEmpty() }
+ .forEach { visit(it, relocated = true) }
+ }
}
else -> {
if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) {
diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt
index dd67ac97..86f70a37 100644
--- a/core/src/main/kotlin/Formats/StandardFormats.kt
+++ b/core/src/main/kotlin/Formats/StandardFormats.kt
@@ -31,17 +31,6 @@ class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponen
class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava
-class KotlinWebsiteFormatDescriptor : KotlinFormatDescriptorBase() {
- override val formatServiceClass = KotlinWebsiteFormatService::class
- override val outlineServiceClass = YamlOutlineService::class
-}
-
-class KotlinWebsiteFormatRunnableSamplesDescriptor : KotlinFormatDescriptorBase() {
- override val formatServiceClass = KotlinWebsiteRunnableSamplesFormatService::class
- override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class
- override val outlineServiceClass = YamlOutlineService::class
-}
-
class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() {
override val formatServiceClass = KotlinWebsiteHtmlFormatService::class
override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index bd27448a..82359454 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -1,18 +1,59 @@
package org.jetbrains.dokka
import org.jetbrains.dokka.LanguageService.RenderMode
+import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
data class FormatLink(val text: String, val href: String)
+private data class Summarized(
+ val data: List<SummarizedBySummary>
+) {
+
+ constructor(data: Map<ContentNode, Map<ContentNode, List<DocumentationNode>>>) : this(
+ data.entries.map { (summary, signatureToMember) ->
+ SummarizedBySummary(
+ summary,
+ signatureToMember.map { (signature, nodes) ->
+ SummarizedNodes(signature, nodes)
+ }
+ )
+ }
+ )
+
+ data class SummarizedNodes(val content: ContentNode, val nodes: List<DocumentationNode>) {
+ val platforms = effectivePlatformsForMembers(nodes)
+ }
+ data class SummarizedBySummary(val content: ContentNode, val signatures: List<SummarizedNodes>) {
+ val platforms = effectivePlatformsForMembers(signatures.flatMap { it.nodes })
+ val platformsOnSignature = !samePlatforms(signatures.map { it.platforms })
+ }
+
+
+ fun computePlatformLevel(): PlatformPlacement {
+ if (data.any { it.platformsOnSignature }) {
+ return PlatformPlacement.Signature
+ }
+ if (samePlatforms(data.map { it.platforms })) {
+ return PlatformPlacement.Row
+ }
+ return PlatformPlacement.Summary
+ }
+ val platformPlacement: PlatformPlacement = computePlatformLevel()
+ val platforms = effectivePlatformsForMembers(data.flatMap { it.signatures.flatMap { it.nodes } })
+
+
+ enum class PlatformPlacement {
+ Row, Summary, Signature
+ }
+}
+
abstract class StructuredOutputBuilder(val to: StringBuilder,
val location: Location,
val generator: NodeLocationAwareGenerator,
val languageService: LanguageService,
val extension: String,
- val impliedPlatforms: List<String>) : FormattedOutputBuilder {
-
- protected fun DocumentationNode.location() = generator.location(this)
+ impliedPlatforms: List<String>) : FormattedOutputBuilder {
protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
to.append(prefix)
@@ -69,10 +110,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
abstract fun appendText(text: String)
open fun appendSinceKotlin(version: String) {
- appendParagraph {
- appendText("Available since Kotlin: ")
+ appendText("Since: ")
appendCode { appendText(version) }
- }
}
open fun appendSectionWithTag(section: ContentSection) {
@@ -83,6 +122,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ open fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ block(platforms)
+ }
+
+ open fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ appendAsPlatformDependentBlock(platforms, block)
+ }
+
open fun appendSymbol(text: String) {
appendText(text)
}
@@ -95,6 +142,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendText(text)
}
+ open fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) {
+ block()
+ }
+
fun appendEntity(text: String) {
to.append(text)
}
@@ -152,9 +203,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ is NodeRenderContent -> {
+ val node = content.node
+ appendContent(languageService.render(node, content.mode))
+ }
is ContentNodeLink -> {
val node = content.node
- val linkTo = if (node != null) locationHref(location, node) else "#"
+ val linkTo = if (node != null) locationHref(location, node, generator) else "#"
appendLinkIfNotThisPage(linkTo, content)
}
is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content)
@@ -169,8 +224,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
content as ContentBlockCode
fun ContentBlockCode.appendBlockCodeContent() {
children
- .dropWhile { it is ContentText && it.text.isBlank() }
- .forEach { appendContent(it) }
+ .dropWhile { it is ContentText && it.text.isBlank() }
+ .forEach { appendContent(it) }
}
when (content) {
is ContentBlockSampleCode ->
@@ -192,51 +247,42 @@ 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,
- extension: String,
- name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
- if (to.owner?.kind == NodeKind.GroupNode)
- return link(from, to.owner!!, extension, name)
-
- if (from.owner?.kind == NodeKind.GroupNode)
- return link(from.owner!!, to, extension, name)
-
- return FormatLink(name(to), from.location().relativePathTo(to.location()))
- }
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink = link(from, to, extension, name)
- 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(topLevelPage.location(), signature?.name ?: to.name)
- }
- return from.relativePathTo(to.location())
- }
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ extension: String,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink =
+ FormatLink(name(to), generator.relativePathToLocation(from, to))
private fun DocumentationNode.isModuleOrPackage(): Boolean =
- kind == NodeKind.Module || kind == NodeKind.Package
+ kind == NodeKind.Module || kind == NodeKind.Package
protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) {
block()
}
- protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) {
block()
}
- protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ protected open fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) {
appendTableRow(block)
}
- protected open fun appendPlatforms(platforms: Set<String>) {
+ protected open fun appendPlatformsAsText(platforms: PlatformsData) {
+ appendPlatforms(platforms)
+ }
+
+ protected open fun appendPlatforms(platforms: PlatformsData) {
if (platforms.isNotEmpty()) {
- appendLine()
- appendText(platforms.joinToString(prefix = "(", postfix = ")"))
+ appendText(platforms.keys.joinToString(prefix = "(", postfix = ") "))
}
}
@@ -250,7 +296,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> =
- sections.filter { it.subjectName != null }.groupBy { it.tag }
+ sections.filter { it.subjectName != null }.groupBy { it.tag }
private fun ContentNode.appendSignature() {
if (this is ContentBlock && this.isEmpty()) {
@@ -285,7 +331,6 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name
appendHeader(2) { appendText("Package $packageName") }
}
- singleNode.appendPlatforms()
appendContent(singleNode.content)
} else {
val breakdownByName = nodes.groupBy { node -> node.name }
@@ -298,45 +343,134 @@ 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.map { it.content }
+ else -> node.content
+ }
+ }
if (breakdownBySummary.size == 1) {
- formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode)
+ val node = breakdownBySummary.values.single()
+ appendAsNodeDescription(effectivePlatformsForMembers(node)) {
+ formatOverloadGroup(node, isSingleNode)
+ }
} else {
for ((_, items) in breakdownBySummary) {
-
- appendAsOverloadGroup(to, platformsOfItems(items)) {
+ appendAsOverloadGroup(to, effectivePlatformsForMembers(items)) {
formatOverloadGroup(items)
}
-
}
}
}
private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) {
+
+ val platformsPerGroup = samePlatforms(
+ items.flatMap {
+ if (it.kind == NodeKind.GroupNode) {
+ it.origins.groupBy { origin ->
+ languageService.render(origin)
+ }.values.map { origins -> effectivePlatformsForMembers(origins) }
+ } else {
+ listOf(effectivePlatformsForNode(it))
+ }
+ }
+ )
+
+ if (platformsPerGroup) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(items)) { platforms ->
+ appendPlatforms(platforms)
+ }
+ }
for ((index, item) in items.withIndex()) {
if (index > 0) appendLine()
+
+ if (item.kind == NodeKind.GroupNode) {
+ renderGroupNode(item, isSingleNode, !platformsPerGroup)
+ } else {
+ renderSimpleNode(item, isSingleNode, !platformsPerGroup)
+ }
+
+ }
+ // All items have exactly the same documentation, so we can use any item to render it
+ val item = items.first()
+ // TODO: remove this block cause there is no one node with OverloadGroupNote detail
+ item.details(NodeKind.OverloadGroupNote).forEach {
+ appendContent(it.content)
+ }
+
+ if (item.kind == NodeKind.GroupNode) {
+ val groupByContent = item.origins.groupBy { it.content }
+ if (groupByContent.count { !it.key.isEmpty() } > 1) {
+ if (groupByContent.size > 1) println("[mult] Found ov diff: ${generator.location(item).path}")
+ }
+ for ((content, origins) in groupByContent) {
+ if (content.isEmpty()) continue
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(origins)) { platforms ->
+ if (groupByContent.count { !it.key.isEmpty() } > 1) {
+ appendPlatformsAsText(platforms)
+ }
+ appendContent(content.summary)
+ content.appendDescription()
+ }
+ }
+ } else {
+ val platforms = effectivePlatformsForNode(item)
+ appendAsPlatformDependentBlock(platforms) {
+ appendContent(item.summary)
+ item.content.appendDescription()
+ }
+ }
+ }
+
+
+ fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(listOf(item))) { platforms ->
+ // TODO: use summarizesignatures
val rendered = languageService.render(item)
item.detailOrNull(NodeKind.Signature)?.let {
if (item.kind !in NodeKind.classLike || !isSingleNode)
appendAnchor(it.name)
}
+ if (withPlatforms) {
+ appendPlatforms(platforms)
+ }
appendAsSignature(rendered) {
appendCode { appendContent(rendered) }
item.appendSourceLink()
}
item.appendOverrides()
item.appendDeprecation()
- item.appendPlatforms()
}
- // All items have exactly the same documentation, so we can use any item to render it
- val item = items.first()
- item.details(NodeKind.OverloadGroupNote).forEach {
- appendContent(it.content)
+ }
+
+ fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) {
+ // TODO: use summarizesignatures
+ val groupBySignature = item.origins.groupBy {
+ languageService.render(it)
}
- appendContent(item.content.summary)
- item.appendDescription()
+ for ((sign, nodes) in groupBySignature) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(nodes)) { platforms ->
+ val first = nodes.first()
+ first.detailOrNull(NodeKind.Signature)?.let {
+ if (item.kind !in NodeKind.classLike || !isSingleNode)
+ appendAnchor(it.name)
+ }
+
+ if (withPlatforms) {
+ appendPlatforms(platforms)
+ }
+
+ appendAsSignature(sign) {
+ appendCode { appendContent(sign) }
+ }
+ first.appendOverrides()
+ first.appendDeprecation()
+ }
+
+ }
}
private fun DocumentationNode.appendSourceLink() {
@@ -351,7 +485,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
overrides.forEach {
appendParagraph {
to.append("Overrides ")
- val location = location().relativePathTo(it.location())
+ val location = generator.relativePathToLocation(this, it)
appendLink(FormatLink(it.owner!!.name + "." + it.name, location))
}
@@ -363,82 +497,95 @@ 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()
- }
- }
- }
-
- private fun DocumentationNode.appendPlatforms() {
- val platforms = if (isModuleOrPackage())
- platformsToShow.toSet() + platformsOfItems(members)
- else
- platformsToShow
-
- if (platforms.isEmpty()) return
-
- appendParagraph {
- appendStrong { to.append("Platform and version requirements:") }
- to.append(" " + platforms.joinToString())
- }
- }
-
- protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> {
- val platforms = items.asSequence().map {
- when (it.kind) {
- NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members)
- else -> it.platformsToShow.toSet()
- }
- }
-
- fun String.isKotlinVersion() = this.startsWith("Kotlin")
-
- // Calculating common platforms for items
- return platforms.reduce { result, platformsOfItem ->
- val otherKotlinVersion = result.find { it.isKotlinVersion() }
- val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
-
- // When no Kotlin version specified, it means that version is 1.0
- if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
- val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct()
-
- val minVersion = allKotlinVersions.min()!!
- val resultVersion = when {
- allKotlinVersions.size == 1 -> allKotlinVersions.single()
- minVersion.endsWith("+") -> minVersion
- else -> minVersion + "+"
+ 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()
}
-
- result.intersect(otherPlatforms) + resultVersion
- } else {
- result.intersect(platformsOfItem)
}
}
}
- val DocumentationNode.platformsToShow: List<String>
- get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it }
- private fun DocumentationNode.appendDescription() {
- if (content.description != ContentEmpty) {
- appendContent(content.description)
- }
- content.getSectionsWithSubjects().forEach {
+// protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> {
+// val platforms = items.asSequence().map {
+// when (it.kind) {
+// NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members)
+// NodeKind.GroupNode -> platformsOfItems(it.origins)
+// else -> it.platformsToShow.toSet()
+// }
+// }
+//
+// fun String.isKotlinVersion() = this.startsWith("Kotlin")
+//
+// if (platforms.count() == 0) return emptySet()
+//
+// // Calculating common platforms for items
+// return platforms.reduce { result, platformsOfItem ->
+// val otherKotlinVersion = result.find { it.isKotlinVersion() }
+// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
+//
+// // When no Kotlin version specified, it means that version is 1.0
+// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
+// result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions)
+// } else {
+// result.intersect(platformsOfItem)
+// }
+// }
+// }
+//
+// protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+// val platforms = items.asSequence().map {
+// when (it.kind) {
+// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+// else -> it.platformsToShow.toSet()
+// }
+// }
+//
+// fun String.isKotlinVersion() = this.startsWith("Kotlin")
+//
+// if (platforms.count() == 0) return emptySet()
+//
+// // Calculating common platforms for items
+// return platforms.reduce { result, platformsOfItem ->
+// val otherKotlinVersion = result.find { it.isKotlinVersion() }
+// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
+//
+// // When no Kotlin version specified, it means that version is 1.0
+// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
+// result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions)
+// } else {
+// result.union(otherPlatforms)
+// }
+// }
+// }
+
+// val DocumentationNode.platformsToShow: List<String>
+// get() = platforms
+
+ private fun Content.appendDescription() {
+ if (description != ContentEmpty) {
+ appendContent(description)
+ }
+
+
+ getSectionsWithSubjects().forEach {
appendSectionWithSubject(it.key, it.value)
}
- for (section in content.sections.filter { it.subjectName == null }) {
+ for (section in sections.filter { it.subjectName == null }) {
appendSectionWithTag(section)
}
}
@@ -457,6 +604,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
}
+
+ fun appendOriginsGroupByContent(node: DocumentationNode) {
+ require(node.kind == NodeKind.GroupNode)
+ val groupByContent =
+ node.origins.groupBy { it.content }
+ .mapValues { (_, origins) ->
+ effectivePlatformsForMembers(origins)
+ }
+ .filterNot { it.key.isEmpty() }
+ .toList()
+ .sortedByDescending { it.second.size }
+
+ if (groupByContent.size > 1) println("[mult] Found diff: ${generator.location(node).path}")
+ for ((content, platforms) in groupByContent) {
+ appendAsPlatformDependentBlock(platforms) {
+ if (groupByContent.size > 1) {
+ appendPlatformsAsText(platforms)
+ }
+ appendContent(content.summary)
+ content.appendDescription()
+ }
+ }
+ }
+ }
+
+ inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) :
+ PageBuilder(listOf(node), noHeader) {
+
+ override fun build() {
+ super.build()
+ SectionsBuilder(node).build()
+ }
}
inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) {
@@ -469,39 +648,37 @@ 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
- }
-
- for (member in node.members.sortedBy(DocumentationNode::priority)) {
-
- appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
- formatSubNodeOfGroup(member)
- }
+ appendAsNodeDescription(effectivePlatformsForNode(node)) {
+ renderGroupNode(node, true)
+ appendOriginsGroupByContent(node)
}
- }
- fun formatSubNodeOfGroup(member: DocumentationNode) {
- SingleNodePageBuilder(member, true).build()
+ SectionsBuilder(node).build()
}
}
-
- inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
- : PageBuilder(listOf(node), noHeader) {
-
+//
+// private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+// val platforms = items.flatMapTo(mutableSetOf<String>()) {
+// when (it.kind) {
+// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+// else -> it.platforms
+// }
+// }
+//
+// return platforms
+// }
+
+
+ inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) {
override fun build() {
- 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> {
@@ -509,40 +686,39 @@ 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(
- NodeKind.Class,
- NodeKind.Interface,
- NodeKind.Enum,
- NodeKind.Object,
- NodeKind.AnnotationClass,
- NodeKind.Exception,
- NodeKind.TypeAlias,
- NodeKind.Constructor,
- NodeKind.Property,
- NodeKind.Package,
- NodeKind.Function,
- NodeKind.CompanionObjectProperty,
- NodeKind.CompanionObjectFunction,
- NodeKind.ExternalClass,
- NodeKind.EnumItem,
- NodeKind.AllTypes,
- NodeKind.GroupNode
+ NodeKind.Class,
+ NodeKind.Interface,
+ NodeKind.Enum,
+ NodeKind.Object,
+ NodeKind.AnnotationClass,
+ NodeKind.Exception,
+ NodeKind.TypeAlias,
+ NodeKind.Constructor,
+ NodeKind.Property,
+ NodeKind.Package,
+ NodeKind.Function,
+ NodeKind.CompanionObjectProperty,
+ NodeKind.CompanionObjectFunction,
+ NodeKind.ExternalClass,
+ NodeKind.EnumItem,
+ NodeKind.AllTypes,
+ NodeKind.GroupNode
)
})
@@ -552,7 +728,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty })
appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction })
appendSection("Inheritors",
- node.inheritors.filter { it.kind != NodeKind.EnumItem })
+ node.inheritors.filter { it.kind != NodeKind.EnumItem })
if (node.kind == NodeKind.Module) {
appendHeader(3) { to.append("Index") }
@@ -570,34 +746,46 @@ 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) }
appendTable("Name", "Summary") {
appendTableBody {
- for ((memberLocation, members) in membersMap) {
- val elementPlatforms = platformsOfItems(members, omitSamePlatforms)
- val platforms = if (platformsBasedOnMembers)
- members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
- else
- elementPlatforms
+ for ((memberLocation, membersList) in membersMap) {
+ val platforms = effectivePlatformsForMembers(membersList)
+// val platforms = if (platformsBasedOnMembers)
+// members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+// else
+// elementPlatforms
+
+ val summarized = computeSummarySignatures(membersList)
+
appendIndexRow(platforms) {
appendTableCell {
- appendParagraph {
- appendLink(memberLocation)
- if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
- appendPlatforms(platforms)
- }
+ if (summarized.platformPlacement == Summarized.PlatformPlacement.Row) {
+ appendPlatforms(platforms)
+ }
+// appendHeader(level = 4) {
+// appendParagraph {
+ appendLink(memberLocation)
+
+ if (node.sinceKotlin != null) {
+ appendSinceKotlin(node.sinceKotlin.toString())
+ }
+
+ if (membersList.singleOrNull()?.sinceKotlin != null){
+ wrap(" (", ")"){ appendSinceKotlin(membersList.single().sinceKotlin.toString()) }
}
+// }
+// if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
+// appendPlatforms(platforms)
+// }
+// }
}
appendTableCell {
- val breakdownBySummary = members.groupBy { it.summary }
- for ((summary, items) in breakdownBySummary) {
- appendSummarySignatures(items)
- appendContent(summary)
- }
+ appendSummarySignatures(summarized)
}
}
}
@@ -605,35 +793,94 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
- val platforms = platformsOfItems(items)
- if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
- return platforms
+//
+// private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
+// if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) {
+// return unionPlatformsOfItems(items)
+// }
+//
+// val platforms = platformsOfItems(items)
+// if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
+// return platforms
+// }
+// return emptySet()
+// }
+
+
+
+ private fun computeSummarySignatures(items: List<DocumentationNode>): Summarized =
+ Summarized(items.groupBy { it.summary }.mapValues { (_, nodes) ->
+ val nodesToAppend = nodes.flatMap { if(it.kind == NodeKind.GroupNode) it.origins else listOf(it) }
+
+ val summarySignature = languageService.summarizeSignatures(nodesToAppend)
+ if (summarySignature != null) {
+ mapOf(summarySignature to nodesToAppend)
+ } else {
+ nodesToAppend.groupBy {
+ languageService.render(it, RenderMode.SUMMARY)
+ }
+ }
+ })
+
+
+ private fun appendSummarySignatures(
+ summarized: Summarized
+ ) {
+ for(summary in summarized.data) {
+
+ appendAsSummaryGroup(summary.platforms) {
+ if (summarized.platformPlacement == Summarized.PlatformPlacement.Summary) {
+ appendPlatforms(summary.platforms)
+ }
+ summary.signatures.subList(0, summary.signatures.size - 1).forEach {
+ appendSignatures(
+ it,
+ summarized.platformPlacement == Summarized.PlatformPlacement.Signature
+ )
+ appendLine()
+ }
+ appendSignatures(
+ summary.signatures.last(),
+ summarized.platformPlacement == Summarized.PlatformPlacement.Signature
+ )
+ appendContent(summary.content)
+ }
+
}
- return emptySet()
}
- private fun appendSummarySignatures(items: List<DocumentationNode>) {
- val summarySignature = languageService.summarizeSignatures(items)
- if (summarySignature != null) {
- appendAsSignature(summarySignature) {
- summarySignature.appendSignature()
+ private fun appendSignatures(
+ signature: Summarized.SummarizedNodes,
+ withPlatforms: Boolean
+ ) {
+
+// val platforms = if (platformsBasedOnMembers)
+// items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+// else
+// elementPlatforms
+
+
+ appendAsPlatformDependentBlock(signature.platforms) {
+ if (withPlatforms) {
+ appendPlatforms(signature.platforms)
}
- return
- }
- val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
- renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
- appendAsSignature(it) {
- it.appendSignature()
+ appendAsSignature(signature.content) {
+ signature.content.appendSignature()
}
- appendLine()
- }
- appendAsSignature(renderedSignatures.last()) {
- renderedSignatures.last().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)) {
@@ -644,21 +891,23 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendTable("Name", "Summary") {
appendTableBody {
for (type in node.members) {
- appendTableRow {
- appendTableCell {
+ val platforms = effectivePlatformsForNode(type)
+ appendIndexRow(platforms) {
+ appendPlatforms(platforms)
+ appendHeader(level = 5) {
appendLink(link(node, type) {
if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName()
})
- if (type.kind == NodeKind.ExternalClass) {
- val packageName = type.owner?.name
- if (packageName != null) {
- appendText(" (extensions in package $packageName)")
- }
- }
}
- appendTableCell {
- appendContent(type.summary)
+
+ if (type.kind == NodeKind.ExternalClass) {
+ val packageName = type.owner?.name
+ if (packageName != null) {
+ appendText(" (extensions in package $packageName)")
+ }
}
+
+ appendContent(type.summary)
}
}
}
@@ -683,3 +932,78 @@ abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator
override final val linkExtension: String = extension) : FormatService {
}
+
+typealias PlatformsData = Map<String, Set<DocumentationNode>>
+
+fun memberPlatforms(node: DocumentationNode): PlatformsData {
+ val members = when {
+ node.kind == NodeKind.GroupNode -> node.origins
+ node.kind in NodeKind.classLike -> emptyList()
+ node.kind in NodeKind.memberLike -> emptyList()
+ else -> node.members
+ }
+
+ return members.map(::effectivePlatformsForNode).fold(mapOf(), ::mergePlatforms)
+}
+
+fun mergePlatforms(a: PlatformsData, b: PlatformsData): PlatformsData {
+ val mutable = a.toMutableMap()
+ b.forEach { (name, declarations) ->
+ mutable.merge(name, declarations) { a, b -> a.union(b) }
+ }
+ return mutable
+}
+
+fun effectivePlatformsForNode(node: DocumentationNode): PlatformsData {
+ val platforms = node.platforms + memberPlatforms(node).keys
+ return platforms.keysToMap { setOf(node) }
+}
+
+fun effectivePlatformsForMembers(nodes: Collection<DocumentationNode>): PlatformsData {
+ return nodes.map { effectivePlatformsForNode(it) }.reduce(::mergePlatforms)
+}
+
+fun mergeVersions(kotlinVersions: List<String>): String {
+ return kotlinVersions.distinct().min()!!
+}
+
+fun effectiveSinceKotlinForNode(node: DocumentationNode, baseVersion: String = "1.0"): String {
+ val members = when {
+ node.kind == NodeKind.GroupNode -> node.origins
+ node.kind in NodeKind.classLike -> emptyList()
+ node.kind in NodeKind.memberLike -> emptyList()
+ else -> node.members
+ }
+ val newBase = node.sinceKotlin ?: baseVersion
+ val memberVersion = if (members.isNotEmpty()) effectiveSinceKotlinForNodes(members, newBase) else newBase
+
+ return node.sinceKotlin ?: memberVersion
+}
+
+fun effectiveSinceKotlinForNodes(nodes: Collection<DocumentationNode>, baseVersion: String = "1.0"): String {
+ val map = nodes.map { effectiveSinceKotlinForNode(it, baseVersion) }
+ return mergeVersions(map)
+}
+
+fun samePlatforms(platformsPerNode: Collection<PlatformsData>): Boolean {
+
+ val first = platformsPerNode.firstOrNull()?.keys ?: return true
+ return platformsPerNode.all { it.keys == first }
+}
+
+fun locationHref(
+ from: Location,
+ to: DocumentationNode,
+ generator: NodeLocationAwareGenerator,
+ pathOnly: Boolean = false
+): String {
+ val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to
+ if (topLevelPage != null) {
+ val signature = to.detailOrNull(NodeKind.Signature)
+ return from.relativePathTo(
+ generator.location(topLevelPage),
+ (signature?.name ?: to.name).takeUnless { pathOnly }
+ )
+ }
+ return from.relativePathTo(generator.location(to))
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt
index c36f98eb..3c92d8ff 100644
--- a/core/src/main/kotlin/Formats/YamlOutlineService.kt
+++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt
@@ -13,7 +13,7 @@ class YamlOutlineService @Inject constructor(
override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
val indent = " ".repeat(outlineLevel)
to.appendln("$indent- title: ${languageService.renderName(node)}")
- to.appendln("$indent url: ${generator.location(node).path}")
+ to.appendln("$indent url: ${generator.relativePathToLocation(node.path.first(), node)}")
}
override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
diff --git a/core/src/main/kotlin/Generation/DocumentationMerger.kt b/core/src/main/kotlin/Generation/DocumentationMerger.kt
new file mode 100644
index 00000000..f58d2f8e
--- /dev/null
+++ b/core/src/main/kotlin/Generation/DocumentationMerger.kt
@@ -0,0 +1,217 @@
+package org.jetbrains.dokka.Generation
+
+import org.jetbrains.dokka.*
+
+class DocumentationMerger(
+ private val documentationModules: List<DocumentationModule>,
+ val logger: DokkaLogger
+) {
+ 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: ${documentationModules.joinToString(", ") {it.name}}")
+ }
+
+ 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) {
+ try {
+ val producedPackage = mergePackagesWithEqualNames(name, from, listOfPackages)
+ updatePendingReferences()
+
+ resultReferences.add(
+ DocumentationReference(from, producedPackage, RefKind.Member)
+ )
+ } catch (t: Throwable) {
+ val entries = listOfPackages.joinToString(",") { "references:${it.allReferences().size}" }
+ throw Error("Failed to merge package $name from $from with entries $entries. ${t.message}", t)
+ }
+ }
+
+ 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 mergeMemberGroupBy(it: DocumentationNode): String {
+ val signature = signatureMap[it]
+
+ if (signature != null) {
+ return signature
+ }
+
+ logger.error("Failed to find signature for $it in \n${it.allReferences().joinToString { "\n ${it.kind} ${it.to}" }}")
+ return "<ERROR>"
+ }
+
+ private fun mergeMemberReferences(
+ from: DocumentationNode,
+ refs: List<DocumentationReference>
+ ): List<DocumentationReference> {
+ val membersBySignature: Map<String, List<DocumentationNode>> = refs.map { it.to }
+ .groupBy(this::mergeMemberGroupBy)
+
+ 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 {
+ require(nodes.isNotEmpty())
+
+ val singleNode = nodes.singleOrNull()
+ if (singleNode != null) {
+ singleNode.dropReferences { it.kind == RefKind.Owner }
+ return singleNode
+ }
+
+ // Specialization processing
+ // Given (Common, JVM, JRE6, JS) and (JVM, JRE6) and (JVM, JRE7)
+ // Sorted: (JVM, JRE6), (JVM, JRE7), (Common, JVM, JRE6, JS)
+ // Should output: (JVM, JRE6), (JVM, JRE7), (Common, JS)
+ // Should not remove first platform
+ val nodesSortedByPlatformCount = nodes.sortedBy { it.platforms.size }
+ val allPlatforms = mutableSetOf<String>()
+ nodesSortedByPlatformCount.forEach { node ->
+ node.platforms
+ .filterNot { allPlatforms.add(it) }
+ .filter { it != node.platforms.first() }
+ .forEach { platform ->
+ node.dropReferences { it.kind == RefKind.Platform && it.to.name == platform }
+ }
+ }
+
+ 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)
+ node.append(groupNode, RefKind.TopLevelPage)
+
+ 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 in NodeKind.classLike }) {
+ 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,
+ content = documentationModules.first().content,
+ 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 59b9dc5e..90d7cfcc 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -7,74 +7,86 @@ 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.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import kotlin.system.measureTimeMillis
-class 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, logger).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
+// val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath
+// ?.let { File(it).absolutePath }
+// val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } }
+ if (descriptor is MemberDescriptor && descriptor.isExpect) {
+ return defaultPlatformAsList.take(1)
+ }
+ 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 +94,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
@@ -155,9 +172,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)
@@ -166,9 +187,21 @@ fun buildDocumentationModule(injector: Injector,
}
}
+fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) {
+ val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.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)
+ val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
?.filterIsInstance<JavaSourceRoot>()
?.map { it.file }
?: listOf()
@@ -187,4 +220,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 aff07648..ee2c068e 100644
--- a/core/src/main/kotlin/Generation/FileGenerator.kt
+++ b/core/src/main/kotlin/Generation/FileGenerator.kt
@@ -3,17 +3,51 @@ 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
+import java.io.PrintWriter
+import java.io.StringWriter
class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator {
@set:Inject(optional = true) var outlineService: OutlineFormatService? = null
@set:Inject(optional = true) lateinit var formatService: FormatService
- @set:Inject(optional = true) lateinit var options: DocumentationOptions
+ @set:Inject(optional = true) lateinit var dokkaConfiguration: DokkaConfiguration
@set:Inject(optional = true) var packageListService: PackageListService? = null
+ private val createdFiles = mutableMapOf<File, List<String>>()
+
+ private fun File.writeFileAndAssert(context: String, action: (File) -> Unit) {
+ //TODO: there is a possible refactoring to drop FileLocation
+ //TODO: aad File from API, Location#path.
+ //TODO: turn [Location] into a final class,
+ //TODO: Use [Location] all over the place without full
+ //TODO: reference to the real target path,
+ //TODO: it opens the way to safely track all files created
+ //TODO: to make sure no files were overwritten by mistake
+ //TODO: also, the NodeLocationAwareGenerator should be removed
+
+ val writes = createdFiles.getOrDefault(this, listOf()) + context
+ createdFiles[this] = writes
+ if (writes.size > 1) {
+ println("ERROR. An attempt to write ${this.relativeTo(root)} several times!")
+ return
+ }
+
+ try {
+ parentFile?.mkdirsOrFail()
+ action(this)
+ } catch (e : Throwable) {
+ println("Failed to write $this. ${e.message}")
+ e.printStackTrace()
+ }
+ }
+
+ private fun File.mkdirsOrFail() {
+ if (!mkdirs() && !exists()) {
+ throw IOException("Failed to create directory $this")
+ }
+ }
+
override fun location(node: DocumentationNode): FileLocation {
return FileLocation(fileForNode(node, formatService.linkExtension))
}
@@ -22,45 +56,36 @@ class FileGenerator @Inject constructor(@Named("outputDir") override val root: F
return File(root, relativePathToNode(node)).appendExtension(extension)
}
- fun locationWithoutExtension(node: DocumentationNode): FileLocation {
+ private fun locationWithoutExtension(node: DocumentationNode): FileLocation {
return FileLocation(fileForNode(node))
}
override fun buildPages(nodes: Iterable<DocumentationNode>) {
for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) {
-
- file.parentFile?.mkdirsOrFail()
- try {
- FileOutputStream(file).use {
- OutputStreamWriter(it, Charsets.UTF_8).use {
- it.write(formatService.format(location(items.first()), items))
- }
- }
- } catch (e: Throwable) {
- println(e)
+ file.writeFileAndAssert("pages") { it ->
+ it.writeText(formatService.format(location(items.first()), items))
}
- buildPages(items.flatMap { it.members })
+
+ buildPages(items.filterNot { it.kind == NodeKind.AllTypes }.flatMap { it.members })
}
}
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
val outlineService = this.outlineService ?: return
for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) {
- val file = outlineService.getOutlineFileName(location)
- file.parentFile?.mkdirsOrFail()
- FileOutputStream(file).use {
- OutputStreamWriter(it, Charsets.UTF_8).use {
- it.write(outlineService.formatOutline(location, items))
- }
+ outlineService.getOutlineFileName(location).writeFileAndAssert("outlines") { file ->
+ file.writeText(outlineService.formatOutline(location, items))
}
}
}
override fun buildSupportFiles() {
formatService.enumerateSupportFiles { resource, targetPath ->
- FileOutputStream(File(root, relativePathToNode(listOf(targetPath), false))).use {
- javaClass.getResourceAsStream(resource).copyTo(it)
+ File(root, relativePathToNode(listOf(targetPath), false)).writeFileAndAssert("support files") { file ->
+ file.outputStream().use {
+ javaClass.getResourceAsStream(resource).copyTo(it)
+ }
}
}
}
@@ -73,16 +98,11 @@ class FileGenerator @Inject constructor(@Named("outputDir") override val root: F
val moduleRoot = location(module).file.parentFile
val packageListFile = File(moduleRoot, "package-list")
- packageListFile.writeText("\$dokka.format:${options.outputFormat}\n" +
- packageListService!!.formatPackageList(module as DocumentationModule))
- }
+ val text = "\$dokka.format:${dokkaConfiguration.format}\n" + packageListService!!.formatPackageList(module as DocumentationModule)
+ packageListFile.writeFileAndAssert("packages-list") { file ->
+ file.writeText(text)
+ }
+ }
}
-
}
-
-private fun File.mkdirsOrFail() {
- if (!mkdirs() && !exists()) {
- throw IOException("Failed to create directory $this")
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
deleted file mode 100644
index 34d4154e..00000000
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.jetbrains.dokka
-
-import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
-import org.jetbrains.dokka.DokkaConfiguration.SourceRoot
-import java.io.File
-
-
-data class SourceLinkDefinitionImpl(override val path: String,
- override val url: String,
- override val lineSuffix: String?) : SourceLinkDefinition {
- companion object {
- fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
- val (path, urlAndLine) = srcLink.split('=')
- return SourceLinkDefinitionImpl(File(path).absolutePath,
- urlAndLine.substringBefore("#"),
- urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
- }
- }
-}
-
-class SourceRootImpl(path: String, override val platforms: List<String> = emptyList()) : 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(','))
- }
- }
-}
-
-data class PackageOptionsImpl(override val prefix: String,
- override val includeNonPublic: Boolean = false,
- override val reportUndocumented: Boolean = true,
- override val skipDeprecated: Boolean = false,
- 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
diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
index 624c5fdc..2eaf4af2 100644
--- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -1,9 +1,12 @@
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.KtLightAbstractAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
@@ -11,17 +14,25 @@ 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 ->
- element.containingClass!!.qualifiedName + "$" + element.name + "(" +
- element.parameterList.parameters.joinToString(",") { it.type.typeSignature() } + ")"
+ methodSignature(element)
+ is PsiParameter -> {
+ val method = (element.parent.parent as PsiMethod)
+ methodSignature(method)
+ }
else -> null
}
+private fun methodSignature(method: PsiMethod): String {
+ return method.containingClass!!.qualifiedName + "$" + method.name + "(" +
+ method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")"
+}
+
private fun PsiType.typeSignature(): String = when(this) {
is PsiArrayType -> "Array((${componentType.typeSignature()}))"
is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize()
@@ -41,18 +52,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 +78,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)
}
@@ -92,9 +109,11 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun nodeForElement(element: PsiNamedElement,
kind: NodeKind,
- name: String = element.name ?: "<anonymous>"): DocumentationNode {
+ name: String = element.name ?: "<anonymous>",
+ register: Boolean = false): DocumentationNode {
val (docComment, deprecatedContent) = docParser.parseDocumentation(element)
val node = DocumentationNode(name, docComment, kind)
+ if (register) register(element, node)
if (element is PsiModifierListOwner) {
node.appendModifiers(element)
val modifierList = element.modifierList
@@ -132,21 +151,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
@@ -161,13 +182,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun PsiClass.build(): DocumentationNode {
val kind = when {
+ isAnnotationType -> NodeKind.AnnotationClass
isInterface -> NodeKind.Interface
isEnum -> NodeKind.Enum
- isAnnotationType -> NodeKind.AnnotationClass
isException() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForElement(this, kind)
+ val node = nodeForElement(this, kind, register = isAnnotationType)
superTypes.filter { !ignoreSupertype(it) }.forEach {
node.appendType(it, NodeKind.Supertype)
val superClass = it.resolve()
@@ -200,11 +221,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 (modifierList.hasExplicitModifier(PsiModifier.FINAL) &&
+ modifierList.hasExplicitModifier(PsiModifier.STATIC)) {
+ val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false)
+ val text = when(value) {
+ null -> return // No value found
+ 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
@@ -274,8 +312,26 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
return node
}
+ private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode {
+ val existing = refGraph.lookup(getSignature(psiClass)!!)
+ if (existing != null) return existing
+ val new = psiClass.build()
+ val packageNode = findOrCreatePackageNode(null, (psiClass.parent as PsiJavaFile).packageName, emptyMap(), refGraph)
+ packageNode.append(new, RefKind.Member)
+ return new
+ }
+
fun PsiAnnotation.build(): DocumentationNode {
- val node = DocumentationNode(nameReferenceElement?.text ?: "<?>", Content.Empty, NodeKind.Annotation)
+
+ val original = when (this) {
+ is KtLightAbstractAnnotation -> clsDelegate
+ else -> this
+ }
+ val node = DocumentationNode(qualifiedName?.substringAfterLast(".") ?: "<?>", Content.Empty, NodeKind.Annotation)
+ val psiClass = original.nameReferenceElement?.resolve() as? PsiClass
+ if (psiClass != null && psiClass.isAnnotationType) {
+ node.append(lookupOrBuildClass(psiClass), RefKind.Link)
+ }
parameterList.attributes.forEach {
val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter)
val value = it.value
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
index ea3a5963..25a974a3 100644
--- a/core/src/main/kotlin/Java/JavadocParser.kt
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -1,14 +1,16 @@
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.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+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,74 +22,163 @@ 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) ?: return@LazyContentBlock emptyList()
+ listOf(ContentBlock().apply {
+ append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
+ symbol(":")
+ text(" ")
+ })
+ })
+ }
+
override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
val docComment = (element as? PsiDocCommentOwner)?.docComment ?: 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 nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
+ val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph }
+ val firstParagraph = ContentParagraph()
+ if (firstParagraphContents.isNotEmpty()) {
+ firstParagraphContents.forEach { firstParagraph.append(it) }
+ result.append(firstParagraph)
+ }
+
+ result.appendAll(nodes.drop(firstParagraphContents.size))
+
+ 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 { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() }
+ ?.detailOrNull(NodeKind.Type)
+ }
+ }
+ "return" -> {
+ section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
+ }
+ }
+ section.appendAll(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 {
+ appendAll(convertJavadocElements(tag.contentElements(), element))
+ }
}
+ in tagsToInherit -> {}
else -> {
val subjectName = tag.getSubjectName()
val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
- section.convertJavadocElements(tag.contentElements())
+ section.appendAll(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 convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<ContentNode> {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ return doc.body().childNodes().mapNotNull {
+ convertHtmlNode(it)
+ }
+ }
+
+ private fun ContentBlock.appendAll(nodes: List<ContentNode>) {
+ nodes.forEach { 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, insidePre: Boolean = false): ContentNode? {
if (node is TextNode) {
- append(ContentText(node.text()))
+ val text = if (insidePre) node.wholeText else node.text()
+ return ContentText(text)
} else if (node is Element) {
- val childBlock = createBlock(node)
+ val childBlock = createBlock(node, insidePre)
+
node.childNodes().forEach {
- childBlock.convertHtmlNode(it)
+ val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode)
+ 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, insidePre: Boolean): ContentBlock = when (element.tagName()) {
"p" -> ContentParagraph()
"b", "strong" -> ContentStrong()
"i", "em" -> ContentEmphasis()
"s", "del" -> ContentStrikethrough()
- "code" -> ContentCode()
+ "code" -> if (insidePre) ContentBlock() else ContentCode()
"pre" -> ContentBlockCode()
"ul" -> ContentUnorderedList()
"ol" -> ContentOrderedList()
@@ -98,42 +189,72 @@ 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() ?: 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 {
""
@@ -145,16 +266,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
}
@@ -165,4 +315,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 da2b7272..88494581 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -1,7 +1,6 @@
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
@@ -11,9 +10,9 @@ 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? {
@@ -32,14 +31,15 @@ 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. " +
+ "This is probably caused by invalid configuration of cross-module dependencies")
}
target
}
@@ -64,7 +64,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 4d276b5a..ce20aeec 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -19,6 +19,8 @@ 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.psi.KtElement
+import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -28,17 +30,20 @@ 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
+ fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content =
+ parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first
- fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
+ fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) {
return parseJavadoc(descriptor)
}
@@ -59,7 +64,10 @@ class DescriptorDocumentationParser
?.resolveToDescriptorIfAny()
?: descriptor
- var kdocText = kdoc.getContent()
+ var kdocText = if (isDefaultNoArgConstructor) {
+ getConstructorTagContent(descriptor) ?: kdoc.getContent()
+ } else kdoc.getContent()
+
// workaround for code fence parsing problem in IJ markdown parser
if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
kdocText += "\n"
@@ -87,6 +95,13 @@ class DescriptorDocumentationParser
return content to { node -> }
}
+ private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? {
+ return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag(
+ KDocKnownTag.CONSTRUCTOR
+ )?.getContent()
+ }
+
+
private fun DeclarationDescriptor.isSuppressWarning() : Boolean {
val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
return if (suppressAnnotation != null) {
@@ -115,7 +130,7 @@ class DescriptorDocumentationParser
anyClassDescriptors.forEach {
val anyMethod = (it as ClassDescriptor).getMemberScope(listOf())
.getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) { it == descriptor.name }
- .single()
+ .single()
val kdoc = anyMethod.findKDoc()
if (kdoc != null) {
return kdoc
@@ -129,7 +144,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 9c726429..eb0399c7 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -3,84 +3,43 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
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.coroutines.hasFunctionOrSuspendFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.idea.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.addRemoveModifier.MODIFIERS_ORDER
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.immediateSupertypes
+import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
+import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
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: Set<File> = emptySet()) {
- 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 {
@@ -103,10 +62,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,
@@ -118,7 +81,7 @@ class DocumentationBuilder
val knownModifiers = setOf(
KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD,
KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD,
- KtTokens.OVERRIDE_KEYWORD)
+ KtTokens.OVERRIDE_KEYWORD, KtTokens.INLINE_KEYWORD)
fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) {
refGraph.link(node, descriptor.signature(), kind)
@@ -134,8 +97,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)
@@ -164,32 +139,32 @@ class DocumentationBuilder
appendTextNode(modifier, NodeKind.Modifier)
}
+ fun DocumentationNode.appendInline(descriptor: DeclarationDescriptor, psi: KtModifierListOwner) {
+ if (!psi.hasModifier(KtTokens.INLINE_KEYWORD)) return
+ if (descriptor is FunctionDescriptor
+ && descriptor.valueParameters.none { it.hasFunctionOrSuspendFunctionType }) return
+ appendTextNode(KtTokens.INLINE_KEYWORD.value, NodeKind.Modifier)
+ }
+
fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
val modifier = descriptor.visibility.normalize().displayName
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)
@@ -227,19 +202,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) {
@@ -273,22 +268,44 @@ 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)
.detail(NodeKind.Value)
.name.removeSurrounding("\"")
- append(platformNodeRegistry["Kotlin " + kotlinVersion], RefKind.Platform)
+ sinceKotlin = kotlinVersion
+ }
+
+ fun DocumentationNode.appendDefaultSinceKotlin() {
+ if (sinceKotlin == null) {
+ sinceKotlin = passConfiguration.sinceKotlin
+ }
}
fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
- KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ KtTokens.MODIFIER_KEYWORDS_ARRAY.filter {
+ it !in knownModifiers
+ }.sortedBy {
+ MODIFIERS_ORDER.indexOf(it)
+ }.forEach {
if (psi.hasModifier(it)) {
appendTextNode(it.value, NodeKind.Modifier)
}
}
+ appendInline(descriptor, psi)
}
fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) {
@@ -302,7 +319,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) {
@@ -310,7 +327,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
@@ -337,7 +354,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) {
@@ -409,41 +426,93 @@ 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 classHierarchy = buildClassHierarchy(documentingClasses)
- val allExtensionFunctions = allDescriptors
+ val allExtensionFunctions =
+ allDescriptors
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.extensionReceiverParameter != null }
val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
+ fun isIgnoredReceiverType(type: KotlinType) =
+ type.isDynamic() ||
+ type.isAnyOrNullableAny() ||
+ (type.isTypeParameter() && type.immediateSupertypes().all { it.isAnyOrNullableAny() })
+
+
for (extensionFunction in allExtensionFunctions) {
+ val extensionReceiverParameter = extensionFunction.extensionReceiverParameter!!
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 ->
+ if (isIgnoredReceiverType(extensionReceiverParameter.type)) continue
+ 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)
}
}
@@ -451,12 +520,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>> {
@@ -495,34 +561,61 @@ 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.appendDefaultSinceKotlin()
+ 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
@@ -532,21 +625,26 @@ 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.appendDefaultSinceKotlin()
+ 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
}
@@ -572,7 +670,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) {
@@ -600,6 +698,7 @@ class DocumentationBuilder
val node = nodeForDescriptor(this, NodeKind.Constructor)
node.appendInPageChildren(valueParameters, RefKind.Detail)
node.appendDefaultPlatforms(this)
+ node.appendDefaultSinceKotlin()
register(this, node)
return node
}
@@ -613,21 +712,28 @@ 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) }
node.appendInPageChildren(valueParameters, RefKind.Detail)
node.appendType(returnType)
+ if (!external) {
+ node.appendDefaultSinceKotlin()
+ }
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)
@@ -648,32 +754,50 @@ 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)
+ if (!external) {
+ node.appendDefaultSinceKotlin()
+ }
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) {
+ this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) }
}
+
+
+ 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
@@ -705,6 +829,7 @@ class DocumentationBuilder
}
}
}
+ node.appendDefaultSinceKotlin()
node.appendAnnotations(this)
node.appendModifiers(this)
if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) {
@@ -775,8 +900,8 @@ class DocumentationBuilder
return node
}
- fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value?.let { value ->
- when (value) {
+ fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value ->
+ val text = when (value) {
is String ->
"\"" + StringUtil.escapeStringCharacters(value) + "\""
is EnumEntrySyntheticClassDescriptor ->
@@ -789,21 +914,46 @@ class DocumentationBuilder
value.toString()
}
}
- else -> value.toString()
- }.let { valueString ->
- DocumentationNode(valueString, Content.Empty, NodeKind.Value)
+ else -> "$value"
}
+ DocumentationNode(text, 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
@@ -817,8 +967,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)
}
}
@@ -829,20 +984,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) {
@@ -871,13 +1021,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()
@@ -893,24 +1043,6 @@ 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) {
@@ -994,8 +1126,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()
@@ -1003,8 +1135,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) {
@@ -1013,3 +1147,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, false)
+ 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 e19ecf76..793f9589 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -2,14 +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.descriptors.impl.EnumEntrySyntheticClassDescriptor
-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.load.java.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -20,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()
@@ -115,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.getValue("dokka-default")
+ val resolverDesc = ExternalDocumentationLinkResolver.services[format]
+ ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver }
+ ?: defaultResolverDesc.also {
+ logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default")
+ }
+
+
+ val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin
+
+ val constructors = resolverClass.constructors
+
+ val constructor = constructors.singleOrNull()
+ ?: constructors.first { it.findAnnotation<Inject>() != null }
+ val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService
val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations)
- 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
}
@@ -170,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 }
}
}
@@ -177,7 +253,7 @@ 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 EnumEntrySyntheticClassDescriptor) {
return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() }
@@ -187,7 +263,7 @@ interface InboundExternalLinkResolutionService {
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 }
@@ -203,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
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 21c1ae0f..ee9d8c51 100644
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -6,9 +6,11 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiNamedElement
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPropertyAccessor
@@ -28,7 +30,7 @@ class KotlinAsJavaDocumentationBuilder
return
}
- val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.options,
+ val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration,
documentationBuilder.refGraph,
kotlinAsJavaDocumentationParser)
@@ -58,8 +60,9 @@ class KotlinAsJavaDocumentationParser
return JavadocParseResult.Empty
}
}
+ val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass
val descriptor = resolutionFacade.resolveToDescriptor(origin)
- val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter)
+ val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor)
return JavadocParseResult(content, null)
}
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
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 597002fb..7310610f 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", "internal", "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) {
@@ -190,15 +166,27 @@ class KotlinLanguageService : LanguageService {
keyword("dynamic")
return
}
+
+ val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier)
+
if (node.isFunctionalType()) {
- renderFunctionalType(node, renderMode)
+ if (nullabilityModifier != null) {
+ symbol("(")
+ renderFunctionalType(node, renderMode)
+ symbol(")")
+ symbol(nullabilityModifier.name)
+ } else {
+ renderFunctionalType(node, renderMode)
+ }
return
}
if (renderMode == RenderMode.FULL) {
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("<")
@@ -207,22 +195,24 @@ class KotlinLanguageService : LanguageService {
}
symbol(">")
}
- val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull()
- if (nullabilityModifier != null) {
+
+ nullabilityModifier ?.apply {
symbol(nullabilityModifier.name)
}
}
- 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", "external" -> {
+ }
else -> {
- keyword(node.name)
- if (nowrap) {
- nbsp()
- }
- else {
- text(" ")
+ if (showModifierInSummary(node) || renderMode == RenderMode.FULL) {
+ super.renderModifier(block, node, renderMode, nowrap)
}
}
}
@@ -238,11 +228,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 +265,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 +284,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 +296,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 +345,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 +367,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 +383,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 +413,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 +449,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 +462,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/Location.kt b/core/src/main/kotlin/Locations/Location.kt
index 4cb0ac39..63c9b913 100644
--- a/core/src/main/kotlin/Locations/Location.kt
+++ b/core/src/main/kotlin/Locations/Location.kt
@@ -26,7 +26,7 @@ data class FileLocation(val file: File) : Location {
}
val ownerFolder = file.parentFile!!
val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/')
- return if (anchor == null) relativePath else relativePath + "#" + anchor
+ return if (anchor == null) relativePath else "$relativePath#$anchor"
}
}
@@ -40,10 +40,25 @@ fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String
}
}
+fun nodeActualQualifier(node: DocumentationNode): List<DocumentationNode> {
+ val topLevelPage = node.references(RefKind.TopLevelPage).singleOrNull()?.to
+ if (topLevelPage != null) {
+ return nodeActualQualifier(topLevelPage)
+ }
+ return node.owner?.let { nodeActualQualifier(it) }.orEmpty() + node
+}
+
+fun relativePathToNode(node: DocumentationNode): String {
+ val qualifier = nodeActualQualifier(node)
+ return relativePathToNode(
+ qualifier.map { it.name },
+ qualifier.last().members.any()
+ )
+}
-fun relativePathToNode(node: DocumentationNode) = relativePathToNode(node.path.map { it.name }, node.members.any())
fun identifierToFilename(path: String): String {
+ if (path.isEmpty()) return "--root--"
val escaped = path.replace('<', '-').replace('>', '-')
val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
return if (lowercase == "index") "--index--" else lowercase
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
index ccb5bff3..5530e1b4 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: () -> List<ContentNode>) : ContentBlock() {
+ private var computed = false
+ override val children: ArrayList<ContentNode>
+ get() {
+ if (!computed) {
+ computed = true
+ children.addAll(fillChildren())
+ }
+ return super.children
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode() + 31 * fillChildren.hashCode()
+ }
+}
+
enum class IdentifierKind {
TypeName,
ParameterName,
@@ -87,6 +115,7 @@ class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: Cont
abstract class ContentNodeLink() : ContentBlock() {
abstract val node: DocumentationNode?
+ abstract val text: String?
}
object ContentHardLineBreak : ContentNode {
@@ -100,6 +129,8 @@ class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLi
override fun hashCode(): Int =
children.hashCode() * 31 + node.name.hashCode()
+
+ override val text: String? = null
}
class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() {
@@ -110,6 +141,8 @@ class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> Documentatio
override fun hashCode(): Int =
children.hashCode() * 31 + linkText.hashCode()
+
+ override val text: String? = linkText
}
class ContentExternalLink(val href : String) : ContentBlock() {
@@ -120,6 +153,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()
@@ -191,7 +227,11 @@ open class Content(): ContentBlock() {
sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) }
companion object {
- val Empty = Content()
+ val Empty = object: Content() {
+ override fun toString(): String {
+ return "EMPTY_CONTENT"
+ }
+ }
fun of(vararg child: ContentNode): Content {
val result = MutableContent()
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 3ff1d127..311b46e4 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,14 @@ 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
+ .map { it.content }
+ .firstOrNull { !it.isEmpty() }
+ ?.summary ?: ContentEmpty
+ else -> content.summary
+ }
+
val owner: DocumentationNode?
get() = references(RefKind.Owner).singleOrNull()?.to
@@ -83,8 +91,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 +116,47 @@ 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()
+
+ var sinceKotlin: String?
+ get() = references(RefKind.SinceKotlin).singleOrNull()?.to?.name
+ set(value) {
+ dropReferences { it.kind == RefKind.SinceKotlin }
+ if (value != null) {
+ append(DocumentationNode(value, Content.Empty, NodeKind.Value), RefKind.SinceKotlin)
+ }
+ }
+
+ 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 +171,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 +182,7 @@ open class DocumentationNode(val name: String,
fun member(kind: NodeKind): DocumentationNode = members.single { it.kind == kind }
fun link(kind: NodeKind): DocumentationNode = links.single { it.kind == kind }
+
fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind }
fun allReferences(): Set<DocumentationReference> = references
@@ -143,9 +191,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,17 +202,21 @@ 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 }
- if (existingNode != null) {
- return existingNode
- }
- val newNode = DocumentationNode(packageName,
+fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
+ val node = refGraph.lookup(packageName) ?: run {
+ val newNode = DocumentationNode(
+ packageName,
packageContent.getOrElse(packageName) { Content.Empty },
- NodeKind.Package)
- append(newNode, RefKind.Member)
- refGraph.register(packageName, newNode)
- return newNode
+ NodeKind.Package
+ )
+
+ refGraph.register(packageName, newNode)
+ newNode
+ }
+ if (module != null && node !in module.members) {
+ module.append(node, RefKind.Member)
+ }
+ return node
}
fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
@@ -173,7 +225,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 +240,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(".")
+ return path.dropWhile { it.kind == NodeKind.Module }.map { it.name }.filter { it.isNotEmpty() }.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 87e7ecea..0b890a78 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,42 +18,84 @@ enum class RefKind {
HiddenAnnotation,
Deprecation,
TopLevelPage,
- Platform
+ Platform,
+ ExternalType,
+ Origin,
+ SinceKotlin
}
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 +103,14 @@ 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`." +
+ "This is probably caused by invalid configuration of cross-module dependencies")
}
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 7ba4bdad..cf6cd808 100644
--- a/core/src/main/kotlin/Model/PackageDocs.kt
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -2,17 +2,25 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
-import com.intellij.openapi.util.text.StringUtil
+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()
@@ -22,7 +30,7 @@ class PackageDocs
fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) {
val file = File(fileName)
if (file.exists()) {
- val text = StringUtil.convertLineSeparators(file.readText())
+ val text = file.readText()
val tree = parseMarkdown(text)
val linkMap = LinkMap.buildLinkMap(tree.node, text)
var targetContent: MutableContent = moduleContent
@@ -41,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/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt
index 7575b2b3..99ee362e 100644
--- a/core/src/main/kotlin/Model/SourceLinks.kt
+++ b/core/src/main/kotlin/Model/SourceLinks.kt
@@ -10,20 +10,20 @@ import java.io.File
fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) {
val path = psi?.containingFile?.virtualFile?.path ?: return
+ val canonicalPath = File(path).canonicalPath
val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi
- val absPath = File(path).absolutePath
- val linkDef = sourceLinks.firstOrNull { absPath.startsWith(it.path) }
- if (linkDef != null) {
- var url = linkDef.url + path.substring(linkDef.path.length)
- if (linkDef.lineSuffix != null) {
+ val pair = determineSourceLinkDefinition(canonicalPath, sourceLinks)
+ if (pair != null) {
+ val (sourceLinkDefinition, sourceLinkCanonicalPath) = pair
+ var url = determineUrl(canonicalPath, sourceLinkDefinition, sourceLinkCanonicalPath)
+ if (sourceLinkDefinition.lineSuffix != null) {
val line = target?.lineNumber()
if (line != null) {
- url += linkDef.lineSuffix + line.toString()
+ url += sourceLinkDefinition.lineSuffix + line.toString()
}
}
- append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl),
- RefKind.Detail)
+ append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), RefKind.Detail)
}
if (target != null) {
@@ -31,6 +31,28 @@ fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<Sourc
}
}
+private fun determineSourceLinkDefinition(
+ canonicalPath: String,
+ sourceLinks: List<SourceLinkDefinition>
+): Pair<SourceLinkDefinition, String>? {
+ return sourceLinks
+ .asSequence()
+ .map { it to File(it.path).canonicalPath }
+ .firstOrNull { (_, sourceLinkCanonicalPath) ->
+ canonicalPath.startsWith(sourceLinkCanonicalPath)
+ }
+}
+
+private fun determineUrl(
+ canonicalPath: String,
+ sourceLinkDefinition: SourceLinkDefinition,
+ sourceLinkCanonicalPath: String
+): String {
+ val relativePath = canonicalPath.substring(sourceLinkCanonicalPath.length)
+ val relativeUrl = relativePath.replace('\\', '/').removePrefix("/")
+ return "${sourceLinkDefinition.url.removeSuffix("/")}/$relativeUrl"
+}
+
private fun PsiElement.sourcePosition(): String {
val path = containingFile.virtualFile.path
val lineNumber = lineNumber()
diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
index ff11ca02..da74495f 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 b0988c35..4525e9d9 100644
--- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
+++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
@@ -1,7 +1,9 @@
package org.jetbrains.dokka.Samples
import com.google.inject.Inject
+import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiTreeUtil
@@ -9,19 +11,27 @@ import org.jetbrains.dokka.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.ImportPath
+import java.io.PrintWriter
+import java.io.StringWriter
+
open class KotlinWebsiteSampleProcessingService
-@Inject constructor(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()
val text: String
get() = builder.toString()
+ val errors = mutableListOf<ConvertError>()
+
+ data class ConvertError(val e: Exception, val text: String, val loc: String)
+
fun KtValueArgument.extractStringArgumentValue() =
(getArgumentExpression() as KtStringTemplateExpression)
.entries.joinToString("") { it.text }
@@ -54,27 +64,42 @@ open class KotlinWebsiteSampleProcessingService
}
fun convertAssertFails(expression: KtCallExpression) {
- val (message, funcArgument) = expression.valueArguments
+ val valueArguments = expression.valueArguments
+
+ val funcArgument: KtValueArgument
+ val message: KtValueArgument?
+
+ if (valueArguments.size == 1) {
+ message = null
+ funcArgument = valueArguments.first()
+ } else {
+ message = valueArguments.first()
+ funcArgument = valueArguments.last()
+ }
+
builder.apply {
- val argument = if (funcArgument.getArgumentExpression() is KtLambdaExpression)
- PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: ""
- else
- funcArgument.text
+ val argument = funcArgument.extractFunctionalArgumentText()
append(argument.lines().joinToString(separator = "\n") { "// $it" })
append(" // ")
- append(message.extractStringArgumentValue())
+ if (message != null) {
+ append(message.extractStringArgumentValue())
+ }
append(" will fail")
}
}
+ private fun KtValueArgument.extractFunctionalArgumentText(): String {
+ return if (getArgumentExpression() is KtLambdaExpression)
+ PsiTreeUtil.findChildOfType(this, KtBlockExpression::class.java)?.text ?: ""
+ else
+ text
+ }
+
fun convertAssertFailsWith(expression: KtCallExpression) {
val (funcArgument) = expression.valueArguments
val (exceptionType) = expression.typeArguments
builder.apply {
- val argument = if (funcArgument.firstChild is KtLambdaExpression)
- PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: ""
- else
- funcArgument.text
+ val argument = funcArgument.extractFunctionalArgumentText()
append(argument.lines().joinToString(separator = "\n") { "// $it" })
append(" // will fail with ")
append(exceptionType.text)
@@ -92,16 +117,51 @@ open class KotlinWebsiteSampleProcessingService
}
}
+ private fun reportProblemConvertingElement(element: PsiElement, e: Exception) {
+ val text = element.text
+ val document = PsiDocumentManager.getInstance(element.project).getDocument(element.containingFile)
+
+ val lineInfo = if (document != null) {
+ val lineNumber = document.getLineNumber(element.startOffset)
+ "$lineNumber, ${element.startOffset - document.getLineStartOffset(lineNumber)}"
+ } else {
+ "offset: ${element.startOffset}"
+ }
+ errors += ConvertError(e, text, lineInfo)
+ }
+
override fun visitElement(element: PsiElement) {
if (element is LeafPsiElement)
builder.append(element.text)
- super.visitElement(element)
+
+ element.acceptChildren(object : PsiElementVisitor() {
+ override fun visitElement(element: PsiElement) {
+ try {
+ element.accept(this@SampleBuilder)
+ } catch (e: Exception) {
+ try {
+ reportProblemConvertingElement(element, e)
+ } finally {
+ builder.append(element.text) //recover
+ }
+ }
+ }
+ })
}
+
}
private fun PsiElement.buildSampleText(): String {
val sampleBuilder = SampleBuilder()
this.accept(sampleBuilder)
+
+ sampleBuilder.errors.forEach {
+ val sw = StringWriter()
+ val pw = PrintWriter(sw)
+ it.e.printStackTrace(pw)
+
+ logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw")
+ }
return sampleBuilder.text
}
diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
index cead29c9..919ec30f 100644
--- a/core/src/main/kotlin/Utilities/DokkaModules.kt
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -4,7 +4,6 @@ import com.google.inject.Binder
import com.google.inject.Module
import com.google.inject.TypeLiteral
import com.google.inject.binder.AnnotatedBindingBuilder
-import com.google.inject.binder.ScopedBindingBuilder
import com.google.inject.name.Names
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Formats.FormatDescriptor
@@ -14,10 +13,21 @@ 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)
@@ -25,32 +35,29 @@ class DokkaAnalysisModule(val environment: AnalysisEnvironment,
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", options.outputFormat)
+ 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(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir))
-
- binder.bind<DocumentationOptions>().toInstance(options)
binder.bind<DokkaLogger>().toInstance(logger)
- binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms)
- val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format)
descriptor.configureOutput(binder)
}
@@ -75,4 +82,4 @@ inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>>
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): ScopedBindingBuilder = to(kClass.java)
+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 6f2ebd0f..835cd34c 100644
--- a/core/src/main/kotlin/Utilities/ServiceLocator.kt
+++ b/core/src/main/kotlin/Utilities/ServiceLocator.kt
@@ -14,10 +14,18 @@ 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.firstOrNull { it.parameterTypes.isEmpty() } ?: 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}")
@@ -59,7 +67,7 @@ object ServiceLocator {
"jar" -> {
val file = JarFile(URL(it.file.substringBefore("!")).toFile())
try {
- val jarPath = it.file.substringAfterLast("!").removePrefix("/")
+ val jarPath = it.file.substringAfterLast("!").removePrefix("/") // TODO: revision b265a9ffacb8f8e8e6226a9458a92697b02355a8 - removeSurrounding for Ant breaks Gradle
file.entries()
.asSequence()
.filter { entry -> !entry.isDirectory && entry.path == jarPath && entry.extension == "properties" }
@@ -77,6 +85,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/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt
index f33b1324..0bf72ccf 100644
--- a/core/src/main/kotlin/javadoc/docbase.kt
+++ b/core/src/main/kotlin/javadoc/docbase.kt
@@ -2,7 +2,7 @@ package org.jetbrains.dokka.javadoc
import com.sun.javadoc.*
import org.jetbrains.dokka.*
-import java.lang.reflect.Modifier
+import java.lang.reflect.Modifier.*
import java.util.*
import kotlin.reflect.KClass
@@ -75,9 +75,7 @@ open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node
node.deprecation?.let {
val content = it.content.asText()
- if (content != null) {
- result.add(TagImpl(this, "deprecated", content))
- }
+ result.add(TagImpl(this, "deprecated", content ?: ""))
}
return result.toTypedArray()
@@ -86,7 +84,7 @@ open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node
// should be extension property but can't because of KT-8745
private fun <T> nodeAnnotations(self: T): List<AnnotationDescAdapter> where T : HasModule, T : HasDocumentationNode
- = self.node.annotations.map { AnnotationDescAdapter(self.module, it) }
+ = self.node.annotations.map { AnnotationDescAdapter(self.module, it) }
private fun DocumentationNode.hasAnnotation(klass: KClass<*>) = klass.qualifiedName in annotations.map { it.qualifiedName() }
private fun DocumentationNode.hasModifier(name: String) = details(NodeKind.Modifier).any { it.name == name }
@@ -96,7 +94,7 @@ class PackageAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : Docum
private val allClasses = listOf(node).collectAllTypesRecursively()
override fun findClass(className: String?): ClassDoc? =
- allClasses.get(className)?.let { ClassDocumentationNodeAdapter(module, it) }
+ allClasses.get(className)?.let { ClassDocumentationNodeAdapter(module, it) }
override fun annotationTypes(): Array<out AnnotationTypeDoc> = emptyArray()
override fun annotations(): Array<out AnnotationDesc> = node.members(NodeKind.AnnotationClass).map { AnnotationDescAdapter(module, it) }.toTypedArray()
@@ -116,20 +114,27 @@ class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc {
- override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node) // TODO ?????
+ override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node.links.find { it.kind == NodeKind.AnnotationClass } ?: node) // TODO ?????
override fun isSynthesized(): Boolean = false
override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO
}
open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc {
- override fun isPublic(): Boolean = true
+ override fun isPublic(): Boolean = node.hasModifier("public") || node.hasModifier("internal")
override fun isPackagePrivate(): Boolean = false
override fun isStatic(): Boolean = node.hasModifier("static")
- override fun modifierSpecifier(): Int = Modifier.PUBLIC + if (isStatic) Modifier.STATIC else 0
+ override fun modifierSpecifier(): Int = visibilityModifier or (if (isStatic) STATIC else 0)
+ private val visibilityModifier
+ get() = when {
+ isPublic -> PUBLIC
+ isPrivate -> PRIVATE
+ isProtected -> PROTECTED
+ else -> 0
+ }
override fun qualifiedName(): String? = node.qualifiedName()
override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray()
override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim()
- override fun isProtected(): Boolean = false
+ override fun isProtected(): Boolean = node.hasModifier("protected")
override fun isFinal(): Boolean = node.hasModifier("final")
@@ -165,7 +170,7 @@ open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationN
return null
}
- override fun isPrivate(): Boolean = false
+ override fun isPrivate(): Boolean = node.hasModifier("private")
override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true
}
@@ -173,31 +178,31 @@ open class TypeAdapter(override val module: ModuleNodeAdapter, override val node
private val javaLanguageService = JavaLanguageService()
override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType()
- override fun typeName(): String = javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()
+ override fun typeName(): String = (javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()) + dimension()
override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName()
override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("")
override fun isPrimitive(): Boolean = simpleTypeName() in setOf("int", "long", "short", "byte", "char", "double", "float", "boolean", "void")
override fun asClassDoc(): ClassDoc? = if (isPrimitive) null else
- elementType?.asClassDoc() ?:
- when (node.kind) {
- in NodeKind.classLike,
- NodeKind.ExternalClass,
- NodeKind.Exception -> module.classNamed(qualifiedTypeName()) ?: ClassDocumentationNodeAdapter(module, node)
-
- else -> when {
- node.links.isNotEmpty() -> TypeAdapter(module, node.links.first()).asClassDoc()
- else -> ClassDocumentationNodeAdapter(module, node) // TODO ?
- }
+ elementType?.asClassDoc() ?:
+ when (node.kind) {
+ in NodeKind.classLike,
+ NodeKind.ExternalClass,
+ NodeKind.Exception -> module.classNamed(qualifiedTypeName()) ?: ClassDocumentationNodeAdapter(module, node)
+
+ else -> when {
+ node.links.isNotEmpty() -> TypeAdapter(module, node.links.first()).asClassDoc()
+ else -> ClassDocumentationNodeAdapter(module, node) // TODO ?
}
+ }
override fun asTypeVariable(): TypeVariable? = if (node.kind == NodeKind.TypeParameter) TypeVariableAdapter(module, node) else null
override fun asParameterizedType(): ParameterizedType? =
- if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null)
- ParameterizedTypeAdapter(module, node)
- else
- null
+ if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null)
+ ParameterizedTypeAdapter(module, node)
+ else
+ null
override fun asAnnotationTypeDoc(): AnnotationTypeDoc? = if (node.kind == NodeKind.AnnotationClass) AnnotationTypeDocAdapter(module, node) else null
override fun asAnnotatedType(): AnnotatedType? = if (node.annotations.isNotEmpty()) AnnotatedTypeAdapter(module, node) else null
@@ -253,15 +258,15 @@ class TypeVariableAdapter(module: ModuleNodeAdapter, node: DocumentationNode) :
class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), ParameterizedType {
override fun typeArguments(): Array<out Type> = node.details(NodeKind.Type).map { TypeVariableAdapter(module, it) }.toTypedArray()
override fun superclassType(): Type? =
- node.lookupSuperClasses(module)
- .firstOrNull { it.kind == NodeKind.Class || it.kind == NodeKind.ExternalClass }
- ?.let { ClassDocumentationNodeAdapter(module, it) }
+ node.lookupSuperClasses(module)
+ .firstOrNull { it.kind == NodeKind.Class || it.kind == NodeKind.ExternalClass }
+ ?.let { ClassDocumentationNodeAdapter(module, it) }
override fun interfaceTypes(): Array<out Type> =
- node.lookupSuperClasses(module)
- .filter { it.kind == NodeKind.Interface }
- .map { ClassDocumentationNodeAdapter(module, it) }
- .toTypedArray()
+ node.lookupSuperClasses(module)
+ .filter { it.kind == NodeKind.Interface }
+ .map { ClassDocumentationNodeAdapter(module, it) }
+ .toTypedArray()
override fun containingType(): Type? = when (node.owner?.kind) {
NodeKind.Package -> null
@@ -275,7 +280,7 @@ class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter {
- override fun typeName(): String? = JavaLanguageService().renderType(node.detail(NodeKind.Type))
+ override fun typeName(): String? = type()?.typeName()
override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type))
override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
}
@@ -302,7 +307,7 @@ fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode
}
private fun DocumentationNode.hasNonEmptyContent() =
- this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty()
+ this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty()
open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), ExecutableMemberDoc {
@@ -312,33 +317,32 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio
override fun thrownExceptions(): Array<out ClassDoc> = emptyArray() // TODO
override fun throwsTags(): Array<out ThrowsTag> =
- node.content.sections
- .filter { it.tag == ContentTags.Exceptions && it.subjectName != null }
- .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) }
- .toTypedArray()
+ node.content.sections
+ .filter { it.tag == ContentTags.Exceptions && it.subjectName != null }
+ .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) }
+ .toTypedArray()
- override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO
+ override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).last().hasModifier("vararg")
override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" }
override fun paramTags(): Array<out ParamTag> =
- collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } })
+ collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } })
override fun thrownExceptionTypes(): Array<out Type> = emptyArray()
override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) }
- override fun flatSignature(): String = node.details(NodeKind.Parameter).joinToString(", ", "(", ")") { JavaLanguageService().renderType(it) }
- override fun signature(): String =
- node.details(NodeKind.Parameter).joinToString(", ", "(", ")") { JavaLanguageService().renderType(it) } // TODO it should be FQ types
+ override fun flatSignature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")")
+ override fun signature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") // TODO it should be FQ types
override fun parameters(): Array<out Parameter> =
- ((receiverNode()?.let { receiver -> listOf<Parameter>(ReceiverParameterAdapter(module, receiver, this)) } ?: emptyList())
- + node.details(NodeKind.Parameter).map { ParameterAdapter(module, it) }
- ).toTypedArray()
+ ((receiverNode()?.let { receiver -> listOf<Parameter>(ReceiverParameterAdapter(module, receiver, this)) } ?: emptyList())
+ + node.details(NodeKind.Parameter).map { ParameterAdapter(module, it) }
+ ).toTypedArray()
override fun typeParameters(): Array<out TypeVariable> = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray()
override fun typeParamTags(): Array<out ParamTag> =
- collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })
+ collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })
private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers ->
when {
@@ -396,8 +400,11 @@ class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : Program
}
open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNode: DocumentationNode)
: ProgramElementAdapter(module, classNode),
- Type by TypeAdapter(module, classNode),
- ClassDoc {
+ Type by TypeAdapter(module, classNode),
+ ClassDoc,
+ AnnotationTypeDoc {
+
+ override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO
override fun name(): String {
val parent = classNode.owner
@@ -407,6 +414,9 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod
return classNode.simpleName()
}
+ override fun qualifiedName(): String? {
+ return super.qualifiedName()
+ }
override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray()
override fun constructors(): Array<out ConstructorDoc> = constructors(true)
override fun importedPackages(): Array<out PackageDoc> = emptyArray()
@@ -420,17 +430,17 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod
override fun enumConstants(): Array<out FieldDoc>? = classNode.members(NodeKind.EnumItem).map { FieldAdapter(module, it) }.toTypedArray()
override fun isAbstract(): Boolean = classNode.details(NodeKind.Modifier).any { it.name == "abstract" }
override fun interfaceTypes(): Array<out Type> = classNode.lookupSuperClasses(module)
- .filter { it.kind == NodeKind.Interface }
- .map { ClassDocumentationNodeAdapter(module, it) }
- .toTypedArray()
+ .filter { it.kind == NodeKind.Interface }
+ .map { ClassDocumentationNodeAdapter(module, it) }
+ .toTypedArray()
override fun interfaces(): Array<out ClassDoc> = classNode.lookupSuperClasses(module)
- .filter { it.kind == NodeKind.Interface }
- .map { ClassDocumentationNodeAdapter(module, it) }
- .toTypedArray()
+ .filter { it.kind == NodeKind.Interface }
+ .map { ClassDocumentationNodeAdapter(module, it) }
+ .toTypedArray()
override fun typeParamTags(): Array<out ParamTag> =
- collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })
+ collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } })
override fun fields(): Array<out FieldDoc> = fields(true)
override fun fields(filter: Boolean): Array<out FieldDoc> = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray()
@@ -495,30 +505,35 @@ class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorR
override fun packageNamed(name: String?): PackageDoc? = allPackages[name]?.let { PackageAdapter(this, it) }
override fun classes(): Array<out ClassDoc> =
- allTypes.values.map { ClassDocumentationNodeAdapter(this, it) }.toTypedArray()
+ allTypes.values.map { ClassDocumentationNodeAdapter(this, it) }.toTypedArray()
override fun options(): Array<out Array<String>> = arrayOf(
- arrayOf("-d", outputPath),
- arrayOf("-docencoding", "UTF-8"),
- arrayOf("-charset", "UTF-8"),
- arrayOf("-keywords")
+ arrayOf("-d", outputPath),
+ arrayOf("-docencoding", "UTF-8"),
+ arrayOf("-charset", "UTF-8"),
+ arrayOf("-keywords")
)
override fun specifiedPackages(): Array<out PackageDoc>? = module.members(NodeKind.Package).map { PackageAdapter(this, it) }.toTypedArray()
override fun classNamed(qualifiedName: String?): ClassDoc? =
- allTypes[qualifiedName]?.let { ClassDocumentationNodeAdapter(this, it) }
+ allTypes[qualifiedName]?.let { ClassDocumentationNodeAdapter(this, it) }
override fun specifiedClasses(): Array<out ClassDoc> = classes()
}
private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) =
- (node.details(kind)
- .filter(DocumentationNode::hasNonEmptyContent)
- .map { ParamTagAdapter(module, this, it.name, true, it.content.children) }
-
- + node.content.sections
- .filter(sectionFilter)
- .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) })
-
- .toTypedArray() \ No newline at end of file
+ (node.details(kind)
+ .filter(DocumentationNode::hasNonEmptyContent)
+ .map { ParamTagAdapter(module, this, it.name, true, it.content.children) }
+
+ + node.content.sections
+ .filter(sectionFilter)
+ .map {
+ ParamTagAdapter(module, this, it.subjectName ?: "?", true,
+ it.children.filterNot { contentNode -> contentNode is LazyContentBlock }
+ )
+ }
+ )
+ .distinctBy { it.parameterName }
+ .toTypedArray() \ 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 483fb3cd..1329876a 100644
--- a/core/src/main/kotlin/javadoc/dokka-adapters.kt
+++ b/core/src/main/kotlin/javadoc/dokka-adapters.kt
@@ -4,16 +4,19 @@ 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.*
+import org.jetbrains.dokka.Formats.DefaultAnalysisComponent
+import org.jetbrains.dokka.Formats.DefaultAnalysisComponentServices
+import org.jetbrains.dokka.Formats.FormatDescriptor
+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
- HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), options.outputDir))
+ HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), configuration.outputDir))
}
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt
index 05d98b71..99c9bfff 100644
--- a/core/src/main/kotlin/javadoc/tags.kt
+++ b/core/src/main/kotlin/javadoc/tags.kt
@@ -71,7 +71,7 @@ class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: Conte
override fun referencedPackage(): PackageDoc? = null
override fun referencedClass(): ClassDoc? = method.containingClass()
override fun referencedClassName(): String = method.containingClass()?.name() ?: ""
- override fun label(): String = "${method.containingClass()?.name()}.${method.name()}"
+ override fun label(): String = content.text ?: "${method.containingClass()?.name()}.${method.name()}"
override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
diff --git a/core/src/main/resources/dokka/format/kotlin-website-samples.properties b/core/src/main/resources/dokka/format/kotlin-website-samples.properties
deleted file mode 100644
index bda616a4..00000000
--- a/core/src/main/resources/dokka/format/kotlin-website-samples.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-class=org.jetbrains.dokka.Formats.KotlinWebsiteFormatRunnableSamplesDescriptor
-description=Generates Kotlin website documentation \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/kotlin-website.properties b/core/src/main/resources/dokka/format/kotlin-website.properties
deleted file mode 100644
index c13e7675..00000000
--- a/core/src/main/resources/dokka/format/kotlin-website.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-class=org.jetbrains.dokka.Formats.KotlinWebsiteFormatDescriptor
-description=Generates Kotlin website documentation \ 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/test/kotlin/DokkaConfigurationTestImplementations.kt b/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt
new file mode 100644
index 00000000..a6f427b1
--- /dev/null
+++ b/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt
@@ -0,0 +1,81 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.Platform
+import java.io.File
+
+
+data class SourceLinkDefinitionImpl(override val path: String,
+ override val url: String,
+ override val lineSuffix: String?) : DokkaConfiguration.SourceLinkDefinition {
+ companion object {
+ fun parseSourceLinkDefinition(srcLink: String): DokkaConfiguration.SourceLinkDefinition {
+ val (path, urlAndLine) = srcLink.split('=')
+ return SourceLinkDefinitionImpl(
+ File(path).canonicalPath,
+ urlAndLine.substringBefore("#"),
+ urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" })
+ }
+ }
+}
+
+class SourceRootImpl(path: String) : DokkaConfiguration.SourceRoot {
+ override val path: String = File(path).absolutePath
+
+ companion object {
+ fun parseSourceRoot(sourceRoot: String): DokkaConfiguration.SourceRoot = SourceRootImpl(sourceRoot)
+ }
+}
+
+data class PackageOptionsImpl(override val prefix: String,
+ override val includeNonPublic: Boolean = false,
+ override val reportUndocumented: Boolean = true,
+ override val skipDeprecated: Boolean = false,
+ override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
+
+ class DokkaConfigurationImpl(
+ override val outputDir: String = "",
+ override val format: String = "html",
+ override val generateIndexPages: Boolean = false,
+ override val cacheRoot: String? = null,
+ override val impliedPlatforms: List<String> = emptyList(),
+ override val passesConfigurations: List<DokkaConfiguration.PassConfiguration> = emptyList()
+) : DokkaConfiguration
+
+class PassConfigurationImpl (
+ override val classpath: List<String> = emptyList(),
+ override val moduleName: String = "",
+ override val sourceRoots: List<DokkaConfiguration.SourceRoot> = emptyList(),
+ override val samples: List<String> = emptyList(),
+ override val includes: List<String> = emptyList(),
+ 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<DokkaConfiguration.SourceLinkDefinition> = emptyList(),
+ override val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+ externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> = emptyList(),
+ 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> = emptyList(),
+ override val collectInheritedExtensionsFromLibraries: Boolean = false,
+ override val analysisPlatform: Platform = Platform.DEFAULT,
+ override val targets: List<String> = emptyList(),
+ override val sinceKotlin: String? = null
+): DokkaConfiguration.PassConfiguration {
+ private val defaultLinks = run {
+ val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
+ if (!noJdkLink)
+ links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://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/test/kotlin/NodeSelect.kt b/core/src/test/kotlin/NodeSelect.kt
new file mode 100644
index 00000000..fe0394f9
--- /dev/null
+++ b/core/src/test/kotlin/NodeSelect.kt
@@ -0,0 +1,90 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+
+class SelectBuilder {
+ private val root = ChainFilterNode(SubgraphTraverseFilter(), null)
+ private var activeNode = root
+ private val chainEnds = mutableListOf<SelectFilter>()
+
+ fun withName(name: String) = matching { it.name == name }
+
+ fun withKind(kind: NodeKind) = matching{ it.kind == kind }
+
+ fun matching(block: (DocumentationNode) -> Boolean) {
+ attachFilterAndMakeActive(PredicateFilter(block))
+ }
+
+ fun subgraph() {
+ attachFilterAndMakeActive(SubgraphTraverseFilter())
+ }
+
+ fun subgraphOf(kind: RefKind) {
+ attachFilterAndMakeActive(DirectEdgeFilter(kind))
+ }
+
+ private fun attachFilterAndMakeActive(next: SelectFilter) {
+ activeNode = ChainFilterNode(next, activeNode)
+ }
+
+ private fun endChain() {
+ chainEnds += activeNode
+ }
+
+ fun build(): SelectFilter {
+ endChain()
+ return CombineFilterNode(chainEnds)
+ }
+}
+
+private class ChainFilterNode(val filter: SelectFilter, val previous: SelectFilter?): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return filter.select(previous?.select(roots) ?: roots)
+ }
+}
+
+private class CombineFilterNode(val previous: List<SelectFilter>): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return previous.asSequence().flatMap { it.select(roots) }
+ }
+}
+
+abstract class SelectFilter {
+ abstract fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode>
+}
+
+private class SubgraphTraverseFilter: SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ val visited = mutableSetOf<DocumentationNode>()
+ return roots.flatMap {
+ generateSequence(listOf(it)) { nodes ->
+ nodes.flatMap { it.allReferences() }
+ .map { it.to }
+ .filter { visited.add(it) }
+ .takeUnless { it.isEmpty() }
+ }
+ }.flatten()
+ }
+
+}
+
+private class PredicateFilter(val condition: (DocumentationNode) -> Boolean): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return roots.filter(condition)
+ }
+}
+
+private class DirectEdgeFilter(val kind: RefKind): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return roots.flatMap { it.references(kind).asSequence() }.map { it.to }
+ }
+}
+
+
+fun selectNodes(root: DocumentationNode, block: SelectBuilder.() -> Unit): List<DocumentationNode> {
+ val builder = SelectBuilder()
+ builder.apply(block)
+ return builder.build().select(sequenceOf(root)).toMutableSet().toList()
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index fa108b99..4f9af761 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -7,56 +7,71 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
+import org.jetbrains.dokka.Utilities.DokkaRunModule
+import org.jetbrains.kotlin.cli.common.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.ContentRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert
import org.junit.Assert.fail
import java.io.File
-fun verifyModel(vararg roots: ContentRoot,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
- verifier: (DocumentationModule) -> Unit) {
+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,
+ val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> = 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 = modelConfig.sourceLinks,
+ 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()) {
+fun appendDocumentation(
+ documentation: DocumentationModule,
+ dokkaConfiguration: DokkaConfiguration,
+ passConfiguration: DokkaConfiguration.PassConfiguration,
+ modelConfig: ModelConfig
+) {
val messageCollector = object : MessageCollector {
override fun clear() {
@@ -81,101 +96,167 @@ 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))
+ }
+ if (analysisPlatform == Platform.js) {
+ val kotlinStdlibJsRoot = PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery")
+ addClasspath(File(kotlinStdlibJsRoot))
+ }
+
+ if (analysisPlatform == Platform.common) {
+ // TODO: Feels hacky
+ val kotlinStdlibCommonRoot = ClassLoader.getSystemResource("kotlin/UInt.kotlin_metadata")
+ addClasspath(File(kotlinStdlibCommonRoot.file.replace("file:", "").replaceAfter(".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) {
- if (!File(source).exists()) {
- throw IllegalArgumentException("Can't find test data file $source")
+fun checkSourceExistsAndVerifyModel(
+ source: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ verifier: (DocumentationModule) -> Unit
+) {
+ require(File(source).exists()) {
+ "Cannot 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,
+ sourceLinks = modelConfig.sourceLinks,
+ analysisPlatform = modelConfig.analysisPlatform
+ ),
+
+ verifier = verifier
+ )
}
-fun verifyPackageMember(source: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- verifier: (DocumentationNode) -> Unit) {
- verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model ->
+fun verifyPackageMember(
+ source: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ verifier: (DocumentationNode) -> Unit
+) {
+ 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,
- verifier: (DocumentationModule) -> Unit) {
+fun verifyJavaModel(
+ source: String,
+ 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)
- }
- finally {
+ verifyModel(
+ ModelConfig(
+ roots = arrayOf(JavaSourceRoot(tempDir, null)),
+ withJdk = true,
+ withKotlinRuntime = modelConfig.withKotlinRuntime,
+ analysisPlatform = modelConfig.analysisPlatform
+ ),
+ verifier = verifier
+ )
+ } finally {
FileUtil.delete(tempDir)
}
}
-fun verifyJavaPackageMember(source: String,
- withKotlinRuntime: Boolean = false,
- verifier: (DocumentationNode) -> Unit) {
- verifyJavaModel(source, withKotlinRuntime) { model ->
+fun verifyJavaPackageMember(
+ source: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ verifier: (DocumentationNode) -> Unit
+) {
+ verifyJavaModel(source, modelConfig) { model ->
val pkg = model.members.single()
verifier(pkg.members.single())
}
}
-fun verifyOutput(roots: Array<ContentRoot>,
- 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)
+fun verifyOutput(
+ modelConfig: ModelConfig = ModelConfig(),
+ outputExtension: String,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
+ verifyModel(modelConfig) {
+ verifyModelOutput(it, outputExtension, modelConfig.roots.first().path, outputGenerator)
}
}
-fun verifyModelOutput(it: DocumentationModule,
- outputExtension: String,
- sourcePath: String,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+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,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
val output = StringBuilder()
outputGenerator(it, output)
val ext = outputExtension.removePrefix(".")
@@ -183,29 +264,13 @@ 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,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
- verifyJavaModel(path, withKotlinRuntime) { model ->
+fun verifyJavaOutput(
+ path: String,
+ outputExtension: String,
+ modelConfig: ModelConfig = ModelConfig(),
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
+ verifyJavaModel(path, modelConfig) { model ->
verifyModelOutput(model, outputExtension, path, outputGenerator)
}
}
@@ -215,7 +280,7 @@ fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) {
val expectedText = expectedFile.readText().replace("\r\n", "\n")
val actualText = output.replace("\r\n", "\n")
- if(expectedText != actualText)
+ if (expectedText != actualText)
throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath)
}
@@ -256,7 +321,18 @@ fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
is ContentBlock -> {
appendChildren(node)
}
- is ContentEmpty -> { /* nothing */ }
+ 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")
}
return this
@@ -270,7 +346,7 @@ fun ContentNode.toTestString(): String {
}
val ContentRoot.path: String
- get() = when(this) {
+ get() = when (this) {
is KotlinSourceRoot -> path
is JavaSourceRoot -> file.path
else -> throw UnsupportedOperationException()
diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt
index b90ab2bf..60de7d29 100644
--- a/core/src/test/kotlin/format/GFMFormatTest.kt
+++ b/core/src/test/kotlin/format/GFMFormatTest.kt
@@ -2,23 +2,26 @@ 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 : FileGeneratorTestCase() {
+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")
+ verifyGFMNodeByName("sample", "Foo", defaultModelConfig)
}
@Test
fun listInTableCell() {
- verifyGFMNodeByName("listInTableCell", "Foo")
+ verifyGFMNodeByName("listInTableCell", "Foo", defaultModelConfig)
}
- private fun verifyGFMNodeByName(fileName: String, name: String) {
- verifyOutput("testdata/format/gfm/$fileName.kt", ".md") { model, output ->
+ 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
@@ -26,3 +29,8 @@ class GFMFormatTest : FileGeneratorTestCase() {
}
}
}
+
+
+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 fd851414..60e29006 100644
--- a/core/src/test/kotlin/format/HtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/HtmlFormatTest.kt
@@ -1,180 +1,192 @@
package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.junit.Test
import java.io.File
-class HtmlFormatTest: FileGeneratorTestCase() {
+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 ->
+ verifyOutput("testdata/format/deprecated.kt", ".package.html", defaultModelConfig) { model, output ->
buildPagesAndReadInto(model.members, output)
}
- verifyOutput("testdata/format/deprecated.kt", ".class.html") { model, output ->
+ 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")
+ verifyHtmlNode("codeBlock", defaultModelConfig)
}
-
- @Test fun javaDeprecated() {
- verifyJavaHtmlNodes("javaDeprecated") { model ->
- model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" }
- }
- }
-
- @Test fun crossLanguageKotlinExtendsJava() {
- verifyOutput(arrayOf(KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt"),
- JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)),
- ".html") { model, output ->
- buildPagesAndReadInto(
- model.members.single().members.filter { it.name == "Bar" },
- output
- )
- }
- }
-
@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 ->
+ verifyOutput("testdata/format/$fileName.kt", ".html", modelConfig) { model, output ->
buildPagesAndReadInto(nodeFilter(model), output)
}
}
- private fun verifyJavaHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
- verifyJavaHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ protected fun verifyJavaHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) {
+ verifyJavaHtmlNodes(fileName, modelConfig) { model -> model.members.single().members }
}
- private fun verifyJavaHtmlNodes(fileName: String,
- withKotlinRuntime: Boolean = false,
- nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
- verifyJavaOutput("testdata/format/$fileName.java", ".html", withKotlinRuntime = withKotlinRuntime) { model, output ->
+ 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", false),
+ JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)
+ ),
+ analysisPlatform = analysisPlatform
+ ), ".html") { model, output ->
+ buildPagesAndReadInto(
+ model.members.single().members.filter { it.name == "Bar" },
+ output
+ )
+ }
+ }
+
+ @Test fun javaLinkTag() {
+ verifyJavaHtmlNode("javaLinkTag", defaultModelConfig)
+ }
+
+ @Test fun javaLinkTagWithLabel() {
+ verifyJavaHtmlNode("javaLinkTagWithLabel", defaultModelConfig)
+ }
+
+ @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
deleted file mode 100644
index 53c83de4..00000000
--- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.jetbrains.dokka.tests
-
-import org.jetbrains.dokka.*
-import org.junit.Ignore
-import org.junit.Test
-
-@Ignore
-class KotlinWebSiteFormatTest: FileGeneratorTestCase() {
- override val formatService = KotlinWebsiteFormatService(fileGenerator, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
-
- @Test fun sample() {
- verifyKWSNodeByName("sample", "foo")
- }
-
- @Test fun returnTag() {
- verifyKWSNodeByName("returnTag", "indexOf")
- }
-
- @Test fun overloadGroup() {
- verifyKWSNodeByName("overloadGroup", "magic")
- }
-
- @Test fun dataTags() {
- val module = buildMultiplePlatforms("dataTags")
- verifyMultiplatformPackage(module, "dataTags")
- }
-
- @Test fun dataTagsInGroupNode() {
- val path = "dataTagsInGroupNode"
- val module = buildMultiplePlatforms(path)
- verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output ->
- 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 ->
- 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,
- 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)
- return module
- }
-
- private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
- verifyModelOutput(module, ".package.md", "testdata/format/website/$path/multiplatform.kt") { model, output ->
- buildPagesAndReadInto(model.members, output)
- }
- }
-
-}
diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
index 8643d3e5..1d5cfa49 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
@@ -1,37 +1,39 @@
package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
+import org.junit.Ignore
import org.junit.Test
-class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() {
+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() {
@@ -51,25 +53,54 @@ class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() {
verifyMultiplatformPackage(module, path)
}
- private fun verifyKWSNodeByName(fileName: String, name: String) {
- verifyOutput("testdata/format/website-html/$fileName.kt", ".html", format = "kotlin-website-html") { model, output ->
+ 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
}
@@ -80,3 +111,11 @@ class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() {
}
}
+@Ignore
+class JsKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.js)
+
+@Ignore
+class JvmKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.jvm)
+
+@Ignore
+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
deleted file mode 100644
index 00630e00..00000000
--- a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.jetbrains.dokka.tests
-
-import org.junit.Ignore
-
-@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 })
-// }
-// }
-}
diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt
index e213315b..4796c17c 100644
--- a/core/src/test/kotlin/format/MarkdownFormatTest.kt
+++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt
@@ -3,49 +3,33 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
import org.junit.Test
-class MarkdownFormatTest: FileGeneratorTestCase() {
+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 ->
- buildPagesAndReadInto(model.members, output)
- }
- verifyOutput("testdata/format/extensions.kt", ".class.md") { model, output ->
- buildPagesAndReadInto(model.members.single().members, output)
- }
+ 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 ->
+ 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]
buildPagesAndReadInto(
enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" },
@@ -55,222 +39,187 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
}
@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 }
+ verifyMarkdownNodes("summarizeSignatures", defaultModelConfig) { model -> model.members }
}
- @Test fun summarizeSignaturesProperty() {
- verifyMarkdownNodes("summarizeSignaturesProperty") { model -> model.members }
+ @Test fun reifiedTypeParameter() {
+ verifyMarkdownNode("reifiedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
}
- @Test fun reifiedTypeParameter() {
- verifyMarkdownNode("reifiedTypeParameter", withKotlinRuntime = true)
+ @Test fun suspendInlineFunctionOrder() {
+ verifyMarkdownNode("suspendInlineFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
+ }
+
+ @Test fun inlineSuspendFunctionOrderChanged() {
+ verifyMarkdownNode("inlineSuspendFunction", 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() {
@@ -307,15 +256,29 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
@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)
}
@@ -351,106 +314,109 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
}
@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")
- }
-
- @Test fun enumRef() {
- verifyMarkdownNode("enumRef")
- }
-
- @Test fun inheritedLink() {
- val filePath = "testdata/format/inheritedLink"
- verifyOutput(
- arrayOf(
- contentRootFromPath("$filePath.kt"),
- contentRootFromPath("$filePath.1.kt")
- ),
- ".md",
- withJdk = true,
- withKotlinRuntime = true,
- includeNonPublic = false
- ) { model, output ->
- buildPagesAndReadInto(model.members.single { it.name == "p2" }.members.single().members, output)
- }
+ 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
}
@@ -470,52 +436,49 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
}
@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 ->
+ 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 ->
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 ->
+ 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")
@@ -523,4 +486,126 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
nodesWithName
}
}
+
+ @Test fun nullableTypeParameterFunction() {
+ verifyMarkdownNode("nullableTypeParameterFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true))
+ }
}
+
+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() {
+ verifyJavaMarkdownNodes("javaCodeInParam", defaultModelConfig) {
+ selectNodes(it) {
+ subgraphOf(RefKind.Member)
+ withKind(NodeKind.Function)
+ }
+ }
+ }
+
+ @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 704f7b99..3ff5f123 100644
--- a/core/src/test/kotlin/format/PackageDocsTest.kt
+++ b/core/src/test/kotlin/format/PackageDocsTest.kt
@@ -1,19 +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.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
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
@@ -22,13 +49,13 @@ class PackageDocsTest {
@Test fun testReferenceLinksInPackageDocs() {
val mockLinkResolver = mock<DeclarationLinkResolver> {
- val exampleCom = "http://example.com"
+ val exampleCom = "https://example.com"
on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) }
}
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")
diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt
index e8cb4d9c..da5acd6e 100644
--- a/core/src/test/kotlin/issues/IssuesTest.kt
+++ b/core/src/test/kotlin/issues/IssuesTest.kt
@@ -2,16 +2,19 @@ package issues
import org.jetbrains.dokka.DocumentationNode
import org.jetbrains.dokka.NodeKind
-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
@@ -25,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 aaeb7d00..1c4dd258 100644
--- a/core/src/test/kotlin/javadoc/JavadocTest.kt
+++ b/core/src/test/kotlin/javadoc/JavadocTest.kt
@@ -3,14 +3,19 @@ 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
+import java.lang.reflect.Modifier.*
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 +31,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 +44,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 +55,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,20 +68,26 @@ 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())
val member = classDoc.methods().find { it.name() == "main" }!!
val paramType = member.parameters()[0].type()
assertNull(paramType.asParameterizedType())
- assertEquals("String", paramType.typeName())
+ assertEquals("String[]", paramType.typeName())
assertEquals("String", paramType.asClassDoc().name())
}
}
@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 +97,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 +111,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 +123,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 +137,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"))
@@ -127,7 +156,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())
@@ -137,7 +169,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("""
@@ -154,7 +189,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,13 +197,135 @@ class JavadocTest {
}
}
+ @Test
+ fun testVararg() {
+ verifyJavadoc("testdata/javadoc/vararg.kt") { doc ->
+ val classDoc = doc.classNamed("VarargKt")!!
+ val methods = classDoc.methods()
+ methods.single { it.name() == "vararg" }.let { method ->
+ assertTrue(method.isVarArgs)
+ assertEquals("int", method.parameters().last().typeName())
+ }
+ methods.single { it.name() == "varargInMiddle" }.let { method ->
+ assertFalse(method.isVarArgs)
+ assertEquals("int[]", method.parameters()[1].typeName())
+ }
+ }
+ }
+
+ @Test
+ fun shouldHaveValidVisibilityModifiers() {
+ verifyJavadoc("testdata/javadoc/visibilityModifiers.kt", ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)) { doc ->
+ val classDoc = doc.classNamed("foo.Apple")!!
+ val methods = classDoc.methods()
+
+ val getName = methods[0]
+ val setName = methods[1]
+ val getWeight = methods[2]
+ val setWeight = methods[3]
+ val getRating = methods[4]
+ val setRating = methods[5]
+ val getCode = methods[6]
+ val color = classDoc.fields()[3]
+ val code = classDoc.fields()[4]
+
+ assertTrue(getName.isProtected)
+ assertEquals(PROTECTED, getName.modifierSpecifier())
+ assertTrue(setName.isProtected)
+ assertEquals(PROTECTED, setName.modifierSpecifier())
+
+ assertTrue(getWeight.isPublic)
+ assertEquals(PUBLIC, getWeight.modifierSpecifier())
+ assertTrue(setWeight.isPublic)
+ assertEquals(PUBLIC, setWeight.modifierSpecifier())
+
+ assertTrue(getRating.isPublic)
+ assertEquals(PUBLIC, getRating.modifierSpecifier())
+ assertTrue(setRating.isPublic)
+ assertEquals(PUBLIC, setRating.modifierSpecifier())
+
+ assertTrue(getCode.isPublic)
+ assertEquals(PUBLIC or STATIC, getCode.modifierSpecifier())
+
+ assertEquals(methods.size, 7)
+
+ assertTrue(color.isPrivate)
+ assertEquals(PRIVATE, color.modifierSpecifier())
+
+ assertTrue(code.isPrivate)
+ assertTrue(code.isStatic)
+ assertEquals(PRIVATE or STATIC, code.modifierSpecifier())
+ }
+ }
+
+ @Test
+ fun shouldNotHaveDuplicatedConstructorParameters() {
+ verifyJavadoc("testdata/javadoc/constructorParameters.kt") { doc ->
+ val classDoc = doc.classNamed("bar.Banana")!!
+ val paramTags = classDoc.constructors()[0].paramTags()
+
+ assertEquals(3, paramTags.size)
+ }
+ }
+
+ @Test fun shouldHaveAllFunctionMarkedAsDeprecated() {
+ verifyJavadoc("testdata/javadoc/deprecated.java") { doc ->
+ val classDoc = doc.classNamed("bar.Banana")!!
+
+ classDoc.methods().forEach { method ->
+ assertTrue(method.tags().any { it.kind() == "deprecated" })
+ }
+ }
+ }
+
+ @Test
+ fun testDefaultNoArgConstructor() {
+ verifyJavadoc("testdata/javadoc/defaultNoArgConstructor.kt") { doc ->
+ val classDoc = doc.classNamed("foo.Peach")!!
+ assertTrue(classDoc.constructors()[0].tags()[2].text() == "print peach")
+ }
+ }
+
+ @Test
+ fun testNoArgConstructor() {
+ verifyJavadoc("testdata/javadoc/noArgConstructor.kt") { doc ->
+ val classDoc = doc.classNamed("foo.Plum")!!
+ assertTrue(classDoc.constructors()[0].tags()[2].text() == "print plum")
+ }
+ }
+
+ @Test
+ fun testArgumentReference() {
+ verifyJavadoc("testdata/javadoc/argumentReference.kt") { doc ->
+ val classDoc = doc.classNamed("ArgumentReferenceKt")!!
+ val method = classDoc.methods().first()
+ val tag = method.seeTags().first()
+ assertEquals("argNamedError", tag.referencedMemberName())
+ assertEquals("error", tag.label())
+ }
+ }
+
+ @Test
+ fun functionParameters() {
+ verifyJavadoc("testdata/javadoc/functionParameters.java") { doc ->
+ val tags = doc.classNamed("bar.Foo")!!.methods().first().paramTags()
+ assertEquals((tags.first() as ParamTagAdapter).content.size, 1)
+ assertEquals((tags[1] as ParamTagAdapter).content.size, 1)
+ }
+ }
+
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..35ec1d09 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,15 +248,18 @@ 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)
+ assertEquals("1.1", sinceKotlin)
}
}
}
@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 8a3f69d8..4c6bfb74 100644
--- a/core/src/test/kotlin/model/FunctionTest.kt
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -2,13 +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
-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)
@@ -21,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)
@@ -53,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)
@@ -77,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)
@@ -117,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)
@@ -142,32 +146,48 @@ 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 suspendFunction() {
+ verifyPackageMember("testdata/functions/suspendFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }
+ assertTrue("suspend" in modifiers)
+ }
+ }
+
+ @Test fun suspendInlineFunctionOrder() {
+ verifyPackageMember("testdata/functions/suspendInlineFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter {
+ it == "suspend" || it == "inline"
+ }
+
+ assertEquals(listOf("suspend", "inline"), modifiers)
+ }
+ }
+
+ @Test fun inlineSuspendFunctionOrderChanged() {
+ verifyPackageMember("testdata/functions/inlineSuspendFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter {
+ it == "suspend" || it == "inline"
+ }
+
+ assertEquals(listOf("suspend", "inline"), 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())
@@ -182,7 +202,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)
@@ -191,7 +211,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]) {
@@ -213,7 +236,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)
@@ -227,10 +250,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)
+ assertEquals("1.1", sinceKotlin)
+ }
+ }
+ }
+}
+
+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..da9da624 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:Int,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 })
}
@@ -150,17 +152,17 @@ public class JavaTest {
/**
* `@suppress` not supported in Java!
*
- * [Proposed tags](http://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html)
+ * [Proposed tags](https://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html)
* 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..8249dd0f 100644
--- a/core/src/test/kotlin/model/KotlinAsJavaTest.kt
+++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
@@ -2,8 +2,11 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.DocumentationModule
import org.jetbrains.dokka.NodeKind
-import org.junit.Test
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.RefKind
+import org.junit.Assert
import org.junit.Assert.assertEquals
+import org.junit.Test
class KotlinAsJavaTest {
@Test fun function() {
@@ -27,14 +30,36 @@ class KotlinAsJavaTest {
assertEquals("doc", getter.content.summary.toTestString())
}
}
+
+
+ @Test fun constants() {
+ verifyModelAsJava("testdata/java/constants.java") { cls ->
+ selectNodes(cls) {
+ subgraphOf(RefKind.Member)
+ matching { it.name == "constStr" || it.name == "refConst" }
+ }.forEach {
+ assertEquals("In $it", "\"some value\"", it.detailOrNull(NodeKind.Value)?.name)
+ }
+ val nullConstNode = selectNodes(cls) {
+ subgraphOf(RefKind.Member)
+ withName("nullConst")
+ }.single()
+
+ Assert.assertNull(nullConstNode.detailOrNull(NodeKind.Value))
+ }
+ }
}
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..e20e6afa 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.kotlin.config.KotlinSourceRoot
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.cli.common.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", false),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false)
+ ),
+ 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", false),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt", false)
+ ),
+ 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", false)),
+ 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", false)),
+ 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..9f070862 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)
+ assertEquals("1.1", sinceKotlin)
}
}
}
}
+
+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("Strictfp", 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/SourceLinksErrorTest.kt b/core/src/test/kotlin/model/SourceLinksErrorTest.kt
new file mode 100644
index 00000000..9812569d
--- /dev/null
+++ b/core/src/test/kotlin/model/SourceLinksErrorTest.kt
@@ -0,0 +1,35 @@
+package org.jetbrains.dokka.tests.model
+
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.tests.ModelConfig
+import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel
+import org.junit.Assert
+import org.junit.Test
+import java.io.File
+
+class SourceLinksErrorTest {
+
+ @Test
+ fun absolutePath_notMatching() {
+ val sourceLink = SourceLinkDefinitionImpl(File("testdata/nonExisting").absolutePath, "http://...", null)
+ verifyNoSourceUrl(sourceLink)
+ }
+
+ @Test
+ fun relativePath_notMatching() {
+ val sourceLink = SourceLinkDefinitionImpl("testdata/nonExisting", "http://...", null)
+ verifyNoSourceUrl(sourceLink)
+ }
+
+ private fun verifyNoSourceUrl(sourceLink: SourceLinkDefinitionImpl) {
+ checkSourceExistsAndVerifyModel("testdata/sourceLinks/dummy.kt", ModelConfig(sourceLinks = listOf(sourceLink))) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals("foo", name)
+ Assert.assertEquals(NodeKind.Function, kind)
+ Assert.assertTrue("should not have source urls", details(NodeKind.SourceUrl).isEmpty())
+ }
+ }
+ }
+}
+
diff --git a/core/src/test/kotlin/model/SourceLinksTest.kt b/core/src/test/kotlin/model/SourceLinksTest.kt
new file mode 100644
index 00000000..a4ba870c
--- /dev/null
+++ b/core/src/test/kotlin/model/SourceLinksTest.kt
@@ -0,0 +1,75 @@
+package org.jetbrains.dokka.tests.model
+
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.tests.ModelConfig
+import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.io.File
+
+@RunWith(Parameterized::class)
+class SourceLinksTest(
+ private val srcLink: String,
+ private val url: String,
+ private val lineSuffix: String?,
+ private val expectedUrl: String
+) {
+
+ @Test
+ fun test() {
+ val link = if(srcLink.contains(sourceLinks)){
+ srcLink.substringBeforeLast(sourceLinks) + sourceLinks
+ } else {
+ srcLink.substringBeforeLast(testdata) + testdata
+ }
+ val sourceLink = SourceLinkDefinitionImpl(link, url, lineSuffix)
+
+ checkSourceExistsAndVerifyModel(filePath, ModelConfig(sourceLinks = listOf(sourceLink))) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals("foo", name)
+ Assert.assertEquals(NodeKind.Function, kind)
+ Assert.assertEquals(expectedUrl, details(NodeKind.SourceUrl).single().name)
+ }
+ }
+ }
+
+ companion object {
+ private const val testdata = "testdata"
+ private const val sourceLinks = "sourceLinks"
+ private const val dummy = "dummy.kt"
+ private const val pathSuffix = "$sourceLinks/$dummy"
+ private const val filePath = "$testdata/$pathSuffix"
+ private const val url = "https://example.com"
+
+ @Parameterized.Parameters(name = "{index}: {0}, {1}, {2} = {3}")
+ @JvmStatic
+ fun data(): Collection<Array<String?>> {
+ val longestPath = File(testdata).absolutePath.removeSuffix("/") + "/../$testdata/"
+ val maxLength = longestPath.length
+ val list = listOf(
+ arrayOf(File(testdata).absolutePath.removeSuffix("/"), "$url/$pathSuffix"),
+ arrayOf(File("$testdata/$sourceLinks").absolutePath.removeSuffix("/") + "/", "$url/$dummy"),
+ arrayOf(longestPath, "$url/$pathSuffix"),
+
+ arrayOf(testdata, "$url/$pathSuffix"),
+ arrayOf("./$testdata", "$url/$pathSuffix"),
+ arrayOf("../core/$testdata", "$url/$pathSuffix"),
+ arrayOf("$testdata/$sourceLinks", "$url/$dummy"),
+ arrayOf("./$testdata/../$testdata/$sourceLinks", "$url/$dummy")
+ )
+
+ return list.map { arrayOf(it[0].padEnd(maxLength, '_'), url, null, it[1]) } +
+ listOf(
+ // check that it also works if url ends with /
+ arrayOf((File(testdata).absolutePath.removeSuffix("/") + "/").padEnd(maxLength, '_'), "$url/", null, "$url/$pathSuffix"),
+ // check if line suffix work
+ arrayOf<String?>("../core/../core/./$testdata/$sourceLinks/".padEnd(maxLength, '_'), "$url/", "#L", "$url/$dummy#L4")
+ )
+ }
+ }
+
+}
+
diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt
index c653ac83..71976dc3 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,9 +123,9 @@ 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)
+ assertEquals("1.1", sinceKotlin)
}
}
}
diff --git a/core/testdata/format/JavaSupertype.html b/core/testdata/format/JavaSupertype.html
index 27b8e5d0..93346a4a 100644
--- a/core/testdata/format/JavaSupertype.html
+++ b/core/testdata/format/JavaSupertype.html
@@ -4,17 +4,16 @@
<title>JavaSupertype.Bar - test</title>
</HEAD>
<BODY>
-<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">JavaSupertype</a>&nbsp;/&nbsp;<a href="./index.html">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="../-foo/index.html"><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">JavaSupertype.Foo</span></a></code>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -25,10 +24,9 @@
<tbody>
<tr>
<td>
-<p><a href="return-foo.html">returnFoo</a></p>
-</td>
+<a href="return-foo.html">returnFoo</a></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="../-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>
+<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">JavaSupertype.Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span></code></td>
</tr>
</tbody>
</table>
diff --git a/core/testdata/format/accessor.md b/core/testdata/format/accessor.md
index 190e8538..9bb2c4ed 100644
--- a/core/testdata/format/accessor.md
+++ b/core/testdata/format/accessor.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](index.md) / [x](./x.md)
+[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 aa622eac..36d8aba9 100644
--- a/core/testdata/format/annotatedTypeParameter.md
+++ b/core/testdata/format/annotatedTypeParameter.md
@@ -1,4 +1,4 @@
-[test](index.md) / [containsAll](./contains-all.md)
+[test](../index.md) / [containsAll](./contains-all.md)
# containsAll
diff --git a/core/testdata/format/annotationClass.md b/core/testdata/format/annotationClass.md
index 55fda40c..9ce4aea9 100644
--- a/core/testdata/format/annotationClass.md
+++ b/core/testdata/format/annotationClass.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [fancy](./index.md)
+[test](../../index.md) / [fancy](./index.md)
# fancy
diff --git a/core/testdata/format/annotationClass.package.md b/core/testdata/format/annotationClass.package.md
index c8aff7a3..25e0d77c 100644
--- a/core/testdata/format/annotationClass.package.md
+++ b/core/testdata/format/annotationClass.package.md
@@ -1,4 +1,4 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
diff --git a/core/testdata/format/annotationParams.md b/core/testdata/format/annotationParams.md
index cfa3b822..7388c83a 100644
--- a/core/testdata/format/annotationParams.md
+++ b/core/testdata/format/annotationParams.md
@@ -1,4 +1,4 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/annotations.md b/core/testdata/format/annotations.md
index 2e1604d0..07c22103 100644
--- a/core/testdata/format/annotations.md
+++ b/core/testdata/format/annotations.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/arrayAverage.md b/core/testdata/format/arrayAverage.md
index 2c6927d6..5867c1be 100644
--- a/core/testdata/format/arrayAverage.md
+++ b/core/testdata/format/arrayAverage.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [XArray](./index.md)
+[test](../../index.md) / [XArray](./index.md)
# XArray
diff --git a/core/testdata/format/backtickInCodeBlock.md b/core/testdata/format/backtickInCodeBlock.md
index 830539ac..d2267204 100644
--- a/core/testdata/format/backtickInCodeBlock.md
+++ b/core/testdata/format/backtickInCodeBlock.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html
index f0351d72..9db7d4f0 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="index.html">test</a>&nbsp;/&nbsp;<a href="./u.html">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 956f8954..1a3ef10e 100644
--- a/core/testdata/format/blankLineInsideCodeBlock.md
+++ b/core/testdata/format/blankLineInsideCodeBlock.md
@@ -1,4 +1,4 @@
-[test](index.md) / [u](./u.md)
+[test](../index.md) / [u](./u.md)
# u
diff --git a/core/testdata/format/bracket.html b/core/testdata/format/bracket.html
index 01aaaf04..5ba19b73 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="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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 c598a73e..db47c05a 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="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">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 88feea5e..95fcbf6b 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="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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,8 +13,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Klass</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -25,8 +24,7 @@
<tbody>
<tr>
<td>
-<p><a href="x.html">x</a></p>
-</td>
+<a href="x.html">x</a></td>
<td>
<code><span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></code></td>
</tr>
@@ -37,8 +35,7 @@
<tbody>
<tr>
<td>
-<p><a href="foo.html">foo</a></p>
-</td>
+<a href="foo.html">foo</a></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>
</tr>
diff --git a/core/testdata/format/classWithCompanionObject.md b/core/testdata/format/classWithCompanionObject.md
index 40f605be..850e51ec 100644
--- a/core/testdata/format/classWithCompanionObject.md
+++ b/core/testdata/format/classWithCompanionObject.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Klass](./index.md)
+[test](../../index.md) / [Klass](./index.md)
# Klass
diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html
index 1e07ff09..a877e8c6 100644
--- a/core/testdata/format/codeBlock.html
+++ b/core/testdata/format/codeBlock.html
@@ -1,11 +1,11 @@
-<!-- File: test/-throws/index.html -->
+<!-- File: test/--root--/-throws/index.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
<title>Throws - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
@@ -19,8 +19,7 @@ fun readFile(name: String): String {...}
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></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>
@@ -30,14 +29,14 @@ fun readFile(name: String): String {...}
</table>
</BODY>
</HTML>
-<!-- File: test/-it-does-some-obfuscated-thing/index.html -->
+<!-- File: test/--root--/-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/>
+<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>
@@ -49,8 +48,7 @@ fun readFile(name: String): String {...}
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">ItDoesSomeObfuscatedThing</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Check output of</p>
diff --git a/core/testdata/format/codeBlock.md b/core/testdata/format/codeBlock.md
index d183a8ba..b6163a37 100644
--- a/core/testdata/format/codeBlock.md
+++ b/core/testdata/format/codeBlock.md
@@ -1,5 +1,5 @@
-<!-- File: test/-throws/index.md -->
-[test](../index.md) / [Throws](./index.md)
+<!-- File: test/--root--/-throws/index.md -->
+[test](../../index.md) / [Throws](./index.md)
# Throws
@@ -18,8 +18,8 @@ fun readFile(name: String): String {...}
| [&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)
+<!-- File: test/--root--/-it-does-some-obfuscated-thing/index.md -->
+[test](../../index.md) / [ItDoesSomeObfuscatedThing](./index.md)
# ItDoesSomeObfuscatedThing
diff --git a/core/testdata/format/codeBlockNoHtmlEscape.md b/core/testdata/format/codeBlockNoHtmlEscape.md
index 548dac4f..a54fc25d 100644
--- a/core/testdata/format/codeBlockNoHtmlEscape.md
+++ b/core/testdata/format/codeBlockNoHtmlEscape.md
@@ -1,4 +1,4 @@
-[test](index.md) / [hackTheArithmetic](./hack-the-arithmetic.md)
+[test](../index.md) / [hackTheArithmetic](./hack-the-arithmetic.md)
# hackTheArithmetic
diff --git a/core/testdata/format/codeSpan.html b/core/testdata/format/codeSpan.html
index bfe93f36..28631dbf 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="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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 aac7b3e6..ead95193 100644
--- a/core/testdata/format/companionImplements.md
+++ b/core/testdata/format/companionImplements.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/companionObjectExtension.md b/core/testdata/format/companionObjectExtension.md
index c0e268e6..f62c856e 100644
--- a/core/testdata/format/companionObjectExtension.md
+++ b/core/testdata/format/companionObjectExtension.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
index 8842969e..c413595b 100644
--- a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
@@ -14,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
@@ -28,8 +27,7 @@
<tbody>
<tr>
<td>
-<p><a href="../-foo/xyzzy.html">xyzzy</a></p>
-</td>
+<a href="../-foo/xyzzy.html">xyzzy</a></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>
</tr>
diff --git a/core/testdata/format/deprecated.class.html b/core/testdata/format/deprecated.class.html
index 540060d1..d0e27e88 100644
--- a/core/testdata/format/deprecated.class.html
+++ b/core/testdata/format/deprecated.class.html
@@ -1,11 +1,11 @@
-<!-- File: test/-c/index.html -->
+<!-- File: test/--root--/-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/>
+<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/>
@@ -16,8 +16,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -25,14 +24,14 @@
</table>
</BODY>
</HTML>
-<!-- File: test/f.html -->
+<!-- File: test/--root--/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/>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
<br/>
<h1>f</h1>
<a name="$f()"></a>
@@ -41,14 +40,14 @@
<br/>
</BODY>
</HTML>
-<!-- File: test/p.html -->
+<!-- File: test/--root--/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/>
+<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 3506de61..a02783b6 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="./index.html">test</a><br/>
+<a href="../index.html">test</a><br/>
<br/>
<h2>Package &lt;root&gt;</h2>
<h3>Types</h3>
@@ -12,8 +12,7 @@
<tbody>
<tr>
<td>
-<p><a href="-c/index.html">C</a></p>
-</td>
+<a href="-c/index.html">C</a></td>
<td>
<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td>
</tr>
@@ -24,8 +23,7 @@
<tbody>
<tr>
<td>
-<p><a href="p.html">p</a></p>
-</td>
+<a href="p.html">p</a></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>
</tr>
@@ -36,8 +34,7 @@
<tbody>
<tr>
<td>
-<p><a href="f.html">f</a></p>
-</td>
+<a href="f.html">f</a></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>
</tr>
diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md
index 382cf973..ed81d66c 100644
--- a/core/testdata/format/dynamicExtension.md
+++ b/core/testdata/format/dynamicExtension.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md
index 07a1d103..cfb8fd25 100644
--- a/core/testdata/format/dynamicType.md
+++ b/core/testdata/format/dynamicType.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/emptyDescription.md b/core/testdata/format/emptyDescription.md
index 5d56d968..3c14ab75 100644
--- a/core/testdata/format/emptyDescription.md
+++ b/core/testdata/format/emptyDescription.md
@@ -1,4 +1,4 @@
-[test](index.md) / [fn](./fn.md)
+[test](../index.md) / [fn](./fn.md)
# fn
diff --git a/core/testdata/format/entity.html b/core/testdata/format/entity.html
index 639f2903..2068009e 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="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Copyright &copy; JetBrains 2015 &#x22;</p>
diff --git a/core/testdata/format/enumClass.md b/core/testdata/format/enumClass.md
index 50cccfbb..15070049 100644
--- a/core/testdata/format/enumClass.md
+++ b/core/testdata/format/enumClass.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [InlineOption](./index.md)
+[test](../../index.md) / [InlineOption](./index.md)
# InlineOption
diff --git a/core/testdata/format/enumClass.value.md b/core/testdata/format/enumClass.value.md
index 150016cc..9000a1c4 100644
--- a/core/testdata/format/enumClass.value.md
+++ b/core/testdata/format/enumClass.value.md
@@ -1,4 +1,4 @@
-[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)
+[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.md b/core/testdata/format/enumRef.md
index 8b2a6650..5f6b2f64 100644
--- a/core/testdata/format/enumRef.md
+++ b/core/testdata/format/enumRef.md
@@ -1,8 +1,8 @@
-[test](index.md) / [f](./f.md)
+[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)
+[java.math.RoundingMode.UP](https://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 1e928bb6..44ad6705 100644
--- a/core/testdata/format/exceptionClass.md
+++ b/core/testdata/format/exceptionClass.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [MyException](./index.md)
+[test](../../index.md) / [MyException](./index.md)
# MyException
diff --git a/core/testdata/format/exceptionClass.package.md b/core/testdata/format/exceptionClass.package.md
index 8716df0a..13b1c7db 100644
--- a/core/testdata/format/exceptionClass.package.md
+++ b/core/testdata/format/exceptionClass.package.md
@@ -1,4 +1,4 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
diff --git a/core/testdata/format/exclInCodeBlock.md b/core/testdata/format/exclInCodeBlock.md
index d665c415..0302570e 100644
--- a/core/testdata/format/exclInCodeBlock.md
+++ b/core/testdata/format/exclInCodeBlock.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/extensionFunctionParameter.md b/core/testdata/format/extensionFunctionParameter.md
index e1e85824..465fe358 100644
--- a/core/testdata/format/extensionFunctionParameter.md
+++ b/core/testdata/format/extensionFunctionParameter.md
@@ -1,4 +1,4 @@
-[test](index.md) / [apply](./apply.md)
+[test](../index.md) / [apply](./apply.md)
# apply
diff --git a/core/testdata/format/extensionScope.md b/core/testdata/format/extensionScope.md
index ea765bd5..2921346a 100644
--- a/core/testdata/format/extensionScope.md
+++ b/core/testdata/format/extensionScope.md
@@ -1,4 +1,4 @@
-[test](index.md) / [test](./test.md)
+[test](../index.md) / [test](./test.md)
# test
diff --git a/core/testdata/format/extensionWithDocumentedReceiver.md b/core/testdata/format/extensionWithDocumentedReceiver.md
index 0679ac8f..4cee0a04 100644
--- a/core/testdata/format/extensionWithDocumentedReceiver.md
+++ b/core/testdata/format/extensionWithDocumentedReceiver.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [kotlin.String](index.md) / [fn](./fn.md)
+[test](../../index.md) / [kotlin.String](index.md) / [fn](./fn.md)
# fn
diff --git a/core/testdata/format/externalReferenceLink.kt b/core/testdata/format/externalReferenceLink.kt
index 4ca0ee21..775b2e66 100644
--- a/core/testdata/format/externalReferenceLink.kt
+++ b/core/testdata/format/externalReferenceLink.kt
@@ -3,7 +3,7 @@
*
* Sure, it is [example.com]
*
- * [example.com]: http://example.com
+ * [example.com]: https://example.com
*/
fun a() {
diff --git a/core/testdata/format/externalReferenceLink.md b/core/testdata/format/externalReferenceLink.md
index 38ffde78..73840e76 100644
--- a/core/testdata/format/externalReferenceLink.md
+++ b/core/testdata/format/externalReferenceLink.md
@@ -1,10 +1,10 @@
-[test](index.md) / [a](./a.md)
+[test](../index.md) / [a](./a.md)
# a
`fun a(): Unit`
-It is link to [example site](http://example.com)
+It is link to [example site](https://example.com)
-Sure, it is [example.com](http://example.com)
+Sure, it is [example.com](https://example.com)
diff --git a/core/testdata/format/functionWithDefaultParameter.md b/core/testdata/format/functionWithDefaultParameter.md
index 05f7fbe6..535ea18d 100644
--- a/core/testdata/format/functionWithDefaultParameter.md
+++ b/core/testdata/format/functionWithDefaultParameter.md
@@ -1,4 +1,4 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.html b/core/testdata/format/functionalTypeWithNamedParameters.html
index 83d03d8f..c0b0306b 100644
--- a/core/testdata/format/functionalTypeWithNamedParameters.html
+++ b/core/testdata/format/functionalTypeWithNamedParameters.html
@@ -1,11 +1,11 @@
-<!-- File: test/-a/index.html -->
+<!-- File: test/--root--/-a/index.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
<title>A - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
@@ -14,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">A</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -23,14 +22,14 @@
</table>
</BODY>
</HTML>
-<!-- File: test/-b/index.html -->
+<!-- File: test/--root--/-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/>
+<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>
@@ -39,8 +38,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">B</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -48,14 +46,14 @@
</table>
</BODY>
</HTML>
-<!-- File: test/-c/index.html -->
+<!-- File: test/--root--/-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/>
+<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>
@@ -64,8 +62,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -73,28 +70,28 @@
</table>
</BODY>
</HTML>
-<!-- File: test/f.html -->
+<!-- File: test/--root--/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/>
+<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="-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 -->
+<!-- File: test/--root--/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/>
+<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>
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.md b/core/testdata/format/functionalTypeWithNamedParameters.md
index 4e78694c..e1c9681a 100644
--- a/core/testdata/format/functionalTypeWithNamedParameters.md
+++ b/core/testdata/format/functionalTypeWithNamedParameters.md
@@ -1,5 +1,5 @@
-<!-- File: test/-a/index.md -->
-[test](../index.md) / [A](./index.md)
+<!-- File: test/--root--/-a/index.md -->
+[test](../../index.md) / [A](./index.md)
# A
@@ -9,8 +9,8 @@
| [&lt;init&gt;](-init-.md) | `A()` |
-<!-- File: test/-b/index.md -->
-[test](../index.md) / [B](./index.md)
+<!-- File: test/--root--/-b/index.md -->
+[test](../../index.md) / [B](./index.md)
# B
@@ -20,8 +20,8 @@
| [&lt;init&gt;](-init-.md) | `B()` |
-<!-- File: test/-c/index.md -->
-[test](../index.md) / [C](./index.md)
+<!-- File: test/--root--/-c/index.md -->
+[test](../../index.md) / [C](./index.md)
# C
@@ -31,14 +31,14 @@
| [&lt;init&gt;](-init-.md) | `C()` |
-<!-- File: test/f.md -->
-[test](index.md) / [f](./f.md)
+<!-- File: test/--root--/f.md -->
+[test](../index.md) / [f](./f.md)
# f
`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)
+<!-- File: test/--root--/accept-function-type-with-named-arguments.md -->
+[test](../index.md) / [acceptFunctionTypeWithNamedArguments](./accept-function-type-with-named-arguments.md)
# acceptFunctionTypeWithNamedArguments
diff --git a/core/testdata/format/genericInheritedExtensions.md b/core/testdata/format/genericInheritedExtensions.md
index 8d0e316f..90397676 100644
--- a/core/testdata/format/genericInheritedExtensions.md
+++ b/core/testdata/format/genericInheritedExtensions.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/gfm/listInTableCell.md b/core/testdata/format/gfm/listInTableCell.md
index 359ad916..59fba5c4 100644
--- a/core/testdata/format/gfm/listInTableCell.md
+++ b/core/testdata/format/gfm/listInTableCell.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/gfm/sample.md b/core/testdata/format/gfm/sample.md
index 2b082296..a720881f 100644
--- a/core/testdata/format/gfm/sample.md
+++ b/core/testdata/format/gfm/sample.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/htmlEscaping.html b/core/testdata/format/htmlEscaping.html
index bd64454d..435c3149 100644
--- a/core/testdata/format/htmlEscaping.html
+++ b/core/testdata/format/htmlEscaping.html
@@ -4,7 +4,7 @@
<title>x - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./x.html">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>
diff --git a/core/testdata/format/inapplicableExtensionFunctions.md b/core/testdata/format/inapplicableExtensionFunctions.md
index 08fc2739..35124b3d 100644
--- a/core/testdata/format/inapplicableExtensionFunctions.md
+++ b/core/testdata/format/inapplicableExtensionFunctions.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/indentedCodeBlock.html b/core/testdata/format/indentedCodeBlock.html
index 86c129fb..5b4677d1 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="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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 77b0630a..7542ddb9 100644
--- a/core/testdata/format/indentedCodeBlock.md
+++ b/core/testdata/format/indentedCodeBlock.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/inheritedCompanionObjectProperties.md b/core/testdata/format/inheritedCompanionObjectProperties.md
index ab8f0aa5..700570cc 100644
--- a/core/testdata/format/inheritedCompanionObjectProperties.md
+++ b/core/testdata/format/inheritedCompanionObjectProperties.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
diff --git a/core/testdata/format/inheritedExtensions.md b/core/testdata/format/inheritedExtensions.md
index 97a73666..3b105e28 100644
--- a/core/testdata/format/inheritedExtensions.md
+++ b/core/testdata/format/inheritedExtensions.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/inheritedLink.md b/core/testdata/format/inheritedLink.md
index e5af326c..aec07a75 100644
--- a/core/testdata/format/inheritedLink.md
+++ b/core/testdata/format/inheritedLink.md
@@ -13,5 +13,5 @@
Overrides [Foo.sayHello](../../p1/-foo/say-hello.md)
-Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
+Says hello - [LinkedList](https://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 334df360..0bf1a5f6 100644
--- a/core/testdata/format/inheritedMembers.md
+++ b/core/testdata/format/inheritedMembers.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/inlineSuspendFunction.kt b/core/testdata/format/inlineSuspendFunction.kt
new file mode 100644
index 00000000..02b3094b
--- /dev/null
+++ b/core/testdata/format/inlineSuspendFunction.kt
@@ -0,0 +1,6 @@
+/**
+ * returns 1
+ */
+inline suspend fun foo(a: () -> String): Int {
+ 1
+}
diff --git a/core/testdata/format/inlineSuspendFunction.md b/core/testdata/format/inlineSuspendFunction.md
new file mode 100644
index 00000000..e109039c
--- /dev/null
+++ b/core/testdata/format/inlineSuspendFunction.md
@@ -0,0 +1,8 @@
+[test](../index.md) / [foo](./foo.md)
+
+# foo
+
+`suspend inline fun foo(a: () -> String): Int`
+
+returns 1
+
diff --git a/core/testdata/format/javaCodeInParam.java b/core/testdata/format/javaCodeInParam.java
index 73025fcc..0d1607ba 100644
--- a/core/testdata/format/javaCodeInParam.java
+++ b/core/testdata/format/javaCodeInParam.java
@@ -1,5 +1,7 @@
-/**
- * @param T this is {@code some code} and other text
- */
-class C<T> {
+class C {
+
+ /**
+ * @param par this is {@code some code} and other text
+ */
+ public void withParam(String par) {}
}
diff --git a/core/testdata/format/javaCodeInParam.md b/core/testdata/format/javaCodeInParam.md
index 319c6d87..7bdf4f62 100644
--- a/core/testdata/format/javaCodeInParam.md
+++ b/core/testdata/format/javaCodeInParam.md
@@ -1,14 +1,9 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](index.md) / [withParam](./with-param.md)
-# C
+# withParam
-`protected open class C<T : Any>`
+`open fun withParam(par: String!): Unit`
### Parameters
-`T` - this is `some code` and other text
-
-### Constructors
-
-| [&lt;init&gt;](-init-.md) | `C()` |
-
+`par` - String!: this is `some code` and other text \ No newline at end of file
diff --git a/core/testdata/format/javaCodeLiteralTags.md b/core/testdata/format/javaCodeLiteralTags.md
index b36be04d..83d535fc 100644
--- a/core/testdata/format/javaCodeLiteralTags.md
+++ b/core/testdata/format/javaCodeLiteralTags.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
@@ -12,5 +12,5 @@ A&lt;B&gt;C
### Constructors
-| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
+| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` |
diff --git a/core/testdata/format/javaDeprecated.html b/core/testdata/format/javaDeprecated.html
index d938fb9d..66e3adef 100644
--- a/core/testdata/format/javaDeprecated.html
+++ b/core/testdata/format/javaDeprecated.html
@@ -4,7 +4,7 @@
<title>Foo.foo - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="index.html">Foo</a>&nbsp;/&nbsp;<a href="./foo.html">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>
diff --git a/core/testdata/format/javaLinkTag.html b/core/testdata/format/javaLinkTag.html
index f90a58df..0a027c0e 100644
--- a/core/testdata/format/javaLinkTag.html
+++ b/core/testdata/format/javaLinkTag.html
@@ -4,7 +4,7 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
@@ -14,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Call <code><a href="bar.html">#bar()</a></code> to do the job.</p>
@@ -28,8 +27,7 @@
<tbody>
<tr>
<td>
-<p><a href="bar.html">bar</a></p>
-</td>
+<a href="bar.html">bar</a></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>
</tr>
diff --git a/core/testdata/format/javaLinkTagWithLabel.html b/core/testdata/format/javaLinkTagWithLabel.html
index 51917f7a..f592f85e 100644
--- a/core/testdata/format/javaLinkTagWithLabel.html
+++ b/core/testdata/format/javaLinkTagWithLabel.html
@@ -4,7 +4,7 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
@@ -14,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
@@ -28,8 +27,7 @@
<tbody>
<tr>
<td>
-<p><a href="bar.html">bar</a></p>
-</td>
+<a href="bar.html">bar</a></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>
</tr>
diff --git a/core/testdata/format/javaSeeTag.html b/core/testdata/format/javaSeeTag.html
index f8866dc2..0f5465aa 100644
--- a/core/testdata/format/javaSeeTag.html
+++ b/core/testdata/format/javaSeeTag.html
@@ -4,7 +4,7 @@
<title>Foo - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
@@ -15,8 +15,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
@@ -27,8 +26,7 @@
<tbody>
<tr>
<td>
-<p><a href="bar.html">bar</a></p>
-</td>
+<a href="bar.html">bar</a></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>
</tr>
diff --git a/core/testdata/format/javaSpaceInAuthor.md b/core/testdata/format/javaSpaceInAuthor.md
index 1d2251d0..fc6c53f8 100644
--- a/core/testdata/format/javaSpaceInAuthor.md
+++ b/core/testdata/format/javaSpaceInAuthor.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java
index 622116b2..9e77402e 100644
--- a/core/testdata/format/javadocHtml.java
+++ b/core/testdata/format/javadocHtml.java
@@ -9,6 +9,18 @@
* <code>Code</code>
* <pre>Block code</pre>
* <ul><li>List Item</li></ul>
+ * <pre>
+ * with( some ) {
+ * multi = lines
+ * sample()
+ * }
+ * </pre>
+ * <pre>
+ * {@code
+ * with (some) { <code> }
+ * }
+ * </pre>
+ *
*/
public class C {
}
diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md
index a3c1baff..0e8c7ca8 100644
--- a/core/testdata/format/javadocHtml.md
+++ b/core/testdata/format/javadocHtml.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
@@ -16,13 +16,23 @@ Block code
* List Item
-### Constructors
-| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
+```
+
+ with( some ) {
+ multi = lines
+ sample()
+ }
+ ```
+
+
```
-Block code<br>```
-<br>
-* List Item
-<br> |
+with (some) { <code> }
+
+ ```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* |
diff --git a/core/testdata/format/javadocOrderedList.md b/core/testdata/format/javadocOrderedList.md
index 88d5f5b6..a861df46 100644
--- a/core/testdata/format/javadocOrderedList.md
+++ b/core/testdata/format/javadocOrderedList.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/jdkLinks.md b/core/testdata/format/jdkLinks.md
index 7498171d..4312aa2d 100644
--- a/core/testdata/format/jdkLinks.md
+++ b/core/testdata/format/jdkLinks.md
@@ -1,14 +1,14 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
-`class C : `[`ClassLoader`](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html)
+`class C : `[`ClassLoader`](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html)
-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))
+This is a [ClassLoader](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String))
-You can print something to [java.lang.System.out](http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now!
+You can print something to [java.lang.System.out](https://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now!
### Constructors
-| [&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)) |
+| [&lt;init&gt;](-init-.md) | `C()`<br>This is a [ClassLoader](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](https://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 59bc6ddf..daed792c 100644
--- a/core/testdata/format/linkWithLabel.html
+++ b/core/testdata/format/linkWithLabel.html
@@ -4,7 +4,7 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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,8 +14,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Use <a href="foo.html">this method</a> for best results.</p>
@@ -28,8 +27,7 @@
<tbody>
<tr>
<td>
-<p><a href="foo.html">foo</a></p>
-</td>
+<a href="foo.html">foo</a></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>
</tr>
diff --git a/core/testdata/format/linkWithStarProjection.html b/core/testdata/format/linkWithStarProjection.html
index e1b6e098..a9d6b125 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="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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,8 +13,7 @@
<tbody>
<tr>
<td>
-<p><a href="foo.html">foo</a></p>
-</td>
+<a href="foo.html">foo</a></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>
</tr>
diff --git a/core/testdata/format/linksInEmphasis.md b/core/testdata/format/linksInEmphasis.md
index d0ae70c8..0cd1aca1 100644
--- a/core/testdata/format/linksInEmphasis.md
+++ b/core/testdata/format/linksInEmphasis.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/linksInHeaders.md b/core/testdata/format/linksInHeaders.md
index 1dc7d18b..f771aabd 100644
--- a/core/testdata/format/linksInHeaders.md
+++ b/core/testdata/format/linksInHeaders.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/linksInStrong.md b/core/testdata/format/linksInStrong.md
index 5b44112d..34359a3d 100644
--- a/core/testdata/format/linksInStrong.md
+++ b/core/testdata/format/linksInStrong.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html
index 596cca73..f1146ea0 100644
--- a/core/testdata/format/markdownInLinks.html
+++ b/core/testdata/format/markdownInLinks.html
@@ -4,11 +4,11 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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>
<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>
-<p><a href="http://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p>
+<p><a href="https://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p>
</BODY>
</HTML>
diff --git a/core/testdata/format/markdownInLinks.kt b/core/testdata/format/markdownInLinks.kt
index 67b6311f..380727ee 100644
--- a/core/testdata/format/markdownInLinks.kt
+++ b/core/testdata/format/markdownInLinks.kt
@@ -1,4 +1,4 @@
/**
- * [a**b**__d__ kas ](http://www.ibm.com)
+ * [a**b**__d__ kas ](https://www.ibm.com)
*/
fun foo() {}
diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md
index 0ec1fda3..f52b46f2 100644
--- a/core/testdata/format/memberExtension.md
+++ b/core/testdata/format/memberExtension.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
index 37e943ad..06b8ead6 100644
--- a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
@@ -1,8 +1,5 @@
-[test](../../../index.md) / [pack](../../index.md) / [Some](../index.md) / [magic](./magic.md)
+[test](../../../index.md) / [pack](../../index.md) / [Some](../index.md) / [Some](index.md) / [magic](./magic.md)
# magic
-`fun magic(): Unit`
-
-**Platform and version requirements:** JS
-
+(JS) `fun magic(): Unit` \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md
index fca2aff4..1329ea7a 100644
--- a/core/testdata/format/multiplatform/implied/foo.md
+++ b/core/testdata/format/multiplatform/implied/foo.md
@@ -2,23 +2,23 @@
# Foo
-`class Foo`
+(JVM, JS) `class Foo`
This is a foo.
### Constructors
-| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+| (JVM, JS) [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
### Properties
-| [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` |
+| (JS) [propJs](prop-js.md) | `val propJs: String` |
+| (JVM) [propJvm](prop-jvm.md) | `val propJvm: String` |
+| (JVM, JS) [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` |
### Functions
-| [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` |
+| (JVM, JS) [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| (JS) [js](js.md) | `fun js(): Unit` |
+| (JVM) [jvm](jvm.md) | `fun jvm(): Unit` |
diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md
index ea78b5a3..2dfdce38 100644
--- a/core/testdata/format/multiplatform/merge/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md
@@ -2,9 +2,7 @@
## Package foo
-**Platform and version requirements:** JVM, JS
-
### Types
-| [Foo](-foo/index.md)<br>(JVM, JS) | `class Foo`<br>This is a foo. |
+| (JVM, JS) [Foo](-foo/index.md) | `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 7f41b7d1..1329ea7a 100644
--- a/core/testdata/format/multiplatform/mergeMembers/foo.md
+++ b/core/testdata/format/multiplatform/mergeMembers/foo.md
@@ -2,25 +2,23 @@
# Foo
-`class Foo`
-
-**Platform and version requirements:** JVM, JS
+(JVM, JS) `class Foo`
This is a foo.
### Constructors
-| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+| (JVM, JS) [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
### Properties
-| [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` |
+| (JS) [propJs](prop-js.md) | `val propJs: String` |
+| (JVM) [propJvm](prop-jvm.md) | `val propJvm: String` |
+| (JVM, JS) [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` |
### Functions
-| [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` |
+| (JVM, JS) [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| (JS) [js](js.md) | `fun js(): Unit` |
+| (JVM) [jvm](jvm.md) | `fun jvm(): Unit` |
diff --git a/core/testdata/format/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md
index a20b14cf..49a46972 100644
--- a/core/testdata/format/multiplatform/omitRedundant/foo.md
+++ b/core/testdata/format/multiplatform/omitRedundant/foo.md
@@ -2,21 +2,19 @@
# Foo
-`class Foo`
-
-**Platform and version requirements:** JVM
+(JVM) `class Foo`
This is a foo.
### Constructors
-| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+| (JVM) [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
### Properties
-| [propJvm](prop-jvm.md) | `val propJvm: String` |
+| (JVM) [propJvm](prop-jvm.md) | `val propJvm: String` |
### Functions
-| [jvm](jvm.md) | `fun jvm(): Unit` |
+| (JVM) [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 6f45342b..9b0d6b63 100644
--- a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
@@ -1,10 +1,8 @@
[test](./index.md)
-**Platform and version requirements:** JVM, JS
-
### Packages
-| [foo.bar](foo.bar/index.md)<br>(JVM, JS) | |
+| (JVM, JS) [foo.bar](foo.bar/index.md) | |
### Index
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
index 4ddfe2e3..a7fef864 100644
--- a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
@@ -2,9 +2,7 @@
## Package foo.bar
-**Platform and version requirements:** JVM, JS
-
### Functions
-| [buz](buz.md)<br>(JVM, JS) | `fun buz(): Unit` |
+| (JVM, JS) [buz](buz.md) | `fun buz(): Unit` |
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
index f4186b6e..b5543c80 100644
--- a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
@@ -1,10 +1,8 @@
[test](./index.md)
-**Platform and version requirements:** JVM
-
### Packages
-| [some](some/index.md)<br>(JVM) | |
+| (JVM) [some](some/index.md) | |
### Index
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
index ff480b5a..6e42d0dc 100644
--- a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
@@ -2,9 +2,7 @@
## Package some
-**Platform and version requirements:** JVM
-
### Extensions for External Classes
-| [kotlin.String](kotlin.-string/index.md) | |
+| (JVM) [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 fad7e90d..b5d9cc7d 100644
--- a/core/testdata/format/multiplatform/simple/multiplatform.package.md
+++ b/core/testdata/format/multiplatform/simple/multiplatform.package.md
@@ -4,6 +4,6 @@
### Types
-| [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. |
+| (JS) [Bar](-bar/index.md) | `class Bar`<br>This is a bar. |
+| (JVM) [Foo](-foo/index.md) | `class Foo`<br>This is a foo. |
diff --git a/core/testdata/format/multipleTypeParameterConstraints.md b/core/testdata/format/multipleTypeParameterConstraints.md
index 78586aca..27581598 100644
--- a/core/testdata/format/multipleTypeParameterConstraints.md
+++ b/core/testdata/format/multipleTypeParameterConstraints.md
@@ -1,17 +1,17 @@
-<!-- File: test/-a.md -->
-[test](index.md) / [A](./-a.md)
+<!-- File: test/--root--/-a.md -->
+[test](../index.md) / [A](./-a.md)
# A
`interface A`
-<!-- File: test/-b.md -->
-[test](index.md) / [B](./-b.md)
+<!-- File: test/--root--/-b.md -->
+[test](../index.md) / [B](./-b.md)
# B
`interface B`
-<!-- File: test/f.md -->
-[test](index.md) / [f](./f.md)
+<!-- File: test/--root--/f.md -->
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/nestedLists.md b/core/testdata/format/nestedLists.md
index fb25bc32..c785ba42 100644
--- a/core/testdata/format/nestedLists.md
+++ b/core/testdata/format/nestedLists.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/newlineInTableCell.package.md b/core/testdata/format/newlineInTableCell.package.md
index 53716db3..dbc1770a 100644
--- a/core/testdata/format/newlineInTableCell.package.md
+++ b/core/testdata/format/newlineInTableCell.package.md
@@ -1,4 +1,4 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
index ca95093c..75b6db3e 100644
--- a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
+++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/nullability.md b/core/testdata/format/nullability.md
index 7b81c255..2acd6f65 100644
--- a/core/testdata/format/nullability.md
+++ b/core/testdata/format/nullability.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](./index.md)
+[test](../../index.md) / [C](./index.md)
# C
diff --git a/core/testdata/format/nullableTypeParameterFunction.kt b/core/testdata/format/nullableTypeParameterFunction.kt
new file mode 100644
index 00000000..01805a7b
--- /dev/null
+++ b/core/testdata/format/nullableTypeParameterFunction.kt
@@ -0,0 +1,8 @@
+class Bar<T> {
+ val dataList = mutableListOf<T>()
+
+ open fun checkElement(
+ elem: T,
+ addFunc: ((elem: T) -> Unit)? = { dataList.add(it) }
+ ): Int = 1
+} \ No newline at end of file
diff --git a/core/testdata/format/nullableTypeParameterFunction.md b/core/testdata/format/nullableTypeParameterFunction.md
new file mode 100644
index 00000000..ec968537
--- /dev/null
+++ b/core/testdata/format/nullableTypeParameterFunction.md
@@ -0,0 +1,18 @@
+[test](../../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Properties
+
+| [dataList](data-list.md) | `val dataList: MutableList<`[`T`](index.md#T)`>` |
+
+### Functions
+
+| [checkElement](check-element.md) | `fun checkElement(elem: `[`T`](index.md#T)`, addFunc: ((elem: `[`T`](index.md#T)`) -> Unit)? = { dataList.add(it) }): Int` |
+
diff --git a/core/testdata/format/operatorOverloading.md b/core/testdata/format/operatorOverloading.md
index 0a4c87b6..df8ea182 100644
--- a/core/testdata/format/operatorOverloading.md
+++ b/core/testdata/format/operatorOverloading.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [C](index.md) / [plus](./plus.md)
+[test](../../index.md) / [C](index.md) / [plus](./plus.md)
# plus
diff --git a/core/testdata/format/orderedList.html b/core/testdata/format/orderedList.html
index 6f735bfd..b011b5b3 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="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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,8 +17,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Usage instructions:</p>
diff --git a/core/testdata/format/overloads.html b/core/testdata/format/overloads.html
index feda82e4..1545cb9f 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="./index.html">test</a><br/>
+<a href="../index.html">test</a><br/>
<br/>
<h2>Package &lt;root&gt;</h2>
<h3>Functions</h3>
@@ -12,8 +12,7 @@
<tbody>
<tr>
<td>
-<p><a href="f.html">f</a></p>
-</td>
+<a href="f.html">f</a></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/>
<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>
diff --git a/core/testdata/format/overloadsWithDescription.html b/core/testdata/format/overloadsWithDescription.html
index 16b03f7e..329393d1 100644
--- a/core/testdata/format/overloadsWithDescription.html
+++ b/core/testdata/format/overloadsWithDescription.html
@@ -4,7 +4,7 @@
<title>f - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">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/overloadsWithDifferentDescriptions.html b/core/testdata/format/overloadsWithDifferentDescriptions.html
index 4c4f7f74..c2a348c6 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="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">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 d0ec82fa..91c81410 100644
--- a/core/testdata/format/overridingFunction.md
+++ b/core/testdata/format/overridingFunction.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [D](index.md) / [f](./f.md)
+[test](../../index.md) / [D](index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/paramTag.md b/core/testdata/format/paramTag.md
index 7cc33d21..9a368d3a 100644
--- a/core/testdata/format/paramTag.md
+++ b/core/testdata/format/paramTag.md
@@ -1,4 +1,4 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/parameterAnchor.html b/core/testdata/format/parameterAnchor.html
index a4ae0997..e54a066b 100644
--- a/core/testdata/format/parameterAnchor.html
+++ b/core/testdata/format/parameterAnchor.html
@@ -4,7 +4,7 @@
<title>processFiles - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./process-files.html">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>
diff --git a/core/testdata/format/parenthesis.html b/core/testdata/format/parenthesis.html
index c63154c1..8f6edbee 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="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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 887d25a5..474379d9 100644
--- a/core/testdata/format/propertyVar.md
+++ b/core/testdata/format/propertyVar.md
@@ -1,4 +1,4 @@
-[test](index.md) / [x](./x.md)
+[test](../index.md) / [x](./x.md)
# x
diff --git a/core/testdata/format/qualifiedNameLink.md b/core/testdata/format/qualifiedNameLink.md
index 92fa8f7a..53984037 100644
--- a/core/testdata/format/qualifiedNameLink.md
+++ b/core/testdata/format/qualifiedNameLink.md
@@ -1,4 +1,4 @@
-[test](index.md) / [foo](./foo.md)
+[test](../index.md) / [foo](./foo.md)
# foo
diff --git a/core/testdata/format/receiverParameterTypeBound.md b/core/testdata/format/receiverParameterTypeBound.md
index 978dc0f8..95f3beec 100644
--- a/core/testdata/format/receiverParameterTypeBound.md
+++ b/core/testdata/format/receiverParameterTypeBound.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/receiverReference.md b/core/testdata/format/receiverReference.md
index bdcce322..9d36e863 100644
--- a/core/testdata/format/receiverReference.md
+++ b/core/testdata/format/receiverReference.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [kotlin.String](./index.md)
+[test](../../index.md) / [kotlin.String](./index.md)
### Extensions for kotlin.String
diff --git a/core/testdata/format/reifiedTypeParameter.kt b/core/testdata/format/reifiedTypeParameter.kt
index 00fa1dc9..1b1de359 100644
--- a/core/testdata/format/reifiedTypeParameter.kt
+++ b/core/testdata/format/reifiedTypeParameter.kt
@@ -1,3 +1,3 @@
-inline fun f<reified T>() {
+inline fun f<reified T>(a: () -> String) {
}
diff --git a/core/testdata/format/reifiedTypeParameter.md b/core/testdata/format/reifiedTypeParameter.md
index 40dbed7b..7bb406ee 100644
--- a/core/testdata/format/reifiedTypeParameter.md
+++ b/core/testdata/format/reifiedTypeParameter.md
@@ -1,5 +1,5 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
-`inline fun <reified T> f(): Unit` \ No newline at end of file
+`inline fun <reified T> f(a: () -> String): Unit` \ No newline at end of file
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
index ad632fef..6d449fb4 100644
--- a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
@@ -1,6 +1,6 @@
-[test](../index.md) / [kotlin.SuspendFunction0](./index.md)
+[test](../../index.md) / [kotlin.coroutines.SuspendFunction0](./index.md)
-### Extensions for kotlin.SuspendFunction0
+### Extensions for kotlin.coroutines.SuspendFunction0
| [foo](foo.md) | `fun (suspend () -> Unit).foo(): Unit` |
diff --git a/core/testdata/format/returnWithLink.html b/core/testdata/format/returnWithLink.html
index fe1d031b..9f36d294 100644
--- a/core/testdata/format/returnWithLink.html
+++ b/core/testdata/format/returnWithLink.html
@@ -4,7 +4,7 @@
<title>foo - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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>
diff --git a/core/testdata/format/see.html b/core/testdata/format/see.html
index 74773951..2a8caf6f 100644
--- a/core/testdata/format/see.html
+++ b/core/testdata/format/see.html
@@ -1,11 +1,11 @@
-<!-- File: test/quux.html -->
+<!-- File: test/--root--/quux.html -->
<HTML>
<HEAD>
<meta charset="UTF-8">
<title>quux - test</title>
</HEAD>
<BODY>
-<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./quux.html">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>
@@ -16,28 +16,28 @@
</p>
</BODY>
</HTML>
-<!-- File: test/foo.html -->
+<!-- File: test/--root--/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/>
+<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>
</BODY>
</HTML>
-<!-- File: test/bar.html -->
+<!-- File: test/--root--/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/>
+<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 f900ecb2..4adfda6a 100644
--- a/core/testdata/format/shadowedExtensionFunctions.md
+++ b/core/testdata/format/shadowedExtensionFunctions.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html
index 32988de2..1173a8ab 100644
--- a/core/testdata/format/sinceKotlin.html
+++ b/core/testdata/format/sinceKotlin.html
@@ -4,19 +4,17 @@
<title>Since1.1 - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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>
-<p><strong>Platform and version requirements:</strong> Kotlin 1.1</p>
<p>Useful</p>
<h3>Constructors</h3>
<table>
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a>Since: <code>1.1</code></td>
<td>
<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code>
<p>Useful</p>
diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md
index df96db0a..e9b29229 100644
--- a/core/testdata/format/sinceKotlin.md
+++ b/core/testdata/format/sinceKotlin.md
@@ -1,14 +1,12 @@
-[test](../index.md) / [Since1.1](./index.md)
+[test](../../index.md) / [Since1.1](./index.md)
# Since1.1
`class Since1.1`
-**Platform and version requirements:** Kotlin 1.1
-
Useful
### Constructors
-| [&lt;init&gt;](-init-.md) | `Since1.1()`<br>Useful |
+| [&lt;init&gt;](-init-.md)Since: `1.1` | `Since1.1()`<br>Useful |
diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md
index eabf88d5..92197648 100644
--- a/core/testdata/format/sinceKotlin.package.md
+++ b/core/testdata/format/sinceKotlin.package.md
@@ -1,10 +1,8 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
-**Platform and version requirements:** Kotlin 1.1
-
### Types
-| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+| [Since1.1](-since1.1/index.md) (Since: `1.1`) | `class Since1.1`<br>Useful |
diff --git a/core/testdata/format/sinceKotlinWide.package.md b/core/testdata/format/sinceKotlinWide.package.md
index 58a5045e..fd7d45aa 100644
--- a/core/testdata/format/sinceKotlinWide.package.md
+++ b/core/testdata/format/sinceKotlinWide.package.md
@@ -1,11 +1,9 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
-**Platform and version requirements:** Kotlin 1.1+
-
### Types
-| [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 |
+| [Since1.1](-since1.1/index.md) (Since: `1.1`) | `class Since1.1`<br>Useful |
+| [Since1.2](-since1.2/index.md) (Since: `1.2`) | `class Since1.2`<br>Useful also |
diff --git a/core/testdata/format/starProjection.md b/core/testdata/format/starProjection.md
index 5a53e5b9..594a7d72 100644
--- a/core/testdata/format/starProjection.md
+++ b/core/testdata/format/starProjection.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [kotlin.collections.Iterable](./index.md)
+[test](../../index.md) / [kotlin.collections.Iterable](./index.md)
### Extensions for kotlin.collections.Iterable
diff --git a/core/testdata/format/suspendInlineFunction.kt b/core/testdata/format/suspendInlineFunction.kt
new file mode 100644
index 00000000..8af0d11a
--- /dev/null
+++ b/core/testdata/format/suspendInlineFunction.kt
@@ -0,0 +1,6 @@
+/**
+ * returns 1
+ */
+suspend inline fun foo(): Int {
+ 1
+}
diff --git a/core/testdata/format/suspendInlineFunction.md b/core/testdata/format/suspendInlineFunction.md
new file mode 100644
index 00000000..056c8799
--- /dev/null
+++ b/core/testdata/format/suspendInlineFunction.md
@@ -0,0 +1,8 @@
+[test](../index.md) / [foo](./foo.md)
+
+# foo
+
+`suspend fun foo(): Int`
+
+returns 1
+
diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md
index ab116140..7bc656f4 100644
--- a/core/testdata/format/suspendParam.md
+++ b/core/testdata/format/suspendParam.md
@@ -1,4 +1,4 @@
-[test](index.md) / [takesSuspendParam](./takes-suspend-param.md)
+[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 92bd7ee7..3fdb1bc6 100644
--- a/core/testdata/format/suspendParam.package.md
+++ b/core/testdata/format/suspendParam.package.md
@@ -1,4 +1,4 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
diff --git a/core/testdata/format/throwsTag.md b/core/testdata/format/throwsTag.md
index 70fba512..104493a7 100644
--- a/core/testdata/format/throwsTag.md
+++ b/core/testdata/format/throwsTag.md
@@ -1,4 +1,4 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/tokensInEmphasis.md b/core/testdata/format/tokensInEmphasis.md
index a68861de..50bd694f 100644
--- a/core/testdata/format/tokensInEmphasis.md
+++ b/core/testdata/format/tokensInEmphasis.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/tokensInHeaders.md b/core/testdata/format/tokensInHeaders.md
index bd25492e..293c15cc 100644
--- a/core/testdata/format/tokensInHeaders.md
+++ b/core/testdata/format/tokensInHeaders.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [The](./index.md)
+[test](../../index.md) / [The](./index.md)
# The
diff --git a/core/testdata/format/tokensInStrong.md b/core/testdata/format/tokensInStrong.md
index 2781656c..e81503e4 100644
--- a/core/testdata/format/tokensInStrong.md
+++ b/core/testdata/format/tokensInStrong.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Yasc](./index.md)
+[test](../../index.md) / [Yasc](./index.md)
# Yasc
diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html
index dacd0567..7dbdf4f2 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="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">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 218c4848..755bd18d 100644
--- a/core/testdata/format/typeAliases.md
+++ b/core/testdata/format/typeAliases.md
@@ -1,5 +1,5 @@
-<!-- File: test/-a/index.md -->
-[test](../index.md) / [A](./index.md)
+<!-- File: test/--root--/-a/index.md -->
+[test](../../index.md) / [A](./index.md)
# A
@@ -9,8 +9,8 @@
| [&lt;init&gt;](-init-.md) | `A()` |
-<!-- File: test/-b/index.md -->
-[test](../index.md) / [B](./index.md)
+<!-- File: test/--root--/-b/index.md -->
+[test](../../index.md) / [B](./index.md)
# B
@@ -20,8 +20,8 @@
| [&lt;init&gt;](-init-.md) | `B()` |
-<!-- File: test/-c/index.md -->
-[test](../index.md) / [C](./index.md)
+<!-- File: test/--root--/-c/index.md -->
+[test](../../index.md) / [C](./index.md)
# C
@@ -31,62 +31,62 @@
| [&lt;init&gt;](-init-.md) | `C()` |
-<!-- File: test/-d.md -->
-[test](index.md) / [D](./-d.md)
+<!-- File: test/--root--/-d.md -->
+[test](../index.md) / [D](./-d.md)
# D
`typealias D = `[`A`](-a/index.md)
-<!-- File: test/-e.md -->
-[test](index.md) / [E](./-e.md)
+<!-- File: test/--root--/-e.md -->
+[test](../index.md) / [E](./-e.md)
# E
`typealias E = `[`D`](-d.md)
-<!-- File: test/-f.md -->
-[test](index.md) / [F](./-f.md)
+<!-- File: test/--root--/-f.md -->
+[test](../index.md) / [F](./-f.md)
# F
`typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md)
-<!-- File: test/-g.md -->
-[test](index.md) / [G](./-g.md)
+<!-- File: test/--root--/-g.md -->
+[test](../index.md) / [G](./-g.md)
# G
`typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>`
-<!-- File: test/-h.md -->
-[test](index.md) / [H](./-h.md)
+<!-- File: test/--root--/-h.md -->
+[test](../index.md) / [H](./-h.md)
# H
`typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>`
-<!-- File: test/-i.md -->
-[test](index.md) / [I](./-i.md)
+<!-- File: test/--root--/-i.md -->
+[test](../index.md) / [I](./-i.md)
# I
`typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>`
-<!-- File: test/-j.md -->
-[test](index.md) / [J](./-j.md)
+<!-- File: test/--root--/-j.md -->
+[test](../index.md) / [J](./-j.md)
# J
`typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>`
-<!-- File: test/-k.md -->
-[test](index.md) / [K](./-k.md)
+<!-- File: test/--root--/-k.md -->
+[test](../index.md) / [K](./-k.md)
# K
`typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>`
-<!-- File: test/-l.md -->
-[test](index.md) / [L](./-l.md)
+<!-- File: test/--root--/-l.md -->
+[test](../index.md) / [L](./-l.md)
# L
`typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md)
-<!-- File: test/-m.md -->
-[test](index.md) / [M](./-m.md)
+<!-- File: test/--root--/-m.md -->
+[test](../index.md) / [M](./-m.md)
# M
@@ -94,8 +94,8 @@
Documented
-<!-- File: test/-n.md -->
-[test](index.md) / [N](./-n.md)
+<!-- File: test/--root--/-n.md -->
+[test](../index.md) / [N](./-n.md)
# N
diff --git a/core/testdata/format/typeAliases.package.md b/core/testdata/format/typeAliases.package.md
index 199e91c2..d5ed2247 100644
--- a/core/testdata/format/typeAliases.package.md
+++ b/core/testdata/format/typeAliases.package.md
@@ -1,4 +1,4 @@
-[test](./index.md)
+[test](../index.md)
## Package &lt;root&gt;
@@ -7,9 +7,6 @@
| [A](-a/index.md) | `class A` |
| [B](-b/index.md) | `class B` |
| [C](-c/index.md) | `class C<T>` |
-
-### Type Aliases
-
| [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) |
diff --git a/core/testdata/format/typeLink.html b/core/testdata/format/typeLink.html
index 30af8a93..f25efe3c 100644
--- a/core/testdata/format/typeLink.html
+++ b/core/testdata/format/typeLink.html
@@ -4,7 +4,7 @@
<title>Bar - test</title>
</HEAD>
<BODY>
-<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">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="../-foo/index.html"><span class="identifier">Foo</span></a></code>
@@ -13,8 +13,7 @@
<tbody>
<tr>
<td>
-<p><a href="-init-.html">&lt;init&gt;</a></p>
-</td>
+<a href="-init-.html">&lt;init&gt;</a></td>
<td>
<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
</tr>
diff --git a/core/testdata/format/typeParameterBounds.md b/core/testdata/format/typeParameterBounds.md
index cf03b3a7..0e207763 100644
--- a/core/testdata/format/typeParameterBounds.md
+++ b/core/testdata/format/typeParameterBounds.md
@@ -1,4 +1,4 @@
-[test](index.md) / [generic](./generic.md)
+[test](../index.md) / [generic](./generic.md)
# generic
diff --git a/core/testdata/format/typeParameterReference.md b/core/testdata/format/typeParameterReference.md
index 5001d321..0baefba0 100644
--- a/core/testdata/format/typeParameterReference.md
+++ b/core/testdata/format/typeParameterReference.md
@@ -1,4 +1,4 @@
-[test](index.md) / [tt](./tt.md)
+[test](../index.md) / [tt](./tt.md)
# tt
diff --git a/core/testdata/format/typeParameterVariance.md b/core/testdata/format/typeParameterVariance.md
index b0615d43..9ea5feda 100644
--- a/core/testdata/format/typeParameterVariance.md
+++ b/core/testdata/format/typeParameterVariance.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Foo](./index.md)
+[test](../../index.md) / [Foo](./index.md)
# Foo
diff --git a/core/testdata/format/typeProjectionVariance.md b/core/testdata/format/typeProjectionVariance.md
index d3a55c58..082ffdba 100644
--- a/core/testdata/format/typeProjectionVariance.md
+++ b/core/testdata/format/typeProjectionVariance.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [kotlin.Array](./index.md)
+[test](../../index.md) / [kotlin.Array](./index.md)
### Extensions for kotlin.Array
diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.html b/core/testdata/format/uninterpretedEmphasisCharacters.html
index dd338f72..a5b182b8 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="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">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 52ad9a71..14143a46 100644
--- a/core/testdata/format/unorderedLists.md
+++ b/core/testdata/format/unorderedLists.md
@@ -1,4 +1,4 @@
-[test](../index.md) / [Bar](./index.md)
+[test](../../index.md) / [Bar](./index.md)
# Bar
diff --git a/core/testdata/format/varargsFunction.md b/core/testdata/format/varargsFunction.md
index 550202cc..b23760f0 100644
--- a/core/testdata/format/varargsFunction.md
+++ b/core/testdata/format/varargsFunction.md
@@ -1,4 +1,4 @@
-[test](index.md) / [f](./f.md)
+[test](../index.md) / [f](./f.md)
# f
diff --git a/core/testdata/format/website-html/sampleWithAsserts.html b/core/testdata/format/website-html/sampleWithAsserts.html
index e91232f5..2b2a9ac5 100644
--- a/core/testdata/format/website-html/sampleWithAsserts.html
+++ b/core/testdata/format/website-html/sampleWithAsserts.html
@@ -14,6 +14,8 @@ println("a() == b() is ${a() == b()}") // true
// readSomeFile(File("some.txt")) // reading file now will fail
// readSomeFile(File("some.txt")) // will fail with FileNotFoundException
+// readSomeFile(File("some.txt")) // will fail
+
fun indented() {
// A neq B
println("a() != b() is ${a() != b()}") // false
diff --git a/core/testdata/format/website-html/sampleWithAsserts.kt b/core/testdata/format/website-html/sampleWithAsserts.kt
index b3bce11d..bb5848e6 100644
--- a/core/testdata/format/website-html/sampleWithAsserts.kt
+++ b/core/testdata/format/website-html/sampleWithAsserts.kt
@@ -26,6 +26,8 @@ fun sample() {
assertFails("reading file now") { readSomeFile(File("some.txt")) }
assertFailsWith<FileNotFoundException> { readSomeFile(File("some.txt")) }
+ assertFails { readSomeFile(File("some.txt")) }
+
fun indented() {
assertFalse(a() != b(), "A neq B")
}
diff --git a/core/testdata/functions/inlineFunction.kt b/core/testdata/functions/inlineFunction.kt
index 11c19672..64a617a4 100644
--- a/core/testdata/functions/inlineFunction.kt
+++ b/core/testdata/functions/inlineFunction.kt
@@ -1,2 +1,2 @@
-inline fun f() {
+inline fun f(a: () -> String) {
}
diff --git a/core/testdata/functions/inlineSuspendFunction.kt b/core/testdata/functions/inlineSuspendFunction.kt
new file mode 100644
index 00000000..5f376267
--- /dev/null
+++ b/core/testdata/functions/inlineSuspendFunction.kt
@@ -0,0 +1,2 @@
+inline suspend fun f(a: () -> String) {
+}
diff --git a/core/testdata/functions/suspendFunction.kt b/core/testdata/functions/suspendFunction.kt
new file mode 100644
index 00000000..49ecca2a
--- /dev/null
+++ b/core/testdata/functions/suspendFunction.kt
@@ -0,0 +1,2 @@
+suspend fun f() {
+}
diff --git a/core/testdata/functions/suspendInlineFunction.kt b/core/testdata/functions/suspendInlineFunction.kt
new file mode 100644
index 00000000..54f65658
--- /dev/null
+++ b/core/testdata/functions/suspendInlineFunction.kt
@@ -0,0 +1,2 @@
+suspend inline fun f(a: () -> String) {
+}
diff --git a/core/testdata/java/constants.java b/core/testdata/java/constants.java
new file mode 100644
index 00000000..26f16639
--- /dev/null
+++ b/core/testdata/java/constants.java
@@ -0,0 +1,5 @@
+public class Constants {
+ public static final String constStr = "some value";
+ public static final Object nullConst = null;
+ public static final String refConst = constStr;
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/argumentReference.kt b/core/testdata/javadoc/argumentReference.kt
new file mode 100644
index 00000000..ac3104e9
--- /dev/null
+++ b/core/testdata/javadoc/argumentReference.kt
@@ -0,0 +1,4 @@
+/**
+ * [error]
+ */
+fun argNamedError(error: String) {} \ No newline at end of file
diff --git a/core/testdata/javadoc/constructorParameters.kt b/core/testdata/javadoc/constructorParameters.kt
new file mode 100644
index 00000000..c29ae912
--- /dev/null
+++ b/core/testdata/javadoc/constructorParameters.kt
@@ -0,0 +1,14 @@
+package bar
+
+/**
+ * Just a fruit
+ *
+ * @param weight in grams
+ * @param ranking quality from 0 to 10, where 10 is best
+ * @param color yellow is default
+ */
+class Banana (
+ private val weight: Double,
+ private val ranking: Int,
+ color: String = "yellow"
+) \ No newline at end of file
diff --git a/core/testdata/javadoc/defaultNoArgConstructor.kt b/core/testdata/javadoc/defaultNoArgConstructor.kt
new file mode 100644
index 00000000..3a6d04a5
--- /dev/null
+++ b/core/testdata/javadoc/defaultNoArgConstructor.kt
@@ -0,0 +1,12 @@
+package foo
+
+/**
+ * Description
+ *
+ * @constructor print peach
+ */
+class Peach {
+ init {
+ println("peach")
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/deprecated.java b/core/testdata/javadoc/deprecated.java
new file mode 100644
index 00000000..5a6cdd77
--- /dev/null
+++ b/core/testdata/javadoc/deprecated.java
@@ -0,0 +1,28 @@
+package bar;
+
+/**
+ * Just a fruit
+ */
+public class Banana {
+ private Double weight;
+
+ /**
+ * Returns weight
+ *
+ * @return weight
+ * @deprecated
+ */
+ public Double getWeight() {
+ return weight;
+ }
+
+ /**
+ * Sets weight
+ *
+ * @param weight in grams
+ * @deprecated with message
+ */
+ public void setWeight(Double weight) {
+ this.weight = weight;
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/functionParameters.java b/core/testdata/javadoc/functionParameters.java
new file mode 100644
index 00000000..8d5f5143
--- /dev/null
+++ b/core/testdata/javadoc/functionParameters.java
@@ -0,0 +1,17 @@
+package bar;
+
+/**
+ * Foo
+ */
+
+public class Foo {
+
+ /** perfom request
+ *
+ * @param name user name
+ * @param password user password
+ */
+ public void request(String name, String password) {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/noArgConstructor.kt b/core/testdata/javadoc/noArgConstructor.kt
new file mode 100644
index 00000000..25e5548c
--- /dev/null
+++ b/core/testdata/javadoc/noArgConstructor.kt
@@ -0,0 +1,12 @@
+package foo
+
+/**
+ * Description
+ *
+ * @constructor print plum
+ */
+class Plum() {
+ init {
+ println("plum")
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/vararg.kt b/core/testdata/javadoc/vararg.kt
new file mode 100644
index 00000000..aa6c26d7
--- /dev/null
+++ b/core/testdata/javadoc/vararg.kt
@@ -0,0 +1,3 @@
+fun vararg(a: String, vararg b: Int) {}
+
+fun varargInMiddle(a: String, vararg b: Int, c: Short) {} \ No newline at end of file
diff --git a/core/testdata/javadoc/visibilityModifiers.kt b/core/testdata/javadoc/visibilityModifiers.kt
new file mode 100644
index 00000000..e48e7f62
--- /dev/null
+++ b/core/testdata/javadoc/visibilityModifiers.kt
@@ -0,0 +1,15 @@
+package foo
+
+abstract class Apple {
+ protected var name: String = "foo"
+ internal var weight: Int = 180
+ var rating: Int = 10
+ private var color: String = "red"
+
+ companion object {
+ @JvmStatic
+ val code : Int = 123456
+ }
+
+
+} \ No newline at end of file
diff --git a/core/testdata/markdown/spec.txt b/core/testdata/markdown/spec.txt
index fce87924..916bdd89 100644
--- a/core/testdata/markdown/spec.txt
+++ b/core/testdata/markdown/spec.txt
@@ -23,7 +23,7 @@ HTML but in LaTeX and many other formats.
## Why is a spec needed?
John Gruber's [canonical description of Markdown's
-syntax](http://daringfireball.net/projects/markdown/syntax)
+syntax](https://daringfireball.net/projects/markdown/syntax)
does not specify the syntax unambiguously. Here are some examples of
questions it does not answer:
@@ -34,7 +34,7 @@ questions it does not answer:
not require that. This is hardly a "corner case," and divergences
between implementations on this issue often lead to surprises for
users in real documents. (See [this comment by John
- Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).)
+ Gruber](https://article.gmane.org/gmane.text.markdown.general/1997).)
2. Is a blank line needed before a block quote or header?
Most implementations do not require the blank line. However,
@@ -42,7 +42,7 @@ questions it does not answer:
also to ambiguities in parsing (note that some implementations
put the header inside the blockquote, while others do not).
(John Gruber has also spoken [in favor of requiring the blank
- lines](http://article.gmane.org/gmane.text.markdown.general/2146).)
+ lines](https://article.gmane.org/gmane.text.markdown.general/2146).)
3. Is a blank line needed before an indented code block?
(`Markdown.pl` requires it, but this is not mentioned in the
@@ -75,7 +75,7 @@ questions it does not answer:
```
(There are some relevant comments by John Gruber
- [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
+ [here](https://article.gmane.org/gmane.text.markdown.general/2554).)
5. Can list markers be indented? Can ordered list markers be right-aligned?
@@ -509,7 +509,7 @@ More than six `#` characters is not a header:
A space is required between the `#` characters and the header's
contents. Note that many implementations currently do not require
the space. However, the space was required by the [original ATX
-implementation](http://www.aaronsw.com/2002/atx/atx.py), and it helps
+implementation](https://www.aaronsw.com/2002/atx/atx.py), and it helps
prevent things like the following from being parsed as headers:
.
@@ -3686,9 +3686,9 @@ raw HTML:
.
.
-<http://google.com?find=\*>
+<https://google.com?find=\*>
.
-<p><a href="http://google.com?find=%5C*">http://google.com?find=\*</a></p>
+<p><a href="https://google.com?find=%5C*">https://google.com?find=\*</a></p>
.
.
@@ -3736,7 +3736,7 @@ and simplifies the job of implementations targetting other languages, as these w
UTF8 chars and need not be HTML-entity aware.
[Named entities](#name-entities) <a id="named-entities"></a> consist of `&`
-+ any of the valid HTML5 entity names + `;`. The [following document](http://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json)
++ any of the valid HTML5 entity names + `;`. The [following document](https://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json)
is used as an authoritative source of the valid entity names and their corresponding codepoints.
Conforming implementations that target Markdown don't need to generate entities for all the valid
@@ -3955,9 +3955,9 @@ And this is not parsed as a link:
But this is a link:
.
-<http://foo.bar.`baz>`
+<https://foo.bar.`baz>`
.
-<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
+<p><a href="https://foo.bar.%60baz">https://foo.bar.`baz</a>`</p>
.
And this is an HTML tag:
@@ -3986,7 +3986,7 @@ we just have literal backticks:
## Emphasis and strong emphasis
John Gruber's original [Markdown syntax
-description](http://daringfireball.net/projects/markdown/syntax#em) says:
+description](https://daringfireball.net/projects/markdown/syntax#em) says:
> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML
@@ -4229,15 +4229,15 @@ _a `_`_
.
.
-**a<http://foo.bar?q=**>
+**a<https://foo.bar?q=**>
.
-<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p>
+<p>**a<a href="https://foo.bar?q=**">https://foo.bar?q=**</a></p>
.
.
-__a<http://foo.bar?q=__>
+__a<https://foo.bar?q=__>
.
-<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p>
+<p>__a<a href="https://foo.bar?q=__">https://foo.bar?q=__</a></p>
.
This is not emphasis, because the opening delimiter is
@@ -5455,15 +5455,15 @@ soap.beep`, `soap.beeps`, `tag`, `tel`, `telnet`, `tftp`, `thismessage`,
Here are some valid autolinks:
.
-<http://foo.bar.baz>
+<https://foo.bar.baz>
.
-<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
+<p><a href="https://foo.bar.baz">https://foo.bar.baz</a></p>
.
.
-<http://foo.bar.baz?q=hello&id=22&boolean>
+<https://foo.bar.baz?q=hello&id=22&boolean>
.
-<p><a href="http://foo.bar.baz?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz?q=hello&amp;id=22&amp;boolean</a></p>
+<p><a href="https://foo.bar.baz?q=hello&amp;id=22&amp;boolean">https://foo.bar.baz?q=hello&amp;id=22&amp;boolean</a></p>
.
.
@@ -5483,9 +5483,9 @@ Uppercase is also fine:
Spaces are not allowed in autolinks:
.
-<http://foo.bar/baz bim>
+<https://foo.bar/baz bim>
.
-<p>&lt;http://foo.bar/baz bim&gt;</p>
+<p>&lt;https://foo.bar/baz bim&gt;</p>
.
An [email autolink](#email-autolink) <a id="email-autolink"></a>
@@ -5496,7 +5496,7 @@ and the URL is `mailto:` followed by the email address.
An [email address](#email-address), <a id="email-address"></a>
for these purposes, is anything that matches
the [non-normative regex from the HTML5
-spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29):
+spec](https://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29):
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
@@ -5530,9 +5530,9 @@ These are not autolinks:
.
.
-< http://foo.bar >
+< https://foo.bar >
.
-<p>&lt; http://foo.bar &gt;</p>
+<p>&lt; https://foo.bar &gt;</p>
.
.
@@ -5548,9 +5548,9 @@ These are not autolinks:
.
.
-http://google.com
+https://google.com
.
-<p>http://google.com</p>
+<p>https://google.com</p>
.
.
diff --git a/core/testdata/packagedocs/referenceLinks.kotlin.md b/core/testdata/packagedocs/referenceLinks.kotlin.md
index ac7e4b48..f7b1edad 100644
--- a/core/testdata/packagedocs/referenceLinks.kotlin.md
+++ b/core/testdata/packagedocs/referenceLinks.kotlin.md
@@ -1,7 +1,6 @@
Core functions and types
-See [ref](http://example.com)
-Also, [example](http://example.com)
+See [ref](https://example.com)
+Also, [example](https://example.com)
- \ No newline at end of file
diff --git a/core/testdata/packagedocs/referenceLinks.md b/core/testdata/packagedocs/referenceLinks.md
index 7583ee9d..177dea0c 100644
--- a/core/testdata/packagedocs/referenceLinks.md
+++ b/core/testdata/packagedocs/referenceLinks.md
@@ -14,4 +14,4 @@ See [ref]
Also, [example][ref]
<!-- Refs -->
-[ref]: http://example.com
+[ref]: https://example.com
diff --git a/core/testdata/packagedocs/referenceLinks.module.md b/core/testdata/packagedocs/referenceLinks.module.md
index ddbdbe2f..08372175 100644
--- a/core/testdata/packagedocs/referenceLinks.module.md
+++ b/core/testdata/packagedocs/referenceLinks.module.md
@@ -4,6 +4,6 @@
The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections,
text and files.
-See [ref](http://example.com)
-Also, [example](http://example.com)
+See [ref](https://example.com)
+Also, [example](https://example.com)
diff --git a/core/testdata/properties/annotatedProperty.kt b/core/testdata/properties/annotatedProperty.kt
index 8990af29..3c12691b 100644
--- a/core/testdata/properties/annotatedProperty.kt
+++ b/core/testdata/properties/annotatedProperty.kt
@@ -1 +1 @@
-@Volatile var property = "test" \ No newline at end of file
+@Strictfp var property = "test" \ No newline at end of file
diff --git a/core/testdata/sourceLinks/dummy.kt b/core/testdata/sourceLinks/dummy.kt
new file mode 100644
index 00000000..cbaffe7c
--- /dev/null
+++ b/core/testdata/sourceLinks/dummy.kt
@@ -0,0 +1,6 @@
+/**
+ * Some doc.
+ */
+fun foo(){
+
+}