aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-11-28 15:58:10 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-11-28 15:58:10 +0000
commitecf8061396ac2d1bb0f75c37d48caf53c3be1ce4 (patch)
tree4058cd853f36166d424eee7e69e95ad3125d1189
parent7f79c3b49be99f3d45d099a30b78faa6d96e93b2 (diff)
parent77665b03848c6574ff3e40ec875c347d5eb41a8f (diff)
downloaddokka-androidx-versionedparcelable-release.tar.gz
Snap for 11149604 from 77665b03848c6574ff3e40ec875c347d5eb41a8f to androidx-versionedparcelable-releaseandroidx-versionedparcelable-release
Change-Id: Ia11dc58bb589d4d20dd8983fae63265d878b9752
-rw-r--r--.gitignore104
-rw-r--r--.idea/.name1
-rw-r--r--.idea/artifacts/dokka_jar.xml9
-rw-r--r--.idea/artifacts/dokka_zip.xml27
-rw-r--r--.idea/artifacts/javadoc_jar.xml8
-rw-r--r--.idea/codeStyles/Project.xml24
-rw-r--r--.idea/codeStyles/codeStyleConfig.xml5
-rw-r--r--.idea/compiler.xml55
-rw-r--r--.idea/copyright/profiles_settings.xml4
-rw-r--r--.idea/encodings.xml6
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml7
-rw-r--r--.idea/jarRepositories.xml70
-rw-r--r--.idea/kotlinc.xml11
-rw-r--r--.idea/misc.xml31
-rw-r--r--.idea/modules.xml68
-rw-r--r--.idea/scopes/scope_settings.xml5
-rw-r--r--Android.bp43
-rw-r--r--METADATA16
-rw-r--r--NOTICE202
-rw-r--r--OWNERS6
-rw-r--r--README.android234
-rw-r--r--README.md483
-rw-r--r--build-docs.xml20
-rw-r--r--build.gradle124
-rw-r--r--buildSrc/build.gradle10
-rw-r--r--buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy39
-rw-r--r--buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy84
-rw-r--r--buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy14
-rw-r--r--buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy71
-rw-r--r--busytown.gradle38
-rwxr-xr-xbusytown.sh6
-rw-r--r--core/build.gradle56
-rw-r--r--core/libs/kotlinx-html-jvm-0.6.8-google.jarbin0 -> 718519 bytes
-rw-r--r--core/samples/memberWithModifiers.java12
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt371
-rw-r--r--core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt42
-rw-r--r--core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt569
-rw-r--r--core/src/main/kotlin/Analysis/JavaResolveExtension.kt128
-rw-r--r--core/src/main/kotlin/DokkaBootstrapImpl.kt83
-rw-r--r--core/src/main/kotlin/Formats/AnalysisComponents.kt45
-rw-r--r--core/src/main/kotlin/Formats/DacHtmlFormat.kt949
-rw-r--r--core/src/main/kotlin/Formats/DacOutlineService.kt395
-rw-r--r--core/src/main/kotlin/Formats/ExtraOutlineServices.kt20
-rw-r--r--core/src/main/kotlin/Formats/FormatDescriptor.kt42
-rw-r--r--core/src/main/kotlin/Formats/FormatService.kt32
-rw-r--r--core/src/main/kotlin/Formats/GFMFormatService.kt61
-rw-r--r--core/src/main/kotlin/Formats/HtmlFormatService.kt168
-rw-r--r--core/src/main/kotlin/Formats/HtmlTemplateService.kt38
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt141
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt1171
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt165
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt154
-rw-r--r--core/src/main/kotlin/Formats/JekyllFormatService.kt44
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt224
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt186
-rw-r--r--core/src/main/kotlin/Formats/MarkdownFormatService.kt239
-rw-r--r--core/src/main/kotlin/Formats/OutlineService.kt29
-rw-r--r--core/src/main/kotlin/Formats/PackageListService.kt63
-rw-r--r--core/src/main/kotlin/Formats/StandardFormats.kt66
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt691
-rw-r--r--core/src/main/kotlin/Formats/YamlOutlineService.kt26
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt207
-rw-r--r--core/src/main/kotlin/Generation/FileGenerator.kt89
-rw-r--r--core/src/main/kotlin/Generation/Generator.kt29
-rw-r--r--core/src/main/kotlin/Generation/configurationImpl.kt67
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt392
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt709
-rw-r--r--core/src/main/kotlin/Kotlin/ContentBuilder.kt188
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt72
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt339
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt1177
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt258
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt68
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt25
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt463
-rw-r--r--core/src/main/kotlin/Languages/CommonLanguageService.kt118
-rw-r--r--core/src/main/kotlin/Languages/JavaLanguageService.kt179
-rw-r--r--core/src/main/kotlin/Languages/LanguageService.kt43
-rw-r--r--core/src/main/kotlin/Languages/NewJavaLanguageService.kt250
-rw-r--r--core/src/main/kotlin/Locations/Location.kt61
-rw-r--r--core/src/main/kotlin/Markdown/MarkdownProcessor.kt53
-rw-r--r--core/src/main/kotlin/Model/CodeNode.kt42
-rw-r--r--core/src/main/kotlin/Model/Content.kt296
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt311
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt86
-rw-r--r--core/src/main/kotlin/Model/ElementSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Model/PackageDocs.kt135
-rw-r--r--core/src/main/kotlin/Model/SourceLinks.kt56
-rw-r--r--core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt105
-rw-r--r--core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt52
-rw-r--r--core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt137
-rw-r--r--core/src/main/kotlin/Samples/SampleProcessingService.kt9
-rw-r--r--core/src/main/kotlin/Utilities/DokkaLogging.kt27
-rw-r--r--core/src/main/kotlin/Utilities/DokkaModules.kt81
-rw-r--r--core/src/main/kotlin/Utilities/DownloadSamples.kt88
-rw-r--r--core/src/main/kotlin/Utilities/Html.kt18
-rw-r--r--core/src/main/kotlin/Utilities/Path.kt5
-rw-r--r--core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt26
-rw-r--r--core/src/main/kotlin/Utilities/ServiceLocator.kt100
-rw-r--r--core/src/main/kotlin/Utilities/StringExtensions.kt51
-rw-r--r--core/src/main/kotlin/Utilities/Uri.kt40
-rw-r--r--core/src/main/kotlin/javadoc/docbase.kt525
-rw-r--r--core/src/main/kotlin/javadoc/dokka-adapters.kt39
-rw-r--r--core/src/main/kotlin/javadoc/reporter.kt34
-rw-r--r--core/src/main/kotlin/javadoc/source-position.kt19
-rw-r--r--core/src/main/kotlin/javadoc/tags.kt240
-rw-r--r--core/src/main/resources/META-INF/MANIFEST.MF4
-rw-r--r--core/src/main/resources/dokka/format/dac-as-java.properties2
-rw-r--r--core/src/main/resources/dokka/format/dac.properties2
-rw-r--r--core/src/main/resources/dokka/format/gfm.properties2
-rw-r--r--core/src/main/resources/dokka/format/html-as-java.properties2
-rw-r--r--core/src/main/resources/dokka/format/html.properties2
-rw-r--r--core/src/main/resources/dokka/format/java-layout-html-as-java.properties2
-rw-r--r--core/src/main/resources/dokka/format/java-layout-html.properties2
-rw-r--r--core/src/main/resources/dokka/format/javadoc.properties2
-rw-r--r--core/src/main/resources/dokka/format/jekyll.properties2
-rw-r--r--core/src/main/resources/dokka/format/kotlin-website-html.properties2
-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/format/markdown.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties2
-rw-r--r--core/src/main/resources/dokka/styles/style.css283
-rw-r--r--core/src/test/kotlin/Model/CodeNodeTest.kt14
-rw-r--r--core/src/test/kotlin/TestAPI.kt302
-rw-r--r--core/src/test/kotlin/format/DacFormatTest.kt58
-rw-r--r--core/src/test/kotlin/format/DacFormatTestCase.kt90
-rw-r--r--core/src/test/kotlin/format/FileGeneratorTestCase.kt35
-rw-r--r--core/src/test/kotlin/format/GFMFormatTest.kt28
-rw-r--r--core/src/test/kotlin/format/HtmlFormatTest.kt182
-rw-r--r--core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt114
-rw-r--r--core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt117
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt74
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt85
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt39
-rw-r--r--core/src/test/kotlin/format/MarkdownFormatTest.kt547
-rw-r--r--core/src/test/kotlin/format/PackageDocsTest.kt92
-rw-r--r--core/src/test/kotlin/issues/IssuesTest.kt28
-rw-r--r--core/src/test/kotlin/javadoc/JavadocTest.kt185
-rw-r--r--core/src/test/kotlin/markdown/ParserTest.kt154
-rw-r--r--core/src/test/kotlin/model/ClassTest.kt293
-rw-r--r--core/src/test/kotlin/model/CommentTest.kt186
-rw-r--r--core/src/test/kotlin/model/FunctionTest.kt251
-rw-r--r--core/src/test/kotlin/model/JavaTest.kt219
-rw-r--r--core/src/test/kotlin/model/KotlinAsJavaTest.kt39
-rw-r--r--core/src/test/kotlin/model/LinkTest.kt75
-rw-r--r--core/src/test/kotlin/model/PackageTest.kt116
-rw-r--r--core/src/test/kotlin/model/PropertyTest.kt112
-rw-r--r--core/src/test/kotlin/model/TypeAliasTest.kt132
-rw-r--r--core/src/test/kotlin/utilities/StringExtensionsTest.kt119
-rw-r--r--core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker1
-rw-r--r--core/testdata/classes/annotatedClass.kt1
-rw-r--r--core/testdata/classes/annotatedClassWithAnnotationParameters.kt1
-rw-r--r--core/testdata/classes/classWithCompanionObject.kt7
-rw-r--r--core/testdata/classes/classWithConstructor.kt1
-rw-r--r--core/testdata/classes/classWithFunction.kt4
-rw-r--r--core/testdata/classes/classWithProperty.kt3
-rw-r--r--core/testdata/classes/companionObjectExtension.kt10
-rw-r--r--core/testdata/classes/dataClass.kt1
-rw-r--r--core/testdata/classes/emptyClass.kt3
-rw-r--r--core/testdata/classes/emptyObject.kt3
-rw-r--r--core/testdata/classes/genericClass.kt3
-rw-r--r--core/testdata/classes/indirectOverride.kt9
-rw-r--r--core/testdata/classes/innerClass.kt5
-rw-r--r--core/testdata/classes/javaAnnotationClass.kt5
-rw-r--r--core/testdata/classes/notOpenClass.kt7
-rw-r--r--core/testdata/classes/privateCompanionObject.kt11
-rw-r--r--core/testdata/classes/sealedClass.kt1
-rw-r--r--core/testdata/classes/secondaryConstructor.kt5
-rw-r--r--core/testdata/classes/sinceKotlin.kt5
-rw-r--r--core/testdata/comments/codeBlockComment.kt14
-rw-r--r--core/testdata/comments/directive.kt35
-rw-r--r--core/testdata/comments/emptyDoc.kt1
-rw-r--r--core/testdata/comments/emptyDocButComment.kt2
-rw-r--r--core/testdata/comments/emptySection.kt6
-rw-r--r--core/testdata/comments/multilineDoc.kt7
-rw-r--r--core/testdata/comments/multilineDocWithComment.kt8
-rw-r--r--core/testdata/comments/multilineSection.kt7
-rw-r--r--core/testdata/comments/oneLineDoc.kt2
-rw-r--r--core/testdata/comments/oneLineDocWithComment.kt3
-rw-r--r--core/testdata/comments/oneLineDocWithEmptyLine.kt3
-rw-r--r--core/testdata/comments/quotes.kt2
-rw-r--r--core/testdata/comments/section1.kt5
-rw-r--r--core/testdata/comments/section2.kt6
-rw-r--r--core/testdata/format/JavaSupertype.html36
-rw-r--r--core/testdata/format/JavaSupertype.java8
-rw-r--r--core/testdata/format/accessor.kt5
-rw-r--r--core/testdata/format/accessor.md14
-rw-r--r--core/testdata/format/annotatedTypeParameter.kt2
-rw-r--r--core/testdata/format/annotatedTypeParameter.md5
-rw-r--r--core/testdata/format/annotationClass.kt1
-rw-r--r--core/testdata/format/annotationClass.md10
-rw-r--r--core/testdata/format/annotationClass.package.md8
-rw-r--r--core/testdata/format/annotationParams.kt1
-rw-r--r--core/testdata/format/annotationParams.md5
-rw-r--r--core/testdata/format/annotations.kt6
-rw-r--r--core/testdata/format/annotations.md18
-rw-r--r--core/testdata/format/arrayAverage.kt8
-rw-r--r--core/testdata/format/arrayAverage.md14
-rw-r--r--core/testdata/format/backtickInCodeBlock.kt9
-rw-r--r--core/testdata/format/backtickInCodeBlock.md12
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.html18
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.kt12
-rw-r--r--core/testdata/format/blankLineInsideCodeBlock.md14
-rw-r--r--core/testdata/format/bracket.html14
-rw-r--r--core/testdata/format/bracket.kt4
-rw-r--r--core/testdata/format/brokenLink.html14
-rw-r--r--core/testdata/format/brokenLink.kt4
-rw-r--r--core/testdata/format/classWithCompanionObject.html48
-rw-r--r--core/testdata/format/classWithCompanionObject.kt7
-rw-r--r--core/testdata/format/classWithCompanionObject.md18
-rw-r--r--core/testdata/format/codeBlock.html62
-rw-r--r--core/testdata/format/codeBlock.kt22
-rw-r--r--core/testdata/format/codeBlock.md37
-rw-r--r--core/testdata/format/codeBlockNoHtmlEscape.kt15
-rw-r--r--core/testdata/format/codeBlockNoHtmlEscape.md18
-rw-r--r--core/testdata/format/codeSpan.html14
-rw-r--r--core/testdata/format/codeSpan.kt4
-rw-r--r--core/testdata/format/companionImplements.kt9
-rw-r--r--core/testdata/format/companionImplements.md16
-rw-r--r--core/testdata/format/companionObjectExtension.kt10
-rw-r--r--core/testdata/format/companionObjectExtension.md14
-rw-r--r--core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html39
-rw-r--r--core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt6
-rw-r--r--core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java6
-rw-r--r--core/testdata/format/dac/deprecation/DeprecatedBar.kt17
-rw-r--r--core/testdata/format/dac/deprecation/DeprecatedFoo.java21
-rw-r--r--core/testdata/format/dac/deprecation/dac-as-java/Bar.html83
-rw-r--r--core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html39
-rw-r--r--core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html37
-rw-r--r--core/testdata/format/dac/deprecation/dac-as-java/Foo.html82
-rw-r--r--core/testdata/format/dac/deprecation/dac/Bar.html83
-rw-r--r--core/testdata/format/dac/deprecation/dac/DeprecatedBar.html39
-rw-r--r--core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html37
-rw-r--r--core/testdata/format/dac/deprecation/dac/Foo.html82
-rw-r--r--core/testdata/format/dac/inheritedMethods/Child.java8
-rw-r--r--core/testdata/format/dac/inheritedMethods/Parent.java10
-rw-r--r--core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html74
-rw-r--r--core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html92
-rw-r--r--core/testdata/format/dac/inheritedMethods/dac/Child.html99
-rw-r--r--core/testdata/format/dac/inheritedMethods/dac/Parent.html92
-rw-r--r--core/testdata/format/dac/javaClassLinks/Bar.java28
-rw-r--r--core/testdata/format/dac/javaClassLinks/Foo.java2
-rw-r--r--core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html147
-rw-r--r--core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html37
-rw-r--r--core/testdata/format/dac/javaClassLinks/dac/Bar.html147
-rw-r--r--core/testdata/format/dac/javaClassLinks/dac/Foo.html37
-rw-r--r--core/testdata/format/dac/javaConstructor/Foo.java4
-rw-r--r--core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html48
-rw-r--r--core/testdata/format/dac/javaConstructor/dac/Foo.html48
-rw-r--r--core/testdata/format/dac/javaDefaultConstructor/Foo.java2
-rw-r--r--core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html37
-rw-r--r--core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html37
-rw-r--r--core/testdata/format/dac/javaMethodVisibilities/Foo.java5
-rw-r--r--core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html97
-rw-r--r--core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html97
-rw-r--r--core/testdata/format/dac/javaSeeTag/Bar.kt5
-rw-r--r--core/testdata/format/dac/javaSeeTag/Foo.java7
-rw-r--r--core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html44
-rw-r--r--core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html57
-rw-r--r--core/testdata/format/dac/javaSeeTag/dac/Bar.html44
-rw-r--r--core/testdata/format/dac/javaSeeTag/dac/Foo.html64
-rw-r--r--core/testdata/format/deprecated.class.html59
-rw-r--r--core/testdata/format/deprecated.kt5
-rw-r--r--core/testdata/format/deprecated.package.html47
-rw-r--r--core/testdata/format/dynamicExtension.kt3
-rw-r--r--core/testdata/format/dynamicExtension.md10
-rw-r--r--core/testdata/format/dynamicType.kt2
-rw-r--r--core/testdata/format/dynamicType.md5
-rw-r--r--core/testdata/format/emptyDescription.kt5
-rw-r--r--core/testdata/format/emptyDescription.md8
-rw-r--r--core/testdata/format/entity.html27
-rw-r--r--core/testdata/format/entity.kt4
-rw-r--r--core/testdata/format/enumClass.kt4
-rw-r--r--core/testdata/format/enumClass.md11
-rw-r--r--core/testdata/format/enumClass.value.md5
-rw-r--r--core/testdata/format/enumRef.kt4
-rw-r--r--core/testdata/format/enumRef.md8
-rw-r--r--core/testdata/format/exceptionClass.kt1
-rw-r--r--core/testdata/format/exceptionClass.md10
-rw-r--r--core/testdata/format/exceptionClass.package.md8
-rw-r--r--core/testdata/format/exclInCodeBlock.kt5
-rw-r--r--core/testdata/format/exclInCodeBlock.md8
-rw-r--r--core/testdata/format/extensionFunctionParameter.kt1
-rw-r--r--core/testdata/format/extensionFunctionParameter.md5
-rw-r--r--core/testdata/format/extensionScope.kt14
-rw-r--r--core/testdata/format/extensionScope.md8
-rw-r--r--core/testdata/format/extensionWithDocumentedReceiver.kt6
-rw-r--r--core/testdata/format/extensionWithDocumentedReceiver.md11
-rw-r--r--core/testdata/format/extensions.class.md7
-rw-r--r--core/testdata/format/extensions.kt19
-rw-r--r--core/testdata/format/extensions.package.md8
-rw-r--r--core/testdata/format/externalReferenceLink.kt10
-rw-r--r--core/testdata/format/externalReferenceLink.md10
-rw-r--r--core/testdata/format/functionWithDefaultParameter.kt1
-rw-r--r--core/testdata/format/functionWithDefaultParameter.md5
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.html103
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.kt9
-rw-r--r--core/testdata/format/functionalTypeWithNamedParameters.md45
-rw-r--r--core/testdata/format/genericInheritedExtensions.kt11
-rw-r--r--core/testdata/format/genericInheritedExtensions.md15
-rw-r--r--core/testdata/format/gfm/listInTableCell.kt8
-rw-r--r--core/testdata/format/gfm/listInTableCell.md17
-rw-r--r--core/testdata/format/gfm/sample.kt18
-rw-r--r--core/testdata/format/gfm/sample.md20
-rw-r--r--core/testdata/format/htmlEscaping.html14
-rw-r--r--core/testdata/format/htmlEscaping.kt4
-rw-r--r--core/testdata/format/inapplicableExtensionFunctions.kt11
-rw-r--r--core/testdata/format/inapplicableExtensionFunctions.md14
-rw-r--r--core/testdata/format/indentedCodeBlock.html17
-rw-r--r--core/testdata/format/indentedCodeBlock.kt10
-rw-r--r--core/testdata/format/indentedCodeBlock.md14
-rw-r--r--core/testdata/format/inheritedCompanionObjectProperties.kt18
-rw-r--r--core/testdata/format/inheritedCompanionObjectProperties.md30
-rw-r--r--core/testdata/format/inheritedExtensions.kt11
-rw-r--r--core/testdata/format/inheritedExtensions.md15
-rw-r--r--core/testdata/format/inheritedLink.1.kt10
-rw-r--r--core/testdata/format/inheritedLink.kt11
-rw-r--r--core/testdata/format/inheritedLink.md17
-rw-r--r--core/testdata/format/inheritedMembers.kt12
-rw-r--r--core/testdata/format/inheritedMembers.md26
-rw-r--r--core/testdata/format/java-layout-html/ConstJava.html93
-rw-r--r--core/testdata/format/java-layout-html/ConstJava.java10
-rw-r--r--core/testdata/format/java-layout-html/codeBlocks.html49
-rw-r--r--core/testdata/format/java-layout-html/codeBlocks.kt26
-rw-r--r--core/testdata/format/java-layout-html/const.html99
-rw-r--r--core/testdata/format/java-layout-html/const.kt14
-rw-r--r--core/testdata/format/java-layout-html/const.package-summary.html35
-rw-r--r--core/testdata/format/java-layout-html/externalClassExtension.kt5
-rw-r--r--core/testdata/format/java-layout-html/externalClassExtension.package-summary.html25
-rw-r--r--core/testdata/format/java-layout-html/genericExtension.html99
-rw-r--r--core/testdata/format/java-layout-html/genericExtension.kt10
-rw-r--r--core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt23
-rw-r--r--core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html80
-rw-r--r--core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt16
-rw-r--r--core/testdata/format/java-layout-html/inboundLinksTestPackageList3
-rw-r--r--core/testdata/format/java-layout-html/sections.html64
-rw-r--r--core/testdata/format/java-layout-html/sections.kt15
-rw-r--r--core/testdata/format/java-layout-html/simple.html177
-rw-r--r--core/testdata/format/java-layout-html/simple.kt8
-rw-r--r--core/testdata/format/java-layout-html/topLevel.kt15
-rw-r--r--core/testdata/format/java-layout-html/topLevel.package-summary.html97
-rw-r--r--core/testdata/format/java-layout-html/unresolvedExternalClass.html63
-rw-r--r--core/testdata/format/java-layout-html/unresolvedExternalClass.kt13
-rw-r--r--core/testdata/format/javaCodeInParam.java5
-rw-r--r--core/testdata/format/javaCodeInParam.md14
-rw-r--r--core/testdata/format/javaCodeLiteralTags.java6
-rw-r--r--core/testdata/format/javaCodeLiteralTags.md16
-rw-r--r--core/testdata/format/javaDeprecated.html14
-rw-r--r--core/testdata/format/javaDeprecated.java5
-rw-r--r--core/testdata/format/javaLinkTag.html39
-rw-r--r--core/testdata/format/javaLinkTag.java6
-rw-r--r--core/testdata/format/javaLinkTagWithLabel.html39
-rw-r--r--core/testdata/format/javaLinkTagWithLabel.java6
-rw-r--r--core/testdata/format/javaSample.java5
-rw-r--r--core/testdata/format/javaSample.md41
-rw-r--r--core/testdata/format/javaSeeTag.html38
-rw-r--r--core/testdata/format/javaSeeTag.java6
-rw-r--r--core/testdata/format/javaSpaceInAuthor.java5
-rw-r--r--core/testdata/format/javaSpaceInAuthor.md13
-rw-r--r--core/testdata/format/javadocCodeMultiline.java10
-rw-r--r--core/testdata/format/javadocCodeMultiline.md26
-rw-r--r--core/testdata/format/javadocHtml.java14
-rw-r--r--core/testdata/format/javadocHtml.md28
-rw-r--r--core/testdata/format/javadocOrderedList.java8
-rw-r--r--core/testdata/format/javadocOrderedList.md17
-rw-r--r--core/testdata/format/jdkLinks.kt7
-rw-r--r--core/testdata/format/jdkLinks.md14
-rw-r--r--core/testdata/format/linkWithLabel.html39
-rw-r--r--core/testdata/format/linkWithLabel.kt6
-rw-r--r--core/testdata/format/linkWithStarProjection.html24
-rw-r--r--core/testdata/format/linkWithStarProjection.kt3
-rw-r--r--core/testdata/format/linksInEmphasis.kt13
-rw-r--r--core/testdata/format/linksInEmphasis.md23
-rw-r--r--core/testdata/format/linksInHeaders.kt24
-rw-r--r--core/testdata/format/linksInHeaders.md34
-rw-r--r--core/testdata/format/linksInStrong.kt13
-rw-r--r--core/testdata/format/linksInStrong.md23
-rw-r--r--core/testdata/format/markdownInLinks.html14
-rw-r--r--core/testdata/format/markdownInLinks.kt4
-rw-r--r--core/testdata/format/memberExtension.kt8
-rw-r--r--core/testdata/format/memberExtension.md10
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt7
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md8
-rw-r--r--core/testdata/format/multiplatform/groupNode/js.kt8
-rw-r--r--core/testdata/format/multiplatform/groupNode/jvm.kt9
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.md20
-rw-r--r--core/testdata/format/multiplatform/groupNode/multiplatform.package.md13
-rw-r--r--core/testdata/format/multiplatform/implied/foo.md24
-rw-r--r--core/testdata/format/multiplatform/implied/js.kt16
-rw-r--r--core/testdata/format/multiplatform/implied/jvm.kt16
-rw-r--r--core/testdata/format/multiplatform/merge/js.kt7
-rw-r--r--core/testdata/format/multiplatform/merge/jvm.kt8
-rw-r--r--core/testdata/format/multiplatform/merge/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/foo.md26
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/js.kt16
-rw-r--r--core/testdata/format/multiplatform/mergeMembers/jvm.kt16
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/foo.md22
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/js.kt2
-rw-r--r--core/testdata/format/multiplatform/omitRedundant/jvm.kt11
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt3
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt3
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt5
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md10
-rw-r--r--core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md10
-rw-r--r--core/testdata/format/multiplatform/simple/js.kt7
-rw-r--r--core/testdata/format/multiplatform/simple/jvm.kt8
-rw-r--r--core/testdata/format/multiplatform/simple/multiplatform.package.md9
-rw-r--r--core/testdata/format/multipleTypeParameterConstraints.kt11
-rw-r--r--core/testdata/format/multipleTypeParameterConstraints.md18
-rw-r--r--core/testdata/format/nestedLists.kt31
-rw-r--r--core/testdata/format/nestedLists.md43
-rw-r--r--core/testdata/format/newlineInTableCell.kt6
-rw-r--r--core/testdata/format/newlineInTableCell.package.md8
-rw-r--r--core/testdata/format/notPublishedTypeAliasAutoExpansion.kt13
-rw-r--r--core/testdata/format/notPublishedTypeAliasAutoExpansion.md9
-rw-r--r--core/testdata/format/nullability.kt5
-rw-r--r--core/testdata/format/nullability.md14
-rw-r--r--core/testdata/format/operatorOverloading.kt3
-rw-r--r--core/testdata/format/operatorOverloading.md5
-rw-r--r--core/testdata/format/orderedList.html30
-rw-r--r--core/testdata/format/orderedList.kt8
-rw-r--r--core/testdata/format/overloads.html26
-rw-r--r--core/testdata/format/overloads.kt5
-rw-r--r--core/testdata/format/overloadsWithDescription.html20
-rw-r--r--core/testdata/format/overloadsWithDescription.kt15
-rw-r--r--core/testdata/format/overloadsWithDifferentDescriptions.html25
-rw-r--r--core/testdata/format/overloadsWithDifferentDescriptions.kt15
-rw-r--r--core/testdata/format/overridingFunction.kt7
-rw-r--r--core/testdata/format/overridingFunction.md8
-rw-r--r--core/testdata/format/paramTag.kt6
-rw-r--r--core/testdata/format/paramTag.md12
-rw-r--r--core/testdata/format/parameterAnchor.html17
-rw-r--r--core/testdata/format/parameterAnchor.kt6
-rw-r--r--core/testdata/format/parenthesis.html14
-rw-r--r--core/testdata/format/parenthesis.kt4
-rw-r--r--core/testdata/format/propertyVar.kt1
-rw-r--r--core/testdata/format/propertyVar.md5
-rw-r--r--core/testdata/format/qualifiedNameLink.kt6
-rw-r--r--core/testdata/format/qualifiedNameLink.md8
-rw-r--r--core/testdata/format/receiverParameterTypeBound.kt5
-rw-r--r--core/testdata/format/receiverParameterTypeBound.md14
-rw-r--r--core/testdata/format/receiverReference.kt6
-rw-r--r--core/testdata/format/receiverReference.md6
-rw-r--r--core/testdata/format/referenceLink.kt16
-rw-r--r--core/testdata/format/referenceLink.md17
-rw-r--r--core/testdata/format/reifiedTypeParameter.kt3
-rw-r--r--core/testdata/format/reifiedTypeParameter.md5
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt3
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md6
-rw-r--r--core/testdata/format/returnWithLink.html15
-rw-r--r--core/testdata/format/returnWithLink.kt4
-rw-r--r--core/testdata/format/sampleByFQName.kt12
-rw-r--r--core/testdata/format/sampleByFQName.md17
-rw-r--r--core/testdata/format/sampleByShortName.kt12
-rw-r--r--core/testdata/format/sampleByShortName.md17
-rw-r--r--core/testdata/format/see.html46
-rw-r--r--core/testdata/format/see.kt12
-rw-r--r--core/testdata/format/shadowedExtensionFunctions.kt18
-rw-r--r--core/testdata/format/shadowedExtensionFunctions.md15
-rw-r--r--core/testdata/format/sinceKotlin.html28
-rw-r--r--core/testdata/format/sinceKotlin.kt5
-rw-r--r--core/testdata/format/sinceKotlin.md14
-rw-r--r--core/testdata/format/sinceKotlin.package.md10
-rw-r--r--core/testdata/format/sinceKotlinWide.kt11
-rw-r--r--core/testdata/format/sinceKotlinWide.package.md11
-rw-r--r--core/testdata/format/starProjection.kt3
-rw-r--r--core/testdata/format/starProjection.md6
-rw-r--r--core/testdata/format/summarizeSignatures.kt20
-rw-r--r--core/testdata/format/summarizeSignatures.md14
-rw-r--r--core/testdata/format/summarizeSignaturesProperty.kt20
-rw-r--r--core/testdata/format/summarizeSignaturesProperty.md14
-rw-r--r--core/testdata/format/suspendParam.kt3
-rw-r--r--core/testdata/format/suspendParam.md5
-rw-r--r--core/testdata/format/suspendParam.package.md8
-rw-r--r--core/testdata/format/throwsTag.kt5
-rw-r--r--core/testdata/format/throwsTag.md11
-rw-r--r--core/testdata/format/tokensInEmphasis.kt10
-rw-r--r--core/testdata/format/tokensInEmphasis.md20
-rw-r--r--core/testdata/format/tokensInHeaders.kt27
-rw-r--r--core/testdata/format/tokensInHeaders.md37
-rw-r--r--core/testdata/format/tokensInStrong.kt10
-rw-r--r--core/testdata/format/tokensInStrong.md20
-rw-r--r--core/testdata/format/tripleBackticks.html16
-rw-r--r--core/testdata/format/tripleBackticks.kt7
-rw-r--r--core/testdata/format/typeAliases.kt27
-rw-r--r--core/testdata/format/typeAliases.md104
-rw-r--r--core/testdata/format/typeAliases.package.md24
-rw-r--r--core/testdata/format/typeLink.html24
-rw-r--r--core/testdata/format/typeLink.kt5
-rw-r--r--core/testdata/format/typeParameterBounds.kt7
-rw-r--r--core/testdata/format/typeParameterBounds.md11
-rw-r--r--core/testdata/format/typeParameterReference.kt6
-rw-r--r--core/testdata/format/typeParameterReference.md8
-rw-r--r--core/testdata/format/typeParameterVariance.kt5
-rw-r--r--core/testdata/format/typeParameterVariance.md14
-rw-r--r--core/testdata/format/typeProjectionVariance.kt1
-rw-r--r--core/testdata/format/typeProjectionVariance.md6
-rw-r--r--core/testdata/format/uninterpretedEmphasisCharacters.html15
-rw-r--r--core/testdata/format/uninterpretedEmphasisCharacters.kt5
-rw-r--r--core/testdata/format/unorderedLists.kt36
-rw-r--r--core/testdata/format/unorderedLists.md47
-rw-r--r--core/testdata/format/varargsFunction.kt1
-rw-r--r--core/testdata/format/varargsFunction.md5
-rw-r--r--core/testdata/format/website-html/dataTags/jre7.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/js.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/jvm.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/multiplatform.package.html71
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt0
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/js.kt8
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html38
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html36
-rw-r--r--core/testdata/format/website-html/dropImport.html11
-rw-r--r--core/testdata/format/website-html/dropImport.kt12
-rw-r--r--core/testdata/format/website-html/newLinesInImportList.html12
-rw-r--r--core/testdata/format/website-html/newLinesInImportList.kt12
-rw-r--r--core/testdata/format/website-html/newLinesInSamples.html19
-rw-r--r--core/testdata/format/website-html/newLinesInSamples.kt19
-rw-r--r--core/testdata/format/website-html/overloadGroup.html16
-rw-r--r--core/testdata/format/website-html/overloadGroup.kt15
-rw-r--r--core/testdata/format/website-html/returnTag.html9
-rw-r--r--core/testdata/format/website-html/returnTag.kt11
-rw-r--r--core/testdata/format/website-html/sample.html19
-rw-r--r--core/testdata/format/website-html/sample.kt16
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.html22
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.kt32
-rw-r--r--core/testdata/format/website-samples/dropImport.kt12
-rw-r--r--core/testdata/format/website-samples/dropImport.md23
-rw-r--r--core/testdata/format/website-samples/newLinesInImportList.kt12
-rw-r--r--core/testdata/format/website-samples/newLinesInImportList.md24
-rw-r--r--core/testdata/format/website-samples/newLinesInSamples.kt19
-rw-r--r--core/testdata/format/website-samples/newLinesInSamples.md31
-rw-r--r--core/testdata/format/website-samples/sample.kt16
-rw-r--r--core/testdata/format/website-samples/sample.md39
-rw-r--r--core/testdata/format/website-samples/sampleWithAsserts.kt15
-rw-r--r--core/testdata/format/website-samples/sampleWithAsserts.md24
-rw-r--r--core/testdata/format/website/dataTags/jre7.kt11
-rw-r--r--core/testdata/format/website/dataTags/js.kt11
-rw-r--r--core/testdata/format/website/dataTags/jvm.kt11
-rw-r--r--core/testdata/format/website/dataTags/multiplatform.package.md71
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/jre7.kt0
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/js.kt8
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/multiplatform.md56
-rw-r--r--core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md43
-rw-r--r--core/testdata/format/website/overloadGroup.kt15
-rw-r--r--core/testdata/format/website/overloadGroup.md34
-rw-r--r--core/testdata/format/website/returnTag.kt11
-rw-r--r--core/testdata/format/website/returnTag.md20
-rw-r--r--core/testdata/format/website/sample.kt16
-rw-r--r--core/testdata/format/website/sample.md29
-rw-r--r--core/testdata/functions/annotatedFunction.kt2
-rw-r--r--core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt7
-rw-r--r--core/testdata/functions/function.kt5
-rw-r--r--core/testdata/functions/functionWithAnnotatedLambdaParam.kt7
-rw-r--r--core/testdata/functions/functionWithAnnotatedParam.kt7
-rw-r--r--core/testdata/functions/functionWithDefaultParameter.kt1
-rw-r--r--core/testdata/functions/functionWithNoinlineParam.kt2
-rw-r--r--core/testdata/functions/functionWithNotDocumentedAnnotation.kt2
-rw-r--r--core/testdata/functions/functionWithParams.kt8
-rw-r--r--core/testdata/functions/functionWithReceiver.kt11
-rw-r--r--core/testdata/functions/genericFunction.kt5
-rw-r--r--core/testdata/functions/genericFunctionWithConstraints.kt6
-rw-r--r--core/testdata/functions/inlineFunction.kt2
-rw-r--r--core/testdata/functions/sinceKotlin.kt5
-rw-r--r--core/testdata/issues/errorClasses.kt20
-rw-r--r--core/testdata/java/InheritorLinks.java7
-rw-r--r--core/testdata/java/InnerClass.java4
-rw-r--r--core/testdata/java/annotatedAnnotation.java6
-rw-r--r--core/testdata/java/arrayType.java5
-rw-r--r--core/testdata/java/constructors.java5
-rw-r--r--core/testdata/java/deprecation.java6
-rw-r--r--core/testdata/java/enumValues.java3
-rw-r--r--core/testdata/java/field.java4
-rw-r--r--core/testdata/java/hideAnnotation.java27
-rw-r--r--core/testdata/java/javaLangObject.java3
-rw-r--r--core/testdata/java/member.java11
-rw-r--r--core/testdata/java/memberWithModifiers.java12
-rw-r--r--core/testdata/java/staticMethod.java4
-rw-r--r--core/testdata/java/superClass.java2
-rw-r--r--core/testdata/java/suppressTag.java10
-rw-r--r--core/testdata/java/typeParameter.java3
-rw-r--r--core/testdata/java/varargs.java3
-rw-r--r--core/testdata/javadoc/blankLineInsideCodeBlock.kt12
-rw-r--r--core/testdata/javadoc/bytearr.kt7
-rw-r--r--core/testdata/javadoc/companionMethodReference.kt13
-rw-r--r--core/testdata/javadoc/deprecated.java28
-rw-r--r--core/testdata/javadoc/exception.kt5
-rw-r--r--core/testdata/javadoc/internal.kt8
-rw-r--r--core/testdata/javadoc/jvmname.kt6
-rw-r--r--core/testdata/javadoc/kdocKeywordsOnMethod.kt12
-rw-r--r--core/testdata/javadoc/obj.kt7
-rw-r--r--core/testdata/javadoc/paramlink.kt10
-rw-r--r--core/testdata/javadoc/stringarr.kt8
-rw-r--r--core/testdata/javadoc/suppress.kt37
-rw-r--r--core/testdata/javadoc/typealiases.kt11
-rw-r--r--core/testdata/javadoc/types.kt4
-rw-r--r--core/testdata/links/linkToConstantWithUnderscores.kt8
-rw-r--r--core/testdata/links/linkToJDK.kt0
-rw-r--r--core/testdata/links/linkToMember.kt6
-rw-r--r--core/testdata/links/linkToPackage.kt8
-rw-r--r--core/testdata/links/linkToParam.kt5
-rw-r--r--core/testdata/links/linkToQualifiedMember.kt6
-rw-r--r--core/testdata/links/linkToSelf.kt6
-rw-r--r--core/testdata/markdown/spec.txt6150
-rw-r--r--core/testdata/packagedocs/referenceLinks.kotlin.md7
-rw-r--r--core/testdata/packagedocs/referenceLinks.md17
-rw-r--r--core/testdata/packagedocs/referenceLinks.module.md9
-rw-r--r--core/testdata/packagedocs/stdlib.md11
-rw-r--r--core/testdata/packages/classInPackage.kt3
-rw-r--r--core/testdata/packages/dottedNamePackage.kt1
-rw-r--r--core/testdata/packages/rootPackage.kt0
-rw-r--r--core/testdata/packages/simpleNamePackage.kt1
-rw-r--r--core/testdata/packages/simpleNamePackage2.kt1
-rw-r--r--core/testdata/properties/annotatedProperty.kt1
-rw-r--r--core/testdata/properties/propertyOverride.kt7
-rw-r--r--core/testdata/properties/propertyWithReceiver.kt2
-rw-r--r--core/testdata/properties/sinceKotlin.kt5
-rw-r--r--core/testdata/properties/valueProperty.kt1
-rw-r--r--core/testdata/properties/valuePropertyWithGetter.kt2
-rw-r--r--core/testdata/properties/variableProperty.kt1
-rw-r--r--core/testdata/properties/variablePropertyWithAccessors.kt4
-rw-r--r--core/testdata/typealias/asTypeBoundWithVariance.kt7
-rw-r--r--core/testdata/typealias/chain.kt8
-rw-r--r--core/testdata/typealias/deprecated.kt7
-rw-r--r--core/testdata/typealias/documented.kt9
-rw-r--r--core/testdata/typealias/functional.kt10
-rw-r--r--core/testdata/typealias/generic.kt7
-rw-r--r--core/testdata/typealias/inheritanceFromTypeAlias.kt7
-rw-r--r--core/testdata/typealias/simple.kt5
-rw-r--r--core/testdata/typealias/sinceKotlin.kt5
-rw-r--r--gradle.properties22
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 54708 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xgradlew172
-rw-r--r--gradlew.bat84
-rw-r--r--integration/build.gradle26
-rw-r--r--integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt10
-rw-r--r--integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt72
-rw-r--r--integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt122
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jarbin1399 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jarbin7371 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom15
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jarbin1399 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jarbin7373 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom15
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jarbin1399 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jarbin7381 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom15
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml13
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jarbin53531621 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom8
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jarbin53566528 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom8
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jarbin53516062 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom8
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml13
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jarbin5672 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jarbin143961 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom21
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jarbin5672 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jarbin143961 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom21
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jarbin5672 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jarbin143966 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom21
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml13
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jarbin6791 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jarbin26075 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom63
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jarbin6791 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jarbin26078 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom63
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jarbin6791 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jarbin26088 -> 0 bytes
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom63
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha11
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml13
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md51
-rw-r--r--maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha11
-rw-r--r--package-list217
-rw-r--r--runners/android-gradle-plugin/build.gradle102
-rw-r--r--runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt39
-rw-r--r--runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties2
-rw-r--r--runners/ant/build.gradle18
-rw-r--r--runners/ant/src/main/kotlin/ant/dokka.kt152
-rw-r--r--runners/ant/src/main/resources/dokka-antlib.xml3
-rw-r--r--runners/build.gradle7
-rw-r--r--runners/cli/build.gradle16
-rw-r--r--runners/cli/src/main/kotlin/cli/main.kt221
-rw-r--r--runners/fatjar/build.gradle50
-rw-r--r--runners/gradle-integration-tests/android-licenses/android-sdk-license2
-rw-r--r--runners/gradle-integration-tests/android-licenses/android-sdk-preview-license2
-rw-r--r--runners/gradle-integration-tests/build.gradle59
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt51
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt45
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt108
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt34
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt52
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt73
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt55
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt25
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt57
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt74
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt56
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/build.gradle50
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml26
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java34
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt28
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml5
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml10
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml20
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/build.gradle21
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/fileTree.txt19
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/settings.gradle3
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle50
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml26
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java34
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt28
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml5
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml10
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml20
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle21
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt21
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle3
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle20
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt14
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle1
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java5
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle39
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml4
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt13
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list1
-rw-r--r--runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle5
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle75
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml9
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt14
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml3
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml26
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java34
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt28
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml24
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml6
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml5
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml10
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml20
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle21
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt45
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle3
-rw-r--r--runners/gradle-integration-tests/testData/basic/build.gradle40
-rw-r--r--runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.classbin0 -> 670 bytes
-rw-r--r--runners/gradle-integration-tests/testData/basic/fileTree.txt33
-rw-r--r--runners/gradle-integration-tests/testData/basic/settings.gradle1
-rw-r--r--runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt45
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle32
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt33
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle3
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle6
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt31
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle7
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt31
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/build.gradle39
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt10
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt11
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/settings.gradle1
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt6
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt11
-rw-r--r--runners/gradle-plugin/build.gradle121
-rw-r--r--runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt46
-rw-r--r--runners/gradle-plugin/src/main/kotlin/main.kt478
-rw-r--r--runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties2
-rw-r--r--runners/maven-plugin/build.gradle112
-rw-r--r--runners/maven-plugin/pom.tpl.xml26
-rw-r--r--runners/maven-plugin/src/main/kotlin/DokkaMojo.kt252
-rw-r--r--runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt18
-rw-r--r--settings.gradle12
-rw-r--r--test/playground.kt99
874 files changed, 38447 insertions, 452 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..8227df3c3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,104 @@
+# mac
+.DS_Store
+
+doc
+### Maven template
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+### Java template
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/dictionaries
+.idea/vcs.xml
+.idea/shelf
+.idea/jsLibraryMappings.xml
+.idea/runConfigurations
+.idea/caches
+
+# Sensitive or high-churn files:
+.idea/dataSources.ids
+.idea/dataSources.xml
+.idea/dataSources.local.xml
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# Gradle:
+.idea/gradle.xml
+.idea/libraries
+
+# Mongo Explorer plugin:
+.idea/mongoSettings.xml
+
+## File-based project format:
+*.iws
+*.iml
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+### Gradle template
+.gradle
+build/
+runners/android-gradle-plugin/out/
+runners/ant/out/
+runners/gradle-integration-tests/out/
+runners/gradle-plugin/out/
+core/out/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+!lib/*.jar
+
+local.properties
+android.local.properties
+
+!runners/gradle-integration-tests/testData/basic/classDir/**/*.class
+
+/samples
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 000000000..c8bb43f20
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+dokka \ No newline at end of file
diff --git a/.idea/artifacts/dokka_jar.xml b/.idea/artifacts/dokka_jar.xml
new file mode 100644
index 000000000..5998df7be
--- /dev/null
+++ b/.idea/artifacts/dokka_jar.xml
@@ -0,0 +1,9 @@
+<component name="ArtifactManager">
+ <artifact type="jar" name="dokka:jar">
+ <output-path>$PROJECT_DIR$/out/artifacts/</output-path>
+ <root id="archive" name="dokka.jar">
+ <element id="module-output" name="dokka" />
+ <element id="module-output" name="ant" />
+ </root>
+ </artifact>
+</component> \ No newline at end of file
diff --git a/.idea/artifacts/dokka_zip.xml b/.idea/artifacts/dokka_zip.xml
new file mode 100644
index 000000000..b636e0972
--- /dev/null
+++ b/.idea/artifacts/dokka_zip.xml
@@ -0,0 +1,27 @@
+<component name="ArtifactManager">
+ <artifact name="dokka:zip">
+ <output-path>$PROJECT_DIR$/out/artifacts</output-path>
+ <root id="root">
+ <element id="archive" name="dokka.zip">
+ <element id="directory" name="lib">
+ <element id="artifact" artifact-name="dokka:jar" />
+ <element id="library" level="project" name="trove4j" />
+ <element id="library" level="project" name="jps-model" />
+ <element id="library" level="project" name="asm" />
+ <element id="library" level="project" name="protobuf" />
+ <element id="library" level="project" name="picocontainer" />
+ <element id="library" level="project" name="intellij-core-analysis" />
+ <element id="library" level="project" name="markdown" />
+ <element id="library" level="project" name="kotlin-for-upsource" />
+ <element id="library" level="project" name="jsoup" />
+ <element id="library" level="project" name="jdom" />
+ <element id="library" level="project" name="ant-1.9.4" />
+ <element id="library" level="project" name="cli-parser" />
+ <element id="library" level="project" name="guava" />
+ <element id="library" level="project" name="com.google.inject:guice:4.0" />
+ <element id="artifact" artifact-name="javadoc:jar" />
+ </element>
+ </element>
+ </root>
+ </artifact>
+</component> \ No newline at end of file
diff --git a/.idea/artifacts/javadoc_jar.xml b/.idea/artifacts/javadoc_jar.xml
new file mode 100644
index 000000000..0f0eee5b4
--- /dev/null
+++ b/.idea/artifacts/javadoc_jar.xml
@@ -0,0 +1,8 @@
+<component name="ArtifactManager">
+ <artifact type="jar" name="javadoc:jar">
+ <output-path>$PROJECT_DIR$/out/artifacts/javadoc_jar</output-path>
+ <root id="archive" name="javadoc.jar">
+ <element id="module-output" name="javadoc" />
+ </root>
+ </artifact>
+</component> \ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 000000000..ba49a93ea
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,24 @@
+<component name="ProjectCodeStyleConfiguration">
+ <code_scheme name="Project" version="173">
+ <JetCodeStyleSettings>
+ <option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="false" />
+ <option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="false" />
+ <option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="false" />
+ <option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="false" />
+ <option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="false" />
+ <option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="false" />
+ <option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="1" />
+ </JetCodeStyleSettings>
+ <codeStyleSettings language="kotlin">
+ <option name="CALL_PARAMETERS_WRAP" value="5" />
+ <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
+ <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
+ <option name="METHOD_PARAMETERS_WRAP" value="5" />
+ <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
+ <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
+ <option name="EXTENDS_LIST_WRAP" value="1" />
+ <option name="METHOD_CALL_CHAIN_WRAP" value="1" />
+ <option name="ASSIGNMENT_WRAP" value="1" />
+ </codeStyleSettings>
+ </code_scheme>
+</component> \ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 000000000..79ee123c2
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+ <state>
+ <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+ </state>
+</component> \ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 000000000..4694bee54
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <bytecodeTargetLevel>
+ <module name="android-gradle-plugin_main" target="1.8" />
+ <module name="android-gradle-plugin_test" target="1.8" />
+ <module name="ant_main" target="1.8" />
+ <module name="ant_test" target="1.8" />
+ <module name="buildSrc_main" target="1.8" />
+ <module name="buildSrc_test" target="1.8" />
+ <module name="cli_main" target="1.8" />
+ <module name="cli_test" target="1.8" />
+ <module name="core_main" target="1.8" />
+ <module name="core_test" target="1.8" />
+ <module name="dokka.buildSrc.main" target="1.8" />
+ <module name="dokka.buildSrc.test" target="1.8" />
+ <module name="dokka.core.main" target="1.8" />
+ <module name="dokka.core.test" target="1.8" />
+ <module name="dokka.integration.main" target="1.8" />
+ <module name="dokka.integration.test" target="1.8" />
+ <module name="dokka.runners.android-gradle-plugin.main" target="1.8" />
+ <module name="dokka.runners.android-gradle-plugin.test" target="1.8" />
+ <module name="dokka.runners.ant.main" target="1.8" />
+ <module name="dokka.runners.ant.test" target="1.8" />
+ <module name="dokka.runners.cli.main" target="1.8" />
+ <module name="dokka.runners.cli.test" target="1.8" />
+ <module name="dokka.runners.fatjar.main" target="1.8" />
+ <module name="dokka.runners.fatjar.test" target="1.8" />
+ <module name="dokka.runners.gradle-integration-tests.main" target="1.8" />
+ <module name="dokka.runners.gradle-integration-tests.test" target="1.8" />
+ <module name="dokka.runners.gradle-plugin.main" target="1.8" />
+ <module name="dokka.runners.gradle-plugin.test" target="1.8" />
+ <module name="fatjar_main" target="1.8" />
+ <module name="fatjar_test" target="1.8" />
+ <module name="gradle-integration-tests_main" target="1.8" />
+ <module name="gradle-integration-tests_test" target="1.8" />
+ <module name="gradle-plugin_main" target="1.8" />
+ <module name="gradle-plugin_test" target="1.8" />
+ <module name="integration_main" target="1.8" />
+ <module name="integration_test" target="1.8" />
+ <module name="maven-plugin_main" target="1.8" />
+ <module name="maven-plugin_test" target="1.8" />
+ </bytecodeTargetLevel>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 000000000..f4aa08e3a
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component name="CopyrightManager">
+ <settings default="" />
+</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..f75895965
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..06bda8c9c
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0">
+ <option name="myName" value="Project Default" />
+ <inspection_tool class="MemberVisibilityCanPrivate" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+ <inspection_tool class="PackageDirectoryMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
+ </profile>
+</component> \ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 000000000..d747fa6cc
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="RemoteRepositoriesConfiguration">
+ <remote-repository>
+ <option name="id" value="central" />
+ <option name="name" value="Maven Central repository" />
+ <option name="url" value="https://repo1.maven.org/maven2" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="jboss.community" />
+ <option name="name" value="JBoss Community repository" />
+ <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven5" />
+ <option name="name" value="maven5" />
+ <option name="url" value="https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/1.3.20-dev-564/maven" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven6" />
+ <option name="name" value="maven6" />
+ <option name="url" value="https://dl.bintray.com/kotlin/kotlinx.html" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="MavenRepo" />
+ <option name="name" value="MavenRepo" />
+ <option name="url" value="https://repo.maven.apache.org/maven2/" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="MavenLocal" />
+ <option name="name" value="MavenLocal" />
+ <option name="url" value="file:$USER_HOME$/.m2/repository/" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven" />
+ <option name="name" value="maven" />
+ <option name="url" value="https://dl.bintray.com/jetbrains/markdown" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven8" />
+ <option name="name" value="maven8" />
+ <option name="url" value="https://www.jetbrains.com/intellij-repository/releases" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven3" />
+ <option name="name" value="maven3" />
+ <option name="url" value="https://dl.bintray.com/kotlin/kotlin-dev" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven4" />
+ <option name="name" value="maven4" />
+ <option name="url" value="https://jitpack.io" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven2" />
+ <option name="name" value="maven2" />
+ <option name="url" value="http://dl.bintray.com/kotlin/kotlin-eap" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="maven7" />
+ <option name="name" value="maven7" />
+ <option name="url" value="https://www.jetbrains.com/intellij-repository/snapshots" />
+ </remote-repository>
+ <remote-repository>
+ <option name="id" value="BintrayJCenter" />
+ <option name="name" value="BintrayJCenter" />
+ <option name="url" value="https://jcenter.bintray.com/" />
+ </remote-repository>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 000000000..38d23597c
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Kotlin2JsCompilerArguments">
+ <option name="sourceMapEmbedSources" />
+ <option name="sourceMapPrefix" />
+ </component>
+ <component name="KotlinCommonCompilerArguments">
+ <option name="apiVersion" value="1.1" />
+ <option name="languageVersion" value="1.2" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..537aa8e47
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ASMPluginConfiguration">
+ <asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" />
+ <groovy codeStyle="LEGACY" />
+ </component>
+ <component name="EntryPointsManager">
+ <list size="5">
+ <item index="0" class="java.lang.String" itemvalue="org.apache.maven.plugins.annotations.Mojo" />
+ <item index="1" class="java.lang.String" itemvalue="org.gradle.api.tasks.InputFiles" />
+ <item index="2" class="java.lang.String" itemvalue="org.gradle.api.tasks.OutputDirectory" />
+ <item index="3" class="java.lang.String" itemvalue="org.gradle.api.tasks.TaskAction" />
+ <item index="4" class="java.lang.String" itemvalue="org.junit.BeforeClass" />
+ </list>
+ </component>
+ <component name="FrameworkDetectionExcludesConfiguration">
+ <file type="web" url="file://$PROJECT_DIR$/dokka-fatjar" />
+ <file type="web" url="file://$PROJECT_DIR$/gradle-plugin" />
+ <file type="web" url="file://$PROJECT_DIR$/javadoc" />
+ </component>
+ <component name="MavenProjectsManager">
+ <option name="originalFiles">
+ <list>
+ <option value="$PROJECT_DIR$/maven-plugin/pom.xml" />
+ </list>
+ </option>
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..772f03fbc
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin.iml" group="runners/android-gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_main.iml" group="runners/android-gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_test.iml" group="runners/android-gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant.iml" group="runners/ant" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant_main.iml" group="runners/ant" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant_test.iml" group="runners/ant" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc_main.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc_main.iml" group="buildSrc" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc_test.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc_test.iml" group="buildSrc" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli.iml" group="runners/cli" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli_main.iml" group="runners/cli" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli_test.iml" group="runners/cli" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core.iml" group="core" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_main.iml" group="core" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_test.iml" group="core" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.main.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.test.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.main.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.test.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/dokka.runners.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/dokka.runners.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.main.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar.iml" group="runners/fatjar" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_main.iml" group="runners/fatjar" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_test.iml" group="runners/fatjar" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests.iml" group="runners/gradle-integration-tests" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_main.iml" group="runners/gradle-integration-tests" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_test.iml" group="runners/gradle-integration-tests" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin.iml" group="runners/gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_main.iml" group="runners/gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_test.iml" group="runners/gradle-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration.iml" group="integration" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration_main.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration_main.iml" group="integration" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration_test.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration_test.iml" group="integration" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin.iml" group="runners/maven-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_main.iml" group="runners/maven-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_test.iml" group="runners/maven-plugin" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/org.jetbrains.dokka.dokka.iml" filepath="$PROJECT_DIR$/.idea/modules/org.jetbrains.dokka.dokka.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/runners.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/runners.iml" group="runners" />
+ </modules>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644
index 000000000..922003b84
--- /dev/null
+++ b/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+ <state>
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </state>
+</component> \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index dec37b241..dbfdb5030 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,9 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_import {
+package {
+ default_applicable_licenses: ["external_dokka_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_dokka_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ "NOTICE",
+ ],
+}
+
+java_binary_host {
name: "dokka",
- host_supported: true,
- jars: ["maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar"],
- installable: true,
+ srcs: [
+ "core/src/main/**/*.kt",
+ "runners/cli/**/*.kt",
+ "integration/**/*.kt",
+ "buildSrc/**/*.groovy"
+ ],
+ static_libs: [
+ "dokka-tools-common-m2-deps",
+ ],
+ // Pin to Java 8 since dokka doesn't compile with the Java 9 module system
+ // (it references packages under com.sun.tools.doclets which are not
+ // exported from the jdk.javadoc module) (see b/140097603):
+ java_version: "1.8",
+ use_tools_jar: true,
+ java_resource_dirs: ["core/src/main/resources"],
}
diff --git a/METADATA b/METADATA
new file mode 100644
index 000000000..dfb86523a
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,16 @@
+name: "dokka"
+description:
+ "Dokka is a documentation engine for Kotlin and Java."
+ "For version information, please see the README.android file"
+third_party {
+ url {
+ type: GIT
+ value: "https://github.com/Kotlin/dokka.git"
+ }
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2019
+ month: 7
+ day: 24
+ }
+}
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/OWNERS b/OWNERS
index ebe8d264f..ae601b9d7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,6 @@
-tiem@google.com
+asfalcone@google.com
aurimas@google.com
+jeffrygaston@google.com
+lpf@google.com
+nickanthony@google.com
+tiem@google.com
diff --git a/README.android b/README.android
new file mode 100644
index 000000000..199cbcf00
--- /dev/null
+++ b/README.android
@@ -0,0 +1,234 @@
+URL: https://github.com/Kotlin/dokka
+License: Apache 2
+Description: "Dokka is a documentation engine for Kotlin and Java."
+
+Changes in this fork
+--------------------
+This fork of Dokka contains changes for generating reference documentation on
+developer.android.com.
+
+This repository was previously hosted at https://github.com/google/dokka, and
+moved to AOSP as of commit 4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad. It was
+initally forked from https://github.com/Kotlin/dokka after commit
+7a066afb0ba47b5b57e98a5d821a0cbe97322670.
+
+Development took place on the "devsite-with-java-layout-html" on both the
+upstream Dokka repository and the fork on GitHub. For a complete list of
+changes, see
+
+https://github.com/google/dokka/compare/7a066afb0ba47b5b57e98a5d821a0cbe97322670...4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad
+
+which is echoed here:
+
+commit 4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad
+Author: Gautam Ajjarapu <gautam@ajjarapu.com>
+Date: Fri Jun 14 13:54:08 2019 -0700
+
+ Automate Downloading of Code Samples (#58)
+
+commit 47cc6dcfb4b39f0ecbb5aba53c34aa7d54a1a01e
+Author: Gautam Ajjarapu <ajjarapu@google.com>
+Date: Fri Jun 7 16:03:00 2019 -0700
+
+ API Info Alignment Fix (#56)
+
+ * alignment fix
+
+ * Removed temporary solution of using method parameter inHeader because
+ changing api-info div solved spacing problem
+
+commit b2821b7a89ce501b9e8edea26b2defa03dcc6bc8
+Author: mvivo <mvivo@google.com>
+Date: Tue May 21 15:53:43 2019 +0200
+
+ Adds API info to classes
+
+commit 04c35131eb4df0899430308c203520627deab0b4
+Author: Tiem Song <tiem@google.com>
+Date: Fri Apr 26 12:36:59 2019 -0700
+
+ Update Dokka to version 0.9.17-g002
+
+commit d0449587ad7e380846c078ecc3714ce3a326448a
+Author: Tiem Song <tiem@google.com>
+Date: Fri Apr 26 11:07:17 2019 -0700
+
+ Process rowspan
+
+ This is a follow up PR to #45
+
+commit d8d07776f790f6be848c6ce6a085dbd00c52ea43
+Author: Manuel Vivo <manuelvicnt@gmail.com>
+Date: Fri Apr 26 19:23:16 2019 +0200
+
+ Process HTML code in comments (#45)
+
+ * Handle Special Reference for callouts
+
+ * Process HTML code in comments
+
+ * Adds colspan to td & th
+
+commit f76cba219c2fcc051f4f19b72df58d14f3c542fc
+Author: Manuel Vivo <mvivo@google.com>
+Date: Wed Apr 24 15:57:39 2019 +0200
+
+ Processes @sample in Java documentation
+
+commit d0bda76a4d07d467594f3fed4359faab74e51608
+Author: mvivo <mvivo@google.com>
+Date: Thu Apr 25 15:07:32 2019 +0200
+
+ Adds devsite-heading HTML tag to table headers
+
+commit 8fcffad51ef034e38d100693fac755cae0766d73
+Author: Manuel Vivo <manuelvicnt@gmail.com>
+Date: Thu Apr 25 07:38:21 2019 +0200
+
+ Display deprecated in a callout and class index (#41)
+
+ * Display deprecated callout in classes and enums
+
+ * Follow Google style reference guide
+
+ * Make deprecation message generic
+
+ * Add deprecation message to class index
+
+ * Add HtmlFormatTest back
+
+ * Extends wording to more classes
+
+commit 1f903e7ebe2e29bcf6773615525d991f49707b5d
+Author: mvivo <mvivo@google.com>
+Date: Mon Apr 22 16:19:44 2019 +0200
+
+ Adds enum table to class summary
+
+commit 3caaa4425bd146b07077d6e040776fd1a5ba6933
+Author: Manuel Vivo <mvivo@google.com>
+Date: Wed Apr 10 20:42:34 2019 +0200
+
+ Display XML attributes with a short description
+
+commit 200d6131c1aea49db8d5a9ed0a120ab46834da37
+Author: mvivo <mvivo@google.com>
+Date: Wed Apr 10 15:26:06 2019 +0200
+
+ Follow Google style reference
+
+commit e4ecf324642e0b7a359aa163848df6996ec7a57f
+Author: Manuel Vivo <mvivo@google.com>
+Date: Tue Apr 9 19:34:01 2019 +0200
+
+ Handle Special Reference for callouts
+
+commit 6c41c5db202ddda907e79a7a63f7c306c81636a3
+Author: Tiem Song <tiem@google.com>
+Date: Thu Apr 11 09:17:20 2019 -0700
+
+ Update Dokka to version 0.9.17-g001
+
+ This release also uses the new naming convention, using a -gXXX suffix
+ instead of a -g<Date> suffix.
+
+commit 83b513effdb12e74f5560a464c266b1965bf44c2
+Author: mvivo <mvivo@google.com>
+Date: Wed Apr 10 16:19:31 2019 +0200
+
+ Add tests to CodeNode
+
+commit f9e69f02ae6704ec797b390b178886c4d53bacb2
+Author: mvivo <mvivo@google.com>
+Date: Tue Apr 9 16:29:23 2019 +0200
+
+ Add Java multiline code
+
+commit 47f3ab52ba16b72ed346db82dd1dd36de23367af
+Author: Manuel Vivo <mvivo@google.com>
+Date: Mon Apr 8 20:27:04 2019 +0200
+
+ Remove deprecated version in classes
+
+commit 9e26ab39a86f18c919257763733ec7a36080065b
+Author: Manuel Vivo <mvivo@google.com>
+Date: Mon Apr 8 20:20:02 2019 +0200
+
+ Add description to XML attributes
+
+commit 7c70db208725d6442b3739a42cc4d7351dde48d9
+Author: Tiem Song <tiem@google.com>
+Date: Fri Apr 5 10:04:47 2019 -0700
+
+ Rename minApiLevel method
+
+ This method was originally created to fetch the API level for minimum API
+ usage. The logic of the method itself doesn't really pertain to minimum or
+ deprecation - it's just finding a Doc Tag from the data elements. Thus,
+ this method should be renamed apiLevel() or something similar, with the
+ "min" part removed to avoid confusion.
+
+ Bug: 129726096
+
+commit b64371e2845b0a05dcf2f50a383bd043fc750384
+Author: Tiem Song <tiem@google.com>
+Date: Thu Apr 4 16:19:06 2019 -0700
+
+ Update package for StringExtensions
+
+ This is to fix issues when developing on a case sensitive file system /
+ MacOS. This is a continuation of
+ https://github.com/google/dokka/pull/29.
+
+commit 7baef1414f61adcd77963581bffcd0f80d820059
+Author: Manuel Vivo <mvivo@google.com>
+Date: Wed Apr 3 20:09:15 2019 +0200
+
+ Fix tests broken by pull 32
+
+commit daf718e858242a82b42d3cecd9ec6d23e680f9da
+Author: Manuel Vivo <mvivo@google.com>
+Date: Wed Apr 3 11:39:44 2019 +0200
+
+ Strikethrough words are inline
+
+commit 67b37de06ed23105a4f78957f1a99654459028b9
+Author: Manuel Vivo <manuelvicnt@gmail.com>
+Date: Wed Apr 3 19:00:09 2019 +0200
+
+ List functions in alphabetical order (#32)
+
+ * List functions in alphabetical order
+
+ * Order other members alphabetically
+
+commit 97871cf42bf724645eb2ed4d1dd16304083e44cd
+Author: Manuel Vivo <manuelvicnt@gmail.com>
+Date: Tue Apr 2 22:15:22 2019 +0200
+
+ Show summary on deprecated methods (#31)
+
+commit b5e480e1c087b5a5307a9176bb2835ebbebee8b0
+Author: Manuel Vivo <manuelvicnt@gmail.com>
+Date: Tue Apr 2 22:12:56 2019 +0200
+
+ Process deprecatedSince annotation (#30)
+
+ * Process deprecatedSince annotation
+
+ * Add info to class
+
+ * Capitalize wording
+
+commit f4f7e6fa673fe13fff68dd1bf3c005bf9d94875c
+Author: mvivo <mvivo@google.com>
+Date: Fri Mar 29 12:20:59 2019 +0100
+
+ Improve Java enums documentation
+
+commit c1c86b92c15b97e7aec41ed9892aa6965974d66f
+Author: Tiem Song <tiem@google.com>
+Date: Fri Mar 29 10:45:06 2019 -0700
+
+ Move StringExtensions file to Utilities directory
+
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..b4b83eeae
--- /dev/null
+++ b/README.md
@@ -0,0 +1,483 @@
+dokka
+=====
+
+**_Note_: This is Google's fork of Dokka, customized for [Android API reference docs](https://developer.android.com/reference/)
+on [developer.android.com](https://developer.android.com/) and other Google products.**
+
+Dokka is a documentation engine for Kotlin, performing the same function as javadoc for Java.
+Just like Kotlin itself, Dokka fully supports mixed-language Java/Kotlin projects. It understands
+standard Javadoc comments in Java files and [KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in Kotlin files,
+and can generate documentation in multiple formats including standard Javadoc, HTML and Markdown.
+
+## Using Dokka
+
+### Using the Gradle plugin
+
+```groovy
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}"
+ }
+}
+
+apply plugin: 'org.jetbrains.dokka'
+```
+
+The plugin adds a task named "dokka" to the project.
+
+Minimal dokka configuration:
+
+```groovy
+dokka {
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/javadoc"
+}
+```
+
+[Output formats](#output_formats)
+
+The available configuration options are shown below:
+
+```groovy
+dokka {
+ moduleName = 'data'
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/javadoc"
+
+ // These tasks will be used to determine source directories and classpath
+ kotlinTasks {
+ defaultKotlinTasks() + [':some:otherCompileKotlin', project("another").compileKotlin]
+ }
+
+ // List of files with module and package documentation
+ // http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation
+ includes = ['packages.md', 'extra.md']
+
+ // The list of files or directories containing sample code (referenced with @sample tags)
+ samples = ['samples/basic.kt', 'samples/advanced.kt']
+
+ jdkVersion = 6 // Used for linking to JDK
+
+ // Use default or set to custom path to cache directory
+ // to enable package-list caching
+ // When set to default, caches stored in $USER_HOME/.cache/dokka
+ cacheRoot = 'default'
+
+ // Use to include or exclude non public members.
+ includeNonPublic = false
+
+ // Do not output deprecated members. Applies globally, can be overridden by packageOptions
+ skipDeprecated = false
+
+ // Emit warnings about not documented members. Applies globally, also can be overridden by packageOptions
+ reportUndocumented = true
+
+ skipEmptyPackages = true // Do not create index pages for empty packages
+
+ impliedPlatforms = ["JVM"] // See platforms section of documentation
+
+ // Manual adding files to classpath
+ // This property not overrides classpath collected from kotlinTasks but appends to it
+ classpath = [new File("$buildDir/other.jar")]
+
+ // By default, sourceRoots is taken from kotlinTasks, following roots will be appended to it
+ // Short form sourceRoots
+ sourceDirs = files('src/main/kotlin')
+
+ // By default, sourceRoots is taken from kotlinTasks, following roots will be appended to it
+ // Full form sourceRoot declaration
+ // Repeat for multiple sourceRoots
+ sourceRoot {
+ // Path to source root
+ path = "src"
+ // See platforms section of documentation
+ platforms = ["JVM"]
+ }
+
+ // Specifies the location of the project source code on the Web.
+ // If provided, Dokka generates "source" links for each declaration.
+ // Repeat for multiple mappings
+ linkMapping {
+ // Source directory
+ dir = "src/main/kotlin"
+
+ // URL showing where the source code can be accessed through the web browser
+ url = "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin"
+
+ // Suffix which is used to append the line number to the URL. Use #L for GitHub
+ suffix = "#L"
+ }
+
+ // No default documentation link to kotlin-stdlib
+ noStdlibLink = false
+
+ // Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka)
+ // Repeat for multiple links
+ externalDocumentationLink {
+ // Root URL of the generated documentation to link with. The trailing slash is required!
+ url = new URL("https://example.com/docs/")
+
+ // If package-list file located in non-standard location
+ // packageListUrl = new URL("file:///home/user/localdocs/package-list")
+ }
+
+ // Allows to customize documentation generation options on a per-package basis
+ // Repeat for multiple packageOptions
+ packageOptions {
+ prefix = "kotlin" // will match kotlin and all sub-packages of it
+ // All options are optional, default values are below:
+ skipDeprecated = false
+ reportUndocumented = true // Emit warnings about not documented members
+ includeNonPublic = false
+ }
+ // Suppress a package
+ packageOptions {
+ prefix = "kotlin.internal" // will match kotlin.internal and all sub-packages of it
+ suppress = true
+ }
+}
+```
+
+To generate the documentation, use the `dokka` Gradle task:
+
+```bash
+./gradlew dokka
+```
+
+More dokka tasks can be added to a project like this:
+
+```groovy
+task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
+ outputFormat = 'javadoc'
+ outputDirectory = "$buildDir/javadoc"
+}
+```
+
+Please see the [Dokka Gradle example project](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/dokka-gradle-example) for an example.
+
+#### Android
+
+If you are using Android there is a separate Gradle plugin. Just make sure you apply the plugin after
+`com.android.library` and `kotlin-android`.
+
+```groovy
+buildscript {
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}"
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'org.jetbrains.dokka-android'
+```
+
+### Using the Maven plugin
+
+The Maven plugin is available in JCenter. You need to add the JCenter repository to the list of plugin repositories if it's not there:
+
+```xml
+<pluginRepositories>
+ <pluginRepository>
+ <id>jcenter</id>
+ <name>JCenter</name>
+ <url>https://jcenter.bintray.com/</url>
+ </pluginRepository>
+</pluginRepositories>
+```
+
+Minimal Maven configuration is
+
+```xml
+<plugin>
+ <groupId>org.jetbrains.dokka</groupId>
+ <artifactId>dokka-maven-plugin</artifactId>
+ <version>${dokka.version}</version>
+ <executions>
+ <execution>
+ <phase>pre-site</phase>
+ <goals>
+ <goal>dokka</goal>
+ </goals>
+ </execution>
+ </executions>
+</plugin>
+```
+
+By default files will be generated in `target/dokka`.
+
+The following goals are provided by the plugin:
+
+ * `dokka:dokka` - generate HTML documentation in Dokka format (showing declarations in Kotlin syntax);
+ * `dokka:javadoc` - generate HTML documentation in JavaDoc format (showing declarations in Java syntax);
+ * `dokka:javadocJar` - generate a .jar file with JavaDoc format documentation.
+
+The available configuration options are shown below:
+
+```xml
+<plugin>
+ <groupId>org.jetbrains.dokka</groupId>
+ <artifactId>dokka-maven-plugin</artifactId>
+ <version>${dokka.version}</version>
+ <executions>
+ <execution>
+ <phase>pre-site</phase>
+ <goals>
+ <goal>dokka</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+
+ <!-- Set to true to skip dokka task, default: false -->
+ <skip>false</skip>
+
+ <!-- Default: ${project.artifactId} -->
+ <moduleName>data</moduleName>
+ <!-- See list of possible formats below -->
+ <outputFormat>html</outputFormat>
+ <!-- Default: ${project.basedir}/target/dokka -->
+ <outputDir>some/out/dir</outputDir>
+
+ <!-- Use default or set to custom path to cache directory to enable package-list caching. -->
+ <!-- When set to default, caches stored in $USER_HOME/.cache/dokka -->
+ <cacheRoot>default</cacheRoot>
+
+ <!-- List of '.md' files with package and module docs -->
+ <!-- http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation -->
+ <includes>
+ <file>packages.md</file>
+ <file>extra.md</file>
+ </includes>
+
+ <!-- List of sample roots -->
+ <samplesDirs>
+ <dir>src/test/samples</dir>
+ </samplesDirs>
+
+ <!-- Used for linking to JDK, default: 6 -->
+ <jdkVersion>6</jdkVersion>
+
+ <!-- Do not output deprecated members, applies globally, can be overridden by packageOptions -->
+ <skipDeprecated>false</skipDeprecated>
+ <!-- Emit warnings about not documented members, applies globally, also can be overridden by packageOptions -->
+ <reportNotDocumented>true</reportNotDocumented>
+ <!-- Do not create index pages for empty packages -->
+ <skipEmptyPackages>true</skipEmptyPackages>
+
+ <!-- See platforms section of documentation -->
+ <impliedPlatforms>
+ <platform>JVM</platform>
+ </impliedPlatforms>
+
+ <!-- Short form list of sourceRoots, by default, set to ${project.compileSourceRoots} -->
+ <sourceDirectories>
+ <dir>src/main/kotlin</dir>
+ </sourceDirectories>
+
+ <!-- Full form list of sourceRoots -->
+ <sourceRoots>
+ <root>
+ <path>src/main/kotlin</path>
+ <!-- See platforms section of documentation -->
+ <platforms>JVM</platforms>
+ </root>
+ </sourceRoots>
+
+ <!-- Specifies the location of the project source code on the Web. If provided, Dokka generates "source" links
+ for each declaration. -->
+ <sourceLinks>
+ <link>
+ <!-- Source directory -->
+ <dir>${project.basedir}/src/main/kotlin</dir>
+ <!-- URL showing where the source code can be accessed through the web browser -->
+ <url>http://github.com/me/myrepo</url>
+ <!--Suffix which is used to append the line number to the URL. Use #L for GitHub -->
+ <urlSuffix>#L</urlSuffix>
+ </link>
+ </sourceLinks>
+
+ <!-- No default documentation link to kotlin-stdlib -->
+ <noStdlibLink>false</noStdlibLink>
+
+ <!-- Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka) -->
+ <externalDocumentationLinks>
+ <link>
+ <!-- Root URL of the generated documentation to link with. The trailing slash is required! -->
+ <url>https://example.com/docs/</url>
+ <!-- If package-list file located in non-standard location -->
+ <!-- <packageListUrl>file:///home/user/localdocs/package-list</packageListUrl> -->
+ </link>
+ </externalDocumentationLinks>
+
+ <!-- Allows to customize documentation generation options on a per-package basis -->
+ <perPackageOptions>
+ <packageOptions>
+ <!-- Will match kotlin and all sub-packages of it -->
+ <prefix>kotlin</prefix>
+
+ <!-- All options are optional, default values are below: -->
+ <skipDeprecated>false</skipDeprecated>
+ <!-- Emit warnings about not documented members -->
+ <reportUndocumented>true</reportUndocumented>
+ <includeNonPublic>false</includeNonPublic>
+ </packageOptions>
+ </perPackageOptions>
+ </configuration>
+</plugin>
+```
+
+Please see the [Dokka Maven example project](https://github.com/JetBrains/kotlin-examples/tree/master/maven/dokka-maven-example) for an example.
+
+[Output formats](#output_formats)
+
+### Using the Ant task
+
+The Ant task definition is also contained in the dokka-fatjar.jar referenced above. Here's an example of using it:
+
+```xml
+<project name="Dokka" default="document">
+ <typedef resource="dokka-antlib.xml" classpath="dokka-fatjar.jar"/>
+
+ <target name="document">
+ <dokka src="src" outputdir="doc" modulename="myproject"/>
+ </target>
+</project>
+```
+
+The Ant task supports the following attributes:
+
+ * `outputDir` - the output directory where the documentation is generated
+ * `outputFormat` - the output format (see the list of supported formats above)
+ * `classpath` - list of directories or .jar files to include in the classpath (used for resolving references)
+ * `samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag)
+ * `moduleName` - the name of the module being documented (used as the root directory of the generated documentation)
+ * `include` - names of files containing the documentation for the module and individual packages
+ * `skipDeprecated` - if set, deprecated elements are not included in the generated documentation
+ * `jdkVersion` - version for linking to JDK
+ * `impliedPlatforms` - See [platforms](#platforms) section
+ * `<sourceRoot path="src" platforms="JVM" />` - analogue of src, but allows to specify [platforms](#platforms)
+ * `<packageOptions prefix="kotlin" includeNonPublic="false" reportUndocumented="true" skipDeprecated="false"/>` -
+ Per package options for package `kotlin` and sub-packages of it
+ * `noStdlibLink` - No default documentation link to kotlin-stdlib
+ * `<externalDocumentationLink url="https://example.com/docs/" packageListUrl="file:///home/user/localdocs/package-list"/>` -
+ linking to external documentation, packageListUrl should be used if package-list located not in standard location
+ * `cacheRoot` - Use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka
+
+
+### Using the Command Line
+
+To run Dokka from the command line, download the [Dokka jar](https://github.com/Kotlin/dokka/releases/download/0.9.10/dokka-fatjar.jar).
+To generate documentation, run the following command:
+
+ java -jar dokka-fatjar.jar <source directories> <arguments>
+
+Dokka supports the following command line arguments:
+
+ * `-output` - the output directory where the documentation is generated
+ * `-format` - the [output format](#output-formats):
+ * `-classpath` - list of directories or .jar files to include in the classpath (used for resolving references)
+ * `-samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag)
+ * `-module` - the name of the module being documented (used as the root directory of the generated documentation)
+ * `-include` - names of files containing the documentation for the module and individual packages
+ * `-nodeprecated` - if set, deprecated elements are not included in the generated documentation
+ * `-impliedPlatforms` - List of implied platforms (comma-separated)
+ * `-packageOptions` - List of package options in format `prefix,-deprecated,-privateApi,+warnUndocumented;...`
+ * `-links` - External documentation links in format `url^packageListUrl^^url2...`
+ * `-noStdlibLink` - Disable documentation link to stdlib
+ * `-cacheRoot` - Use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka
+
+
+### Output formats<a name="output_formats"></a>
+
+ * `html` - minimalistic html format used by default
+ * `javadoc` - Dokka mimic to javadoc
+ * `html-as-java` - as `html` but using java syntax
+ * `markdown` - Markdown structured as `html`
+ * `gfm` - GitHub flavored markdown
+ * `jekyll` - Jekyll compatible markdown
+ * `kotlin-website*` - internal format used for documentation on [kotlinlang.org](https://kotlinlang.org)
+
+### Platforms<a name="platforms"></a>
+
+Dokka can annotate elements with special `platform` block with platform requirements
+
+Example of usage can be found on [kotlinlang.org](https://kotlinlang.org/api/latest/jvm/stdlib/)
+
+Each source root has a list of platforms for which members are suitable.
+Also, the list of 'implied' platforms is passed to Dokka.
+If a member is not available for all platforms in the implied platforms set, its documentation will show
+the list of platforms for which it's available.
+
+## Dokka Internals
+
+### Documentation Model
+
+Dokka uses Kotlin-as-a-service technology to build `code model`, then processes it into `documentation model`.
+`Documentation model` is graph of items describing code elements such as classes, packages, functions, etc.
+
+Each node has semantic attached, e.g. Value:name -> Type:String means that some value `name` is of type `String`.
+
+Each reference between nodes also has semantic attached, and there are three of them:
+
+1. Member - reference means that target is member of the source, form tree.
+2. Detail - reference means that target describes source in more details, form tree.
+3. Link - any link to any other node, free form.
+
+Member & Detail has reverse Owner reference, while Link's back reference is also Link.
+
+Nodes that are Details of other nodes cannot have Members.
+
+### Rendering Docs
+
+When we have documentation model, we can render docs in various formats, languages and layouts. We have some core services:
+
+* FormatService -- represents output format
+* LocationService -- represents folder and file layout
+* SignatureGenerator -- represents target language by generating class/function/package signatures from model
+
+Basically, given the `documentation` as a model, we do this:
+
+```kotlin
+ val signatureGenerator = KotlinSignatureGenerator()
+ val locationService = FoldersLocationService(arguments.outputDir)
+ val markdown = JekyllFormatService(locationService, signatureGenerator)
+ val generator = FileGenerator(signatureGenerator, locationService, markdown)
+ generator.generate(documentation)
+```
+
+## Building Dokka
+
+Dokka is built with Gradle. To build it, use `./gradlew build`.
+Alternatively, open the project directory in IntelliJ IDEA and use the IDE to build and run Dokka.
+
+Here's how to import and configure Dokka in IntelliJ IDEA:
+
+* Select "Open" from the IDEA welcome screen, or File > Open if a project is
+ already open
+* Select the directory with your clone of Dokka
+ * Note: IDEA may have an error after the project is initally opened; it is OK
+ to ignore this as the next step will address this error
+* After IDEA opens the project, select File > New > Module from existing sources
+ and select the `build.gradle` file from the root directory of your Dokka clone
+* Use the default options and select "OK"
+* After Dokka is loaded into IDEA, open the Gradle tool window (View > Tool
+ Windows > Gradle) and click on the top left "Refresh all Gradle projects"
+ button
+* Verify the following project settings. In File > Settings > Build, Execution,
+ Deployment > Build Tools > Gradle > Runner:
+ * Ensure "Delegate IDE build/run actions to gradle" is checked
+ * "Gradle Test Runner" should be selected in the "Run tests using" drop-down
+ menu
+* Note: After closing and re-opening the project, IDEA may give an error
+ message: "Error Loading Project: Cannot load 3 modules". Open up the details
+ of the error, and click "Remove Selected", as these module `.iml` files are
+ safe to remove.
diff --git a/build-docs.xml b/build-docs.xml
new file mode 100644
index 000000000..b46353b01
--- /dev/null
+++ b/build-docs.xml
@@ -0,0 +1,20 @@
+<project name="Dokka" default="document">
+ <!-- Demonstrates the usage of the Dokka Ant task. Assumes Dokka has already been compiled -->
+
+ <typedef resource="dokka-antlib.xml">
+ <classpath>
+ <fileset dir="runners/fatjar/build/libs" includes="dokka-fatjar-*.jar"/>
+ </classpath>
+ </typedef>
+
+ <path id="dokka.source.path">
+ <pathelement location="core/src/main/kotlin"/>
+ <fileset dir="runners" includes="*/src/main/kotlin/**" />
+ </path>
+
+ <target name="document">
+ <dokka srcref="dokka.source.path" outputdir="doc" modulename="dokka">
+ <sourcelink path="." url="https://github.com/kotlin/dokka/blob/master" linesuffix="#L"/>
+ </dokka>
+ </target>
+</project>
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..08951ee9a
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,124 @@
+allprojects {
+ group 'org.jetbrains.dokka'
+ version dokka_version
+
+ def repo = {
+ artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/[revision]/internal/[module](.[ext])")
+ artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/IntelliJMarkdownParser_Build/[revision]/([module]_[ext]/)[module](.[ext])")
+ }
+
+ buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ maven { url "https://plugins.gradle.org/m2/" }
+ ivy(repo)
+ }
+ dependencies {
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
+ classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
+
+ classpath "com.gradle.publish:plugin-publish-plugin:0.9.10"
+ }
+ }
+
+ repositories {
+ mavenCentral()
+ mavenLocal()
+ maven { url 'https://kotlin.bintray.com/kotlin-plugin' }
+ maven { url 'https://www.jetbrains.com/intellij-repository/releases' }
+ maven { url "https://dl.bintray.com/jetbrains/markdown" }
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ maven { url 'https://jitpack.io' }
+ maven { url "https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/$bundled_kotlin_compiler_version/maven" }
+ ivy(repo)
+ maven { url "https://dl.bintray.com/kotlin/kotlinx.html" }
+ }
+}
+
+
+def bintrayPublication(project, List<String> _publications) {
+ configure(project, {
+ apply plugin: 'com.jfrog.bintray'
+
+ bintray {
+ user = System.getenv('BINTRAY_USER')
+ key = System.getenv('BINTRAY_KEY')
+
+ pkg {
+ repo = dokka_publication_channel
+ name = 'dokka'
+ userOrg = 'kotlin'
+ desc = 'Dokka, the Kotlin documentation tool'
+ vcsUrl = 'https://github.com/kotlin/dokka.git'
+ licenses = ['Apache-2.0']
+ version {
+ name = dokka_version
+ }
+ }
+
+ publications = _publications
+ }
+ })
+}
+
+task wrapper(type: Wrapper) {
+ gradleVersion = '4.2.1'
+ distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
+}
+
+configurations {
+ ideaIC
+ intellijCore
+}
+
+repositories {
+ maven { url 'https://kotlin.bintray.com/kotlin-plugin' }
+ maven { url 'https://www.jetbrains.com/intellij-repository/snapshots' }
+ maven { url 'https://www.jetbrains.com/intellij-repository/releases' }
+}
+
+dependencies {
+ intellijCore "com.jetbrains.intellij.idea:intellij-core:$idea_version"
+ ideaIC "com.jetbrains.intellij.idea:ideaIC:$idea_version"
+}
+
+def intellijCoreAnalysis() {
+ return zipTree(configurations.intellijCore.singleFile).matching ({
+ include("intellij-core-analysis.jar")
+ })
+}
+
+def ideaRT() {
+ return zipTree(project.configurations.ideaIC.singleFile).matching ({
+ include("lib/idea_rt.jar")
+ })
+}
+
+def repoLocation = uri(file("$buildDir/dist-maven"))
+
+allprojects {
+
+ task publishToDistMaven {
+ group "publishing"
+ description "Publishes all Maven publications to Maven repository 'distMaven'."
+ dependsOn tasks.withType(PublishToMavenRepository).matching {
+ it.repository == publishing.repositories.distMaven
+ }
+ }
+
+ plugins.withType(MavenPublishPlugin) {
+ publishing {
+ repositories {
+ maven {
+ name 'distMaven'
+ url repoLocation
+ }
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 000000000..874dabc96
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,10 @@
+apply plugin: 'groovy'
+repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+}
+dependencies {
+ compile 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
+} \ No newline at end of file
diff --git a/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy
new file mode 100644
index 000000000..aacede449
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy
@@ -0,0 +1,39 @@
+package org.jetbrains
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.ModuleVersionIdentifier
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.SelfResolvingDependency
+import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyPublicationResolver
+import org.gradle.api.publish.maven.MavenPom
+import org.gradle.api.publish.maven.MavenPublication
+
+//https://github.com/johnrengelman/shadow/issues/334
+static void configure(MavenPublication publication, Project project) {
+ publication.artifact(project.tasks.shadowJar)
+
+ publication.pom { MavenPom pom ->
+ pom.withXml { xml ->
+ def dependenciesNode = xml.asNode().appendNode('dependencies')
+
+ project.configurations.shadow.allDependencies.each {
+ if (it instanceof ProjectDependency) {
+ final ProjectDependencyPublicationResolver projectDependencyResolver = project.gradle.services.get(ProjectDependencyPublicationResolver)
+ final ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier, it)
+ addDependency(dependenciesNode, identifier)
+ } else if (!(it instanceof SelfResolvingDependency)) {
+ addDependency(dependenciesNode, it)
+ }
+
+ }
+ }
+ }
+}
+
+private static void addDependency(Node dependenciesNode, dep) {
+ def dependencyNode = dependenciesNode.appendNode('dependency')
+ dependencyNode.appendNode('groupId', dep.group)
+ dependencyNode.appendNode('artifactId', dep.name)
+ dependencyNode.appendNode('version', dep.version)
+ dependencyNode.appendNode('scope', 'compile')
+}
diff --git a/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy b/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy
new file mode 100644
index 000000000..d3973a8a2
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy
@@ -0,0 +1,84 @@
+package org.jetbrains
+
+import org.gradle.api.tasks.AbstractExecTask
+import org.gradle.api.tasks.TaskAction
+import org.gradle.internal.os.OperatingSystem
+
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+
+class CrossPlatformExec extends AbstractExecTask {
+ private static final def windowsExtensions = ['bat', 'cmd', 'exe'];
+ private static final def unixExtensions = [null, 'sh'];
+
+ private boolean windows;
+
+ public CrossPlatformExec() {
+ super(CrossPlatformExec.class);
+ windows = OperatingSystem.current().windows;
+ }
+
+ @Override
+ @TaskAction
+ protected void exec() {
+ List<String> commandLine = this.getCommandLine();
+
+ if (!commandLine.isEmpty()) {
+ commandLine[0] = findCommand(commandLine[0], windows);
+ }
+
+ if (windows) {
+ if (!commandLine.isEmpty() && commandLine[0]) {
+ commandLine
+ }
+ commandLine.add(0, '/c');
+ commandLine.add(0, 'cmd');
+ }
+
+ this.setCommandLine(commandLine);
+
+ super.exec();
+ }
+
+ private static String findCommand(String command, boolean windows) {
+ command = normalizeCommandPaths(command);
+ def extensions = windows ? windowsExtensions : unixExtensions;
+
+ return extensions.findResult(command) { extension ->
+ Path commandFile
+ if (extension) {
+ commandFile = Paths.get(command + '.' + extension);
+ } else {
+ commandFile = Paths.get(command);
+ }
+
+ return resolveCommandFromFile(commandFile, windows);
+ };
+ }
+
+ private static String resolveCommandFromFile(Path commandFile, boolean windows) {
+ if (!Files.isExecutable(commandFile)) {
+ return null;
+ }
+
+ return commandFile.toAbsolutePath().normalize();
+ }
+
+ private static String normalizeCommandPaths(String command) {
+ // need to escape backslash so it works with regex
+ String backslashSeparator = '\\\\';
+
+ String forwardSlashSeparator = '/';
+
+ // escape separator if it's a backslash
+ char backslash = '\\';
+ String separator = File.separatorChar == backslash ? backslashSeparator : File.separator
+
+ return command
+ // first replace all of the backslashes with forward slashes
+ .replaceAll(backslashSeparator, forwardSlashSeparator)
+ // then replace all forward slashes with whatever the separator actually is
+ .replaceAll(forwardSlashSeparator, separator);
+ }
+} \ No newline at end of file
diff --git a/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy b/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy
new file mode 100644
index 000000000..194f11afb
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy
@@ -0,0 +1,14 @@
+package org.jetbrains
+
+import org.gradle.api.Project
+
+class DependenciesVersionGetter {
+ static Properties getVersions(Project project, String artifactVersionSelector) {
+ def dep = project.dependencies.create(group: 'teamcity', name: 'dependencies', version: artifactVersionSelector, ext: 'properties')
+ def file = project.configurations.detachedConfiguration(dep).resolve().first()
+
+ def prop = new Properties()
+ prop.load(new FileReader(file))
+ return prop
+ }
+}
diff --git a/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy b/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy
new file mode 100644
index 000000000..e711388f4
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy
@@ -0,0 +1,71 @@
+package org.jetbrains
+
+import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext
+import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
+import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
+import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
+import groovy.xml.XmlUtil
+import org.gradle.api.file.FileTreeElement
+import shadow.org.apache.tools.zip.ZipEntry
+import shadow.org.apache.tools.zip.ZipOutputStream
+
+public class PluginXmlTransformer implements Transformer {
+ private Map<String, Node> transformedPluginXmlFiles = new HashMap<>();
+
+ @Override
+ boolean canTransformResource(FileTreeElement fileTreeElement) {
+ return fileTreeElement.relativePath.segments.contains("META-INF") && fileTreeElement.name.endsWith(".xml")
+ }
+
+ @Override
+ void transform(TransformerContext context) {
+ def path = context.path
+ def inputStream = context.is
+ System.out.println(path)
+ Node node = new XmlParser().parse(inputStream)
+ relocateXml(node, context)
+ transformedPluginXmlFiles.put(path, node)
+ }
+
+ @Override
+ boolean hasTransformedResource() {
+ return !transformedPluginXmlFiles.isEmpty()
+ }
+
+ @Override
+ void modifyOutputStream(ZipOutputStream zipOutputStream) {
+ for (Map.Entry<String, Node> entry : transformedPluginXmlFiles.entrySet()) {
+ zipOutputStream.putNextEntry(new ZipEntry(entry.key))
+ XmlUtil.serialize(entry.value, zipOutputStream)
+ }
+ }
+
+ private static void relocateXml(Node node, TransformerContext context) {
+ Map attributes = node.attributes()
+ RelocateClassContext relocateClassContext = new RelocateClassContext()
+ relocateClassContext.stats = context.stats
+ for (Map.Entry entry : attributes.entrySet()) {
+ relocateClassContext.setClassName((String) entry.getValue())
+ entry.setValue(relocateClassName(relocateClassContext, context))
+ }
+ List<String> localText = node.localText()
+ if (localText.size() == 1) {
+ relocateClassContext.setClassName(localText[0])
+ node.setValue(relocateClassName(relocateClassContext, context))
+ }
+ node.children().each {
+ if (it instanceof Node) {
+ relocateXml((Node) it, context)
+ }
+ }
+ }
+
+ private static String relocateClassName(RelocateClassContext relocateContext, TransformerContext context) {
+ for (Relocator relocator : context.relocators) {
+ if (relocator.canRelocateClass(relocateContext)) {
+ return relocator.relocateClass(relocateContext)
+ }
+ }
+ return relocateContext.className
+ }
+} \ No newline at end of file
diff --git a/busytown.gradle b/busytown.gradle
new file mode 100644
index 000000000..1b9a7119f
--- /dev/null
+++ b/busytown.gradle
@@ -0,0 +1,38 @@
+def destDir = (System.getenv("DIST_DIR") == null) ? file("dist") : file(System.getenv("DIST_DIR"))
+
+def hostTestDir = new File(destDir, "host-test-reports")
+
+allprojects { project ->
+ project.tasks.withType(Test) { task ->
+ def report = task.reports.junitXml
+ if (report.isEnabled()) {
+ def zipTask = project.tasks.create("zipResultsOf${project.name}", Zip) {
+ destinationDir = hostTestDir
+ archiveName = "${project.name}.zip"
+ }
+ task.finalizedBy(zipTask)
+ task.doFirst {
+ zipTask.from(report.destination)
+ }
+ }
+ task.ignoreFailures = true
+ }
+ if (project.rootProject == project) {
+ def zipMaven = project.tasks.create("zipMaven", Zip) {
+ from file("${project.buildDir}/dist-maven")
+ destinationDir destDir
+ archiveName = "maven.zip"
+ }
+
+ def copyRepository = project.tasks.create("copyRepository", Copy) {
+ from file("${project.buildDir}/dist-maven")
+ into "${destDir}/repository"
+ }
+
+ [copyRepository, zipMaven].forEach {
+ it.dependsOn(":runners:android-gradle-plugin:publishToDistMaven")
+ it.dependsOn(":runners:gradle-plugin:publishToDistMaven")
+ it.dependsOn(":runners:fatjar:publishToDistMaven")
+ }
+ }
+}
diff --git a/busytown.sh b/busytown.sh
new file mode 100755
index 000000000..ca1ae6199
--- /dev/null
+++ b/busytown.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+SCRIPT_DIR="$(cd $(dirname $0) && pwd)"
+
+"$SCRIPT_DIR"/gradlew -p "$SCRIPT_DIR" -I "$SCRIPT_DIR"/busytown.gradle --no-daemon :core:build :runners:android-gradle-plugin:build :runners:gradle-integration-tests:build zipMaven copyRepository \ No newline at end of file
diff --git a/core/build.gradle b/core/build.gradle
new file mode 100644
index 000000000..4a8928de1
--- /dev/null
+++ b/core/build.gradle
@@ -0,0 +1,56 @@
+import javax.tools.ToolProvider
+
+buildscript {
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+apply plugin: 'kotlin'
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ languageVersion = "1.2"
+ apiVersion = languageVersion
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$bundled_kotlin_compiler_version"
+ compile "org.jetbrains.kotlin:kotlin-reflect:$bundled_kotlin_compiler_version"
+
+ compile group: 'com.google.inject', name: 'guice', version: '3.0'
+ compile "org.jsoup:jsoup:1.8.3"
+
+ compile "org.jetbrains.kotlin:kotlin-compiler:$bundled_kotlin_compiler_version"
+ compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version"
+
+ compile "org.jetbrains:markdown:0.1.41"
+
+ implementation "com.squareup.okhttp3:okhttp:4.0.0-RC1"
+
+ compile intellijCoreAnalysis()
+
+ compile "org.jetbrains.kotlin:kotlin-plugin-ij193:$kotlin_plugin_version" //TODO: parametrize ij version after 1.3.70
+
+// Google version of the library in the libs folder. Fixing 129528660
+// compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8'
+
+ //tools.jar
+ def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") })
+ compileOnly toolsJar
+ testCompile toolsJar
+
+ compile project(":integration")
+
+ 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"
+
+ testCompile ideaRT()
+} \ No newline at end of file
diff --git a/core/libs/kotlinx-html-jvm-0.6.8-google.jar b/core/libs/kotlinx-html-jvm-0.6.8-google.jar
new file mode 100644
index 000000000..08d3c2efa
--- /dev/null
+++ b/core/libs/kotlinx-html-jvm-0.6.8-google.jar
Binary files differ
diff --git a/core/samples/memberWithModifiers.java b/core/samples/memberWithModifiers.java
new file mode 100644
index 000000000..ea645c218
--- /dev/null
+++ b/core/samples/memberWithModifiers.java
@@ -0,0 +1,12 @@
+public abstract class Test {
+ /**
+ * Summary for Function
+ * @param name is String parameter
+ * @param value is int parameter
+ */
+ protected final void fn(String name, int value) {
+
+ }
+
+ protected void openFn() {}
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
new file mode 100644
index 000000000..9fea67407
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -0,0 +1,371 @@
+package org.jetbrains.dokka
+
+import com.google.common.collect.ImmutableMap
+import com.intellij.core.CoreApplicationEnvironment
+import com.intellij.core.CoreModuleManager
+import com.intellij.mock.MockComponentManager
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.extensions.Extensions
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.module.ModuleManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.roots.OrderEnumerationHandler
+import com.intellij.openapi.roots.ProjectFileIndex
+import com.intellij.openapi.roots.ProjectRootManager
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.psi.PsiElement
+import com.intellij.psi.impl.source.javadoc.JavadocManagerImpl
+import com.intellij.psi.javadoc.CustomJavadocTagProvider
+import com.intellij.psi.javadoc.JavadocManager
+import com.intellij.psi.javadoc.JavadocTagInfo
+import com.intellij.psi.search.GlobalSearchScope
+import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
+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
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
+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.context.withModule
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.platform.TargetPlatform
+import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
+import org.jetbrains.kotlin.platform.konan.KonanPlatforms
+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.PlatformDependentAnalyzerServices
+import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
+import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
+import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
+import org.jetbrains.kotlin.util.slicedMap.WritableSlice
+import java.io.File
+
+/**
+ * Kotlin as a service entry point
+ *
+ * Configures environment, analyses files and provides facilities to perform code processing without emitting bytecode
+ *
+ * $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 {
+ val configuration = CompilerConfiguration()
+
+ init {
+ configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
+ }
+
+ fun createCoreEnvironment(): KotlinCoreEnvironment {
+ System.setProperty("idea.io.use.fallback", "true")
+ val environment = KotlinCoreEnvironment.createForProduction(this, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+ val projectComponentManager = environment.project as MockComponentManager
+
+ val projectFileIndex = CoreProjectFileIndex(environment.project,
+ environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
+
+ val moduleManager = object : CoreModuleManager(environment.project, this) {
+ override fun getModules(): Array<out Module> = arrayOf(projectFileIndex.module)
+ }
+
+ CoreApplicationEnvironment.registerComponentInstance(projectComponentManager.picoContainer,
+ ModuleManager::class.java, moduleManager)
+
+ Extensions.registerAreaClass("IDEA_MODULE", null)
+ CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(),
+ OrderEnumerationHandler.EP_NAME, OrderEnumerationHandler.Factory::class.java)
+
+ CoreApplicationEnvironment.registerExtensionPoint(Extensions.getArea(environment.project),
+ JavadocTagInfo.EP_NAME, JavadocTagInfo::class.java)
+ CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(),
+ CustomJavadocTagProvider.EP_NAME, CustomJavadocTagProvider::class.java)
+
+ projectComponentManager.registerService(ProjectFileIndex::class.java,
+ projectFileIndex)
+ projectComponentManager.registerService(ProjectRootManager::class.java,
+ CoreProjectRootManager(projectFileIndex))
+ projectComponentManager.registerService(JavadocManager::class.java,
+ JavadocManagerImpl(environment.project))
+ projectComponentManager.registerService(CustomJavadocTagProvider::class.java,
+ CustomJavadocTagProvider { emptyList() })
+ 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 createResolutionFacade(environment: KotlinCoreEnvironment): Pair<DokkaResolutionFacade, DokkaResolutionFacade> {
+
+ val projectContext = ProjectContext(environment.project, "Dokka")
+ val sourceFiles = environment.getSourceFiles()
+
+
+ val library = object : ModuleInfo {
+ override val name: Name = Name.special("<library>")
+ override val platform: TargetPlatform
+ get() = JvmPlatforms.defaultJvmPlatform
+ override val analyzerServices: PlatformDependentAnalyzerServices =
+ JvmPlatformAnalyzerServices
+ override fun dependencies(): List<ModuleInfo> = listOf(this)
+ }
+ val module = object : ModuleInfo {
+ override val name: Name = Name.special("<module>")
+ override val platform: TargetPlatform
+ get() = JvmPlatforms.defaultJvmPlatform
+ override val analyzerServices: PlatformDependentAnalyzerServices =
+ JvmPlatformAnalyzerServices
+ override fun dependencies(): List<ModuleInfo> = listOf(this, library)
+ }
+
+ val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles)
+
+ val builtIns = JvmBuiltIns(
+ projectContext.storageManager,
+ JvmBuiltIns.Kind.FROM_CLASS_LOADER
+ )
+
+
+ val javaRoots = classpath
+ .mapNotNull {
+ val rootFile = when {
+ it.extension == "jar" ->
+ StandardFileSystems.jar().findFileByPath("${it.absolutePath}${"!/"}")
+ else ->
+ StandardFileSystems.local().findFileByPath(it.absolutePath)
+ }
+
+ rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
+ }
+
+ val resolverForProject = object : AbstractResolverForProject<ModuleInfo>(
+ "Dokka",
+ projectContext,
+ modules = listOf(module, library)
+ ) {
+ override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> =
+ when (module) {
+ library -> ModuleContent(module, emptyList(), GlobalSearchScope.notScope(sourcesScope))
+ module -> ModuleContent(module, emptyList(), sourcesScope)
+ else -> throw IllegalArgumentException("Unexpected module info")
+ }
+
+ override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = builtIns
+
+ override fun createResolverForModule(
+ descriptor: ModuleDescriptor,
+ moduleInfo: ModuleInfo
+ ): ResolverForModule = JvmResolverForModuleFactory(
+ 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
+ }),
+ CompilerEnvironment,
+ KonanPlatforms.defaultKonanPlatform
+ ).createResolverForModule(
+ descriptor as ModuleDescriptorImpl,
+ projectContext.withModule(descriptor),
+ modulesContent(moduleInfo),
+ this,
+ LanguageVersionSettingsImpl.DEFAULT
+ )
+
+ override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null
+ }
+
+ 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
+ }
+
+ fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) {
+ val languageVersion = LanguageVersion.fromVersionString(languageVersionString) ?: LanguageVersion.LATEST_STABLE
+ val apiVersion = apiVersionString?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(languageVersion)
+ configuration.languageVersionSettings = LanguageVersionSettingsImpl(languageVersion, apiVersion)
+ }
+
+ /**
+ * Classpath for this environment.
+ */
+ val classpath: List<File>
+ get() = configuration.jvmClasspathRoots
+
+ /**
+ * Adds list of paths to classpath.
+ * $paths: collection of files to add
+ */
+ fun addClasspath(paths: List<File>) {
+ configuration.addJvmClasspathRoots(paths)
+ }
+
+ /**
+ * Adds path to classpath.
+ * $path: path to add
+ */
+ fun addClasspath(path: File) {
+ configuration.addJvmClasspathRoot(path)
+ }
+
+ /**
+ * List of source roots for this environment.
+ */
+ val sources: List<String>
+ get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
+ ?.filterIsInstance<KotlinSourceRoot>()
+ ?.map { it.path } ?: emptyList()
+
+ /**
+ * Adds list of paths to source roots.
+ * $list: collection of files to add
+ */
+ fun addSources(list: List<String>) {
+ list.forEach {
+ configuration.addKotlinSourceRoot(it)
+ val file = File(it)
+ if (file.isDirectory || file.extension == ".java") {
+ configuration.addJavaSourceRoot(file)
+ }
+ }
+ }
+
+ fun addRoots(list: List<ContentRoot>) {
+ configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list)
+ }
+
+ /**
+ * Disposes the environment and frees all associated resources.
+ */
+ override fun dispose() {
+ Disposer.dispose(this)
+ }
+}
+
+fun contentRootFromPath(path: String): ContentRoot {
+ val file = File(path)
+ return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false)
+}
+
+
+class DokkaResolutionFacade(override val project: Project,
+ override val moduleDescriptor: ModuleDescriptor,
+ val resolverForModule: ResolverForModule) : ResolutionFacade {
+ override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
+ return resolverForModule.componentProvider.tryGetService(serviceClass)
+ }
+
+ override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
+ return resolveSession.resolveToDescriptor(declaration)
+ }
+
+ override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext {
+ throw UnsupportedOperationException()
+ }
+
+ val resolveSession: ResolveSession get() = getFrontendService(ResolveSession::class.java)
+
+ override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext {
+ if (element is KtDeclaration) {
+ val descriptor = resolveToDescriptor(element)
+ return object : BindingContext {
+ override fun <K : Any?, V : Any?> getKeys(p0: WritableSlice<K, V>?): Collection<K> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getType(p0: KtExpression): KotlinType? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <K : Any?, V : Any?> get(slice: ReadOnlySlice<K, V>?, key: K): V? {
+ if (key != element) {
+ throw UnsupportedOperationException()
+ }
+ return when {
+ slice == BindingContext.DECLARATION_TO_DESCRIPTOR -> descriptor as V
+ slice == BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER && (element as KtParameter).hasValOrVar() -> descriptor as V
+ else -> null
+ }
+ }
+
+ override fun getDiagnostics(): Diagnostics {
+ throw UnsupportedOperationException()
+ }
+
+ override fun addOwnDataTo(p0: BindingTrace, p1: Boolean) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <K : Any?, V : Any?> getSliceContents(p0: ReadOnlySlice<K, V>): ImmutableMap<K, V> {
+ throw UnsupportedOperationException()
+ }
+
+ }
+ }
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any> getFrontendService(serviceClass: Class<T>): T {
+ return resolverForModule.componentProvider.getService(serviceClass)
+ }
+
+ override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T {
+ return resolverForModule.componentProvider.getService(serviceClass)
+ }
+
+ override fun <T : Any> getIdeService(serviceClass: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+}
diff --git a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt
new file mode 100644
index 000000000..d9093760c
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiFile
+import org.jetbrains.kotlin.analyzer.ModuleInfo
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
+
+
+class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService {
+ override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
+ return resolutionFacade
+ }
+
+ override fun getResolutionFacade(
+ elements: List<KtElement>,
+ platform: org.jetbrains.kotlin.platform.TargetPlatform
+ ): ResolutionFacade {
+ return resolutionFacade
+ }
+
+ override fun getResolutionFacadeByFile(
+ file: PsiFile,
+ platform: org.jetbrains.kotlin.platform.TargetPlatform
+ ): ResolutionFacade? {
+ return resolutionFacade
+ }
+
+ override fun getResolutionFacadeByModuleInfo(
+ moduleInfo: ModuleInfo,
+ platform: org.jetbrains.kotlin.platform.TargetPlatform
+ ): ResolutionFacade? {
+ return resolutionFacade
+ }
+
+ override fun getSuppressionCache(): KotlinSuppressCache {
+ throw UnsupportedOperationException()
+ }
+
+}
+
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
new file mode 100644
index 000000000..4ece8d300
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
@@ -0,0 +1,569 @@
+package org.jetbrains.dokka
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.components.BaseComponent
+import com.intellij.openapi.extensions.ExtensionPointName
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.projectRoots.Sdk
+import com.intellij.openapi.projectRoots.SdkAdditionalData
+import com.intellij.openapi.projectRoots.SdkModificator
+import com.intellij.openapi.projectRoots.SdkTypeId
+import com.intellij.openapi.roots.*
+import com.intellij.openapi.roots.impl.ProjectOrderEnumerator
+import com.intellij.openapi.util.Condition
+import com.intellij.openapi.util.Key
+import com.intellij.openapi.util.UserDataHolderBase
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.openapi.vfs.VfsUtilCore.getVirtualFileForJar
+import com.intellij.openapi.vfs.VirtualFile
+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.picocontainer.PicoContainer
+import java.io.File
+
+/**
+ * Workaround for the lack of ability to create a ProjectFileIndex implementation using only
+ * classes from projectModel-{api,impl}.
+ */
+class CoreProjectFileIndex(private val project: Project, contentRoots: List<ContentRoot>) : ProjectFileIndex, ModuleFileIndex {
+ override fun iterateContent(p0: ContentIterator, p1: VirtualFileFilter?): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator, p2: VirtualFileFilter?): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInLibrary(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ val sourceRoots = contentRoots.filter { it !is JvmClasspathRoot }
+ val classpathRoots = contentRoots.filterIsInstance<JvmClasspathRoot>()
+
+ val module: Module = object : UserDataHolderBase(), Module {
+ override fun isDisposed(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getOptionValue(p0: String): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun clearOption(p0: String) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getName(): String = "<Dokka module>"
+
+ override fun getModuleWithLibrariesScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleWithDependentsScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleContentScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isLoaded(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun setOption(p0: String, p1: String?) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleWithDependenciesScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleWithDependenciesAndLibrariesScope(p0: Boolean): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getProject(): Project = this@CoreProjectFileIndex.project
+
+ override fun getModuleContentWithDependenciesScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleFilePath(): String {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleTestsWithDependentsScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleScope(): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleScope(p0: Boolean): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleRuntimeScope(p0: Boolean): GlobalSearchScope {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleFile(): VirtualFile? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> getExtensions(p0: ExtensionPointName<T>): Array<out T> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getComponent(p0: String): BaseComponent? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> getComponent(p0: Class<T>, p1: T): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> getComponent(interfaceClass: Class<T>): T? {
+ if (interfaceClass == ModuleRootManager::class.java) {
+ return moduleRootManager as T
+ }
+ throw UnsupportedOperationException()
+ }
+
+ override fun getDisposed(): Condition<*> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> getComponents(p0: Class<T>): Array<out T> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getPicoContainer(): PicoContainer {
+ throw UnsupportedOperationException()
+ }
+
+ override fun hasComponent(p0: Class<*>): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getMessageBus(): MessageBus {
+ throw UnsupportedOperationException()
+ }
+
+ override fun dispose() {
+ throw UnsupportedOperationException()
+ }
+ }
+
+ private val sdk: Sdk = object : Sdk, RootProvider {
+ override fun getFiles(rootType: OrderRootType): Array<out VirtualFile> = classpathRoots
+ .mapNotNull { StandardFileSystems.local().findFileByPath(it.file.path) }
+ .toTypedArray()
+
+ override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener, p1: Disposable) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getUrls(p0: OrderRootType): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun removeRootSetChangedListener(p0: RootProvider.RootSetChangedListener) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSdkModificator(): SdkModificator {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getName(): String = "<dokka SDK>"
+
+ override fun getRootProvider(): RootProvider = this
+
+ override fun getHomePath(): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getVersionString(): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSdkAdditionalData(): SdkAdditionalData? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun clone(): Any {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSdkType(): SdkTypeId {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getHomeDirectory(): VirtualFile? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> getUserData(p0: Key<T>): T? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <T : Any?> putUserData(p0: Key<T>, p1: T?) {
+ throw UnsupportedOperationException()
+ }
+ }
+
+ private val moduleSourceOrderEntry = object : ModuleSourceOrderEntry {
+ override fun getFiles(p0: OrderRootType): Array<VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getUrls(p0: OrderRootType): Array<String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R {
+ throw UnsupportedOperationException()
+ }
+
+
+ override fun getPresentableName(): String {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getOwnerModule(): Module = module
+
+
+ override fun isValid(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun compareTo(other: OrderEntry?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getRootModel(): ModuleRootModel = moduleRootManager
+
+ override fun isSynthetic(): Boolean {
+ throw UnsupportedOperationException()
+ }
+ }
+
+ private val sdkOrderEntry = object : JdkOrderEntry {
+ override fun getFiles(p0: OrderRootType): Array<VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getUrls(p0: OrderRootType): Array<String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getJdkName(): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getJdk(): Sdk = sdk
+
+ override fun getPresentableName(): String {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getOwnerModule(): Module {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isValid(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getRootFiles(p0: OrderRootType): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getRootUrls(p0: OrderRootType): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun compareTo(other: OrderEntry?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isSynthetic(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ }
+
+ inner class MyModuleRootManager : ModuleRootManager() {
+ override fun getExternalSource(): ProjectModelExternalSource? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getExcludeRoots(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentEntries(): Array<out ContentEntry> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getExcludeRootUrls(): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun <R : Any?> processOrder(p0: RootPolicy<R>, p1: R): R {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRoots(p0: Boolean): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRoots(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRoots(p0: JpsModuleSourceRootType<*>): MutableList<VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRoots(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun orderEntries(): OrderEnumerator =
+ ProjectOrderEnumerator(project, null).using(object : RootModelProvider {
+ override fun getModules(): Array<out Module> = arrayOf(module)
+
+ override fun getRootModel(p0: Module): ModuleRootModel = this@MyModuleRootManager
+ })
+
+ override fun <T : Any?> getModuleExtension(p0: Class<T>): T {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getDependencyModuleNames(): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModule(): Module = this@CoreProjectFileIndex.module
+
+ override fun isSdkInherited(): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getOrderEntries(): Array<out OrderEntry> = arrayOf(moduleSourceOrderEntry, sdkOrderEntry)
+
+ override fun getSourceRootUrls(): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRootUrls(p0: Boolean): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSdk(): Sdk? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRootUrls(): Array<out String> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleDependencies(): Array<out Module> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleDependencies(p0: Boolean): Array<out Module> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModifiableModel(): ModifiableRootModel {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isDependsOn(p0: Module): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getFileIndex(): ModuleFileIndex {
+ return this@CoreProjectFileIndex
+ }
+
+ override fun getDependencies(): Array<out Module> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getDependencies(p0: Boolean): Array<out Module> {
+ throw UnsupportedOperationException()
+ }
+ }
+
+ val moduleRootManager = MyModuleRootManager()
+
+ override fun getContentRootForFile(p0: VirtualFile): VirtualFile? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRootForFile(p0: VirtualFile, p1: Boolean): VirtualFile? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getPackageNameByDirectory(p0: VirtualFile): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInLibrarySource(file: VirtualFile): Boolean = false
+
+ override fun getClassRootForFile(file: VirtualFile): VirtualFile? =
+ classpathRoots.firstOrNull { it.contains(file) }?.let { StandardFileSystems.local().findFileByPath(it.file.path) }
+
+ override fun getOrderEntriesForFile(file: VirtualFile): List<OrderEntry> =
+ if (classpathRoots.contains(file)) listOf(sdkOrderEntry) else emptyList()
+
+ override fun isInLibraryClasses(file: VirtualFile): Boolean = classpathRoots.contains(file)
+
+ override fun isExcluded(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSourceRootForFile(p0: VirtualFile): VirtualFile? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isUnderIgnored(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isLibraryClassFile(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleForFile(file: VirtualFile): Module? =
+ if (sourceRoots.contains(file)) module else null
+
+ private fun List<ContentRoot>.contains(file: VirtualFile): Boolean = any { it.contains(file) }
+
+ override fun getModuleForFile(p0: VirtualFile, p1: Boolean): Module? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInSource(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isIgnored(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isContentSourceFile(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInSourceContent(file: VirtualFile): Boolean = sourceRoots.contains(file)
+
+ override fun iterateContent(p0: ContentIterator): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInContent(p0: VirtualFile): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isInTestSourceContent(file: VirtualFile): Boolean = false
+
+ override fun isUnderSourceRootOfType(p0: VirtualFile, p1: MutableSet<out JpsModuleSourceRootType<*>>): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getOrderEntryForFile(p0: VirtualFile): OrderEntry? {
+ throw UnsupportedOperationException()
+ }
+}
+
+class CoreProjectRootManager(val projectFileIndex: CoreProjectFileIndex) : ProjectRootManager() {
+ override fun orderEntries(): OrderEnumerator {
+ throw UnsupportedOperationException()
+ }
+
+ override fun orderEntries(p0: MutableCollection<out Module>): OrderEnumerator {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRootsFromAllModules(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun setProjectSdk(p0: Sdk?) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun setProjectSdkName(p0: String) {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getModuleSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentSourceRoots(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getFileIndex(): ProjectFileIndex = projectFileIndex
+
+ override fun getProjectSdkName(): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getProjectSdk(): Sdk? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRoots(): Array<out VirtualFile> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getContentRootUrls(): MutableList<String> {
+ throw UnsupportedOperationException()
+ }
+
+}
+
+fun ContentRoot.contains(file: VirtualFile) = when (this) {
+ is JvmContentRoot -> {
+ val path = if (file.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL)
+ getVirtualFileForJar(file)?.path ?: file.path
+ else
+ file.path
+ File(path).startsWith(this.file.absoluteFile)
+ }
+ is KotlinSourceRoot -> File(file.path).startsWith(File(this.path).absoluteFile)
+ else -> false
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
new file mode 100644
index 000000000..4a4c78e56
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.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.platform.jvm.JvmPlatforms
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+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, JvmPlatforms.defaultJvmPlatform)!!
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
new file mode 100644
index 000000000..402018102
--- /dev/null
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -0,0 +1,83 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.DokkaConfiguration.PackageOptions
+import ru.yole.jkid.deserialization.deserialize
+import java.io.File
+import java.util.function.BiConsumer
+
+
+fun parsePerPackageOptions(arg: String): List<PackageOptions> {
+ if (arg.isBlank()) return emptyList()
+
+ return arg.split(";").map { it.split(",") }.map {
+ val prefix = it.first()
+ if (prefix == "")
+ throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
+ val args = it.subList(1, it.size)
+ val deprecated = args.find { it.endsWith("deprecated") }?.startsWith("+") ?: true
+ val reportUndocumented = args.find { it.endsWith("warnUndocumented") }?.startsWith("+") ?: true
+ val privateApi = args.find { it.endsWith("privateApi") }?.startsWith("+") ?: false
+ val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") ?: false
+ PackageOptionsImpl(prefix, includeNonPublic = privateApi, reportUndocumented = reportUndocumented, skipDeprecated = !deprecated, suppress = suppress)
+ }
+}
+
+class DokkaBootstrapImpl : DokkaBootstrap {
+
+ private class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger {
+ override fun info(message: String) {
+ consumer.accept("info", message)
+ }
+
+ override fun warn(message: String) {
+ consumer.accept("warn", message)
+ }
+
+ override fun error(message: String) {
+ consumer.accept("error", message)
+ }
+ }
+
+ lateinit var generator: DokkaGenerator
+
+ 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 = outputDir,
+ outputFormat = format,
+ includeNonPublic = includeNonPublic,
+ includeRootPackage = includeRootPackage,
+ reportUndocumented = reportUndocumented,
+ skipEmptyPackages = skipEmptyPackages,
+ skipDeprecated = skipDeprecated,
+ jdkVersion = jdkVersion,
+ generateClassIndexPage = generateClassIndexPage,
+ generatePackageIndexPage = generatePackageIndexPage,
+ sourceLinks = sourceLinks,
+ impliedPlatforms = impliedPlatforms,
+ perPackageOptions = perPackageOptions,
+ externalDocumentationLinks = externalDocumentationLinks,
+ noStdlibLink = noStdlibLink,
+ noJdkLink = noJdkLink,
+ languageVersion = languageVersion,
+ apiVersion = apiVersion,
+ cacheRoot = cacheRoot,
+ suppressedFiles = suppressedFiles.map { File(it) }.toSet(),
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries,
+ outlineRoot = outlineRoot,
+ dacRoot = dacRoot
+ )
+ )
+ }
+
+ 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
new file mode 100644
index 000000000..d78d4a0c1
--- /dev/null
+++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider
+import org.jetbrains.dokka.KotlinElementSignatureProvider
+import org.jetbrains.dokka.ElementSignatureProvider
+import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
+import org.jetbrains.dokka.Samples.SampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.toType
+import kotlin.reflect.KClass
+
+
+interface DefaultAnalysisComponentServices {
+ val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder>
+ val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder>
+ val sampleProcessingService: KClass<out SampleProcessingService>
+ val elementSignatureProvider: KClass<out ElementSignatureProvider>
+}
+
+interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices {
+ override fun configureAnalysis(binder: Binder): Unit = with(binder) {
+ bind<ElementSignatureProvider>() toType elementSignatureProvider
+ bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass
+ bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass
+ bind<SampleProcessingService>() toType sampleProcessingService
+ }
+}
+
+
+object KotlinAsJava : DefaultAnalysisComponentServices {
+ override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
+ override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
+ override val sampleProcessingService = DefaultSampleProcessingService::class
+ override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class
+}
+
+
+object KotlinAsKotlin : DefaultAnalysisComponentServices {
+ override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
+ override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
+ override val sampleProcessingService = DefaultSampleProcessingService::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/DacHtmlFormat.kt b/core/src/main/kotlin/Formats/DacHtmlFormat.kt
new file mode 100644
index 000000000..e2399435b
--- /dev/null
+++ b/core/src/main/kotlin/Formats/DacHtmlFormat.kt
@@ -0,0 +1,949 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import kotlinx.html.*
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Samples.DevsiteSampleProcessingService
+import org.jetbrains.dokka.Kotlin.ParameterInfoNode
+import org.jetbrains.dokka.Utilities.firstSentence
+import java.lang.Math.max
+import java.net.URI
+import kotlin.reflect.KClass
+
+/**
+ * On Devsite, certain headers and footers are needed for generating Devsite metadata.
+ */
+class DevsiteHtmlTemplateService @Inject constructor(
+ @Named("outlineRoot") val outlineRoot: String,
+ @Named("dacRoot") val dacRoot: String
+) : JavaLayoutHtmlTemplateService {
+ override fun composePage(page: JavaLayoutHtmlFormatOutputBuilder.Page, tagConsumer: TagConsumer<Appendable>, headContent: HEAD.() -> Unit, bodyContent: BODY.() -> Unit) {
+ tagConsumer.html {
+ attributes["devsite"] = "true"
+ head {
+ headContent()
+ title {
+ +when (page) {
+ is JavaLayoutHtmlFormatOutputBuilder.Page.ClassIndex -> "Class Index"
+ is JavaLayoutHtmlFormatOutputBuilder.Page.ClassPage -> page.node.nameWithOuterClass()
+ is JavaLayoutHtmlFormatOutputBuilder.Page.PackageIndex -> "Package Index"
+ is JavaLayoutHtmlFormatOutputBuilder.Page.PackagePage -> page.node.nameWithOuterClass()
+ }
+ }
+ unsafe { +"{% setvar book_path %}${dacRoot}/${outlineRoot}_book.yaml{% endsetvar %}\n{% include \"_shared/_reference-head-tags.html\" %}\n" }
+ }
+ body {
+ bodyContent()
+ }
+ }
+ }
+}
+
+class DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl @javax.inject.Inject constructor(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger
+) : JavaLayoutHtmlFormatOutputBuilderFactory {
+ override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
+ return createOutputBuilder(output, uriProvider.mainUri(node))
+ }
+
+ override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
+ return DevsiteLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
+ }
+}
+
+class DevsiteLayoutHtmlFormatOutputBuilder(
+ output: Appendable,
+ languageService: LanguageService,
+ uriProvider: JavaLayoutHtmlUriProvider,
+ templateService: JavaLayoutHtmlTemplateService,
+ logger: DokkaLogger,
+ uri: URI
+) : JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) {
+ override fun FlowContent.fullMemberDocs(node: DocumentationNode) {
+ fullMemberDocs(node, node)
+ }
+
+ override fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) {
+ a {
+ attributes["name"] = uriNode.signatureForAnchor(logger).anchorEncoded()
+ }
+ div(classes = "api apilevel-${node.apiLevel.name}") {
+ attributes["data-version-added"] = node.apiLevel.name
+ h3(classes = "api-name") {
+ //id = node.signatureForAnchor(logger).urlEncoded()
+ +node.prettyName
+ }
+ apiAndDeprecatedVersions(node)
+ pre(classes = "api-signature no-pretty-print") { renderedSignature(node, LanguageService.RenderMode.FULL) }
+ deprecationWarningToMarkup(node, prefix = true)
+ nodeContent(node, uriNode)
+ node.constantValue()?.let { value ->
+ pre {
+ +"Value: "
+ code { +value }
+ }
+ }
+ for ((name, sections) in node.content.sections.groupBy { it.tag }) {
+ when (name) {
+ ContentTags.Return -> {
+ table(classes = "responsive") {
+ tbody {
+ tr {
+ th {
+ colSpan = "2"
+ +name
+ }
+ }
+ sections.forEach {
+ tr {
+ if (it.children.size > 0) {
+ td {
+ val firstChild = it.children.first()
+ if (firstChild is ContentBlock &&
+ firstChild.children.size == 3 &&
+ firstChild.children[0] is NodeRenderContent &&
+ firstChild.children[1] is ContentSymbol &&
+ firstChild.children[2] is ContentText) {
+ // it.children is expected to have two items
+ // First should have 3 children of its own:
+ // - NodeRenderContent is the return type
+ // - ContentSymbol - ":"
+ // - ContentText - " "
+ // We want to only use NodeRenderContent in a separate <td> and
+ // <code> to get proper formatting in DAC.
+ code {
+ metaMarkup(listOf(firstChild.children[0]))
+ }
+ } else {
+ metaMarkup(listOf(firstChild))
+ }
+ }
+ td {
+ if (it.children.size > 1) {
+ metaMarkup(it.children.subList(1, it.children.size))
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ContentTags.Parameters -> {
+ table(classes = "responsive") {
+ tbody {
+ tr {
+ th {
+ colSpan = "2"
+ +name
+ }
+ }
+ sections.forEach { section ->
+ tr {
+ td {
+ val parameterInfoNode = section.children.find { it is ParameterInfoNode } as? ParameterInfoNode
+ // If there is no info found, just display the parameter
+ // name.
+ if (parameterInfoNode?.parameterContent == null) {
+ code {
+ section.subjectName?.let { +it }
+ }
+ } else {
+ // Add already marked up type information here
+ metaMarkup(
+ listOf(parameterInfoNode.parameterContent!!)
+ )
+ }
+ }
+ td {
+ metaMarkup(section.children)
+ }
+ }
+ }
+ }
+ }
+ }
+ ContentTags.SeeAlso -> {
+ div {
+ p {
+ b {
+ +name
+ }
+ }
+ ul(classes = "nolist") {
+ sections.filter {it.tag == "See Also"}.forEach {
+ it.children.forEach { child ->
+ if (child is ContentNodeLazyLink || child is ContentExternalLink) {
+ li {
+ code {
+ contentNodeToMarkup(child) // Wrap bare links in listItems.
+ } // bare links come from the java-to-kotlin parser.
+ }
+ }
+ else if (child is ContentUnorderedList) {
+ metaMarkup(child.children) // Already wrapped in listItems.
+ } // this is how we want things to look. No parser currently does this (yet).
+ else if (child is ContentParagraph) {
+ li{
+ code {
+ metaMarkup (child.children) // Replace paragraphs with listItems.
+ } // paragraph-wrapped links come from the kotlin parser
+ }
+ } // NOTE: currently the java-to-java parser does not add See Also links!
+ }
+ }
+ }
+ }
+ }
+ ContentTags.Exceptions -> {
+ table(classes = "responsive") {
+ tbody {
+ tr {
+ th {
+ colSpan = "2"
+ +name
+ }
+ }
+ sections.forEach {
+ tr {
+ td {
+ code {
+ it.subjectName?.let { +it }
+ }
+ }
+ td {
+ metaMarkup(it.children)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun summary(node: DocumentationNode) = node.firstSentenceOfSummary()
+
+ fun TBODY.xmlAttributeRow(attr: DocumentationNode) = tr {
+ td {
+ a(href = attr) {
+ code {
+ +attr.attributeRef!!.name
+ }
+ }
+ }
+ td {
+ +attr.attributeRef!!.firstSentence()
+ }
+ }
+
+ protected fun FlowContent.fullAttributeDocs(
+ attributes: List<DocumentationNode>,
+ header: String
+ ) {
+ if (attributes.none()) return
+ h2 {
+ +header
+ }
+ attributes.forEach {
+ fullMemberDocs(it.attributeRef!!, it)
+ }
+ }
+
+ override fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
+ fullAttributeDocs(attributes, "XML attributes")
+ fullMemberDocs(enumValues, "Enum values")
+ fullMemberDocs(constants, "Constants")
+
+ constructors.forEach { (visibility, group) ->
+ fullMemberDocs(group, "${visibility.capitalize()} constructors")
+ }
+
+ functions.forEach { (visibility, group) ->
+ fullMemberDocs(group, "${visibility.capitalize()} methods")
+ }
+
+ fullMemberDocs(properties, "Properties")
+
+ fields.forEach { (visibility, group) ->
+ fullMemberDocs(group, "${visibility.capitalize()} fields")
+ }
+ if (!hasMeaningfulCompanion) {
+ fullMemberDocs(companionFunctions, "Companion functions")
+ fullMemberDocs(companionProperties, "Companion properties")
+ }
+ }
+
+ override fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
+ this@classLikeSummaries.summaryNodeGroup(
+ nestedClasses,
+ header = "Nested classes",
+ summaryId = "nestedclasses",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ nestedClassSummaryRow(it)
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ attributes,
+ header="XML attributes",
+ summaryId="lattrs",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ xmlAttributeRow(it)
+ }
+
+ this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
+ superClasses = inheritedAttributes.entries,
+ header="Inherited XML attributes",
+ tableId="inhattrs",
+ tableClass = "responsive",
+ row = { inheritedXmlAttributeRow(it)}
+ )
+
+ this@classLikeSummaries.summaryNodeGroup(
+ constants,
+ header = "Constants",
+ summaryId = "constants",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) { propertyLikeSummaryRow(it) }
+
+ this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
+ superClasses = inheritedConstants.entries,
+ header = "Inherited constants",
+ tableId = "inhconstants",
+ tableClass = "responsive constants inhtable",
+ row = { inheritedMemberRow(it) }
+ )
+
+ constructors.forEach { (visibility, group) ->
+ this@classLikeSummaries.summaryNodeGroup(
+ group,
+ header = "${visibility.capitalize()} constructors",
+ summaryId = "${visibility.take(3)}ctors",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ enumValues,
+ header = "Enum values",
+ summaryId = "enumvalues",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ propertyLikeSummaryRow(it, showSignature = false)
+ }
+
+ functions.forEach { (visibility, group) ->
+ this@classLikeSummaries.summaryNodeGroup(
+ group,
+ header = "${visibility.capitalize()} methods",
+ summaryId = "${visibility.take(3)}methods",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ companionFunctions,
+ header = "Companion functions",
+ summaryId = "compmethods",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+
+ this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
+ superClasses = inheritedFunctionsByReceiver.entries,
+ header = "Inherited functions",
+ tableId = "inhmethods",
+ tableClass = "responsive",
+ row = { inheritedMemberRow(it) }
+ )
+
+ this@classLikeSummaries.summaryNodeGroup(
+ extensionFunctions.entries,
+ header = "Extension functions",
+ summaryId = "extmethods",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedExtensionFunctions.entries,
+ header = "Inherited extension functions",
+ summaryId = "inhextmethods",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ fields.forEach { (visibility, group) ->
+ this@classLikeSummaries.summaryNodeGroup(
+ group,
+ header = "${visibility.capitalize()} fields",
+ summaryId = "${visibility.take(3)}fields",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) { propertyLikeSummaryRow(it) }
+ }
+
+ this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
+ superClasses = inheritedFieldsByReceiver.entries,
+ header = "Inherited fields",
+ tableId = "inhfields",
+ tableClass = "responsive properties inhtable",
+ row = { inheritedMemberRow(it) }
+ )
+
+ this@classLikeSummaries.summaryNodeGroup(
+ properties,
+ header = "Properties",
+ summaryId = "properties",
+ tableClass = "responsive",
+ headerAsRow = true
+ ) { propertyLikeSummaryRow(it) }
+
+
+ this@classLikeSummaries.summaryNodeGroup(
+ companionProperties,
+ "Companion properties",
+ headerAsRow = true
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
+ superClasses = inheritedPropertiesByReceiver.entries,
+ header = "Inherited properties",
+ tableId = "inhfields",
+ tableClass = "responsive properties inhtable",
+ row = { inheritedMemberRow(it) }
+ )
+
+ this@classLikeSummaries.summaryNodeGroup(
+ extensionProperties.entries,
+ "Extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedExtensionProperties.entries,
+ "Inherited extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ }
+
+ fun <T> FlowContent.summaryNodeGroup(
+ nodes: Iterable<T>,
+ header: String,
+ headerAsRow: Boolean,
+ summaryId: String,
+ tableClass: String = "responsive",
+ row: TBODY.(T) -> Unit
+ ) {
+ if (nodes.none()) return
+ if (!headerAsRow) {
+ h2 { +header }
+ }
+ table(classes = tableClass) {
+ id = summaryId
+ tbody {
+ if (headerAsRow) {
+ developerHeading(header)
+ }
+ nodes.forEach { node ->
+ row(node)
+ }
+ }
+ }
+ }
+
+ override fun FlowContent.contentBlockCode(content: ContentBlockCode) {
+ pre {
+ attributes["class"] = "prettyprint"
+ contentNodesToMarkup(content.children)
+ }
+ }
+
+ override fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
+ pre {
+ attributes["class"] = "prettyprint"
+ contentNodesToMarkup(content.importsBlock.children)
+ +"\n\n"
+ contentNodesToMarkup(content.children)
+ }
+ }
+
+ override fun generatePackage(page: Page.PackagePage) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +page.node.name }
+ nodeContent(page.node)
+ this@composePage.summaryNodeGroup(page.interfaces.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Interfaces", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.classes.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Classes", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.exceptions.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.typeAliases.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.annotations.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Annotations", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.enums.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+ this@composePage.summaryNodeGroup(
+ page.constants.sortedBy { it.name },
+ "Top-level constants summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.functions.sortedBy { it.name },
+ "Top-level functions summary",
+ headerAsRow = false
+ ) {
+ functionLikeSummaryRow(it)
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.properties.sortedBy { it.name },
+ "Top-level properties summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ summaryNodeGroupForExtensions("Extension functions summary", page.extensionFunctions.entries)
+ summaryNodeGroupForExtensions("Extension properties summary", page.extensionProperties.entries)
+
+ fullMemberDocs(page.constants.sortedBy { it.name }, "Top-level constants")
+ fullMemberDocs(page.functions.sortedBy { it.name }, "Top-level functions")
+ fullMemberDocs(page.properties.sortedBy { it.name }, "Top-level properties")
+ fullMemberDocs(page.extensionFunctions.values.flatten().sortedBy { it.name }, "Extension functions")
+ fullMemberDocs(page.extensionProperties.values.flatten().sortedBy { it.name }, "Extension properties")
+ }
+ )
+
+ private fun TBODY.inheritedXmlAttributeRow(inheritedMember: DocumentationNode) {
+ tr(classes = "api apilevel-${inheritedMember.attributeRef!!.apiLevel.name}") {
+ attributes["data-version-added"] = "${inheritedMember.apiLevel}"
+ td {
+ code {
+ a(href = inheritedMember) { +inheritedMember.attributeRef!!.name }
+ }
+ }
+ td {
+ attributes["width"] = "100%"
+ p {
+ nodeContent(inheritedMember.attributeRef!!, inheritedMember)
+ }
+ }
+ }
+ }
+
+ private fun TBODY.inheritedMemberRow(inheritedMember: DocumentationNode) {
+ tr(classes = "api apilevel-${inheritedMember.apiLevel.name}") {
+ attributes["data-version-added"] = "${inheritedMember.apiLevel}"
+ val type = inheritedMember.detailOrNull(NodeKind.Type)
+ td {
+ code {
+ type?.let {
+ renderedSignature(it, LanguageService.RenderMode.SUMMARY)
+ }
+ }
+ }
+ td {
+ attributes["width"] = "100%"
+ code {
+ a(href = inheritedMember) { +inheritedMember.name }
+ if (inheritedMember.kind == NodeKind.Function) {
+ shortFunctionParametersList(inheritedMember)
+ }
+ }
+ p {
+ nodeContent(inheritedMember)
+ }
+ }
+ }
+ }
+
+ private fun FlowContent.expandableSummaryNodeGroupForInheritedMembers(
+ tableId: String,
+ header: String,
+ tableClass: String,
+ superClasses: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>,
+ row: TBODY.(inheritedMember: DocumentationNode) -> Unit
+ ) {
+ if (superClasses.none()) return
+ table(classes = tableClass) {
+ attributes["id"] = tableId
+ tbody {
+ developerHeading(header)
+ superClasses.forEach { (superClass, members) ->
+ tr(classes = "api apilevel-${superClass.apiLevel.name}") {
+ td {
+ attributes["colSpan"] = "2"
+ div(classes = "expandable jd-inherited-apis") {
+ span(classes = "expand-control exw-expanded") {
+ +"From class "
+ code {
+ a(href = superClass) { +superClass.name }
+ }
+ }
+ table(classes = "responsive exw-expanded-content") {
+ tbody {
+ members.forEach { inheritedMember ->
+ row(inheritedMember)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun FlowContent.summaryNodeGroupForExtensions(
+ header: String,
+ receivers: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>
+ ) {
+ if (receivers.none()) return
+ h2 { +header }
+ div {
+ receivers.forEach {
+ table {
+ tr {
+ td {
+ attributes["colSpan"] = "2"
+ +"For "
+ a(href = it.key) { +it.key.name }
+ }
+ }
+ it.value.forEach { node ->
+ tr {
+ if (node.kind != NodeKind.Constructor) {
+ td {
+ modifiers(node)
+ renderedSignature(node.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
+ }
+ }
+ td {
+ div {
+ code {
+ val receiver = node.detailOrNull(NodeKind.Receiver)
+ if (receiver != null) {
+ renderedSignature(receiver.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
+ +"."
+ }
+ a(href = node) { +node.name }
+ shortFunctionParametersList(node)
+ }
+ }
+
+ nodeSummary(node)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ override fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Package Index" }
+ table {
+ tbody {
+ for (node in page.packages) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ override fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Class Index" }
+
+ p {
+ +"These are all the API classes. See all "
+ a(href="packages.html") {
+ +"API packages."
+ }
+ }
+
+ div(classes = "jd-letterlist") {
+ page.classesByFirstLetter.forEach { (letter) ->
+ +"\n "
+ a(href = "#letter_$letter") { +letter }
+ unsafe {
+ raw("&nbsp;&nbsp;")
+ }
+ }
+ +"\n "
+ }
+
+ page.classesByFirstLetter.forEach { (letter, classes) ->
+ h2 {
+ id = "letter_$letter"
+ +letter
+ }
+ table {
+ tbody {
+ for (node in classes) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
+ }
+ td {
+ if (!deprecatedIndexSummary(node)) {
+ nodeSummary(node)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ override fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
+ table(classes = "jd-inheritance-table") {
+ var level = superclasses.size
+ superclasses.forEach {
+ tr {
+ var spaceColumns = max(superclasses.size - 1 - level, 0)
+ while (spaceColumns > 0) {
+ td(classes = "jd-inheritance-space") {
+ +" "
+ }
+ spaceColumns--
+ }
+ if (it != superclasses.first()) {
+ td(classes = "jd-inheritance-space") {
+ +"   ↳"
+ }
+ }
+ td(classes = "jd-inheritance-class-cell") {
+ attributes["colSpan"] = "$level"
+ qualifiedTypeReference(it)
+ }
+ }
+ level--
+ }
+ }
+ }
+
+ override fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
+ if (inheritors.isEmpty()) return
+
+ // The number of subclasses in collapsed view before truncating and adding a "and xx others".
+ // See https://developer.android.com/reference/android/view/View for an example.
+ val numToShow = 12
+
+ table(classes = "jd-sumtable jd-sumtable-subclasses") {
+ tbody {
+ tr {
+ td {
+ div(classes = "expandable") {
+ span(classes = "expand-control") {
+ if (direct)
+ +"Known Direct Subclasses"
+ else
+ +"Known Indirect Subclasses"
+ }
+ div(classes = "showalways") {
+ attributes["id"] = if (direct) "subclasses-direct" else "subclasses-indirect"
+
+ inheritors.take(numToShow).forEach { inheritor ->
+ a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+ if (inheritor != inheritors.last()) +", "
+ }
+
+ if (inheritors.size > numToShow) {
+ +"and ${inheritors.size - numToShow} others."
+ }
+ }
+ div(classes = "exw-expanded-content") {
+ attributes["id"] = if (direct) "subclasses-direct-summary" else "subclasses-indirect-summary"
+ table(classes = "jd-sumtable-expando") {
+ inheritors.forEach { inheritor ->
+ tr(classes = "api api-level-${inheritor.apiLevel.name}") {
+ attributes["data-version-added"] = inheritor.apiLevel.name
+ td(classes = "jd-linkcol") {
+ a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+ }
+ td(classes = "jd-descrcol") {
+ attributes["width"] = "100%"
+ nodeSummary(inheritor)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ fun DocumentationNode.firstSentenceOfSummary(): ContentNode {
+
+ fun Sequence<ContentNode>.flatten(): Sequence<ContentNode> {
+ return flatMap {
+ when (it) {
+ is ContentParagraph -> it.children.asSequence().flatten()
+ else -> sequenceOf(it)
+ }
+ }
+ }
+
+ fun ContentNode.firstSentence(): ContentText? = when(this) {
+ is ContentText -> ContentText(text.firstSentence())
+ else -> null
+ }
+
+ val elements = sequenceOf(summary).flatten()
+ fun containsDot(it: ContentNode) = (it as? ContentText)?.text?.contains(".") == true
+
+ val paragraph = ContentParagraph()
+ (elements.takeWhile { !containsDot(it) } + elements.firstOrNull { containsDot(it) }?.firstSentence()).forEach {
+ if (it != null) {
+ paragraph.append(it)
+ }
+ }
+ if (paragraph.isEmpty()) {
+ return ContentEmpty
+ }
+
+ return paragraph
+ }
+
+ fun DocumentationNode.firstSentence(): String {
+ val sb = StringBuilder()
+ addContentNodeToStringBuilder(content, sb)
+ return sb.toString().firstSentence()
+ }
+
+ private fun addContentNodesToStringBuilder(content: List<ContentNode>, sb: StringBuilder): Unit =
+ content.forEach { addContentNodeToStringBuilder(it, sb) }
+
+ private fun addContentNodeToStringBuilder(content: ContentNode, sb: StringBuilder) {
+ when (content) {
+ is ContentText -> sb.appendWith(content.text)
+ is ContentSymbol -> sb.appendWith(content.text)
+ is ContentKeyword -> sb.appendWith(content.text)
+ is ContentIdentifier -> sb.appendWith(content.text)
+ is ContentEntity -> sb.appendWith(content.text)
+
+ is ContentHeading -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentStrong -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentStrikethrough -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentEmphasis -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentOrderedList -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentUnorderedList -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentListItem -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentCode -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentBlockSampleCode -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentBlockCode -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentParagraph -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentNodeLink -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentBookmark -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentExternalLink -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentLocalLink -> addContentNodesToStringBuilder(content.children, sb)
+ is ContentSection -> { }
+ is ContentBlock -> addContentNodesToStringBuilder(content.children, sb)
+ }
+ }
+
+ private fun StringBuilder.appendWith(text: String, delimiter: String = " ") {
+ if (this.length == 0) {
+ append(text)
+ } else {
+ append(delimiter)
+ append(text)
+ }
+ }
+}
+
+fun TBODY.developerHeading(header: String) {
+ tr {
+ th {
+ attributes["colSpan"] = "2"
+ +header
+ }
+ }
+}
+
+class DacFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin {
+ override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
+
+ override val outlineFactoryClass = DacOutlineFormatter::class
+ override val languageServiceClass = KotlinLanguageService::class
+ override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
+ override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
+ override val sampleProcessingService = DevsiteSampleProcessingService::class
+}
+
+
+class DacAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava {
+ override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
+
+ override val outlineFactoryClass = DacOutlineFormatter::class
+ override val languageServiceClass = NewJavaLanguageService::class
+ override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
+ override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
+}
diff --git a/core/src/main/kotlin/Formats/DacOutlineService.kt b/core/src/main/kotlin/Formats/DacOutlineService.kt
new file mode 100644
index 000000000..e249c39f7
--- /dev/null
+++ b/core/src/main/kotlin/Formats/DacOutlineService.kt
@@ -0,0 +1,395 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Inject
+import org.jetbrains.dokka.*
+import java.net.URI
+import com.google.inject.name.Named
+import org.jetbrains.kotlin.cfg.pseudocode.AllTypes
+
+
+interface DacOutlineFormatService {
+ fun computeOutlineURI(node: DocumentationNode): URI
+ fun format(to: Appendable, node: DocumentationNode)
+}
+
+class DacOutlineFormatter @Inject constructor(
+ uriProvider: JavaLayoutHtmlUriProvider,
+ languageService: LanguageService,
+ @Named("dacRoot") dacRoot: String,
+ @Named("generateClassIndex") generateClassIndex: Boolean,
+ @Named("generatePackageIndex") generatePackageIndex: Boolean
+) : JavaLayoutHtmlFormatOutlineFactoryService {
+ val tocOutline = TocOutlineService(uriProvider, languageService, dacRoot, generateClassIndex, generatePackageIndex)
+ val outlines = listOf(tocOutline)
+
+ override fun generateOutlines(outputProvider: (URI) -> Appendable, nodes: Iterable<DocumentationNode>) {
+ for (node in nodes) {
+ for (outline in outlines) {
+ val uri = outline.computeOutlineURI(node)
+ val output = outputProvider(uri)
+ outline.format(output, node)
+ }
+ }
+ }
+}
+
+/**
+ * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each
+ * index.html file in the doc tree.
+ */
+class BookOutlineService(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val dacRoot: String,
+ val generateClassIndex: Boolean,
+ val generatePackageIndex: Boolean
+) : DacOutlineFormatService {
+ override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_book.yaml")
+
+ override fun format(to: Appendable, node: DocumentationNode) {
+ appendOutline(to, listOf(node))
+ }
+
+ var outlineLevel = 0
+
+ /** Appends formatted outline to [StringBuilder](to) using specified [location] */
+ fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) {
+ if (outlineLevel == 0) to.appendln("reference:")
+ for (node in nodes) {
+ appendOutlineHeader(node, to)
+ val subPackages = node.members.filter {
+ it.kind == NodeKind.Package
+ }
+ if (subPackages.any()) {
+ val sortedMembers = subPackages.sortedBy { it.name.toLowerCase() }
+ appendOutlineLevel(to) {
+ appendOutline(to, sortedMembers)
+ }
+ }
+
+ }
+ }
+
+ fun appendOutlineHeader(node: DocumentationNode, to: Appendable) {
+ if (node is DocumentationModule) {
+ to.appendln("- title: Package Index")
+ to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}")
+ to.appendln(" status_text: no-toggle")
+ } else {
+ to.appendln("- title: ${languageService.renderName(node)}")
+ to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(node)}")
+ to.appendln(" status_text: no-toggle")
+ }
+ }
+
+ fun appendOutlineLevel(to: Appendable, body: () -> Unit) {
+ outlineLevel++
+ body()
+ outlineLevel--
+ }
+}
+
+/**
+ * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each
+ * index.html file in the doc tree.
+ */
+class TocOutlineService(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val dacRoot: String,
+ val generateClassIndex: Boolean,
+ val generatePackageIndex: Boolean
+) : DacOutlineFormatService {
+ override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_toc.yaml")
+
+ override fun format(to: Appendable, node: DocumentationNode) {
+ appendOutline(to, listOf(node))
+ }
+
+ var outlineLevel = 0
+
+ /** Appends formatted outline to [StringBuilder](to) using specified [location] */
+ fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) {
+ if (outlineLevel == 0) to.appendln("toc:")
+ for (node in nodes) {
+ appendOutlineHeader(node, to)
+ val subPackages = node.members.filter {
+ it.kind == NodeKind.Package
+ }
+ if (subPackages.any()) {
+ val sortedMembers = subPackages.sortedBy { it.nameWithOuterClass() }
+ appendOutlineLevel {
+ appendOutline(to, sortedMembers)
+ }
+ }
+ }
+ }
+
+ fun appendOutlineHeader(node: DocumentationNode, to: Appendable) {
+ if (node is DocumentationModule) {
+ if (generateClassIndex) {
+ node.members.filter { it.kind == NodeKind.AllTypes }.firstOrNull()?.let {
+ to.appendln("- title: Class Index")
+ to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(it).resolve("classes.html")}")
+ to.appendln()
+ }
+ }
+ if (generatePackageIndex) {
+ to.appendln("- title: Package Index")
+ to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}")
+ to.appendln()
+ }
+ } else if (node.kind != NodeKind.AllTypes && !(node is DocumentationModule)) {
+ to.appendln("- title: ${languageService.renderName(node)}")
+ to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(node)}")
+ to.appendln()
+ var addedSectionHeader = false
+ for (kind in NodeKind.classLike) {
+ val members = node.getMembersOfKinds(kind)
+ if (members.isNotEmpty()) {
+ if (!addedSectionHeader) {
+ to.appendln(" section:")
+ addedSectionHeader = true
+ }
+ to.appendln(" - title: ${kind.pluralizedName()}")
+ to.appendln()
+ to.appendln(" section:")
+ members.sortedBy { it.nameWithOuterClass().toLowerCase() }.forEach { member ->
+ to.appendln(" - title: ${languageService.renderNameWithOuterClass(member)}")
+ to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(member)}".trimEnd('#'))
+ to.appendln()
+ }
+ }
+ }
+ to.appendln().appendln()
+ }
+ }
+
+ fun appendOutlineLevel(body: () -> Unit) {
+ outlineLevel++
+ body()
+ outlineLevel--
+ }
+}
+
+class DacNavOutlineService constructor(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val dacRoot: String
+) : DacOutlineFormatService {
+ override fun computeOutlineURI(node: DocumentationNode): URI =
+ uriProvider.outlineRootUri(node).resolve("navtree_data.js")
+
+ override fun format(to: Appendable, node: DocumentationNode) {
+ to.append("var NAVTREE_DATA = ").appendNavTree(node.members).append(";")
+ }
+
+ private fun Appendable.appendNavTree(nodes: Iterable<DocumentationNode>): Appendable {
+ append("[ ")
+ var first = true
+ for (node in nodes) {
+ if (!first) append(", ")
+ first = false
+ val interfaces = node.getMembersOfKinds(NodeKind.Interface)
+ val classes = node.getMembersOfKinds(NodeKind.Class)
+ val objects = node.getMembersOfKinds(NodeKind.Object)
+ val annotations = node.getMembersOfKinds(NodeKind.AnnotationClass)
+ val enums = node.getMembersOfKinds(NodeKind.Enum)
+ val exceptions = node.getMembersOfKinds(NodeKind.Exception)
+
+ append("[ \"${node.name}\", \"$dacRoot${uriProvider.tryGetMainUri(node)}\", [ ")
+ var needComma = false
+ if (interfaces.firstOrNull() != null) {
+ appendNavTreePagesOfKind("Interfaces", interfaces)
+ needComma = true
+ }
+ if (classes.firstOrNull() != null) {
+ if (needComma) append(", ")
+ appendNavTreePagesOfKind("Classes", classes)
+ needComma = true
+ }
+ if (objects.firstOrNull() != null) {
+ if (needComma) append(", ")
+ appendNavTreePagesOfKind("Objects", objects)
+ }
+ if (annotations.firstOrNull() != null) {
+ if (needComma) append(", ")
+ appendNavTreePagesOfKind("Annotations", annotations)
+ needComma = true
+ }
+ if (enums.firstOrNull() != null) {
+ if (needComma) append(", ")
+ appendNavTreePagesOfKind("Enums", enums)
+ needComma = true
+ }
+ if (exceptions.firstOrNull() != null) {
+ if (needComma) append(", ")
+ appendNavTreePagesOfKind("Exceptions", exceptions)
+ }
+ append(" ] ]")
+ }
+ append(" ]")
+ return this
+ }
+
+ private fun Appendable.appendNavTreePagesOfKind(kindTitle: String,
+ nodesOfKind: Iterable<DocumentationNode>): Appendable {
+ append("[ \"$kindTitle\", null, [ ")
+ var started = false
+ for (node in nodesOfKind) {
+ if (started) append(", ")
+ started = true
+ appendNavTreeChild(node)
+ }
+ append(" ], null, null ]")
+ return this
+ }
+
+ private fun Appendable.appendNavTreeChild(node: DocumentationNode): Appendable {
+ append("[ \"${node.nameWithOuterClass()}\", \"${dacRoot}${uriProvider.tryGetMainUri(node)}\"")
+ append(", null, null, null ]")
+ return this
+ }
+}
+
+class DacSearchOutlineService(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val dacRoot: String
+) : DacOutlineFormatService {
+
+ override fun computeOutlineURI(node: DocumentationNode): URI =
+ uriProvider.outlineRootUri(node).resolve("lists.js")
+
+ override fun format(to: Appendable, node: DocumentationNode) {
+ val pageNodes = node.getAllPageNodes()
+ var id = 0
+ to.append("var KTX_CORE_DATA = [\n")
+ var first = true
+ for (pageNode in pageNodes) {
+ if (pageNode.kind == NodeKind.Module) continue
+ if (!first) to.append(", \n")
+ first = false
+ to.append(" { " +
+ "id:$id, " +
+ "label:\"${pageNode.qualifiedName()}\", " +
+ "link:\"${dacRoot}${uriProvider.tryGetMainUri(pageNode)}\", " +
+ "type:\"${pageNode.getClassOrPackage()}\", " +
+ "deprecated:\"false\" }")
+ id++
+ }
+ to.append("\n];")
+ }
+
+ private fun DocumentationNode.getClassOrPackage(): String =
+ if (hasOwnPage())
+ "class"
+ else if (isPackage()) {
+ "package"
+ } else {
+ ""
+ }
+
+ private fun DocumentationNode.getAllPageNodes(): Iterable<DocumentationNode> {
+ val allPageNodes = mutableListOf<DocumentationNode>()
+ recursiveSetAllPageNodes(allPageNodes)
+ return allPageNodes
+ }
+
+ private fun DocumentationNode.recursiveSetAllPageNodes(
+ allPageNodes: MutableList<DocumentationNode>) {
+ for (child in members) {
+ if (child.hasOwnPage() || child.isPackage()) {
+ allPageNodes.add(child)
+ child.qualifiedName()
+ child.recursiveSetAllPageNodes(allPageNodes)
+ }
+ }
+ }
+
+}
+
+/**
+ * Return all children of the node who are one of the selected `NodeKind`s. It recursively fetches
+ * all offspring, not just immediate children.
+ */
+fun DocumentationNode.getMembersOfKinds(vararg kinds: NodeKind): MutableList<DocumentationNode> {
+ val membersOfKind = mutableListOf<DocumentationNode>()
+ recursiveSetMembersOfKinds(kinds, membersOfKind)
+ return membersOfKind
+}
+
+private fun DocumentationNode.recursiveSetMembersOfKinds(kinds: Array<out NodeKind>,
+ membersOfKind: MutableList<DocumentationNode>) {
+ for (member in members) {
+ if (member.kind in kinds) {
+ membersOfKind.add(member)
+ }
+ member.recursiveSetMembersOfKinds(kinds, membersOfKind)
+ }
+}
+
+/**
+ * Returns whether or not this node owns a page. The criteria for whether a node owns a page is
+ * similar to the way javadoc is structured. Classes, Interfaces, Enums, AnnotationClasses,
+ * Exceptions, and Objects (Kotlin-specific) meet the criteria.
+ */
+fun DocumentationNode.hasOwnPage() =
+ kind == NodeKind.Class || kind == NodeKind.Interface || kind == NodeKind.Enum ||
+ kind == NodeKind.AnnotationClass || kind == NodeKind.Exception ||
+ kind == NodeKind.Object
+
+/**
+ * In most cases, this returns the short name of the `Type`. When the Type is an inner Type, it
+ * prepends the name with the containing Type name(s).
+ *
+ * For example, if you have a class named OuterClass and an inner class named InnerClass, this would
+ * return OuterClass.InnerClass.
+ *
+ */
+fun DocumentationNode.nameWithOuterClass(): String {
+ val nameBuilder = StringBuilder(name)
+ var parent = owner
+ if (hasOwnPage()) {
+ while (parent != null && parent.hasOwnPage()) {
+ nameBuilder.insert(0, "${parent.name}.")
+ parent = parent.owner
+ }
+ }
+ return nameBuilder.toString()
+}
+
+/**
+ * Return whether the node is a package.
+ */
+fun DocumentationNode.isPackage(): Boolean {
+ return kind == NodeKind.Package
+}
+
+/**
+ * Return the 'page owner' of this node. `DocumentationNode.hasOwnPage()` defines the criteria for
+ * a page owner. If this node is not a page owner, then it iterates up through its ancestors to
+ * find the first page owner.
+ */
+fun DocumentationNode.pageOwner(): DocumentationNode {
+ if (hasOwnPage() || owner == null) {
+ return this
+ } else {
+ var parent: DocumentationNode = owner!!
+ while (!parent.hasOwnPage() && !parent.isPackage()) {
+ parent = parent.owner!!
+ }
+ return parent
+ }
+}
+
+fun NodeKind.pluralizedName() = when(this) {
+ NodeKind.Class -> "Classes"
+ NodeKind.Interface -> "Interfaces"
+ NodeKind.AnnotationClass -> "Annotations"
+ NodeKind.Enum -> "Enums"
+ NodeKind.Exception -> "Exceptions"
+ NodeKind.Object -> "Objects"
+ NodeKind.TypeAlias -> "TypeAliases"
+ else -> "${name}s"
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/ExtraOutlineServices.kt b/core/src/main/kotlin/Formats/ExtraOutlineServices.kt
new file mode 100644
index 000000000..e4eeac01a
--- /dev/null
+++ b/core/src/main/kotlin/Formats/ExtraOutlineServices.kt
@@ -0,0 +1,20 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+/**
+ * Outline service that is responsible for generating a single outline format.
+ *
+ * TODO: port existing implementations of ExtraOutlineService to OutlineService, and remove this.
+ */
+interface ExtraOutlineService {
+ fun getFileName(): String
+ fun getFile(location: Location): File
+ fun format(node: DocumentationNode): String
+}
+
+/**
+ * Holder of all of the extra outline services needed for a StandardFormat, in addition to the main
+ * [OutlineFormatService].
+ */
+abstract class ExtraOutlineServices(vararg val services: ExtraOutlineService)
diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt
new file mode 100644
index 000000000..b497fb0f5
--- /dev/null
+++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.lazyBind
+import org.jetbrains.dokka.Utilities.toOptional
+import org.jetbrains.dokka.Utilities.toType
+import kotlin.reflect.KClass
+
+
+interface FormatDescriptorAnalysisComponent {
+ fun configureAnalysis(binder: Binder)
+}
+
+interface FormatDescriptorOutputComponent {
+ fun configureOutput(binder: Binder)
+}
+
+interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent
+
+
+abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor {
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType NodeLocationAwareGenerator::class
+ bind<NodeLocationAwareGenerator>() toType generatorServiceClass
+
+ bind<LanguageService>() toType languageServiceClass
+
+ lazyBind<OutlineFormatService>() toOptional (outlineServiceClass)
+ lazyBind<FormatService>() toOptional formatServiceClass
+ lazyBind<PackageListService>() toOptional packageListServiceClass
+ }
+
+ abstract val formatServiceClass: KClass<out FormatService>?
+ abstract val outlineServiceClass: KClass<out OutlineFormatService>?
+ abstract val generatorServiceClass: KClass<out FileGenerator>
+ abstract val packageListServiceClass: KClass<out PackageListService>?
+
+ open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/FormatService.kt b/core/src/main/kotlin/Formats/FormatService.kt
new file mode 100644
index 000000000..63f25008f
--- /dev/null
+++ b/core/src/main/kotlin/Formats/FormatService.kt
@@ -0,0 +1,32 @@
+package org.jetbrains.dokka
+
+/**
+ * Abstract representation of a formatting service used to output documentation in desired format
+ *
+ * Bundled Formatters:
+ * * [HtmlFormatService] – outputs documentation to HTML format
+ * * [MarkdownFormatService] – outputs documentation in Markdown format
+ */
+interface FormatService {
+ /** Returns extension for output files */
+ val extension: String
+
+ /** extension which will be used for internal and external linking */
+ val linkExtension: String
+ get() = extension
+
+ fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder
+
+ fun enumerateSupportFiles(callback: (resource: String, targetPath: String) -> Unit) {
+ }
+}
+
+interface FormattedOutputBuilder {
+ /** Appends formatted content to [StringBuilder](to) using specified [location] */
+ fun appendNodes(nodes: Iterable<DocumentationNode>)
+}
+
+/** Format content to [String] using specified [location] */
+fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply {
+ createOutputBuilder(this, location).appendNodes(nodes)
+}.toString()
diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt
new file mode 100644
index 000000000..036ec8564
--- /dev/null
+++ b/core/src/main/kotlin/Formats/GFMFormatService.kt
@@ -0,0 +1,61 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+
+open class GFMOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>
+) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
+ override fun appendTable(vararg columns: String, body: () -> Unit) {
+ to.appendln(columns.joinToString(" | ", "| ", " |"))
+ to.appendln("|" + "---|".repeat(columns.size))
+ body()
+ }
+
+ override fun appendUnorderedList(body: () -> Unit) {
+ if (inTableCell) {
+ wrapInTag("ul", body)
+ } else {
+ super.appendUnorderedList(body)
+ }
+ }
+
+ override fun appendOrderedList(body: () -> Unit) {
+ if (inTableCell) {
+ wrapInTag("ol", body)
+ } else {
+ super.appendOrderedList(body)
+ }
+ }
+
+ override fun appendListItem(body: () -> Unit) {
+ if (inTableCell) {
+ wrapInTag("li", body)
+ } else {
+ super.appendListItem(body)
+ }
+ }
+}
+
+open class GFMFormatService(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ linkExtension: String,
+ impliedPlatforms: List<String>
+) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) {
+
+ @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>
+ ) : this(generator, signatureGenerator, "md", impliedPlatforms)
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
+ GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+}
diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt
new file mode 100644
index 000000000..0ad946be2
--- /dev/null
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -0,0 +1,168 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import java.io.File
+
+open class HtmlOutputBuilder(to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>,
+ val templateService: HtmlTemplateService)
+ : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+{
+ override fun appendText(text: String) {
+ to.append(text.htmlEscape())
+ }
+
+ override fun appendSymbol(text: String) {
+ to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
+ }
+
+ override fun appendKeyword(text: String) {
+ to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
+ }
+
+ override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+ val id = signature?.let { " id=\"$it\"" }.orEmpty()
+ to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>")
+ }
+
+ override fun appendBlockCode(language: String, body: () -> Unit) {
+ val openTags = if (language.isNotBlank())
+ "<pre><code class=\"lang-$language\">"
+ else
+ "<pre><code>"
+ wrap(openTags, "</code></pre>", body)
+ }
+
+ override fun appendHeader(level: Int, body: () -> Unit) =
+ wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true)
+ override fun appendParagraph(body: () -> Unit) =
+ wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true)
+
+ override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body)
+
+ override fun appendLine() {
+ to.appendln("<br/>")
+ }
+
+ override fun appendAnchor(anchor: String) {
+ to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>")
+ }
+
+ override fun appendTable(vararg columns: String, body: () -> Unit) =
+ wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true)
+ override fun appendTableBody(body: () -> Unit) =
+ wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true)
+ override fun appendTableRow(body: () -> Unit) =
+ wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true)
+ override fun appendTableCell(body: () -> Unit) =
+ wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true)
+
+ override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
+
+ override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body)
+ override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body)
+ override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
+ override fun appendCode(body: () -> Unit) = wrapInTag("code", body)
+
+ override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true)
+ override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true)
+ override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true)
+
+ override fun appendBreadcrumbSeparator() {
+ to.append("&nbsp;/&nbsp;")
+ }
+
+ override fun appendNodes(nodes: Iterable<DocumentationNode>) {
+ templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location))
+ super.appendNodes(nodes)
+ templateService.appendFooter(to)
+ }
+
+ override fun appendNonBreakingSpace() {
+ to.append("&nbsp;")
+ }
+
+ override fun ensureParagraph() {
+
+ }
+}
+
+open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ val templateService: HtmlTemplateService,
+ @Named(impliedPlatformsName) val impliedPlatforms: List<String>)
+: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService {
+
+ override fun enumerateSupportFiles(callback: (String, String) -> Unit) {
+ callback("/dokka/styles/style.css", "style.css")
+ }
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location) =
+ HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
+
+ override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
+ templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location))
+ super.appendOutline(location, to, nodes)
+ templateService.appendFooter(to)
+ }
+
+ override fun getOutlineFileName(location: Location): File {
+ return File("${location.path}-outline.html")
+ }
+
+ override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
+ val link = ContentNodeDirectLink(node)
+ link.append(languageService.render(node, LanguageService.RenderMode.FULL))
+ val tempBuilder = StringBuilder()
+ createOutputBuilder(tempBuilder, location).appendContent(link)
+ to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>")
+ }
+
+ override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
+ to.appendln("<ul>")
+ body()
+ to.appendln("</ul>")
+ }
+}
+
+fun getPageTitle(nodes: Iterable<DocumentationNode>): String? {
+ val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) }
+ return breakdownByLocation.keys.singleOrNull()
+}
+
+fun formatPageTitle(node: DocumentationNode): String {
+ val path = node.path
+ val moduleName = path.first().name
+ if (path.size == 1) {
+ return moduleName
+ }
+
+ val qName = qualifiedNameForPageTitle(node)
+ return qName + " - " + moduleName
+}
+
+private fun qualifiedNameForPageTitle(node: DocumentationNode): String {
+ if (node.kind == NodeKind.Package) {
+ var packageName = node.qualifiedName()
+ if (packageName.isEmpty()) {
+ packageName = "root package"
+ }
+ return packageName
+ }
+
+ val path = node.path
+ var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike }
+ if (pathFromToplevelMember.isEmpty()) {
+ pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function }
+ }
+ if (pathFromToplevelMember.isNotEmpty()) {
+ return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".")
+ }
+ return node.qualifiedName()
+}
diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
new file mode 100644
index 000000000..a65a7b18c
--- /dev/null
+++ b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
@@ -0,0 +1,38 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+interface HtmlTemplateService {
+ fun appendHeader(to: StringBuilder, title: String?, basePath: File)
+ fun appendFooter(to: StringBuilder)
+
+ companion object {
+ fun default(css: String? = null): HtmlTemplateService {
+ return object : HtmlTemplateService {
+ override fun appendFooter(to: StringBuilder) {
+ if (!to.endsWith('\n')) {
+ to.append('\n')
+ }
+ to.appendln("</BODY>")
+ to.appendln("</HTML>")
+ }
+ override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {
+ to.appendln("<HTML>")
+ to.appendln("<HEAD>")
+ to.appendln("<meta charset=\"UTF-8\">")
+ if (title != null) {
+ to.appendln("<title>$title</title>")
+ }
+ if (css != null) {
+ val cssPath = basePath.resolve(css).toUnixString()
+ to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">")
+ }
+ to.appendln("</HEAD>")
+ to.appendln("<BODY>")
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
new file mode 100644
index 000000000..b94886693
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
@@ -0,0 +1,141 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import kotlinx.html.*
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.lazyBind
+import org.jetbrains.dokka.Utilities.toOptional
+import org.jetbrains.dokka.Utilities.toType
+import java.net.URI
+import kotlin.reflect.KClass
+
+
+abstract class JavaLayoutHtmlFormatDescriptorBase : FormatDescriptor, DefaultAnalysisComponent {
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType generatorServiceClass
+ bind<LanguageService>() toType languageServiceClass
+ bind<JavaLayoutHtmlTemplateService>() toType templateServiceClass
+ bind<JavaLayoutHtmlUriProvider>() toType generatorServiceClass
+ lazyBind<JavaLayoutHtmlFormatOutlineFactoryService>() toOptional outlineFactoryClass
+ bind<PackageListService>() toType packageListServiceClass
+ bind<JavaLayoutHtmlFormatOutputBuilderFactory>() toType outputBuilderFactoryClass
+ }
+
+ val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class
+ abstract val languageServiceClass: KClass<out LanguageService>
+ abstract val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService>
+ abstract val outlineFactoryClass: KClass<out JavaLayoutHtmlFormatOutlineFactoryService>?
+ abstract val packageListServiceClass: KClass<out PackageListService>
+ abstract val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory>
+}
+
+class JavaLayoutHtmlFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin {
+ override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = JavaLayoutHtmlFormatOutputBuilderFactoryImpl::class
+ override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
+ override val languageServiceClass = KotlinLanguageService::class
+ override val templateServiceClass = JavaLayoutHtmlTemplateService.Default::class
+ override val outlineFactoryClass = null
+}
+
+class JavaLayoutHtmlAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava {
+ override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = JavaLayoutHtmlFormatOutputBuilderFactoryImpl::class
+ override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
+ override val languageServiceClass = NewJavaLanguageService::class
+ override val templateServiceClass = JavaLayoutHtmlTemplateService.Default::class
+ override val outlineFactoryClass = null
+}
+
+interface JavaLayoutHtmlFormatOutlineFactoryService {
+ fun generateOutlines(outputProvider: (URI) -> Appendable, nodes: Iterable<DocumentationNode>)
+}
+
+
+interface JavaLayoutHtmlUriProvider {
+ fun tryGetContainerUri(node: DocumentationNode): URI?
+ fun tryGetMainUri(node: DocumentationNode): URI?
+ fun tryGetOutlineRootUri(node: DocumentationNode): URI?
+ fun containerUri(node: DocumentationNode): URI = tryGetContainerUri(node) ?: error("Unsupported ${node.kind}")
+ fun mainUri(node: DocumentationNode): URI = tryGetMainUri(node) ?: error("Unsupported ${node.kind}")
+ fun outlineRootUri(node: DocumentationNode): URI = tryGetOutlineRootUri(node) ?: error("Unsupported ${node.kind}")
+
+
+ fun linkTo(to: DocumentationNode, from: URI): String {
+ return mainUri(to).relativeTo(from).toString()
+ }
+
+ fun linkToFromOutline(to: DocumentationNode, from: URI): String {
+ return outlineRootUri(to).relativeTo(from).toString()
+ }
+
+ fun mainUriOrWarn(node: DocumentationNode): URI? = tryGetMainUri(node) ?: (null).also {
+ AssertionError("Not implemented mainUri for ${node.kind} (${node})").printStackTrace()
+ }
+}
+
+
+interface JavaLayoutHtmlTemplateService {
+ fun composePage(
+ page: JavaLayoutHtmlFormatOutputBuilder.Page,
+ tagConsumer: TagConsumer<Appendable>,
+ headContent: HEAD.() -> Unit,
+ bodyContent: BODY.() -> Unit
+ )
+
+ class Default : JavaLayoutHtmlTemplateService {
+ override fun composePage(
+ page: JavaLayoutHtmlFormatOutputBuilder.Page,
+ tagConsumer: TagConsumer<Appendable>,
+ headContent: HEAD.() -> Unit,
+ bodyContent: BODY.() -> Unit
+ ) {
+ tagConsumer.html {
+ head {
+ meta(charset = "UTF-8")
+ headContent()
+ }
+ body(block = bodyContent)
+ }
+ }
+ }
+}
+
+val DocumentationNode.companion get() = members(NodeKind.Object).find { it.details(NodeKind.Modifier).any { it.name == "companion" } }
+
+fun DocumentationNode.signatureForAnchor(logger: DokkaLogger): String {
+
+ fun StringBuilder.appendReceiverIfSo() {
+ detailOrNull(NodeKind.Receiver)?.let {
+ append("(")
+ append(it.detail(NodeKind.Type).qualifiedNameFromType())
+ append(").")
+ }
+ }
+
+ return when (kind) {
+ NodeKind.Function, NodeKind.Constructor, NodeKind.CompanionObjectFunction -> buildString {
+ if (kind == NodeKind.CompanionObjectFunction) {
+ append("Companion.")
+ }
+ appendReceiverIfSo()
+ append(prettyName)
+ details(NodeKind.Parameter).joinTo(this, prefix = "(", postfix = ")") { it.detail(NodeKind.Type).qualifiedNameFromType() }
+ }
+ NodeKind.Property, NodeKind.CompanionObjectProperty -> buildString {
+ if (kind == NodeKind.CompanionObjectProperty) {
+ append("Companion.")
+ }
+ appendReceiverIfSo()
+ append(name)
+ append(":")
+ append(detail(NodeKind.Type).qualifiedNameFromType())
+ }
+ NodeKind.TypeParameter, NodeKind.Parameter -> this.detail(NodeKind.Signature).name // Todo Why not signatureForAnchor
+ NodeKind.Field -> name
+ NodeKind.EnumItem -> "ENUM_VALUE:$name"
+ NodeKind.Attribute -> "attr_$name"
+ else -> "Not implemented signatureForAnchor $this".also { logger.warn(it) }
+ }
+}
+
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
new file mode 100644
index 000000000..59d898a2a
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
@@ -0,0 +1,1171 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.common.base.Throwables
+import kotlinx.html.*
+import kotlinx.html.Entities.nbsp
+import kotlinx.html.stream.appendHTML
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.LanguageService.RenderMode.FULL
+import org.jetbrains.dokka.LanguageService.RenderMode.SUMMARY
+import org.jetbrains.dokka.NodeKind.Companion.classLike
+import java.net.URI
+import javax.inject.Inject
+
+
+open class JavaLayoutHtmlFormatOutputBuilder(
+ val output: Appendable,
+ val languageService: LanguageService,
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger,
+ val uri: URI
+) {
+
+ val htmlConsumer = output.appendHTML()
+
+
+ private fun FlowContent.hN(
+ level: Int,
+ classes: String? = null,
+ block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit
+ ) {
+ when (level) {
+ 1 -> h1(classes, block)
+ 2 -> h2(classes, block)
+ 3 -> h3(classes, block)
+ 4 -> h4(classes, block)
+ 5 -> h5(classes, block)
+ 6 -> h6(classes, block)
+ }
+ }
+
+ protected open fun FlowContent.metaMarkup(content: List<ContentNode>, contextUri: URI = uri) =
+ contentNodesToMarkup(content, contextUri)
+
+ protected fun FlowContent.nodeContent(node: DocumentationNode, uriNode: DocumentationNode) =
+ contentNodeToMarkup(node.content, uriProvider.mainUriOrWarn(uriNode) ?: uri)
+
+ protected fun FlowContent.nodeContent(node: DocumentationNode) =
+ nodeContent(node, node)
+
+ protected fun FlowContent.contentNodesToMarkup(content: List<ContentNode>, contextUri: URI = uri): Unit =
+ content.forEach { contentNodeToMarkup(it, contextUri) }
+
+ protected fun FlowContent.contentNodeToMarkup(content: ContentNode, contextUri: URI = uri) {
+ when (content) {
+ is ContentText -> +content.text
+ is ContentSymbol -> span("symbol") { +content.text }
+ is ContentKeyword -> span("keyword") { +content.text }
+ is ContentIdentifier -> span("identifier") {
+ content.signature?.let { id = it }
+ +content.text
+ }
+
+ is ContentHeading -> hN(level = content.level) { contentNodesToMarkup(content.children, contextUri) }
+
+ is ContentEntity -> +content.text
+
+ is ContentStrong -> strong { contentNodesToMarkup(content.children, contextUri) }
+ is ContentStrikethrough -> del { contentNodesToMarkup(content.children, contextUri) }
+ is ContentEmphasis -> em { contentNodesToMarkup(content.children, contextUri) }
+
+ is ContentOrderedList -> ol { contentNodesToMarkup(content.children, contextUri) }
+ is ContentUnorderedList -> ul { contentNodesToMarkup(content.children, contextUri) }
+ is ContentListItem -> consumer.li {
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) }
+ ?: contentNodesToMarkup(content.children, contextUri)
+ }
+
+ is ContentDescriptionList -> dl { contentNodesToMarkup(content.children, contextUri) }
+ is ContentDescriptionTerm -> consumer.dt {
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> this@contentNodeToMarkup.contentNodesToMarkup(paragraph.children, contextUri) }
+ ?: this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri)
+ }
+ is ContentDescriptionDefinition -> consumer.dd {
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) }
+ ?: contentNodesToMarkup(content.children, contextUri)
+ }
+
+ is ContentTable -> table { contentNodesToMarkup(content.children, contextUri) }
+ is ContentTableBody -> consumer.tbody { this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) }
+ is ContentTableRow -> consumer.tr { this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) }
+ is ContentTableHeader -> consumer.th {
+ content.colspan?.let {
+ if (it.isNotBlank()) {
+ attributes["colspan"] = content.colspan
+ }
+ }
+ content.rowspan?.let {
+ if (it.isNotBlank()) {
+ attributes["rowspan"] = content.rowspan
+ }
+ }
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> this@contentNodeToMarkup.contentNodesToMarkup(paragraph.children, contextUri) }
+ ?: this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri)
+ }
+ is ContentTableCell -> consumer.td {
+ content.colspan?.let {
+ if (it.isNotBlank()) {
+ attributes["colspan"] = content.colspan
+ }
+ }
+ content.rowspan?.let {
+ if (it.isNotBlank()) {
+ attributes["rowspan"] = content.rowspan
+ }
+ }
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) }
+ ?: contentNodesToMarkup(content.children, contextUri)
+ }
+
+ is ContentSpecialReference -> aside(classes = "note") {
+ contentNodesToMarkup(content.children, contextUri)
+ }
+
+ is ContentCode -> contentInlineCode(content)
+ is ContentBlockSampleCode -> contentBlockSampleCode(content)
+ is ContentBlockCode -> contentBlockCode(content)
+
+ ContentNonBreakingSpace -> +nbsp
+ ContentSoftLineBreak, ContentIndentedSoftLineBreak -> {
+ }
+ ContentHardLineBreak -> br
+
+ is ContentParagraph -> p(classes = content.label) { contentNodesToMarkup(content.children, contextUri) }
+
+ is NodeRenderContent -> renderedSignature(content.node, mode = content.mode)
+ is ContentNodeLink -> {
+ fun FlowContent.body() = contentNodesToMarkup(content.children, contextUri)
+
+ when (content.node?.kind) {
+ NodeKind.TypeParameter -> body()
+ else -> a(href = content.node, block = FlowContent::body)
+ }
+ }
+ is ContentBookmark -> a {
+ id = content.name
+ contentNodesToMarkup(content.children, contextUri)
+ }
+ is ContentExternalLink -> contentExternalLink(content)
+ is ContentLocalLink -> a(href = contextUri.resolve(content.href).relativeTo(uri).toString()) {
+ contentNodesToMarkup(content.children, contextUri)
+ }
+ is ContentSection -> {
+ }
+ is ScriptBlock -> script(content.type, content.src) {}
+ is ContentBlock -> contentNodesToMarkup(content.children, contextUri)
+ }
+ }
+
+ protected open fun FlowContent.contentInlineCode(content: ContentCode) {
+ code { contentNodesToMarkup(content.children) }
+ }
+
+ protected open fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
+ pre {
+ code {
+ attributes["data-language"] = content.language
+ contentNodesToMarkup(content.importsBlock.children)
+ +"\n\n"
+ contentNodesToMarkup(content.children)
+ }
+ }
+ }
+
+ protected open fun FlowContent.contentBlockCode(content: ContentBlockCode) {
+ pre {
+ code {
+ attributes["data-language"] = content.language
+ contentNodesToMarkup(content.children)
+ }
+ }
+ }
+
+ protected open fun FlowContent.contentExternalLink(content: ContentExternalLink) {
+ a(href = content.href) { contentNodesToMarkup(content.children) }
+ }
+
+ protected open fun <T> FlowContent.summaryNodeGroup(
+ nodes: Iterable<T>,
+ header: String,
+ headerAsRow: Boolean = true,
+ row: TBODY.(T) -> Unit
+ ) {
+ if (nodes.none()) return
+ if (!headerAsRow) {
+ h2 { +header }
+ }
+ table {
+ tbody {
+ if (headerAsRow) {
+ developerHeading(header)
+ }
+ nodes.forEach { node ->
+ row(node)
+ }
+ }
+ }
+ }
+
+
+ protected open fun summary(node: DocumentationNode) = node.summary
+
+ protected open fun TBODY.classLikeRow(node: DocumentationNode) = tr {
+ td { a(href = uriProvider.linkTo(node, uri)) { +node.simpleName() } }
+ td { nodeSummary(node) }
+ }
+
+ protected fun FlowContent.modifiers(node: DocumentationNode) {
+ for (modifier in node.details(NodeKind.Modifier)) {
+ renderedSignature(modifier, SUMMARY)
+ }
+ }
+
+ protected fun FlowContent.shortFunctionParametersList(func: DocumentationNode) {
+ val params = func.details(NodeKind.Parameter)
+ .map { languageService.render(it, FULL) }
+ .run {
+ drop(1).fold(listOfNotNull(firstOrNull())) { acc, node ->
+ acc + ContentText(", ") + node
+ }
+ }
+ metaMarkup(listOf(ContentText("(")) + params + listOf(ContentText(")")))
+ }
+
+
+ protected open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr {
+ if (node.kind != NodeKind.Constructor) {
+ td {
+ modifiers(node)
+ renderedSignature(node.detail(NodeKind.Type), SUMMARY)
+ }
+ }
+ td {
+ div {
+ code {
+ val receiver = node.detailOrNull(NodeKind.Receiver)
+ if (receiver != null) {
+ renderedSignature(receiver.detail(NodeKind.Type), SUMMARY)
+ +"."
+ }
+ a(href = node) { +node.prettyName }
+ shortFunctionParametersList(node)
+ }
+ }
+
+ nodeSummary(node)
+ }
+ }
+
+ protected open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode, showSignature: Boolean = true) = tr {
+ if (showSignature) {
+ td {
+ modifiers(node)
+ renderedSignature(node.detail(NodeKind.Type), SUMMARY)
+ }
+ }
+ td {
+ div {
+ code {
+ a(href = node) { +node.name }
+ }
+ }
+
+ nodeSummary(node)
+ }
+ }
+
+ protected open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr {
+ td {
+ modifiers(node)
+ }
+ td {
+ div {
+ code {
+ a(href = node) { +node.name }
+ }
+ }
+
+ nodeSummary(node)
+ }
+ }
+
+ protected fun HtmlBlockTag.nodeSummary(node: DocumentationNode, uriNode: DocumentationNode) {
+ contentNodeToMarkup(summary(node), uriProvider.mainUriOrWarn(uriNode) ?: uri)
+ }
+
+ protected fun HtmlBlockTag.nodeSummary(node: DocumentationNode) {
+ nodeSummary(node, node)
+ }
+
+ protected open fun TBODY.inheritRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = tr {
+ td {
+ val (from, nodes) = entry
+ +"From class "
+ a(href = from.owner!!) { +from.qualifiedName() }
+ table {
+ tbody {
+ for (node in nodes) {
+ summaryRow(node)
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun TBODY.groupedRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ groupHeader: HtmlBlockTag.(DocumentationNode) -> Unit,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = tr {
+ td {
+ val (from, nodes) = entry
+ groupHeader(from)
+ table {
+ tbody {
+ for (node in nodes) {
+ summaryRow(node)
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun TBODY.extensionRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = groupedRow(entry, { from ->
+ +"From "
+ a(href = from) { +from.qualifiedName() }
+ }, summaryRow)
+
+
+ protected open fun TBODY.extensionByReceiverRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = groupedRow(entry, { from ->
+ +"For "
+ a(href = from) { +from.name }
+ }, summaryRow)
+
+ protected open fun FlowOrInteractiveOrPhrasingContent.a(href: DocumentationNode?, classes: String? = null, block: HtmlBlockInlineTag.() -> Unit) {
+ if (href == null) {
+ return a(href = "#", classes = classes, block = block)
+ }
+
+ val hrefText = try {
+ href.name.takeIf { href.kind == NodeKind.ExternalLink }
+ ?: href.links.firstOrNull { it.kind == NodeKind.ExternalLink }?.name
+ ?: "#".takeIf { href.kind == NodeKind.ExternalClass } // When external class unresolved
+ ?: uriProvider.linkTo(href, uri)
+ } catch (e: Exception) {
+ val owners = generateSequence(href) { it.owner }.toList().reversed()
+ logger.warn("Exception while resolving link to ${owners.joinToString(separator = " ")}\n"
+ + Throwables.getStackTraceAsString(e))
+ "#"
+ }
+
+ a(href = hrefText, classes = classes, block = block)
+ }
+
+ protected open fun FlowContent.renderedSignature(
+ node: DocumentationNode,
+ mode: LanguageService.RenderMode = SUMMARY
+ ) {
+ contentNodeToMarkup(languageService.render(node, mode), uri)
+ }
+
+ protected open fun generatePackage(page: Page.PackagePage) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +page.node.name }
+ nodeContent(page.node)
+ this@composePage.summaryNodeGroup(page.interfaces, "Interfaces", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) }
+ this@composePage.summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+ this@composePage.summaryNodeGroup(
+ page.constants,
+ "Top-level constants summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.functions,
+ "Top-level functions summary",
+ headerAsRow = false
+ ) {
+ functionLikeSummaryRow(it)
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.properties,
+ "Top-level properties summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.extensionFunctions.entries,
+ "Extension functions summary",
+ headerAsRow = false
+ ) {
+ extensionByReceiverRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ this@composePage.summaryNodeGroup(
+ page.extensionProperties.entries,
+ "Extension properties summary",
+ headerAsRow = false
+ ) {
+ extensionByReceiverRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ fullMemberDocs(page.constants, "Top-level constants")
+ fullMemberDocs(page.functions, "Top-level functions")
+ fullMemberDocs(page.properties, "Top-level properties")
+ fullMemberDocs(page.extensionFunctions.values.flatten(), "Extension functions")
+ fullMemberDocs(page.extensionProperties.values.flatten(), "Extension properties")
+ }
+ )
+
+ protected fun FlowContent.qualifiedTypeReference(node: DocumentationNode) {
+ if (node.kind in classLike) {
+ a(href = node) { +node.qualifiedName() }
+ return
+ }
+
+ val targetLink = node.links.firstOrNull()
+
+ if (targetLink?.kind == NodeKind.TypeParameter) {
+ +node.name
+ return
+ }
+
+ a(href = targetLink) {
+ +node.qualifiedNameFromType()
+ }
+ val typeParameters = node.details(NodeKind.Type)
+ if (typeParameters.isNotEmpty()) {
+ +"<"
+ typeParameters.forEach {
+ if (it != typeParameters.first()) {
+ +", "
+ }
+ qualifiedTypeReference(it)
+ }
+ +">"
+ }
+ }
+
+ protected open fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
+ table {
+ superclasses.forEach {
+ tr {
+ if (it != superclasses.first()) {
+ td {
+ +"   ↳"
+ }
+ }
+ td {
+ qualifiedTypeReference(it)
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
+ if (inheritors.isEmpty()) return
+ div {
+ table {
+ thead {
+ tr {
+ td {
+ if (direct)
+ +"Known Direct Subclasses"
+ else
+ +"Known Indirect Subclasses"
+ }
+ }
+ }
+ tbody {
+ inheritors.forEach { inheritor ->
+ tr {
+ td {
+ a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+ }
+ td {
+ nodeSummary(inheritor)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
+ this@classLikeSummaries.summaryNodeGroup(
+ nestedClasses,
+ "Nested classes",
+ headerAsRow = true
+ ) {
+ nestedClassSummaryRow(it)
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(enumValues, "Enum values") {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(constants, "Constants") { propertyLikeSummaryRow(it) }
+
+ constructors.forEach { (visibility, group) ->
+ this@classLikeSummaries.summaryNodeGroup(
+ group,
+ "${visibility.capitalize()} constructors",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ functions.forEach { (visibility, group) ->
+ this@classLikeSummaries.summaryNodeGroup(
+ group,
+ "${visibility.capitalize()} functions",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ companionFunctions,
+ "Companion functions",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedFunctionsByReceiver.entries,
+ "Inherited functions",
+ headerAsRow = true
+ ) {
+ inheritRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ extensionFunctions.entries,
+ "Extension functions",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedExtensionFunctions.entries,
+ "Inherited extension functions",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+
+ this@classLikeSummaries.summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
+ this@classLikeSummaries.summaryNodeGroup(
+ companionProperties,
+ "Companion properties",
+ headerAsRow = true
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedPropertiesByReceiver.entries,
+ "Inherited properties",
+ headerAsRow = true
+ ) {
+ inheritRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ extensionProperties.entries,
+ "Extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ this@classLikeSummaries.summaryNodeGroup(
+ inheritedExtensionProperties.entries,
+ "Inherited extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ }
+
+ protected open fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
+ fullMemberDocs(enumValues, "Enum values")
+ fullMemberDocs(constants, "Constants")
+
+ constructors.forEach { (visibility, group) ->
+ fullMemberDocs(group, "${visibility.capitalize()} constructors")
+ }
+
+ functions.forEach { (visibility, group) ->
+ fullMemberDocs(group, "${visibility.capitalize()} methods")
+ }
+
+ fullMemberDocs(properties, "Properties")
+ if (!hasMeaningfulCompanion) {
+ fullMemberDocs(companionFunctions, "Companion functions")
+ fullMemberDocs(companionProperties, "Companion properties")
+ }
+ }
+
+ protected open fun generateClassLike(page: Page.ClassPage) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ val node = page.node
+ with(page) {
+
+ div {
+ id = "api-info-block"
+ apiAndDeprecatedVersions(node)
+ }
+
+ if (node.artifactId.name.isNotEmpty()) {
+ div(classes = "api-level") { br { +"belongs to Maven artifact ${node.artifactId}" } }
+ }
+ h1 { +node.name }
+ pre { renderedSignature(node, FULL) }
+ classHierarchy(page.superclasses)
+
+ subclasses(page.directInheritors, true)
+ subclasses(page.indirectInheritors, false)
+
+ deprecatedClassCallOut(node)
+ nodeContent(node)
+
+ h2 { +"Summary" }
+ classLikeSummaries(page)
+ classLikeFullMemberDocs(page)
+ }
+ }
+ )
+
+ protected open fun FlowContent.classIndexSummary(node: DocumentationNode) {
+ nodeContent(node)
+ }
+
+ protected open fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Class Index" }
+
+
+ ul {
+ page.classesByFirstLetter.forEach { (letter) ->
+ li { a(href = "#letter_$letter") { +letter } }
+ }
+ }
+
+ page.classesByFirstLetter.forEach { (letter, classes) ->
+ h2 {
+ id = "letter_$letter"
+ +letter
+ }
+ table {
+ tbody {
+ for (node in classes) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
+ }
+ td {
+ if (!deprecatedIndexSummary(node)) {
+ classIndexSummary(node)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ protected open fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Package Index" }
+ table {
+ tbody {
+ for (node in page.packages) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ }
+ td {
+ nodeContent(node)
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ fun generatePage(page: Page) {
+ when (page) {
+ is Page.PackageIndex -> generatePackageIndex(page)
+ is Page.ClassIndex -> generateClassIndex(page)
+ is Page.ClassPage -> generateClassLike(page)
+ is Page.PackagePage -> generatePackage(page)
+ }
+ }
+
+ protected fun FlowContent.fullMemberDocs(
+ nodes: List<DocumentationNode>,
+ header: String
+ ) {
+ if (nodes.none()) return
+ h2 {
+ +header
+ }
+ for (node in nodes) {
+ fullMemberDocs(node)
+ }
+ }
+
+ protected open fun FlowContent.seeAlsoSection(links: List<List<ContentNode>>) {
+ p { b { +"See Also" } }
+ ul {
+ links.forEach { linkParts ->
+ li { code { metaMarkup(linkParts) } }
+ }
+ }
+ }
+
+ protected open fun FlowContent.regularSection(name: String, entries: List<ContentSection>) {
+ table {
+ thead {
+ tr {
+ th {
+ colSpan = "2"
+ +name
+ }
+ }
+ }
+ tbody {
+ entries.forEach {
+ tr {
+ if (it.subjectName != null) {
+ td { +it.subjectName }
+ }
+ td {
+ metaMarkup(it.children)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun FlowContent.deprecationWarningToMarkup(
+ node: DocumentationNode,
+ prefix: Boolean = false,
+ emphasis: Boolean = true
+ ): Boolean {
+ val deprecated = formatDeprecationOrNull(node, prefix, emphasis)
+ deprecated?.let {
+ contentNodeToMarkup(deprecated, uriProvider.mainUri(node))
+ return true
+ }
+ return false
+ }
+
+ protected open fun FlowContent.deprecatedClassCallOut(node: DocumentationNode) {
+ val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty()
+ if (deprecatedLevelExists) {
+ hr { }
+ aside(classes = "caution") {
+ strong { +node.deprecatedLevelMessage() }
+ deprecationWarningToMarkup(node, emphasis = false)
+ }
+ }
+ }
+
+ protected open fun FlowContent.deprecatedIndexSummary(node: DocumentationNode): Boolean {
+ val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty()
+ if (deprecatedLevelExists) {
+ val em = ContentEmphasis()
+ em.append(ContentText(node.deprecatedLevelMessage()))
+ em.append(ContentText(" "))
+ for (child in node.deprecation?.content?.children ?: emptyList<ContentNode>()) {
+ em.append(child)
+ }
+ contentNodeToMarkup(em, uriProvider.mainUri(node))
+ return true
+ }
+ return false
+ }
+
+ protected open fun FlowContent.apiAndDeprecatedVersions(node: DocumentationNode) {
+ val apiLevelExists = node.apiLevel.name.isNotEmpty()
+ val sdkExtSinceExists = node.sdkExtSince.name.isNotEmpty()
+ val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty()
+ if (apiLevelExists || sdkExtSinceExists || deprecatedLevelExists) {
+ div(classes = "api-level") {
+ if (apiLevelExists) {
+ +"Added in "
+ a(href = "https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels") {
+ +"API level ${node.apiLevel.name}"
+ }
+ }
+ if (sdkExtSinceExists) {
+ if (apiLevelExists) {
+ br
+ +"Also in "
+ } else {
+ +"Added in "
+ }
+ a(href = "https://developer.android.com/sdkExtensions") {
+ +"${node.sdkExtSince.name}"
+ }
+ }
+ if (deprecatedLevelExists) {
+ if (apiLevelExists || sdkExtSinceExists) {
+ br
+ }
+ +"Deprecated in "
+ a(href = "https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels") {
+ +"API level ${node.deprecatedLevel.name}"
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun formatDeprecationOrNull(
+ node: DocumentationNode,
+ prefix: Boolean = false,
+ emphasis: Boolean = true): ContentNode? {
+ val deprecated = node.deprecation
+ deprecated?.let {
+ return ContentParagraph("caution").apply {
+ if (prefix) {
+ append(ContentStrong().apply { text(
+ if (deprecated.content.children.size == 0) "Deprecated."
+ else "Deprecated: "
+ ) })
+ }
+ val em = if (emphasis) ContentEmphasis() else ContentBlock()
+ for (child in deprecated.content.children) {
+ em.append(child)
+ }
+ append(em)
+ }
+ }
+ return null
+ }
+
+ protected open fun FlowContent.section(name: String, sectionParts: List<ContentSection>) {
+ when (name) {
+ ContentTags.SeeAlso -> seeAlsoSection(sectionParts.map { it.children.flatMap { (it as? ContentParagraph)?.children ?: listOf(it) } })
+ else -> regularSection(name, sectionParts)
+ }
+ }
+
+ protected open fun FlowContent.sections(content: Content) {
+ val sectionsByTag = content.sections.groupByTo(mutableMapOf()) { it.tag }
+
+ val seeAlso = sectionsByTag.remove(ContentTags.SeeAlso)
+
+ for ((name, entries) in sectionsByTag) {
+ section(name, entries)
+ }
+
+ seeAlso?.let { section(ContentTags.SeeAlso, it) }
+ }
+
+ protected open fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) {
+ div {
+ id = node.signatureForAnchor(logger)
+ h3 { +node.name }
+ pre { renderedSignature(node, FULL) }
+ deprecationWarningToMarkup(node, prefix = true)
+ nodeContent(node)
+ node.constantValue()?.let { value ->
+ pre {
+ +"Value: "
+ code { +value }
+ }
+ }
+
+ sections(node.content)
+ }
+ }
+
+ protected open fun FlowContent.fullMemberDocs(node: DocumentationNode) {
+ fullMemberDocs(node, node)
+ }
+
+ sealed class Page {
+ class PackageIndex(packages: List<DocumentationNode>) : Page() {
+ init {
+ assert(packages.all { it.kind == NodeKind.Package })
+ }
+
+ val packages = packages.sortedBy { it.name }
+ }
+
+ class ClassIndex(allTypesNode: DocumentationNode) : Page() {
+ init {
+ assert(allTypesNode.kind == NodeKind.AllTypes)
+ }
+
+ // Wide-collect all nested classes
+ val classes: List<DocumentationNode> =
+ generateSequence(listOf(allTypesNode)) { nodes ->
+ nodes
+ .flatMap { it.members.filter { it.kind in NodeKind.classLike } }
+ .takeUnless { it.isEmpty() }
+ }.drop(1)
+ .flatten()
+ .sortedBy { it.classNodeNameWithOuterClass().toLowerCase() }
+ .toList()
+
+
+ // Group all classes by it's first letter and sort
+ val classesByFirstLetter =
+ classes
+ .groupBy {
+ it.classNodeNameWithOuterClass().first().toString()
+ }
+ .entries
+ .sortedBy { (letter) ->
+ val x = letter.toLowerCase()
+ x
+ }
+ }
+
+ class ClassPage(val node: DocumentationNode) : Page() {
+
+ init {
+ assert(node.kind in NodeKind.classLike)
+ }
+
+ val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed()
+
+ val enumValues = node.members(NodeKind.EnumItem).sortedBy { it.name }
+
+ val directInheritors: List<DocumentationNode>
+ val indirectInheritors: List<DocumentationNode>
+
+ init {
+ // Wide-collect all inheritors
+ val inheritors = generateSequence(node.inheritors) { inheritors ->
+ inheritors
+ .flatMap { it.inheritors }
+ .takeUnless { it.isEmpty() }
+ }
+ directInheritors = inheritors.first().sortedBy { it.classNodeNameWithOuterClass() }
+ indirectInheritors = inheritors.drop(1).flatten().toList().sortedBy { it.classNodeNameWithOuterClass() }
+ }
+
+ val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" }
+ val hasMeaningfulCompanion = !isCompanion && node.companion != null
+
+ private fun DocumentationNode.thisTypeExtension() =
+ detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node }
+
+ val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction
+ val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty
+
+ private fun DocumentationNode.isFunction() = kind == functionKind
+ private fun DocumentationNode.isProperty() = kind == propertyKind
+
+
+ val nestedClasses = node.members.filter { it.kind in NodeKind.classLike } - enumValues
+
+ val attributes = node.attributes
+
+ val inheritedAttributes =
+ node.superclassTypeSequence
+ .toList()
+ .sortedBy { it.name }
+ .flatMap { it.typeDeclarationClass?.attributes.orEmpty() }
+ .distinctBy { it.attributeRef!!.name }
+ .groupBy { it.owner!! }
+
+ val allInheritedMembers = node.allInheritedMembers
+ val constants = node.members.filter { it.constantValue() != null }
+ val inheritedConstants = allInheritedMembers.filter { it.constantValue() != null }.groupBy { it.owner!! }
+
+
+ fun compareVisibilities(a: String, b: String): Int {
+ return visibilityNames.indexOf(a) - visibilityNames.indexOf(b)
+ }
+
+ fun Collection<DocumentationNode>.groupByVisibility() =
+ groupBy { it.visibility() }.toSortedMap(Comparator { a, b -> compareVisibilities(a, b) })
+
+
+ val constructors = node.members(NodeKind.Constructor).groupByVisibility()
+ val functions = node.members(functionKind).groupByVisibility()
+ val fields = (node.members(NodeKind.Field) - constants).groupByVisibility()
+ val properties = node.members(propertyKind) - constants
+ val inheritedFunctionsByReceiver = allInheritedMembers.filter { it.kind == functionKind }.groupBy { it.owner!! }
+ val inheritedPropertiesByReceiver =
+ allInheritedMembers.filter {
+ it.kind == propertyKind && it.constantValue() == null
+ }.groupBy { it.owner!! }
+
+ val inheritedFieldsByReceiver =
+ allInheritedMembers.filter {
+ it.kind == NodeKind.Field && it.constantValue() != null
+ }.groupBy { it.owner!! }
+
+ val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions
+
+ val extensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+ val extensionProperties: Map<DocumentationNode, List<DocumentationNode>>
+ val inheritedExtensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+ val inheritedExtensionProperties: Map<DocumentationNode, List<DocumentationNode>>
+
+ init {
+ val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() }
+ extensionFunctions = extensions.filter { it.isFunction() }.sortedBy { it.name }.groupBy { it.owner!! }
+ extensionProperties = extensions.filter { it.isProperty() }.sortedBy { it.name }.groupBy { it.owner!! }
+ inheritedExtensionFunctions =
+ inheritedExtensions.filter { it.isFunction() }.sortedBy { it.name }.groupBy { it.owner!! }
+ inheritedExtensionProperties =
+ inheritedExtensions.filter { it.isProperty() }.sortedBy { it.name }.groupBy { it.owner!! }
+ }
+
+ val companionFunctions = node.members(NodeKind.CompanionObjectFunction).takeUnless { isCompanion }.orEmpty()
+ val companionProperties =
+ node.members(NodeKind.CompanionObjectProperty).takeUnless { isCompanion }.orEmpty() - constants
+
+
+ }
+
+ class PackagePage(val node: DocumentationNode) : Page() {
+
+ init {
+ assert(node.kind == NodeKind.Package)
+ }
+
+ val interfaces = node.members(NodeKind.Interface) +
+ node.members(NodeKind.Class).flatMap { it.members(NodeKind.Interface) }
+ val classes = node.members(NodeKind.Class)
+ val exceptions = node.members(NodeKind.Exception)
+ val typeAliases = node.members(NodeKind.TypeAlias)
+ val annotations = node.members(NodeKind.AnnotationClass)
+ val enums = node.members(NodeKind.Enum)
+
+ val constants = node.members(NodeKind.Property).filter { it.constantValue() != null }
+
+
+ private fun DocumentationNode.getClassExtensionReceiver() =
+ detailOrNull(NodeKind.Receiver)?.detailOrNull(NodeKind.Type)?.takeIf {
+ it.links.any { it.kind == NodeKind.ExternalLink || it.kind in NodeKind.classLike }
+ }
+
+ private fun List<DocumentationNode>.groupedExtensions() =
+ filter { it.getClassExtensionReceiver() != null }
+ .groupBy {
+ val receiverType = it.getClassExtensionReceiver()!!
+ receiverType.links.filter { it.kind != NodeKind.ExternalLink}.firstOrNull() ?:
+ receiverType.links(NodeKind.ExternalLink).first()
+ }
+
+ private fun List<DocumentationNode>.externalExtensions(kind: NodeKind) =
+ associateBy({ it }, { it.members(kind) })
+ .filterNot { (_, values) -> values.isEmpty() }
+
+ val extensionFunctions =
+ node.members(NodeKind.ExternalClass).externalExtensions(NodeKind.Function) +
+ node.members(NodeKind.Function).groupedExtensions()
+
+ val extensionProperties =
+ node.members(NodeKind.ExternalClass).externalExtensions(NodeKind.Property) +
+ node.members(NodeKind.Property).groupedExtensions()
+
+ val functions = node.members(NodeKind.Function) - extensionFunctions.values.flatten()
+ val properties = node.members(NodeKind.Property) - constants - extensionProperties.values.flatten()
+
+ }
+ }
+}
+
+class JavaLayoutHtmlFormatOutputBuilderFactoryImpl @Inject constructor(
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger
+) : JavaLayoutHtmlFormatOutputBuilderFactory {
+ override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
+ return createOutputBuilder(output, uriProvider.mainUri(node))
+ }
+
+ override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
+ return JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
+ }
+}
+
+fun DocumentationNode.constantValue(): String? =
+ detailOrNull(NodeKind.Value)?.name.takeIf {
+ kind == NodeKind.Field || kind == NodeKind.Property || kind == NodeKind.CompanionObjectProperty
+ }
+
+
+private val visibilityNames = setOf("public", "protected", "internal", "package-local", "private")
+
+fun DocumentationNode.visibility(): String =
+ details(NodeKind.Modifier).firstOrNull { it.name in visibilityNames }?.name ?: ""
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
new file mode 100644
index 000000000..9928a8e9e
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
@@ -0,0 +1,165 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatOutputBuilder.Page
+import org.jetbrains.dokka.NodeKind.Companion.classLike
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
+import java.io.BufferedWriter
+import java.io.File
+import java.net.URI
+
+class JavaLayoutHtmlFormatGenerator @Inject constructor(
+ @Named("outputDir") val root: File,
+ val packageListService: PackageListService,
+ val outputBuilderFactoryService: JavaLayoutHtmlFormatOutputBuilderFactory,
+ private val options: DocumentationOptions,
+ val logger: DokkaLogger,
+ @Named("outlineRoot") val outlineRoot: String
+) : Generator, JavaLayoutHtmlUriProvider {
+
+ @set:Inject(optional = true)
+ var outlineFactoryService: JavaLayoutHtmlFormatOutlineFactoryService? = null
+
+ fun createOutputBuilderForNode(node: DocumentationNode, output: Appendable) = outputBuilderFactoryService.createOutputBuilder(output, node)
+
+ fun DocumentationNode.getOwnerOrReport() = owner ?: run {
+ error("Owner not found for $this")
+ }
+
+ override fun tryGetContainerUri(node: DocumentationNode): URI? {
+ return when (node.kind) {
+ NodeKind.Module -> URI("/").resolve(node.name + "/")
+ NodeKind.Package -> tryGetContainerUri(node.getOwnerOrReport())?.resolve(node.name.replace('.', '/') + '/')
+ NodeKind.GroupNode -> tryGetContainerUri(node.getOwnerOrReport())
+ in NodeKind.classLike -> tryGetContainerUri(node.getOwnerOrReport())?.resolve("${node.classNodeNameWithOuterClass()}.html")
+ else -> null
+ }
+ }
+
+ override fun tryGetMainUri(node: DocumentationNode): URI? {
+ return when (node.kind) {
+ NodeKind.Package -> tryGetContainerUri(node)?.resolve("package-summary.html")
+ in NodeKind.classLike -> tryGetContainerUri(node)?.resolve("#")
+ in NodeKind.memberLike -> {
+ val owner = if (node.owner?.kind != NodeKind.ExternalClass) node.owner else node.owner?.owner
+ if (owner!!.kind in classLike &&
+ (node.kind == NodeKind.CompanionObjectProperty || node.kind == NodeKind.CompanionObjectFunction) &&
+ owner.companion != null
+ ) {
+ val signature = node.detail(NodeKind.Signature)
+ val originalFunction = owner.companion!!.members.first { it.detailOrNull(NodeKind.Signature)?.name == signature.name }
+ tryGetMainUri(owner.companion!!)?.resolveInPage(originalFunction)
+ } else {
+ tryGetMainUri(owner)?.resolveInPage(node)
+ }
+ }
+ NodeKind.TypeParameter, NodeKind.Parameter -> node.path.asReversed().drop(1).firstNotNullResult(this::tryGetMainUri)?.resolveInPage(node)
+ NodeKind.AllTypes -> outlineRootUri(node).resolve ("classes.html")
+ else -> null
+ }
+ }
+
+ override fun tryGetOutlineRootUri(node: DocumentationNode): URI? {
+ return when(node.kind) {
+ NodeKind.AllTypes -> tryGetContainerUri(node.getOwnerOrReport())
+ else -> tryGetContainerUri(node)
+ }?.resolve(outlineRoot)
+ }
+
+ fun URI.resolveInPage(node: DocumentationNode): URI = resolve("#${node.signatureForAnchor(logger).anchorEncoded()}")
+
+ fun buildClass(node: DocumentationNode, parentDir: File) {
+ val fileForClass = parentDir.resolve(node.classNodeNameWithOuterClass() + ".html")
+ fileForClass.bufferedWriter().use {
+ createOutputBuilderForNode(node, it).generatePage(Page.ClassPage(node))
+ }
+ for (memberClass in node.members.filter { it.kind in NodeKind.classLike }) {
+ buildClass(memberClass, parentDir)
+ }
+ }
+
+ fun buildPackage(node: DocumentationNode, parentDir: File) {
+ assert(node.kind == NodeKind.Package)
+ var members = node.members
+ val directoryForPackage = parentDir.resolve(node.name.replace('.', File.separatorChar))
+ directoryForPackage.mkdirsOrFail()
+
+ directoryForPackage.resolve("package-summary.html").bufferedWriter().use {
+ createOutputBuilderForNode(node, it).generatePage(Page.PackagePage(node))
+ }
+
+ members.filter { it.kind == NodeKind.GroupNode }.forEach {
+ members += it.members
+ }
+ members.filter { it.kind in NodeKind.classLike }.forEach {
+ buildClass(it, directoryForPackage)
+ }
+ }
+
+ fun buildClassIndex(node: DocumentationNode, parentDir: File) {
+ val file = parentDir.resolve("classes.html")
+ file.bufferedWriter().use {
+ createOutputBuilderForNode(node, it).generatePage(Page.ClassIndex(node))
+ }
+ }
+
+ fun buildPackageIndex(module: DocumentationNode, nodes: List<DocumentationNode>, parentDir: File) {
+ val file = parentDir.resolve("packages.html")
+ file.bufferedWriter().use {
+ val uri = outlineRootUri(module).resolve("packages.html")
+ outputBuilderFactoryService.createOutputBuilder(it, uri)
+ .generatePage(Page.PackageIndex(nodes))
+ }
+ }
+
+ override fun buildPages(nodes: Iterable<DocumentationNode>) {
+ val module = nodes.single()
+
+ val moduleRoot = root.resolve(module.name)
+ val packages = module.members.filter { it.kind == NodeKind.Package }
+ packages.forEach { buildPackage(it, moduleRoot) }
+ val outlineRootFile = moduleRoot.resolve(outlineRoot)
+ if (options.generateClassIndexPage) {
+ buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, outlineRootFile)
+ }
+
+ if (options.generatePackageIndexPage) {
+ buildPackageIndex(module, packages, outlineRootFile)
+ }
+ }
+
+ override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
+ val uriToWriter = mutableMapOf<URI, BufferedWriter>()
+
+ fun provideOutput(uri: URI): BufferedWriter {
+ val normalized = uri.normalize()
+ uriToWriter[normalized]?.let { return it }
+ val file = root.resolve(normalized.path.removePrefix("/"))
+ file.parentFile.mkdirsOrFail()
+ val writer = file.bufferedWriter()
+ uriToWriter[normalized] = writer
+ return writer
+ }
+
+ outlineFactoryService?.generateOutlines(::provideOutput, nodes)
+
+ uriToWriter.values.forEach { it.close() }
+ }
+
+ override fun buildSupportFiles() {}
+
+ override fun buildPackageList(nodes: Iterable<DocumentationNode>) {
+ nodes.filter { it.kind == NodeKind.Module }.forEach { module ->
+ val moduleRoot = root.resolve(module.name)
+ val packageListFile = moduleRoot.resolve("package-list")
+ packageListFile.writeText(packageListService.formatPackageList(module as DocumentationModule))
+ }
+ }
+}
+
+interface JavaLayoutHtmlFormatOutputBuilderFactory {
+ fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder
+ fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder
+}
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 000000000..ce05fe897
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
@@ -0,0 +1,154 @@
+package org.jetbrains.dokka.Formats
+
+import com.intellij.psi.PsiMember
+import com.intellij.psi.PsiParameter
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX
+import org.jetbrains.kotlin.asJava.toLightElements
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.psi.KtDeclaration
+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>>,
+ private val resolutionFacade: DokkaResolutionFacade) : InboundExternalLinkResolutionService {
+
+ constructor(asJava: Boolean, resolutionFacade: DokkaResolutionFacade) :
+ this(mapOf("mode" to listOf(if (asJava) "java" else "kotlin")), resolutionFacade)
+
+
+ private val isJavaMode = paramMap["mode"]!!.single() == "java"
+
+ 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 getJavaPagePath(symbol: DeclarationDescriptor): String? {
+
+ val sourcePsi = symbol.sourcePsi() ?: return null
+ val source = (if (sourcePsi is KtDeclaration) {
+ sourcePsi.toLightElements().firstOrNull()
+ } else {
+ sourcePsi
+ }) as? PsiMember ?: return null
+ val desc = source.getJavaMemberDescriptor(resolutionFacade) ?: return null
+ return getPagePath(desc)
+ }
+
+ 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 JavaMethodDescriptor -> buildString {
+ append(name.asString())
+ valueParameters.joinTo(this, prefix = "(", postfix = ")") {
+ val param = it.sourcePsi() as PsiParameter
+ param.type.canonicalText
+ }
+ }
+ is JavaPropertyDescriptor -> buildString {
+ 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()?.anchorEncoded()
+
+ override fun getPath(symbol: DeclarationDescriptor) = if (isJavaMode) getJavaPagePath(symbol) else getPagePath(symbol)
+}
diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt
new file mode 100644
index 000000000..a948dfa93
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt
@@ -0,0 +1,44 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+
+open class JekyllOutputBuilder(to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>)
+ : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
+ override fun appendNodes(nodes: Iterable<DocumentationNode>) {
+ to.appendln("---")
+ appendFrontMatter(nodes, to)
+ to.appendln("---")
+ to.appendln("")
+ super.appendNodes(nodes)
+ }
+
+ protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) {
+ to.appendln("title: ${getPageTitle(nodes)}")
+ }
+}
+
+
+open class JekyllFormatService(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ linkExtension: String,
+ impliedPlatforms: List<String>
+) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) {
+
+ @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>
+ ) : this(generator, signatureGenerator, "html", impliedPlatforms)
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
+ JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+
+}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
new file mode 100644
index 000000000..a98002d49
--- /dev/null
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -0,0 +1,224 @@
+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
new file mode 100644
index 000000000..6ced75b55
--- /dev/null
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -0,0 +1,186 @@
+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
+
+
+object EmptyHtmlTemplateService : HtmlTemplateService {
+ override fun appendFooter(to: StringBuilder) {}
+
+ override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {}
+}
+
+
+open class KotlinWebsiteHtmlOutputBuilder(
+ to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>,
+ templateService: HtmlTemplateService
+) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) {
+ private var needHardLineBreaks = false
+ private var insideDiv = 0
+
+ override fun appendLine() {}
+
+ override fun appendBreadcrumbs(path: Iterable<FormatLink>) {
+ if (path.count() > 1) {
+ to.append("<div class='api-docs-breadcrumbs'>")
+ super.appendBreadcrumbs(path)
+ to.append("</div>")
+ }
+ }
+
+ override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body)
+
+ protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) {
+ to.append("<div class=\"$cssClass\"$otherAttributes")
+ to.append(">")
+ insideDiv++
+ block()
+ insideDiv--
+ to.append("</div>\n")
+ }
+
+ override fun appendAsSignature(node: ContentNode, block: () -> Unit) {
+ val contentLength = node.textLength
+ if (contentLength == 0) return
+ div(to, "signature") {
+ needHardLineBreaks = contentLength >= 62
+ try {
+ block()
+ } finally {
+ needHardLineBreaks = false
+ }
+ }
+ }
+
+ override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ div(to, "overload-group", calculateDataAttributes(platforms)) {
+ block()
+ }
+ }
+
+ override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
+
+ override fun appendTable(vararg columns: String, body: () -> Unit) {
+ to.appendln("<table class=\"api-docs-table\">")
+ body()
+ to.appendln("</table>")
+ }
+
+ override fun appendTableBody(body: () -> Unit) {
+ to.appendln("<tbody>")
+ body()
+ to.appendln("</tbody>")
+ }
+
+ override fun appendTableRow(body: () -> Unit) {
+ to.appendln("<tr>")
+ body()
+ to.appendln("</tr>")
+ }
+
+ override fun appendTableCell(body: () -> Unit) {
+ to.appendln("<td>")
+ body()
+ to.appendln("\n</td>")
+ }
+
+ override fun appendSymbol(text: String) {
+ to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
+ }
+
+ override fun appendKeyword(text: String) {
+ to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
+ }
+
+ override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+ val id = signature?.let { " id=\"$it\"" }.orEmpty()
+ to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>")
+ }
+
+ override fun appendSoftLineBreak() {
+ if (needHardLineBreaks)
+ to.append("<br/>")
+ }
+
+ override fun appendIndentedSoftLineBreak() {
+ if (needHardLineBreaks) {
+ to.append("<br/>&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>) {}
+
+ override fun appendBreadcrumbSeparator() {
+ to.append(" / ")
+ }
+
+ override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
+ div(to, "sample") {
+ appendBlockCode(language) {
+ imports()
+ wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") {
+ wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
+ }
+ }
+ }
+ }
+
+ override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body)
+
+
+ override fun appendSectionWithTag(section: ContentSection) {
+ appendParagraph {
+ appendStrong { appendText(section.tag) }
+ appendText(" ")
+ appendContent(section)
+ }
+ }
+}
+
+class KotlinWebsiteHtmlFormatService @Inject constructor(
+ generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+ templateService: HtmlTemplateService
+) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) {
+
+ override fun enumerateSupportFiles(callback: (String, String) -> Unit) {}
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location) =
+ KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
+}
+
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
new file mode 100644
index 000000000..4265394f2
--- /dev/null
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -0,0 +1,239 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import java.util.*
+
+enum class ListKind {
+ Ordered,
+ Unordered
+}
+
+private class ListState(val kind: ListKind, var size: Int = 1) {
+ fun getTagAndIncrement() = when (kind) {
+ ListKind.Ordered -> "${size++}. "
+ else -> "* "
+ }
+}
+
+private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator()
+
+open class MarkdownOutputBuilder(to: StringBuilder,
+ location: Location,
+ generator: NodeLocationAwareGenerator,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>)
+ : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+{
+ private val listStack = ArrayDeque<ListState>()
+ protected var inTableCell = false
+ protected var inCodeBlock = false
+ private var lastTableCellStart = -1
+ private var maxBackticksInCodeBlock = 0
+
+ private fun appendNewline() {
+ while (to.endsWith(' ')) {
+ to.setLength(to.length - 1)
+ }
+ to.appendln()
+ }
+
+ private fun ensureNewline() {
+ if (inTableCell && listStack.isEmpty()) {
+ if (to.length != lastTableCellStart && !to.endsWith("<br>")) {
+ to.append("<br>")
+ }
+ }
+ else {
+ if (!endsWithNewline()) {
+ appendNewline()
+ }
+ }
+ }
+
+ private fun endsWithNewline(): Boolean {
+ var index = to.length - 1
+ while (index > 0) {
+ val c = to[index]
+ if (c != ' ') {
+ return c == '\n'
+ }
+ index--
+ }
+ return false
+ }
+
+ override fun ensureParagraph() {
+ if (!to.endsWith(TWO_LINE_BREAKS)) {
+ if (!to.endsWith('\n')) {
+ appendNewline()
+ }
+ appendNewline()
+ }
+ }
+ override fun appendBreadcrumbSeparator() {
+ to.append(" / ")
+ }
+
+ private val backTickFindingRegex = """(`+)""".toRegex()
+
+ override fun appendText(text: String) {
+ if (inCodeBlock) {
+ to.append(text)
+ val backTicks = backTickFindingRegex.findAll(text)
+ val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0
+ maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun)
+ }
+ else {
+ if (text == "\n" && inTableCell) {
+ to.append(" ")
+ } else {
+ to.append(text.htmlEscape())
+ }
+ }
+ }
+
+ override fun appendCode(body: () -> Unit) {
+ inCodeBlock = true
+ val codeBlockStart = to.length
+ maxBackticksInCodeBlock = 0
+
+ wrapIfNotEmpty("`", "`", body, checkEndsWith = true)
+
+ if (maxBackticksInCodeBlock > 0) {
+ val extraBackticks = "`".repeat(maxBackticksInCodeBlock)
+ to.insert(codeBlockStart, extraBackticks)
+ to.append(extraBackticks)
+ }
+
+ inCodeBlock = false
+ }
+
+ override fun appendUnorderedList(body: () -> Unit) {
+ listStack.push(ListState(ListKind.Unordered))
+ body()
+ listStack.pop()
+ ensureNewline()
+ }
+
+ override fun appendOrderedList(body: () -> Unit) {
+ listStack.push(ListState(ListKind.Ordered))
+ body()
+ listStack.pop()
+ ensureNewline()
+ }
+
+ override fun appendListItem(body: () -> Unit) {
+ ensureNewline()
+ to.append(listStack.peek()?.getTagAndIncrement())
+ body()
+ ensureNewline()
+ }
+
+ override fun appendStrong(body: () -> Unit) = wrap("**", "**", body)
+ override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body)
+ override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body)
+
+ override fun appendLink(href: String, body: () -> Unit) {
+ if (inCodeBlock) {
+ wrap("`[`", "`]($href)`", body)
+ }
+ else {
+ wrap("[", "]($href)", body)
+ }
+ }
+
+ override fun appendLine() {
+ if (inTableCell) {
+ to.append("<br>")
+ }
+ else {
+ appendNewline()
+ }
+ }
+
+ override fun appendAnchor(anchor: String) {
+ // no anchors in Markdown
+ }
+
+ override fun appendParagraph(body: () -> Unit) {
+ if (inTableCell) {
+ ensureNewline()
+ body()
+ } else if (listStack.isNotEmpty()) {
+ body()
+ ensureNewline()
+ } else {
+ ensureParagraph()
+ body()
+ ensureParagraph()
+ }
+ }
+
+ override fun appendHeader(level: Int, body: () -> Unit) {
+ ensureParagraph()
+ to.append("${"#".repeat(level)} ")
+ body()
+ ensureParagraph()
+ }
+
+ override fun appendBlockCode(language: String, body: () -> Unit) {
+ inCodeBlock = true
+ ensureParagraph()
+ to.appendln(if (language.isEmpty()) "```" else "``` $language")
+ body()
+ ensureNewline()
+ to.appendln("```")
+ appendLine()
+ inCodeBlock = false
+ }
+
+ override fun appendTable(vararg columns: String, body: () -> Unit) {
+ ensureParagraph()
+ body()
+ ensureParagraph()
+ }
+
+ override fun appendTableBody(body: () -> Unit) {
+ body()
+ }
+
+ override fun appendTableRow(body: () -> Unit) {
+ to.append("|")
+ body()
+ appendNewline()
+ }
+
+ override fun appendTableCell(body: () -> Unit) {
+ to.append(" ")
+ inTableCell = true
+ lastTableCellStart = to.length
+ body()
+ inTableCell = false
+ to.append(" |")
+ }
+
+ override fun appendNonBreakingSpace() {
+ if (inCodeBlock) {
+ to.append(" ")
+ }
+ else {
+ to.append("&nbsp;")
+ }
+ }
+}
+
+open class MarkdownFormatService(generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ linkExtension: String,
+ val impliedPlatforms: List<String>)
+: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) {
+ @Inject constructor(generator: NodeLocationAwareGenerator,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms)
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
+ MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+}
diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt
new file mode 100644
index 000000000..958e93aff
--- /dev/null
+++ b/core/src/main/kotlin/Formats/OutlineService.kt
@@ -0,0 +1,29 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+/**
+ * Service for building the outline of the package contents.
+ */
+interface OutlineFormatService {
+ fun getOutlineFileName(location: Location): File
+
+ fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder)
+ fun appendOutlineLevel(to: StringBuilder, body: () -> Unit)
+
+ /** Appends formatted outline to [StringBuilder](to) using specified [location] */
+ fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
+ for (node in nodes) {
+ appendOutlineHeader(location, node, to)
+ if (node.members.any()) {
+ val sortedMembers = node.members.sortedBy { it.name.toLowerCase() }
+ appendOutlineLevel(to) {
+ appendOutline(location, to, sortedMembers)
+ }
+ }
+ }
+ }
+
+ fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String =
+ StringBuilder().apply { appendOutline(location, this, nodes) }.toString()
+}
diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt
new file mode 100644
index 000000000..7b68098e8
--- /dev/null
+++ b/core/src/main/kotlin/Formats/PackageListService.kt
@@ -0,0 +1,63 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+
+
+interface PackageListService {
+ fun formatPackageList(module: DocumentationModule): String
+}
+
+class DefaultPackageListService @Inject constructor(
+ val generator: NodeLocationAwareGenerator,
+ val formatService: FormatService
+) : PackageListService {
+
+ override fun formatPackageList(module: DocumentationModule): String {
+ val packages = mutableSetOf<String>()
+ val nonStandardLocations = mutableMapOf<String, String>()
+
+ fun visit(node: DocumentationNode, relocated: Boolean = false) {
+ val nodeKind = node.kind
+
+ when (nodeKind) {
+ NodeKind.Package -> {
+ packages.add(node.qualifiedName())
+ node.members.forEach { visit(it) }
+ }
+ NodeKind.Signature -> {
+ if (relocated)
+ nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!)
+ }
+ NodeKind.ExternalClass -> {
+ node.members.forEach { visit(it, relocated = true) }
+ }
+ NodeKind.GroupNode -> {
+ //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) }
+ }
+ else -> {
+ if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) {
+ node.details(NodeKind.Signature).forEach { visit(it, relocated) }
+ node.members.forEach { visit(it, relocated) }
+ }
+ }
+ }
+ }
+
+ module.members.forEach { visit(it) }
+
+ return buildString {
+ appendln("\$dokka.linkExtension:${formatService.linkExtension}")
+
+ nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" }
+ .sorted().joinTo(this, separator = "\n", postfix = "\n")
+
+ packages.sorted().joinTo(this, separator = "\n", postfix = "\n")
+ }
+
+ }
+
+}
+
diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt
new file mode 100644
index 000000000..dd67ac972
--- /dev/null
+++ b/core/src/main/kotlin/Formats/StandardFormats.kt
@@ -0,0 +1,66 @@
+package org.jetbrains.dokka.Formats
+
+import com.google.inject.Binder
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService
+import org.jetbrains.dokka.Utilities.bind
+import kotlin.reflect.KClass
+
+abstract class KotlinFormatDescriptorBase
+ : FileGeneratorBasedFormatDescriptor(),
+ DefaultAnalysisComponent,
+ DefaultAnalysisComponentServices by KotlinAsKotlin {
+ override val generatorServiceClass = FileGenerator::class
+ override val outlineServiceClass: KClass<out OutlineFormatService>? = null
+ override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class
+}
+
+abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent {
+ override val formatServiceClass = HtmlFormatService::class
+ override val outlineServiceClass = HtmlFormatService::class
+ override val generatorServiceClass = FileGenerator::class
+ override val packageListServiceClass = DefaultPackageListService::class
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ super.configureOutput(binder)
+ bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") }
+ }
+}
+
+class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin
+
+class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava
+
+class 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
+ override val outlineServiceClass = YamlOutlineService::class
+
+ override fun configureOutput(binder: Binder) = with(binder) {
+ super.configureOutput(binder)
+ bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService)
+ }
+}
+
+class JekyllFormatDescriptor : KotlinFormatDescriptorBase() {
+ override val formatServiceClass = JekyllFormatService::class
+}
+
+class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() {
+ override val formatServiceClass = MarkdownFormatService::class
+}
+
+class GFMFormatDescriptor : KotlinFormatDescriptorBase() {
+ override val formatServiceClass = GFMFormatService::class
+}
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
new file mode 100644
index 000000000..a2c9078f3
--- /dev/null
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -0,0 +1,691 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.LanguageService.RenderMode
+import java.util.*
+
+data class FormatLink(val text: String, val href: String)
+
+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)
+
+ protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
+ to.append(prefix)
+ body()
+ to.append(suffix)
+ }
+
+ protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) {
+ val startLength = to.length
+ to.append(prefix)
+ body()
+ if (checkEndsWith && to.endsWith(suffix)) {
+ to.setLength(to.length - suffix.length)
+ } else if (to.length > startLength + prefix.length) {
+ to.append(suffix)
+ } else {
+ to.setLength(startLength)
+ }
+ }
+
+ protected fun wrapInTag(tag: String,
+ body: () -> Unit,
+ newlineBeforeOpen: Boolean = false,
+ newlineAfterOpen: Boolean = false,
+ newlineAfterClose: Boolean = false) {
+ if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln()
+ to.append("<$tag>")
+ if (newlineAfterOpen) to.appendln()
+ body()
+ to.append("</$tag>")
+ if (newlineAfterClose) to.appendln()
+ }
+
+ protected abstract fun ensureParagraph()
+
+ open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body)
+ abstract fun appendBlockCode(language: String, body: () -> Unit)
+ abstract fun appendHeader(level: Int = 1, body: () -> Unit)
+ abstract fun appendParagraph(body: () -> Unit)
+
+ open fun appendSoftParagraph(body: () -> Unit) {
+ ensureParagraph()
+ body()
+ }
+
+ abstract fun appendLine()
+ abstract fun appendAnchor(anchor: String)
+
+ abstract fun appendTable(vararg columns: String, body: () -> Unit)
+ abstract fun appendTableBody(body: () -> Unit)
+ abstract fun appendTableRow(body: () -> Unit)
+ abstract fun appendTableCell(body: () -> Unit)
+
+ abstract fun appendText(text: String)
+
+ open fun appendSinceKotlin(version: String) {
+ appendParagraph {
+ appendText("Available since Kotlin: ")
+ appendCode { appendText(version) }
+ }
+ }
+
+ open fun appendSectionWithTag(section: ContentSection) {
+ appendParagraph {
+ appendStrong { appendText(section.tag) }
+ appendLine()
+ appendContent(section)
+ }
+ }
+
+ open fun appendSymbol(text: String) {
+ appendText(text)
+ }
+
+ open fun appendKeyword(text: String) {
+ appendText(text)
+ }
+
+ open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+ appendText(text)
+ }
+
+ fun appendEntity(text: String) {
+ to.append(text)
+ }
+
+ abstract fun appendLink(href: String, body: () -> Unit)
+
+ open fun appendLink(link: FormatLink) {
+ appendLink(link.href) { appendText(link.text) }
+ }
+
+ abstract fun appendStrong(body: () -> Unit)
+ abstract fun appendStrikethrough(body: () -> Unit)
+ abstract fun appendEmphasis(body: () -> Unit)
+ abstract fun appendCode(body: () -> Unit)
+ abstract fun appendUnorderedList(body: () -> Unit)
+ abstract fun appendOrderedList(body: () -> Unit)
+ abstract fun appendListItem(body: () -> Unit)
+
+ abstract fun appendBreadcrumbSeparator()
+ abstract fun appendNonBreakingSpace()
+ open fun appendSoftLineBreak() {
+ }
+
+ open fun appendIndentedSoftLineBreak() {
+ }
+
+ fun appendContent(content: List<ContentNode>) {
+ for (contentNode in content) {
+ appendContent(contentNode)
+ }
+ }
+
+ open fun appendContent(content: ContentNode) {
+ when (content) {
+ is ContentText -> appendText(content.text)
+ is ContentSymbol -> appendSymbol(content.text)
+ is ContentKeyword -> appendKeyword(content.text)
+ is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature)
+ is ContentNonBreakingSpace -> appendNonBreakingSpace()
+ is ContentSoftLineBreak -> appendSoftLineBreak()
+ is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak()
+ is ContentEntity -> appendEntity(content.text)
+ is ContentStrong -> appendStrong { appendContent(content.children) }
+ is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) }
+ is ContentCode -> appendCode { appendContent(content.children) }
+ is ContentEmphasis -> appendEmphasis { appendContent(content.children) }
+ is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) }
+ is ContentOrderedList -> appendOrderedList { appendContent(content.children) }
+ is ContentListItem -> appendListItem {
+ val child = content.children.singleOrNull()
+ if (child is ContentParagraph) {
+ appendContent(child.children)
+ } else {
+ appendContent(content.children)
+ }
+ }
+
+ is ContentNodeLink -> {
+ val node = content.node
+ val linkTo = if (node != null) locationHref(location, node) else "#"
+ appendLinkIfNotThisPage(linkTo, content)
+ }
+ is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content)
+
+ is ContentParagraph -> {
+ if (!content.isEmpty()) {
+ appendParagraph { appendContent(content.children) }
+ }
+ }
+
+ is ContentSpecialReference -> wrapInTag(tag = "aside class=\"note\"", body = {
+ if (!content.isEmpty()) {
+ appendContent(content.children)
+ }
+ })
+
+ is ContentBlockSampleCode, is ContentBlockCode -> {
+ content as ContentBlockCode
+ fun ContentBlockCode.appendBlockCodeContent() {
+ children
+ .dropWhile { it is ContentText && it.text.isBlank() }
+ .forEach { appendContent(it) }
+ }
+ when (content) {
+ is ContentBlockSampleCode ->
+ appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent, { content.appendBlockCodeContent() })
+ is ContentBlockCode ->
+ appendBlockCode(content.language, { content.appendBlockCodeContent() })
+ }
+ }
+ is ContentHeading -> appendHeader(content.level) { appendContent(content.children) }
+ is ContentBlock -> appendContent(content.children)
+ }
+ }
+
+ private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) {
+ if (href == ".") {
+ appendContent(content.children)
+ } else {
+ appendLink(href) { appendContent(content.children) }
+ }
+ }
+
+ open fun link(from: DocumentationNode,
+ to: DocumentationNode,
+ name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink = link(from, to, extension, name)
+
+ open fun link(from: DocumentationNode,
+ to: DocumentationNode,
+ extension: String,
+ name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
+ 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()))
+ }
+
+ 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())
+ }
+
+ private fun DocumentationNode.isModuleOrPackage(): Boolean =
+ kind == NodeKind.Module || kind == NodeKind.Package
+
+ protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) {
+ block()
+ }
+
+ protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ block()
+ }
+
+ protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ appendTableRow(block)
+ }
+
+ protected open fun appendPlatforms(platforms: Set<String>) {
+ if (platforms.isNotEmpty()) {
+ appendLine()
+ appendText(platforms.joinToString(prefix = "(", postfix = ")"))
+ }
+ }
+
+ protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) {
+ for ((index, item) in path.withIndex()) {
+ if (index > 0) {
+ appendBreadcrumbSeparator()
+ }
+ appendLink(item)
+ }
+ }
+
+ fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> =
+ sections.filter { it.subjectName != null }.groupBy { it.tag }
+
+ private fun ContentNode.appendSignature() {
+ if (this is ContentBlock && this.isEmpty()) {
+ return
+ }
+
+ val signatureAsCode = ContentCode()
+ signatureAsCode.append(this)
+ appendContent(signatureAsCode)
+ }
+
+ open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) {
+ open fun build() {
+ val breakdownByLocation = nodes.groupBy { node ->
+ node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct()
+ }
+
+ for ((path, nodes) in breakdownByLocation) {
+ if (!noHeader && path.isNotEmpty()) {
+ appendBreadcrumbs(path)
+ appendLine()
+ appendLine()
+ }
+ appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass })
+ }
+ }
+
+ private fun appendLocation(nodes: Iterable<DocumentationNode>) {
+ val singleNode = nodes.singleOrNull()
+ if (singleNode != null && singleNode.isModuleOrPackage()) {
+ if (singleNode.kind == NodeKind.Package) {
+ val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name
+ appendHeader(2) { appendText("Package $packageName") }
+ }
+ singleNode.appendPlatforms()
+ appendContent(singleNode.content)
+ } else {
+ val breakdownByName = nodes.groupBy { node -> node.name }
+ for ((name, items) in breakdownByName) {
+ if (!noHeader)
+ appendHeader { appendText(name) }
+ appendDocumentation(items, singleNode != null)
+ }
+ }
+ }
+
+ private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) {
+ val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content }
+
+ if (breakdownBySummary.size == 1) {
+ formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode)
+ } else {
+ for ((_, items) in breakdownBySummary) {
+
+ appendAsOverloadGroup(to, platformsOfItems(items)) {
+ formatOverloadGroup(items)
+ }
+
+ }
+ }
+ }
+
+ private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) {
+ for ((index, item) in items.withIndex()) {
+ if (index > 0) appendLine()
+ val rendered = languageService.render(item)
+ item.detailOrNull(NodeKind.Signature)?.let {
+ if (item.kind !in NodeKind.classLike || !isSingleNode)
+ appendAnchor(it.name)
+ }
+ appendAsSignature(rendered) {
+ appendCode { appendContent(rendered) }
+ item.appendSourceLink()
+ }
+ item.appendOverrides()
+ item.appendDeprecation()
+ item.appendPlatforms()
+ }
+ // All items have exactly the same documentation, so we can use any item to render it
+ val item = items.first()
+ item.details(NodeKind.OverloadGroupNote).forEach {
+ appendContent(it.content)
+ }
+
+ appendContent(item.content.summary)
+ item.appendDescription()
+ }
+
+ private fun DocumentationNode.appendSourceLink() {
+ val sourceUrl = details(NodeKind.SourceUrl).firstOrNull()
+ if (sourceUrl != null) {
+ to.append(" ")
+ appendLink(sourceUrl.name) { to.append("(source)") }
+ }
+ }
+
+ private fun DocumentationNode.appendOverrides() {
+ overrides.forEach {
+ appendParagraph {
+ to.append("Overrides ")
+ val location = location().relativePathTo(it.location())
+
+ appendLink(FormatLink(it.owner!!.name + "." + it.name, location))
+ }
+ }
+ }
+
+ private fun DocumentationNode.appendDeprecation() {
+ if (deprecation != null) {
+ val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull()
+ val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull()
+ appendLine()
+ 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 + "+"
+ }
+
+ 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 {
+ appendSectionWithSubject(it.key, it.value)
+ }
+
+ for (section in content.sections.filter { it.subjectName == null }) {
+ appendSectionWithTag(section)
+ }
+ }
+
+ fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) {
+ appendHeader(3) { appendText(title) }
+ subjectSections.forEach {
+ val subjectName = it.subjectName
+ if (subjectName != null) {
+ appendSoftParagraph {
+ appendAnchor(subjectName)
+ appendCode { to.append(subjectName) }
+ to.append(" - ")
+ appendContent(it)
+ }
+ }
+ }
+ }
+ }
+
+ inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) {
+
+ override fun build() {
+ val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }
+
+ appendBreadcrumbs(breakdownByLocation)
+ appendLine()
+ appendLine()
+ appendHeader { appendText(node.name) }
+
+ fun DocumentationNode.priority(): Int = when (kind) {
+ NodeKind.TypeAlias -> 1
+ NodeKind.Class -> 2
+ else -> 3
+ }
+
+ for (member in node.members.sortedBy(DocumentationNode::priority)) {
+
+ appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
+ formatSubNodeOfGroup(member)
+ }
+
+ }
+ }
+
+ fun formatSubNodeOfGroup(member: DocumentationNode) {
+ SingleNodePageBuilder(member, true).build()
+ }
+ }
+
+ inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
+ : PageBuilder(listOf(node), noHeader) {
+
+ override fun build() {
+ super.build()
+
+ 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) }
+ }
+
+ fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> {
+ return membersOrGroupMembers { it.kind == kind }
+ }
+
+ appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true)
+ appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
+ appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass))
+ appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception))
+ appendSection("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias))
+ appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass))
+ appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true)
+ appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true)
+ appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true)
+ appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property))
+ appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true)
+ appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function))
+ appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
+ appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property))
+ appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
+ appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function))
+ appendSection("Other members", node.members.filter {
+ it.kind !in setOf(
+ NodeKind.Class,
+ NodeKind.Interface,
+ NodeKind.Enum,
+ NodeKind.Object,
+ NodeKind.AnnotationClass,
+ NodeKind.Exception,
+ NodeKind.TypeAlias,
+ NodeKind.Constructor,
+ NodeKind.Property,
+ NodeKind.Package,
+ NodeKind.Function,
+ NodeKind.CompanionObjectProperty,
+ NodeKind.CompanionObjectFunction,
+ NodeKind.ExternalClass,
+ NodeKind.EnumItem,
+ NodeKind.AllTypes,
+ NodeKind.GroupNode
+ )
+ })
+
+ val allExtensions = node.extensions
+ appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property })
+ appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function })
+ appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty })
+ appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction })
+ appendSection("Inheritors",
+ node.inheritors.filter { it.kind != NodeKind.EnumItem })
+
+ if (node.kind == NodeKind.Module) {
+ appendHeader(3) { to.append("Index") }
+ node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes ->
+ appendLink(link(node, allTypes, { "All Types" }))
+ }
+ }
+ }
+
+ private fun appendSection(caption: String, members: List<DocumentationNode>,
+ sortMembers: Boolean = true,
+ omitSamePlatforms: Boolean = false,
+ platformsBasedOnMembers: Boolean = false) {
+ if (members.isEmpty()) return
+
+ appendHeader(3) { appendText(caption) }
+
+ val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members
+ val membersMap = children.groupBy { link(node, it) }
+
+
+
+ appendTable("Name", "Summary") {
+ appendTableBody {
+ for ((memberLocation, members) in membersMap) {
+ val elementPlatforms = platformsOfItems(members, omitSamePlatforms)
+ val platforms = if (platformsBasedOnMembers)
+ members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+ else
+ elementPlatforms
+ appendIndexRow(platforms) {
+ appendTableCell {
+ appendParagraph {
+ appendLink(memberLocation)
+ if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
+ appendPlatforms(platforms)
+ }
+ }
+ }
+ appendTableCell {
+ val breakdownBySummary = members.groupBy { it.summary }
+ for ((summary, items) in breakdownBySummary) {
+ appendSummarySignatures(items)
+ appendContent(summary)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
+ val platforms = platformsOfItems(items)
+ if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
+ return platforms
+ }
+ return emptySet()
+ }
+
+ private fun appendSummarySignatures(items: List<DocumentationNode>) {
+ val summarySignature = languageService.summarizeSignatures(items)
+ if (summarySignature != null) {
+ appendAsSignature(summarySignature) {
+ summarySignature.appendSignature()
+ }
+ return
+ }
+ val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
+ renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
+ appendAsSignature(it) {
+ it.appendSignature()
+ }
+ appendLine()
+ }
+ appendAsSignature(renderedSignatures.last()) {
+ renderedSignatures.last().appendSignature()
+ }
+ }
+ }
+
+ inner class AllTypesNodeBuilder(val node: DocumentationNode)
+ : PageBuilder(listOf(node)) {
+
+ override fun build() {
+ appendContent(node.owner!!.summary)
+ appendHeader(3) { to.append("All Types") }
+
+ appendTable("Name", "Summary") {
+ appendTableBody {
+ for (type in node.members) {
+ appendTableRow {
+ appendTableCell {
+ 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)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun appendNodes(nodes: Iterable<DocumentationNode>) {
+ val singleNode = nodes.singleOrNull()
+ when (singleNode?.kind) {
+ NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build()
+ NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build()
+ null -> PageBuilder(nodes).build()
+ else -> SingleNodePageBuilder(singleNode).build()
+ }
+ }
+}
+
+abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator,
+ val languageService: LanguageService,
+ override val extension: String,
+ override final val linkExtension: String = extension) : FormatService {
+
+}
diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt
new file mode 100644
index 000000000..c36f98eb0
--- /dev/null
+++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt
@@ -0,0 +1,26 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import java.io.File
+
+class YamlOutlineService @Inject constructor(
+ val generator: NodeLocationAwareGenerator,
+ val languageService: LanguageService
+) : OutlineFormatService {
+ override fun getOutlineFileName(location: Location): File = File("${location.path}.yml")
+
+ var outlineLevel = 0
+ override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
+ val indent = " ".repeat(outlineLevel)
+ to.appendln("$indent- title: ${languageService.renderName(node)}")
+ to.appendln("$indent url: ${generator.location(node).path}")
+ }
+
+ override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
+ val indent = " ".repeat(outlineLevel)
+ to.appendln("$indent content:")
+ outlineLevel++
+ body()
+ outlineLevel--
+ }
+}
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
new file mode 100644
index 000000000..6f063b587
--- /dev/null
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -0,0 +1,207 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Guice
+import com.google.inject.Injector
+import com.intellij.openapi.util.Disposer
+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.Utilities.DokkaAnalysisModule
+import org.jetbrains.dokka.Utilities.DokkaOutputModule
+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.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
+import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
+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) {
+
+ private val documentationModule = DocumentationModule(moduleName)
+
+ fun generate() {
+ val sourcesGroupedByPlatform = sources.groupBy { it.platforms.firstOrNull() }
+ for ((platform, roots) in sourcesGroupedByPlatform) {
+ appendSourceModule(platform, roots)
+ }
+ documentationModule.prepareForGeneration(options)
+
+ val timeBuild = measureTimeMillis {
+ logger.info("Generating pages... ")
+ val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger))
+ outputInjector.getInstance(Generator::class.java).buildAll(documentationModule)
+ }
+ logger.info("done in ${timeBuild / 1000} secs")
+ }
+
+ private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) {
+ val sourcePaths = sourceRoots.map { it.path }
+ val environment = createAnalysisEnvironment(sourcePaths)
+
+ logger.info("Module: $moduleName")
+ logger.info("Output: ${File(options.outputDir)}")
+ logger.info("Sources: ${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 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 injector = Guice.createInjector(
+ DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger))
+
+ buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes)
+
+ val timeAnalyse = System.currentTimeMillis() - startAnalyse
+ logger.info("done in ${timeAnalyse / 1000} secs")
+
+ Disposer.dispose(environment)
+ }
+
+ fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment {
+ val environment = AnalysisEnvironment(DokkaMessageCollector(logger))
+
+ environment.apply {
+ //addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
+ // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath())
+ for (element in this@DokkaGenerator.classpath) {
+ addClasspath(File(element))
+ }
+
+ addSources(sourcePaths)
+ addSources(this@DokkaGenerator.samples)
+
+ loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
+ }
+
+ return environment
+ }
+
+ fun isNotSample(file: PsiFile): Boolean {
+ val sourceFile = File(file.virtualFile!!.path)
+ return samples.none { sample ->
+ val canonicalSample = File(sample).canonicalPath
+ val canonicalSource = sourceFile.canonicalPath
+ canonicalSource.startsWith(canonicalSample)
+ }
+ }
+}
+
+class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector {
+ override fun clear() {
+ seenErrors = false
+ }
+
+ private var seenErrors = false
+
+ override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
+ if (severity == CompilerMessageSeverity.ERROR) {
+ seenErrors = true
+ }
+ logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location))
+ }
+
+ override fun hasErrors() = seenErrors
+}
+
+fun buildDocumentationModule(injector: Injector,
+ documentationModule: DocumentationModule,
+ filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true },
+ includes: List<String> = listOf()) {
+
+ val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java)
+ val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter)
+
+ val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java)
+ val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java)
+ analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles)
+
+ val fragments = fragmentFiles
+ .map { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) }
+ .filterNotNull()
+ .distinct()
+
+ val packageDocs = injector.getInstance(PackageDocs::class.java)
+ for (include in includes) {
+ packageDocs.parse(include, fragments)
+ }
+ if (documentationModule.content.isEmpty()) {
+ documentationModule.updateContent {
+ for (node in packageDocs.moduleContent.children) {
+ append(node)
+ }
+ }
+ }
+
+ 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)
+ with(injector.getInstance(JavaDocumentationBuilder::class.java)) {
+ javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) }
+ }
+}
+
+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(CLIConfigurationKeys.CONTENT_ROOTS)
+ ?.filterIsInstance<JavaSourceRoot>()
+ ?.map { it.file }
+ ?: listOf()
+
+ val result = arrayListOf<PsiJavaFile>()
+ val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file")
+ sourceRoots.forEach { sourceRoot ->
+ sourceRoot.absoluteFile.walkTopDown().forEach {
+ val vFile = localFileSystem.findFileByPath(it.path)
+ if (vFile != null) {
+ val psiFile = PsiManager.getInstance(project).findFile(vFile)
+ if (psiFile is PsiJavaFile) {
+ result.add(psiFile)
+ }
+ }
+ }
+ }
+ return result
+}
diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt
new file mode 100644
index 000000000..2d202db84
--- /dev/null
+++ b/core/src/main/kotlin/Generation/FileGenerator.kt
@@ -0,0 +1,89 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
+
+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) var packageListService: PackageListService? = null
+
+ override fun location(node: DocumentationNode): FileLocation {
+ return FileLocation(fileForNode(node, formatService.linkExtension))
+ }
+
+ private fun fileForNode(node: DocumentationNode, extension: String = ""): File {
+ return File(root, relativePathToNode(node)).appendExtension(extension)
+ }
+
+ fun locationWithoutExtension(node: DocumentationNode): FileLocation {
+ return FileLocation(fileForNode(node))
+ }
+
+ override fun buildPages(nodes: Iterable<DocumentationNode>) {
+
+ 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)
+ }
+ buildPages(items.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))
+ }
+ }
+ }
+ }
+
+ override fun buildSupportFiles() {
+ formatService.enumerateSupportFiles { resource, targetPath ->
+ FileOutputStream(File(root, relativePathToNode(listOf(targetPath), false))).use {
+ javaClass.getResourceAsStream(resource).copyTo(it)
+ }
+ }
+ }
+
+ override fun buildPackageList(nodes: Iterable<DocumentationNode>) {
+ if (packageListService == null) return
+
+ for (module in nodes) {
+
+ val moduleRoot = location(module).file.parentFile
+ val packageListFile = File(moduleRoot, "package-list")
+
+ packageListFile.writeText("\$dokka.format:${options.outputFormat}\n" +
+ packageListService!!.formatPackageList(module as DocumentationModule))
+ }
+
+ }
+
+}
+
+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/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt
new file mode 100644
index 000000000..23286e299
--- /dev/null
+++ b/core/src/main/kotlin/Generation/Generator.kt
@@ -0,0 +1,29 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+interface Generator {
+ fun buildPages(nodes: Iterable<DocumentationNode>)
+ fun buildOutlines(nodes: Iterable<DocumentationNode>)
+ fun buildSupportFiles()
+ fun buildPackageList(nodes: Iterable<DocumentationNode>)
+}
+
+fun Generator.buildAll(nodes: Iterable<DocumentationNode>) {
+ buildPages(nodes)
+ buildOutlines(nodes)
+ buildSupportFiles()
+ buildPackageList(nodes)
+}
+
+fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node))
+
+fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node))
+
+fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node))
+
+
+interface NodeLocationAwareGenerator: Generator {
+ fun location(node: DocumentationNode): Location
+ val root: File
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
new file mode 100644
index 000000000..c8f93c519
--- /dev/null
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -0,0 +1,67 @@
+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 generateClassIndexPage: Boolean,
+ override val generatePackageIndexPage: 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 noJdkLink: Boolean,
+ override val cacheRoot: String?,
+ override val suppressedFiles: List<String>,
+ override val languageVersion: String?,
+ override val apiVersion: String?,
+ override val collectInheritedExtensionsFromLibraries: Boolean,
+ override val outlineRoot: String,
+ override val dacRoot: 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
new file mode 100644
index 000000000..94bb0455d
--- /dev/null
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -0,0 +1,392 @@
+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.impl.source.PsiClassReferenceType
+import com.intellij.psi.util.InheritanceUtil
+import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
+import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+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.map { it.type.typeSignature() }.joinToString(",") + ")"
+ else -> null
+}
+
+private fun PsiType.typeSignature(): String = when(this) {
+ is PsiArrayType -> "Array((${componentType.typeSignature()}))"
+ is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize()
+ is PsiClassType -> resolve()?.qualifiedName ?: className
+ else -> mapTypeName(this)
+}
+
+private fun mapTypeName(psiType: PsiType): String = when (psiType) {
+ is PsiPrimitiveType -> psiType.canonicalText
+ is PsiClassType -> psiType.resolve()?.name ?: psiType.className
+ is PsiEllipsisType -> mapTypeName(psiType.componentType)
+ is PsiArrayType -> "kotlin.Array"
+ else -> psiType.canonicalText
+}
+
+interface JavaDocumentationBuilder {
+ fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>)
+}
+
+class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
+ private val options: DocumentationOptions
+ private val refGraph: NodeReferenceGraph
+ private val docParser: JavaDocumentationParser
+ private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+
+ @Inject constructor(
+ options: DocumentationOptions,
+ refGraph: NodeReferenceGraph,
+ logger: DokkaLogger,
+ signatureProvider: ElementSignatureProvider,
+ externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+ ) {
+ this.options = options
+ this.refGraph = refGraph
+ this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver)
+ this.externalDocumentationLinkResolver = externalDocumentationLinkResolver
+ }
+
+ constructor(
+ options: DocumentationOptions,
+ refGraph: NodeReferenceGraph,
+ docParser: JavaDocumentationParser,
+ externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+ ) {
+ this.options = options
+ this.refGraph = refGraph
+ this.docParser = docParser
+ this.externalDocumentationLinkResolver = externalDocumentationLinkResolver
+ }
+
+ override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
+ if (skipFile(file) || file.classes.all { skipElement(it) }) {
+ return
+ }
+ val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph)
+ appendClasses(packageNode, file.classes)
+ }
+
+ fun appendClasses(packageNode: DocumentationNode, classes: Array<PsiClass>) {
+ packageNode.appendChildren(classes) { build() }
+ }
+
+ fun register(element: PsiElement, node: DocumentationNode) {
+ val signature = getSignature(element)
+ if (signature != null) {
+ refGraph.register(signature, node)
+ }
+ }
+
+ fun link(node: DocumentationNode, element: PsiElement?) {
+ val qualifiedName = getSignature(element)
+ if (qualifiedName != null) {
+ refGraph.link(node, qualifiedName, RefKind.Link)
+ }
+ }
+
+ fun link(element: PsiElement?, node: DocumentationNode, kind: RefKind) {
+ val qualifiedName = getSignature(element)
+ if (qualifiedName != null) {
+ refGraph.link(qualifiedName, node, kind)
+ }
+ }
+
+ fun nodeForElement(element: PsiNamedElement,
+ kind: NodeKind,
+ name: String = element.name ?: "<anonymous>"): DocumentationNode {
+ val (docComment, deprecatedContent, attrs, apiLevel, sdkExtSince, deprecatedLevel, artifactId, attribute) = docParser.parseDocumentation(element)
+ val node = DocumentationNode(name, docComment, kind)
+ if (element is PsiModifierListOwner) {
+ node.appendModifiers(element)
+ val modifierList = element.modifierList
+ if (modifierList != null) {
+ modifierList.annotations.filter { !ignoreAnnotation(it) }.forEach {
+ val annotation = it.build()
+ if (it.qualifiedName == "java.lang.Deprecated" || it.qualifiedName == "kotlin.Deprecated") {
+ node.append(annotation, RefKind.Deprecation)
+ annotation.convertDeprecationDetailsToChildren()
+ } else {
+ node.append(annotation, RefKind.Annotation)
+ }
+ }
+ }
+ }
+ if (deprecatedContent != null) {
+ val deprecationNode = DocumentationNode("", deprecatedContent, NodeKind.Modifier)
+ node.append(deprecationNode, RefKind.Deprecation)
+ }
+ if (element is PsiDocCommentOwner && element.isDeprecated && node.deprecation == null) {
+ val deprecationNode = DocumentationNode("", Content.of(ContentText("Deprecated")), NodeKind.Modifier)
+ node.append(deprecationNode, RefKind.Deprecation)
+ }
+ apiLevel?.let {
+ node.append(it, RefKind.Detail)
+ }
+ sdkExtSince?.let {
+ node.append(it, RefKind.Detail)
+ }
+ deprecatedLevel?.let {
+ node.append(it, RefKind.Detail)
+ }
+ artifactId?.let {
+ node.append(it, RefKind.Detail)
+ }
+ attrs.forEach {
+ refGraph.link(node, it, RefKind.Detail)
+ refGraph.link(it, node, RefKind.Owner)
+ }
+ attribute?.let {
+ val attrName = node.qualifiedName()
+ refGraph.register("Attr:$attrName", attribute)
+ }
+ return node
+ }
+
+ fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) {
+ "java.lang.SuppressWarnings" -> true
+ else -> false
+ }
+
+ fun <T : Any> DocumentationNode.appendChildren(elements: Array<T>,
+ kind: RefKind = RefKind.Member,
+ buildFn: T.() -> DocumentationNode) {
+ elements.forEach {
+ if (!skipElement(it)) {
+ append(it.buildFn(), kind)
+ }
+ }
+ }
+
+ private fun skipFile(javaFile: PsiJavaFile): Boolean = options.effectivePackageOptions(javaFile.packageName).suppress
+
+ private fun skipElement(element: Any) =
+ skipElementByVisibility(element) ||
+ hasSuppressDocTag(element) ||
+ hasHideAnnotation(element) ||
+ skipElementBySuppressedFiles(element)
+
+ private fun skipElementByVisibility(element: Any): Boolean =
+ element is PsiModifierListOwner &&
+ element !is PsiParameter &&
+ !(options.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 && element.containingFile.virtualFile != null && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles
+
+ private fun PsiElement.isInternal(): Boolean {
+ val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false
+ return (ktElement as? KtModifierListOwner)?.hasModifier(KtTokens.INTERNAL_KEYWORD) ?: false
+ }
+
+ fun <T : Any> DocumentationNode.appendMembers(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
+ appendChildren(elements, RefKind.Member, buildFn)
+
+ fun <T : Any> DocumentationNode.appendDetails(elements: Array<T>, buildFn: T.() -> DocumentationNode) =
+ appendChildren(elements, RefKind.Detail, buildFn)
+
+ fun PsiClass.build(): DocumentationNode {
+ val kind = when {
+ isInterface -> NodeKind.Interface
+ isEnum -> NodeKind.Enum
+ isAnnotationType -> NodeKind.AnnotationClass
+ isException() -> NodeKind.Exception
+ else -> NodeKind.Class
+ }
+ val node = nodeForElement(this, kind)
+ superTypes.filter { !ignoreSupertype(it) }.forEach { superType ->
+ node.appendType(superType, NodeKind.Supertype)
+ val superClass = superType.resolve()
+ if (superClass != null) {
+ link(superClass, node, RefKind.Inheritor)
+ }
+ }
+
+ var methodsAndConstructors = methods
+
+ if (constructors.isEmpty()) {
+ // Having no constructor represents a class that only has an implicit/default constructor
+ // so we create one synthetically for documentation
+ val factory = JavaPsiFacade.getElementFactory(this.project)
+ methodsAndConstructors += factory.createMethodFromText("public $name() {}", this)
+ }
+ node.appendDetails(typeParameters) { build() }
+ node.appendMembers(methodsAndConstructors) { build() }
+ node.appendMembers(fields) { build() }
+ node.appendMembers(innerClasses) { build() }
+ register(this, node)
+ return node
+ }
+
+ fun PsiClass.isException() = InheritanceUtil.isInheritor(this, "java.lang.Throwable")
+
+ fun ignoreSupertype(psiType: PsiClassType): Boolean = false
+// psiType.isClass("java.lang.Enum") || psiType.isClass("java.lang.Object")
+
+ fun PsiClassType.isClass(qName: String): Boolean {
+ val shortName = qName.substringAfterLast('.')
+ if (className == shortName) {
+ val psiClass = resolve()
+ return psiClass?.qualifiedName == qName
+ }
+ return false
+ }
+
+ fun PsiField.build(): DocumentationNode {
+ val node = nodeForElement(this, nodeKind())
+ node.appendType(type)
+
+ 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) ?: return
+ val text = when(value) {
+ is String -> "\"${StringUtil.escapeStringCharacters(value)}\""
+ else -> value.toString()
+ }
+ append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail)
+ }
+ }
+
+ private fun PsiField.nodeKind(): NodeKind = when {
+ this is PsiEnumConstant -> NodeKind.EnumItem
+ else -> NodeKind.Field
+ }
+
+ fun PsiMethod.build(): DocumentationNode {
+ val node = nodeForElement(this, nodeKind(), name)
+
+ if (!isConstructor) {
+ node.appendType(returnType)
+ }
+ node.appendDetails(parameterList.parameters) { build() }
+ node.appendDetails(typeParameters) { build() }
+ register(this, node)
+ return node
+ }
+
+ private fun PsiMethod.nodeKind(): NodeKind = when {
+ isConstructor -> NodeKind.Constructor
+ else -> NodeKind.Function
+ }
+
+ fun PsiParameter.build(): DocumentationNode {
+ val node = nodeForElement(this, NodeKind.Parameter)
+ node.appendType(type)
+ if (type is PsiEllipsisType) {
+ node.appendTextNode("vararg", NodeKind.Modifier, RefKind.Detail)
+ }
+ return node
+ }
+
+ fun PsiTypeParameter.build(): DocumentationNode {
+ val node = nodeForElement(this, NodeKind.TypeParameter)
+ extendsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) }
+ implementsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) }
+ return node
+ }
+
+ fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) {
+ val modifierList = element.modifierList ?: return
+
+ PsiModifier.MODIFIERS.forEach {
+ if (modifierList.hasExplicitModifier(it)) {
+ appendTextNode(it, NodeKind.Modifier)
+ }
+ }
+ }
+
+ fun DocumentationNode.appendType(psiType: PsiType?, kind: NodeKind = NodeKind.Type) {
+ if (psiType == null) {
+ return
+ }
+
+ val node = psiType.build(kind)
+ append(node, RefKind.Detail)
+
+ // Attempt to create an external link if the psiType is one
+ if (psiType is PsiClassReferenceType) {
+ val target = psiType.reference.resolve()
+ if (target != null) {
+ val externalLink = externalDocumentationLinkResolver.buildExternalDocumentationLink(target)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ }
+ }
+ }
+
+ fun PsiType.build(kind: NodeKind = NodeKind.Type): DocumentationNode {
+ val name = mapTypeName(this)
+ val node = DocumentationNode(name, Content.Empty, kind)
+ if (this is PsiClassType) {
+ node.appendDetails(parameters) { build(NodeKind.Type) }
+ link(node, resolve())
+ }
+ if (this is PsiArrayType && this !is PsiEllipsisType) {
+ node.append(componentType.build(NodeKind.Type), RefKind.Detail)
+ }
+ return node
+ }
+
+ fun PsiAnnotation.build(): DocumentationNode {
+ val node = DocumentationNode(nameReferenceElement?.text ?: "<?>", Content.Empty, NodeKind.Annotation)
+ parameterList.attributes.forEach {
+ val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter)
+ val value = it.value
+ if (value != null) {
+ val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text
+ val valueNode = DocumentationNode(valueText, Content.Empty, NodeKind.Value)
+ parameter.append(valueNode, RefKind.Detail)
+ }
+ node.append(parameter, RefKind.Detail)
+ }
+ return node
+ }
+}
+
+fun hasSuppressDocTag(element: Any?): Boolean {
+ val declaration = (element as? KtLightDeclaration<*, *>)?.kotlinOrigin as? KtDeclaration ?: return false
+ return PsiTreeUtil.findChildrenOfType(declaration.docComment, KDocTag::class.java).any { it.knownTag == KDocKnownTag.SUPPRESS }
+}
+
+/**
+ * Determines if the @hide annotation is present in a Javadoc comment.
+ *
+ * @param element a doc element to analyze for the presence of @hide
+ *
+ * @return true if @hide is present, otherwise false
+ *
+ * Note: this does not process @hide annotations in KDoc. For KDoc, use the @suppress tag instead, which is processed
+ * by [hasSuppressDocTag].
+ */
+fun hasHideAnnotation(element: Any?): Boolean {
+ return element is PsiDocCommentOwner && element.docComment?.run { findTagByName("hide") != null } ?: false
+}
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
new file mode 100644
index 000000000..0c73e7661
--- /dev/null
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -0,0 +1,709 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.javadoc.PsiDocTagValueImpl
+import com.intellij.psi.impl.source.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.IncorrectOperationException
+import org.jetbrains.dokka.Model.CodeNode
+import org.jetbrains.kotlin.utils.join
+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.io.File
+import java.net.URI
+import java.util.regex.Pattern
+
+private val NAME_TEXT = Pattern.compile("(\\S+)(.*)", Pattern.DOTALL)
+private val TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL)
+
+data class JavadocParseResult(
+ val content: Content,
+ val deprecatedContent: Content?,
+ val attributeRefs: List<String>,
+ val apiLevel: DocumentationNode? = null,
+ val sdkExtSince: DocumentationNode? = null,
+ val deprecatedLevel: DocumentationNode? = null,
+ val artifactId: DocumentationNode? = null,
+ val attribute: DocumentationNode? = null
+) {
+ companion object {
+ val Empty = JavadocParseResult(Content.Empty,
+ null,
+ emptyList(),
+ null,
+ null,
+ null,
+ null
+ )
+ }
+}
+
+interface JavaDocumentationParser {
+ fun parseDocumentation(element: PsiNamedElement): JavadocParseResult
+}
+
+class JavadocParser(
+ private val refGraph: NodeReferenceGraph,
+ private val logger: DokkaLogger,
+ private val signatureProvider: ElementSignatureProvider,
+ private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+) : JavaDocumentationParser {
+
+ private fun ContentSection.appendTypeElement(
+ signature: String,
+ selector: (DocumentationNode) -> DocumentationNode?
+ ) {
+ append(LazyContentBlock {
+ val node = refGraph.lookupOrWarn(signature, logger)?.let(selector)
+ if (node != null) {
+ it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
+ it.symbol(":")
+ it.text(" ")
+ }
+ })
+ }
+
+ override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
+ val docComment = (element as? PsiDocCommentOwner)?.docComment
+ if (docComment == null) return JavadocParseResult.Empty
+ val result = MutableContent()
+ var deprecatedContent: Content? = null
+ val firstParagraph = ContentParagraph()
+ firstParagraph.convertJavadocElements(
+ docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() },
+ element
+ )
+ val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph }
+ firstParagraph.children.removeAll(paragraphs)
+ if (!firstParagraph.isEmpty()) {
+ result.append(firstParagraph)
+ }
+ paragraphs.forEach {
+ result.append(it)
+ }
+
+ if (element is PsiMethod) {
+ val tagsByName = element.searchInheritedTags()
+ for ((tagName, tags) in tagsByName) {
+ for ((tag, context) in tags) {
+ val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName())
+ val signature = signatureProvider.signature(element)
+ when (tagName) {
+ "param" -> {
+ section.appendTypeElement(signature) {
+ it.details
+ .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() }
+ ?.detailOrNull(NodeKind.Type)
+ }
+ }
+ "return" -> {
+ section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
+ }
+ }
+ section.convertJavadocElements(tag.contentElements(), context)
+ }
+ }
+ }
+
+ val attrRefSignatures = mutableListOf<String>()
+ var since: DocumentationNode? = null
+ var sdkextsince: DocumentationNode? = null
+ var deprecated: DocumentationNode? = null
+ var artifactId: DocumentationNode? = null
+ var attrName: String? = null
+ var attrDesc: Content? = null
+ var attr: DocumentationNode? = null
+ docComment.tags.forEach { tag ->
+ when (tag.name.toLowerCase()) {
+ "see" -> result.convertSeeTag(tag)
+ "deprecated" -> {
+ deprecatedContent = Content().apply {
+ convertJavadocElements(tag.contentElements(), element)
+ }
+ }
+ "attr" -> {
+ when (tag.valueElement?.text) {
+ "ref" ->
+ tag.getAttrRef(element)?.let {
+ attrRefSignatures.add(it)
+ }
+ "name" -> attrName = tag.getAttrName()
+ "description" -> attrDesc = tag.getAttrDesc(element)
+ }
+ }
+ "since", "apisince" -> {
+ since = DocumentationNode(tag.getApiLevel() ?: "", Content.Empty, NodeKind.ApiLevel)
+ }
+ "sdkextsince" -> {
+ sdkextsince = DocumentationNode(tag.getSdkExtSince() ?: "", Content.Empty, NodeKind.SdkExtSince)
+ }
+ "deprecatedsince" -> {
+ deprecated = DocumentationNode(tag.getApiLevel() ?: "", Content.Empty, NodeKind.DeprecatedLevel)
+ }
+ "artifactid" -> {
+ artifactId = DocumentationNode(tag.artifactId() ?: "", Content.Empty, NodeKind.ArtifactId)
+ }
+ in tagsToInherit -> {
+ }
+ else -> {
+ val subjectName = tag.getSubjectName()
+ val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
+ section.convertJavadocElements(tag.contentElements(), element)
+ }
+ }
+ }
+ attrName?.let { name ->
+ attr = DocumentationNode(name, attrDesc ?: Content.Empty, NodeKind.AttributeRef)
+ }
+ return JavadocParseResult(result, deprecatedContent, attrRefSignatures, since, sdkextsince, deprecated, artifactId, attr)
+ }
+
+ private val tagsToInherit = setOf("param", "return", "throws")
+
+ private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement)
+
+ fun PsiDocTag.artifactId(): String? {
+ var artifactName: String? = null
+ if (dataElements.isNotEmpty()) {
+ artifactName = join(dataElements.map { it.text }, "")
+ }
+ return artifactName
+ }
+
+ fun PsiDocTag.getApiLevel(): String? {
+ if (dataElements.isNotEmpty()) {
+ val data = dataElements
+ if (data[0] is PsiDocTagValueImpl) {
+ val docTagValue = data[0]
+ if (docTagValue.firstChild != null) {
+ val apiLevel = docTagValue.firstChild
+ return apiLevel.text
+ }
+ }
+ }
+ return null
+ }
+
+ fun PsiDocTag.getSdkExtSince(): String? {
+ if (dataElements.isNotEmpty()) {
+ return join(dataElements.map { it.text }, " ")
+ }
+ return null
+ }
+
+ private fun PsiDocTag.getAttrRef(element: PsiNamedElement): String? {
+ if (dataElements.size > 1) {
+ val elementText = dataElements[1].text
+ try {
+ val linkComment = JavaPsiFacade.getInstance(project).elementFactory
+ .createDocCommentFromText("/** {@link $elementText} */", element)
+ val linkElement = PsiTreeUtil.getChildOfType(linkComment, PsiInlineDocTag::class.java)?.linkElement()
+ val signature = resolveInternalLink(linkElement)
+ val attrSignature = "AttrMain:$signature"
+ return attrSignature
+ } catch (e: IncorrectOperationException) {
+ return null
+ }
+ } else return null
+ }
+
+ private fun PsiDocTag.getAttrName(): String? {
+ if (dataElements.size > 1) {
+ val nameMatcher = NAME_TEXT.matcher(dataElements[1].text)
+ if (nameMatcher.matches()) {
+ return nameMatcher.group(1)
+ } else {
+ return null
+ }
+ } else return null
+ }
+
+ private fun PsiDocTag.getAttrDesc(element: PsiNamedElement): Content? {
+ return Content().apply {
+ convertJavadocElementsToAttrDesc(contentElements(), element)
+ }
+ }
+
+ 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 }
+ return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements
+ }
+
+ private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement) {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ doc.body().childNodes().forEach {
+ convertHtmlNode(it)?.let { append(it) }
+ }
+ doc.head().childNodes().forEach {
+ convertHtmlNode(it)?.let { append(it) }
+ }
+ }
+
+ private fun ContentBlock.convertJavadocElementsToAttrDesc(elements: Iterable<PsiElement>, element: PsiNamedElement) {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ doc.body().childNodes().forEach {
+ convertHtmlNode(it)?.let {
+ var content = it
+ if (content is ContentText) {
+ var description = content.text
+ val matcher = TEXT.matcher(content.text)
+ if (matcher.matches()) {
+ val command = matcher.group(1)
+ if (command == "description") {
+ description = matcher.group(2)
+ content = ContentText(description)
+ }
+ }
+ }
+ append(content)
+ }
+ }
+ }
+
+ private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String {
+ val htmlBuilder = StringBuilder()
+ elements.forEach {
+ if (it is PsiInlineDocTag) {
+ htmlBuilder.append(convertInlineDocTag(it, element))
+ } else {
+ htmlBuilder.append(it.text)
+ }
+ }
+ return htmlBuilder.toString().trim()
+ }
+
+ private fun convertHtmlNode(node: Node, isBlockCode: Boolean = false): ContentNode? {
+ if (isBlockCode) {
+ return if (node is TextNode) { // Fixes b/129762453
+ val codeNode = CodeNode(node.wholeText, "")
+ ContentText(codeNode.text().removePrefix("#"))
+ } else { // Fixes b/129857975
+ ContentText(node.toString())
+ }
+ }
+ if (node is TextNode) {
+ return ContentText(node.text().removePrefix("#"))
+ } else if (node is Element) {
+ val childBlock = createBlock(node)
+ node.childNodes().forEach {
+ val child = convertHtmlNode(it, isBlockCode = childBlock is ContentBlockCode)
+ if (child != null) {
+ childBlock.append(child)
+ }
+ }
+ return (childBlock)
+ }
+ return null
+ }
+
+ private fun createBlock(element: Element): ContentBlock = when (element.tagName()) {
+ "p" -> ContentParagraph()
+ "b", "strong" -> ContentStrong()
+ "i", "em" -> ContentEmphasis()
+ "s", "del" -> ContentStrikethrough()
+ "code" -> ContentCode()
+ "pre" -> ContentBlockCode()
+ "ul" -> ContentUnorderedList()
+ "ol" -> ContentOrderedList()
+ "li" -> ContentListItem()
+ "a" -> createLink(element)
+ "br" -> ContentBlock().apply { hardLineBreak() }
+
+ "dl" -> ContentDescriptionList()
+ "dt" -> ContentDescriptionTerm()
+ "dd" -> ContentDescriptionDefinition()
+
+ "table" -> ContentTable()
+ "tbody" -> ContentTableBody()
+ "tr" -> ContentTableRow()
+ "th" -> {
+ val colspan = element.attr("colspan")
+ val rowspan = element.attr("rowspan")
+ ContentTableHeader(colspan, rowspan)
+ }
+ "td" -> {
+ val colspan = element.attr("colspan")
+ val rowspan = element.attr("rowspan")
+ ContentTableCell(colspan, rowspan)
+ }
+
+ "h1" -> ContentHeading(1)
+ "h2" -> ContentHeading(2)
+ "h3" -> ContentHeading(3)
+ "h4" -> ContentHeading(4)
+ "h5" -> ContentHeading(5)
+ "h6" -> ContentHeading(6)
+
+ "div" -> {
+ val divClass = element.attr("class")
+ if (divClass == "special reference" || divClass == "note") ContentSpecialReference()
+ else ContentParagraph()
+ }
+
+ "script" -> {
+
+ // If the `type` attr is an empty string, we want to use null instead so that the resulting generated
+ // Javascript does not contain a `type` attr.
+ //
+ // Example:
+ // type == "" => <script type="" src="...">
+ // type == null => <script src="...">
+ val type = if (element.attr("type").isNotEmpty()) {
+ element.attr("type")
+ } else {
+ null
+ }
+ ScriptBlock(type, element.attr("src"))
+ }
+
+ else -> ContentBlock()
+ }
+
+ private fun createLink(element: Element): 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 valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val text = ContentText(linkElement.text)
+
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ val node = when {
+ externalLink != null -> {
+ val linkNode = ContentExternalLink(externalLink)
+ linkNode.append(text)
+ linkNode
+ }
+ linkSignature != null -> {
+ @Suppress("USELESS_CAST")
+ val signature: String = linkSignature as String
+ val linkNode =
+ ContentNodeLazyLink(
+ (tag.valueElement ?: linkElement).text
+ ) { refGraph.lookupOrWarn(signature, logger) }
+ linkNode.append(text)
+ linkNode
+ }
+ else -> text
+ }
+ seeSection.append(node)
+ }
+
+ private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) {
+ "link", "linkplain" -> {
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ if (externalLink != null || linkSignature != null) {
+
+ // sometimes `dataElements` contains multiple `PsiDocToken` elements and some have whitespace in them
+ // this is best effort to find the first non-empty one before falling back to using the symbol name.
+ val labelText = tag.dataElements.firstOrNull {
+ it is PsiDocToken && it.text?.trim()?.isNotEmpty() ?: false
+ }?.text ?: valueElement!!.text
+
+ val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\""
+ val link = "<a $linkTarget>$labelText</a>"
+ if (tag.name == "link") "<code>$link</code>" else link
+ } else if (valueElement != null) {
+ valueElement.text
+ } else {
+ ""
+ }
+ }
+ "code", "literal" -> {
+ val text = StringBuilder()
+ tag.dataElements.forEach { text.append(it.text) }
+ 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
+ }
+ "docRoot" -> {
+ // TODO: fix that
+ "https://developer.android.com/"
+ }
+ "sample" -> {
+ tag.text?.let { tagText ->
+ val (absolutePath, delimiter) = getSampleAnnotationInformation(tagText)
+ val code = retrieveCodeInFile(absolutePath, delimiter)
+ return if (code != null && code.isNotEmpty()) {
+ "<pre is-upgraded>$code</pre>"
+ } else {
+ ""
+ }
+ }
+ }
+
+ // Loads MathJax script from local source, which then updates MathJax HTML code
+ "usesMathJax" -> {
+ "<script src=\"/_static/js/managed/mathjax/MathJax.js?config=TeX-AMS_SVG\"></script>"
+ }
+
+ 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 }
+
+ private fun resolveExternalLink(valueElement: PsiElement?): String? {
+ val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return externalDocumentationLinkResolver.buildExternalDocumentationLink(target)
+ }
+ return null
+ }
+
+ private fun resolveInternalLink(valueElement: PsiElement?): String? {
+ val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return signatureProvider.signature(target)
+ }
+ return null
+ }
+
+ fun PsiDocTag.getSubjectName(): String? {
+ if (name == "param" || name == "throws" || name == "exception") {
+ return valueElement?.text
+ }
+ 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?.isNotEmpty() == true) {
+ 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
+ }
+
+ /**
+ * Returns information inside @sample
+ *
+ * Component1 is the absolute path to the file
+ * Component2 is the delimiter if exists in the file
+ */
+ private fun getSampleAnnotationInformation(tagText: String): Pair<String, String> {
+ val pathContent = tagText
+ .trim { it == '{' || it == '}' }
+ .removePrefix("@sample ")
+
+ val formattedPath = pathContent.substringBefore(" ").trim()
+ val potentialDelimiter = pathContent.substringAfterLast(" ").trim()
+
+ val delimiter = if (potentialDelimiter == formattedPath) "" else potentialDelimiter
+ val path = "samples/$formattedPath"
+
+ return Pair(path, delimiter)
+ }
+
+ /**
+ * Retrieves the code inside a file.
+ *
+ * If betweenTag is not empty, it retrieves the code between
+ * BEGIN_INCLUDE($betweenTag) and END_INCLUDE($betweenTag) comments.
+ *
+ * Also, the method will trim every line with the number of spaces in the first line
+ */
+ private fun retrieveCodeInFile(path: String, betweenTag: String = "") = StringBuilder().apply {
+ try {
+ if (betweenTag.isEmpty()) {
+ appendContent(path)
+ } else {
+ appendContentBetweenIncludes(path, betweenTag)
+ }
+ } catch (e: java.lang.Exception) {
+ logger.error("No file found when processing Java @sample. Path to sample: $path\n")
+ }
+ }
+
+ private fun StringBuilder.appendContent(path: String) {
+ val spaces = InitialSpaceIndent()
+ File(path).forEachLine {
+ appendWithoutInitialIndent(it, spaces)
+ }
+ }
+
+ private fun StringBuilder.appendContentBetweenIncludes(path: String, includeTag: String) {
+ var shouldAppend = false
+ val beginning = "BEGIN_INCLUDE($includeTag)"
+ val end = "END_INCLUDE($includeTag)"
+ val spaces = InitialSpaceIndent()
+ File(path).forEachLine {
+ if (shouldAppend) {
+ if (it.contains(end)) {
+ shouldAppend = false
+ } else {
+ appendWithoutInitialIndent(it, spaces)
+ }
+ } else {
+ if (it.contains(beginning)) shouldAppend = true
+ }
+ }
+ }
+
+ private fun StringBuilder.appendWithoutInitialIndent(it: String, spaces: InitialSpaceIndent) {
+ if (spaces.value == -1) {
+ spaces.value = (it.length - it.trimStart().length).coerceAtLeast(0)
+ appendln(it)
+ } else {
+ appendln(if (it.isBlank()) it else it.substring(spaces.value, it.length))
+ }
+ }
+
+ private data class InitialSpaceIndent(var value: Int = -1)
+}
diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
new file mode 100644
index 000000000..c60625a4a
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
@@ -0,0 +1,188 @@
+package org.jetbrains.dokka
+
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.html.entities.EntityConverter
+import org.intellij.markdown.parser.LinkMap
+import java.util.*
+
+class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) {
+ fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel)
+ fun resolve(href: String): ContentBlock = contentFactory(href)
+}
+
+fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent {
+ val result = MutableContent()
+ if (inline) {
+ buildInlineContentTo(tree, result, linkResolver)
+ } else {
+ buildContentTo(tree, result, linkResolver)
+ }
+ return result
+}
+
+fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+// println(tree.toTestString())
+ val nodeStack = ArrayDeque<ContentBlock>()
+ nodeStack.push(target)
+
+ tree.visit { node, processChildren ->
+ val parent = nodeStack.peek()
+
+ fun appendNodeWithChildren(content: ContentBlock) {
+ nodeStack.push(content)
+ processChildren()
+ parent.append(nodeStack.pop())
+ }
+
+ when (node.type) {
+ MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1))
+ MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2))
+ MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3))
+ MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4))
+ MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5))
+ MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6))
+ MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList())
+ MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList())
+ MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem())
+ MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis())
+ MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong())
+ MarkdownElementTypes.CODE_SPAN -> {
+ val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text
+ if (startDelimiter != null) {
+ val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter)
+ val codeSpan = ContentCode().apply { append(ContentText(text)) }
+ parent.append(codeSpan)
+ }
+ }
+ MarkdownElementTypes.CODE_BLOCK,
+ MarkdownElementTypes.CODE_FENCE -> {
+ val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: ""
+ appendNodeWithChildren(ContentBlockCode(language))
+ }
+ MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph())
+
+ MarkdownElementTypes.INLINE_LINK -> {
+ val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT)
+ val destination = node.child(MarkdownElementTypes.LINK_DESTINATION)
+ if (linkTextNode != null) {
+ if (destination != null) {
+ val link = ContentExternalLink(destination.text)
+ renderLinkTextTo(linkTextNode, link, linkResolver)
+ parent.append(link)
+ } else {
+ val link = ContentExternalLink(linkTextNode.getLabelText())
+ renderLinkTextTo(linkTextNode, link, linkResolver)
+ parent.append(link)
+ }
+ }
+ }
+ MarkdownElementTypes.SHORT_REFERENCE_LINK,
+ MarkdownElementTypes.FULL_REFERENCE_LINK -> {
+ val labelElement = node.child(MarkdownElementTypes.LINK_LABEL)
+ if (labelElement != null) {
+ val linkInfo = linkResolver.getLinkInfo(labelElement.text)
+ val labelText = labelElement.getLabelText()
+ val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText)
+ val linkText = node.child(MarkdownElementTypes.LINK_TEXT)
+ if (linkText != null) {
+ renderLinkTextTo(linkText, link, linkResolver)
+ } else {
+ link.append(ContentText(labelText))
+ }
+ parent.append(link)
+ }
+ }
+ MarkdownTokenTypes.WHITE_SPACE -> {
+ // Don't append first space if start of header (it is added during formatting later)
+ // v
+ // #### Some Heading
+ if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) {
+ parent.append(ContentText(node.text))
+ }
+ }
+ MarkdownTokenTypes.EOL -> {
+ if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) ||
+ // Keep extra blank lines when processing lists (affects Markdown formatting)
+ (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) {
+ parent.append(ContentText(node.text))
+ }
+ }
+
+ MarkdownTokenTypes.CODE_LINE -> {
+ val content = ContentText(node.text)
+ if (parent is ContentBlockCode) {
+ parent.append(content)
+ } else {
+ parent.append(ContentBlockCode().apply { append(content) })
+ }
+ }
+
+ MarkdownTokenTypes.TEXT -> {
+ fun createEntityOrText(text: String): ContentNode {
+ if (text == "&amp;" || text == "&quot;" || text == "&lt;" || text == "&gt;") {
+ return ContentEntity(text)
+ }
+ if (text == "&") {
+ return ContentEntity("&amp;")
+ }
+ val decodedText = EntityConverter.replaceEntities(text, true, true)
+ if (decodedText != text) {
+ return ContentEntity(text)
+ }
+ return ContentText(text)
+ }
+
+ parent.append(createEntityOrText(node.text))
+ }
+
+ MarkdownTokenTypes.EMPH -> {
+ val parentNodeType = node.parent?.type
+ if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) {
+ parent.append(ContentText(node.text))
+ }
+ }
+
+ MarkdownTokenTypes.COLON,
+ MarkdownTokenTypes.SINGLE_QUOTE,
+ MarkdownTokenTypes.DOUBLE_QUOTE,
+ MarkdownTokenTypes.LT,
+ MarkdownTokenTypes.GT,
+ MarkdownTokenTypes.LPAREN,
+ MarkdownTokenTypes.RPAREN,
+ MarkdownTokenTypes.LBRACKET,
+ MarkdownTokenTypes.RBRACKET,
+ MarkdownTokenTypes.EXCLAMATION_MARK,
+ MarkdownTokenTypes.BACKTICK,
+ MarkdownTokenTypes.CODE_FENCE_CONTENT -> {
+ parent.append(ContentText(node.text))
+ }
+
+ MarkdownElementTypes.LINK_DEFINITION -> {
+ }
+
+ else -> {
+ processChildren()
+ }
+ }
+ }
+}
+
+private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text }
+
+private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode
+private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList
+
+fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+ val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree)
+ inlineContent.forEach {
+ buildContentTo(it, target, linkResolver)
+ }
+}
+
+fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+ val linkTextNodes = tree.children.drop(1).dropLast(1)
+ linkTextNodes.forEach {
+ buildContentTo(it, target, linkResolver)
+ }
+}
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
new file mode 100644
index 000000000..d73bef4a5
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -0,0 +1,72 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
+import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
+
+class DeclarationLinkResolver
+ @Inject constructor(val resolutionFacade: DokkaResolutionFacade,
+ val refGraph: NodeReferenceGraph,
+ val logger: DokkaLogger,
+ val options: DocumentationOptions,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver,
+ val elementSignatureProvider: ElementSignatureProvider) {
+
+
+ fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? {
+ val symbol = try {
+ val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext,
+ resolutionFacade, fromDescriptor, null, href.split('.').toList())
+ findTargetSymbol(symbols)
+ } catch(e: Exception) {
+ null
+ }
+
+ // don't include unresolved links in generated doc
+ // assume that if an href doesn't contain '/', it's not an attempt to reference an external file
+ if (symbol != null) {
+ val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol)
+ if (externalHref != null) {
+ return ContentExternalLink(externalHref)
+ }
+ val signature = elementSignatureProvider.signature(symbol)
+ val referencedAt = fromDescriptor.signatureWithSourceLocation()
+
+ return ContentNodeLazyLink(href, { ->
+ val target = refGraph.lookup(signature)
+
+ if (target == null) {
+ logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt")
+ }
+ target
+ })
+ }
+ if ("/" in href) {
+ return ContentExternalLink(href)
+ }
+ return null
+ }
+
+ fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) =
+ tryResolveContentLink(fromDescriptor, href) ?: run {
+ logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}")
+ ContentExternalLink("#")
+ }
+
+ fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? {
+ if (symbols.isEmpty()) {
+ return null
+ }
+ val symbol = symbols.first()
+ if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
+ return symbol.overriddenDescriptors.firstOrNull()
+ }
+ if (symbol is TypeAliasDescriptor && !symbol.isDocumented(options)) {
+ return symbol.classDescriptor
+ }
+ return symbol
+ }
+
+}
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
new file mode 100644
index 000000000..098a17f97
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -0,0 +1,339 @@
+package org.jetbrains.dokka.Kotlin
+
+import com.google.inject.Inject
+import com.intellij.psi.PsiDocCommentOwner
+import com.intellij.psi.PsiNamedElement
+import com.intellij.psi.util.PsiTreeUtil
+import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Samples.SampleProcessingService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
+import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.annotations.argumentValue
+import org.jetbrains.kotlin.resolve.constants.StringValue
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
+import org.jetbrains.kotlin.resolve.source.PsiSourceElement
+import java.util.regex.Pattern
+
+private val REF_COMMAND = "ref"
+private val NAME_COMMAND = "name"
+private val DESCRIPTION_COMMAND = "description"
+private val TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL)
+private val NAME_TEXT = Pattern.compile("(\\S+)(.*)", Pattern.DOTALL)
+
+class DescriptorDocumentationParser @Inject constructor(
+ val options: DocumentationOptions,
+ val logger: DokkaLogger,
+ val linkResolver: DeclarationLinkResolver,
+ val resolutionFacade: DokkaResolutionFacade,
+ val refGraph: NodeReferenceGraph,
+ val sampleService: SampleProcessingService,
+ val signatureProvider: KotlinElementSignatureProvider,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+) {
+ fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content =
+ parseDocumentationAndDetails(descriptor, inline).first
+
+ fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
+ if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor ||
+ descriptor is EnumEntrySyntheticClassDescriptor) {
+ return parseJavadoc(descriptor)
+ }
+
+ val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor)
+ if (kdoc == null) {
+ if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() &&
+ descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor &&
+ descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) {
+ logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}")
+ }
+ return Content.Empty to { node -> }
+ }
+
+ val contextDescriptor =
+ (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration)
+ ?.takeIf { it != descriptor.original.sourcePsi() }
+ ?.resolveToDescriptorIfAny()
+ ?: descriptor
+
+ // This will build the initial node for all content above the tags, however we also sometimes have @Sample
+ // tags between content, so we handle that case below
+ var kdocText = kdoc.getContent()
+ // workaround for code fence parsing problem in IJ markdown parser
+ if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
+ kdocText += "\n"
+ }
+ val tree = parseMarkdown(kdocText)
+ val linkMap = LinkMap.buildLinkMap(tree.node, kdocText)
+ val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }), inline)
+ if (kdoc is KDocSection) {
+ val tags = kdoc.getTags()
+ tags.forEach {
+ when (it.knownTag) {
+ KDocKnownTag.SAMPLE -> {
+ content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it))
+ // If the sample tag has text below it, it will be considered as the child of the tag, so add it
+ val tagSubContent = it.getContent()
+ if (tagSubContent.isNotBlank()) {
+ val markdownNode = parseMarkdown(tagSubContent)
+ buildInlineContentTo(markdownNode, content, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }))
+ }
+ }
+ KDocKnownTag.SEE ->
+ content.addTagToSeeAlso(contextDescriptor, it)
+ KDocKnownTag.PARAM -> {
+ val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName())
+ section.append(ParameterInfoNode {
+ val signature = signatureProvider.signature(descriptor)
+ refGraph.lookupOrWarn(signature, logger)?.details?.find { node ->
+ node.kind == NodeKind.Parameter && node.name == it.getSubjectName()
+ }
+ })
+ val sectionContent = it.getContent()
+ val markdownNode = parseMarkdown(sectionContent)
+ buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }))
+ }
+ else -> {
+ val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName())
+ val sectionContent = it.getContent()
+ val markdownNode = parseMarkdown(sectionContent)
+ buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }))
+ }
+ }
+ }
+ }
+ return content to { node ->
+ if (kdoc is KDocSection) {
+ val tags = kdoc.getTags()
+ node.addExtraTags(tags, descriptor)
+ }
+ }
+ }
+
+ /**
+ * Adds @attr tag. There are 3 types of syntax for this:
+ * *@attr ref <android.>R.styleable.<attribute_name>
+ * *@attr name <attribute_name>
+ * *@attr description <attribute_description>
+ * This also adds the @since and @apiSince tags.
+ */
+ private fun DocumentationNode.addExtraTags(tags: Array<KDocTag>, descriptor: DeclarationDescriptor) {
+ tags.forEach {
+ val name = it.name
+ if (name?.toLowerCase() == "attr") {
+ it.getAttr(descriptor)?.let { append(it, RefKind.Detail) }
+ } else if (name?.toLowerCase() == "since" || name?.toLowerCase() == "apisince") {
+ val apiLevel = DocumentationNode(it.getContent(), Content.Empty, NodeKind.ApiLevel)
+ append(apiLevel, RefKind.Detail)
+ } else if (name?.toLowerCase() == "sdkextsince") {
+ val sdkExtSince = DocumentationNode(it.getContent(), Content.Empty, NodeKind.SdkExtSince)
+ append(sdkExtSince, RefKind.Detail)
+ } else if (name?.toLowerCase() == "deprecatedsince") {
+ val deprecatedLevel = DocumentationNode(it.getContent(), Content.Empty, NodeKind.DeprecatedLevel)
+ append(deprecatedLevel, RefKind.Detail)
+ } else if (name?.toLowerCase() == "artifactid") {
+ val artifactId = DocumentationNode(it.getContent(), Content.Empty, NodeKind.ArtifactId)
+ append(artifactId, RefKind.Detail)
+ }
+ }
+ }
+
+ private fun DeclarationDescriptor.isSuppressWarning(): Boolean {
+ val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
+ return if (suppressAnnotation != null) {
+ @Suppress("UNCHECKED_CAST")
+ (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
+ } else containingDeclaration?.isSuppressWarning() ?: false
+ }
+
+ /**
+ * Special case for generating stdlib documentation (the Any class to which the override chain will resolve
+ * is not the same one as the Any class included in the source scope).
+ */
+ fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? {
+ if (descriptor !is CallableMemberDescriptor) {
+ return null
+ }
+ val name = descriptor.name.asString()
+ if (name == "equals" || name == "hashCode" || name == "toString") {
+ var deepestDescriptor: CallableMemberDescriptor = descriptor
+ while (!deepestDescriptor.overriddenDescriptors.isEmpty()) {
+ deepestDescriptor = deepestDescriptor.overriddenDescriptors.first()
+ }
+ if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") {
+ val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors(
+ FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE)
+ anyClassDescriptors.forEach {
+ val anyMethod = (it as ClassDescriptor).getMemberScope(listOf())
+ .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS, { it == descriptor.name })
+ .single()
+ val kdoc = anyMethod.findKDoc()
+ if (kdoc != null) {
+ return kdoc
+ }
+ }
+ }
+ }
+ return null
+ }
+
+ 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,
+ signatureProvider,
+ externalDocumentationLinkResolver
+ ).parseDocumentation(psi as PsiNamedElement)
+ return parseResult.content to { node ->
+ parseResult.deprecatedContent?.let {
+ val deprecationNode = DocumentationNode("", it, NodeKind.Modifier)
+ node.append(deprecationNode, RefKind.Deprecation)
+ }
+ if (node.kind in NodeKind.classLike) {
+ parseResult.attributeRefs.forEach {
+ val signature = node.detailOrNull(NodeKind.Signature)
+ val signatureName = signature?.name
+ val classAttrSignature = "${signatureName}:$it"
+ refGraph.register(classAttrSignature, DocumentationNode(node.name, Content.Empty, NodeKind.Attribute))
+ refGraph.link(node, classAttrSignature, RefKind.Detail)
+ refGraph.link(classAttrSignature, node, RefKind.Owner)
+ refGraph.link(classAttrSignature, it, RefKind.AttributeRef)
+ }
+ } else if (node.kind in NodeKind.memberLike) {
+ parseResult.attributeRefs.forEach {
+ refGraph.link(node, it, RefKind.HiddenLink)
+ }
+ }
+ parseResult.apiLevel?.let {
+ node.append(it, RefKind.Detail)
+ }
+ parseResult.sdkExtSince?.let {
+ node.append(it, RefKind.Detail)
+ }
+ parseResult.deprecatedLevel?.let {
+ node.append(it, RefKind.Detail)
+ }
+ parseResult.artifactId?.let {
+ node.append(it, RefKind.Detail)
+ }
+ parseResult.attribute?.let {
+ val signature = node.detailOrNull(NodeKind.Signature)
+ val signatureName = signature?.name
+ val attrSignature = "AttrMain:$signatureName"
+ refGraph.register(attrSignature, it)
+ refGraph.link(attrSignature, node, RefKind.AttributeSource)
+ }
+ }
+ }
+ return Content.Empty to { _ -> }
+ }
+
+ fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java)
+ ?: arrayOf()
+
+ private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) {
+ addTagToSection(seeTag, descriptor, "See Also")
+ }
+
+ private fun MutableContent.addTagToSection(seeTag: KDocTag, descriptor: DeclarationDescriptor, sectionName: String) {
+ val subjectName = seeTag.getSubjectName()
+ if (subjectName != null) {
+ val section = findSectionByTag(sectionName) ?: addSection(sectionName, null)
+ val link = linkResolver.resolveContentLink(descriptor, subjectName)
+ link.append(ContentText(subjectName))
+ val para = ContentParagraph()
+ para.append(link)
+ section.append(para)
+ }
+ }
+
+ private fun KDocTag.getAttr(descriptor: DeclarationDescriptor): DocumentationNode? {
+ var attribute: DocumentationNode? = null
+ val matcher = TEXT.matcher(getContent())
+ if (matcher.matches()) {
+ val command = matcher.group(1)
+ val more = matcher.group(2)
+ attribute = when (command) {
+ REF_COMMAND -> {
+ val attrRef = more.trim()
+ val qualified = attrRef.split('.', '#')
+ val targetDescriptor = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, resolutionFacade, descriptor, this, qualified)
+ DocumentationNode(attrRef, Content.Empty, NodeKind.Attribute).also {
+ if (targetDescriptor.isNotEmpty()) {
+ refGraph.link(it, targetDescriptor.first().signature(), RefKind.Detail)
+ }
+ }
+ }
+ NAME_COMMAND -> {
+ val nameMatcher = NAME_TEXT.matcher(more)
+ if (nameMatcher.matches()) {
+ val attrName = nameMatcher.group(1)
+ DocumentationNode(attrName, Content.Empty, NodeKind.Attribute)
+ } else {
+ null
+ }
+ }
+ DESCRIPTION_COMMAND -> {
+ val attrDescription = more
+ DocumentationNode(attrDescription, Content.Empty, NodeKind.Attribute)
+ }
+ else -> null
+ }
+ }
+ return attribute
+ }
+
+}
+
+/**
+ * Lazily executed wrapper node holding a [NodeKind.Parameter] node that will be used to add type
+ * and default value information to
+ * [org.jetbrains.dokka.Formats.DevsiteLayoutHtmlFormatOutputBuilder].
+ *
+ * We make this a [ContentBlock] instead of a [ContentNode] so we won't fallback to calling
+ * [toString] on this and trying to add it to documentation somewhere - returning an empty list
+ * should make this a no-op.
+ *
+ * @property wrappedNode lazily executable lambda that will return the matching documentation node
+ * for this parameter (if it exists)
+ */
+class ParameterInfoNode(private val wrappedNode: () -> DocumentationNode?) : ContentBlock() {
+ private var computed = false
+
+ val parameterContent: NodeRenderContent?
+ get() = lazyNode
+
+ private var lazyNode: NodeRenderContent? = null
+ get() {
+ if (!computed) {
+ computed = true
+
+ val node = wrappedNode()
+ if (node != null) {
+ field = NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)
+ }
+ }
+ return field
+ }
+
+ override val children = arrayListOf<ContentNode>()
+}
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
new file mode 100644
index 000000000..b9fe8483e
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -0,0 +1,1177 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiJavaFile
+import org.jetbrains.dokka.DokkaConfiguration.*
+import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.annotations.Annotated
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
+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.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtModifierListOwner
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtVariableDeclaration
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.constants.ConstantValue
+import org.jetbrains.kotlin.resolve.descriptorUtil.*
+import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
+import org.jetbrains.kotlin.resolve.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.supertypes
+import org.jetbrains.kotlin.util.supertypesWithAny
+import java.io.File
+import java.nio.file.Path
+import java.nio.file.Paths
+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 generateClassIndexPage: Boolean = true,
+ val generatePackageIndexPage: 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,
+ noJdkLink: Boolean = false,
+ val languageVersion: String?,
+ val apiVersion: String?,
+ cacheRoot: String? = null,
+ val suppressedFiles: Set<File> = emptySet(),
+ val collectInheritedExtensionsFromLibraries: Boolean = false,
+ val outlineRoot: String = "",
+ val dacRoot: String = "") {
+ 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>()
+ //links += ExternalDocumentationLink.Builder("https://developer.android.com/reference/").build()
+ if (!noJdkLink)
+ links += 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 {
+ val extensionFunctionPackage = DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java)
+ val extensionReceiverPackage = DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java)
+ return extensionFunctionPackage != null && extensionReceiverPackage != null &&
+ extensionFunctionPackage.fqName != extensionReceiverPackage.fqName &&
+ extensionReceiverPackage.fqName !in allFqNames
+}
+
+interface PackageDocumentationBuilder {
+ fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
+ packageName: FqName,
+ packageNode: DocumentationNode,
+ declarations: List<DeclarationDescriptor>,
+ allFqNames: Collection<FqName>)
+}
+
+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 refGraph: NodeReferenceGraph,
+ val platformNodeRegistry: PlatformNodeRegistry,
+ val logger: DokkaLogger,
+ val linkResolver: DeclarationLinkResolver,
+ val defaultPlatformsProvider: DefaultPlatformsProvider) {
+ val boringBuiltinClasses = setOf(
+ "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean",
+ "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any")
+ 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)
+
+ fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) {
+ refGraph.link(node, descriptor.signature(), kind)
+ }
+
+ fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: RefKind) {
+ if (fromDescriptor != null && toDescriptor != null) {
+ refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind)
+ }
+ }
+
+ fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
+ refGraph.register(descriptor.signature(), node)
+ }
+
+ 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)
+ return node
+ }
+
+ private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode {
+ if (descriptor is MemberDescriptor) {
+ appendVisibility(descriptor)
+ if (descriptor !is ConstructorDescriptor) {
+ appendModality(descriptor)
+ }
+ }
+ return this
+ }
+
+ fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
+ var modality = descriptor.modality
+ if (modality == Modality.OPEN) {
+ val containingClass = descriptor.containingDeclaration as? ClassDescriptor
+ if (containingClass?.modality == Modality.FINAL) {
+ modality = Modality.FINAL
+ }
+ }
+ val modifier = modality.name.toLowerCase()
+ appendTextNode(modifier, NodeKind.Modifier)
+ }
+
+ fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
+ val modifier = descriptor.visibility.normalize().displayName
+ appendTextNode(modifier, NodeKind.Modifier)
+ }
+
+ fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) {
+ val unwrappedType = superType.unwrap()
+ if (unwrappedType is AbbreviatedType) {
+ appendSupertype(descriptor, unwrappedType.abbreviation, backref)
+ } else {
+ appendType(unwrappedType, NodeKind.Supertype)
+ val superclass = unwrappedType.constructor.declarationDescriptor
+ if (backref) {
+ link(superclass, descriptor, RefKind.Inheritor)
+ }
+ link(descriptor, superclass, RefKind.Superclass)
+ }
+ }
+
+ fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
+ if (projection.isStarProjection) {
+ appendTextNode("*", NodeKind.Type)
+ } else {
+ appendType(projection.type, kind, projection.projectionKind.label)
+ }
+ }
+
+ fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") {
+ if (kotlinType == null)
+ return
+ (kotlinType.unwrap() as? AbbreviatedType)?.let {
+ return appendType(it.abbreviation)
+ }
+
+ if (kotlinType.isDynamic()) {
+ append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail)
+ return
+ }
+
+ val classifierDescriptor = kotlinType.constructor.declarationDescriptor
+ val name = when (classifierDescriptor) {
+ is ClassDescriptor -> {
+ if (classifierDescriptor.isCompanionObject) {
+ classifierDescriptor.containingDeclaration.name.asString() +
+ "." + classifierDescriptor.name.asString()
+ } else {
+ classifierDescriptor.name.asString()
+ }
+ }
+ is Named -> classifierDescriptor.name.asString()
+ else -> "<anonymous>"
+ }
+ val node = DocumentationNode(name, Content.Empty, kind)
+ if (prefix != "") {
+ node.appendTextNode(prefix, NodeKind.Modifier)
+ }
+ if (kotlinType.isNullabilityFlexible()) {
+ node.appendTextNode("!", NodeKind.NullabilityModifier)
+ } else if (kotlinType.isMarkedNullable) {
+ node.appendTextNode("?", NodeKind.NullabilityModifier)
+ }
+ if (classifierDescriptor != null) {
+ val externalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ if (externalLink != null) {
+ 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)
+ }
+ }
+ 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) {
+ node.appendProjection(typeArgument)
+ }
+ }
+
+ fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean =
+ DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses
+
+ fun DocumentationNode.appendAnnotations(annotated: Annotated) {
+ annotated.annotations.forEach {
+ it.build()?.let { annotationNode ->
+ if (annotationNode.isSinceKotlin()) {
+ appendSinceKotlin(annotationNode)
+ }
+ else {
+ val refKind = when {
+ it.isDocumented() ->
+ when {
+ annotationNode.isDeprecation() -> RefKind.Deprecation
+ else -> RefKind.Annotation
+ }
+ it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation
+ else -> return@forEach
+ }
+ append(annotationNode, refKind)
+ if (refKind == RefKind.Deprecation) annotationNode.convertDeprecationDetailsToChildren()
+ }
+ }
+ }
+ }
+
+ 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)
+ }
+
+ fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
+ val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
+ KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ if (psi.hasModifier(it)) {
+ appendTextNode(it.value, NodeKind.Modifier)
+ }
+ }
+ }
+
+ fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) {
+ for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) {
+ append(platformNodeRegistry[platform], RefKind.Platform)
+ }
+ }
+
+ fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated"
+
+ fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation
+
+ fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
+ appendSourceLink(sourceElement.getPsi(), options.sourceLinks)
+ }
+
+ fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) {
+ appendTextNode(descriptor.signature(), NodeKind.Signature, RefKind.Detail)
+ }
+
+ fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? {
+ if (!descriptor.isGenerated() && descriptor.isDocumented(options)) {
+ val node = descriptor.build()
+ append(node, kind)
+ return node
+ }
+ return null
+ }
+
+ fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?:
+ DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply {
+ appendTextNode(signature, NodeKind.Signature, RefKind.Detail)
+ })
+ .also { groupNode ->
+ nodes.forEach { node ->
+ if (node != groupNode) {
+ node.owner?.let { owner ->
+ node.dropReferences { it.to == owner && it.kind == RefKind.Owner }
+ owner.dropReferences { it.to == node && it.kind == RefKind.Member }
+ owner.append(groupNode, RefKind.Member)
+ }
+ groupNode.append(node, RefKind.Member)
+ }
+ }
+ }
+
+
+ fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) {
+ if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return
+
+ val existingNode = refGraph.lookup(descriptor.signature())
+ if (existingNode != null) {
+ if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor
+ || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) {
+ val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build()))
+ register(descriptor, node)
+ return
+ }
+
+ existingNode.updatePlatforms(descriptor)
+
+ if (descriptor is ClassDescriptor) {
+ val membersToDocument = descriptor.collectMembersToDocument()
+ for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) {
+ if (memberDescriptor is ClassDescriptor) {
+ existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes
+ }
+ else {
+ val existingMemberNode = refGraph.lookup(memberDescriptor.signature())
+ if (existingMemberNode != null) {
+ existingMemberNode.updatePlatforms(memberDescriptor)
+ }
+ else {
+ existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier)
+ }
+ }
+ }
+ }
+ }
+ else {
+ appendChild(descriptor, RefKind.Member)
+ }
+ }
+
+ private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) {
+ for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) {
+ append(platformNodeRegistry[platform], RefKind.Platform)
+ }
+ }
+
+ fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor,
+ inheritedLinkKind: RefKind = RefKind.InheritedMember,
+ extraModifier: String?) {
+ if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
+ val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull()
+ if (baseDescriptor != null) {
+ link(this, baseDescriptor, inheritedLinkKind)
+ }
+ } else {
+ val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original
+ val child = appendChild(descriptorToUse, RefKind.Member)
+ if (extraModifier != null) {
+ child?.appendTextNode("static", NodeKind.Modifier)
+ }
+ }
+ }
+
+ fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) {
+ descriptors.forEach { descriptor ->
+ val node = appendChild(descriptor, kind)
+ node?.addReferenceTo(this, RefKind.TopLevelPage)
+ }
+ }
+
+ fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>,
+ packageContent: Map<String, Content>,
+ packageDocumentationBuilder: PackageDocumentationBuilder) {
+ val allFqNames = fragments.map { it.fqName }.distinct()
+
+ for (packageName in allFqNames) {
+ if (packageName.isRoot && !options.includeRootPackage) continue
+ val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() }
+
+ if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue
+ logger.info(" package $packageName: ${declarations.count()} declarations")
+ val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
+ packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode,
+ declarations, allFqNames)
+ }
+
+ }
+
+ 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 (options.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
+ .filterIsInstance<CallableMemberDescriptor>()
+ .filter { it.extensionReceiverParameter != null }
+ val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
+
+ for (extensionFunction in allExtensionFunctions) {
+ if (extensionFunction.dispatchReceiverParameter != null) continue
+ val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
+ ?.filter { fn -> fn.canShadow(extensionFunction) }
+ ?: emptyList()
+
+ if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) 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) }) {
+
+ 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)
+ }
+ }
+ }
+ }
+
+ private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
+ 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>> {
+ val result = hashMapOf<ClassDescriptor, MutableList<ClassDescriptor>>()
+ classes.forEach { cls ->
+ TypeUtils.getAllSupertypes(cls.defaultType).forEach { supertype ->
+ val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor
+ if (classDescriptor != null) {
+ val subtypesList = result.getOrPut(classDescriptor) { arrayListOf() }
+ subtypesList.add(cls)
+ }
+ }
+ }
+ return result
+ }
+
+ private fun CallableMemberDescriptor.canShadow(other: CallableMemberDescriptor): Boolean {
+ if (this == other) return false
+ if (this is PropertyDescriptor && other is PropertyDescriptor) {
+ return true
+ }
+ if (this is FunctionDescriptor && other is FunctionDescriptor) {
+ val parameters1 = valueParameters
+ val parameters2 = other.valueParameters
+ if (parameters1.size != parameters2.size) {
+ return false
+ }
+ for ((p1, p2) in parameters1 zip parameters2) {
+ if (p1.type != p2.type) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+ }
+
+ fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
+ is ClassifierDescriptor -> build()
+ is ConstructorDescriptor -> build()
+ is PropertyDescriptor -> build()
+ is FunctionDescriptor -> build()
+ is ValueParameterDescriptor -> build()
+ is ReceiverParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ 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)
+
+ if (!external) {
+ node.appendAnnotations(this)
+ }
+ node.appendModifiers(this)
+ node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
+
+ node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
+
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
+ val kind = when {
+ kind == ClassKind.OBJECT -> NodeKind.Object
+ kind == ClassKind.INTERFACE -> NodeKind.Interface
+ kind == ClassKind.ENUM_CLASS -> NodeKind.Enum
+ kind == ClassKind.ANNOTATION_CLASS -> NodeKind.AnnotationClass
+ kind == ClassKind.ENUM_ENTRY -> NodeKind.EnumItem
+ isSubclassOfThrowable() -> NodeKind.Exception
+ else -> NodeKind.Class
+ }
+ 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)
+ }
+ if (!external) {
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
+ }
+ node.appendModifiers(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
+ return node
+ }
+
+ data class ClassMember(val descriptor: DeclarationDescriptor,
+ val inheritedLinkKind: RefKind = RefKind.InheritedMember,
+ val extraModifier: String? = null)
+
+ fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> {
+ val result = arrayListOf<ClassMember>()
+ if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
+ val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS)
+ constructors.filter { it.valueParameters.size > 0 }
+ else
+ constructors
+ constructorsToDocument.mapTo(result) { ClassMember(it) }
+ }
+
+ defaultType.memberScope.getContributedDescriptors()
+ .filter { it != companionObjectDescriptor }
+ .mapTo(result) { ClassMember(it) }
+
+ staticScope.getContributedDescriptors()
+ .mapTo(result) { ClassMember(it, extraModifier = "static") }
+
+ val companionObjectDescriptor = companionObjectDescriptor
+ if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(options)) {
+ val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()
+ val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() }
+ descriptorsToDocument.mapTo(result) {
+ ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember)
+ }
+
+ if (companionObjectDescriptor.getAllSuperclassesWithoutAny().isNotEmpty()
+ || companionObjectDescriptor.getSuperInterfaces().isNotEmpty()) {
+ result += ClassMember(companionObjectDescriptor)
+ }
+ }
+ return result
+ }
+
+ fun CallableDescriptor.isInheritedFromAny(): Boolean {
+ return findTopMostOverriddenDescriptors().any {
+ DescriptorUtils.getFqNameSafe(it.containingDeclaration).asString() == "kotlin.Any"
+ }
+ }
+
+ fun ClassDescriptor.isSubclassOfThrowable(): Boolean =
+ defaultType.supertypes().any { it.constructor.declarationDescriptor == builtIns.throwable }
+
+ fun ConstructorDescriptor.build(): DocumentationNode {
+ val node = nodeForDescriptor(this, NodeKind.Constructor)
+ node.appendInPageChildren(valueParameters, RefKind.Detail)
+ node.appendDefaultPlatforms(this)
+ register(this, node)
+ return node
+ }
+
+ private fun CallableMemberDescriptor.inCompanionObject(): Boolean {
+ val containingDeclaration = containingDeclaration
+ if ((containingDeclaration as? ClassDescriptor)?.isCompanionObject ?: false) {
+ return true
+ }
+ val receiver = extensionReceiverParameter
+ return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
+ }
+
+ fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
+ if (ErrorUtils.containsErrorTypeInParameters(this) || ErrorUtils.containsErrorType(this.returnType)) {
+ logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
+ }
+
+ 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)
+ node.appendAnnotations(this)
+ node.appendModifiers(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ } else {
+ node.appendExternalLink(this)
+ }
+
+ overriddenDescriptors.forEach {
+ addOverrideLink(it, this)
+ }
+
+ register(this, node)
+ return node
+ }
+
+ fun addOverrideLink(baseClassFunction: CallableMemberDescriptor, overridingFunction: CallableMemberDescriptor) {
+ val source = baseClassFunction.original.source.getPsi()
+ if (source != null) {
+ link(overridingFunction, baseClassFunction, RefKind.Override)
+ } else {
+ baseClassFunction.overriddenDescriptors.forEach {
+ addOverrideLink(it, overridingFunction)
+ }
+ }
+ }
+
+ fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+ val node = nodeForDescriptor(
+ this,
+ if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+ external
+ )
+ node.appendInPageChildren(typeParameters, RefKind.Detail)
+ extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
+ node.appendType(returnType)
+ node.appendAnnotations(this)
+ node.appendModifiers(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ if (isVar) {
+ node.appendTextNode("var", NodeKind.Modifier)
+ }
+
+ if (isConst) {
+ val psi = sourcePsi()
+ val valueText = when (psi) {
+ is KtVariableDeclaration -> psi.initializer?.text
+ is PsiField -> psi.initializer?.text
+ else -> null
+ }
+ valueText?.let { node.appendTextNode(it, NodeKind.Value) }
+ }
+
+
+ getter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ }
+ }
+ setter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+ }
+ }
+ node.appendDefaultPlatforms(this)
+ }
+ if (external) {
+ node.appendExternalLink(this)
+ }
+
+ overriddenDescriptors.forEach {
+ addOverrideLink(it, this)
+ }
+
+ register(this, node)
+ return node
+ }
+
+ fun DocumentationNode.addAccessorDocumentation(documentation: Content, prefix: String) {
+ if (documentation == Content.Empty) return
+ updateContent {
+ if (!documentation.children.isEmpty()) {
+ val section = addSection(prefix, null)
+ documentation.children.forEach { section.append(it) }
+ }
+ documentation.sections.forEach {
+ val section = addSection("$prefix ${it.tag}", it.subjectName)
+ it.children.forEach { section.append(it) }
+ }
+ }
+ }
+
+ fun ValueParameterDescriptor.build(): DocumentationNode {
+ val node = nodeForDescriptor(this, NodeKind.Parameter)
+ node.appendType(varargElementType ?: type)
+ if (declaresDefaultValue()) {
+ val psi = source.getPsi() as? KtParameter
+ if (psi != null) {
+ val defaultValueText = psi.defaultValue?.text
+ if (defaultValueText != null) {
+ node.appendTextNode(defaultValueText, NodeKind.Value)
+ }
+ }
+ }
+ node.appendAnnotations(this)
+ node.appendModifiers(this)
+ if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) {
+ node.appendTextNode("vararg", NodeKind.Modifier)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun TypeParameterDescriptor.build(): DocumentationNode {
+ val doc = descriptorDocumentationParser.parseDocumentation(this)
+ val name = name.asString()
+ val prefix = variance.label
+
+ val node = DocumentationNode(name, doc, NodeKind.TypeParameter)
+ if (prefix != "") {
+ node.appendTextNode(prefix, NodeKind.Modifier)
+ }
+ if (isReified) {
+ node.appendTextNode("reified", NodeKind.Modifier)
+ }
+
+ for (constraint in upperBounds) {
+ if (KotlinBuiltIns.isDefaultBound(constraint)) {
+ continue
+ }
+ node.appendType(constraint, NodeKind.UpperBound)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun ReceiverParameterDescriptor.build(): DocumentationNode {
+ var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!!
+ if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) {
+ receiverClass = receiverClass.containingDeclaration!!
+ } else if (receiverClass is TypeParameterDescriptor) {
+ val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor
+ if (upperBoundClass != null) {
+ receiverClass = upperBoundClass
+ }
+ }
+
+ if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) {
+ link(receiverClass, containingDeclaration, RefKind.Extension)
+ }
+
+ val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver)
+ node.appendType(type)
+ register(this, node)
+ return node
+ }
+
+ fun AnnotationDescriptor.build(isWithinReplaceWith: Boolean = false): DocumentationNode? {
+ val annotationClass = type.constructor.declarationDescriptor
+ if (annotationClass == null || ErrorUtils.isError(annotationClass)) {
+ return null
+ }
+ val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, NodeKind.Annotation)
+ allValueArguments.forEach foreach@{ (name, value) ->
+ if (name.toString() == "imports" && value.toString() == "[]") return@foreach
+ var valueNode: DocumentationNode? = null
+ if (value.toString() == "@kotlin.ReplaceWith") {
+ valueNode = (value.value as AnnotationDescriptor).build(true)
+ }
+ else valueNode = value.toDocumentationNode(isWithinReplaceWith)
+ if (valueNode != null) {
+ val paramNode = DocumentationNode(name.asString(), Content.Empty, NodeKind.Parameter)
+ paramNode.append(valueNode, RefKind.Detail)
+ node.append(paramNode, RefKind.Detail)
+ }
+ }
+ return node
+ }
+
+ fun ConstantValue<*>.toDocumentationNode(isWithinReplaceWith: Boolean = false): DocumentationNode? = value?.let { value ->
+ when (value) {
+ is String ->
+ (if (isWithinReplaceWith) "Replace with: " else "") + "\"" + StringUtil.escapeStringCharacters(value) + "\""
+ is EnumEntrySyntheticClassDescriptor ->
+ value.containingDeclaration.name.asString() + "." + value.name.asString()
+ is Pair<*, *> -> {
+ val (classId, name) = value
+ if (classId is ClassId && name is Name) {
+ classId.shortClassName.asString() + "." + name.asString()
+ } else {
+ value.toString()
+ }
+ }
+ else -> value.toString()
+ }.let { valueString ->
+ DocumentationNode(valueString, Content.Empty, NodeKind.Value)
+ }
+ }
+
+
+ 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)
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor)
+ if (externalLink != null) {
+ newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ append(newNode, RefKind.Member)
+ newNode
+ })
+ }
+ }
+ return this
+ }
+
+}
+
+fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean {
+ return (options.effectivePackageOptions(fqNameSafe).includeNonPublic
+ || this !is MemberDescriptor
+ || this.visibility.isPublicAPI)
+ && !isDocumentationSuppressed(options)
+ && (!options.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
+}
+
+private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION
+
+class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
+ override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
+ packageName: FqName,
+ packageNode: DocumentationNode,
+ declarations: List<DeclarationDescriptor>,
+ allFqNames: Collection<FqName>) {
+ val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
+ declarations.forEach { descriptor ->
+ with(documentationBuilder) {
+ if (descriptor.isDocumented(options)) {
+ val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames)
+ parent.appendOrUpdateMember(descriptor)
+ }
+ }
+ }
+ }
+}
+
+class KotlinJavaDocumentationBuilder
+@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
+ val documentationBuilder: DocumentationBuilder,
+ val options: DocumentationOptions,
+ val logger: DokkaLogger) : JavaDocumentationBuilder {
+ override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
+ val classDescriptors = file.classes.map {
+ it.getJavaClassDescriptor(resolutionFacade)
+ }
+
+ if (classDescriptors.any { it != null && it.isDocumented(options) }) {
+ val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph)
+
+ for (descriptor in classDescriptors.filterNotNull()) {
+ with(documentationBuilder) {
+ packageNode.appendChild(descriptor, RefKind.Member)
+ }
+ }
+ }
+ }
+}
+
+private val hiddenAnnotations = setOf(
+ KotlinBuiltIns.FQ_NAMES.parameterName.asString()
+)
+
+private fun AnnotationDescriptor.isHiddenInDocumentation() =
+ type.constructor.declarationDescriptor?.fqNameSafe?.asString() in hiddenAnnotations
+
+private fun AnnotationDescriptor.isDocumented(): Boolean {
+ if (source.getPsi() != null && mustBeDocumented()) return true
+ val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString()
+ return annotationClassName == KotlinBuiltIns.FQ_NAMES.extensionFunctionType.asString()
+}
+
+fun AnnotationDescriptor.mustBeDocumented(): Boolean {
+ val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false
+ return annotationClass.isDocumentedAnnotation()
+}
+
+fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOptions): Boolean {
+
+ if (options.effectivePackageOptions(fqNameSafe).suppress) return true
+
+ val path = this.findPsi()?.containingFile?.virtualFile?.path
+ if (path != null) {
+ if (File(path).absoluteFile in options.suppressedFiles) return true
+ }
+
+ val doc = findKDoc()
+ if (doc is KDocSection && doc.findTagByName("suppress") != null) return true
+
+ return hasSuppressDocTag(sourcePsi()) || hasHideAnnotation(sourcePsi())
+}
+
+fun DeclarationDescriptor.sourcePsi() =
+ ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
+
+fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any {
+ DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated"
+} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated())
+
+fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
+ val extensionReceiver = extensionReceiverParameter
+ if (extensionReceiver != null) {
+ val type = extensionReceiver.type
+ val receiverClass = type.constructor.declarationDescriptor as? ClassDescriptor
+ if (receiverClass?.isCompanionObject ?: false) {
+ return receiverClass?.containingDeclaration as? ClassifierDescriptor
+ }
+ return receiverClass
+ }
+ return null
+}
+
+fun DeclarationDescriptor.signature(): String {
+ if (this != original) return original.signature()
+ return when (this) {
+ is ClassDescriptor,
+ is PackageFragmentDescriptor,
+ is PackageViewDescriptor,
+ is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString()
+
+ is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature()
+ is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature()
+ is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name
+ is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name
+ is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name
+ else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this")
+ }
+}
+
+fun PropertyDescriptor.receiverSignature(): String {
+ val receiver = extensionReceiverParameter
+ if (receiver != null) {
+ return "#" + receiver.type.signature()
+ }
+ return ""
+}
+
+fun CallableMemberDescriptor.parameterSignature(): String {
+ val params = valueParameters.map { it.type }.toMutableList()
+ val extensionReceiver = extensionReceiverParameter
+ if (extensionReceiver != null) {
+ params.add(0, extensionReceiver.type)
+ }
+ return params.joinToString(prefix = "(", postfix = ")") { it.signature() }
+}
+
+fun KotlinType.signature(): String {
+ val visited = hashSetOf<KotlinType>()
+
+ fun KotlinType.signatureRecursive(): String {
+ if (this in visited) {
+ return ""
+ }
+ visited.add(this)
+
+ val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>"
+ val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString()
+ if (arguments.isEmpty()) {
+ return typeName
+ }
+ return typeName + arguments.joinToString(prefix = "((", postfix = "))") { it.type.signatureRecursive() }
+ }
+
+ return signatureRecursive()
+}
+
+fun DeclarationDescriptor.signatureWithSourceLocation(): String {
+ val signature = signature()
+ val sourceLocation = sourceLocation()
+ return if (sourceLocation != null) "$signature ($sourceLocation)" else signature
+}
+
+fun DeclarationDescriptor.sourceLocation(): String? {
+ val psi = sourcePsi()
+ if (psi != null) {
+ val fileName = psi.containingFile.name
+ val lineNumber = psi.lineNumber()
+ return if (lineNumber != null) "$fileName:$lineNumber" else fileName
+ }
+ return null
+}
+
+fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
+ if (options.generateClassIndexPage) {
+ generateAllTypesNode()
+ }
+ nodeRefGraph.resolveReferences()
+}
+
+fun DocumentationNode.generateAllTypesNode() {
+ val allTypes = members(NodeKind.Package)
+ .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } }
+ .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() }
+
+ val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
+ for (typeNode in allTypes) {
+ allTypesNode.addReferenceTo(typeNode, RefKind.Member)
+ }
+
+ append(allTypesNode, RefKind.Member)
+}
+
+fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> {
+ if (KotlinBuiltIns.isAny(this)) {
+ return emptyList()
+ }
+ return typeConstructor.supertypesWithAny()
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
new file mode 100644
index 000000000..d09bc1c9f
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -0,0 +1,258 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import com.intellij.util.io.*
+import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor
+import org.jetbrains.dokka.Formats.FormatDescriptor
+import org.jetbrains.dokka.Utilities.ServiceLocator
+import org.jetbrains.dokka.Utilities.lookup
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.*
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.parents
+import java.io.ByteArrayOutputStream
+import java.io.PrintWriter
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+import java.nio.file.Path
+import java.security.MessageDigest
+import javax.inject.Named
+import kotlin.reflect.full.findAnnotation
+
+fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) }
+
+@Singleton
+class ExternalDocumentationLinkResolver @Inject constructor(
+ val options: DocumentationOptions,
+ @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade,
+ val logger: DokkaLogger
+) {
+
+ val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
+ val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+
+ class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) {
+ override fun toString(): String = rootUrl.toString()
+ }
+
+ val cacheDir: Path? = options.cacheRoot?.resolve("packageListCache")?.apply { toFile().mkdirs() }
+
+ val cachedProtocols = setOf("http", "https", "ftp")
+
+ fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
+ val connection = this.openConnection()
+ connection.connectTimeout = timeout
+ connection.readTimeout = timeout
+
+ when (connection) {
+ is HttpURLConnection -> {
+ return when (connection.responseCode) {
+ in 200..299 -> {
+ connection
+ }
+ HttpURLConnection.HTTP_MOVED_PERM,
+ HttpURLConnection.HTTP_MOVED_TEMP,
+ HttpURLConnection.HTTP_SEE_OTHER -> {
+ if (redirectsAllowed > 0) {
+ val newUrl = connection.getHeaderField("Location")
+ URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1)
+ } else {
+ throw RuntimeException("Too many redirects")
+ }
+ }
+ else -> {
+ throw RuntimeException("Unhandled http code: ${connection.responseCode}")
+ }
+ }
+ }
+ else -> return connection
+ }
+ }
+
+ fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) {
+
+ val packageListUrl = link.packageListUrl
+ val needsCache = packageListUrl.protocol in cachedProtocols
+
+ val packageListStream = if (cacheDir != null && needsCache) {
+ val packageListLink = packageListUrl.toExternalForm()
+
+ val digest = MessageDigest.getInstance("SHA-256")
+ val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString()
+ val cacheEntry = cacheDir.resolve(hash).toFile()
+
+ if (cacheEntry.exists()) {
+ try {
+ val connection = packageListUrl.doOpenConnectionToReadContent()
+ val originModifiedDate = connection.date
+ val cacheDate = cacheEntry.lastModified()
+ if (originModifiedDate > cacheDate || originModifiedDate == 0L) {
+ if (originModifiedDate == 0L)
+ logger.warn("No date header for $packageListUrl, downloading anyway")
+ else
+ logger.info("Renewing package-list from $packageListUrl")
+ connection.getInputStream().copyTo(cacheEntry.outputStream())
+ }
+ } catch (e: Exception) {
+ logger.error("Failed to update package-list cache for $link")
+ val baos = ByteArrayOutputStream()
+ PrintWriter(baos).use {
+ e.printStackTrace(it)
+ }
+ baos.flush()
+ logger.error(baos.toString())
+ }
+ } else {
+ logger.info("Downloading package-list from $packageListUrl")
+ packageListUrl.openStream().copyTo(cacheEntry.outputStream())
+ }
+ cacheEntry.inputStream()
+ } else {
+ packageListUrl.doOpenConnectionToReadContent().getInputStream()
+ }
+
+ val (params, packages) =
+ packageListStream
+ .bufferedReader()
+ .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
+
+ val paramsMap = params.asSequence()
+ .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
+ .groupBy({ (key, _) -> key }, { (_, value) -> value })
+
+ val format = paramsMap["format"]?.singleOrNull() ?: "javadoc"
+
+ val locations = paramsMap["location"].orEmpty()
+ .map { it.split("\u001f", limit = 2) }
+ .map { (key, value) -> key to value }
+ .toMap()
+
+
+ val defaultResolverDesc = services["dokka-default"]!!
+ val resolverDesc = 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 }
+ }
+
+ init {
+ options.externalDocumentationLinks.forEach {
+ try {
+ loadPackageList(it)
+ } catch (e: Exception) {
+ throw RuntimeException("Exception while loading package-list from $it", e)
+ }
+ }
+ }
+
+ fun buildExternalDocumentationLink(element: PsiElement): String? {
+ return element.extractDescriptor(libraryResolutionFacade)?.let {
+ buildExternalDocumentationLink(it)
+ }
+ }
+
+ fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
+ val packageFqName: FqName =
+ when (symbol) {
+ is PackageFragmentDescriptor -> symbol.fqName
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
+ else -> return null
+ }
+
+ val externalLocation = packageFqNameToLocation[packageFqName] ?: return null
+
+ val path = externalLocation.locations[symbol.signature()] ?:
+ externalLocation.resolver.getPath(symbol) ?: return null
+
+ return URL(externalLocation.rootUrl, path).toExternalForm()
+ }
+
+ companion object {
+ const val DOKKA_PARAM_PREFIX = "\$dokka."
+ val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name }
+ private val formatsWithDefaultResolver =
+ ServiceLocator
+ .allServices("format")
+ .filter {
+ val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor
+ desc?.generatorServiceClass == FileGenerator::class
+ }.map { it.name }
+ .toSet()
+ }
+}
+
+
+interface InboundExternalLinkResolutionService {
+ fun getPath(symbol: DeclarationDescriptor): String?
+
+ class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ override fun getPath(symbol: DeclarationDescriptor): String? {
+ if (symbol is EnumEntrySyntheticClassDescriptor) {
+ return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() }
+ } else if (symbol is ClassDescriptor) {
+ return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html"
+ } else if (symbol is JavaCallableMemberDescriptor) {
+ val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
+ val containingClassLink = getPath(containingClass)
+ if (containingClassLink != null) {
+ if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) {
+ val psi = symbol.sourcePsi() as? PsiMethod
+ if (psi != null) {
+ val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
+ return containingClassLink + "#" + symbol.name + "(" + params + ")"
+ }
+ } else if (symbol is JavaPropertyDescriptor) {
+ return "$containingClassLink#${symbol.name}"
+ }
+ }
+ }
+ // TODO Kotlin javadoc
+ return null
+ }
+ }
+
+ class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver")
+
+ override fun getPath(symbol: DeclarationDescriptor): String? {
+ val leafElement = when (symbol) {
+ is CallableDescriptor, is TypeAliasDescriptor -> true
+ else -> false
+ }
+ val path = getPathWithoutExtension(symbol)
+ if (leafElement) return "$path.$extension"
+ else return "$path/index.$extension"
+ }
+
+ private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String {
+ return when {
+ symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString())
+ symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString())
+ else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString())
+ }
+ }
+
+ }
+}
+
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
new file mode 100644
index 000000000..c5fb15385
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -0,0 +1,68 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.intellij.psi.JavaPsiFacade
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiNamedElement
+import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
+import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtPropertyAccessor
+
+class KotlinAsJavaDocumentationBuilder
+ @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder
+{
+ override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
+ packageName: FqName,
+ packageNode: DocumentationNode,
+ declarations: List<DeclarationDescriptor>,
+ allFqNames: Collection<FqName>) {
+ val project = documentationBuilder.resolutionFacade.project
+ val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString())
+ if (psiPackage == null) {
+ documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}")
+ return
+ }
+
+ val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.options,
+ documentationBuilder.refGraph,
+ kotlinAsJavaDocumentationParser,
+ documentationBuilder.linkResolver.externalDocumentationLinkResolver
+ )
+
+ psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach {
+ javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it))
+ }
+ }
+
+ fun PsiClass.isVisibleInDocumentation(): Boolean {
+ val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true
+
+ return origin.hasModifier(KtTokens.INTERNAL_KEYWORD) != true &&
+ origin.hasModifier(KtTokens.PRIVATE_KEYWORD) != true
+ }
+}
+
+class KotlinAsJavaDocumentationParser
+ @Inject constructor(val resolutionFacade: DokkaResolutionFacade,
+ val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser
+{
+ override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
+ val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty
+ val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty
+ if (origin is KtParameter) {
+ // LazyDeclarationResolver does not support setter parameters
+ val grandFather = origin.parent?.parent
+ if (grandFather is KtPropertyAccessor) {
+ return JavadocParseResult.Empty
+ }
+ }
+ val descriptor = resolutionFacade.resolveToDescriptor(origin)
+ val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter)
+ return JavadocParseResult(content, null, emptyList(), null, null)
+ }
+}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
new file mode 100644
index 000000000..20ea179ee
--- /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/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
new file mode 100644
index 000000000..bcac01829
--- /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
new file mode 100644
index 000000000..8a33ff8b7
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -0,0 +1,463 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.classNodeNameWithOuterClass
+import org.jetbrains.dokka.LanguageService.RenderMode
+
+/**
+ * Implements [LanguageService] and provides rendering of symbols in Kotlin language
+ */
+class KotlinLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return node.name !in fullOnlyModifiers
+ }
+
+ private val fullOnlyModifiers =
+ setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
+
+ override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
+ return content {
+ when (node.kind) {
+ NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node)
+ in NodeKind.classLike -> renderClass(node, renderMode)
+
+ NodeKind.EnumItem -> renderClass(node, renderMode)
+ 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(this, node, renderMode)
+ NodeKind.Constructor,
+ NodeKind.Function,
+ NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
+ NodeKind.Property,
+ NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode)
+ else -> identifier(node.name)
+ }
+ }
+ }
+
+
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
+ if (nodes.size < 2) return null
+ val receiverKind = nodes.getReceiverKind() ?: return null
+ val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null
+ 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)
+ )
+ }
+ }
+ }
+
+ private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? {
+ val qNames = map { it.getReceiverQName() }.filterNotNull()
+ if (qNames.size != size)
+ return null
+
+ return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } }
+ }
+
+ private fun DocumentationNode.getReceiverQName(): String? {
+ if (kind != NodeKind.Function && kind != NodeKind.Property) return null
+ val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null
+ return receiver.detail(NodeKind.Type).qualifiedNameFromType()
+ }
+
+ companion object {
+ private val arrayClasses = setOf(
+ "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
+ }
+
+ private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
+ ARRAY("any_array", arrayClasses),
+ ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses),
+ ITERABLE("any_iterable", iterableClasses),
+ }
+
+ interface SignatureMapper {
+ fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
+ }
+
+ 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.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()
+ }
+ }
+
+ private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
+ var typeArguments = node.details(NodeKind.Type)
+
+ if (node.name.startsWith("Suspend")) {
+ keyword("suspend ")
+ }
+
+ // lambda
+ val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" }
+ if (isExtension) {
+ renderType(typeArguments.first(), renderMode)
+ symbol(".")
+ typeArguments = typeArguments.drop(1)
+ }
+ symbol("(")
+ renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
+ renderFunctionalTypeParameterName(it, renderMode)
+ renderType(it, renderMode)
+ }
+ symbol(")")
+ nbsp()
+ symbol("->")
+ nbsp()
+ renderType(typeArguments.last(), renderMode)
+
+ }
+
+ private fun DocumentationNode.isFunctionalType(): Boolean {
+ val typeArguments = details(NodeKind.Type)
+ val functionalTypeName = "Function${typeArguments.count() - 1}"
+ val suspendFunctionalTypeName = "Suspend$functionalTypeName"
+ return name == functionalTypeName || name == suspendFunctionalTypeName
+ }
+
+ private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
+ if (node.name == "dynamic") {
+ keyword("dynamic")
+ return
+ }
+ if (node.isFunctionalType()) {
+ renderFunctionalType(node, renderMode)
+ return
+ }
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode, true)
+ renderLinked(this, node) {
+ identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
+ }
+ val typeArguments = node.details(NodeKind.Type)
+ if (typeArguments.isNotEmpty()) {
+ symbol("<")
+ renderList(typeArguments, noWrap = true) {
+ renderType(it, renderMode)
+ }
+ symbol(">")
+ }
+ val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull()
+ if (nullabilityModifier != null) {
+ symbol(nullabilityModifier.name)
+ }
+ }
+
+ override fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean
+ ) {
+ when (node.name) {
+ "final", "public", "var" -> {
+ }
+ else -> {
+ if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
+ super.renderModifier(block, node, renderMode, nowrap)
+ }
+ }
+ }
+ }
+
+ private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode, true)
+
+ identifier(node.name)
+
+ val constraints = node.details(NodeKind.UpperBound)
+ if (constraints.size == 1) {
+ nbsp()
+ symbol(":")
+ nbsp()
+ renderList(constraints, noWrap = true) {
+ renderType(it, renderMode)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode)
+ identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name)
+ symbol(":")
+ nbsp()
+ val parameterType = node.detail(NodeKind.Type)
+ renderType(parameterType, renderMode)
+ val valueNode = node.details(NodeKind.Value).firstOrNull()
+ if (valueNode != null) {
+ nbsp()
+ symbol("=")
+ nbsp()
+ text(valueNode.name)
+ }
+ }
+
+ private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) {
+ val typeParameters = node.details(NodeKind.TypeParameter)
+ if (typeParameters.any()) {
+ symbol("<")
+ renderList(typeParameters) {
+ renderTypeParameter(it, renderMode)
+ }
+ symbol(">")
+ }
+ }
+
+ private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
+ 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 }
+ }
+ if (parametersWithMultipleConstraints.isNotEmpty()) {
+ keyword(" where ")
+ renderList(parametersWithConstraints) {
+ identifier(it.first.name)
+ nbsp()
+ symbol(":")
+ nbsp()
+ renderType(it.second, renderMode)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
+ val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
+ if (supertypes.any()) {
+ nbsp()
+ symbol(":")
+ nbsp()
+ renderList(supertypes) {
+ indentedSoftLineBreak()
+ renderType(it, renderMode)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
+ node.annotations.forEach {
+ renderAnnotation(it)
+ }
+ }
+
+ private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
+ identifier("@" + node.name, IdentifierKind.AnnotationName)
+ val parameters = node.details(NodeKind.Parameter)
+ if (!parameters.isEmpty()) {
+ symbol("(")
+ renderList(parameters) {
+ text(it.detail(NodeKind.Value).name)
+ }
+ symbol(")")
+ }
+ text(" ")
+ }
+
+ private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode)
+ when (node.kind) {
+ NodeKind.Class,
+ NodeKind.AnnotationClass,
+ NodeKind.Exception,
+ NodeKind.Enum -> keyword("class ")
+ NodeKind.Interface -> keyword("interface ")
+ NodeKind.EnumItem -> keyword("enum val ")
+ NodeKind.Object -> keyword("object ")
+ NodeKind.TypeAlias -> keyword("typealias ")
+ else -> throw IllegalArgumentException("Node $node is not a class-like object")
+ }
+
+ identifierOrDeprecated(node)
+ renderTypeParametersForNode(node, renderMode)
+ renderSupertypesForNode(node, renderMode)
+ renderExtraTypeParameterConstraints(node, renderMode)
+
+ if (node.kind == NodeKind.TypeAlias) {
+ nbsp()
+ symbol("=")
+ nbsp()
+ renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode)
+ }
+ }
+
+ private fun ContentBlock.renderFunction(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode)
+ when (node.kind) {
+ NodeKind.Constructor -> identifier(node.owner!!.name)
+ NodeKind.Function,
+ NodeKind.CompanionObjectFunction -> keyword("fun ")
+ else -> throw IllegalArgumentException("Node $node is not a function-like object")
+ }
+ renderTypeParametersForNode(node, renderMode)
+ if (node.details(NodeKind.TypeParameter).any()) {
+ text(" ")
+ }
+
+ renderReceiver(node, renderMode, signatureMapper)
+
+ if (node.kind != NodeKind.Constructor)
+ identifierOrDeprecated(node)
+
+ symbol("(")
+ val parameters = node.details(NodeKind.Parameter)
+ renderHardWrappingList(parameters) {
+ renderParameter(it, renderMode)
+ }
+ if (needReturnType(node)) {
+ if (parameters.size > 1) {
+ hardLineBreak()
+ }
+ symbol(")")
+ symbol(": ")
+ renderType(node.detail(NodeKind.Type), renderMode)
+ } else {
+ symbol(")")
+ }
+ renderExtraTypeParameterConstraints(node, renderMode)
+ }
+
+ private fun ContentBlock.renderReceiver(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper?
+ ) {
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ if (receiver != null) {
+ if (signatureMapper != null) {
+ signatureMapper.renderReceiver(receiver, this)
+ } else {
+ val type = receiver.detail(NodeKind.Type)
+
+ if (type.isFunctionalType()) {
+ symbol("(")
+ renderFunctionalType(type, renderMode)
+ symbol(")")
+ } else {
+ renderType(type, renderMode)
+ }
+ }
+ symbol(".")
+ }
+ }
+
+ 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"
+
+ private fun ContentBlock.renderProperty(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode)
+ when (node.kind) {
+ NodeKind.Property,
+ NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ")
+ else -> throw IllegalArgumentException("Node $node is not a property")
+ }
+ renderTypeParametersForNode(node, renderMode)
+ if (node.details(NodeKind.TypeParameter).any()) {
+ text(" ")
+ }
+
+ renderReceiver(node, renderMode, signatureMapper)
+
+ identifierOrDeprecated(node)
+ symbol(": ")
+ renderType(node.detail(NodeKind.Type), renderMode)
+ renderExtraTypeParameterConstraints(node, renderMode)
+ }
+
+ fun DocumentationNode.getPropertyKeyword() =
+ if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
+
+ fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
+ if (node.deprecation != null) {
+ val strike = ContentStrikethrough()
+ strike.identifier(node.name)
+ append(strike)
+ } else {
+ identifier(node.name)
+ }
+ }
+}
+
+fun DocumentationNode.qualifiedNameFromType(): String {
+ return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
+ ?: (links.firstOrNull { it.kind != NodeKind.ExternalLink } ?: 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 000000000..c6e3cd37f
--- /dev/null
+++ b/core/src/main/kotlin/Languages/CommonLanguageService.kt
@@ -0,0 +1,118 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.Formats.nameWithOuterClass
+
+
+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
+ }
+ }
+
+ override fun renderNameWithOuterClass(node: DocumentationNode): String {
+ return when (node.kind) {
+ NodeKind.Constructor -> node.owner!!.nameWithOuterClass()
+ else -> node.nameWithOuterClass()
+ }
+ }
+
+ 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.renderHardWrappingList(
+ nodes: List<T>, separator: String = ", ",
+ renderItem: (T) -> Unit
+ ) {
+ if (nodes.none())
+ return
+
+ if (nodes.count() > 1) {
+ hardLineBreak()
+ repeat(4) {
+ nbsp()
+ }
+ }
+
+ renderItem(nodes.first())
+ nodes.drop(1).forEach {
+ symbol(separator)
+ hardLineBreak()
+ repeat(4) {
+ nbsp()
+ }
+ renderItem(it)
+ }
+ }
+
+ 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/JavaLanguageService.kt b/core/src/main/kotlin/Languages/JavaLanguageService.kt
new file mode 100644
index 000000000..4b3979b54
--- /dev/null
+++ b/core/src/main/kotlin/Languages/JavaLanguageService.kt
@@ -0,0 +1,179 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.Formats.nameWithOuterClass
+import org.jetbrains.dokka.LanguageService.RenderMode
+
+/**
+ * Implements [LanguageService] and provides rendering of symbols in Java language
+ */
+class JavaLanguageService : LanguageService {
+ override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
+ return ContentText(when (node.kind) {
+ NodeKind.Package -> renderPackage(node)
+ in NodeKind.classLike -> renderClass(node)
+
+ NodeKind.TypeParameter -> renderTypeParameter(node)
+ NodeKind.Type,
+ NodeKind.UpperBound -> renderType(node)
+
+ NodeKind.Constructor,
+ NodeKind.Function -> renderFunction(node)
+ NodeKind.Property -> renderProperty(node)
+ else -> "${node.kind}: ${node.name}"
+ })
+ }
+
+ override fun renderName(node: DocumentationNode): String {
+ return when (node.kind) {
+ NodeKind.Constructor -> node.owner!!.name
+ else -> node.name
+ }
+ }
+
+ override fun renderNameWithOuterClass(node: DocumentationNode): String {
+ return when (node.kind) {
+ NodeKind.Constructor -> node.owner!!.nameWithOuterClass()
+ else -> node.nameWithOuterClass()
+ }
+ }
+
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
+
+ private fun renderPackage(node: DocumentationNode): String {
+ return "package ${node.name}"
+ }
+
+ private fun renderModifier(node: DocumentationNode): String {
+ return when (node.name) {
+ "open" -> ""
+ "internal" -> ""
+ else -> node.name
+ }
+ }
+
+ fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?:
+ DocumentationNode("Object", node.content, NodeKind.ExternalClass)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type)
+
+ else -> null
+ }
+
+ fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ 1
+ else -> 0
+ }
+
+ fun renderType(node: DocumentationNode): String {
+ return when (node.name) {
+ "Unit" -> "void"
+ "Int" -> "int"
+ "Long" -> "long"
+ "Double" -> "double"
+ "Float" -> "float"
+ "Char" -> "char"
+ "Boolean" -> "bool"
+ // TODO: render arrays
+ else -> node.name
+ }
+ }
+
+ private fun renderTypeParameter(node: DocumentationNode): String {
+ val constraints = node.details(NodeKind.UpperBound)
+ return if (constraints.none())
+ node.name
+ else {
+ node.name + " extends " + constraints.map { renderType(node) }.joinToString()
+ }
+ }
+
+ private fun renderParameter(node: DocumentationNode): String {
+ return "${renderType(node.detail(NodeKind.Type))} ${node.name}"
+ }
+
+ private fun renderTypeParametersForNode(node: DocumentationNode): String {
+ return StringBuilder().apply {
+ val typeParameters = node.details(NodeKind.TypeParameter)
+ if (typeParameters.any()) {
+ append("<")
+ append(typeParameters.map { renderTypeParameter(it) }.joinToString())
+ append("> ")
+ }
+ }.toString()
+ }
+
+ private fun renderModifiersForNode(node: DocumentationNode): String {
+ val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" }
+ if (modifiers.none())
+ return ""
+ return modifiers.joinToString(" ", postfix = " ")
+ }
+
+ private fun renderClass(node: DocumentationNode): String {
+ return StringBuilder().apply {
+ when (node.kind) {
+ NodeKind.Class -> append("class ")
+ NodeKind.Interface -> append("interface ")
+ NodeKind.Enum -> append("enum ")
+ NodeKind.EnumItem -> append("enum value ")
+ NodeKind.Object -> append("class ")
+ else -> throw IllegalArgumentException("Node $node is not a class-like object")
+ }
+
+ append(node.name)
+ append(renderTypeParametersForNode(node))
+ }.toString()
+ }
+
+ private fun renderFunction(node: DocumentationNode): String {
+ return StringBuilder().apply {
+ when (node.kind) {
+ NodeKind.Constructor -> append(node.owner?.name)
+ NodeKind.Function -> {
+ append(renderTypeParametersForNode(node))
+ append(renderType(node.detail(NodeKind.Type)))
+ append(" ")
+ append(node.name)
+ }
+ else -> throw IllegalArgumentException("Node $node is not a function-like object")
+ }
+
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ append("(")
+ if (receiver != null)
+ (listOf(receiver) + node.details(NodeKind.Parameter)).map { renderParameter(it) }.joinTo(this)
+ else
+ node.details(NodeKind.Parameter).map { renderParameter(it) }.joinTo(this)
+
+ append(")")
+ }.toString()
+ }
+
+ private fun renderProperty(node: DocumentationNode): String {
+ return StringBuilder().apply {
+ when (node.kind) {
+ NodeKind.Property -> append("val ")
+ else -> throw IllegalArgumentException("Node $node is not a property")
+ }
+ append(renderTypeParametersForNode(node))
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ if (receiver != null) {
+ append(renderType(receiver.detail(NodeKind.Type)))
+ append(".")
+ }
+
+ append(node.name)
+ append(": ")
+ append(renderType(node.detail(NodeKind.Type)))
+ }.toString()
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Languages/LanguageService.kt b/core/src/main/kotlin/Languages/LanguageService.kt
new file mode 100644
index 000000000..e43081993
--- /dev/null
+++ b/core/src/main/kotlin/Languages/LanguageService.kt
@@ -0,0 +1,43 @@
+package org.jetbrains.dokka
+
+/**
+ * Provides facility for rendering [DocumentationNode] as a language-dependent declaration
+ */
+interface LanguageService {
+ enum class RenderMode {
+ /** Brief signature (used in a list of all members of the class). */
+ SUMMARY,
+ /** Full signature (used in the page describing the member itself */
+ FULL
+ }
+
+ /**
+ * Renders a [node] as a class, function, property or other signature in a target language.
+ * @param node A [DocumentationNode] to render
+ * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService]
+ */
+ fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode
+
+ /**
+ * Tries to summarize the signatures of the specified documentation nodes in a compact representation.
+ * Returns the representation if successful, or null if the signatures could not be summarized.
+ */
+ fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode?
+
+ /**
+ * Renders [node] as a named representation in the target language
+ *
+ * For example:
+ * ${code org.jetbrains.dokka.example}
+ *
+ * $node: A [DocumentationNode] to render
+ * $returns: [String] which is a string representation of the node's name
+ */
+ fun renderName(node: DocumentationNode): String
+
+ fun renderNameWithOuterClass(node: DocumentationNode): String
+}
+
+fun example(service: LanguageService, node: DocumentationNode) {
+ println("Node name: ${service.renderName(node)}")
+} \ 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 000000000..c4b5fa090
--- /dev/null
+++ b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
@@ -0,0 +1,250 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.classNodeNameWithOuterClass
+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 node.name !in fullOnlyModifiers
+ }
+
+ private val fullOnlyModifiers = setOf("public", "protected", "private")
+
+ 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, renderMode)
+ NodeKind.Property -> renderProperty(node)
+ NodeKind.Field -> renderField(node, renderMode)
+ NodeKind.EnumItem -> renderClass(node, renderMode)
+ 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 -> {
+ if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
+ 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(
+ it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name,
+ IdentifierKind.TypeName
+ )
+ }
+ renderTypeArgumentsForType(node)
+ }
+ }
+ }
+
+ 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 ContentBlock.renderTypeArgumentsForType(node: DocumentationNode) {
+ val typeArguments = node.details(NodeKind.Type)
+ if (typeArguments.any()) {
+ symbol("<")
+ renderList(typeArguments, noWrap = true) {
+ renderType(it)
+ }
+ symbol(">")
+ }
+ }
+
+// 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)
+ val superClassType = node.superclassType
+ val interfaces = node.supertypes - superClassType
+ if (superClassType != null) {
+ text(" ")
+ keyword("extends")
+ text(" ")
+ renderType(superClassType)
+ }
+ if (interfaces.isNotEmpty()) {
+ text(" ")
+ keyword("implements")
+ text(" ")
+ renderList(interfaces.filterNotNull()) {
+ renderType(it)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) {
+ renderList(nodes) {
+ renderParameter(it)
+ }
+ }
+
+ private fun ContentBlock.renderFunction(
+ node: DocumentationNode,
+ renderMode: RenderMode
+ ) {
+ renderModifiersForNode(node, renderMode)
+ 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))
+
+ }
+
+ private fun ContentBlock.renderField(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode)
+ renderType(node.detail(NodeKind.Type))
+ text(" ")
+ identifier(node.name)
+ }
+}
diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt
new file mode 100644
index 000000000..4cb0ac39c
--- /dev/null
+++ b/core/src/main/kotlin/Locations/Location.kt
@@ -0,0 +1,61 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+interface Location {
+ val path: String get
+ fun relativePathTo(other: Location, anchor: String? = null): String
+}
+
+/**
+ * Represents locations in the documentation in the form of [path](File).
+ *
+ * $file: [File] for this location
+ * $path: [String] representing path of this location
+ */
+data class FileLocation(val file: File) : Location {
+ override val path: String
+ get() = file.path
+
+ override fun relativePathTo(other: Location, anchor: String?): String {
+ if (other !is FileLocation) {
+ throw IllegalArgumentException("$other is not a FileLocation")
+ }
+ if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) {
+ return "./${file.name}"
+ }
+ val ownerFolder = file.parentFile!!
+ val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/')
+ return if (anchor == null) relativePath else relativePath + "#" + anchor
+ }
+}
+
+fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String {
+ val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() }
+ return if (!hasMembers) {
+ // leaf node, use file in owner's folder
+ parts.joinToString("/")
+ } else {
+ parts.joinToString("/") + (if (parts.none()) "" else "/") + "index"
+ }
+}
+
+
+fun relativePathToNode(node: DocumentationNode) = relativePathToNode(node.path.map { it.name }, node.members.any())
+
+fun identifierToFilename(path: String): String {
+ val escaped = path.replace('<', '-').replace('>', '-')
+ val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
+ return if (lowercase == "index") "--index--" else lowercase
+}
+
+fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String {
+ return location(owner).relativePathTo(location(node), null)
+}
+
+fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File {
+ val file = File(from.path).parentFile
+ return root.relativeTo(file)
+}
+
+fun File.toUnixString() = toString().replace(File.separatorChar, '/')
diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt
new file mode 100644
index 000000000..2c8f7a739
--- /dev/null
+++ b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt
@@ -0,0 +1,53 @@
+package org.jetbrains.dokka
+
+import org.intellij.markdown.IElementType
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.ast.ASTNode
+import org.intellij.markdown.ast.LeafASTNode
+import org.intellij.markdown.ast.getTextInNode
+import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
+import org.intellij.markdown.parser.MarkdownParser
+
+class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) {
+ val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) }
+ val type: IElementType get() = node.type
+ val text: String get() = node.getTextInNode(markdown).toString()
+ fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }
+
+ val previous get() = parent?.children?.getOrNull(parent.children.indexOf(this) - 1)
+
+ override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString()
+}
+
+fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) {
+ action(this) {
+ for (child in children) {
+ child.visit(action)
+ }
+ }
+}
+
+fun MarkdownNode.toTestString(): String {
+ val sb = StringBuilder()
+ var level = 0
+ visit { node, visitChildren ->
+ sb.append(" ".repeat(level * 2))
+ node.presentTo(sb)
+ sb.appendln()
+ level++
+ visitChildren()
+ level--
+ }
+ return sb.toString()
+}
+
+private fun MarkdownNode.presentTo(sb: StringBuilder) {
+ sb.append(type.toString())
+ sb.append(":" + text.replace("\n", "\u23CE"))
+}
+
+fun parseMarkdown(markdown: String): MarkdownNode {
+ if (markdown.isEmpty())
+ return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown)
+ return MarkdownNode(MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown), null, markdown)
+}
diff --git a/core/src/main/kotlin/Model/CodeNode.kt b/core/src/main/kotlin/Model/CodeNode.kt
new file mode 100644
index 000000000..17b506276
--- /dev/null
+++ b/core/src/main/kotlin/Model/CodeNode.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.dokka.Model
+
+import org.jsoup.helper.StringUtil.isWhitespace
+import org.jsoup.nodes.TextNode
+
+class CodeNode(text: String, baseUri: String): TextNode(text, baseUri) {
+
+ override fun text(): String {
+ return normaliseInitialWhitespace(wholeText.removePrefix("<code>")
+ .removeSuffix("</code>"))
+ }
+
+ private fun normaliseInitialWhitespace(text: String): String {
+ val sb = StringBuilder(text.length)
+ removeInitialWhitespace(sb, text)
+ return sb.toString()
+ }
+
+ /**
+ * Remove initial whitespace.
+ * @param accum builder to append to
+ * @param string string to remove the initial whitespace
+ */
+ private fun removeInitialWhitespace(accum: StringBuilder, string: String) {
+ var reachedNonWhite = false
+
+ val len = string.length
+ var c: Int
+ var i = 0
+ while (i < len) {
+ c = string.codePointAt(i)
+ if (isWhitespace(c) && !reachedNonWhite) {
+ i += Character.charCount(c)
+ continue
+ } else {
+ accum.appendCodePoint(c)
+ reachedNonWhite = true
+ }
+ i += Character.charCount(c)
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
new file mode 100644
index 000000000..a4c78fabf
--- /dev/null
+++ b/core/src/main/kotlin/Model/Content.kt
@@ -0,0 +1,296 @@
+package org.jetbrains.dokka
+
+interface ContentNode {
+ val textLength: Int
+}
+
+object ContentEmpty : ContentNode {
+ override val textLength: Int get() = 0
+}
+
+open class ContentBlock() : ContentNode {
+ open val children = arrayListOf<ContentNode>()
+
+ fun append(node: ContentNode) {
+ children.add(node)
+ }
+
+ fun isEmpty() = children.isEmpty()
+
+ override fun equals(other: Any?): Boolean =
+ other is ContentBlock && javaClass == other.javaClass && children == other.children
+
+ override fun hashCode(): Int =
+ children.hashCode()
+
+ override val textLength: Int
+ get() = children.sumBy { it.textLength }
+}
+
+class NodeRenderContent(
+ val node: DocumentationNode,
+ val mode: LanguageService.RenderMode
+): ContentNode {
+ override val textLength: Int
+ get() = 0 //TODO: Clarify?
+}
+
+class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() {
+ private var computed = false
+ override val children: ArrayList<ContentNode>
+ get() {
+ if (!computed) {
+ computed = true
+ fillChildren(this)
+ }
+ return super.children
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode() + 31 * fillChildren.hashCode()
+ }
+}
+
+enum class IdentifierKind {
+ TypeName,
+ ParameterName,
+ AnnotationName,
+ SummarizedTypeName,
+ Other
+}
+
+data class ContentText(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+data class ContentKeyword(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+data class ContentIdentifier(val text: String,
+ val kind: IdentifierKind = IdentifierKind.Other,
+ val signature: String? = null) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+data class ContentSymbol(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+data class ContentEntity(val text: String) : ContentNode {
+ override val textLength: Int
+ get() = text.length
+}
+
+object ContentNonBreakingSpace: ContentNode {
+ override val textLength: Int
+ get() = 1
+}
+
+object ContentSoftLineBreak: ContentNode {
+ override val textLength: Int
+ get() = 0
+}
+
+object ContentIndentedSoftLineBreak: ContentNode {
+ override val textLength: Int
+ get() = 0
+}
+class ScriptBlock(val type: String?, val src: String) : ContentBlock()
+
+class ContentParagraph(val label: String? = null) : ContentBlock()
+class ContentEmphasis() : ContentBlock()
+class ContentStrong() : ContentBlock()
+class ContentStrikethrough() : ContentBlock()
+class ContentCode() : ContentBlock()
+
+class ContentDescriptionList() : ContentBlock()
+class ContentDescriptionTerm() : ContentBlock()
+class ContentDescriptionDefinition() : ContentBlock()
+
+class ContentTable() : ContentBlock()
+class ContentTableBody() : ContentBlock()
+class ContentTableRow() : ContentBlock()
+class ContentTableHeader(val colspan: String? = null, val rowspan: String? = null) : ContentBlock()
+class ContentTableCell(val colspan: String? = null, val rowspan: String? = null) : ContentBlock()
+
+class ContentSpecialReference() : ContentBlock()
+
+open class ContentBlockCode(val language: String = "") : ContentBlock()
+class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language)
+
+abstract class ContentNodeLink() : ContentBlock() {
+ abstract val node: DocumentationNode?
+}
+
+object ContentHardLineBreak : ContentNode {
+ override val textLength: Int
+ get() = 0
+}
+
+class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() {
+ override fun equals(other: Any?): Boolean =
+ super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name
+
+ override fun hashCode(): Int =
+ children.hashCode() * 31 + node.name.hashCode()
+}
+
+class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() {
+ override val node: DocumentationNode? get() = lazyNode()
+
+ override fun equals(other: Any?): Boolean =
+ super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText
+
+ override fun hashCode(): Int =
+ children.hashCode() * 31 + linkText.hashCode()
+}
+
+class ContentExternalLink(val href : String) : ContentBlock() {
+ override fun equals(other: Any?): Boolean =
+ super.equals(other) && other is ContentExternalLink && href == other.href
+
+ override fun hashCode(): Int =
+ children.hashCode() * 31 + href.hashCode()
+}
+
+data class ContentBookmark(val name: String): ContentBlock()
+data class ContentLocalLink(val href: String) : ContentBlock()
+
+class ContentUnorderedList() : ContentBlock()
+class ContentOrderedList() : ContentBlock()
+class ContentListItem() : ContentBlock()
+
+class ContentHeading(val level: Int) : ContentBlock()
+
+class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() {
+ override fun equals(other: Any?): Boolean =
+ super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName
+
+ override fun hashCode(): Int =
+ children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0)
+}
+
+object ContentTags {
+ val Description = "Description"
+ val SeeAlso = "See Also"
+ val Return = "Return"
+ val Exceptions = "Exceptions"
+ val Parameters = "Parameters"
+}
+
+fun content(body: ContentBlock.() -> Unit): ContentBlock {
+ val block = ContentBlock()
+ block.body()
+ return block
+}
+
+fun ContentBlock.text(value: String) = append(ContentText(value))
+fun ContentBlock.keyword(value: String) = append(ContentKeyword(value))
+fun ContentBlock.symbol(value: String) = append(ContentSymbol(value))
+
+fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other, signature: String? = null) {
+ append(ContentIdentifier(value, kind, signature))
+}
+
+fun ContentBlock.nbsp() = append(ContentNonBreakingSpace)
+fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak)
+fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak)
+fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak)
+
+fun ContentBlock.strong(body: ContentBlock.() -> Unit) {
+ val strong = ContentStrong()
+ strong.body()
+ append(strong)
+}
+
+fun ContentBlock.code(body: ContentBlock.() -> Unit) {
+ val code = ContentCode()
+ code.body()
+ append(code)
+}
+
+fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) {
+ val block = if (to.kind == NodeKind.ExternalLink)
+ ContentExternalLink(to.name)
+ else
+ ContentNodeDirectLink(to)
+
+ block.body()
+ append(block)
+}
+
+open class Content(): ContentBlock() {
+ open val sections: List<ContentSection> get() = emptyList()
+ open val summary: ContentNode get() = ContentEmpty
+ open val description: ContentNode get() = ContentEmpty
+
+ fun findSectionByTag(tag: String): ContentSection? =
+ sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) }
+
+ companion object {
+ val Empty = Content()
+
+ fun of(vararg child: ContentNode): Content {
+ val result = MutableContent()
+ child.forEach { result.append(it) }
+ return result
+ }
+ }
+}
+
+open class MutableContent() : Content() {
+ private val sectionList = arrayListOf<ContentSection>()
+ override val sections: List<ContentSection>
+ get() = sectionList
+
+ fun addSection(tag: String?, subjectName: String?): ContentSection {
+ val section = ContentSection(tag ?: "", subjectName)
+ sectionList.add(section)
+ return section
+ }
+
+ override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty
+
+ override val description: ContentNode by lazy {
+ val descriptionNodes = children.drop(1)
+ if (descriptionNodes.isEmpty()) {
+ ContentEmpty
+ } else {
+ val result = ContentSection(ContentTags.Description, null)
+ result.children.addAll(descriptionNodes)
+ result
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is Content)
+ return false
+ return sections == other.sections && children == other.children
+ }
+
+ override fun hashCode(): Int {
+ return sections.map { it.hashCode() }.sum()
+ }
+
+ override fun toString(): String {
+ if (sections.isEmpty())
+ return "<empty>"
+ return (listOf(summary, description) + sections).joinToString()
+ }
+}
+
+fun javadocSectionDisplayName(sectionName: String?): String? =
+ when(sectionName) {
+ "param" -> "Parameters"
+ "throws", "exception" -> ContentTags.Exceptions
+ else -> sectionName?.capitalize()
+ }
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
new file mode 100644
index 000000000..7ac6d8f4a
--- /dev/null
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -0,0 +1,311 @@
+package org.jetbrains.dokka
+
+import java.util.*
+
+enum class NodeKind {
+ Unknown,
+
+ Package,
+ Class,
+ Interface,
+ Enum,
+ AnnotationClass,
+ Exception,
+ EnumItem,
+ Object,
+ TypeAlias,
+
+ Constructor,
+ Function,
+ Property,
+ Field,
+
+ CompanionObjectProperty,
+ CompanionObjectFunction,
+
+ Parameter,
+ Receiver,
+ TypeParameter,
+ Type,
+ Supertype,
+ UpperBound,
+ LowerBound,
+
+ TypeAliasUnderlyingType,
+
+ Modifier,
+ NullabilityModifier,
+
+ Module,
+
+ ExternalClass,
+ Annotation,
+
+ Value,
+
+ SourceUrl,
+ SourcePosition,
+ Signature,
+
+ ExternalLink,
+ QualifiedName,
+ Platform,
+
+ AllTypes,
+
+ /**
+ * A note which is rendered once on a page documenting a group of overloaded functions.
+ * Needs to be generated equally on all overloads.
+ */
+ OverloadGroupNote,
+
+ Attribute,
+
+ AttributeRef,
+
+ ApiLevel,
+ SdkExtSince,
+
+ DeprecatedLevel,
+
+ ArtifactId,
+
+ GroupNode;
+
+ companion object {
+ val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
+ val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem, Attribute)
+ }
+}
+
+open class DocumentationNode(val name: String,
+ content: Content,
+ val kind: NodeKind) {
+
+ private val references = LinkedHashSet<DocumentationReference>()
+
+ var content: Content = content
+ private set
+
+ val summary: ContentNode get() = content.summary
+
+ val owner: DocumentationNode?
+ get() = references(RefKind.Owner).singleOrNull()?.to
+ val details: List<DocumentationNode>
+ get() = references(RefKind.Detail).map { it.to }
+ val members: List<DocumentationNode>
+ get() = references(RefKind.Member).map { it.to }.sortedBy { it.name }
+ val inheritedMembers: List<DocumentationNode>
+ get() = references(RefKind.InheritedMember).map { it.to }
+ val allInheritedMembers: List<DocumentationNode>
+ get() = recursiveInheritedMembers().sortedBy { it.name }
+ val inheritedCompanionObjectMembers: List<DocumentationNode>
+ get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
+ val extensions: List<DocumentationNode>
+ get() = references(RefKind.Extension).map { it.to }
+ val inheritors: List<DocumentationNode>
+ get() = references(RefKind.Inheritor).map { it.to }
+ val overrides: List<DocumentationNode>
+ get() = references(RefKind.Override).map { it.to }
+ val links: List<DocumentationNode>
+ get() = references(RefKind.Link).map { it.to }
+ val hiddenLinks: List<DocumentationNode>
+ get() = references(RefKind.HiddenLink).map { it.to }
+ val annotations: List<DocumentationNode>
+ get() = references(RefKind.Annotation).map { it.to }
+ val deprecation: DocumentationNode?
+ get() = references(RefKind.Deprecation).map { it.to }.firstOrNull()
+ val platforms: List<String>
+ get() = references(RefKind.Platform).map { it.to.name }
+ val externalType: DocumentationNode?
+ get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
+ val apiLevel: DocumentationNode
+ get() = detailOrNull(NodeKind.ApiLevel) ?: DocumentationNode("", Content.Empty, NodeKind.ApiLevel)
+ val sdkExtSince: DocumentationNode
+ get() = detailOrNull(NodeKind.SdkExtSince) ?: DocumentationNode("", Content.Empty, NodeKind.SdkExtSince)
+ val deprecatedLevel: DocumentationNode
+ get() = detailOrNull(NodeKind.DeprecatedLevel) ?: DocumentationNode("", Content.Empty, NodeKind.DeprecatedLevel)
+ val artifactId: DocumentationNode
+ get() = detailOrNull(NodeKind.ArtifactId) ?: DocumentationNode("", Content.Empty, NodeKind.ArtifactId)
+ val attributes: List<DocumentationNode>
+ get() = details(NodeKind.Attribute).sortedBy { it.attributeRef!!.name }
+ val attributeRef: DocumentationNode?
+ get() = references(RefKind.AttributeRef).map { it.to }.firstOrNull()
+ val relatedAttributes: List<DocumentationNode>
+ get() = hiddenLinks(NodeKind.Attribute)
+ val supertypes: List<DocumentationNode>
+ get() = details(NodeKind.Supertype)
+ val signatureName = detailOrNull(NodeKind.Signature)?.name
+
+ val prettyName : String
+ get() = when(kind) {
+ NodeKind.Constructor -> owner!!.name
+ else -> name
+ }
+
+ 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 dropReferences(predicate: (DocumentationReference) -> Boolean) {
+ references.removeAll(predicate)
+ }
+
+ fun addAllReferencesFrom(other: DocumentationNode) {
+ references.addAll(other.references)
+ }
+
+ fun updateContent(body: MutableContent.() -> Unit) {
+ if (content !is MutableContent) {
+ content = MutableContent()
+ }
+ (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 hiddenLinks(kind: NodeKind): List<DocumentationNode> = hiddenLinks.filter { it.kind == kind }
+ fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
+ fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind }
+ fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind }
+
+ fun detail(kind: NodeKind): DocumentationNode = details.filter { it.kind == kind }.single()
+ fun detailOrNull(kind: NodeKind): DocumentationNode? = details.filter { it.kind == kind }.singleOrNull()
+ fun member(kind: NodeKind): DocumentationNode = members.filter { it.kind == kind }.single()
+ fun link(kind: NodeKind): DocumentationNode = links.filter { it.kind == kind }.single()
+
+ fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind }
+ fun allReferences(): Set<DocumentationReference> = references
+
+ override fun toString(): String {
+ return "$kind:$name"
+ }
+}
+
+class DocumentationModule(name: String, content: Content = Content.Empty)
+ : DocumentationNode(name, content, NodeKind.Module) {
+ val nodeRefGraph = NodeReferenceGraph()
+}
+
+val DocumentationNode.path: List<DocumentationNode>
+ get() {
+ val parent = owner ?: return listOf(this)
+ return parent.path + this
+ }
+
+fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
+ val existingNode = refGraph.lookup(packageName)
+ if (existingNode != null) {
+ return existingNode
+ }
+ val newNode = DocumentationNode(packageName,
+ packageContent.getOrElse(packageName) { Content.Empty },
+ NodeKind.Package)
+
+ refGraph.register(packageName, newNode)
+ module?.append(newNode, RefKind.Member)
+ return newNode
+}
+
+fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
+ addReferenceTo(child, kind)
+ when (kind) {
+ 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 */
+ }
+ }
+}
+
+fun DocumentationNode.appendTextNode(text: String,
+ kind: NodeKind,
+ refKind: RefKind = RefKind.Detail) {
+ append(DocumentationNode(text, Content.Empty, kind), refKind)
+}
+
+fun DocumentationNode.qualifiedName(): String {
+ if (kind == NodeKind.Type) {
+ return qualifiedNameFromType()
+ } else if (kind == NodeKind.Package) {
+ return name
+ }
+ return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
+}
+
+fun DocumentationNode.simpleName() = name.substringAfterLast('.')
+
+private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
+ val allInheritedMembers = mutableListOf<DocumentationNode>()
+ recursiveInheritedMembers(allInheritedMembers)
+ return allInheritedMembers
+}
+
+private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
+ allInheritedMembers.addAll(inheritedMembers)
+ 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 !in NodeKind.classLike }.joinToString(separator = ".") { it.name }
+}
+
+fun DocumentationNode.deprecatedLevelMessage(): String {
+ val kindName = when(kind) {
+ NodeKind.Enum -> "enum"
+ NodeKind.Interface -> "interface"
+ NodeKind.AnnotationClass -> "annotation"
+ NodeKind.Exception -> "exception"
+ else -> "class"
+ }
+ return "This $kindName was deprecated in API level ${deprecatedLevel.name}."
+}
+
+fun DocumentationNode.convertDeprecationDetailsToChildren() {
+ val toProcess = details.toMutableList()
+ while (!toProcess.isEmpty()) {
+ var child = toProcess.removeAt(0)
+ if (child.details.isEmpty() && child.name != "") {
+ updateContent { text(child.name.cleanForAppending()) }
+ }
+ else toProcess.addAll(0, child.details)
+ }
+}
+
+ /*
+ * Removes extraneous quotation marks and adds a ". " to make appending children readable.
+ */
+fun String.cleanForAppending(): String {
+ var result = this
+ if (this.first() == this.last() && this.first() == '"') result = result.substring(1, result.length - 1)
+ if (result[result.length - 2] != '.' && result.last() != ' ') result += ". "
+ return result
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
new file mode 100644
index 000000000..cc582a0fd
--- /dev/null
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -0,0 +1,86 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Singleton
+
+enum class RefKind {
+ Owner,
+ Member,
+ InheritedMember,
+ InheritedCompanionObjectMember,
+ Detail,
+ Link,
+ HiddenLink,
+ Extension,
+ Inheritor,
+ Superclass,
+ Override,
+ Annotation,
+ HiddenAnnotation,
+ Deprecation,
+ TopLevelPage,
+ Platform,
+ ExternalType,
+ AttributeRef,
+ AttributeSource
+}
+
+data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
+}
+
+class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?,
+ val lazyNodeTo: () -> DocumentationNode?,
+ val kind: RefKind) {
+ fun resolve() {
+ val fromNode = lazyNodeFrom()
+ val toNode = lazyNodeTo()
+ if (fromNode != null && toNode != null) {
+ fromNode.addReferenceTo(toNode, kind)
+ }
+ }
+}
+
+class NodeReferenceGraph() {
+ private val nodeMap = hashMapOf<String, DocumentationNode>()
+ val references = arrayListOf<PendingDocumentationReference>()
+
+ fun register(signature: String, node: DocumentationNode) {
+ nodeMap.put(signature, node)
+ }
+
+ fun link(fromNode: DocumentationNode, toSignature: String, kind: RefKind) {
+ references.add(PendingDocumentationReference({ -> fromNode}, { -> nodeMap[toSignature]}, kind))
+ }
+
+ fun link(fromSignature: String, toNode: DocumentationNode, kind: RefKind) {
+ references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> toNode}, kind))
+ }
+
+ fun link(fromSignature: String, toSignature: String, kind: RefKind) {
+ references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind))
+ }
+
+ fun lookup(signature: String) = nodeMap[signature]
+
+ fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? {
+ val result = nodeMap[signature]
+ if (result == null) {
+ logger.warn("Can't find node by signature `$signature`")
+ }
+ return result
+ }
+
+ fun resolveReferences() {
+ references.forEach { it.resolve() }
+ }
+}
+
+@Singleton
+class PlatformNodeRegistry {
+ private val platformNodes = hashMapOf<String, DocumentationNode>()
+
+ operator fun get(platform: String): DocumentationNode {
+ return platformNodes.getOrPut(platform) {
+ DocumentationNode(platform, Content.Empty, NodeKind.Platform)
+ }
+ }
+}
diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
new file mode 100644
index 000000000..e8fdde6e3
--- /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
new file mode 100644
index 000000000..5b6289146
--- /dev/null
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -0,0 +1,135 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiFileFactory
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.LocalTimeCounter
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
+import java.io.File
+
+@Singleton
+class PackageDocs
+ @Inject constructor(val linkResolver: DeclarationLinkResolver?,
+ val logger: DokkaLogger,
+ val environment: KotlinCoreEnvironment,
+ val refGraph: NodeReferenceGraph,
+ val elementSignatureProvider: ElementSignatureProvider)
+{
+ val moduleContent: MutableContent = MutableContent()
+ private val _packageContent: MutableMap<String, MutableContent> = hashMapOf()
+ val packageContent: Map<String, Content>
+ get() = _packageContent
+
+ fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) {
+ val file = File(fileName)
+ if (file.exists()) {
+ val text = file.readText()
+ val tree = parseMarkdown(text)
+ val linkMap = LinkMap.buildLinkMap(tree.node, text)
+ var targetContent: MutableContent = moduleContent
+ tree.children.forEach {
+ if (it.type == MarkdownElementTypes.ATX_1) {
+ val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text
+ if (headingText != null) {
+ targetContent = findTargetContent(headingText.trimStart())
+ }
+ } else {
+ buildContentTo(it, targetContent, LinkResolver(linkMap, { resolveContentLink(fileName, it, linkResolveContext) }))
+ }
+ }
+ } else {
+ logger.warn("Include file $file was not found.")
+ }
+ }
+
+ 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
+ }
+ if (heading.startsWith("Package") || heading.startsWith("package")) {
+ return findOrCreatePackageContent(heading.substring("package".length).trim())
+ }
+ return findOrCreatePackageContent(heading)
+ }
+
+ private fun findOrCreatePackageContent(packageName: String) =
+ _packageContent.getOrPut(packageName) { -> MutableContent() }
+
+ private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock {
+ if (linkResolver != null) {
+ linkResolveContext
+ .asSequence()
+ .map { p -> linkResolver.tryResolveContentLink(p, href) }
+ .filterNotNull()
+ .firstOrNull()
+ ?.let { return it }
+ }
+ logger.warn("Unresolved link to `$href` in include ($fileName)")
+ return ContentExternalLink("#")
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt
new file mode 100644
index 000000000..2c75cfdac
--- /dev/null
+++ b/core/src/main/kotlin/Model/SourceLinks.kt
@@ -0,0 +1,56 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiNameIdentifierOwner
+import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import java.io.File
+
+
+fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) {
+ val path = psi?.containingFile?.virtualFile?.path ?: return
+
+ 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 line = target?.lineNumber()
+ if (line != null) {
+ url += linkDef.lineSuffix + line.toString()
+ }
+ }
+ append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl),
+ RefKind.Detail);
+ }
+
+ if (target != null) {
+ append(DocumentationNode(target.sourcePosition(), Content.Empty, NodeKind.SourcePosition), RefKind.Detail)
+ }
+}
+
+private fun PsiElement.sourcePosition(): String {
+ val path = containingFile.virtualFile.path
+ val lineNumber = lineNumber()
+ val columnNumber = columnNumber()
+
+ return when {
+ lineNumber == null -> path
+ columnNumber == null -> "$path:$lineNumber"
+ else -> "$path:$lineNumber:$columnNumber"
+ }
+}
+
+fun PsiElement.lineNumber(): Int? {
+ val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile)
+ // IJ uses 0-based line-numbers; external source browsers use 1-based
+ return doc?.getLineNumber(textRange.startOffset)?.plus(1)
+}
+
+fun PsiElement.columnNumber(): Int? {
+ val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) ?: return null
+ val lineNumber = doc.getLineNumber(textRange.startOffset)
+ return startOffset - doc.getLineStartOffset(lineNumber)
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
new file mode 100644
index 000000000..116a5c02f
--- /dev/null
+++ b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt
@@ -0,0 +1,105 @@
+package org.jetbrains.dokka.Samples
+
+import com.google.inject.Inject
+import com.intellij.psi.PsiElement
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
+import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope
+import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtDeclarationWithBody
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
+
+
+open class DefaultSampleProcessingService
+@Inject constructor(val options: DocumentationOptions,
+ val logger: DokkaLogger,
+ val resolutionFacade: DokkaResolutionFacade)
+ : SampleProcessingService {
+
+ override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode {
+ if (functionName == null) {
+ logger.warn("Missing function name in @sample in ${descriptor.signature()}")
+ return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) }
+ }
+ val bindingContext = BindingContext.EMPTY
+ val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull()
+ if (symbol == null) {
+ logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}")
+ return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) }
+ }
+ val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol)
+ if (psiElement == null) {
+ logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}")
+ return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) }
+ }
+
+ val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd()
+ val lines = text.split("\n")
+ val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0
+ val finalText = lines.map { it.drop(indent) }.joinToString("\n")
+
+ return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) }
+ }
+
+ protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) {
+ is KtDeclarationWithBody -> {
+ val bodyExpression = psiElement.bodyExpression
+ when (bodyExpression) {
+ is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
+ else -> bodyExpression!!.text
+ }
+ }
+ else -> psiElement.text
+ }
+
+ protected open fun processImports(psiElement: PsiElement): ContentBlockCode {
+ val psiFile = psiElement.containingFile
+ if (psiFile is KtFile) {
+ return ContentBlockCode("kotlin").apply {
+ append(ContentText(psiFile.importList?.text ?: ""))
+ }
+ } else {
+ return ContentBlockCode("")
+ }
+ }
+
+ private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? {
+ var currentScope = scope
+ val parts = functionName.split('.')
+
+ var symbol: DeclarationDescriptor? = null
+
+ for (part in parts) {
+ // short name
+ val symbolName = Name.identifier(part)
+ val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL, { it == symbolName })
+ .filter { it.name == symbolName }
+ .firstOrNull()
+
+ if (partSymbol == null) {
+ symbol = null
+ break
+ }
+ @Suppress("IfThenToElvis")
+ currentScope = if (partSymbol is ClassDescriptor)
+ partSymbol.defaultType.memberScope
+ else if (partSymbol is PackageViewDescriptor)
+ partSymbol.memberScope
+ else
+ getKDocLinkResolutionScope(resolutionFacade, partSymbol)
+ symbol = partSymbol
+ }
+
+ return symbol
+ }
+}
+
diff --git a/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt
new file mode 100644
index 000000000..33d6cfeba
--- /dev/null
+++ b/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt
@@ -0,0 +1,52 @@
+package org.jetbrains.dokka.Samples
+
+import com.google.inject.Inject
+import com.intellij.psi.PsiElement
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.utils.addIfNotNull
+
+open class DevsiteSampleProcessingService
+@Inject constructor(
+ options: DocumentationOptions,
+ logger: DokkaLogger,
+ resolutionFacade: DokkaResolutionFacade
+) : DefaultSampleProcessingService(options, logger, resolutionFacade) {
+
+ override fun processImports(psiElement: PsiElement): ContentBlockCode {
+ // List of expression calls inside this sample, so we can trim the imports to only show relevant expressions
+ val sampleExpressionCalls = mutableSetOf<String>()
+ val psiFile = psiElement.containingFile
+ (psiElement as KtDeclarationWithBody).bodyExpression!!.accept(object : KtTreeVisitorVoid() {
+ override fun visitCallExpression(expression: KtCallExpression) {
+ sampleExpressionCalls.addIfNotNull(expression.calleeExpression?.text)
+ super.visitCallExpression(expression)
+ }
+ })
+ val androidxPackage = Name.identifier("androidx")
+ if (psiFile is KtFile) {
+ val filteredImports = psiFile.importList?.imports?.filter { element ->
+ val fqImportName = element.importPath?.fqName ?: return@filter false
+
+ val shortName = fqImportName.shortName().identifier
+ // Hide all non-androidx imports
+ if (!fqImportName.startsWith(androidxPackage)) return@filter false
+
+ sampleExpressionCalls.any { call ->
+ call == shortName
+ }
+ }
+
+ return ContentBlockCode("kotlin").apply {
+ filteredImports?.forEach { import ->
+ if (import != filteredImports.first()) {
+ append(ContentText("\n"))
+ }
+ append(ContentText(import.text))
+ }
+ }
+ }
+ return super.processImports(psiElement)
+ }
+}
diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
new file mode 100644
index 000000000..b0988c352
--- /dev/null
+++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
@@ -0,0 +1,137 @@
+package org.jetbrains.dokka.Samples
+
+import com.google.inject.Inject
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiWhiteSpace
+import com.intellij.psi.impl.source.tree.LeafPsiElement
+import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.allChildren
+import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
+import org.jetbrains.kotlin.resolve.ImportPath
+
+open class KotlinWebsiteSampleProcessingService
+@Inject constructor(options: DocumentationOptions,
+ logger: DokkaLogger,
+ resolutionFacade: DokkaResolutionFacade)
+ : DefaultSampleProcessingService(options, logger, resolutionFacade) {
+
+ private class SampleBuilder : KtTreeVisitorVoid() {
+ val builder = StringBuilder()
+ val text: String
+ get() = builder.toString()
+
+ fun KtValueArgument.extractStringArgumentValue() =
+ (getArgumentExpression() as KtStringTemplateExpression)
+ .entries.joinToString("") { it.text }
+
+
+ fun convertAssertPrints(expression: KtCallExpression) {
+ val (argument, commentArgument) = expression.valueArguments
+ builder.apply {
+ append("println(")
+ append(argument.text)
+ append(") // ")
+ append(commentArgument.extractStringArgumentValue())
+ }
+ }
+
+ fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) {
+ val (argument) = expression.valueArguments
+ builder.apply {
+ expression.valueArguments.getOrNull(1)?.let {
+ append("// ${it.extractStringArgumentValue()}")
+ val ws = expression.prevLeaf { it is PsiWhiteSpace }
+ append(ws?.text ?: "\n")
+ }
+ append("println(\"")
+ append(argument.text)
+ append(" is \${")
+ append(argument.text)
+ append("}\") // $expectedResult")
+ }
+ }
+
+ fun convertAssertFails(expression: KtCallExpression) {
+ val (message, funcArgument) = expression.valueArguments
+ builder.apply {
+ val argument = if (funcArgument.getArgumentExpression() is KtLambdaExpression)
+ PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: ""
+ else
+ funcArgument.text
+ append(argument.lines().joinToString(separator = "\n") { "// $it" })
+ append(" // ")
+ append(message.extractStringArgumentValue())
+ append(" will fail")
+ }
+ }
+
+ 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
+ append(argument.lines().joinToString(separator = "\n") { "// $it" })
+ append(" // will fail with ")
+ append(exceptionType.text)
+ }
+ }
+
+ override fun visitCallExpression(expression: KtCallExpression) {
+ when (expression.calleeExpression?.text) {
+ "assertPrints" -> convertAssertPrints(expression)
+ "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true)
+ "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false)
+ "assertFails" -> convertAssertFails(expression)
+ "assertFailsWith" -> convertAssertFailsWith(expression)
+ else -> super.visitCallExpression(expression)
+ }
+ }
+
+ override fun visitElement(element: PsiElement) {
+ if (element is LeafPsiElement)
+ builder.append(element.text)
+ super.visitElement(element)
+ }
+ }
+
+ private fun PsiElement.buildSampleText(): String {
+ val sampleBuilder = SampleBuilder()
+ this.accept(sampleBuilder)
+ return sampleBuilder.text
+ }
+
+ val importsToIgnore = arrayOf("samples.*").map { ImportPath.fromString(it) }
+
+ override fun processImports(psiElement: PsiElement): ContentBlockCode {
+ val psiFile = psiElement.containingFile
+ if (psiFile is KtFile) {
+ return ContentBlockCode("kotlin").apply {
+ append(ContentText("\n"))
+ psiFile.importList?.let {
+ it.allChildren.filter {
+ it !is KtImportDirective || it.importPath !in importsToIgnore
+ }.forEach { append(ContentText(it.text)) }
+ }
+ }
+ }
+ return super.processImports(psiElement)
+ }
+
+ override fun processSampleBody(psiElement: PsiElement) = when (psiElement) {
+ is KtDeclarationWithBody -> {
+ val bodyExpression = psiElement.bodyExpression
+ val bodyExpressionText = bodyExpression!!.buildSampleText()
+ when (bodyExpression) {
+ is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}")
+ else -> bodyExpressionText
+ }
+ }
+ else -> psiElement.buildSampleText()
+ }
+}
+
diff --git a/core/src/main/kotlin/Samples/SampleProcessingService.kt b/core/src/main/kotlin/Samples/SampleProcessingService.kt
new file mode 100644
index 000000000..86c917cf5
--- /dev/null
+++ b/core/src/main/kotlin/Samples/SampleProcessingService.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.dokka.Samples
+
+import org.jetbrains.dokka.ContentNode
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+
+interface SampleProcessingService {
+ fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Utilities/DokkaLogging.kt b/core/src/main/kotlin/Utilities/DokkaLogging.kt
new file mode 100644
index 000000000..1ef528378
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/DokkaLogging.kt
@@ -0,0 +1,27 @@
+package org.jetbrains.dokka
+
+interface DokkaLogger {
+ fun info(message: String)
+ fun warn(message: String)
+ fun error(message: String)
+}
+
+object DokkaConsoleLogger : DokkaLogger {
+ var warningCount: Int = 0
+
+ override fun info(message: String) = println(message)
+ override fun warn(message: String) {
+ println("WARN: $message")
+ warningCount++
+ }
+
+ override fun error(message: String) = println("ERROR: $message")
+
+ fun report() {
+ if (warningCount > 0) {
+ println("generation completed with $warningCount warnings")
+ } else {
+ println("generation completed successfully")
+ }
+ }
+}
diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
new file mode 100644
index 000000000..7c8e5c347
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -0,0 +1,81 @@
+package org.jetbrains.dokka.Utilities
+
+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.name.Names
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.FormatDescriptor
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import java.io.File
+import kotlin.reflect.KClass
+
+const val impliedPlatformsName = "impliedPlatforms"
+
+class DokkaAnalysisModule(val environment: AnalysisEnvironment,
+ val options: DocumentationOptions,
+ val defaultPlatformsProvider: DefaultPlatformsProvider,
+ val nodeReferenceGraph: NodeReferenceGraph,
+ val logger: DokkaLogger) : Module {
+ override fun configure(binder: Binder) {
+ binder.bind<DokkaLogger>().toInstance(logger)
+
+ val coreEnvironment = environment.createCoreEnvironment()
+ binder.bind<KotlinCoreEnvironment>().toInstance(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<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider)
+
+ binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph)
+
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
+ descriptor.configureAnalysis(binder)
+ }
+}
+
+object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>()
+
+class DokkaOutputModule(val options: DocumentationOptions,
+ 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)
+ binder.bind<String>().annotatedWith(Names.named("outlineRoot")).toInstance(options.outlineRoot)
+ binder.bind<String>().annotatedWith(Names.named("dacRoot")).toInstance(options.dacRoot)
+ binder.bind<Boolean>().annotatedWith(Names.named("generateClassIndex")).toInstance(options.generateClassIndexPage)
+ binder.bind<Boolean>().annotatedWith(Names.named("generatePackageIndex")).toInstance(options.generatePackageIndexPage)
+ val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat)
+
+ descriptor.configureOutput(binder)
+ }
+}
+
+private inline fun <reified T: Any> Binder.registerCategory(category: String) {
+ ServiceLocator.allServices(category).forEach {
+ @Suppress("UNCHECKED_CAST")
+ bind(T::class.java).annotatedWith(Names.named(it.name)).to(T::class.java.classLoader.loadClass(it.className) as Class<T>)
+ }
+}
+
+private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotated(name: String) {
+ bind(Base::class.java).annotatedWith(Names.named(name)).to(T::class.java)
+}
+
+
+inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java)
+
+inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) }
+
+inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) =
+ kClass?.let { value toType it }
+
+inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java)
diff --git a/core/src/main/kotlin/Utilities/DownloadSamples.kt b/core/src/main/kotlin/Utilities/DownloadSamples.kt
new file mode 100644
index 000000000..3c28d6cc7
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/DownloadSamples.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+package org.jetbrains.dokka.Utilities
+
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import java.io.File
+import java.io.FileOutputStream
+
+object DownloadSamples {
+
+ /** HTTP Client to make requests **/
+ val client = OkHttpClient()
+
+ /**
+ * Function that downloads samples based on the directory structure described in hashmap
+ */
+ fun downloadSamples(): Boolean {
+
+ //loop through each directory of AOSP code in SamplesPathsToURLs.kt
+ filepathsToUrls.forEach { (filepath, url) ->
+
+ //build request using each URL
+ val request = Request.Builder()
+ .url(url)
+ .build()
+
+ val response = client.newCall(request).execute()
+
+ if (response.isSuccessful) {
+
+ //save .tar.gz file to filepath designated by map
+ val currentFile = File(filepath)
+ currentFile.mkdirs()
+
+ val fos = FileOutputStream("$filepath.tar.gz")
+ fos.write(response.body?.bytes())
+ fos.close()
+
+ //Unzip, Untar, and delete compressed file after
+ extractFiles(filepath)
+
+ } else {
+ println("Error Downloading Samples: $response")
+ return false
+ }
+ }
+
+ println("Successfully completed download of samples.")
+ return true
+
+ }
+
+ /**
+ * Execute bash commands to extract file, then delete archive file
+ */
+ private fun extractFiles(pathToFile: String) {
+
+ ProcessBuilder()
+ .command("tar","-zxf", "$pathToFile.tar.gz", "-C", pathToFile)
+ .redirectError(ProcessBuilder.Redirect.INHERIT)
+ .redirectOutput(ProcessBuilder.Redirect.INHERIT)
+ .start()
+ .waitFor()
+
+ ProcessBuilder()
+ .command("rm", "$pathToFile.tar.gz")
+ .redirectError(ProcessBuilder.Redirect.INHERIT)
+ .redirectOutput(ProcessBuilder.Redirect.INHERIT)
+ .start()
+ .waitFor()
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/Utilities/Html.kt
new file mode 100644
index 000000000..d9463c595
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/Html.kt
@@ -0,0 +1,18 @@
+package org.jetbrains.dokka
+
+import java.net.URI
+
+
+/**
+ * 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;")
+
+// A URI consists of several parts (as described in https://docs.oracle.com/javase/7/docs/api/java/net/URI.html ):
+// [scheme:][//authority][path][?query][#fragment]
+//
+// The anchorEnchoded() function encodes the given string to make it a legal value for <fragment>
+fun String.anchorEncoded(): String {
+ return URI(null, null, this).getRawFragment()
+}
diff --git a/core/src/main/kotlin/Utilities/Path.kt b/core/src/main/kotlin/Utilities/Path.kt
new file mode 100644
index 000000000..058384993
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/Path.kt
@@ -0,0 +1,5 @@
+package org.jetbrains.dokka
+
+import java.io.File
+
+fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(path + "." + extension)
diff --git a/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt b/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt
new file mode 100644
index 000000000..173389d5e
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+package org.jetbrains.dokka.Utilities
+
+//HashMap of all filepaths to the URLs that should be downloaded to that filepath
+val filepathsToUrls: HashMap<String, String> = hashMapOf(
+ "./samples/development/samples/ApiDemos" to "https://android.googlesource.com/platform/development/+archive/refs/heads/master/samples/ApiDemos.tar.gz",
+ "./samples/development/samples/NotePad" to "https://android.googlesource.com/platform/development/+archive/refs/heads/master/samples/NotePad.tar.gz",
+ "./samples/external/icu/android_icu4j/src/samples/java/android/icu/samples/text" to "https://android.googlesource.com/platform/external/icu/+archive/refs/heads/master/android_icu4j/src/samples/java/android/icu/samples/text.tar.gz",
+ "./samples/frameworks/base/core/java/android/content" to "https://android.googlesource.com/platform/frameworks/base/+archive/refs/heads/master/core/java/android/content.tar.gz",
+ "./samples/frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost" to "https://android.googlesource.com/platform/frameworks/base/+archive/refs/heads/master/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost.tar.gz"
+ )
diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/Utilities/ServiceLocator.kt
new file mode 100644
index 000000000..83c4c65c1
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/ServiceLocator.kt
@@ -0,0 +1,100 @@
+package org.jetbrains.dokka.Utilities
+
+import java.io.File
+import java.net.URISyntaxException
+import java.net.URL
+import java.util.*
+import java.util.jar.JarFile
+import java.util.zip.ZipEntry
+
+data class ServiceDescriptor(val name: String, val category: String, val description: String?, val className: String)
+
+class ServiceLookupException(message: String) : Exception(message)
+
+object ServiceLocator {
+ fun <T : Any> lookup(clazz: Class<T>, category: String, implementationName: String): T {
+ val descriptor = lookupDescriptor(category, implementationName)
+ return lookup(clazz, descriptor)
+ }
+
+ fun <T : Any> lookup(
+ clazz: Class<T>,
+ descriptor: ServiceDescriptor
+ ): T {
+ val loadedClass = javaClass.classLoader.loadClass(descriptor.className)
+ val constructor = loadedClass.constructors
+ .filter { it.parameterTypes.isEmpty() }
+ .firstOrNull()
+ ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
+
+ 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}")
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ return implementationRawType as T
+ }
+
+ private fun lookupDescriptor(category: String, implementationName: String): ServiceDescriptor {
+ val properties = javaClass.classLoader.getResourceAsStream("dokka/$category/$implementationName.properties")?.use { stream ->
+ Properties().let { properties ->
+ properties.load(stream)
+ properties
+ }
+ } ?: throw ServiceLookupException("No implementation with name $implementationName found in category $category")
+
+ val className = properties["class"]?.toString() ?: throw ServiceLookupException("Implementation $implementationName has no class configured")
+
+ return ServiceDescriptor(implementationName, category, properties["description"]?.toString(), className)
+ }
+
+ fun URL.toFile(): File {
+ assert(protocol == "file")
+
+ return try {
+ File(toURI())
+ } catch (e: URISyntaxException) { //Try to handle broken URLs, with unescaped spaces
+ File(path)
+ }
+ }
+
+ fun allServices(category: String): List<ServiceDescriptor> {
+ val entries = this.javaClass.classLoader.getResources("dokka/$category")?.toList() ?: emptyList()
+
+ return entries.flatMap {
+ when (it.protocol) {
+ "file" -> it.toFile().listFiles()?.filter { it.extension == "properties" }?.map { lookupDescriptor(category, it.nameWithoutExtension) } ?: emptyList()
+ "jar" -> {
+ val file = JarFile(URL(it.file.substringBefore("!")).toFile())
+ try {
+ val jarPath = it.file.substringAfterLast("!").removePrefix("/")
+ file.entries()
+ .asSequence()
+ .filter { entry -> !entry.isDirectory && entry.path == jarPath && entry.extension == "properties" }
+ .map { entry ->
+ lookupDescriptor(category, entry.fileName.substringBeforeLast("."))
+ }.toList()
+ } finally {
+ file.close()
+ }
+ }
+ else -> emptyList<ServiceDescriptor>()
+ }
+ }
+ }
+}
+
+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)
+
+private val ZipEntry.path: String
+ get() = name.substringBeforeLast("/", "").removePrefix("/")
+
+private val ZipEntry.extension: String?
+ get() = fileName.let { fn -> if ("." in fn) fn.substringAfterLast(".") else null }
diff --git a/core/src/main/kotlin/Utilities/StringExtensions.kt b/core/src/main/kotlin/Utilities/StringExtensions.kt
new file mode 100644
index 000000000..98f8c8036
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/StringExtensions.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+package org.jetbrains.dokka.Utilities
+
+/**
+ * Finds the first sentence of a string, accounting for periods that may occur in parenthesis.
+ */
+fun String.firstSentence(): String {
+
+ // First, search for location of first period and first parenthesis.
+ val firstPeriodIndex = this.indexOf('.')
+ val openParenIndex = this.indexOf('(')
+
+ // If there is no opening parenthesis found or if it occurs after the occurrence of the first period, just return
+ // the first sentence, or the entire string if no period is found.
+ if (openParenIndex == -1 || openParenIndex > firstPeriodIndex) {
+ return if (firstPeriodIndex != -1) {
+ this.substring(0, firstPeriodIndex + 1)
+ } else {
+ this
+ }
+ }
+
+ // At this point we know that the opening parenthesis occurs before the first period, so we look for the matching
+ // closing parenthesis.
+ val closeParenIndex = this.indexOf(')', openParenIndex)
+
+ // If a matching closing parenthesis is found, take that substring and recursively process the rest of the string.
+ // This is to accommodate periods inside of parenthesis. If a matching closing parenthesis is not found, return the
+ // original string.
+ return if (closeParenIndex != -1) {
+ this.substring(0, closeParenIndex) + this.substring(closeParenIndex, this.length).firstSentence()
+ } else {
+ this
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/Utilities/Uri.kt
new file mode 100644
index 000000000..9827c624c
--- /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
new file mode 100644
index 000000000..12f571bee
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/docbase.kt
@@ -0,0 +1,525 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.*
+import org.jetbrains.dokka.*
+import java.lang.reflect.Modifier
+import java.util.*
+import kotlin.reflect.KClass
+
+private interface HasModule {
+ val module: ModuleNodeAdapter
+}
+
+private interface HasDocumentationNode {
+ val node: DocumentationNode
+}
+
+open class DocumentationNodeBareAdapter(override val node: DocumentationNode) : Doc, HasDocumentationNode {
+ private var rawCommentText_: String? = null
+
+ override fun name(): String = node.name
+ override fun position(): SourcePosition? = SourcePositionAdapter(node)
+
+ override fun inlineTags(): Array<out Tag>? = emptyArray()
+ override fun firstSentenceTags(): Array<out Tag>? = emptyArray()
+ override fun tags(): Array<out Tag> = emptyArray()
+ override fun tags(tagname: String?): Array<out Tag>? = tags().filter { it.kind() == tagname || it.kind() == "@$tagname" }.toTypedArray()
+ override fun seeTags(): Array<out SeeTag>? = tags().filterIsInstance<SeeTag>().toTypedArray()
+ override fun commentText(): String = ""
+
+ override fun setRawCommentText(rawDocumentation: String?) {
+ rawCommentText_ = rawDocumentation ?: ""
+ }
+
+ override fun getRawCommentText(): String = rawCommentText_ ?: ""
+
+ override fun isError(): Boolean = false
+ override fun isException(): Boolean = node.kind == NodeKind.Exception
+ override fun isEnumConstant(): Boolean = node.kind == NodeKind.EnumItem
+ override fun isEnum(): Boolean = node.kind == NodeKind.Enum
+ override fun isMethod(): Boolean = node.kind == NodeKind.Function
+ override fun isInterface(): Boolean = node.kind == NodeKind.Interface
+ override fun isField(): Boolean = node.kind == NodeKind.Field
+ override fun isClass(): Boolean = node.kind == NodeKind.Class
+ override fun isAnnotationType(): Boolean = node.kind == NodeKind.AnnotationClass
+ override fun isConstructor(): Boolean = node.kind == NodeKind.Constructor
+ override fun isOrdinaryClass(): Boolean = node.kind == NodeKind.Class
+ override fun isAnnotationTypeElement(): Boolean = node.kind == NodeKind.Annotation
+
+ override fun compareTo(other: Any?): Int = when (other) {
+ !is DocumentationNodeAdapter -> 1
+ else -> node.name.compareTo(other.node.name)
+ }
+
+ override fun equals(other: Any?): Boolean = node.qualifiedName() == (other as? DocumentationNodeAdapter)?.node?.qualifiedName()
+ override fun hashCode(): Int = node.name.hashCode()
+
+ override fun isIncluded(): Boolean = node.kind != NodeKind.ExternalClass
+}
+
+
+// TODO think of source position instead of null
+// TODO tags
+open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeBareAdapter(node), HasModule {
+ override fun inlineTags(): Array<out Tag> = buildInlineTags(module, this, node.content).toTypedArray()
+ override fun firstSentenceTags(): Array<out Tag> = buildInlineTags(module, this, node.summary).toTypedArray()
+
+ override fun tags(): Array<out Tag> {
+ val result = ArrayList<Tag>(buildInlineTags(module, this, node.content))
+ node.content.sections.flatMapTo(result) {
+ when (it.tag) {
+ ContentTags.SeeAlso -> buildInlineTags(module, this, it)
+ else -> emptyList<Tag>()
+ }
+ }
+
+ node.deprecation?.let {
+ val content = it.content.asText()
+ result.add(TagImpl(this, "deprecated", content ?: ""))
+ }
+
+ return result.toTypedArray()
+ }
+}
+
+// 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) }
+
+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 }
+
+
+class PackageAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), PackageDoc {
+ private val allClasses = listOf(node).collectAllTypesRecursively()
+
+ override fun findClass(className: String?): ClassDoc? =
+ 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()
+ override fun exceptions(): Array<out ClassDoc> = node.members(NodeKind.Exception).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun ordinaryClasses(): Array<out ClassDoc> = node.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun interfaces(): Array<out ClassDoc> = node.members(NodeKind.Interface).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun errors(): Array<out ClassDoc> = emptyArray()
+ override fun enums(): Array<out ClassDoc> = node.members(NodeKind.Enum).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun allClasses(filter: Boolean): Array<out ClassDoc> = allClasses.values.map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun allClasses(): Array<out ClassDoc> = allClasses(true)
+
+ override fun isIncluded(): Boolean = node.name in module.allPackages
+}
+
+class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ClassDocumentationNodeAdapter(module, node), AnnotationTypeDoc {
+ override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO
+}
+
+class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc {
+ override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, 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 isPackagePrivate(): Boolean = false
+ override fun isStatic(): Boolean = node.hasModifier("static")
+ override fun modifierSpecifier(): Int = Modifier.PUBLIC + if (isStatic) Modifier.STATIC 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 isFinal(): Boolean = node.hasModifier("final")
+
+ override fun containingPackage(): PackageDoc? {
+ if (node.kind == NodeKind.Type) {
+ return null
+ }
+
+ var owner: DocumentationNode? = node
+ while (owner != null) {
+ if (owner.kind == NodeKind.Package) {
+ return PackageAdapter(module, owner)
+ }
+ owner = owner.owner
+ }
+
+ return null
+ }
+
+ override fun containingClass(): ClassDoc? {
+ if (node.kind == NodeKind.Type) {
+ return null
+ }
+
+ var owner = node.owner
+ while (owner != null) {
+ if (owner.kind in NodeKind.classLike) {
+ return ClassDocumentationNodeAdapter(module, owner)
+ }
+ owner = owner.owner
+ }
+
+ return null
+ }
+
+ override fun isPrivate(): Boolean = false
+ override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true
+}
+
+open class TypeAdapter(override val module: ModuleNodeAdapter, override val node: DocumentationNode) : Type, HasDocumentationNode, HasModule {
+ 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 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.firstOrNull { it.kind != NodeKind.ExternalLink } != null -> {
+ TypeAdapter(module, node.links.firstOrNull { it.kind != NodeKind.ExternalLink }!!).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
+
+ 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
+ override fun getElementType(): Type? = javaLanguageService.getArrayElementType(node)?.let { et -> TypeAdapter(module, et) }
+ override fun asWildcardType(): WildcardType? = null
+
+ override fun toString(): String = qualifiedTypeName() + dimension()
+ override fun hashCode(): Int = node.name.hashCode()
+ override fun equals(other: Any?): Boolean = other is TypeAdapter && toString() == other.toString()
+}
+
+class NotAnnotatedTypeAdapter(typeAdapter: AnnotatedTypeAdapter) : Type by typeAdapter {
+ override fun asAnnotatedType() = null
+}
+
+class AnnotatedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), AnnotatedType {
+ override fun underlyingType(): Type? = NotAnnotatedTypeAdapter(this)
+ override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
+}
+
+class WildcardTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), WildcardType {
+ override fun extendsBounds(): Array<out Type> = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray()
+ override fun superBounds(): Array<out Type> = node.details(NodeKind.LowerBound).map { TypeAdapter(module, it) }.toTypedArray()
+}
+
+class TypeVariableAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), TypeVariable {
+ override fun owner(): ProgramElementDoc = node.owner!!.let<DocumentationNode, ProgramElementDoc> { owner ->
+ when (owner.kind) {
+ NodeKind.Function,
+ NodeKind.Constructor -> ExecutableMemberAdapter(module, owner)
+
+ NodeKind.Class,
+ NodeKind.Interface,
+ NodeKind.Enum -> ClassDocumentationNodeAdapter(module, owner)
+
+ else -> ProgramElementAdapter(module, node.owner!!)
+ }
+ }
+
+ override fun bounds(): Array<out Type>? = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray()
+ override fun annotations(): Array<out AnnotationDesc>? = node.members(NodeKind.Annotation).map { AnnotationDescAdapter(module, it) }.toTypedArray()
+
+ override fun qualifiedTypeName(): String = node.name
+ override fun simpleTypeName(): String = node.name
+ override fun typeName(): String = node.name
+
+ override fun hashCode(): Int = node.name.hashCode()
+ override fun equals(other: Any?): Boolean = other is Type && other.typeName() == typeName() && other.asTypeVariable()?.owner() == owner()
+
+ override fun asTypeVariable(): TypeVariableAdapter = this
+}
+
+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) }
+
+ override fun interfaceTypes(): Array<out Type> =
+ node.lookupSuperClasses(module)
+ .filter { it.kind == NodeKind.Interface }
+ .map { ClassDocumentationNodeAdapter(module, it) }
+ .toTypedArray()
+
+ override fun containingType(): Type? = when (node.owner?.kind) {
+ NodeKind.Package -> null
+ NodeKind.Class,
+ NodeKind.Interface,
+ NodeKind.Object,
+ NodeKind.Enum -> ClassDocumentationNodeAdapter(module, node.owner!!)
+
+ else -> null
+ }
+}
+
+class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter {
+ override fun typeName(): String? = JavaLanguageService().renderType(node.detail(NodeKind.Type))
+ override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type))
+ override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
+}
+
+class ReceiverParameterAdapter(module: ModuleNodeAdapter, val receiverType: DocumentationNode, val parent: ExecutableMemberAdapter) : DocumentationNodeAdapter(module, receiverType), Parameter {
+ override fun typeName(): String? = receiverType.name
+ override fun type(): Type? = TypeAdapter(module, receiverType)
+ override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
+ override fun name(): String = tryName("receiver")
+
+ private tailrec fun tryName(name: String): String = when (name) {
+ in parent.parameters().drop(1).map { it.name() } -> tryName("$$name")
+ else -> name
+ }
+}
+
+fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode(fqName.substringAfterLast(".", fqName), Content.Empty, kind).let { node ->
+ val pkg = fqName.substringBeforeLast(".", "")
+ if (pkg.isNotEmpty()) {
+ node.append(DocumentationNode(pkg, Content.Empty, NodeKind.Package), RefKind.Owner)
+ }
+
+ node
+}
+
+private fun DocumentationNode.hasNonEmptyContent() =
+ 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 {
+
+ override fun isSynthetic(): Boolean = false
+ override fun isNative(): Boolean = node.annotations.any { it.name == "native" }
+
+ 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()
+
+ override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO
+
+ 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() } })
+
+ 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).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()
+
+ 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() } })
+
+ private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers ->
+ when {
+ receivers.isNotEmpty() -> receivers.single().detail(NodeKind.Type)
+ else -> null
+ }
+ }
+}
+
+class ConstructorAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), ConstructorDoc {
+ override fun name(): String = node.owner?.name ?: throw IllegalStateException("No owner for $node")
+
+ override fun containingClass(): ClassDoc? {
+ return super.containingClass()
+ }
+}
+
+class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), MethodDoc {
+ override fun overrides(meth: MethodDoc?): Boolean = false // TODO
+
+ override fun overriddenType(): Type? = node.overrides.firstOrNull()?.owner?.let { owner -> TypeAdapter(module, owner) }
+
+ override fun overriddenMethod(): MethodDoc? = node.overrides.map { MethodAdapter(module, it) }.firstOrNull()
+ override fun overriddenClass(): ClassDoc? = overriddenMethod()?.containingClass()
+
+ override fun isAbstract(): Boolean = false // TODO
+
+ override fun isDefault(): Boolean = false
+
+ override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type))
+
+ override fun tags(tagname: String?) = super.tags(tagname)
+
+ override fun tags(): Array<out Tag> {
+ val tags = super.tags().toMutableList()
+ node.content.findSectionByTag(ContentTags.Return)?.let {
+ tags += ReturnTagAdapter(module, this, it.children)
+ }
+
+ return tags.toTypedArray()
+ }
+}
+
+class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), FieldDoc {
+ override fun isSynthetic(): Boolean = false
+
+ override fun constantValueExpression(): String? = node.detailOrNull(NodeKind.Value)?.let { it.name }
+ override fun constantValue(): Any? = constantValueExpression()
+
+ override fun type(): Type = TypeAdapter(module, node.detail(NodeKind.Type))
+ override fun isTransient(): Boolean = node.hasAnnotation(Transient::class)
+ override fun serialFieldTags(): Array<out SerialFieldTag> = emptyArray()
+
+ override fun isVolatile(): Boolean = node.hasAnnotation(Volatile::class)
+}
+open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNode: DocumentationNode)
+ : ProgramElementAdapter(module, classNode),
+ Type by TypeAdapter(module, classNode),
+ ClassDoc {
+
+ override fun name(): String {
+ val parent = classNode.owner
+ if (parent?.kind in NodeKind.classLike) {
+ return parent!!.name + "." + classNode.name
+ }
+ return classNode.simpleName()
+ }
+
+ 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()
+ override fun importedClasses(): Array<out ClassDoc>? = emptyArray()
+ override fun typeParameters(): Array<out TypeVariable> = classNode.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray()
+ override fun asTypeVariable(): TypeVariable? = if (classNode.kind == NodeKind.Class) TypeVariableAdapter(module, classNode) else null
+ override fun isExternalizable(): Boolean = interfaces().any { it.qualifiedName() == "java.io.Externalizable" }
+ override fun definesSerializableFields(): Boolean = false
+ override fun methods(filter: Boolean): Array<out MethodDoc> = classNode.members(NodeKind.Function).map { MethodAdapter(module, it) }.toTypedArray() // TODO include get/set methods
+ override fun methods(): Array<out MethodDoc> = methods(true)
+ 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()
+
+ override fun interfaces(): Array<out ClassDoc> = classNode.lookupSuperClasses(module)
+ .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() } })
+
+ 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()
+
+ override fun findClass(className: String?): ClassDoc? = null // TODO !!!
+ override fun serializableFields(): Array<out FieldDoc> = emptyArray()
+ override fun superclassType(): Type? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) }
+ override fun serializationMethods(): Array<out MethodDoc> = emptyArray() // TODO
+ override fun superclass(): ClassDoc? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) }
+ override fun isSerializable(): Boolean = false // TODO
+ override fun subclassOf(cd: ClassDoc?): Boolean {
+ if (cd == null) {
+ return false
+ }
+
+ val expectedFQName = cd.qualifiedName()
+ val types = arrayListOf(classNode)
+ val visitedTypes = HashSet<String>()
+
+ while (types.isNotEmpty()) {
+ val type = types.removeAt(types.lastIndex)
+ val fqName = type.qualifiedName()
+
+ if (expectedFQName == fqName) {
+ return true
+ }
+
+ visitedTypes.add(fqName)
+ types.addAll(type.details(NodeKind.Supertype).filter { it.qualifiedName() !in visitedTypes })
+ }
+
+ return false
+ }
+
+ override fun innerClasses(): Array<out ClassDoc> = classNode.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray()
+ override fun innerClasses(filter: Boolean): Array<out ClassDoc> = innerClasses()
+}
+
+fun DocumentationNode.lookupSuperClasses(module: ModuleNodeAdapter) =
+ details(NodeKind.Supertype)
+ .map { it.links.firstOrNull() }
+ .map { module.allTypes[it?.qualifiedName()] }
+ .filterNotNull()
+
+fun List<DocumentationNode>.collectAllTypesRecursively(): Map<String, DocumentationNode> {
+ val result = hashMapOf<String, DocumentationNode>()
+
+ fun DocumentationNode.collectTypesRecursively() {
+ val classLikeMembers = NodeKind.classLike.flatMap { members(it) }
+ classLikeMembers.forEach {
+ result.put(it.qualifiedName(), it)
+ it.collectTypesRecursively()
+ }
+ }
+
+ forEach { it.collectTypesRecursively() }
+ return result
+}
+
+class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorReporter, val outputPath: String) : DocumentationNodeBareAdapter(module), DocErrorReporter by reporter, RootDoc {
+ val allPackages = module.members(NodeKind.Package).associateBy { it.name }
+ val allTypes = module.members(NodeKind.Package).collectAllTypesRecursively()
+
+ 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()
+
+ override fun options(): Array<out Array<String>> = arrayOf(
+ 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) }
+
+ 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
diff --git a/core/src/main/kotlin/javadoc/dokka-adapters.kt b/core/src/main/kotlin/javadoc/dokka-adapters.kt
new file mode 100644
index 000000000..483fb3cdc
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/dokka-adapters.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.javadoc
+
+import com.google.inject.Binder
+import com.google.inject.Inject
+import com.sun.tools.doclets.formats.html.HtmlDoclet
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.*
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.toType
+
+class JavadocGenerator @Inject constructor(val options: DocumentationOptions, val logger: DokkaLogger) : Generator {
+
+ override fun buildPages(nodes: Iterable<DocumentationNode>) {
+ val module = nodes.single() as DocumentationModule
+
+ HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), options.outputDir))
+ }
+
+ override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
+ // no outline could be generated separately
+ }
+
+ override fun buildSupportFiles() {
+ }
+
+ override fun buildPackageList(nodes: Iterable<DocumentationNode>) {
+ // handled by javadoc itself
+ }
+}
+
+class JavadocFormatDescriptor :
+ FormatDescriptor,
+ DefaultAnalysisComponent,
+ DefaultAnalysisComponentServices by KotlinAsJava {
+
+ override fun configureOutput(binder: Binder): Unit = with(binder) {
+ bind<Generator>() toType JavadocGenerator::class
+ }
+}
diff --git a/core/src/main/kotlin/javadoc/reporter.kt b/core/src/main/kotlin/javadoc/reporter.kt
new file mode 100644
index 000000000..fc38368c9
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/reporter.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.DocErrorReporter
+import com.sun.javadoc.SourcePosition
+import org.jetbrains.dokka.DokkaLogger
+
+class StandardReporter(val logger: DokkaLogger) : DocErrorReporter {
+ override fun printWarning(msg: String?) {
+ logger.warn(msg.toString())
+ }
+
+ override fun printWarning(pos: SourcePosition?, msg: String?) {
+ logger.warn(format(pos, msg))
+ }
+
+ override fun printError(msg: String?) {
+ logger.error(msg.toString())
+ }
+
+ override fun printError(pos: SourcePosition?, msg: String?) {
+ logger.error(format(pos, msg))
+ }
+
+ override fun printNotice(msg: String?) {
+ logger.info(msg.toString())
+ }
+
+ override fun printNotice(pos: SourcePosition?, msg: String?) {
+ logger.info(format(pos, msg))
+ }
+
+ private fun format(pos: SourcePosition?, msg: String?) =
+ if (pos == null) msg.toString() else "${pos.file()}:${pos.line()}:${pos.column()}: $msg"
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/javadoc/source-position.kt b/core/src/main/kotlin/javadoc/source-position.kt
new file mode 100644
index 000000000..6125f9689
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/source-position.kt
@@ -0,0 +1,19 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.SourcePosition
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.NodeKind
+import java.io.File
+
+class SourcePositionAdapter(val docNode: DocumentationNode) : SourcePosition {
+
+ private val sourcePositionParts: List<String> by lazy {
+ docNode.details(NodeKind.SourcePosition).firstOrNull()?.name?.split(":") ?: emptyList()
+ }
+
+ override fun file(): File? = if (sourcePositionParts.isEmpty()) null else File(sourcePositionParts[0])
+
+ override fun line(): Int = sourcePositionParts.getOrNull(1)?.toInt() ?: -1
+
+ override fun column(): Int = sourcePositionParts.getOrNull(2)?.toInt() ?: -1
+}
diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt
new file mode 100644
index 000000000..95c6e87fc
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/tags.kt
@@ -0,0 +1,240 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.*
+import org.jetbrains.dokka.*
+import java.util.*
+
+class TagImpl(val holder: Doc, val name: String, val text: String): Tag {
+ override fun text(): String? = text
+
+ override fun holder(): Doc = holder
+ override fun firstSentenceTags(): Array<out Tag>? = arrayOf()
+ override fun inlineTags(): Array<out Tag>? = arrayOf()
+
+ override fun name(): String = name
+ override fun kind(): String = name
+
+ override fun position(): SourcePosition = holder.position()
+}
+
+class TextTag(val holder: Doc, val content: ContentText) : Tag {
+ val plainText: String
+ get() = content.text
+
+ override fun name(): String = "Text"
+ override fun kind(): String = name()
+ override fun text(): String? = plainText
+ override fun inlineTags(): Array<out Tag> = arrayOf(this)
+ override fun holder(): Doc = holder
+ override fun firstSentenceTags(): Array<out Tag> = arrayOf(this)
+ override fun position(): SourcePosition = holder.position()
+}
+
+abstract class SeeTagAdapter(val holder: Doc, val content: ContentNodeLink) : SeeTag {
+ override fun position(): SourcePosition? = holder.position()
+ override fun name(): String = "@see"
+ override fun kind(): String = "@see"
+ override fun holder(): Doc = holder
+
+ override fun text(): String? = content.node?.name ?: "(?)"
+}
+
+class SeeExternalLinkTagAdapter(val holder: Doc, val link: ContentExternalLink) : SeeTag {
+ override fun position(): SourcePosition = holder.position()
+ override fun text(): String = label()
+ override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
+
+ override fun label(): String {
+ val label = link.asText() ?: link.href
+ return "<a href=\"${link.href}\">$label</a>"
+ }
+
+ override fun referencedPackage(): PackageDoc? = null
+ override fun referencedClass(): ClassDoc? = null
+ override fun referencedMemberName(): String? = null
+ override fun referencedClassName(): String? = null
+ override fun referencedMember(): MemberDoc? = null
+ override fun holder(): Doc = holder
+ override fun firstSentenceTags(): Array<out Tag> = inlineTags()
+ override fun name(): String = "@link"
+ override fun kind(): String = "@see"
+}
+
+fun ContentBlock.asText(): String? {
+ val contentText = children.singleOrNull() as? ContentText
+ return contentText?.text
+}
+
+class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) {
+ override fun referencedMember(): MemberDoc = method
+ override fun referencedMemberName(): String = method.name()
+ 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 inlineTags(): Array<out Tag> = emptyArray() // TODO
+ override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
+}
+
+class SeeClassTagAdapter(holder: Doc, val clazz: ClassDocumentationNodeAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) {
+ override fun referencedMember(): MemberDoc? = null
+ override fun referencedMemberName(): String? = null
+ override fun referencedPackage(): PackageDoc? = null
+ override fun referencedClass(): ClassDoc = clazz
+ override fun referencedClassName(): String = clazz.name()
+ override fun label(): String = "${clazz.classNode.kind.name.toLowerCase()} ${clazz.name()}"
+
+ override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
+ override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
+}
+
+class ParamTagAdapter(val module: ModuleNodeAdapter,
+ val holder: Doc,
+ val parameterName: String,
+ val typeParameter: Boolean,
+ val content: List<ContentNode>) : ParamTag {
+
+ constructor(module: ModuleNodeAdapter, holder: Doc, parameterName: String, isTypeParameter: Boolean, content: ContentNode)
+ : this(module, holder, parameterName, isTypeParameter, listOf(content)) {
+ }
+
+ override fun name(): String = "@param"
+ override fun kind(): String = name()
+ override fun holder(): Doc = holder
+ override fun position(): SourcePosition? = holder.position()
+
+ override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug
+ override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray()
+ override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text())))
+
+ override fun isTypeParameter(): Boolean = typeParameter
+ override fun parameterComment(): String = content.toString() // TODO
+ override fun parameterName(): String = parameterName
+}
+
+
+class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag {
+ override fun name(): String = "@throws"
+ override fun kind(): String = name()
+ override fun holder(): Doc = holder
+ override fun position(): SourcePosition? = holder.position()
+
+ override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}"
+ override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray()
+ override fun firstSentenceTags(): Array<out Tag> = emptyArray()
+
+ override fun exceptionComment(): String = content.toString()
+ override fun exceptionType(): Type = type
+ override fun exception(): ClassDoc = type
+ override fun exceptionName(): String = type.qualifiedTypeName()
+}
+
+class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag {
+ override fun name(): String = "@return"
+ override fun kind() = name()
+ override fun holder() = holder
+ override fun position(): SourcePosition? = holder.position()
+
+ override fun text(): String = "@return $content" // Seems has no effect, so used for debug
+ override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray()
+ override fun firstSentenceTags(): Array<Tag> = inlineTags()
+}
+
+fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } }
+
+fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) }
+
+private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) {
+ nodes.forEach {
+ buildInlineTags(module, holder, it, result)
+ }
+}
+
+
+private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, node: ContentNode, result: MutableList<Tag>) {
+ fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentBlock, result: MutableList<Tag>) {
+ if (node.children.isNotEmpty()) {
+ val open = TextTag(holder, ContentText(prefix))
+ val close = TextTag(holder, ContentText(postfix))
+
+ result.add(open)
+ buildInlineTags(module, holder, node.children, result)
+
+ if (result.last() === open) {
+ result.removeAt(result.lastIndex)
+ } else {
+ result.add(close)
+ }
+ }
+ }
+
+ fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentNode, result: MutableList<Tag>) {
+ if (node !is ContentEmpty) {
+ val open = TextTag(holder, ContentText(prefix))
+ val close = TextTag(holder, ContentText(postfix))
+
+ result.add(open)
+ buildInlineTags(module, holder, node, result)
+ if (result.last() === open) {
+ result.removeAt(result.lastIndex)
+ } else {
+ result.add(close)
+ }
+ }
+ }
+
+ when (node) {
+ is ContentText -> result.add(TextTag(holder, node))
+ is ContentNodeLink -> {
+ val target = node.node
+ when (target?.kind) {
+ NodeKind.Function -> result.add(SeeMethodTagAdapter(holder, MethodAdapter(module, node.node!!), node))
+
+ in NodeKind.classLike -> result.add(SeeClassTagAdapter(holder, ClassDocumentationNodeAdapter(module, node.node!!), node))
+
+ else -> buildInlineTags(module, holder, node.children, result)
+ }
+ }
+ is ContentExternalLink -> result.add(SeeExternalLinkTagAdapter(holder, node))
+ is ContentSpecialReference -> surroundWith(module, holder, "<aside class=\"note\">", "</aside>", node, result)
+ is ContentCode -> surroundWith(module, holder, "<code>", "</code>", node, result)
+ is ContentBlockCode -> surroundWith(module, holder, "<code><pre>", "</pre></code>", node, result)
+ is ContentEmpty -> {}
+ is ContentEmphasis -> surroundWith(module, holder, "<em>", "</em>", node, result)
+ is ContentHeading -> surroundWith(module, holder, "<h${node.level}>", "</h${node.level}>", node, result)
+ is ContentEntity -> result.add(TextTag(holder, ContentText(node.text))) // TODO ??
+ is ContentIdentifier -> result.add(TextTag(holder, ContentText(node.text))) // TODO
+ is ContentKeyword -> result.add(TextTag(holder, ContentText(node.text))) // TODO
+ is ContentListItem -> surroundWith(module, holder, "<li>", "</li>", node, result)
+ is ContentOrderedList -> surroundWith(module, holder, "<ol>", "</ol>", node, result)
+ is ContentUnorderedList -> surroundWith(module, holder, "<ul>", "</ul>", node, result)
+ is ContentParagraph -> surroundWith(module, holder, "<p>", "</p>", node, result)
+
+ is ContentDescriptionList -> surroundWith(module, holder, "<dl>", "</dl>", node, result)
+ is ContentDescriptionTerm -> surroundWith(module, holder, "<dt>", "</dt>", node, result)
+ is ContentDescriptionDefinition -> surroundWith(module, holder, "<dd>", "</dd>", node, result)
+
+ is ContentTable -> surroundWith(module, holder, "<table>", "</table>", node, result)
+ is ContentTableBody -> surroundWith(module, holder, "<tbody>", "</tbody>", node, result)
+ is ContentTableRow -> surroundWith(module, holder, "<tr>", "</tr>", node, result)
+ is ContentTableHeader -> surroundWith(module, holder, "<th>", "</th>", node, result)
+ is ContentTableCell -> surroundWith(module, holder, "<td>", "</td>", node, result)
+
+ is ContentSection -> surroundWith(module, holder, "<p>", "</p>", node, result) // TODO how section should be represented?
+ is ContentNonBreakingSpace -> result.add(TextTag(holder, ContentText("&nbsp;")))
+ is ContentStrikethrough -> surroundWith(module, holder, "<strike>", "</strike>", node, result)
+ is ContentStrong -> surroundWith(module, holder, "<strong>", "</strong>", node, result)
+ is ContentSymbol -> result.add(TextTag(holder, ContentText(node.text))) // TODO?
+ is Content -> {
+ surroundWith(module, holder, "<p>", "</p>", node.summary, result)
+ surroundWith(module, holder, "<p>", "</p>", node.description, result)
+ }
+ is ContentBlock -> {
+ surroundWith(module, holder, "", "", node, result)
+ }
+ is ContentHardLineBreak -> result.add(TextTag(holder, ContentText("<br/>")))
+
+ else -> result.add(TextTag(holder, ContentText("$node")))
+ }
+} \ No newline at end of file
diff --git a/core/src/main/resources/META-INF/MANIFEST.MF b/core/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..87807e1e3
--- /dev/null
+++ b/core/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Class-Path: kotlin-plugin.jar
+Main-Class: org.jetbrains.dokka.MainKt
+
diff --git a/core/src/main/resources/dokka/format/dac-as-java.properties b/core/src/main/resources/dokka/format/dac-as-java.properties
new file mode 100644
index 000000000..29e05b3fd
--- /dev/null
+++ b/core/src/main/resources/dokka/format/dac-as-java.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.DacAsJavaFormatDescriptor
+description=Generates developer.android.com website documentation \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/dac.properties b/core/src/main/resources/dokka/format/dac.properties
new file mode 100644
index 000000000..52b19097f
--- /dev/null
+++ b/core/src/main/resources/dokka/format/dac.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.DacFormatDescriptor
+description=Generates developer.android.com website documentation \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/gfm.properties b/core/src/main/resources/dokka/format/gfm.properties
new file mode 100644
index 000000000..5e8f7aa8c
--- /dev/null
+++ b/core/src/main/resources/dokka/format/gfm.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.GFMFormatDescriptor
+description=Produces documentation in GitHub-flavored markdown format
diff --git a/core/src/main/resources/dokka/format/html-as-java.properties b/core/src/main/resources/dokka/format/html-as-java.properties
new file mode 100644
index 000000000..f598f3771
--- /dev/null
+++ b/core/src/main/resources/dokka/format/html-as-java.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.HtmlAsJavaFormatDescriptor
+description=Produces output in HTML format using Java syntax \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/html.properties b/core/src/main/resources/dokka/format/html.properties
new file mode 100644
index 000000000..7881dfae8
--- /dev/null
+++ b/core/src/main/resources/dokka/format/html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.HtmlFormatDescriptor
+description=Produces output in HTML format \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/java-layout-html-as-java.properties b/core/src/main/resources/dokka/format/java-layout-html-as-java.properties
new file mode 100644
index 000000000..7d178ba4a
--- /dev/null
+++ b/core/src/main/resources/dokka/format/java-layout-html-as-java.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlAsJavaFormatDescriptor
+description=Produces Java Style Docs with Javadoc like layout \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/java-layout-html.properties b/core/src/main/resources/dokka/format/java-layout-html.properties
new file mode 100644
index 000000000..fbb2bbedc
--- /dev/null
+++ b/core/src/main/resources/dokka/format/java-layout-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
+description=Produces Kotlin Style Docs with Javadoc like layout \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/javadoc.properties b/core/src/main/resources/dokka/format/javadoc.properties
new file mode 100644
index 000000000..a0d8a945d
--- /dev/null
+++ b/core/src/main/resources/dokka/format/javadoc.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor
+description=Produces Javadoc, with Kotlin declarations as Java view \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/jekyll.properties b/core/src/main/resources/dokka/format/jekyll.properties
new file mode 100644
index 000000000..b11401a4b
--- /dev/null
+++ b/core/src/main/resources/dokka/format/jekyll.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JekyllFormatDescriptor
+description=Produces documentation in Jekyll format \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/kotlin-website-html.properties b/core/src/main/resources/dokka/format/kotlin-website-html.properties
new file mode 100644
index 000000000..f4c320b9f
--- /dev/null
+++ b/core/src/main/resources/dokka/format/kotlin-website-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.KotlinWebsiteHtmlFormatDescriptor
+description=Generates Kotlin website documentation \ No newline at end of file
diff --git a/core/src/main/resources/dokka/format/kotlin-website-samples.properties b/core/src/main/resources/dokka/format/kotlin-website-samples.properties
new file mode 100644
index 000000000..bda616a41
--- /dev/null
+++ b/core/src/main/resources/dokka/format/kotlin-website-samples.properties
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 000000000..c13e76754
--- /dev/null
+++ b/core/src/main/resources/dokka/format/kotlin-website.properties
@@ -0,0 +1,2 @@
+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/format/markdown.properties b/core/src/main/resources/dokka/format/markdown.properties
new file mode 100644
index 000000000..6217a6df1
--- /dev/null
+++ b/core/src/main/resources/dokka/format/markdown.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.MarkdownFormatDescriptor
+description=Produces documentation in markdown format \ 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 000000000..c484a920d
--- /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 000000000..3b61eabe7
--- /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 000000000..0d5d7d17c
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Javadoc
+description=Uses Javadoc Default resolver \ No newline at end of file
diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css
new file mode 100644
index 000000000..914be69d6
--- /dev/null
+++ b/core/src/main/resources/dokka/styles/style.css
@@ -0,0 +1,283 @@
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700);
+
+body, table {
+ padding:50px;
+ font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color:#555;
+ font-weight:300;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 1440px;
+}
+
+.keyword {
+ color:black;
+ font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
+ font-size:12px;
+}
+
+.symbol {
+ font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
+ font-size:12px;
+}
+
+.identifier {
+ color: darkblue;
+ font-size:12px;
+ font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color:#222;
+ margin:0 0 20px;
+}
+
+p, ul, ol, table, pre, dl {
+ margin:0 0 20px;
+}
+
+h1, h2, h3 {
+ line-height:1.1;
+}
+
+h1 {
+ font-size:28px;
+}
+
+h2 {
+ color:#393939;
+}
+
+h3, h4, h5, h6 {
+ color:#494949;
+}
+
+a {
+ color:#258aaf;
+ font-weight:400;
+ text-decoration:none;
+}
+
+a:hover {
+ color: inherit;
+ text-decoration:underline;
+}
+
+a small {
+ font-size:11px;
+ color:#555;
+ margin-top:-0.6em;
+ display:block;
+}
+
+.wrapper {
+ width:860px;
+ margin:0 auto;
+}
+
+blockquote {
+ border-left:1px solid #e5e5e5;
+ margin:0;
+ padding:0 0 0 20px;
+ font-style:italic;
+}
+
+code, pre {
+ font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal;
+ color:#333;
+ font-size:12px;
+}
+
+pre {
+ display: block;
+/*
+ padding:8px 8px;
+ background: #f8f8f8;
+ border-radius:5px;
+ border:1px solid #e5e5e5;
+*/
+ overflow-x: auto;
+}
+
+table {
+ width:100%;
+ border-collapse:collapse;
+}
+
+th, td {
+ text-align:left;
+ vertical-align: top;
+ padding:5px 10px;
+}
+
+dt {
+ color:#444;
+ font-weight:700;
+}
+
+th {
+ color:#444;
+}
+
+img {
+ max-width:100%;
+}
+
+header {
+ width:270px;
+ float:left;
+ position:fixed;
+}
+
+header ul {
+ list-style:none;
+ height:40px;
+
+ padding:0;
+
+ background: #eee;
+ background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));
+ background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
+ background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
+ background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
+ background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%);
+
+ border-radius:5px;
+ border:1px solid #d2d2d2;
+ box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;
+ width:270px;
+}
+
+header li {
+ width:89px;
+ float:left;
+ border-right:1px solid #d2d2d2;
+ height:40px;
+}
+
+header ul a {
+ line-height:1;
+ font-size:11px;
+ color:#999;
+ display:block;
+ text-align:center;
+ padding-top:6px;
+ height:40px;
+}
+
+strong {
+ color:#222;
+ font-weight:700;
+}
+
+header ul li + li {
+ width:88px;
+ border-left:1px solid #fff;
+}
+
+header ul li + li + li {
+ border-right:none;
+ width:89px;
+}
+
+header ul a strong {
+ font-size:14px;
+ display:block;
+ color:#222;
+}
+
+section {
+ width:500px;
+ float:right;
+ padding-bottom:50px;
+}
+
+small {
+ font-size:11px;
+}
+
+hr {
+ border:0;
+ background:#e5e5e5;
+ height:1px;
+ margin:0 0 20px;
+}
+
+footer {
+ width:270px;
+ float:left;
+ position:fixed;
+ bottom:50px;
+}
+
+@media print, screen and (max-width: 960px) {
+
+ div.wrapper {
+ width:auto;
+ margin:0;
+ }
+
+ header, section, footer {
+ float:none;
+ position:static;
+ width:auto;
+ }
+
+ header {
+ padding-right:320px;
+ }
+
+ section {
+ border:1px solid #e5e5e5;
+ border-width:1px 0;
+ padding:20px 0;
+ margin:0 0 20px;
+ }
+
+ header a small {
+ display:inline;
+ }
+
+ header ul {
+ position:absolute;
+ right:50px;
+ top:52px;
+ }
+}
+
+@media print, screen and (max-width: 720px) {
+ body {
+ word-wrap:break-word;
+ }
+
+ header {
+ padding:0;
+ }
+
+ header ul, header p.view {
+ position:static;
+ }
+
+ pre, code {
+ word-wrap:normal;
+ }
+}
+
+@media print, screen and (max-width: 480px) {
+ body {
+ padding:15px;
+ }
+
+ header ul {
+ display:none;
+ }
+}
+
+@media print {
+ body {
+ padding:0.4in;
+ font-size:12pt;
+ color:#444;
+ }
+}
diff --git a/core/src/test/kotlin/Model/CodeNodeTest.kt b/core/src/test/kotlin/Model/CodeNodeTest.kt
new file mode 100644
index 000000000..ae3e67183
--- /dev/null
+++ b/core/src/test/kotlin/Model/CodeNodeTest.kt
@@ -0,0 +1,14 @@
+package Model
+
+import org.jetbrains.dokka.Model.CodeNode
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class CodeNodeTest {
+
+ @Test fun text_normalisesInitialWhitespace() {
+ val expected = "Expected\ntext in this\ttest"
+ val sut = CodeNode("\n \t \r $expected", "")
+ assertEquals(expected, sut.text())
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
new file mode 100644
index 000000000..ef2923cce
--- /dev/null
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -0,0 +1,302 @@
+package org.jetbrains.dokka.tests
+
+import com.google.inject.Guice
+import com.intellij.openapi.application.PathManager
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.rt.execution.junit.FileComparisonFailure
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
+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.descriptors.DeclarationDescriptor
+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(),
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
+ verifier: (DocumentationModule) -> Unit) {
+ val documentation = DocumentationModule("test")
+
+ val options = DocumentationOptions(
+ "",
+ format,
+ includeNonPublic = includeNonPublic,
+ skipEmptyPackages = false,
+ includeRootPackage = true,
+ sourceLinks = listOf(),
+ perPackageOptions = perPackageOptions,
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ noStdlibLink = noStdlibLink,
+ noJdkLink = false,
+ cacheRoot = "default",
+ languageVersion = null,
+ apiVersion = null,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
+ )
+
+ appendDocumentation(documentation, *roots,
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ options = options)
+ documentation.prepareForGeneration(options)
+
+ verifier(documentation)
+}
+
+fun appendDocumentation(documentation: DocumentationModule,
+ vararg roots: ContentRoot,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ options: DocumentationOptions,
+ defaultPlatforms: List<String> = emptyList()) {
+ val messageCollector = object : MessageCollector {
+ override fun clear() {
+
+ }
+
+ override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
+ when (severity) {
+ CompilerMessageSeverity.STRONG_WARNING,
+ CompilerMessageSeverity.WARNING,
+ CompilerMessageSeverity.LOGGING,
+ CompilerMessageSeverity.OUTPUT,
+ CompilerMessageSeverity.INFO,
+ CompilerMessageSeverity.ERROR -> {
+ println("$severity: $message at $location")
+ }
+ CompilerMessageSeverity.EXCEPTION -> {
+ fail("$severity: $message at $location")
+ }
+ }
+ }
+
+ override fun hasErrors() = false
+ }
+
+ val environment = AnalysisEnvironment(messageCollector)
+ environment.apply {
+ if (withJdk || withKotlinRuntime) {
+ val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class")
+ addClasspath(File(stringRoot))
+ }
+ if (withKotlinRuntime) {
+ val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class")
+ addClasspath(File(kotlinStrictfpRoot))
+ }
+ addRoots(roots.toList())
+
+ loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
+ }
+ val defaultPlatformsProvider = object : DefaultPlatformsProvider {
+ override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms
+ }
+ val injector = Guice.createInjector(
+ DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger))
+ buildDocumentationModule(injector, documentation)
+ Disposer.dispose(environment)
+}
+
+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")
+ }
+ verifyModel(contentRootFromPath(source),
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ format = format,
+ includeNonPublic = includeNonPublic,
+ verifier = verifier)
+}
+
+fun verifyPackageMember(source: String,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ verifier: (DocumentationNode) -> Unit) {
+ verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model ->
+ val pkg = model.members.single()
+ verifier(pkg.members.single())
+ }
+}
+
+fun verifyJavaModel(source: String,
+ withKotlinRuntime: Boolean = false,
+ format: String = "html",
+ verifier: (DocumentationModule) -> Unit) {
+ val tempDir = FileUtil.createTempDirectory("dokka", "")
+ try {
+ val sourceFile = File(source)
+ FileUtil.copy(sourceFile, File(tempDir, sourceFile.name))
+ verifyModel(JavaSourceRoot(tempDir, null), format = format, withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier)
+ }
+ finally {
+ FileUtil.delete(tempDir)
+ }
+}
+
+fun verifyJavaPackageMember(source: String,
+ withKotlinRuntime: Boolean = false,
+ verifier: (DocumentationNode) -> Unit) {
+ verifyJavaModel(source, withKotlinRuntime) { 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,
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+ verifyModel(
+ *roots,
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ format = format,
+ includeNonPublic = includeNonPublic,
+ noStdlibLink = noStdlibLink,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
+ ) {
+ verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
+ }
+}
+
+fun verifyModelOutput(it: DocumentationModule,
+ outputExtension: String,
+ sourcePath: String,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+ val output = StringBuilder()
+ outputGenerator(it, output)
+ val ext = outputExtension.removePrefix(".")
+ val expectedFile = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext))
+ assertEqualsIgnoringSeparators(expectedFile, output.toString())
+}
+
+fun verifyOutput(
+ path: String,
+ outputExtension: String,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ format: String = "html",
+ includeNonPublic: Boolean = true,
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
+ verifyOutput(
+ arrayOf(contentRootFromPath(path)),
+ outputExtension,
+ withJdk,
+ withKotlinRuntime,
+ format,
+ includeNonPublic,
+ noStdlibLink,
+ collectInheritedExtensionsFromLibraries,
+ outputGenerator
+ )
+}
+
+fun verifyJavaOutput(path: String,
+ outputExtension: String,
+ withKotlinRuntime: Boolean = false,
+ format: String = "html",
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+ verifyJavaModel(path, withKotlinRuntime, format) { model ->
+ verifyModelOutput(model, outputExtension, path, outputGenerator)
+ }
+}
+
+fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) {
+ if (!expectedFile.exists()) expectedFile.createNewFile()
+ val expectedText = expectedFile.readText().replace("\r\n", "\n")
+ val actualText = output.replace("\r\n", "\n")
+
+ if(expectedText != actualText)
+ throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath)
+}
+
+fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) {
+ Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n"))
+}
+
+fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder {
+ for (child in node.children) {
+ val childText = child.toTestString()
+ append(childText)
+ }
+ return this
+}
+
+fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
+ when (node) {
+ is ContentText -> {
+ append(node.text)
+ }
+ is ContentEmphasis -> append("*").appendChildren(node).append("*")
+ is ContentBlockCode -> {
+ if (node.language.isNotBlank())
+ appendln("[code lang=${node.language}]")
+ else
+ appendln("[code]")
+ appendChildren(node)
+ appendln()
+ appendln("[/code]")
+ }
+ is ContentNodeLink -> {
+ append("[")
+ appendChildren(node)
+ append(" -> ")
+ append(node.node.toString())
+ append("]")
+ }
+ is ContentBlock -> {
+ appendChildren(node)
+ }
+ is NodeRenderContent -> {
+ append("render(")
+ append(node.node)
+ append(",")
+ append(node.mode)
+ append(")")
+ }
+ is ContentSymbol -> { append(node.text) }
+ is ContentEmpty -> { /* nothing */ }
+ else -> throw IllegalStateException("Don't know how to format node $node")
+ }
+ return this
+}
+
+fun ContentNode.toTestString(): String {
+ val node = this
+ return StringBuilder().apply {
+ appendNode(node)
+ }.toString()
+}
+
+val ContentRoot.path: String
+ get() = when(this) {
+ is KotlinSourceRoot -> path
+ is JavaSourceRoot -> file.path
+ else -> throw UnsupportedOperationException()
+ }
diff --git a/core/src/test/kotlin/format/DacFormatTest.kt b/core/src/test/kotlin/format/DacFormatTest.kt
new file mode 100644
index 000000000..5d8babc3d
--- /dev/null
+++ b/core/src/test/kotlin/format/DacFormatTest.kt
@@ -0,0 +1,58 @@
+package org.jetbrains.dokka.tests.format
+
+import org.jetbrains.dokka.Formats.DacAsJavaFormatDescriptor
+import org.jetbrains.dokka.Formats.DacFormatDescriptor
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase
+import org.junit.Test
+
+class DacFormatTest: DacFormatTestCase() {
+ val dacFormatDescriptor = DacFormatDescriptor()
+ val dacAsJavaFormatDescriptor = DacAsJavaFormatDescriptor()
+ val dacFormat = "dac"
+ val dacAsJavaFormat = "dac-as-java"
+
+ private fun verifyBothFormats(directory: String) {
+ verifyDirectory(directory, dacFormatDescriptor, dacFormat)
+ verifyDirectory(directory, dacAsJavaFormatDescriptor, dacAsJavaFormat)
+ }
+
+ @Test fun javaSeeTag() {
+ verifyBothFormats("javaSeeTag")
+ }
+
+ @Test fun javaConstructor() {
+ verifyBothFormats("javaConstructor")
+ }
+
+ @Test
+ fun javaSeeTagAsJava() {
+ verifyBothFormats("javaSeeTag")
+ }
+
+ @Test
+ fun javaConstructorAsJava() {
+ verifyBothFormats("javaConstructor")
+ }
+
+ @Test
+ fun javaDefaultConstructor() {
+ verifyBothFormats("javaDefaultConstructor")
+ }
+
+ @Test
+ fun javaInheritedMethods() {
+ verifyBothFormats("inheritedMethods")
+ }
+
+ @Test fun javaMethodVisibilities() {
+ verifyBothFormats("javaMethodVisibilities")
+ }
+
+ @Test fun javaClassLinks() {
+ verifyBothFormats("javaClassLinks")
+ }
+
+ @Test fun deprecation() {
+ verifyBothFormats("deprecation")
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/DacFormatTestCase.kt b/core/src/test/kotlin/format/DacFormatTestCase.kt
new file mode 100644
index 000000000..922b58097
--- /dev/null
+++ b/core/src/test/kotlin/format/DacFormatTestCase.kt
@@ -0,0 +1,90 @@
+package org.jetbrains.dokka.tests.format
+
+import com.google.inject.Guice
+import com.google.inject.Injector
+import com.google.inject.Module
+import com.google.inject.name.Names
+import org.jetbrains.dokka.DocumentationOptions
+import org.jetbrains.dokka.DokkaLogger
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator
+import org.jetbrains.dokka.Generator
+import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
+import org.jetbrains.dokka.tests.verifyModel
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
+import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.net.URI
+
+abstract class DacFormatTestCase {
+ @get:Rule
+ var folder = TemporaryFolder()
+
+ protected fun verifyDirectory(directory: String, formatDescriptor: JavaLayoutHtmlFormatDescriptorBase, dokkaFormat: String) {
+ val injector: Injector by lazy {
+ val options =
+ DocumentationOptions(
+ folder.toString(),
+ dokkaFormat,
+ apiVersion = null,
+ languageVersion = null,
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ noStdlibLink = false,
+ noJdkLink = false,
+ collectInheritedExtensionsFromLibraries = true
+ )
+
+ Guice.createInjector(Module { binder ->
+
+ binder.bind<Boolean>().annotatedWith(Names.named("generateClassIndex")).toInstance(false)
+ binder.bind<Boolean>().annotatedWith(Names.named("generatePackageIndex")).toInstance(false)
+
+ binder.bind<String>().annotatedWith(Names.named("dacRoot")).toInstance("")
+ binder.bind<String>().annotatedWith(Names.named("outlineRoot")).toInstance("")
+ binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.root)
+
+ binder.bind<DocumentationOptions>().toProvider { options }
+ binder.bind<DokkaLogger>().toInstance(object : DokkaLogger {
+ override fun info(message: String) {
+ println(message)
+ }
+
+ override fun warn(message: String) {
+ println("WARN: $message")
+ }
+
+ override fun error(message: String) {
+ println("ERROR: $message")
+ }
+ })
+
+ formatDescriptor.configureOutput(binder)
+ })
+ }
+
+
+ val directoryFile = File("testdata/format/dac/$directory")
+ verifyModel(
+ JavaSourceRoot(directoryFile, null), KotlinSourceRoot(directoryFile.path, false),
+ format = dokkaFormat
+ ) { documentationModule ->
+ val nodes = documentationModule.members.single().members
+ with(injector.getInstance(Generator::class.java)) {
+ this as JavaLayoutHtmlFormatGenerator
+ buildPages(listOf(documentationModule))
+ val byLocations = nodes.groupBy { mainUri(it) }
+ val tmpFolder = folder.root.toURI().resolve("${documentationModule.name}/")
+ byLocations.forEach { (loc, node) ->
+ val output = StringBuilder()
+ output.append(tmpFolder.resolve(URI("/").relativize(loc)).toURL().readText())
+ val expectedFile = File(File(directoryFile, dokkaFormat), "${node.first().name}.html")
+ assertEqualsIgnoringSeparators(expectedFile, output.toString())
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt
new file mode 100644
index 000000000..ef9e815d2
--- /dev/null
+++ b/core/src/test/kotlin/format/FileGeneratorTestCase.kt
@@ -0,0 +1,35 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.junit.Before
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+
+
+abstract class FileGeneratorTestCase {
+ abstract val formatService: FormatService
+
+ @get:Rule
+ var folder = TemporaryFolder()
+
+ val fileGenerator = FileGenerator(folder.apply { create() }.root)
+
+ @Before
+ fun bindGenerator() {
+ fileGenerator.formatService = formatService
+ }
+
+ fun buildPagesAndReadInto(nodes: List<DocumentationNode>, sb: StringBuilder) = with(fileGenerator) {
+ buildPages(nodes)
+ val byLocations = nodes.groupBy { location(it) }
+ byLocations.forEach { (loc, _) ->
+ if (byLocations.size > 1) {
+ if (sb.isNotBlank() && !sb.endsWith('\n')) {
+ sb.appendln()
+ }
+ sb.appendln("<!-- File: ${loc.file.relativeTo(root).toUnixString()} -->")
+ }
+ sb.append(loc.file.readText())
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt
new file mode 100644
index 000000000..b90ab2bf2
--- /dev/null
+++ b/core/src/test/kotlin/format/GFMFormatTest.kt
@@ -0,0 +1,28 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.GFMFormatService
+import org.jetbrains.dokka.KotlinLanguageService
+import org.junit.Test
+
+class GFMFormatTest : FileGeneratorTestCase() {
+ override val formatService = GFMFormatService(fileGenerator, KotlinLanguageService(), listOf())
+
+ @Test
+ fun sample() {
+ verifyGFMNodeByName("sample", "Foo")
+ }
+
+ @Test
+ fun listInTableCell() {
+ verifyGFMNodeByName("listInTableCell", "Foo")
+ }
+
+ private fun verifyGFMNodeByName(fileName: String, name: String) {
+ verifyOutput("testdata/format/gfm/$fileName.kt", ".md") { model, output ->
+ buildPagesAndReadInto(
+ model.members.single().members.filter { it.name == name },
+ output
+ )
+ }
+ }
+}
diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt
new file mode 100644
index 000000000..01e9b3c5f
--- /dev/null
+++ b/core/src/test/kotlin/format/HtmlFormatTest.kt
@@ -0,0 +1,182 @@
+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.junit.Test
+import java.io.File
+
+// TODO: add tests back
+class HtmlFormatTest: FileGeneratorTestCase() {
+ override val formatService = HtmlFormatService(fileGenerator, KotlinLanguageService(), HtmlTemplateService.default(), listOf())
+
+ @Test fun classWithCompanionObject() {
+ verifyHtmlNode("classWithCompanionObject")
+ }
+
+ @Test fun htmlEscaping() {
+ verifyHtmlNode("htmlEscaping")
+ }
+
+ @Test fun overloads() {
+ verifyHtmlNodes("overloads") { model -> model.members }
+ }
+
+ @Test fun overloadsWithDescription() {
+ verifyHtmlNode("overloadsWithDescription")
+ }
+
+ @Test fun overloadsWithDifferentDescriptions() {
+ verifyHtmlNode("overloadsWithDifferentDescriptions")
+ }
+
+ @Test fun deprecated() {
+ verifyOutput("testdata/format/deprecated.kt", ".package.html") { model, output ->
+ buildPagesAndReadInto(model.members, output)
+ }
+ verifyOutput("testdata/format/deprecated.kt", ".class.html") { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ }
+
+ @Test fun brokenLink() {
+ verifyHtmlNode("brokenLink")
+ }
+
+ @Test fun codeSpan() {
+ verifyHtmlNode("codeSpan")
+ }
+
+ @Test fun parenthesis() {
+ verifyHtmlNode("parenthesis")
+ }
+
+ @Test fun bracket() {
+ verifyHtmlNode("bracket")
+ }
+
+ @Test fun see() {
+ verifyHtmlNode("see")
+ }
+
+ @Test fun tripleBackticks() {
+ verifyHtmlNode("tripleBackticks")
+ }
+
+ @Test fun typeLink() {
+ verifyHtmlNodes("typeLink") { 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" }
+ }
+ }
+
+ @Test fun codeBlock() {
+ verifyHtmlNode("codeBlock")
+ }
+
+ @Test fun javaLinkTag() {
+ verifyJavaHtmlNode("javaLinkTag")
+ }
+
+ @Test fun javaLinkTagWithLabel() {
+ verifyJavaHtmlNode("javaLinkTagWithLabel")
+ }
+
+ @Test fun javaSeeTag() {
+ verifyJavaHtmlNode("javaSeeTag")
+ }
+
+ @Test fun javaDeprecated() {
+ verifyJavaHtmlNodes("javaDeprecated") { model ->
+ model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" }
+ }
+ }
+
+ @Test fun crossLanguageKotlinExtendsJava() {
+ verifyOutput(arrayOf(
+ KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt", false),
+ 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" } }
+ }
+
+ @Test fun linkWithLabel() {
+ verifyHtmlNodes("linkWithLabel") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ }
+
+ @Test fun entity() {
+ verifyHtmlNodes("entity") { model -> model.members.single().members.filter { it.name == "Bar" } }
+ }
+
+ @Test fun uninterpretedEmphasisCharacters() {
+ verifyHtmlNode("uninterpretedEmphasisCharacters")
+ }
+
+ @Test fun markdownInLinks() {
+ verifyHtmlNode("markdownInLinks")
+ }
+
+ @Test fun returnWithLink() {
+ verifyHtmlNode("returnWithLink")
+ }
+
+ @Test fun linkWithStarProjection() {
+ verifyHtmlNode("linkWithStarProjection", withKotlinRuntime = true)
+ }
+
+ @Test fun functionalTypeWithNamedParameters() {
+ verifyHtmlNode("functionalTypeWithNamedParameters")
+ }
+
+ @Test fun sinceKotlin() {
+ verifyHtmlNode("sinceKotlin")
+ }
+
+ @Test fun blankLineInsideCodeBlock() {
+ verifyHtmlNode("blankLineInsideCodeBlock")
+ }
+
+ @Test fun indentedCodeBlock() {
+ verifyHtmlNode("indentedCodeBlock")
+ }
+
+ private fun verifyHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
+ verifyHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ }
+
+ private fun verifyHtmlNodes(fileName: String,
+ withKotlinRuntime: Boolean = false,
+ nodeFilter: (DocumentationModule) -> List<DocumentationNode>) {
+ verifyOutput("testdata/format/$fileName.kt", ".html", withKotlinRuntime = withKotlinRuntime) { model, output ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+
+ private fun verifyJavaHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) {
+ verifyJavaHtmlNodes(fileName, withKotlinRuntime) { 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 ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+}
+
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
new file mode 100644
index 000000000..59746b10f
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
@@ -0,0 +1,114 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
+import org.junit.Test
+import java.io.File
+import java.net.URL
+
+class JavaLayoutHtmlFormatTest : JavaLayoutHtmlFormatTestCase() {
+ override val formatDescriptor = JavaLayoutHtmlFormatDescriptor()
+
+// @Test
+// fun simple() {
+// verifyNode("simple.kt")
+// }
+//
+//// @Test
+//// fun topLevel() {
+//// verifyPackageNode("topLevel.kt")
+//// }
+//
+// @Test
+// fun codeBlocks() {
+// verifyNode("codeBlocks.kt") { model ->
+// listOf(model.members.single().members.single { it.name == "foo" })
+// }
+// }
+//
+// @Test
+// fun const() {
+// verifyPackageNode("const.kt", noStdlibLink = true)
+// verifyNode("const.kt", noStdlibLink = true) { model ->
+// model.members.single().members.filter { it.kind in NodeKind.classLike }
+// }
+// }
+//
+// @Test
+// fun externalClassExtension() {
+// verifyPackageNode("externalClassExtension.kt")
+// }
+//
+// @Test
+// fun unresolvedExternalClass() {
+// verifyNode("unresolvedExternalClass.kt", noStdlibLink = true) { model ->
+// listOf(model.members.single().members.single { it.name == "MyException" })
+// }
+// }
+//
+// @Test
+// fun genericExtension() {
+// verifyNode("genericExtension.kt", noStdlibLink = true) { model ->
+// model.members.single().members(NodeKind.Class)
+// }
+// }
+//
+//
+// @Test
+// fun sections() {
+// verifyNode("sections.kt", noStdlibLink = true) { model ->
+// model.members.single().members.filter { it.name == "sectionsTest" }
+// }
+// }
+//
+// @Test
+// fun constJava() {
+// verifyNode("ConstJava.java", noStdlibLink = true)
+// }
+//
+// @Test
+// fun inboundLinksInKotlinMode() {
+// val root = "./testdata/format/java-layout-html"
+//
+// val options = DocumentationOptions(
+// "",
+// "java-layout-html",
+// sourceLinks = listOf(),
+// generateClassIndexPage = false,
+// generatePackageIndexPage = false,
+// noStdlibLink = true,
+// apiVersion = null,
+// languageVersion = null,
+// perPackageOptions = listOf(PackageOptionsImpl("foo", suppress = true)),
+// externalDocumentationLinks =
+// listOf(
+// DokkaConfiguration.ExternalDocumentationLink.Builder(
+// URL("file:///"),
+// File(root, "inboundLinksTestPackageList").toURI().toURL()
+// ).build()
+// )
+// )
+//
+//
+// val sourcePath = "$root/inboundLinksInKotlinMode.kt"
+// val documentation = DocumentationModule("test")
+//
+// appendDocumentation(
+// documentation,
+// contentRootFromPath(sourcePath),
+// contentRootFromPath("$root/inboundLinksInKotlinMode.Dep.kt"),
+// withJdk = false,
+// withKotlinRuntime = false,
+// options = options
+// )
+// documentation.prepareForGeneration(options)
+//
+// verifyModelOutput(documentation, ".html", sourcePath) { model, output ->
+// buildPagesAndReadInto(
+// model,
+// model.members.single { it.name == "bar" }.members,
+// output
+// )
+// }
+// }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
new file mode 100644
index 000000000..620f10dda
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
@@ -0,0 +1,117 @@
+package org.jetbrains.dokka.tests
+
+import com.google.inject.Guice
+import com.google.inject.Injector
+import com.google.inject.Module
+import com.google.inject.name.Names
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.DocumentationOptions
+import org.jetbrains.dokka.DokkaLogger
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator
+import org.jetbrains.dokka.Generator
+import org.jetbrains.dokka.Utilities.bind
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.net.URI
+
+abstract class JavaLayoutHtmlFormatTestCase {
+
+ abstract val formatDescriptor: JavaLayoutHtmlFormatDescriptorBase
+
+ @get:Rule
+ var folder = TemporaryFolder()
+
+ var options =
+ DocumentationOptions(
+ "",
+ "java-layout-html",
+ apiVersion = null,
+ languageVersion = null,
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ noStdlibLink = false,
+ noJdkLink = false,
+ collectInheritedExtensionsFromLibraries = true
+ )
+
+ val injector: Injector by lazy {
+ Guice.createInjector(Module { binder ->
+ binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.apply { create() }.root)
+
+ binder.bind<DocumentationOptions>().toProvider { options }
+ binder.bind<DokkaLogger>().toInstance(object : DokkaLogger {
+ override fun info(message: String) {
+ println(message)
+ }
+
+ override fun warn(message: String) {
+ println("WARN: $message")
+ }
+
+ override fun error(message: String) {
+ println("ERROR: $message")
+ }
+
+ })
+
+ formatDescriptor.configureOutput(binder)
+ })
+ }
+
+
+ protected fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) =
+ with(injector.getInstance(Generator::class.java)) {
+ this as JavaLayoutHtmlFormatGenerator
+ buildPages(listOf(model))
+ val byLocations = nodes.groupBy { mainUri(it) }
+ byLocations.forEach { (loc, _) ->
+ sb.appendln("<!-- File: $loc -->")
+ sb.append(folder.root.toURI().resolve(URI("/").relativize(loc)).toURL().readText())
+ }
+ }
+
+
+ protected fun verifyNode(
+ fileName: String,
+ noStdlibLink: Boolean = false,
+ fileExtension: String = ".html",
+ select: (model: DocumentationNode) -> List<DocumentationNode>
+ ) {
+ verifyOutput(
+ "testdata/format/java-layout-html/$fileName",
+ fileExtension,
+ format = "java-layout-html",
+ withKotlinRuntime = true,
+ noStdlibLink = noStdlibLink,
+ collectInheritedExtensionsFromLibraries = true
+ ) { model, output ->
+ buildPagesAndReadInto(
+ model,
+ select(model),
+ output
+ )
+ }
+ }
+
+ protected fun verifyNode(fileName: String, noStdlibLink: Boolean = false) {
+ verifyNode(fileName, noStdlibLink) { model -> listOf(model.members.single().members.single()) }
+ }
+
+ protected fun verifyPackageNode(fileName: String, noStdlibLink: Boolean = false) {
+ verifyOutput(
+ "testdata/format/java-layout-html/$fileName",
+ ".package-summary.html",
+ format = "java-layout-html",
+ withKotlinRuntime = true,
+ noStdlibLink = noStdlibLink
+ ) { model, output ->
+ buildPagesAndReadInto(
+ model,
+ listOf(model.members.single()),
+ output
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
new file mode 100644
index 000000000..01ac58da4
--- /dev/null
+++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
@@ -0,0 +1,74 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.junit.Before
+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",
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ noStdlibLink = true,
+ noJdkLink = true,
+ languageVersion = null,
+ apiVersion = null
+ )
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options)
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options)
+ appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options)
+ 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
new file mode 100644
index 000000000..63d7d5766
--- /dev/null
+++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
@@ -0,0 +1,85 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.junit.Before
+import org.junit.Test
+
+class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() {
+ override val formatService = KotlinWebsiteHtmlFormatService(fileGenerator, KotlinLanguageService(), listOf(), EmptyHtmlTemplateService)
+
+ @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")
+ }
+
+ @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, ".html", "testdata/format/website-html/$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-html/$fileName.kt", ".html", 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",
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ 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)
+ return module
+ }
+
+ private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".package.html", "testdata/format/website-html/$path/multiplatform.kt") { model, output ->
+ buildPagesAndReadInto(model.members, output)
+ }
+ }
+
+}
diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
new file mode 100644
index 000000000..453b1de85
--- /dev/null
+++ b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.DokkaConsoleLogger
+import org.jetbrains.dokka.KotlinLanguageService
+import org.jetbrains.dokka.KotlinWebsiteRunnableSamplesFormatService
+import org.junit.Ignore
+import org.junit.Test
+
+@Ignore
+class KotlinWebSiteRunnableSamplesFormatTest {
+// private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf(), DokkaConsoleLogger)
+//
+//
+// @Test fun dropImport() {
+// verifyKWSNodeByName("dropImport", "foo")
+// }
+//
+// @Test fun sample() {
+// verifyKWSNodeByName("sample", "foo")
+// }
+//
+// @Test fun sampleWithAsserts() {
+// verifyKWSNodeByName("sampleWithAsserts", "a")
+// }
+//
+// @Test fun newLinesInSamples() {
+// verifyKWSNodeByName("newLinesInSamples", "foo")
+// }
+//
+// @Test fun newLinesInImportList() {
+// verifyKWSNodeByName("newLinesInImportList", "foo")
+// }
+//
+// private fun verifyKWSNodeByName(fileName: String, name: String) {
+// verifyOutput("testdata/format/website-samples/$fileName.kt", ".md", format = "kotlin-website-samples") { model, output ->
+// kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
+// }
+// }
+}
diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt
new file mode 100644
index 000000000..08d467995
--- /dev/null
+++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt
@@ -0,0 +1,547 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+class MarkdownFormatTest: FileGeneratorTestCase() {
+ override val formatService = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf())
+
+ @Test fun emptyDescription() {
+ verifyMarkdownNode("emptyDescription")
+ }
+
+ @Test fun classWithCompanionObject() {
+ verifyMarkdownNode("classWithCompanionObject")
+ }
+
+ @Test fun annotations() {
+ verifyMarkdownNode("annotations")
+ }
+
+ @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)
+ }
+ }
+
+ @Test fun enumClass() {
+ verifyOutput("testdata/format/enumClass.kt", ".md") { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ verifyOutput("testdata/format/enumClass.kt", ".value.md") { model, output ->
+ val enumClassNode = model.members.single().members[0]
+ buildPagesAndReadInto(
+ enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" },
+ output
+ )
+ }
+ }
+
+ @Test fun varargsFunction() {
+ verifyMarkdownNode("varargsFunction")
+ }
+
+ @Test fun overridingFunction() {
+ verifyMarkdownNodes("overridingFunction") { model->
+ val classMembers = model.members.single().members.first { it.name == "D" }.members
+ classMembers.filter { it.name == "f" }
+ }
+ }
+
+ @Test fun propertyVar() {
+ verifyMarkdownNode("propertyVar")
+ }
+
+ @Test fun functionWithDefaultParameter() {
+ verifyMarkdownNode("functionWithDefaultParameter")
+ }
+
+ @Test fun accessor() {
+ verifyMarkdownNodes("accessor") { model ->
+ model.members.single().members.first { it.name == "C" }.members.filter { it.name == "x" }
+ }
+ }
+
+ @Test fun paramTag() {
+ verifyMarkdownNode("paramTag")
+ }
+
+ @Test fun throwsTag() {
+ verifyMarkdownNode("throwsTag")
+ }
+
+ @Test fun typeParameterBounds() {
+ verifyMarkdownNode("typeParameterBounds")
+ }
+
+ @Test fun typeParameterVariance() {
+ verifyMarkdownNode("typeParameterVariance")
+ }
+
+ @Test fun typeProjectionVariance() {
+ verifyMarkdownNode("typeProjectionVariance")
+ }
+
+ @Test
+ fun javadocCodeMultiline() {
+ verifyJavaMarkdownNode("javadocCodeMultiline")
+ }
+
+ // TODO: FIXME
+ @Ignore
+ @Test
+ fun javadocHtml() {
+ verifyJavaMarkdownNode("javadocHtml")
+ }
+
+ // TODO: FIXME
+ @Ignore
+ @Test
+ fun javaCodeLiteralTags() {
+ verifyJavaMarkdownNode("javaCodeLiteralTags")
+ }
+
+ @Test
+ fun javaSample() {
+ verifyJavaMarkdownNode("javaSample")
+ }
+
+ // TODO: FIXME
+ @Ignore
+ @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" }
+ }
+ }
+
+ @Test fun codeBlockNoHtmlEscape() {
+ verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic")
+ }
+
+ @Test fun companionObjectExtension() {
+ verifyMarkdownNodeByName("companionObjectExtension", "Foo")
+ }
+
+ @Test fun starProjection() {
+ verifyMarkdownNode("starProjection")
+ }
+
+ @Test fun extensionFunctionParameter() {
+ verifyMarkdownNode("extensionFunctionParameter")
+ }
+
+ @Test fun summarizeSignatures() {
+ verifyMarkdownNodes("summarizeSignatures") { model -> model.members }
+ }
+
+ @Test fun summarizeSignaturesProperty() {
+ verifyMarkdownNodes("summarizeSignaturesProperty") { model -> model.members }
+ }
+
+ @Test fun reifiedTypeParameter() {
+ verifyMarkdownNode("reifiedTypeParameter", withKotlinRuntime = true)
+ }
+
+ @Test fun annotatedTypeParameter() {
+ verifyMarkdownNode("annotatedTypeParameter", withKotlinRuntime = true)
+ }
+
+ @Test fun inheritedMembers() {
+ verifyMarkdownNodeByName("inheritedMembers", "Bar")
+ }
+
+ @Test fun inheritedExtensions() {
+ verifyMarkdownNodeByName("inheritedExtensions", "Bar")
+ }
+
+ @Test fun genericInheritedExtensions() {
+ verifyMarkdownNodeByName("genericInheritedExtensions", "Bar")
+ }
+
+ @Test fun arrayAverage() {
+ verifyMarkdownNodeByName("arrayAverage", "XArray")
+ }
+
+ @Test fun multipleTypeParameterConstraints() {
+ verifyMarkdownNode("multipleTypeParameterConstraints", withKotlinRuntime = true)
+ }
+
+ @Test fun inheritedCompanionObjectProperties() {
+ verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C")
+ }
+
+ @Test fun shadowedExtensionFunctions() {
+ verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar")
+ }
+
+ @Test fun inapplicableExtensionFunctions() {
+ verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar")
+ }
+
+ @Test fun receiverParameterTypeBound() {
+ verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo")
+ }
+
+ @Test fun extensionWithDocumentedReceiver() {
+ verifyMarkdownNodes("extensionWithDocumentedReceiver") { model ->
+ model.members.single().members.single().members.filter { it.name == "fn" }
+ }
+ }
+
+ @Test fun codeBlock() {
+ verifyMarkdownNode("codeBlock")
+ }
+
+ @Test fun exclInCodeBlock() {
+ verifyMarkdownNodeByName("exclInCodeBlock", "foo")
+ }
+
+ @Test fun backtickInCodeBlock() {
+ verifyMarkdownNodeByName("backtickInCodeBlock", "foo")
+ }
+
+ @Test fun qualifiedNameLink() {
+ verifyMarkdownNodeByName("qualifiedNameLink", "foo", withKotlinRuntime = true)
+ }
+
+ @Test fun functionalTypeWithNamedParameters() {
+ verifyMarkdownNode("functionalTypeWithNamedParameters")
+ }
+
+ @Test fun typeAliases() {
+ verifyMarkdownNode("typeAliases")
+ verifyMarkdownPackage("typeAliases")
+ }
+
+ @Test fun sampleByFQName() {
+ verifyMarkdownNode("sampleByFQName")
+ }
+
+ @Test fun sampleByShortName() {
+ verifyMarkdownNode("sampleByShortName")
+ }
+
+
+ @Test fun suspendParam() {
+ verifyMarkdownNode("suspendParam")
+ verifyMarkdownPackage("suspendParam")
+ }
+
+ @Test fun sinceKotlin() {
+ verifyMarkdownNode("sinceKotlin")
+ verifyMarkdownPackage("sinceKotlin")
+ }
+
+ @Test fun sinceKotlinWide() {
+ verifyMarkdownPackage("sinceKotlinWide")
+ }
+
+ @Test fun dynamicType() {
+ verifyMarkdownNode("dynamicType")
+ }
+
+ @Test fun dynamicExtension() {
+ verifyMarkdownNodes("dynamicExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ }
+
+ @Test fun memberExtension() {
+ verifyMarkdownNodes("memberExtension") { model -> model.members.single().members.filter { it.name == "Foo" } }
+ }
+
+ @Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() {
+ verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver")
+ }
+
+ @Test fun multiplePlatforms() {
+ verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple")
+ }
+
+ @Test fun multiplePlatformsMerge() {
+ verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge")
+ }
+
+ @Test fun multiplePlatformsMergeMembers() {
+ val module = buildMultiplePlatforms("multiplatform/mergeMembers")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ }
+
+ @Test fun multiplePlatformsOmitRedundant() {
+ val module = buildMultiplePlatforms("multiplatform/omitRedundant")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output ->
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ }
+
+ @Test fun multiplePlatformsImplied() {
+ val module = buildMultiplePlatforms("multiplatform/implied")
+ verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output ->
+ val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf("JVM", "JS"))
+ fileGenerator.formatService = service
+ buildPagesAndReadInto(model.members.single().members, output)
+ }
+ }
+
+ @Test fun packagePlatformsWithExtExtensions() {
+ val path = "multiplatform/packagePlatformsWithExtExtensions"
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions(
+ outputDir = "",
+ outputFormat = "html",
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ noStdlibLink = true,
+ noJdkLink = true,
+ languageVersion = null,
+ apiVersion = null
+ )
+ appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), withKotlinRuntime = true, options = options)
+ verifyMultiplatformIndex(module, path)
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsPackagePlatformFromMembers() {
+ val path = "multiplatform/packagePlatformsFromMembers"
+ val module = buildMultiplePlatforms(path)
+ verifyMultiplatformIndex(module, path)
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsGroupNode() {
+ val path = "multiplatform/groupNode"
+ val module = buildMultiplePlatforms(path)
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }),
+ output
+ )
+ }
+ verifyMultiplatformPackage(module, path)
+ }
+
+ @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() {
+ val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode"
+ val module = buildMultiplePlatforms(path)
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ buildPagesAndReadInto(
+ listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Class)?.member(NodeKind.Function)),
+ output
+ )
+ }
+ }
+
+ @Test fun linksInEmphasis() {
+ verifyMarkdownNode("linksInEmphasis")
+ }
+
+ @Test fun linksInStrong() {
+ verifyMarkdownNode("linksInStrong")
+ }
+
+ @Test fun linksInHeaders() {
+ verifyMarkdownNode("linksInHeaders")
+ }
+
+ @Test fun tokensInEmphasis() {
+ verifyMarkdownNode("tokensInEmphasis")
+ }
+
+ @Test fun tokensInStrong() {
+ verifyMarkdownNode("tokensInStrong")
+ }
+
+ @Test fun tokensInHeaders() {
+ verifyMarkdownNode("tokensInHeaders")
+ }
+
+ @Test fun unorderedLists() {
+ verifyMarkdownNode("unorderedLists")
+ }
+
+ @Test fun nestedLists() {
+ verifyMarkdownNode("nestedLists")
+ }
+
+ @Test fun referenceLink() {
+ verifyMarkdownNode("referenceLink")
+ }
+
+ @Test fun externalReferenceLink() {
+ verifyMarkdownNode("externalReferenceLink")
+ }
+
+ @Test fun newlineInTableCell() {
+ verifyMarkdownPackage("newlineInTableCell")
+ }
+
+ @Test fun indentedCodeBlock() {
+ verifyMarkdownNode("indentedCodeBlock")
+ }
+
+ @Test fun receiverReference() {
+ verifyMarkdownNode("receiverReference")
+ }
+
+ @Test fun extensionScope() {
+ verifyMarkdownNodeByName("extensionScope", "test")
+ }
+
+ @Test fun typeParameterReference() {
+ verifyMarkdownNode("typeParameterReference")
+ }
+
+ @Test fun notPublishedTypeAliasAutoExpansion() {
+ verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", 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)
+ }
+ }
+
+
+ private fun buildMultiplePlatforms(path: String): DocumentationModule {
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions(
+ outputDir = "",
+ outputFormat = "html",
+ generateClassIndexPage = false,
+ generatePackageIndexPage = false,
+ 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)
+ return module
+ }
+
+ private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output ->
+ buildPagesAndReadInto(model.members, output)
+ }
+ }
+
+ private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) {
+ verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") {
+ model, output ->
+ val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf())
+ fileGenerator.formatService = service
+ buildPagesAndReadInto(listOf(model), output)
+ }
+ }
+
+ @Test fun blankLineInsideCodeBlock() {
+ verifyMarkdownNode("blankLineInsideCodeBlock")
+ }
+
+ private fun verifyMarkdownPackage(fileName: String, withKotlinRuntime: Boolean = false) {
+ verifyOutput("testdata/format/$fileName.kt", ".package.md", withKotlinRuntime = withKotlinRuntime) { model, output ->
+ buildPagesAndReadInto(model.members, output)
+ }
+ }
+
+ private fun verifyMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) {
+ verifyMarkdownNodes(fileName, withKotlinRuntime) { model -> model.members.single().members }
+ }
+
+ private fun verifyMarkdownNodes(
+ fileName: String,
+ withKotlinRuntime: Boolean = false,
+ includeNonPublic: Boolean = true,
+ nodeFilter: (DocumentationModule) -> List<DocumentationNode>
+ ) {
+ verifyOutput(
+ "testdata/format/$fileName.kt",
+ ".md",
+ withKotlinRuntime = withKotlinRuntime,
+ includeNonPublic = includeNonPublic
+ ) { model, output ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+
+ private fun verifyJavaMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) {
+ verifyJavaMarkdownNodes(fileName, withKotlinRuntime) { 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 ->
+ buildPagesAndReadInto(nodeFilter(model), output)
+ }
+ }
+
+ private fun verifyMarkdownNodeByName(
+ fileName: String,
+ name: String,
+ withKotlinRuntime: Boolean = false,
+ includeNonPublic: Boolean = true
+ ) {
+ verifyMarkdownNodes(fileName, withKotlinRuntime, includeNonPublic) { model->
+ val nodesWithName = model.members.single().members.filter { it.name == name }
+ if (nodesWithName.isEmpty()) {
+ throw IllegalArgumentException("Found no nodes named $name")
+ }
+ nodesWithName
+ }
+ }
+}
diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt
new file mode 100644
index 000000000..b7fff1e2e
--- /dev/null
+++ b/core/src/test/kotlin/format/PackageDocsTest.kt
@@ -0,0 +1,92 @@
+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 = 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
+ assertEquals("Core functions and types", block.text)
+ }
+
+ @Test fun testReferenceLinksInPackageDocs() {
+ val mockLinkResolver = mock<DeclarationLinkResolver> {
+ val exampleCom = "http://example.com"
+ on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) }
+ }
+
+ val mockPackageDescriptor = mock<PackageFragmentDescriptor> {}
+
+ val docs = createPackageDocs(mockLinkResolver)
+ docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor))
+
+ checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks")
+ }
+
+ fun checkMarkdownOutput(docs: PackageDocs, expectedFilePrefix: String) {
+
+ val generator = FileGenerator(File(""))
+
+ val out = StringBuilder()
+ val outputBuilder = MarkdownOutputBuilder(
+ out,
+ FileLocation(generator.root),
+ generator,
+ KotlinLanguageService(),
+ ".md",
+ emptyList()
+ )
+ fun checkOutput(content: Content, filePostfix: String) {
+ outputBuilder.appendContent(content)
+ val expectedFile = File(expectedFilePrefix + filePostfix)
+ assertEqualsIgnoringSeparators(expectedFile, out.toString())
+ out.setLength(0)
+ }
+
+ checkOutput(docs.moduleContent, ".module.md")
+
+ docs.packageContent.forEach {
+ (name, content) ->
+ checkOutput(content, ".$name.md")
+ }
+
+ }
+}
diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt
new file mode 100644
index 000000000..d61088180
--- /dev/null
+++ b/core/src/test/kotlin/issues/IssuesTest.kt
@@ -0,0 +1,28 @@
+package issues
+
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.tests.toTestString
+import org.jetbrains.dokka.tests.verifyModel
+import org.junit.Test
+import kotlin.test.assertEquals
+
+
+class IssuesTest {
+
+ @Test
+ fun errorClasses() {
+ verifyModel("testdata/issues/errorClasses.kt", withJdk = true, withKotlinRuntime = true) { model ->
+ val cls = model.members.single().members.single()
+
+ fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name
+ assertEquals("Test", cls.members[1].returnType())
+ assertEquals("List", cls.members[2].returnType())
+ assertEquals("Test", cls.members[3].returnType())
+ assertEquals("Test", cls.members[4].returnType())
+ assertEquals("String", cls.members[5].returnType())
+ assertEquals("String", cls.members[6].returnType())
+ assertEquals("String", cls.members[7].returnType())
+ }
+ }
+}
diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt
new file mode 100644
index 000000000..a42d63933
--- /dev/null
+++ b/core/src/test/kotlin/javadoc/JavadocTest.kt
@@ -0,0 +1,185 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.Tag
+import com.sun.javadoc.Type
+import org.jetbrains.dokka.DokkaConsoleLogger
+import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
+import org.jetbrains.dokka.tests.verifyModel
+import org.junit.Assert.*
+import org.junit.Test
+
+class JavadocTest {
+ @Test fun testTypes() {
+ verifyJavadoc("testdata/javadoc/types.kt", withJdk = true) { doc ->
+ val classDoc = doc.classNamed("foo.TypesKt")!!
+ val method = classDoc.methods().find { it.name() == "foo" }!!
+
+ val type = method.returnType()
+ assertFalse(type.asClassDoc().isIncluded)
+ assertEquals("String", type.qualifiedTypeName())
+ assertEquals("String", type.asClassDoc().qualifiedName())
+
+ val params = method.parameters()
+ assertTrue(params[0].type().isPrimitive)
+ assertFalse(params[1].type().asClassDoc().isIncluded)
+ }
+ }
+
+ @Test fun testObject() {
+ verifyJavadoc("testdata/javadoc/obj.kt") { doc ->
+ val classDoc = doc.classNamed("foo.O")
+ assertNotNull(classDoc)
+
+ val companionDoc = doc.classNamed("foo.O.Companion")
+ assertNotNull(companionDoc)
+
+ val pkgDoc = doc.packageNamed("foo")!!
+ assertEquals(2, pkgDoc.allClasses().size)
+ }
+ }
+
+ @Test fun testException() {
+ verifyJavadoc("testdata/javadoc/exception.kt", withKotlinRuntime = true) { doc ->
+ val classDoc = doc.classNamed("foo.MyException")!!
+ val member = classDoc.methods().find { it.name() == "foo" }
+ assertEquals(classDoc, member!!.containingClass())
+ }
+ }
+
+ @Test fun testByteArray() {
+ verifyJavadoc("testdata/javadoc/bytearr.kt", withKotlinRuntime = true) { doc ->
+ val classDoc = doc.classNamed("foo.ByteArray")!!
+ assertNotNull(classDoc.asClassDoc())
+
+ val member = classDoc.methods().find { it.name() == "foo" }!!
+ assertEquals("[]", member.returnType().dimension())
+ }
+ }
+
+ @Test fun testStringArray() {
+ verifyJavadoc("testdata/javadoc/stringarr.kt", 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.asClassDoc().name())
+ }
+ }
+
+ @Test fun testJvmName() {
+ verifyJavadoc("testdata/javadoc/jvmname.kt", withKotlinRuntime = true) { doc ->
+ val classDoc = doc.classNamed("foo.Apple")!!
+ assertNotNull(classDoc.asClassDoc())
+
+ val member = classDoc.methods().find { it.name() == "_tree" }
+ assertNotNull(member)
+ }
+ }
+
+ @Test fun testLinkWithParam() {
+ verifyJavadoc("testdata/javadoc/paramlink.kt", withKotlinRuntime = true) { doc ->
+ val classDoc = doc.classNamed("demo.Apple")!!
+ assertNotNull(classDoc.asClassDoc())
+ val tags = classDoc.inlineTags().filterIsInstance<SeeTagAdapter>()
+ assertEquals(2, tags.size)
+ val linkTag = tags[1] as SeeMethodTagAdapter
+ assertEquals("cutIntoPieces", linkTag.method.name())
+ }
+ }
+
+ @Test fun testInternalVisibility() {
+ verifyJavadoc("testdata/javadoc/internal.kt", withKotlinRuntime = true, includeNonPublic = false) { doc ->
+ val classDoc = doc.classNamed("foo.Person")!!
+ val constructors = classDoc.constructors()
+ assertEquals(1, constructors.size)
+ assertEquals(1, constructors.single().parameters().size)
+ }
+ }
+
+ @Test fun testSuppress() {
+ verifyJavadoc("testdata/javadoc/suppress.kt", withKotlinRuntime = true) { doc ->
+ assertNull(doc.classNamed("Some"))
+ assertNull(doc.classNamed("SomeAgain"))
+ assertNull(doc.classNamed("Interface"))
+ val classSame = doc.classNamed("Same")!!
+ assertTrue(classSame.fields().isEmpty())
+ assertTrue(classSame.methods().isEmpty())
+ }
+ }
+
+ @Test fun testTypeAliases() {
+ verifyJavadoc("testdata/javadoc/typealiases.kt", withKotlinRuntime = true) { doc ->
+ assertNull(doc.classNamed("B"))
+ assertNull(doc.classNamed("D"))
+
+ assertEquals("A", doc.classNamed("C")!!.superclass().name())
+ val methodParamType = doc.classNamed("TypealiasesKt")!!.methods()
+ .find { it.name() == "some" }!!.parameters().first()
+ .type()
+ assertEquals("Function1", methodParamType.qualifiedTypeName())
+ assertEquals("? super A, C", methodParamType.asParameterizedType().typeArguments()
+ .map(Type::qualifiedTypeName).joinToString())
+ }
+ }
+
+ @Test fun testKDocKeywordsOnMethod() {
+ verifyJavadoc("testdata/javadoc/kdocKeywordsOnMethod.kt", 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())
+ assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text())
+ }
+ }
+
+ @Test
+ fun testBlankLineInsideCodeBlock() {
+ verifyJavadoc("testdata/javadoc/blankLineInsideCodeBlock.kt", withKotlinRuntime = true) { doc ->
+ val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0]
+ val text = method.inlineTags().joinToString(separator = "", transform = Tag::text)
+ assertEqualsIgnoringSeparators("""
+ <p><code><pre>
+ This is a test
+ of Dokka's code blocks.
+ Here is a blank line.
+
+ The previous line was blank.
+ </pre></code></p>
+ """.trimIndent(), text)
+ }
+ }
+
+ @Test
+ fun testCompanionMethodReference() {
+ verifyJavadoc("testdata/javadoc/companionMethodReference.kt") { doc ->
+ val classDoc = doc.classNamed("foo.TestClass")!!
+ val tag = classDoc.inlineTags().filterIsInstance<SeeMethodTagAdapter>().first()
+ assertEquals("TestClass.Companion", tag.referencedClassName())
+ assertEquals("test", tag.referencedMemberName())
+ }
+ }
+
+ @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" })
+ }
+ }
+ }
+
+ private fun verifyJavadoc(name: String,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ includeNonPublic: Boolean = true,
+ callback: (ModuleNodeAdapter) -> Unit) {
+
+ verifyModel(name, format = "javadoc", withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, includeNonPublic = includeNonPublic) { model ->
+ val doc = ModuleNodeAdapter(model, StandardReporter(DokkaConsoleLogger), "")
+ callback(doc)
+ }
+ }
+}
diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt
new file mode 100644
index 000000000..b0ec68ff9
--- /dev/null
+++ b/core/src/test/kotlin/markdown/ParserTest.kt
@@ -0,0 +1,154 @@
+package org.jetbrains.dokka.tests
+
+import org.junit.Test
+import org.jetbrains.dokka.toTestString
+import org.jetbrains.dokka.parseMarkdown
+import org.junit.Ignore
+
+@Ignore public class ParserTest {
+ fun runTestFor(text : String) {
+ println("MD: ---")
+ println(text)
+ val markdownTree = parseMarkdown(text)
+ println("AST: ---")
+ println(markdownTree.toTestString())
+ println()
+ }
+
+ @Test fun text() {
+ runTestFor("text")
+ }
+
+ @Test fun textWithSpaces() {
+ runTestFor("text and string")
+ }
+
+ @Test fun textWithColon() {
+ runTestFor("text and string: cool!")
+ }
+
+ @Test fun link() {
+ runTestFor("text [links]")
+ }
+
+ @Test fun linkWithHref() {
+ runTestFor("text [links](http://google.com)")
+ }
+
+ @Test fun multiline() {
+ runTestFor(
+ """
+text
+and
+string
+""")
+ }
+
+ @Test fun para() {
+ runTestFor(
+ """
+paragraph number
+one
+
+paragraph
+number two
+""")
+ }
+
+ @Test fun bulletList() {
+ runTestFor(
+ """* list item 1
+* list item 2
+""")
+ }
+
+ @Test fun bulletListWithLines() {
+ runTestFor(
+ """
+* list item 1
+ continue 1
+* list item 2
+ continue 2
+ """)
+ }
+
+ @Test fun bulletListStrong() {
+ runTestFor(
+ """
+* list *item* 1
+ continue 1
+* list *item* 2
+ continue 2
+ """)
+ }
+
+ @Test fun emph() {
+ runTestFor("*text*")
+ }
+
+ @Test fun underscoresNoEmph() {
+ runTestFor("text_with_underscores")
+ }
+
+ @Test fun emphUnderscores() {
+ runTestFor("_text_")
+ }
+
+ @Test fun singleStar() {
+ runTestFor("Embedded*Star")
+ }
+
+ @Test fun directive() {
+ runTestFor("A text \${code with.another.value} with directive")
+ }
+
+ @Test fun emphAndEmptySection() {
+ runTestFor("*text*\n\$sec:\n")
+ }
+
+ @Test fun emphAndSection() {
+ runTestFor("*text*\n\$sec: some text\n")
+ }
+
+ @Test fun emphAndBracedSection() {
+ runTestFor("Text *bold* text \n\${sec}: some text")
+ }
+
+ @Test fun section() {
+ runTestFor(
+ "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example")
+ }
+
+ @Test fun anonymousSection() {
+ runTestFor("Summary\n\nDescription\n")
+ }
+
+ @Test fun specialSection() {
+ runTestFor(
+ "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example")
+ }
+
+ @Test fun emptySection() {
+ runTestFor(
+ "Plain text \n\$summary:")
+ }
+
+ val b = "$"
+ @Test fun pair() {
+ runTestFor(
+ """Represents a generic pair of two values.
+
+There is no meaning attached to values in this class, it can be used for any purpose.
+Pair exhibits value semantics, i.e. two pairs are equal if both components are equal.
+
+An example of decomposing it into values:
+${b}{code test.tuples.PairTest.pairMultiAssignment}
+
+${b}constructor: Creates new instance of [Pair]
+${b}first: First value
+${b}second: Second value""""
+ )
+ }
+
+}
+
diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt
new file mode 100644
index 000000000..6bc45db10
--- /dev/null
+++ b/core/src/test/kotlin/model/ClassTest.kt
@@ -0,0 +1,293 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class ClassTest {
+ @Test fun emptyClass() {
+ verifyModel("testdata/classes/emptyClass.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Klass", name)
+ assertEquals(Content.Empty, content)
+ assertEquals("<init>", members.single().name)
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun emptyObject() {
+ verifyModel("testdata/classes/emptyObject.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(NodeKind.Object, kind)
+ assertEquals("Obj", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun classWithConstructor() {
+ verifyModel("testdata/classes/classWithConstructor.kt") { model ->
+ with (model.members.single().members.single()) {
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Klass", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(links.none())
+
+ assertEquals(1, members.count())
+ with(members.elementAt(0)) {
+ assertEquals("<init>", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Constructor, kind)
+ assertEquals(3, details.count())
+ assertEquals("public", details.elementAt(0).name)
+ with(details.elementAt(2)) {
+ assertEquals("name", name)
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", detail(NodeKind.Type).name)
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ }
+ }
+ }
+
+ @Test fun classWithFunction() {
+ verifyModel("testdata/classes/classWithFunction.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Klass", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(links.none())
+
+ assertEquals(2, members.count())
+ with(members.elementAt(0)) {
+ assertEquals("<init>", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Constructor, kind)
+ assertEquals(2, details.count())
+ assertEquals("public", details.elementAt(0).name)
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ with(members.elementAt(1)) {
+ assertEquals("fn", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("Unit", detail(NodeKind.Type).name)
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ }
+ }
+ }
+
+ @Test fun classWithProperty() {
+ verifyModel("testdata/classes/classWithProperty.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Klass", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(links.none())
+
+ assertEquals(2, members.count())
+ with(members.elementAt(0)) {
+ assertEquals("<init>", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Constructor, kind)
+ assertEquals(2, details.count())
+ assertEquals("public", details.elementAt(0).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ with(members.elementAt(1)) {
+ assertEquals("name", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Property, kind)
+ assertEquals("String", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+ }
+
+ @Test fun classWithCompanionObject() {
+ verifyModel("testdata/classes/classWithCompanionObject.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Klass", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(links.none())
+
+ assertEquals(3, members.count())
+ with(members.elementAt(0)) {
+ assertEquals("<init>", name)
+ assertEquals(Content.Empty, content)
+ }
+ with(members.elementAt(1)) {
+ assertEquals("foo", name)
+ assertEquals(NodeKind.CompanionObjectFunction, kind)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ with(members.elementAt(2)) {
+ assertEquals("x", name)
+ assertEquals(NodeKind.CompanionObjectProperty, kind)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+ }
+
+ @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 ->
+ val modifiers = cls.details(NodeKind.Modifier).map { it.name }
+ assertTrue("data" in modifiers)
+ }
+ }
+
+ @Test fun sealedClass() {
+ verifyPackageMember("testdata/classes/sealedClass.kt") { 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 ->
+ with(model.members.single().members.single()) {
+ with(deprecation!!) {
+ assertEquals("Deprecated", name)
+ // assertEquals(Content.Empty, content) // this is now an empty MutableContent instead
+ assertEquals(NodeKind.Annotation, kind)
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Value, kind)
+ assertEquals("\"should no longer be used\"", name)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @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 ->
+ with(model.members.single().members.first { it.name == "D"}.members.first { it.name == "f" }) {
+ val modifiers = details(NodeKind.Modifier)
+ assertEquals(2, modifiers.size)
+ assertEquals("final", modifiers[1].name)
+
+ val overrideReferences = references(RefKind.Override)
+ assertEquals(1, overrideReferences.size)
+ }
+ }
+ }
+
+ @Test fun indirectOverride() {
+ verifyModel("testdata/classes/indirectOverride.kt") { model ->
+ with(model.members.single().members.first { it.name == "E"}.members.first { it.name == "foo" }) {
+ val modifiers = details(NodeKind.Modifier)
+ assertEquals(2, modifiers.size)
+ assertEquals("final", modifiers[1].name)
+
+ val overrideReferences = references(RefKind.Override)
+ assertEquals(1, overrideReferences.size)
+ }
+ }
+ }
+
+ @Test fun innerClass() {
+ verifyPackageMember("testdata/classes/innerClass.kt") { cls ->
+ val innerClass = cls.members.single { it.name == "D" }
+ val modifiers = innerClass.details(NodeKind.Modifier)
+ assertEquals(3, modifiers.size)
+ assertEquals("inner", modifiers[2].name)
+ }
+ }
+
+ @Test fun companionObjectExtension() {
+ verifyModel("testdata/classes/companionObjectExtension.kt") { model ->
+ val pkg = model.members.single()
+ val cls = pkg.members.single { it.name == "Foo" }
+ val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty }
+ assertEquals(1, extensions.size)
+ }
+ }
+
+ @Test fun secondaryConstructor() {
+ verifyPackageMember("testdata/classes/secondaryConstructor.kt") { cls ->
+ val constructors = cls.members(NodeKind.Constructor)
+ assertEquals(2, constructors.size)
+ with (constructors.first { it.details(NodeKind.Parameter).size == 1}) {
+ assertEquals("<init>", name)
+ assertEquals("This is a secondary constructor.", summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/classes/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
+
+ @Test fun privateCompanionObject() {
+ verifyModel("testdata/classes/privateCompanionObject.kt", includeNonPublic = false) { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(0, members(NodeKind.CompanionObjectFunction).size)
+ assertEquals(0, members(NodeKind.CompanionObjectProperty).size)
+ }
+ }
+ }
+
+}
diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt
new file mode 100644
index 000000000..7869837c1
--- /dev/null
+++ b/core/src/test/kotlin/model/CommentTest.kt
@@ -0,0 +1,186 @@
+package org.jetbrains.dokka.tests
+
+import org.junit.Test
+import org.junit.Assert.*
+import org.jetbrains.dokka.*
+
+public class CommentTest {
+
+ @Test fun codeBlockComment() {
+ verifyModel("testdata/comments/codeBlockComment.kt") { model ->
+ with(model.members.single().members.first()) {
+ assertEqualsIgnoringSeparators("""[code lang=brainfuck]
+ |
+ |++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+ |
+ |[/code]
+ |""".trimMargin(),
+ content.toTestString())
+ }
+ with(model.members.single().members.last()) {
+ assertEqualsIgnoringSeparators("""[code]
+ |
+ |a + b - c
+ |
+ |[/code]
+ |""".trimMargin(),
+ content.toTestString())
+ }
+ }
+ }
+
+ @Test fun emptyDoc() {
+ verifyModel("testdata/comments/emptyDoc.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(Content.Empty, content)
+ }
+ }
+ }
+
+ @Test fun emptyDocButComment() {
+ verifyModel("testdata/comments/emptyDocButComment.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(Content.Empty, content)
+ }
+ }
+ }
+
+ @Test fun multilineDoc() {
+ verifyModel("testdata/comments/multilineDoc.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("doc1", content.summary.toTestString())
+ assertEquals("doc2\ndoc3", content.description.toTestString())
+ }
+ }
+ }
+
+ @Test fun multilineDocWithComment() {
+ verifyModel("testdata/comments/multilineDocWithComment.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("doc1", content.summary.toTestString())
+ assertEquals("doc2\ndoc3", content.description.toTestString())
+ }
+ }
+ }
+
+ @Test fun oneLineDoc() {
+ verifyModel("testdata/comments/oneLineDoc.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("doc", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun oneLineDocWithComment() {
+ verifyModel("testdata/comments/oneLineDocWithComment.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("doc", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun oneLineDocWithEmptyLine() {
+ verifyModel("testdata/comments/oneLineDocWithEmptyLine.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("doc", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun emptySection() {
+ verifyModel("testdata/comments/emptySection.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Summary", content.summary.toTestString())
+ assertEquals(1, content.sections.count())
+ with (content.findSectionByTag("one")!!) {
+ assertEquals("One", tag)
+ assertEquals("", toTestString())
+ }
+ }
+ }
+ }
+
+ @Test fun quotes() {
+ verifyModel("testdata/comments/quotes.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("it's \"useful\"", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun section1() {
+ verifyModel("testdata/comments/section1.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Summary", content.summary.toTestString())
+ assertEquals(1, content.sections.count())
+ with (content.findSectionByTag("one")!!) {
+ assertEquals("One", tag)
+ assertEquals("section one", toTestString())
+ }
+ }
+ }
+ }
+
+ @Test fun section2() {
+ verifyModel("testdata/comments/section2.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Summary", content.summary.toTestString())
+ assertEquals(2, content.sections.count())
+ with (content.findSectionByTag("one")!!) {
+ assertEquals("One", tag)
+ assertEquals("section one", toTestString())
+ }
+ with (content.findSectionByTag("two")!!) {
+ assertEquals("Two", tag)
+ assertEquals("section two", toTestString())
+ }
+ }
+ }
+ }
+
+ @Test fun multilineSection() {
+ verifyModel("testdata/comments/multilineSection.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Summary", content.summary.toTestString())
+ assertEquals(1, content.sections.count())
+ with (content.findSectionByTag("one")!!) {
+ assertEquals("One", tag)
+ assertEquals("""line one
+line two""", toTestString())
+ }
+ }
+ }
+ }
+
+ @Test fun directive() {
+ verifyModel("testdata/comments/directive.kt") { model ->
+ with(model.members.single().members[3]) {
+ assertEquals("Summary", content.summary.toTestString())
+ with (content.description) {
+ assertEqualsIgnoringSeparators("""
+ |[code lang=kotlin]
+ |if (true) {
+ | println(property)
+ |}
+ |[/code]
+ |[code lang=kotlin]
+ |if (true) {
+ | println(property)
+ |}
+ |[/code]
+ |[code lang=kotlin]
+ |if (true) {
+ | println(property)
+ |}
+ |[/code]
+ |[code lang=kotlin]
+ |if (true) {
+ | println(property)
+ |}
+ |[/code]
+ |""".trimMargin(), toTestString())
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt
new file mode 100644
index 000000000..c94d7e990
--- /dev/null
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -0,0 +1,251 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.NodeKind
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import kotlin.test.assertNotNull
+
+class FunctionTest {
+ @Test fun function() {
+ verifyModel("testdata/functions/function.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("fn", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("Function fn", content.summary.toTestString())
+ assertEquals("Unit", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun functionWithReceiver() {
+ verifyModel("testdata/functions/functionWithReceiver.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("kotlin.String", name)
+ assertEquals(NodeKind.ExternalClass, kind)
+ assertEquals(2, members.count())
+ with(members[0]) {
+ assertEquals("fn", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("Function with receiver", content.summary.toTestString())
+ assertEquals("public", details.elementAt(0).name)
+ assertEquals("final", details.elementAt(1).name)
+ with(details.elementAt(3)) {
+ assertEquals("<this>", name)
+ assertEquals(NodeKind.Receiver, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", details.single().name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ assertEquals("Unit", details.elementAt(4).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ with(members[1]) {
+ assertEquals("fn", name)
+ assertEquals(NodeKind.Function, kind)
+ }
+ }
+ }
+ }
+
+ @Test fun genericFunction() {
+ verifyModel("testdata/functions/genericFunction.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("generic", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("generic function", content.summary.toTestString())
+
+ assertEquals("private", details.elementAt(0).name)
+ assertEquals("final", details.elementAt(1).name)
+ with(details.elementAt(3)) {
+ assertEquals("T", name)
+ assertEquals(NodeKind.TypeParameter, kind)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ assertEquals("Unit", details.elementAt(4).name)
+
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+ @Test fun genericFunctionWithConstraints() {
+ verifyModel("testdata/functions/genericFunctionWithConstraints.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("generic", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("generic function", content.summary.toTestString())
+
+ val functionDetails = details
+ assertEquals("public", functionDetails.elementAt(0).name)
+ assertEquals("final", functionDetails.elementAt(1).name)
+ with(functionDetails.elementAt(3)) {
+ assertEquals("T", name)
+ assertEquals(NodeKind.TypeParameter, kind)
+ assertEquals(Content.Empty, content)
+ with(details.single()) {
+ assertEquals("R", name)
+ assertEquals(NodeKind.UpperBound, kind)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.singleOrNull() == functionDetails.elementAt(4))
+ }
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ with(functionDetails.elementAt(4)) {
+ assertEquals("R", name)
+ assertEquals(NodeKind.TypeParameter, kind)
+ assertEquals(Content.Empty, content)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ assertEquals("Unit", functionDetails.elementAt(5).name)
+
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun functionWithParams() {
+ verifyModel("testdata/functions/functionWithParams.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("function", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("Multiline", content.summary.toTestString())
+ assertEquals("""Function
+Documentation""", content.description.toTestString())
+
+ assertEquals("public", details.elementAt(0).name)
+ assertEquals("final", details.elementAt(1).name)
+ with(details.elementAt(3)) {
+ assertEquals("x", name)
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals("parameter", content.summary.toTestString())
+ assertEquals("Int", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ assertEquals("Unit", details.elementAt(4).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @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 ->
+ assertEquals(0, func.annotations.count())
+ }
+ }
+
+ @Test fun inlineFunction() {
+ verifyPackageMember("testdata/functions/inlineFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }
+ assertTrue("inline" in modifiers)
+ }
+ }
+
+ @Test fun functionWithAnnotatedParam() {
+ verifyModel("testdata/functions/functionWithAnnotatedParam.kt") { model ->
+ with(model.members.single().members.single { it.name == "function" }) {
+ with(details(NodeKind.Parameter).first()) {
+ assertEquals(1, annotations.count())
+ with(annotations[0]) {
+ assertEquals("Fancy", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Annotation, kind)
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun functionWithNoinlineParam() {
+ verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt") { func ->
+ with(func.details(NodeKind.Parameter).first()) {
+ val modifiers = details(NodeKind.Modifier).map { it.name }
+ assertTrue("noinline" in modifiers)
+ }
+ }
+ }
+
+ @Test fun annotatedFunctionWithAnnotationParameters() {
+ verifyModel("testdata/functions/annotatedFunctionWithAnnotationParameters.kt") { model ->
+ with(model.members.single().members.single { it.name == "f" }) {
+ assertEquals(1, annotations.count())
+ with(annotations[0]) {
+ assertEquals("Fancy", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Annotation, kind)
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Value, kind)
+ assertEquals("1", name)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun functionWithDefaultParameter() {
+ verifyModel("testdata/functions/functionWithDefaultParameter.kt") { model ->
+ with(model.members.single().members.single()) {
+ with(details.elementAt(3)) {
+ val value = details(NodeKind.Value)
+ assertEquals(1, value.count())
+ with(value[0]) {
+ assertEquals("\"\"", name)
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/functions/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
+
+ // Test for b/159470920, to ensure that we correctly parse annotated function types without resolving 'ERROR CLASS'
+ // types. Note that the actual annotation is not included in the type information, this is tracked in b/145517104.
+ @Test fun functionWithAnnotatedLambdaParam() {
+ verifyModel("testdata/functions/functionWithAnnotatedLambdaParam.kt") { model ->
+ with(model.members.single().members.single { it.name == "function" }) {
+ with(details(NodeKind.Parameter).first()) {
+ with(details(NodeKind.Type).first()) {
+ assertEquals("Function0", name)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
new file mode 100644
index 000000000..c00d8dc37
--- /dev/null
+++ b/core/src/test/kotlin/model/JavaTest.kt
@@ -0,0 +1,219 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+import org.junit.Assert.*
+import org.junit.Ignore
+import org.junit.Test
+
+public class JavaTest {
+ @Test fun function() {
+ verifyJavaPackageMember("testdata/java/member.java") { cls ->
+ assertEquals("Test", cls.name)
+ assertEquals(NodeKind.Class, cls.kind)
+ with(cls.members(NodeKind.Function).single()) {
+ assertEquals("fn", name)
+ assertEquals("Summary for Function", content.summary.toTestString().trimEnd())
+ assertEquals(3, content.sections.size)
+ with(content.sections[0]) {
+ assertEquals("Parameters", tag)
+ assertEquals("name", subjectName)
+ assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString())
+ }
+ with(content.sections[1]) {
+ assertEquals("Parameters", tag)
+ assertEquals("value", subjectName)
+ assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString())
+ }
+ with(content.sections[2]) {
+ assertEquals("Author", tag)
+ assertEquals("yole", toTestString())
+ }
+ assertEquals("Unit", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ with(details.first { it.name == "name" }) {
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals("String", detail(NodeKind.Type).name)
+ }
+ with(details.first { it.name == "value" }) {
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals("Int", detail(NodeKind.Type).name)
+ }
+ }
+ }
+ }
+
+ @Test fun memberWithModifiers() {
+ verifyJavaPackageMember("testdata/java/memberWithModifiers.java") { cls ->
+ val modifiers = cls.details(NodeKind.Modifier).map { it.name }
+ assertTrue("abstract" in modifiers)
+ with(cls.members.single { it.name == "fn" }) {
+ assertEquals("protected", details[0].name)
+ }
+ with(cls.members.single { it.name == "openFn" }) {
+ assertEquals("open", details[1].name)
+ }
+ }
+ }
+
+ @Test fun superClass() {
+ verifyJavaPackageMember("testdata/java/superClass.java") { cls ->
+ val superTypes = cls.details(NodeKind.Supertype)
+ assertEquals(2, superTypes.size)
+ assertEquals("Exception", superTypes[0].name)
+ assertEquals("Cloneable", superTypes[1].name)
+ }
+ }
+
+ @Test fun arrayType() {
+ verifyJavaPackageMember("testdata/java/arrayType.java") { cls ->
+ with(cls.members(NodeKind.Function).single()) {
+ val type = detail(NodeKind.Type)
+ assertEquals("Array", type.name)
+ assertEquals("String", type.detail(NodeKind.Type).name)
+ with(details(NodeKind.Parameter).single()) {
+ val parameterType = detail(NodeKind.Type)
+ assertEquals("IntArray", parameterType.name)
+ }
+ }
+ }
+ }
+
+ @Test fun typeParameter() {
+ verifyJavaPackageMember("testdata/java/typeParameter.java") { cls ->
+ val typeParameters = cls.details(NodeKind.TypeParameter)
+ with(typeParameters.single()) {
+ assertEquals("T", name)
+ with(detail(NodeKind.UpperBound)) {
+ assertEquals("Comparable", name)
+ assertEquals("T", detail(NodeKind.Type).name)
+ }
+ }
+ with(cls.members(NodeKind.Function).single()) {
+ val methodTypeParameters = details(NodeKind.TypeParameter)
+ with(methodTypeParameters.single()) {
+ assertEquals("E", name)
+ }
+ }
+ }
+ }
+
+ @Test fun constructors() {
+ verifyJavaPackageMember("testdata/java/constructors.java") { cls ->
+ val constructors = cls.members(NodeKind.Constructor)
+ assertEquals(2, constructors.size)
+ with(constructors[0]) {
+ assertEquals("<init>", name)
+ }
+ }
+ }
+
+ @Test fun innerClass() {
+ verifyJavaPackageMember("testdata/java/InnerClass.java") { cls ->
+ val innerClass = cls.members(NodeKind.Class).single()
+ assertEquals("D", innerClass.name)
+ }
+ }
+
+ @Test fun varargs() {
+ verifyJavaPackageMember("testdata/java/varargs.java") { cls ->
+ val fn = cls.members(NodeKind.Function).single()
+ val param = fn.detail(NodeKind.Parameter)
+ assertEquals("vararg", param.details(NodeKind.Modifier).first().name)
+ val psiType = param.detail(NodeKind.Type)
+ assertEquals("String", psiType.name)
+ assertTrue(psiType.details(NodeKind.Type).isEmpty())
+ }
+ }
+
+ @Test fun fields() {
+ verifyJavaPackageMember("testdata/java/field.java") { 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 })
+
+ val s = cls.members(NodeKind.Property).single { it.name == "s" }
+ assertEquals("String", s.detail(NodeKind.Type).name)
+ assertFalse("var" in s.details(NodeKind.Modifier).map { it.name })
+ assertTrue("static" in s.details(NodeKind.Modifier).map { it.name })
+ }
+ }
+
+ @Test fun staticMethod() {
+ verifyJavaPackageMember("testdata/java/staticMethod.java") { cls ->
+ val m = cls.members(NodeKind.Function).single { it.name == "foo" }
+ assertTrue("static" in m.details(NodeKind.Modifier).map { it.name })
+ }
+ }
+
+ /**
+ * `@suppress` not supported in Java!
+ *
+ * [Proposed tags](http://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 ->
+ assertEquals(1, cls.members(NodeKind.Function).size)
+ }
+ }
+
+ @Test fun hideAnnotation() {
+ verifyJavaPackageMember("testdata/java/hideAnnotation.java") { cls ->
+ assertEquals(1, cls.members(NodeKind.Function).size)
+ assertEquals(1, cls.members(NodeKind.Property).size)
+
+ // The test file contains two classes, one of which is hidden.
+ // The test for @hide annotation on classes is via verifyJavaPackageMember(),
+ // which will throw an IllegalArgumentException if it detects more than one class.
+ }
+ }
+
+ @Test fun annotatedAnnotation() {
+ verifyJavaPackageMember("testdata/java/annotatedAnnotation.java") { cls ->
+ assertEquals(1, cls.annotations.size)
+ with(cls.annotations[0]) {
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Parameter, kind)
+ assertEquals(1, details.count())
+ with(details[0]) {
+ assertEquals(NodeKind.Value, kind)
+ assertEquals("[AnnotationTarget.FIELD, AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER]", name)
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun deprecation() {
+ verifyJavaPackageMember("testdata/java/deprecation.java") { 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 ->
+ val fn = cls.members(NodeKind.Function).single()
+ assertEquals("Any", fn.detail(NodeKind.Type).name)
+ }
+ }
+
+ @Test fun enumValues() {
+ verifyJavaPackageMember("testdata/java/enumValues.java") { cls ->
+ val superTypes = cls.details(NodeKind.Supertype)
+ assertEquals(1, superTypes.size)
+ assertEquals(1, cls.members(NodeKind.EnumItem).size)
+ }
+ }
+
+ @Test fun inheritorLinks() {
+ verifyJavaPackageMember("testdata/java/InheritorLinks.java") { 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
new file mode 100644
index 000000000..4a054a880
--- /dev/null
+++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.DocumentationModule
+import org.jetbrains.dokka.NodeKind
+import org.junit.Test
+import org.junit.Assert.assertEquals
+
+class KotlinAsJavaTest {
+ @Test fun function() {
+ verifyModelAsJava("testdata/functions/function.kt") { model ->
+ val pkg = model.members.single()
+
+ val facadeClass = pkg.members.single { it.name == "FunctionKt" }
+ assertEquals(NodeKind.Class, facadeClass.kind)
+
+ val fn = facadeClass.members.single { it.kind == NodeKind.Function}
+ assertEquals("fn", fn.name)
+ }
+ }
+
+ @Test fun propertyWithComment() {
+ verifyModelAsJava("testdata/comments/oneLineDoc.kt") { model ->
+ val facadeClass = model.members.single().members.single { it.name == "OneLineDocKt" }
+ val getter = facadeClass.members.single { it.name == "getProperty" }
+ assertEquals(NodeKind.Function, getter.kind)
+ assertEquals("doc", getter.content.summary.toTestString())
+ }
+ }
+}
+
+fun verifyModelAsJava(source: String,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ verifier: (DocumentationModule) -> Unit) {
+ verifyModel(source,
+ withJdk = withJdk, withKotlinRuntime = withKotlinRuntime,
+ format = "html-as-java",
+ verifier = verifier)
+}
diff --git a/core/src/test/kotlin/model/LinkTest.kt b/core/src/test/kotlin/model/LinkTest.kt
new file mode 100644
index 000000000..6b72525fd
--- /dev/null
+++ b/core/src/test/kotlin/model/LinkTest.kt
@@ -0,0 +1,75 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.ContentBlock
+import org.jetbrains.dokka.ContentNodeLazyLink
+import org.jetbrains.dokka.NodeKind
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class LinkTest {
+ @Test fun linkToSelf() {
+ verifyModel("testdata/links/linkToSelf.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Foo", name)
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("This is link to [Foo -> Class:Foo]", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun linkToMember() {
+ verifyModel("testdata/links/linkToMember.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Foo", name)
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("This is link to [member -> Function:member]", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun linkToConstantWithUnderscores() {
+ verifyModel("testdata/links/linkToConstantWithUnderscores.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Foo", name)
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("This is link to [MY_CONSTANT_VALUE -> CompanionObjectProperty:MY_CONSTANT_VALUE]", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun linkToQualifiedMember() {
+ verifyModel("testdata/links/linkToQualifiedMember.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Foo", name)
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("This is link to [Foo.member -> Function:member]", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun linkToParam() {
+ verifyModel("testdata/links/linkToParam.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("Foo", name)
+ assertEquals(NodeKind.Function, kind)
+ assertEquals("This is link to [param -> Parameter:param]", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test fun linkToPackage() {
+ verifyModel("testdata/links/linkToPackage.kt") { model ->
+ val packageNode = model.members.single()
+ with(packageNode) {
+ assertEquals(this.name, "test.magic")
+ }
+ with(packageNode.members.single()) {
+ assertEquals("Magic", name)
+ assertEquals(NodeKind.Class, kind)
+ assertEquals("Basic implementations of [Magic -> Class:Magic] are located in [test.magic -> Package:test.magic] package", content.summary.toTestString())
+ assertEquals(packageNode, ((this.content.summary as ContentBlock).children.filterIsInstance<ContentNodeLazyLink>().last()).lazyNode.invoke())
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt
new file mode 100644
index 000000000..3936fb4f3
--- /dev/null
+++ b/core/src/test/kotlin/model/PackageTest.kt
@@ -0,0 +1,116 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.PackageOptionsImpl
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
+import org.junit.Assert.*
+import org.junit.Test
+
+public class PackageTest {
+ @Test fun rootPackage() {
+ verifyModel("testdata/packages/rootPackage.kt") { model ->
+ with(model.members.single()) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun simpleNamePackage() {
+ verifyModel("testdata/packages/simpleNamePackage.kt") { model ->
+ with(model.members.single()) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("simple", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun dottedNamePackage() {
+ verifyModel("testdata/packages/dottedNamePackage.kt") { model ->
+ with(model.members.single()) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("dot.name", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun multipleFiles() {
+ verifyModel(KotlinSourceRoot("testdata/packages/dottedNamePackage.kt", false),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false)
+ ) { model ->
+ assertEquals(2, model.members.count())
+ with(model.members.single { it.name == "simple" }) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("simple", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ with(model.members.single { it.name == "dot.name" }) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun multipleFilesSamePackage() {
+ verifyModel(KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false),
+ KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt", false)) { model ->
+ assertEquals(1, model.members.count())
+ with(model.members.elementAt(0)) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("simple", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun classAtPackageLevel() {
+ verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)) { model ->
+ assertEquals(1, model.members.count())
+ with(model.members.elementAt(0)) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("simple.name", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertEquals(1, members.size)
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun suppressAtPackageLevel() {
+ verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt", false),
+ perPackageOptions = listOf(PackageOptionsImpl(prefix = "simple.name", suppress = true))) { model ->
+ assertEquals(1, model.members.count())
+ with(model.members.elementAt(0)) {
+ assertEquals(NodeKind.Package, kind)
+ assertEquals("simple.name", name)
+ assertEquals(Content.Empty, content)
+ assertTrue(details.none())
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+}
diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt
new file mode 100644
index 000000000..41c3a4c8f
--- /dev/null
+++ b/core/src/test/kotlin/model/PropertyTest.kt
@@ -0,0 +1,112 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Ignore
+import org.junit.Test
+
+class PropertyTest {
+ @Test fun valueProperty() {
+ verifyModel("testdata/properties/valueProperty.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("property", name)
+ assertEquals(NodeKind.Property, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun variableProperty() {
+ verifyModel("testdata/properties/variableProperty.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("property", name)
+ assertEquals(NodeKind.Property, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", detail(NodeKind.Type).name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ }
+ }
+
+ @Test fun valuePropertyWithGetter() {
+ verifyModel("testdata/properties/valuePropertyWithGetter.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("property", name)
+ assertEquals(NodeKind.Property, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", detail(NodeKind.Type).name)
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ }
+ }
+
+ @Test fun variablePropertyWithAccessors() {
+ verifyModel("testdata/properties/variablePropertyWithAccessors.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("property", name)
+ assertEquals(NodeKind.Property, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", detail(NodeKind.Type).name)
+ val modifiers = details(NodeKind.Modifier).map { it.name }
+ assertTrue("final" in modifiers)
+ assertTrue("public" in modifiers)
+ assertTrue("var" in modifiers)
+ assertTrue(links.none())
+ assertTrue(members.none())
+ }
+ }
+ }
+
+ @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("Strictfp", name)
+ assertEquals(Content.Empty, content)
+ assertEquals(NodeKind.Annotation, kind)
+ }
+ }
+ }
+ }
+
+ @Test fun propertyWithReceiver() {
+ verifyModel("testdata/properties/propertyWithReceiver.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("kotlin.String", name)
+ assertEquals(NodeKind.ExternalClass, kind)
+ with(members.single()) {
+ assertEquals("foobar", name)
+ assertEquals(NodeKind.Property, kind)
+ }
+ }
+ }
+ }
+
+ @Test fun propertyOverride() {
+ verifyModel("testdata/properties/propertyOverride.kt") { 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
+ assertEquals("xyzzy", override.name)
+ assertEquals("Foo", override.owner!!.name)
+ }
+ }
+ }
+
+ @Test fun sinceKotlin() {
+ verifyModel("testdata/properties/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
+}
diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt
new file mode 100644
index 000000000..c653ac83a
--- /dev/null
+++ b/core/src/test/kotlin/model/TypeAliasTest.kt
@@ -0,0 +1,132 @@
+package org.jetbrains.dokka.tests
+
+import junit.framework.TestCase.assertEquals
+import org.jetbrains.dokka.Content
+import org.jetbrains.dokka.NodeKind
+import org.junit.Test
+
+class TypeAliasTest {
+ @Test
+ fun testSimple() {
+ verifyModel("testdata/typealias/simple.kt") {
+ val pkg = it.members.single()
+ with(pkg.member(NodeKind.TypeAlias)) {
+ assertEquals(Content.Empty, content)
+ assertEquals("B", name)
+ assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name)
+ }
+ }
+ }
+
+ @Test
+ fun testInheritanceFromTypeAlias() {
+ verifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") {
+ val pkg = it.members.single()
+ with(pkg.member(NodeKind.TypeAlias)) {
+ assertEquals(Content.Empty, content)
+ assertEquals("Same", name)
+ assertEquals("Some", detail(NodeKind.TypeAliasUnderlyingType).name)
+ assertEquals("My", inheritors.single().name)
+ }
+ with(pkg.members(NodeKind.Class).find { it.name == "My" }!!) {
+ assertEquals("Same", detail(NodeKind.Supertype).name)
+ }
+ }
+ }
+
+ @Test
+ fun testChain() {
+ verifyModel("testdata/typealias/chain.kt") {
+ val pkg = it.members.single()
+ with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) {
+ assertEquals(Content.Empty, content)
+ assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name)
+ }
+ with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) {
+ assertEquals(Content.Empty, content)
+ assertEquals("B", detail(NodeKind.TypeAliasUnderlyingType).name)
+ }
+ }
+ }
+
+ @Test
+ fun testDocumented() {
+ verifyModel("testdata/typealias/documented.kt") {
+ val pkg = it.members.single()
+ with(pkg.member(NodeKind.TypeAlias)) {
+ assertEquals("Just typealias", content.summary.toTestString())
+ }
+ }
+ }
+
+ @Test
+ fun testDeprecated() {
+ verifyModel("testdata/typealias/deprecated.kt") {
+ val pkg = it.members.single()
+ with(pkg.member(NodeKind.TypeAlias)) {
+ assertEquals(Content.Empty, content)
+ assertEquals("Deprecated", deprecation!!.name)
+ assertEquals("\"Not mainstream now\"", deprecation!!.detail(NodeKind.Parameter).detail(NodeKind.Value).name)
+ }
+ }
+ }
+
+ @Test
+ fun testGeneric() {
+ verifyModel("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)
+ }
+
+ with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) {
+ assertEquals("T", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name)
+ assertEquals("T", detail(NodeKind.TypeParameter).name)
+ }
+ }
+ }
+
+ @Test
+ fun testFunctional() {
+ verifyModel("testdata/typealias/functional.kt") {
+ val pkg = it.members.single()
+ with(pkg.member(NodeKind.TypeAlias)) {
+ assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name)
+ val typeParams = detail(NodeKind.TypeAliasUnderlyingType).details(NodeKind.Type)
+ assertEquals("A", typeParams.first().name)
+ assertEquals("B", typeParams.last().name)
+ }
+
+ with(pkg.member(NodeKind.Function)) {
+ assertEquals("Spell", detail(NodeKind.Parameter).detail(NodeKind.Type).name)
+ }
+ }
+ }
+
+ @Test
+ fun testAsTypeBoundWithVariance() {
+ verifyModel("testdata/typealias/asTypeBoundWithVariance.kt") {
+ val pkg = it.members.single()
+ with(pkg.members(NodeKind.Class).find { it.name == "C" }!!) {
+ val tParam = detail(NodeKind.TypeParameter)
+ assertEquals("out", tParam.detail(NodeKind.Modifier).name)
+ assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name)
+ }
+
+ with(pkg.members(NodeKind.Class).find { it.name == "D" }!!) {
+ val tParam = detail(NodeKind.TypeParameter)
+ assertEquals("in", tParam.detail(NodeKind.Modifier).name)
+ assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name)
+ }
+ }
+ }
+
+ @Test
+ fun sinceKotlin() {
+ verifyModel("testdata/typealias/sinceKotlin.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals(listOf("Kotlin 1.1"), platforms)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/utilities/StringExtensionsTest.kt b/core/src/test/kotlin/utilities/StringExtensionsTest.kt
new file mode 100644
index 000000000..80c18df6d
--- /dev/null
+++ b/core/src/test/kotlin/utilities/StringExtensionsTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+package org.jetbrains.dokka.tests.utilities
+
+import org.jetbrains.dokka.Utilities.firstSentence
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class StringExtensionsTest {
+
+ @Test
+ fun firstSentence_emptyString() {
+ assertEquals("", "".firstSentence())
+ }
+
+ @Test
+ fun incompleteSentence() {
+ assertEquals("Hello there", "Hello there".firstSentence())
+ }
+
+ @Test
+ fun incompleteSentence_withParenthesis() {
+ assertEquals("Hello there (hi)", "Hello there (hi)".firstSentence())
+ assertEquals("Hello there (hi.)", "Hello there (hi.)".firstSentence())
+ }
+
+ @Test
+ fun incompleteSentence_apiLevel() {
+ assertEquals("API level 8 (Android 2.2, Froyo)", "API level 8 (Android 2.2, Froyo)".firstSentence())
+ }
+
+ @Test
+ fun unmatchedClosingParen() {
+ assertEquals(
+ "A notation either declares, by name, the format of an unparsed entity (see \n",
+ "A notation either declares, by name, the format of an unparsed entity (see \n".firstSentence()
+ )
+ }
+
+ @Test
+ fun unmatchedClosingParen_withFullFirstSentence() {
+ assertEquals(
+ "This interface represents a notation declared in the DTD.",
+ ("This interface represents a notation declared in the DTD. A notation either declares, by name, " +
+ "the format of an unparsed entity (see \n").firstSentence()
+ )
+ }
+
+ @Test
+ fun firstSentence_singleSentence() {
+ assertEquals("Hello there.", "Hello there.".firstSentence())
+ }
+
+ @Test
+ fun firstSentence_multipleSentences() {
+ assertEquals("Hello there.", "Hello there. How are you?".firstSentence())
+ }
+
+ @Test
+ fun firstSentence_singleSentence_withParenthesis() {
+ assertEquals("API level 28 (Android Pie).", "API level 28 (Android Pie).".firstSentence())
+ }
+
+ @Test
+ fun firstSentence_multipleSentences_withParenthesis() {
+ assertEquals(
+ "API level 28 (Android Pie).",
+ "API level 28 (Android Pie). API level 27 (Android Oreo)".firstSentence()
+ )
+ }
+
+ @Test
+ fun firstSentence_singleSentence_withPeriodInParenthesis() {
+ assertEquals("API level 28 (Android 9.0 Pie).", "API level 28 (Android 9.0 Pie).".firstSentence())
+ }
+
+ @Test
+ fun firstSentence_multipleSentences_withPeriodInParenthesis() {
+ assertEquals(
+ "API level 28 (Android 9.0 Pie).",
+ "API level 28 (Android 9.0 Pie). API level 27 (Android 8.0 Oreo).".firstSentence()
+ )
+ }
+
+ @Test
+ fun parenthesisWithperiod_notFirstSentence() {
+ assertEquals("Foo bar.", "Foo bar. Baz (Wow)".firstSentence())
+ assertEquals("Foo bar.", "Foo bar. Baz (Wow).".firstSentence())
+ }
+
+ @Test
+ fun periodInsideParenthesis() {
+ assertEquals(
+ "A ViewGroup is a special view that can contain other views (called children.) " +
+ "The view group is the base class for layouts and views containers.",
+ ("A ViewGroup is a special view that can contain other views (called children.) " +
+ "The view group is the base class for layouts and views containers. " +
+ "This class also defines the android.view.ViewGroup.LayoutParams class " +
+ "which serves as the base class for layouts parameters.").firstSentence()
+ )
+ assertEquals("Foo (Foo.) bar.", "Foo (Foo.) bar. Baz.".firstSentence())
+ assertEquals("Foo (Foo.) bar (bar.) baz.", "Foo (Foo.) bar (bar.) baz. Wow".firstSentence())
+ assertEquals("Foo (Foo.) bar (bar.) baz (baz.) Wow", "Foo (Foo.) bar (bar.) baz (baz.) Wow".firstSentence())
+ }
+} \ No newline at end of file
diff --git a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 000000000..ca6ee9cea
--- /dev/null
+++ b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline \ No newline at end of file
diff --git a/core/testdata/classes/annotatedClass.kt b/core/testdata/classes/annotatedClass.kt
new file mode 100644
index 000000000..1b58f56c1
--- /dev/null
+++ b/core/testdata/classes/annotatedClass.kt
@@ -0,0 +1 @@
+@Strictfp class Foo() {}
diff --git a/core/testdata/classes/annotatedClassWithAnnotationParameters.kt b/core/testdata/classes/annotatedClassWithAnnotationParameters.kt
new file mode 100644
index 000000000..930d6a627
--- /dev/null
+++ b/core/testdata/classes/annotatedClassWithAnnotationParameters.kt
@@ -0,0 +1 @@
+@Deprecated("should no longer be used") class Foo() {}
diff --git a/core/testdata/classes/classWithCompanionObject.kt b/core/testdata/classes/classWithCompanionObject.kt
new file mode 100644
index 000000000..fdbd915d6
--- /dev/null
+++ b/core/testdata/classes/classWithCompanionObject.kt
@@ -0,0 +1,7 @@
+class Klass() {
+ companion object {
+ val x = 1
+
+ fun foo() {}
+ }
+}
diff --git a/core/testdata/classes/classWithConstructor.kt b/core/testdata/classes/classWithConstructor.kt
new file mode 100644
index 000000000..0751d570e
--- /dev/null
+++ b/core/testdata/classes/classWithConstructor.kt
@@ -0,0 +1 @@
+class Klass(name: String) \ No newline at end of file
diff --git a/core/testdata/classes/classWithFunction.kt b/core/testdata/classes/classWithFunction.kt
new file mode 100644
index 000000000..a981cfb6a
--- /dev/null
+++ b/core/testdata/classes/classWithFunction.kt
@@ -0,0 +1,4 @@
+class Klass {
+ fun fn() {
+ }
+}
diff --git a/core/testdata/classes/classWithProperty.kt b/core/testdata/classes/classWithProperty.kt
new file mode 100644
index 000000000..2a849572e
--- /dev/null
+++ b/core/testdata/classes/classWithProperty.kt
@@ -0,0 +1,3 @@
+class Klass {
+ val name: String = ""
+} \ No newline at end of file
diff --git a/core/testdata/classes/companionObjectExtension.kt b/core/testdata/classes/companionObjectExtension.kt
new file mode 100644
index 000000000..4b471376b
--- /dev/null
+++ b/core/testdata/classes/companionObjectExtension.kt
@@ -0,0 +1,10 @@
+class Foo {
+ companion object Default {
+ }
+}
+
+
+/**
+ * The def
+ */
+val Foo.Default.x: Int get() = 1
diff --git a/core/testdata/classes/dataClass.kt b/core/testdata/classes/dataClass.kt
new file mode 100644
index 000000000..62c6f0ece
--- /dev/null
+++ b/core/testdata/classes/dataClass.kt
@@ -0,0 +1 @@
+data class Foo() {}
diff --git a/core/testdata/classes/emptyClass.kt b/core/testdata/classes/emptyClass.kt
new file mode 100644
index 000000000..abd20cc8d
--- /dev/null
+++ b/core/testdata/classes/emptyClass.kt
@@ -0,0 +1,3 @@
+class Klass {
+
+} \ No newline at end of file
diff --git a/core/testdata/classes/emptyObject.kt b/core/testdata/classes/emptyObject.kt
new file mode 100644
index 000000000..4138bf313
--- /dev/null
+++ b/core/testdata/classes/emptyObject.kt
@@ -0,0 +1,3 @@
+object Obj {
+
+} \ No newline at end of file
diff --git a/core/testdata/classes/genericClass.kt b/core/testdata/classes/genericClass.kt
new file mode 100644
index 000000000..db20ff7e5
--- /dev/null
+++ b/core/testdata/classes/genericClass.kt
@@ -0,0 +1,3 @@
+class Klass<T> {
+
+} \ No newline at end of file
diff --git a/core/testdata/classes/indirectOverride.kt b/core/testdata/classes/indirectOverride.kt
new file mode 100644
index 000000000..8d091b801
--- /dev/null
+++ b/core/testdata/classes/indirectOverride.kt
@@ -0,0 +1,9 @@
+abstract class C() {
+ abstract fun foo()
+}
+
+abstract class D(): C()
+
+class E(): D() {
+ override fun foo() {}
+}
diff --git a/core/testdata/classes/innerClass.kt b/core/testdata/classes/innerClass.kt
new file mode 100644
index 000000000..3c6e497df
--- /dev/null
+++ b/core/testdata/classes/innerClass.kt
@@ -0,0 +1,5 @@
+class C {
+ inner class D {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/classes/javaAnnotationClass.kt b/core/testdata/classes/javaAnnotationClass.kt
new file mode 100644
index 000000000..95600147f
--- /dev/null
+++ b/core/testdata/classes/javaAnnotationClass.kt
@@ -0,0 +1,5 @@
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+
+@Retention(RetentionPolicy.SOURCE)
+public annotation class throws()
diff --git a/core/testdata/classes/notOpenClass.kt b/core/testdata/classes/notOpenClass.kt
new file mode 100644
index 000000000..edee2c1af
--- /dev/null
+++ b/core/testdata/classes/notOpenClass.kt
@@ -0,0 +1,7 @@
+open class C() {
+ open fun f() {}
+}
+
+class D() : C() {
+ override fun f() {}
+}
diff --git a/core/testdata/classes/privateCompanionObject.kt b/core/testdata/classes/privateCompanionObject.kt
new file mode 100644
index 000000000..df43b5f92
--- /dev/null
+++ b/core/testdata/classes/privateCompanionObject.kt
@@ -0,0 +1,11 @@
+package p
+
+class Clz {
+ private companion object {
+ fun fuun() {
+
+ }
+
+ val aaaa = 0
+ }
+} \ No newline at end of file
diff --git a/core/testdata/classes/sealedClass.kt b/core/testdata/classes/sealedClass.kt
new file mode 100644
index 000000000..933503936
--- /dev/null
+++ b/core/testdata/classes/sealedClass.kt
@@ -0,0 +1 @@
+sealed class Foo() {}
diff --git a/core/testdata/classes/secondaryConstructor.kt b/core/testdata/classes/secondaryConstructor.kt
new file mode 100644
index 000000000..e5cb2557c
--- /dev/null
+++ b/core/testdata/classes/secondaryConstructor.kt
@@ -0,0 +1,5 @@
+class C() {
+ /** This is a secondary constructor. */
+ constructor(s: String): this() {
+ }
+}
diff --git a/core/testdata/classes/sinceKotlin.kt b/core/testdata/classes/sinceKotlin.kt
new file mode 100644
index 000000000..1025cf0d3
--- /dev/null
+++ b/core/testdata/classes/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Useful
+ */
+@SinceKotlin("1.1")
+class `Since1.1` \ No newline at end of file
diff --git a/core/testdata/comments/codeBlockComment.kt b/core/testdata/comments/codeBlockComment.kt
new file mode 100644
index 000000000..aa5f5ffcd
--- /dev/null
+++ b/core/testdata/comments/codeBlockComment.kt
@@ -0,0 +1,14 @@
+/**
+ * ``` brainfuck
+ * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+ * ```
+ */
+val prop1 = ""
+
+
+/**
+ * ```
+ * a + b - c
+ * ```
+ */
+val prop2 = "" \ No newline at end of file
diff --git a/core/testdata/comments/directive.kt b/core/testdata/comments/directive.kt
new file mode 100644
index 000000000..b27f5a482
--- /dev/null
+++ b/core/testdata/comments/directive.kt
@@ -0,0 +1,35 @@
+/**
+ * Summary
+ *
+ * @sample example1
+ * @sample example2
+ * @sample X.example3
+ * @sample X.Y.example4
+ */
+val property = "test"
+
+fun example1(node: String) = if (true) {
+ println(property)
+}
+
+fun example2(node: String) {
+ if (true) {
+ println(property)
+ }
+}
+
+class X {
+ fun example3(node: String) {
+ if (true) {
+ println(property)
+ }
+ }
+
+ class Y {
+ fun example4(node: String) {
+ if (true) {
+ println(property)
+ }
+ }
+ }
+}
diff --git a/core/testdata/comments/emptyDoc.kt b/core/testdata/comments/emptyDoc.kt
new file mode 100644
index 000000000..b87cce575
--- /dev/null
+++ b/core/testdata/comments/emptyDoc.kt
@@ -0,0 +1 @@
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/emptyDocButComment.kt b/core/testdata/comments/emptyDocButComment.kt
new file mode 100644
index 000000000..ceb247531
--- /dev/null
+++ b/core/testdata/comments/emptyDocButComment.kt
@@ -0,0 +1,2 @@
+/* comment */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/emptySection.kt b/core/testdata/comments/emptySection.kt
new file mode 100644
index 000000000..47d6b1a58
--- /dev/null
+++ b/core/testdata/comments/emptySection.kt
@@ -0,0 +1,6 @@
+
+/**
+ * Summary
+ * @one
+ */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/multilineDoc.kt b/core/testdata/comments/multilineDoc.kt
new file mode 100644
index 000000000..31cfa3a7e
--- /dev/null
+++ b/core/testdata/comments/multilineDoc.kt
@@ -0,0 +1,7 @@
+/**
+ * doc1
+ *
+ * doc2
+ * doc3
+ */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/multilineDocWithComment.kt b/core/testdata/comments/multilineDocWithComment.kt
new file mode 100644
index 000000000..88d226426
--- /dev/null
+++ b/core/testdata/comments/multilineDocWithComment.kt
@@ -0,0 +1,8 @@
+/**
+ * doc1
+ *
+ * doc2
+ * doc3
+ */
+// comment
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/multilineSection.kt b/core/testdata/comments/multilineSection.kt
new file mode 100644
index 000000000..6ef4df2cd
--- /dev/null
+++ b/core/testdata/comments/multilineSection.kt
@@ -0,0 +1,7 @@
+/**
+ * Summary
+ * @one
+ * line one
+ * line two
+ */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/oneLineDoc.kt b/core/testdata/comments/oneLineDoc.kt
new file mode 100644
index 000000000..92a40c670
--- /dev/null
+++ b/core/testdata/comments/oneLineDoc.kt
@@ -0,0 +1,2 @@
+/** doc */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/oneLineDocWithComment.kt b/core/testdata/comments/oneLineDocWithComment.kt
new file mode 100644
index 000000000..c84679333
--- /dev/null
+++ b/core/testdata/comments/oneLineDocWithComment.kt
@@ -0,0 +1,3 @@
+/** doc */
+// comment
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/oneLineDocWithEmptyLine.kt b/core/testdata/comments/oneLineDocWithEmptyLine.kt
new file mode 100644
index 000000000..e364c4168
--- /dev/null
+++ b/core/testdata/comments/oneLineDocWithEmptyLine.kt
@@ -0,0 +1,3 @@
+/** doc */
+
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/quotes.kt b/core/testdata/comments/quotes.kt
new file mode 100644
index 000000000..47ae6892a
--- /dev/null
+++ b/core/testdata/comments/quotes.kt
@@ -0,0 +1,2 @@
+/** it's "useful" */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/section1.kt b/core/testdata/comments/section1.kt
new file mode 100644
index 000000000..7c763b4c0
--- /dev/null
+++ b/core/testdata/comments/section1.kt
@@ -0,0 +1,5 @@
+/**
+ * Summary
+ * @one section one
+ */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/comments/section2.kt b/core/testdata/comments/section2.kt
new file mode 100644
index 000000000..e280793e8
--- /dev/null
+++ b/core/testdata/comments/section2.kt
@@ -0,0 +1,6 @@
+/**
+ * Summary
+ * @one section one
+ * @two section two
+ */
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/format/JavaSupertype.html b/core/testdata/format/JavaSupertype.html
new file mode 100644
index 000000000..892ef63ab
--- /dev/null
+++ b/core/testdata/format/JavaSupertype.html
@@ -0,0 +1,36 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<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/>
+<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">JavaSupertype.Foo</span></a></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="return-foo.html">returnFoo</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span>&nbsp;<a href="../-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>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/JavaSupertype.java b/core/testdata/format/JavaSupertype.java
new file mode 100644
index 000000000..2045573c2
--- /dev/null
+++ b/core/testdata/format/JavaSupertype.java
@@ -0,0 +1,8 @@
+public class JavaSupertype {
+ public static class Foo {
+ }
+
+ public static class Bar extends Foo {
+ public Foo returnFoo(Foo foo) { return foo; }
+ }
+}
diff --git a/core/testdata/format/accessor.kt b/core/testdata/format/accessor.kt
new file mode 100644
index 000000000..5a4d1742a
--- /dev/null
+++ b/core/testdata/format/accessor.kt
@@ -0,0 +1,5 @@
+class C() {
+ var x: String
+ /** The getter returns an empty string. */ get() = ""
+ /** The setter does nothing. */ set(value) { }
+}
diff --git a/core/testdata/format/accessor.md b/core/testdata/format/accessor.md
new file mode 100644
index 000000000..190e8538d
--- /dev/null
+++ b/core/testdata/format/accessor.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](index.md) / [x](./x.md)
+
+# x
+
+`var x: String`
+
+**Getter**
+
+The getter returns an empty string.
+
+**Setter**
+
+The setter does nothing.
+
diff --git a/core/testdata/format/annotatedTypeParameter.kt b/core/testdata/format/annotatedTypeParameter.kt
new file mode 100644
index 000000000..cc3bfc1a8
--- /dev/null
+++ b/core/testdata/format/annotatedTypeParameter.kt
@@ -0,0 +1,2 @@
+public fun <E> containsAll(elements: Collection<@UnsafeVariance E>): @UnsafeVariance E {
+}
diff --git a/core/testdata/format/annotatedTypeParameter.md b/core/testdata/format/annotatedTypeParameter.md
new file mode 100644
index 000000000..aa622eac0
--- /dev/null
+++ b/core/testdata/format/annotatedTypeParameter.md
@@ -0,0 +1,5 @@
+[test](index.md) / [containsAll](./contains-all.md)
+
+# containsAll
+
+`fun <E> containsAll(elements: Collection<@UnsafeVariance `[`E`](contains-all.md#E)`>): @UnsafeVariance `[`E`](contains-all.md#E) \ No newline at end of file
diff --git a/core/testdata/format/annotationClass.kt b/core/testdata/format/annotationClass.kt
new file mode 100644
index 000000000..89d494fb7
--- /dev/null
+++ b/core/testdata/format/annotationClass.kt
@@ -0,0 +1 @@
+annotation class fancy
diff --git a/core/testdata/format/annotationClass.md b/core/testdata/format/annotationClass.md
new file mode 100644
index 000000000..55fda40c7
--- /dev/null
+++ b/core/testdata/format/annotationClass.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [fancy](./index.md)
+
+# fancy
+
+`annotation class fancy`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `fancy()` |
+
diff --git a/core/testdata/format/annotationClass.package.md b/core/testdata/format/annotationClass.package.md
new file mode 100644
index 000000000..c8aff7a3d
--- /dev/null
+++ b/core/testdata/format/annotationClass.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Annotations
+
+| [fancy](fancy/index.md) | `annotation class fancy` |
+
diff --git a/core/testdata/format/annotationParams.kt b/core/testdata/format/annotationParams.kt
new file mode 100644
index 000000000..f259a7407
--- /dev/null
+++ b/core/testdata/format/annotationParams.kt
@@ -0,0 +1 @@
+@JvmName("FFF") fun f() {}
diff --git a/core/testdata/format/annotationParams.md b/core/testdata/format/annotationParams.md
new file mode 100644
index 000000000..cfa3b8226
--- /dev/null
+++ b/core/testdata/format/annotationParams.md
@@ -0,0 +1,5 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`@JvmName("FFF") fun f(): Unit` \ No newline at end of file
diff --git a/core/testdata/format/annotations.kt b/core/testdata/format/annotations.kt
new file mode 100644
index 000000000..57f76249b
--- /dev/null
+++ b/core/testdata/format/annotations.kt
@@ -0,0 +1,6 @@
+data class Foo {
+ inline fun bar(noinline notInlined: () -> Unit) {
+ }
+
+ inline val x: Int
+}
diff --git a/core/testdata/format/annotations.md b/core/testdata/format/annotations.md
new file mode 100644
index 000000000..2e1604d0e
--- /dev/null
+++ b/core/testdata/format/annotations.md
@@ -0,0 +1,18 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`data class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Properties
+
+| [x](x.md) | `val x: Int` |
+
+### Functions
+
+| [bar](bar.md) | `fun bar(notInlined: () -> Unit): Unit` |
+
diff --git a/core/testdata/format/arrayAverage.kt b/core/testdata/format/arrayAverage.kt
new file mode 100644
index 000000000..1f9e12db0
--- /dev/null
+++ b/core/testdata/format/arrayAverage.kt
@@ -0,0 +1,8 @@
+class XArray<T>
+
+fun XArray<out Byte>.average(): Double = 0.0
+fun XArray<out Double>.average(): Double = 0.0
+fun XArray<out Float>.average(): Double = 0.0
+fun XArray<out Int>.average(): Double = 0.0
+fun XArray<out Long>.average(): Double = 0.0
+fun XArray<out Short>.average(): Double = 0.0
diff --git a/core/testdata/format/arrayAverage.md b/core/testdata/format/arrayAverage.md
new file mode 100644
index 000000000..2c6927d68
--- /dev/null
+++ b/core/testdata/format/arrayAverage.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [XArray](./index.md)
+
+# XArray
+
+`class XArray<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `XArray()` |
+
+### Extension Functions
+
+| [average](../average.md) | `fun `[`XArray`](./index.md)`<out Byte>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Double>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Float>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Int>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Long>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Short>.average(): Double` |
+
diff --git a/core/testdata/format/backtickInCodeBlock.kt b/core/testdata/format/backtickInCodeBlock.kt
new file mode 100644
index 000000000..b457efbd0
--- /dev/null
+++ b/core/testdata/format/backtickInCodeBlock.kt
@@ -0,0 +1,9 @@
+/**
+ * bt : `` ` ``
+ *
+ * bt+ : ``prefix ` postfix``
+ *
+ * backslash: `\`
+ */
+fun foo() {
+}
diff --git a/core/testdata/format/backtickInCodeBlock.md b/core/testdata/format/backtickInCodeBlock.md
new file mode 100644
index 000000000..830539ace
--- /dev/null
+++ b/core/testdata/format/backtickInCodeBlock.md
@@ -0,0 +1,12 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): Unit`
+
+bt : `` ` ``
+
+bt+ : ``prefix ` postfix``
+
+backslash: `\`
+
diff --git a/core/testdata/format/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html
new file mode 100644
index 000000000..f0351d724
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.html
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>u - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./u.html">u</a><br/>
+<br/>
+<h1>u</h1>
+<a name="$u()"></a>
+<code><span class="keyword">fun </span><span class="identifier">u</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><pre><code>This is a test
+ of Dokka's code blocks.
+Here is a blank line.
+
+The previous line was blank.
+</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/blankLineInsideCodeBlock.kt b/core/testdata/format/blankLineInsideCodeBlock.kt
new file mode 100644
index 000000000..9430f4d58
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.kt
@@ -0,0 +1,12 @@
+/**
+ * ```
+ * This is a test
+ * of Dokka's code blocks.
+ * Here is a blank line.
+ *
+ * The previous line was blank.
+ * ```
+ */
+fun u() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/blankLineInsideCodeBlock.md b/core/testdata/format/blankLineInsideCodeBlock.md
new file mode 100644
index 000000000..956f8954a
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.md
@@ -0,0 +1,14 @@
+[test](index.md) / [u](./u.md)
+
+# u
+
+`fun u(): Unit`
+
+```
+This is a test
+ of Dokka's code blocks.
+Here is a blank line.
+
+The previous line was blank.
+```
+
diff --git a/core/testdata/format/bracket.html b/core/testdata/format/bracket.html
new file mode 100644
index 000000000..01aaaf041
--- /dev/null
+++ b/core/testdata/format/bracket.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>bar[]</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/bracket.kt b/core/testdata/format/bracket.kt
new file mode 100644
index 000000000..d41b0073a
--- /dev/null
+++ b/core/testdata/format/bracket.kt
@@ -0,0 +1,4 @@
+/**
+ * bar[]
+ */
+fun foo() {}
diff --git a/core/testdata/format/brokenLink.html b/core/testdata/format/brokenLink.html
new file mode 100644
index 000000000..c598a73e9
--- /dev/null
+++ b/core/testdata/format/brokenLink.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This references <a href="#">noSuchIdentifier</a>.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/brokenLink.kt b/core/testdata/format/brokenLink.kt
new file mode 100644
index 000000000..268a986e4
--- /dev/null
+++ b/core/testdata/format/brokenLink.kt
@@ -0,0 +1,4 @@
+/**
+ * This references [noSuchIdentifier].
+ */
+fun f() { }
diff --git a/core/testdata/format/classWithCompanionObject.html b/core/testdata/format/classWithCompanionObject.html
new file mode 100644
index 000000000..88feea5e3
--- /dev/null
+++ b/core/testdata/format/classWithCompanionObject.html
@@ -0,0 +1,48 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Klass - test</title>
+</HEAD>
+<BODY>
+<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>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Klass</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Companion Object Properties</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="x.html">x</a></p>
+</td>
+<td>
+<code><span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Companion Object Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/classWithCompanionObject.kt b/core/testdata/format/classWithCompanionObject.kt
new file mode 100644
index 000000000..fdbd915d6
--- /dev/null
+++ b/core/testdata/format/classWithCompanionObject.kt
@@ -0,0 +1,7 @@
+class Klass() {
+ companion object {
+ val x = 1
+
+ fun foo() {}
+ }
+}
diff --git a/core/testdata/format/classWithCompanionObject.md b/core/testdata/format/classWithCompanionObject.md
new file mode 100644
index 000000000..40f605be7
--- /dev/null
+++ b/core/testdata/format/classWithCompanionObject.md
@@ -0,0 +1,18 @@
+[test](../index.md) / [Klass](./index.md)
+
+# Klass
+
+`class Klass`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Klass()` |
+
+### Companion Object Properties
+
+| [x](x.md) | `val x: Int` |
+
+### Companion Object Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+
diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html
new file mode 100644
index 000000000..5ef09d637
--- /dev/null
+++ b/core/testdata/format/codeBlock.html
@@ -0,0 +1,62 @@
+<!-- File: test/-it-does-some-obfuscated-thing/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>ItDoesSomeObfuscatedThing - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">ItDoesSomeObfuscatedThing</a><br/>
+<br/>
+<h1>ItDoesSomeObfuscatedThing</h1>
+<code><span class="keyword">class </span><span class="identifier">ItDoesSomeObfuscatedThing</span></code>
+<p>Check output of</p>
+<pre><code class="lang-brainfuck">++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++.------.--------.&gt;+.&gt;.
+</code></pre>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">ItDoesSomeObfuscatedThing</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Check output of</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-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/>
+<br/>
+<h1>Throws</h1>
+<code><span class="keyword">class </span><span class="identifier">Throws</span></code>
+<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
+<p>Example:</p>
+<pre><code>Throws(IOException::class)
+fun readFile(name: String): String {...}
+</code></pre>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Throws</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/codeBlock.kt b/core/testdata/format/codeBlock.kt
new file mode 100644
index 000000000..633bf414b
--- /dev/null
+++ b/core/testdata/format/codeBlock.kt
@@ -0,0 +1,22 @@
+import kotlin.reflect.KClass
+
+/**
+ * This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.
+ *
+ * Example:
+ *
+ * ```
+ * Throws(IOException::class)
+ * fun readFile(name: String): String {...}
+ * ```
+ */
+class Throws
+
+
+/**
+ * Check output of
+ * ``` brainfuck
+ * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+ * ```
+ */
+class ItDoesSomeObfuscatedThing \ No newline at end of file
diff --git a/core/testdata/format/codeBlock.md b/core/testdata/format/codeBlock.md
new file mode 100644
index 000000000..d697e437d
--- /dev/null
+++ b/core/testdata/format/codeBlock.md
@@ -0,0 +1,37 @@
+<!-- File: test/-it-does-some-obfuscated-thing/index.md -->
+[test](../index.md) / [ItDoesSomeObfuscatedThing](./index.md)
+
+# ItDoesSomeObfuscatedThing
+
+`class ItDoesSomeObfuscatedThing`
+
+Check output of
+
+``` brainfuck
+++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `ItDoesSomeObfuscatedThing()`<br>Check output of |
+
+<!-- File: test/-throws/index.md -->
+[test](../index.md) / [Throws](./index.md)
+
+# Throws
+
+`class Throws`
+
+This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.
+
+Example:
+
+```
+Throws(IOException::class)
+fun readFile(name: String): String {...}
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Throws()`<br>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. |
+
diff --git a/core/testdata/format/codeBlockNoHtmlEscape.kt b/core/testdata/format/codeBlockNoHtmlEscape.kt
new file mode 100644
index 000000000..5f48b39cb
--- /dev/null
+++ b/core/testdata/format/codeBlockNoHtmlEscape.kt
@@ -0,0 +1,15 @@
+/**
+ * Try to make this check pass
+ * ```
+ * if(1 > 2)
+ * ```
+ * Or just piece of html
+ * ```
+ * <p>1 = 3</p>
+ * ```
+ */
+fun hackTheArithmetic(){
+ valueOf(1) {
+ set(3)
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/codeBlockNoHtmlEscape.md b/core/testdata/format/codeBlockNoHtmlEscape.md
new file mode 100644
index 000000000..548dac4f0
--- /dev/null
+++ b/core/testdata/format/codeBlockNoHtmlEscape.md
@@ -0,0 +1,18 @@
+[test](index.md) / [hackTheArithmetic](./hack-the-arithmetic.md)
+
+# hackTheArithmetic
+
+`fun hackTheArithmetic(): Unit`
+
+Try to make this check pass
+
+```
+if(1 > 2)
+```
+
+Or just piece of html
+
+```
+<p>1 = 3</p>
+```
+
diff --git a/core/testdata/format/codeSpan.html b/core/testdata/format/codeSpan.html
new file mode 100644
index 000000000..bfe93f364
--- /dev/null
+++ b/core/testdata/format/codeSpan.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This is a <code>code span</code>.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/codeSpan.kt b/core/testdata/format/codeSpan.kt
new file mode 100644
index 000000000..645f454a7
--- /dev/null
+++ b/core/testdata/format/codeSpan.kt
@@ -0,0 +1,4 @@
+/**
+ * This is a `code span`.
+ */
+fun foo() {} \ No newline at end of file
diff --git a/core/testdata/format/companionImplements.kt b/core/testdata/format/companionImplements.kt
new file mode 100644
index 000000000..154ef9b1c
--- /dev/null
+++ b/core/testdata/format/companionImplements.kt
@@ -0,0 +1,9 @@
+
+interface Bar
+
+/**
+ * Correct ref [Foo.Companion]
+ */
+class Foo {
+ companion object : Bar
+} \ No newline at end of file
diff --git a/core/testdata/format/companionImplements.md b/core/testdata/format/companionImplements.md
new file mode 100644
index 000000000..aac7b3e60
--- /dev/null
+++ b/core/testdata/format/companionImplements.md
@@ -0,0 +1,16 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+Correct ref [Foo.Companion](-companion.md)
+
+### Types
+
+| [Companion](-companion.md) | `companion object Companion : `[`Bar`](../-bar.md) |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>Correct ref [Foo.Companion](-companion.md) |
+
diff --git a/core/testdata/format/companionObjectExtension.kt b/core/testdata/format/companionObjectExtension.kt
new file mode 100644
index 000000000..f452de2c8
--- /dev/null
+++ b/core/testdata/format/companionObjectExtension.kt
@@ -0,0 +1,10 @@
+class Foo {
+ companion object Default {
+ }
+}
+
+
+/**
+ * The default object property.
+ */
+val Foo.Default.x: Int get() = 1
diff --git a/core/testdata/format/companionObjectExtension.md b/core/testdata/format/companionObjectExtension.md
new file mode 100644
index 000000000..c0e268e6f
--- /dev/null
+++ b/core/testdata/format/companionObjectExtension.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Companion Object Extension Properties
+
+| [x](../x.md) | `val Foo.Default.x: Int`<br>The default object property. |
+
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
new file mode 100644
index 000000000..8842969e5
--- /dev/null
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Inherited Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="../-foo/xyzzy.html">xyzzy</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">xyzzy</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt
new file mode 100644
index 000000000..102782f95
--- /dev/null
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt
@@ -0,0 +1,6 @@
+package test
+
+/**
+ * See [xyzzy]
+ */
+class Bar(): Foo()
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java b/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java
new file mode 100644
index 000000000..7c1430308
--- /dev/null
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java
@@ -0,0 +1,6 @@
+package test;
+
+public class Foo {
+ public void xyzzy() {
+ }
+}
diff --git a/core/testdata/format/dac/deprecation/DeprecatedBar.kt b/core/testdata/format/dac/deprecation/DeprecatedBar.kt
new file mode 100644
index 000000000..39ac1c43d
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/DeprecatedBar.kt
@@ -0,0 +1,17 @@
+class Bar {
+ fun replacementBarMethod(): Bar { return Bar() }
+
+ fun badBarMethod(): DeprecatedBar { return DeprecatedBar() }
+
+ /**
+ * This method has been deprecated in favor of replacementBarMethod().
+ */
+ @Deprecated("Obsolete method", ReplaceWith("replacementBarMethod()"))
+ fun goodBarMethod(): DeprecatedBar { return DeprecatedBar() }
+}
+
+/**
+ * This class has been deprecated in favor of Bar.
+ */
+@Deprecated("Obsolete class", ReplaceWith("Bar"))
+class DeprecatedBar \ No newline at end of file
diff --git a/core/testdata/format/dac/deprecation/DeprecatedFoo.java b/core/testdata/format/dac/deprecation/DeprecatedFoo.java
new file mode 100644
index 000000000..3c9c360ee
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/DeprecatedFoo.java
@@ -0,0 +1,21 @@
+public class Foo {
+ public Foo() { }
+
+ public static Foo replacementFooMethod() { return Foo() }
+
+ public static DeprecatedFoo badFooMethod() { return new DeprecatedFoo() }
+
+ /**
+ * @deprecated Use {@link #replacementFooMethod()} instead.
+ */
+ @Deprecated
+ public static DeprecatedFoo goodFooMethod() { return new DeprecatedFoo() }
+}
+
+/**
+ * @deprecated Use {@link #Foo} instead.
+ */
+@Deprecated
+public class DeprecatedFoo {
+ public DeprecatedFoo() { }
+}
diff --git a/core/testdata/format/dac/deprecation/dac-as-java/Bar.html b/core/testdata/format/dac/deprecation/dac-as-java/Bar.html
new file mode 100644
index 000000000..9f9346ee7
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac-as-java/Bar.html
@@ -0,0 +1,83 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td>
+ <td>
+ <div><code><a href="#badBarMethod()">badBarMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td>
+ <td>
+ <div><code><a href="#goodBarMethod()">goodBarMethod</a>()</code></div>
+ <p>This method has been deprecated in favor of replacementBarMethod().</p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">final</span> <a href="#"><span class="identifier">Bar</span></a></td>
+ <td>
+ <div><code><a href="#replacementBarMethod()">replacementBarMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="badBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">badBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a> <span class="identifier">badBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+<a name="goodBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">goodBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a> <span class="identifier">goodBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p class="caution"><strong>Deprecated: </strong><em>Obsolete method. ReplaceWith(&quot;replacementBarMethod()&quot;). </em></p>
+ <p>This method has been deprecated in favor of replacementBarMethod().</p>
+ </div>
+<a name="replacementBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">replacementBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="#"><span class="identifier">Bar</span></a> <span class="identifier">replacementBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html
new file mode 100644
index 000000000..d6e955413
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html
@@ -0,0 +1,39 @@
+<html devsite="true">
+ <head>
+ <title>DeprecatedBar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>DeprecatedBar</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">DeprecatedBar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedBar</a></td>
+ </tr>
+ </table>
+ <p>This class has been deprecated in favor of Bar.</p>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#DeprecatedBar()">DeprecatedBar</a>()</code></div>
+ <p>This class has been deprecated in favor of Bar.</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="DeprecatedBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">DeprecatedBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">DeprecatedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>This class has been deprecated in favor of Bar.</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html
new file mode 100644
index 000000000..360b24ef3
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>DeprecatedFoo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>DeprecatedFoo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">DeprecatedFoo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedFoo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#DeprecatedFoo()">DeprecatedFoo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="DeprecatedFoo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">DeprecatedFoo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">DeprecatedFoo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac-as-java/Foo.html b/core/testdata/format/dac/deprecation/dac-as-java/Foo.html
new file mode 100644
index 000000000..b468e4daa
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac-as-java/Foo.html
@@ -0,0 +1,82 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a></td>
+ <td>
+ <div><code><a href="#badFooMethod()">badFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a></td>
+ <td>
+ <div><code><a href="#goodFooMethod()">goodFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a></td>
+ <td>
+ <div><code><a href="#replacementFooMethod()">replacementFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="badFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">badFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a> <span class="identifier">badFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+<a name="goodFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">goodFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a> <span class="identifier">goodFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p class="caution"><strong>Deprecated.</strong><em></em></p>
+ </div>
+<a name="replacementFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">replacementFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a> <span class="identifier">replacementFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac/Bar.html b/core/testdata/format/dac/deprecation/dac/Bar.html
new file mode 100644
index 000000000..7ae5c5c46
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac/Bar.html
@@ -0,0 +1,83 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Bar</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td>
+ <td>
+ <div><code><a href="#badBarMethod()">badBarMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td>
+ <td>
+ <div><code><a href="#goodBarMethod()">goodBarMethod</a>()</code></div>
+ <p>This method has been deprecated in favor of replacementBarMethod().</p>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="#"><span class="identifier">Bar</span></a></td>
+ <td>
+ <div><code><a href="#replacementBarMethod()">replacementBarMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="badBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">badBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><span class="identifier">badBarMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></pre>
+ </div>
+<a name="goodBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">goodBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><del><span class="identifier">goodBarMethod</span></del><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></pre>
+ <p class="caution"><strong>Deprecated: </strong><em>Obsolete method. Replace with: &quot;replacementBarMethod()&quot;. </em></p>
+ <p>This method has been deprecated in favor of replacementBarMethod().</p>
+ </div>
+<a name="replacementBarMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">replacementBarMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><span class="identifier">replacementBarMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="#"><span class="identifier">Bar</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html b/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html
new file mode 100644
index 000000000..11a6a5667
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html
@@ -0,0 +1,39 @@
+<html devsite="true">
+ <head>
+ <title>DeprecatedBar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>DeprecatedBar</h1>
+ <pre><span class="keyword">class </span><del><span class="identifier">DeprecatedBar</span></del></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedBar</a></td>
+ </tr>
+ </table>
+ <p>This class has been deprecated in favor of Bar.</p>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#DeprecatedBar()">DeprecatedBar</a>()</code></div>
+ <p>This class has been deprecated in favor of Bar.</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="DeprecatedBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">DeprecatedBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">DeprecatedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>This class has been deprecated in favor of Bar.</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html b/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html
new file mode 100644
index 000000000..4dcf39c19
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>DeprecatedFoo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>DeprecatedFoo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><del><span class="identifier">DeprecatedFoo</span></del></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedFoo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#DeprecatedFoo()">DeprecatedFoo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="DeprecatedFoo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">DeprecatedFoo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">DeprecatedFoo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/deprecation/dac/Foo.html b/core/testdata/format/dac/deprecation/dac/Foo.html
new file mode 100644
index 000000000..9fe6ad2d2
--- /dev/null
+++ b/core/testdata/format/dac/deprecation/dac/Foo.html
@@ -0,0 +1,82 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></td>
+ <td>
+ <div><code><a href="#badFooMethod()">badFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></td>
+ <td>
+ <div><code><a href="#goodFooMethod()">goodFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a><span class="symbol">!</span></td>
+ <td>
+ <div><code><a href="#replacementFooMethod()">replacementFooMethod</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="badFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">badFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><span class="identifier">badFooMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></pre>
+ </div>
+<a name="goodFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">goodFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><del><span class="identifier">goodFooMethod</span></del><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></pre>
+ <p class="caution"><strong>Deprecated: </strong><em>Use <code><a href="#replacementFooMethod()">replacementFooMethod()</a></code> instead.</em></p>
+ </div>
+<a name="replacementFooMethod()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">replacementFooMethod</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><span class="identifier">replacementFooMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="#"><span class="identifier">Foo</span></a><span class="symbol">!</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/inheritedMethods/Child.java b/core/testdata/format/dac/inheritedMethods/Child.java
new file mode 100644
index 000000000..902f64947
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/Child.java
@@ -0,0 +1,8 @@
+public class Child extends Parent {
+ public void Bar() { }
+ /**
+ * Do an override subclass baz
+ */
+ @override
+ public void baz() { }
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/inheritedMethods/Parent.java b/core/testdata/format/dac/inheritedMethods/Parent.java
new file mode 100644
index 000000000..59d9ab0f3
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/Parent.java
@@ -0,0 +1,10 @@
+public class Parent {
+ /**
+ * Do a superclass foo
+ */
+ public void foo() { }
+ /**
+ * Do a superclass baz
+ */
+ public void baz() { }
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html b/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html
new file mode 100644
index 000000000..67a58125a
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html
@@ -0,0 +1,74 @@
+<html devsite="true">
+ <head>
+ <title>Child</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Child</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Child</span> <span class="keyword">extends</span> <a href="Parent.html#"><span class="identifier">Parent</span></a></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="2"><a href="Parent.html#">Parent</a></td>
+ </tr>
+ <tr>
+ <td class="jd-inheritance-space">   ↳</td>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Child</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Child()">Child</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#baz()">baz</a>()</code></div>
+ <p>Do an override subclass baz</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Child()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Child</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Child</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+<a name="baz()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">baz</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>Do an override subclass baz</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html b/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html
new file mode 100644
index 000000000..977e693d8
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html
@@ -0,0 +1,92 @@
+<html devsite="true">
+ <head>
+ <title>Parent</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Parent</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Parent</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Parent</a></td>
+ </tr>
+ </table>
+ <table class="jd-sumtable jd-sumtable-subclasses">
+ <tbody>
+ <tr>
+ <td>
+ <div class="expandable"><span class="expand-control">Known Direct Subclasses</span>
+ <div class="showalways" id="subclasses-direct"><a href="Child.html#">Child</a></div>
+ <div class="exw-expanded-content" id="subclasses-direct-summary">
+ <table class="jd-sumtable-expando">
+ <tr class="api api-level-" data-version-added="">
+ <td class="jd-linkcol"><a href="Child.html#">Child</a></td>
+ <td class="jd-descrcol" width="100%">
+ <p></p>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Parent()">Parent</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#baz()">baz</a>()</code></div>
+ <p>Do a superclass baz</p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#foo()">foo</a>()</code></div>
+ <p>Do a superclass foo</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Parent()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Parent</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Parent</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="baz()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">baz</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>Do a superclass baz</p>
+ </div>
+<a name="foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>Do a superclass foo</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/inheritedMethods/dac/Child.html b/core/testdata/format/dac/inheritedMethods/dac/Child.html
new file mode 100644
index 000000000..c645a89e7
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/dac/Child.html
@@ -0,0 +1,99 @@
+<html devsite="true">
+ <head>
+ <title>Child</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Child</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Child</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="Parent.html#"><span class="identifier">Parent</span></a></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="2"><a href="Parent.html#">Parent</a></td>
+ </tr>
+ <tr>
+ <td class="jd-inheritance-space">   ↳</td>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Child</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Child()">Child</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#baz()">baz</a>()</code></div>
+ <p>Do an override subclass baz</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="inhmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Inherited functions</th>
+ </tr>
+ <tr class="api apilevel-">
+ <td colSpan="2">
+ <div class="expandable jd-inherited-apis"><span class="expand-control exw-expanded">From class <code><a href="Parent.html#">Parent</a></code></span>
+ <table class="responsive exw-expanded-content">
+ <tbody>
+ <tr class="api apilevel-" data-version-added="ApiLevel:">
+ <td><code><span class="identifier">Unit</span></code></td>
+ <td width="100%"><code><a href="Parent.html#foo()">foo</a>()</code>
+ <p>
+ <p>Do a superclass foo</p>
+ </p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Child()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Child</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Child</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><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></pre>
+ </div>
+<a name="baz()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">baz</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ <p>Do an override subclass baz</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/inheritedMethods/dac/Parent.html b/core/testdata/format/dac/inheritedMethods/dac/Parent.html
new file mode 100644
index 000000000..72b0b72a1
--- /dev/null
+++ b/core/testdata/format/dac/inheritedMethods/dac/Parent.html
@@ -0,0 +1,92 @@
+<html devsite="true">
+ <head>
+ <title>Parent</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Parent</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Parent</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Parent</a></td>
+ </tr>
+ </table>
+ <table class="jd-sumtable jd-sumtable-subclasses">
+ <tbody>
+ <tr>
+ <td>
+ <div class="expandable"><span class="expand-control">Known Direct Subclasses</span>
+ <div class="showalways" id="subclasses-direct"><a href="Child.html#">Child</a></div>
+ <div class="exw-expanded-content" id="subclasses-direct-summary">
+ <table class="jd-sumtable-expando">
+ <tr class="api api-level-" data-version-added="">
+ <td class="jd-linkcol"><a href="Child.html#">Child</a></td>
+ <td class="jd-descrcol" width="100%">
+ <p></p>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Parent()">Parent</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#baz()">baz</a>()</code></div>
+ <p>Do a superclass baz</p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#foo()">foo</a>()</code></div>
+ <p>Do a superclass foo</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Parent()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Parent</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Parent</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="baz()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">baz</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ <p>Do a superclass baz</p>
+ </div>
+<a name="foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <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></pre>
+ <p>Do a superclass foo</p>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaClassLinks/Bar.java b/core/testdata/format/dac/javaClassLinks/Bar.java
new file mode 100644
index 000000000..88192654b
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/Bar.java
@@ -0,0 +1,28 @@
+import java.util.ArrayList;
+import java.util.HashSet;
+
+public class Bar {
+ /**
+ * @return The best of bars.
+ */
+ public Bar() { }
+
+ /**
+ * @param input The best foo you can find
+ * @return Another spectacular Foo for you to use.
+ */
+ public Foo getMyFoo(Foo input) { return null; }
+
+ /**
+ * @param input The best set you can afford.
+ * @return
+ */
+ public ArrayList<String> getMyList(HashSet input) { return null; }
+
+ /**
+ * @return {@code true} if the fun mode available, {@code false} otherwise.
+ */
+ public boolean isFunAvailable() { throw new RuntimeException("Stub!"); }
+
+
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaClassLinks/Foo.java b/core/testdata/format/dac/javaClassLinks/Foo.java
new file mode 100644
index 000000000..0a2b05268
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/Foo.java
@@ -0,0 +1,2 @@
+public class Foo {
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html b/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html
new file mode 100644
index 000000000..64d0c4cd8
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html
@@ -0,0 +1,147 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><a href="Foo.html#"><span class="identifier">Foo</span></a></td>
+ <td>
+ <div><code><a href="#getMyFoo(Foo)">getMyFoo</a>(<a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">input</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">&gt;</span></td>
+ <td>
+ <div><code><a href="#getMyList(HashSet)">getMyList</a>(<a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a> <span class="identifier">input</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">boolean</span></td>
+ <td>
+ <div><code><a href="#isFunAvailable()">isFunAvailable</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td></td>
+ <td>The best of bars.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <h2>Public methods</h2>
+<a name="getMyFoo(Foo)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">getMyFoo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">getMyFoo</span><span class="symbol">(</span><a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">input</span><span class="symbol">)</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Parameters</th>
+ </tr>
+ <tr>
+ <td><code>input</code></td>
+ <td><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">:</span> The best foo you can find</td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><a href="Foo.html#"><span class="identifier">Foo</span></a></code></td>
+ <td>Another spectacular Foo for you to use.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+<a name="getMyList(HashSet)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">getMyList</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">&gt;</span> <span class="identifier">getMyList</span><span class="symbol">(</span><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a> <span class="identifier">input</span><span class="symbol">)</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Parameters</th>
+ </tr>
+ <tr>
+ <td><code>input</code></td>
+ <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol">:</span> The best set you can afford.</td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">&gt;</span></code></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+<a name="isFunAvailable()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">isFunAvailable</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">boolean</span> <span class="identifier">isFunAvailable</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><span class="identifier">boolean</span></code></td>
+ <td><code>true</code> if the fun mode available, <code>false</code> otherwise.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html b/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html
new file mode 100644
index 000000000..bddb7ff8a
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaClassLinks/dac/Bar.html b/core/testdata/format/dac/javaClassLinks/dac/Bar.html
new file mode 100644
index 000000000..c8961e07f
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/dac/Bar.html
@@ -0,0 +1,147 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></td>
+ <td>
+ <div><code><a href="#getMyFoo(Foo)">getMyFoo</a>(<span class="identifier" id="Bar$getMyFoo(Foo)/input">input</span><span class="symbol">:</span>&nbsp;<a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span></td>
+ <td>
+ <div><code><a href="#getMyList(java.util.HashSet)">getMyList</a>(<span class="identifier" id="Bar$getMyList(java.util.HashSet((kotlin.Any)))/input">input</span><span class="symbol">:</span>&nbsp;<a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol">&lt;</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Boolean</span></td>
+ <td>
+ <div><code><a href="#isFunAvailable()">isFunAvailable</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td></td>
+ <td>The best of bars.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <h2>Public methods</h2>
+<a name="getMyFoo(Foo)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">getMyFoo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">getMyFoo</span><span class="symbol">(</span><span class="identifier" id="Bar$getMyFoo(Foo)/input">input</span><span class="symbol">:</span>&nbsp;<a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Parameters</th>
+ </tr>
+ <tr>
+ <td><code>input</code></td>
+ <td><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span><span class="symbol">:</span> The best foo you can find</td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></code></td>
+ <td>Another spectacular Foo for you to use.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+<a name="getMyList(java.util.HashSet)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">getMyList</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">getMyList</span><span class="symbol">(</span><span class="identifier" id="Bar$getMyList(java.util.HashSet((kotlin.Any)))/input">input</span><span class="symbol">:</span>&nbsp;<a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol">&lt;</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Parameters</th>
+ </tr>
+ <tr>
+ <td><code>input</code></td>
+ <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol">&lt;</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span><span class="symbol">:</span> The best set you can afford.</td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol">&lt;</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">&gt;</span><span class="symbol">!</span></code></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+<a name="isFunAvailable()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">isFunAvailable</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">isFunAvailable</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Boolean</span></pre>
+ <table class="responsive">
+ <tbody>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ <tr>
+ <td><code><span class="identifier">Boolean</span></code></td>
+ <td><code>true</code> if the fun mode available, <code>false</code> otherwise.</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaClassLinks/dac/Foo.html b/core/testdata/format/dac/javaClassLinks/dac/Foo.html
new file mode 100644
index 000000000..b1ed934c3
--- /dev/null
+++ b/core/testdata/format/dac/javaClassLinks/dac/Foo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaConstructor/Foo.java b/core/testdata/format/dac/javaConstructor/Foo.java
new file mode 100644
index 000000000..4d70be4fb
--- /dev/null
+++ b/core/testdata/format/dac/javaConstructor/Foo.java
@@ -0,0 +1,4 @@
+public class Foo {
+ public Foo() {}
+ public Foo(String value) {}
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html b/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html
new file mode 100644
index 000000000..01cb1bb19
--- /dev/null
+++ b/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html
@@ -0,0 +1,48 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo(String)">Foo</a>(<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html"><span class="identifier">String</span></a> <span class="identifier">value</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+<a name="Foo(String)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html"><span class="identifier">String</span></a> <span class="identifier">value</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaConstructor/dac/Foo.html b/core/testdata/format/dac/javaConstructor/dac/Foo.html
new file mode 100644
index 000000000..948d024b8
--- /dev/null
+++ b/core/testdata/format/dac/javaConstructor/dac/Foo.html
@@ -0,0 +1,48 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo(kotlin.String)">Foo</a>(<span class="identifier" id="Foo$&lt;init&gt;(kotlin.String)/value">value</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">!</span>)</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+<a name="Foo(kotlin.String)"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="identifier" id="Foo$&lt;init&gt;(kotlin.String)/value">value</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">!</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaDefaultConstructor/Foo.java b/core/testdata/format/dac/javaDefaultConstructor/Foo.java
new file mode 100644
index 000000000..0a2b05268
--- /dev/null
+++ b/core/testdata/format/dac/javaDefaultConstructor/Foo.java
@@ -0,0 +1,2 @@
+public class Foo {
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html b/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html
new file mode 100644
index 000000000..bddb7ff8a
--- /dev/null
+++ b/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html b/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html
new file mode 100644
index 000000000..b1ed934c3
--- /dev/null
+++ b/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html
@@ -0,0 +1,37 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaMethodVisibilities/Foo.java b/core/testdata/format/dac/javaMethodVisibilities/Foo.java
new file mode 100644
index 000000000..936632df9
--- /dev/null
+++ b/core/testdata/format/dac/javaMethodVisibilities/Foo.java
@@ -0,0 +1,5 @@
+public class Foo {
+ public void publicBar() {}
+ protected void protectedBar() {}
+ private void privateBar() {}
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html b/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html
new file mode 100644
index 000000000..978b1071d
--- /dev/null
+++ b/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html
@@ -0,0 +1,97 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#publicBar()">publicBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="promethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Protected methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#protectedBar()">protectedBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="primethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Private methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#privateBar()">privateBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="publicBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">publicBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">publicBar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Protected methods</h2>
+<a name="protectedBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">protectedBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">protected</span> <span class="identifier">void</span> <span class="identifier">protectedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Private methods</h2>
+<a name="privateBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">privateBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">private</span> <span class="identifier">void</span> <span class="identifier">privateBar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html b/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html
new file mode 100644
index 000000000..b4dddf185
--- /dev/null
+++ b/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html
@@ -0,0 +1,97 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#publicBar()">publicBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="promethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Protected methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#protectedBar()">protectedBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="primethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Private methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#privateBar()">privateBar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="publicBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">publicBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">publicBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ </div>
+ <h2>Protected methods</h2>
+<a name="protectedBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">protectedBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">protectedBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ </div>
+ <h2>Private methods</h2>
+<a name="privateBar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">privateBar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">private</span> <span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">privateBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaSeeTag/Bar.kt b/core/testdata/format/dac/javaSeeTag/Bar.kt
new file mode 100644
index 000000000..21170d363
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/Bar.kt
@@ -0,0 +1,5 @@
+/**
+ * @see Foo
+ * @see java.lang.String
+ */
+class Bar \ No newline at end of file
diff --git a/core/testdata/format/dac/javaSeeTag/Foo.java b/core/testdata/format/dac/javaSeeTag/Foo.java
new file mode 100644
index 000000000..b784ae225
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/Foo.java
@@ -0,0 +1,7 @@
+/**
+ * @see #bar
+ * @see java.lang.String
+ */
+public class Foo {
+ public void bar() {}
+} \ No newline at end of file
diff --git a/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html b/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html
new file mode 100644
index 000000000..7eb3cdf85
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html
@@ -0,0 +1,44 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <div>
+ <p><b>See Also</b></p>
+ <ul class="nolist">
+ <li><code><a href="Foo.html#">Foo</a></code></li>
+ <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li>
+ </ul>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html b/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html
new file mode 100644
index 000000000..4529fd04d
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html
@@ -0,0 +1,57 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="identifier">void</span></td>
+ <td>
+ <div><code><a href="#bar()">bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Public methods</h2>
+<a name="bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaSeeTag/dac/Bar.html b/core/testdata/format/dac/javaSeeTag/dac/Bar.html
new file mode 100644
index 000000000..c21dfb27b
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/dac/Bar.html
@@ -0,0 +1,44 @@
+<html devsite="true">
+ <head>
+ <title>Bar</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Bar</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Bar</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Bar()">Bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Bar</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <div>
+ <p><b>See Also</b></p>
+ <ul class="nolist">
+ <li><code><a href="Foo.html#">Foo</a></code></li>
+ <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li>
+ </ul>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/dac/javaSeeTag/dac/Foo.html b/core/testdata/format/dac/javaSeeTag/dac/Foo.html
new file mode 100644
index 000000000..7640805ff
--- /dev/null
+++ b/core/testdata/format/dac/javaSeeTag/dac/Foo.html
@@ -0,0 +1,64 @@
+<html devsite="true">
+ <head>
+ <title>Foo</title>
+{% setvar book_path %}/_book.yaml{% endsetvar %}
+{% include "_shared/_reference-head-tags.html" %}
+ </head>
+ <body>
+ <div id="api-info-block"></div>
+ <h1>Foo</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table class="jd-inheritance-table">
+ <tr>
+ <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table class="responsive" id="pubctors">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public constructors</th>
+ </tr>
+ <tr>
+ <td>
+ <div><code><a href="#Foo()">Foo</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table class="responsive" id="pubmethods">
+ <tbody>
+ <tr>
+ <th colSpan="2">Public methods</th>
+ </tr>
+ <tr>
+ <td><span class="keyword">open</span> <span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#bar()">bar</a>()</code></div>
+ <p></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Public constructors</h2>
+<a name="Foo()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">Foo</h3>
+ <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <div>
+ <p><b>See Also</b></p>
+ <ul class="nolist">
+ <li><code><a href="#bar()">#bar</a></code></li>
+ <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li>
+ </ul>
+ </div>
+ </div>
+ <h2>Public methods</h2>
+<a name="bar()"></a>
+ <div class="api apilevel-" data-version-added="">
+ <h3 class="api-name">bar</h3>
+ <pre class="api-signature no-pretty-print"><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></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/deprecated.class.html b/core/testdata/format/deprecated.class.html
new file mode 100644
index 000000000..540060d1b
--- /dev/null
+++ b/core/testdata/format/deprecated.class.html
@@ -0,0 +1,59 @@
+<!-- File: test/-c/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>C - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
+<br/>
+<h1>C</h1>
+<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code><br/>
+<strong>Deprecated:</strong> This class sucks<br/>
+<br/>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<strong>Deprecated:</strong> This function sucks<br/>
+<br/>
+</BODY>
+</HTML>
+<!-- File: test/p.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>p - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./p.html">p</a><br/>
+<br/>
+<h1>p</h1>
+<a name="$p"></a>
+<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code><br/>
+<strong>Deprecated:</strong> This property sucks<br/>
+<br/>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/deprecated.kt b/core/testdata/format/deprecated.kt
new file mode 100644
index 000000000..4fc568c23
--- /dev/null
+++ b/core/testdata/format/deprecated.kt
@@ -0,0 +1,5 @@
+@Deprecated("This class sucks") class C() { }
+
+@Deprecated("This function sucks") fun f() { }
+
+@Deprecated("This property sucks") val p: Int get() = 0
diff --git a/core/testdata/format/deprecated.package.html b/core/testdata/format/deprecated.package.html
new file mode 100644
index 000000000..3506de61e
--- /dev/null
+++ b/core/testdata/format/deprecated.package.html
@@ -0,0 +1,47 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>root package - test</title>
+</HEAD>
+<BODY>
+<a href="./index.html">test</a><br/>
+<br/>
+<h2>Package &lt;root&gt;</h2>
+<h3>Types</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-c/index.html">C</a></p>
+</td>
+<td>
+<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Properties</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="p.html">p</a></p>
+</td>
+<td>
+<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="f.html">f</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/dynamicExtension.kt b/core/testdata/format/dynamicExtension.kt
new file mode 100644
index 000000000..5c83bf229
--- /dev/null
+++ b/core/testdata/format/dynamicExtension.kt
@@ -0,0 +1,3 @@
+class Foo
+
+fun dynamic.bar() {}
diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md
new file mode 100644
index 000000000..382cf9733
--- /dev/null
+++ b/core/testdata/format/dynamicExtension.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/dynamicType.kt b/core/testdata/format/dynamicType.kt
new file mode 100644
index 000000000..9d557ac0a
--- /dev/null
+++ b/core/testdata/format/dynamicType.kt
@@ -0,0 +1,2 @@
+fun foo(): dynamic = ""
+
diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md
new file mode 100644
index 000000000..07a1d103c
--- /dev/null
+++ b/core/testdata/format/dynamicType.md
@@ -0,0 +1,5 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): dynamic` \ No newline at end of file
diff --git a/core/testdata/format/emptyDescription.kt b/core/testdata/format/emptyDescription.kt
new file mode 100644
index 000000000..3ed81dfa5
--- /dev/null
+++ b/core/testdata/format/emptyDescription.kt
@@ -0,0 +1,5 @@
+/**
+ * Function fn
+ */
+fun fn() {
+} \ No newline at end of file
diff --git a/core/testdata/format/emptyDescription.md b/core/testdata/format/emptyDescription.md
new file mode 100644
index 000000000..5d56d968e
--- /dev/null
+++ b/core/testdata/format/emptyDescription.md
@@ -0,0 +1,8 @@
+[test](index.md) / [fn](./fn.md)
+
+# fn
+
+`fun fn(): Unit`
+
+Function fn
+
diff --git a/core/testdata/format/entity.html b/core/testdata/format/entity.html
new file mode 100644
index 000000000..639f2903a
--- /dev/null
+++ b/core/testdata/format/entity.html
@@ -0,0 +1,27 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Copyright &copy; JetBrains 2015 &#x22;</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Copyright &copy; JetBrains 2015 &#x22;</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/entity.kt b/core/testdata/format/entity.kt
new file mode 100644
index 000000000..163d2ee6d
--- /dev/null
+++ b/core/testdata/format/entity.kt
@@ -0,0 +1,4 @@
+/**
+ * Copyright &copy; JetBrains 2015 &#x22;
+ */
+class Bar {}
diff --git a/core/testdata/format/enumClass.kt b/core/testdata/format/enumClass.kt
new file mode 100644
index 000000000..3c2a49fcf
--- /dev/null
+++ b/core/testdata/format/enumClass.kt
@@ -0,0 +1,4 @@
+public enum class InlineOption {
+ ONLY_LOCAL_RETURN,
+ LOCAL_CONTINUE_AND_BREAK
+}
diff --git a/core/testdata/format/enumClass.md b/core/testdata/format/enumClass.md
new file mode 100644
index 000000000..76c83b9db
--- /dev/null
+++ b/core/testdata/format/enumClass.md
@@ -0,0 +1,11 @@
+[test](../index.md) / [InlineOption](./index.md)
+
+# InlineOption
+
+`enum class InlineOption`
+
+### Enum Values
+
+| [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) | `enum val LOCAL_CONTINUE_AND_BREAK : `[`InlineOption`](./index.md) |
+| [ONLY_LOCAL_RETURN](-o-n-l-y_-l-o-c-a-l_-r-e-t-u-r-n.md) | `enum val ONLY_LOCAL_RETURN : `[`InlineOption`](./index.md) |
+
diff --git a/core/testdata/format/enumClass.value.md b/core/testdata/format/enumClass.value.md
new file mode 100644
index 000000000..590577dae
--- /dev/null
+++ b/core/testdata/format/enumClass.value.md
@@ -0,0 +1,5 @@
+[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
+
+`enum val LOCAL_CONTINUE_AND_BREAK : `[`InlineOption`](index.md) \ No newline at end of file
diff --git a/core/testdata/format/enumRef.kt b/core/testdata/format/enumRef.kt
new file mode 100644
index 000000000..5c0b2de9e
--- /dev/null
+++ b/core/testdata/format/enumRef.kt
@@ -0,0 +1,4 @@
+/**
+ * [java.math.RoundingMode.UP]
+ */
+fun f() {} \ No newline at end of file
diff --git a/core/testdata/format/enumRef.md b/core/testdata/format/enumRef.md
new file mode 100644
index 000000000..8b2a6650e
--- /dev/null
+++ b/core/testdata/format/enumRef.md
@@ -0,0 +1,8 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f(): Unit`
+
+[java.math.RoundingMode.UP](http://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html#UP)
+
diff --git a/core/testdata/format/exceptionClass.kt b/core/testdata/format/exceptionClass.kt
new file mode 100644
index 000000000..d005bd898
--- /dev/null
+++ b/core/testdata/format/exceptionClass.kt
@@ -0,0 +1 @@
+class MyException : Exception
diff --git a/core/testdata/format/exceptionClass.md b/core/testdata/format/exceptionClass.md
new file mode 100644
index 000000000..1e928bb6a
--- /dev/null
+++ b/core/testdata/format/exceptionClass.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [MyException](./index.md)
+
+# MyException
+
+`class MyException : Exception`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `MyException()` |
+
diff --git a/core/testdata/format/exceptionClass.package.md b/core/testdata/format/exceptionClass.package.md
new file mode 100644
index 000000000..8716df0a5
--- /dev/null
+++ b/core/testdata/format/exceptionClass.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Exceptions
+
+| [MyException](-my-exception/index.md) | `class MyException : Exception` |
+
diff --git a/core/testdata/format/exclInCodeBlock.kt b/core/testdata/format/exclInCodeBlock.kt
new file mode 100644
index 000000000..62b234ddf
--- /dev/null
+++ b/core/testdata/format/exclInCodeBlock.kt
@@ -0,0 +1,5 @@
+/**
+ * The magic word is `!`
+ */
+fun foo() {
+}
diff --git a/core/testdata/format/exclInCodeBlock.md b/core/testdata/format/exclInCodeBlock.md
new file mode 100644
index 000000000..d665c4153
--- /dev/null
+++ b/core/testdata/format/exclInCodeBlock.md
@@ -0,0 +1,8 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): Unit`
+
+The magic word is `!`
+
diff --git a/core/testdata/format/extensionFunctionParameter.kt b/core/testdata/format/extensionFunctionParameter.kt
new file mode 100644
index 000000000..bfb344b9a
--- /dev/null
+++ b/core/testdata/format/extensionFunctionParameter.kt
@@ -0,0 +1 @@
+public inline fun <T> T.apply(f: T.() -> Unit): T { f(); return this }
diff --git a/core/testdata/format/extensionFunctionParameter.md b/core/testdata/format/extensionFunctionParameter.md
new file mode 100644
index 000000000..e1e858245
--- /dev/null
+++ b/core/testdata/format/extensionFunctionParameter.md
@@ -0,0 +1,5 @@
+[test](index.md) / [apply](./apply.md)
+
+# apply
+
+`inline fun <T> `[`T`](apply.md#T)`.apply(f: `[`T`](apply.md#T)`.() -> Unit): `[`T`](apply.md#T) \ No newline at end of file
diff --git a/core/testdata/format/extensionScope.kt b/core/testdata/format/extensionScope.kt
new file mode 100644
index 000000000..9f3130b85
--- /dev/null
+++ b/core/testdata/format/extensionScope.kt
@@ -0,0 +1,14 @@
+/**
+ * Test class with Type-parameter
+ */
+class Foo<T>
+
+/**
+ * Some extension on Foo
+ */
+fun <T> Foo<T>.ext() {}
+
+/**
+ * Correct link: [Foo.ext]
+ */
+fun test() {} \ No newline at end of file
diff --git a/core/testdata/format/extensionScope.md b/core/testdata/format/extensionScope.md
new file mode 100644
index 000000000..ea765bd59
--- /dev/null
+++ b/core/testdata/format/extensionScope.md
@@ -0,0 +1,8 @@
+[test](index.md) / [test](./test.md)
+
+# test
+
+`fun test(): Unit`
+
+Correct link: [Foo.ext](ext.md)
+
diff --git a/core/testdata/format/extensionWithDocumentedReceiver.kt b/core/testdata/format/extensionWithDocumentedReceiver.kt
new file mode 100644
index 000000000..37fc09d9d
--- /dev/null
+++ b/core/testdata/format/extensionWithDocumentedReceiver.kt
@@ -0,0 +1,6 @@
+/**
+ * Function with receiver
+ * @receiver must be a non-empty string
+ */
+fun String.fn() {
+}
diff --git a/core/testdata/format/extensionWithDocumentedReceiver.md b/core/testdata/format/extensionWithDocumentedReceiver.md
new file mode 100644
index 000000000..0679ac8f7
--- /dev/null
+++ b/core/testdata/format/extensionWithDocumentedReceiver.md
@@ -0,0 +1,11 @@
+[test](../index.md) / [kotlin.String](index.md) / [fn](./fn.md)
+
+# fn
+
+`fun String.fn(): Unit`
+
+Function with receiver
+
+**Receiver**
+must be a non-empty string
+
diff --git a/core/testdata/format/extensions.class.md b/core/testdata/format/extensions.class.md
new file mode 100644
index 000000000..b8fa200a1
--- /dev/null
+++ b/core/testdata/format/extensions.class.md
@@ -0,0 +1,7 @@
+[test](../../index.md) / [foo](../index.md) / [kotlin.String](./index.md)
+
+### Extensions for kotlin.String
+
+| [fn](fn.md) | `fun String.fn(): Unit`<br>`fun String.fn(x: Int): Unit`<br>Function with receiver |
+| [foobar](foobar.md) | `val String.foobar: Int`<br>Property with receiver. |
+
diff --git a/core/testdata/format/extensions.kt b/core/testdata/format/extensions.kt
new file mode 100644
index 000000000..6f2eff9d2
--- /dev/null
+++ b/core/testdata/format/extensions.kt
@@ -0,0 +1,19 @@
+package foo
+
+/**
+ * Function with receiver
+ */
+fun String.fn() {
+}
+
+/**
+ * Function with receiver
+ */
+fun String.fn(x: Int) {
+}
+
+/**
+ * Property with receiver.
+ */
+val String.foobar: Int
+ get() = size() * 2
diff --git a/core/testdata/format/extensions.package.md b/core/testdata/format/extensions.package.md
new file mode 100644
index 000000000..ad895116b
--- /dev/null
+++ b/core/testdata/format/extensions.package.md
@@ -0,0 +1,8 @@
+[test](../index.md) / [foo](./index.md)
+
+## Package foo
+
+### Extensions for External Classes
+
+| [kotlin.String](kotlin.-string/index.md) | |
+
diff --git a/core/testdata/format/externalReferenceLink.kt b/core/testdata/format/externalReferenceLink.kt
new file mode 100644
index 000000000..4ca0ee216
--- /dev/null
+++ b/core/testdata/format/externalReferenceLink.kt
@@ -0,0 +1,10 @@
+/**
+ * It is link to [example site][example.com]
+ *
+ * Sure, it is [example.com]
+ *
+ * [example.com]: http://example.com
+ */
+fun a() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/externalReferenceLink.md b/core/testdata/format/externalReferenceLink.md
new file mode 100644
index 000000000..38ffde785
--- /dev/null
+++ b/core/testdata/format/externalReferenceLink.md
@@ -0,0 +1,10 @@
+[test](index.md) / [a](./a.md)
+
+# a
+
+`fun a(): Unit`
+
+It is link to [example site](http://example.com)
+
+Sure, it is [example.com](http://example.com)
+
diff --git a/core/testdata/format/functionWithDefaultParameter.kt b/core/testdata/format/functionWithDefaultParameter.kt
new file mode 100644
index 000000000..3a3a102fb
--- /dev/null
+++ b/core/testdata/format/functionWithDefaultParameter.kt
@@ -0,0 +1 @@
+fun f(x: String = "") {}
diff --git a/core/testdata/format/functionWithDefaultParameter.md b/core/testdata/format/functionWithDefaultParameter.md
new file mode 100644
index 000000000..05f7fbe63
--- /dev/null
+++ b/core/testdata/format/functionWithDefaultParameter.md
@@ -0,0 +1,5 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f(x: String = ""): Unit` \ No newline at end of file
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.html b/core/testdata/format/functionalTypeWithNamedParameters.html
new file mode 100644
index 000000000..82a9cc03d
--- /dev/null
+++ b/core/testdata/format/functionalTypeWithNamedParameters.html
@@ -0,0 +1,103 @@
+<!-- File: test/-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/>
+<br/>
+<h1>A</h1>
+<code><span class="keyword">class </span><span class="identifier">A</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">A</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-b/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>B - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">B</a><br/>
+<br/>
+<h1>B</h1>
+<code><span class="keyword">class </span><span class="identifier">B</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">B</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-c/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>C - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
+<br/>
+<h1>C</h1>
+<code><span class="keyword">class </span><span class="identifier">C</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/accept-function-type-with-named-arguments.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>acceptFunctionTypeWithNamedArguments - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./accept-function-type-with-named-arguments.html">acceptFunctionTypeWithNamedArguments</a><br/>
+<br/>
+<h1>acceptFunctionTypeWithNamedArguments</h1>
+<a name="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))"></a>
+<code><span class="keyword">fun </span><span class="identifier">acceptFunctionTypeWithNamedArguments</span><span class="symbol">(</span><span class="identifier" id="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))/f">f</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">bb</span><span class="symbol">:</span>&nbsp;<a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">aa</span><span class="symbol">:</span>&nbsp;<a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="-c/index.html"><span class="identifier">C</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f"></a>
+<code><span class="keyword">val </span><span class="identifier">f</span><span class="symbol">: </span><span class="symbol">(</span><span class="identifier">a</span><span class="symbol">:</span>&nbsp;<a href="-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>
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.kt b/core/testdata/format/functionalTypeWithNamedParameters.kt
new file mode 100644
index 000000000..3dada27aa
--- /dev/null
+++ b/core/testdata/format/functionalTypeWithNamedParameters.kt
@@ -0,0 +1,9 @@
+class A
+class B
+class C
+
+val f: (a: A, b: B) -> C = { a, b -> C() }
+
+fun acceptFunctionTypeWithNamedArguments(f: (bb: B, aa: A) -> C) {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.md b/core/testdata/format/functionalTypeWithNamedParameters.md
new file mode 100644
index 000000000..2255c7ae6
--- /dev/null
+++ b/core/testdata/format/functionalTypeWithNamedParameters.md
@@ -0,0 +1,45 @@
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
+
+# A
+
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
+
+# B
+
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/accept-function-type-with-named-arguments.md -->
+[test](index.md) / [acceptFunctionTypeWithNamedArguments](./accept-function-type-with-named-arguments.md)
+
+# acceptFunctionTypeWithNamedArguments
+
+`fun acceptFunctionTypeWithNamedArguments(f: (bb: `[`B`](-b/index.md)`, aa: `[`A`](-a/index.md)`) -> `[`C`](-c/index.md)`): Unit`
+<!-- File: test/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) \ No newline at end of file
diff --git a/core/testdata/format/genericInheritedExtensions.kt b/core/testdata/format/genericInheritedExtensions.kt
new file mode 100644
index 000000000..4c07e1e57
--- /dev/null
+++ b/core/testdata/format/genericInheritedExtensions.kt
@@ -0,0 +1,11 @@
+open class Foo<T>
+
+class Bar<T> : Foo<T>()
+
+fun <T> Foo<T>.first() {
+
+}
+
+fun <T> Bar<T>.second() {
+
+}
diff --git a/core/testdata/format/genericInheritedExtensions.md b/core/testdata/format/genericInheritedExtensions.md
new file mode 100644
index 000000000..8d0e316f8
--- /dev/null
+++ b/core/testdata/format/genericInheritedExtensions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar<T> : `[`Foo`](../-foo/index.md)`<`[`T`](index.md#T)`>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [first](../first.md) | `fun <T> `[`Foo`](../-foo/index.md)`<`[`T`](../first.md#T)`>.first(): Unit` |
+| [second](../second.md) | `fun <T> `[`Bar`](./index.md)`<`[`T`](../second.md#T)`>.second(): Unit` |
+
diff --git a/core/testdata/format/gfm/listInTableCell.kt b/core/testdata/format/gfm/listInTableCell.kt
new file mode 100644
index 000000000..2f4fdf54e
--- /dev/null
+++ b/core/testdata/format/gfm/listInTableCell.kt
@@ -0,0 +1,8 @@
+class Foo {
+ /**
+ * 1. Foo
+ * 1. Bar
+ */
+ fun foo() {
+ }
+}
diff --git a/core/testdata/format/gfm/listInTableCell.md b/core/testdata/format/gfm/listInTableCell.md
new file mode 100644
index 000000000..359ad9161
--- /dev/null
+++ b/core/testdata/format/gfm/listInTableCell.md
@@ -0,0 +1,17 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| Name | Summary |
+|---|---|
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Functions
+
+| Name | Summary |
+|---|---|
+| [foo](foo.md) | `fun foo(): Unit`<ol><li>Foo</li><li>Bar</li></ol> |
diff --git a/core/testdata/format/gfm/sample.kt b/core/testdata/format/gfm/sample.kt
new file mode 100644
index 000000000..3300d2c8c
--- /dev/null
+++ b/core/testdata/format/gfm/sample.kt
@@ -0,0 +1,18 @@
+/**
+ * The class Foo.
+ */
+class Foo {
+ /**
+ * The method bar.
+ */
+ fun bar() {
+
+ }
+
+ /**
+ * The method baz.
+ */
+ fun baz() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/gfm/sample.md b/core/testdata/format/gfm/sample.md
new file mode 100644
index 000000000..2b082296b
--- /dev/null
+++ b/core/testdata/format/gfm/sample.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+The class Foo.
+
+### Constructors
+
+| Name | Summary |
+|---|---|
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>The class Foo. |
+
+### Functions
+
+| Name | Summary |
+|---|---|
+| [bar](bar.md) | `fun bar(): Unit`<br>The method bar. |
+| [baz](baz.md) | `fun baz(): Unit`<br>The method baz. |
diff --git a/core/testdata/format/htmlEscaping.html b/core/testdata/format/htmlEscaping.html
new file mode 100644
index 000000000..bd64454d6
--- /dev/null
+++ b/core/testdata/format/htmlEscaping.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>x - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./x.html">x</a><br/>
+<br/>
+<h1>x</h1>
+<a name="$x()"></a>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">x</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="x.html#T"><span class="identifier">T</span></a><span class="symbol">?</span></code>
+<p>Special characters: &lt; is "less than", &gt; is "greater than", &amp; is "ampersand"</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/htmlEscaping.kt b/core/testdata/format/htmlEscaping.kt
new file mode 100644
index 000000000..8778d8a55
--- /dev/null
+++ b/core/testdata/format/htmlEscaping.kt
@@ -0,0 +1,4 @@
+/**
+ * Special characters: < is "less than", > is "greater than", & is "ampersand"
+ */
+fun x<T>(): T? = null
diff --git a/core/testdata/format/inapplicableExtensionFunctions.kt b/core/testdata/format/inapplicableExtensionFunctions.kt
new file mode 100644
index 000000000..d2c65b465
--- /dev/null
+++ b/core/testdata/format/inapplicableExtensionFunctions.kt
@@ -0,0 +1,11 @@
+open class Foo<T> {
+}
+
+class Bar : Foo<Char>() {
+}
+
+fun Foo<Int>.shazam() {
+}
+
+fun Bar.xyzzy() {
+}
diff --git a/core/testdata/format/inapplicableExtensionFunctions.md b/core/testdata/format/inapplicableExtensionFunctions.md
new file mode 100644
index 000000000..08fc2739f
--- /dev/null
+++ b/core/testdata/format/inapplicableExtensionFunctions.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)`<Char>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/indentedCodeBlock.html b/core/testdata/format/indentedCodeBlock.html
new file mode 100644
index 000000000..86c129fb4
--- /dev/null
+++ b/core/testdata/format/indentedCodeBlock.html
@@ -0,0 +1,17 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Create a new Foo value as follows:</p>
+<pre><code> val foo = Foo.create {
+ type { "ABC" }
+ }</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/indentedCodeBlock.kt b/core/testdata/format/indentedCodeBlock.kt
new file mode 100644
index 000000000..19c5365b6
--- /dev/null
+++ b/core/testdata/format/indentedCodeBlock.kt
@@ -0,0 +1,10 @@
+/**
+ * Create a new Foo value as follows:
+ *
+ * val foo = Foo.create {
+ * type { "ABC" }
+ * }
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/indentedCodeBlock.md b/core/testdata/format/indentedCodeBlock.md
new file mode 100644
index 000000000..77b0630a0
--- /dev/null
+++ b/core/testdata/format/indentedCodeBlock.md
@@ -0,0 +1,14 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): Unit`
+
+Create a new Foo value as follows:
+
+```
+ val foo = Foo.create {
+ type { "ABC" }
+ }
+```
+
diff --git a/core/testdata/format/inheritedCompanionObjectProperties.kt b/core/testdata/format/inheritedCompanionObjectProperties.kt
new file mode 100644
index 000000000..74a3749cb
--- /dev/null
+++ b/core/testdata/format/inheritedCompanionObjectProperties.kt
@@ -0,0 +1,18 @@
+open class A {
+ fun foo() {
+ }
+}
+
+open class B {
+ fun bar() {
+ }
+}
+
+class C : A {
+ fun xyzzy() {
+ }
+
+ companion object : B () {
+ fun shazam()
+ }
+}
diff --git a/core/testdata/format/inheritedCompanionObjectProperties.md b/core/testdata/format/inheritedCompanionObjectProperties.md
new file mode 100644
index 000000000..ab8f0aa5b
--- /dev/null
+++ b/core/testdata/format/inheritedCompanionObjectProperties.md
@@ -0,0 +1,30 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C : `[`A`](../-a/index.md)
+
+### Types
+
+| [Companion](-companion/index.md) | `companion object Companion : `[`B`](../-b/index.md) |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+### Functions
+
+| [xyzzy](xyzzy.md) | `fun xyzzy(): Unit` |
+
+### Inherited Functions
+
+| [foo](../-a/foo.md) | `fun foo(): Unit` |
+
+### Companion Object Functions
+
+| [shazam](shazam.md) | `fun shazam(): Unit` |
+
+### Inherited Companion Object Functions
+
+| [bar](../-b/bar.md) | `fun bar(): Unit` |
+
diff --git a/core/testdata/format/inheritedExtensions.kt b/core/testdata/format/inheritedExtensions.kt
new file mode 100644
index 000000000..e38fe00d3
--- /dev/null
+++ b/core/testdata/format/inheritedExtensions.kt
@@ -0,0 +1,11 @@
+open class Foo
+
+class Bar : Foo()
+
+fun Foo.first() {
+
+}
+
+fun Bar.second() {
+
+}
diff --git a/core/testdata/format/inheritedExtensions.md b/core/testdata/format/inheritedExtensions.md
new file mode 100644
index 000000000..97a73666b
--- /dev/null
+++ b/core/testdata/format/inheritedExtensions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [first](../first.md) | `fun `[`Foo`](../-foo/index.md)`.first(): Unit` |
+| [second](../second.md) | `fun `[`Bar`](./index.md)`.second(): Unit` |
+
diff --git a/core/testdata/format/inheritedLink.1.kt b/core/testdata/format/inheritedLink.1.kt
new file mode 100644
index 000000000..29cc12b48
--- /dev/null
+++ b/core/testdata/format/inheritedLink.1.kt
@@ -0,0 +1,10 @@
+package p1
+
+import java.util.LinkedList
+
+interface Foo {
+
+ /** Says hello - [LinkedList]. */
+ fun sayHello() : String
+
+} \ No newline at end of file
diff --git a/core/testdata/format/inheritedLink.kt b/core/testdata/format/inheritedLink.kt
new file mode 100644
index 000000000..86b8f4e2a
--- /dev/null
+++ b/core/testdata/format/inheritedLink.kt
@@ -0,0 +1,11 @@
+package p2
+
+import p1.Foo
+
+class FooBar : Foo {
+
+ override fun sayHello(): String {
+ return "Hello!"
+ }
+
+} \ No newline at end of file
diff --git a/core/testdata/format/inheritedLink.md b/core/testdata/format/inheritedLink.md
new file mode 100644
index 000000000..e5af326c2
--- /dev/null
+++ b/core/testdata/format/inheritedLink.md
@@ -0,0 +1,17 @@
+<!-- File: test/p2/-foo-bar/-init-.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [&lt;init&gt;](./-init-.md)
+
+# &lt;init&gt;
+
+`FooBar()`
+<!-- File: test/p2/-foo-bar/say-hello.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [sayHello](./say-hello.md)
+
+# sayHello
+
+`fun sayHello(): String`
+
+Overrides [Foo.sayHello](../../p1/-foo/say-hello.md)
+
+Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
+
diff --git a/core/testdata/format/inheritedMembers.kt b/core/testdata/format/inheritedMembers.kt
new file mode 100644
index 000000000..2d0c4ca12
--- /dev/null
+++ b/core/testdata/format/inheritedMembers.kt
@@ -0,0 +1,12 @@
+open class Foo {
+ fun first() {
+ }
+
+ val firstProp: Int = 0
+}
+
+class Bar : Foo() {
+ fun second()
+
+ val secondProp: Int = 1
+}
diff --git a/core/testdata/format/inheritedMembers.md b/core/testdata/format/inheritedMembers.md
new file mode 100644
index 000000000..334df360d
--- /dev/null
+++ b/core/testdata/format/inheritedMembers.md
@@ -0,0 +1,26 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Properties
+
+| [secondProp](second-prop.md) | `val secondProp: Int` |
+
+### Inherited Properties
+
+| [firstProp](../-foo/first-prop.md) | `val firstProp: Int` |
+
+### Functions
+
+| [second](second.md) | `fun second(): Unit` |
+
+### Inherited Functions
+
+| [first](../-foo/first.md) | `fun first(): Unit` |
+
diff --git a/core/testdata/format/java-layout-html/ConstJava.html b/core/testdata/format/java-layout-html/ConstJava.html
new file mode 100644
index 000000000..26c9aa2dc
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.html
@@ -0,0 +1,93 @@
+<!-- File: /test/p/ConstJava.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>ConstJava</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">ConstJava</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.ConstJava</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">static</span> <span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="#myStringConst%3Akotlin.String">myStringConst</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">static</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#myIntConst%3Akotlin.Int">myIntConst</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">static</span> <a href="#"><span class="identifier">ConstJava</span></a></td>
+ <td>
+ <div><code><a href="#myConstObjConst%3Ap.ConstJava">myConstObjConst</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="myStringConst:kotlin.String">
+ <h3>myStringConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myStringConst</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+ <pre>Value: <code>&quot;&quot;</code></pre>
+ </div>
+ <div id="myIntConst:kotlin.Int">
+ <h3>myIntConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myIntConst</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">ConstJava</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Properties</h2>
+ <div id="myConstObjConst:p.ConstJava">
+ <h3>myConstObjConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myConstObjConst</span><span class="symbol">: </span><a href="#"><span class="identifier">ConstJava</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/ConstJava.java b/core/testdata/format/java-layout-html/ConstJava.java
new file mode 100644
index 000000000..eb5bb2bf0
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.java
@@ -0,0 +1,10 @@
+package p;
+
+
+public class ConstJava {
+
+ public static final String myStringConst = "";
+ public static final int myIntConst = 0;
+
+ public static final ConstJava myConstObjConst = new ConstJava(); // Not a constant, as it have not primitive type
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/codeBlocks.html b/core/testdata/format/java-layout-html/codeBlocks.html
new file mode 100644
index 000000000..1d6bc6cb1
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.html
@@ -0,0 +1,49 @@
+<!-- File: /test/p/package-summary.html#foo%28%29 -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></td>
+ <td>
+ <div><code><a href="#foo%28%29">foo</a>()</code></div>
+ <p>See that <code>inline code</code> here</p>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#sample%28%29">sample</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="foo()">
+ <h3>foo</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></pre>
+ <p>See that <code>inline code</code> here</p>
+ <p>Some full code-block</p>
+ <pre><code data-language="Kotlin">
+ println(foo()) // Prints 42
+ println(foo() - 10) // Prints 32
+</code></pre>
+ <p>Some indented code-block
+ fun ref() = foo()
+ val a = 2</p>
+ <pre><code data-language="kotlin">
+
+println(foo()) // Answer unlimate question of all
+println(foo() * 2) // 84!</code></pre>
+ </div>
+ <div id="sample()">
+ <h3>sample</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">sample</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/codeBlocks.kt b/core/testdata/format/java-layout-html/codeBlocks.kt
new file mode 100644
index 000000000..de0c79441
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.kt
@@ -0,0 +1,26 @@
+package p
+
+/**
+ * See that `inline code` here
+ *
+ * Some full code-block
+ * ```Kotlin
+ * println(foo()) // Prints 42
+ * println(foo() - 10) // Prints 32
+ * ```
+ *
+ * Some indented code-block
+ * fun ref() = foo()
+ * val a = 2
+ *
+ * @sample p.sample
+ */
+fun foo(): Int {
+ return 42
+}
+
+
+fun sample() {
+ println(foo()) // Answer unlimate question of all
+ println(foo() * 2) // 84!
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.html b/core/testdata/format/java-layout-html/const.html
new file mode 100644
index 000000000..89cb572e7
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/G.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>G</h1>
+ <pre><span class="keyword">object </span><span class="identifier">G</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.G</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#y%3Akotlin.Int">y</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="y:kotlin.Int">
+ <h3>y</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">y</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ </body>
+</html>
+<!-- File: /test/p/D.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>D</h1>
+ <pre><span class="keyword">class </span><span class="identifier">D</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.D</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#Companion.z%3Akotlin.Int">z</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="Companion.z:kotlin.Int">
+ <h3>z</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">z</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">D</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/const.kt b/core/testdata/format/java-layout-html/const.kt
new file mode 100644
index 000000000..a6a0884f2
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.kt
@@ -0,0 +1,14 @@
+package p
+
+const val x = 0
+
+
+object G {
+ const val y = 0
+}
+
+class D {
+ companion object {
+ const val z = 0
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.package-summary.html b/core/testdata/format/java-layout-html/const.package-summary.html
new file mode 100644
index 000000000..50f06531b
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.package-summary.html
@@ -0,0 +1,35 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Classes</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="D.html#">D</a></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#x%3Akotlin.Int">x</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants</h2>
+ <div id="x:kotlin.Int">
+ <h3>x</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.kt b/core/testdata/format/java-layout-html/externalClassExtension.kt
new file mode 100644
index 000000000..04415cc23
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.kt
@@ -0,0 +1,5 @@
+package p
+
+fun String.ext() {
+ println(this)
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
new file mode 100644
index 000000000..39db6c8d3
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
@@ -0,0 +1,25 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>.<a href="#%28kotlin.String%29.ext%28%29">ext</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="(kotlin.String).ext()">
+ <h3>ext</h3>
+ <pre><span class="keyword">fun </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.html b/core/testdata/format/java-layout-html/genericExtension.html
new file mode 100644
index 000000000..0cbd034c4
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/Some.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>Some</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Some</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.Some</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="package-summary.html">p</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.extFun.T%29.extFun%28%29">extFun</a>()</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29">nullableExtFun</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="package-summary.html">p</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.extVal.T%29.extVal%3Akotlin.String">extVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.nullableExtVal.T%29.nullableExtVal%3Akotlin.String">nullableExtVal</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.kt b/core/testdata/format/java-layout-html/genericExtension.kt
new file mode 100644
index 000000000..0a9d74e64
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.kt
@@ -0,0 +1,10 @@
+package p
+
+class Some
+
+
+fun <T : Some> T.extFun() = ""
+val <T : Some> T.extVal get() = ""
+
+fun <T : Some?> T.nullableExtFun() = ""
+val <T : Some?> T.nullableExtVal get() = "" \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt
new file mode 100644
index 000000000..610ebb221
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt
@@ -0,0 +1,23 @@
+package foo
+
+
+fun foobar() {
+
+}
+
+
+val v = 22
+
+class G {
+
+ fun oo() = ""
+
+ val og = 11
+
+ companion object {
+
+ fun dg() = "22"
+
+ val dv = 12
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html
new file mode 100644
index 000000000..7025fc771
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html
@@ -0,0 +1,80 @@
+<!-- File: /test/bar/X.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>X</h1>
+ <pre><span class="keyword">class </span><span class="identifier">X</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">bar.X</a></td>
+ </tr>
+ </table>
+ <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="file:/foo/G.html#"><span class="identifier">G</span></a>.<a href="#%28foo.G%29.ext%28%29">ext</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">X</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+ </div>
+ <h2>Functions</h2>
+ <div id="(foo.G).ext()">
+ <h3>ext</h3>
+ <pre><span class="keyword">fun </span><a href="file:/foo/G.html#"><span class="identifier">G</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt
new file mode 100644
index 000000000..6420d7876
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt
@@ -0,0 +1,16 @@
+package bar
+
+/**
+ * See [foo.foobar]
+ * See [foo.v]
+ * See [foo.G]
+ * See [foo.G.oo]
+ * See [foo.G.og]
+ * See [foo.G.Companion.dg]
+ * See [foo.G.Companion.dv]
+ */
+class X {
+
+ fun (foo.G).ext() = this.oo()
+}
+
diff --git a/core/testdata/format/java-layout-html/inboundLinksTestPackageList b/core/testdata/format/java-layout-html/inboundLinksTestPackageList
new file mode 100644
index 000000000..64d25c312
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksTestPackageList
@@ -0,0 +1,3 @@
+$dokka.format:java-layout-html
+$dokka.mode:kotlin
+foo
diff --git a/core/testdata/format/java-layout-html/sections.html b/core/testdata/format/java-layout-html/sections.html
new file mode 100644
index 000000000..c4c0469e4
--- /dev/null
+++ b/core/testdata/format/java-layout-html/sections.html
@@ -0,0 +1,64 @@
+<!-- File: /test/foo/package-summary.html#sectionsTest%28%29 -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>foo</h1>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#sectionsTest%28%29">sectionsTest</a>()</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">Unit</span></td>
+ <td>
+ <div><code><a href="#seeMeAlso%28%29">seeMeAlso</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="sectionsTest()">
+ <h3>sectionsTest</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">sectionsTest</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ <table>
+ <thead>
+ <tr>
+ <th colspan="2">Exceptions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>ArrayOutOfBoundsException</td>
+ <td>sometimes</td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <th colspan="2">Return</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>It's simple void</td>
+ </tr>
+ </tbody>
+ </table>
+ <p><b>See Also</b></p>
+ <ul>
+ <li><code><a href="#seeMeAlso%28%29">foo.seeMeAlso</a></code></li>
+ </ul>
+ </div>
+ <div id="seeMeAlso()">
+ <h3>seeMeAlso</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">seeMeAlso</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/sections.kt b/core/testdata/format/java-layout-html/sections.kt
new file mode 100644
index 000000000..51358d2f0
--- /dev/null
+++ b/core/testdata/format/java-layout-html/sections.kt
@@ -0,0 +1,15 @@
+package foo
+
+/**
+ * @throws ArrayOutOfBoundsException sometimes
+ * @return It's simple void
+ * @see foo.seeMeAlso
+ */
+fun sectionsTest(): Unit {
+ println("!")
+}
+
+
+fun seeMeAlso() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/simple.html b/core/testdata/format/java-layout-html/simple.html
new file mode 100644
index 000000000..0b82c493a
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.html
@@ -0,0 +1,177 @@
+<!-- File: /test/p/Foo.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>Foo</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">kotlin.Any</a></td>
+ </tr>
+ <tr>
+ <td>   ↳</td>
+ <td><a href="#">p.Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#s%28%29">s</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html">kotlin</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">T</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html">also</a>(<span class="identifier" id="kotlin$also(kotlin.also.T, kotlin.Function1((kotlin.also.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html">apply</a>(<span class="identifier" id="kotlin$apply(kotlin.apply.T, kotlin.Function1((kotlin.apply.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">R</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html">let</a>(<span class="identifier" id="kotlin$let(kotlin.let.T, kotlin.Function1((kotlin.let.T, kotlin.let.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">R</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html">run</a>(<span class="identifier" id="kotlin$run(kotlin.run.T, kotlin.Function1((kotlin.run.T, kotlin.run.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span><span class="symbol">?</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html">takeIf</a>(<span class="identifier" id="kotlin$takeIf(kotlin.takeIf.T, kotlin.Function1((kotlin.takeIf.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span><span class="symbol">?</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html">takeUnless</a>(<span class="identifier" id="kotlin$takeUnless(kotlin.takeUnless.T, kotlin.Function1((kotlin.takeUnless.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html"><span class="identifier">Pair</span></a><span class="symbol">&lt;</span><span class="identifier">A</span><span class="symbol">,</span>&nbsp;<span class="identifier">B</span><span class="symbol">&gt;</span></td>
+ <td>
+ <div><code><span class="identifier">A</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html">to</a>(<span class="identifier" id="kotlin$to(kotlin.to.A, kotlin.to.B)/that">that</span><span class="symbol">:</span>&nbsp;<span class="identifier">B</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html"><span class="identifier">Any</span></a><span class="symbol">?</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to-string.html">toString</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#g%3Akotlin.String">g</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html">kotlin.jvm</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html"><span class="identifier">Class</span></a><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html">javaClass</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Functions</h2>
+ <div id="s()">
+ <h3>s</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">s</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <h2>Properties</h2>
+ <div id="g:kotlin.String">
+ <h3>g</h3>
+ <pre><span class="keyword">val </span><span class="identifier">g</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/simple.kt b/core/testdata/format/java-layout-html/simple.kt
new file mode 100644
index 000000000..89789c452
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.kt
@@ -0,0 +1,8 @@
+package p
+
+
+class Foo {
+ fun s(): Unit {}
+
+ val g = ""
+} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.kt b/core/testdata/format/java-layout-html/topLevel.kt
new file mode 100644
index 000000000..85b1a4372
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.kt
@@ -0,0 +1,15 @@
+package p
+
+class Some
+
+fun topLevelFun() {}
+
+val topLevelVal = ""
+
+const val topLevelConst = ""
+
+val topLevelGetVal get() = ""
+
+val Some.topLevelExtVal get() = ""
+
+fun Some.topLevelExtFun() {} \ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.package-summary.html b/core/testdata/format/java-layout-html/topLevel.package-summary.html
new file mode 100644
index 000000000..03665c6cf
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.package-summary.html
@@ -0,0 +1,97 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Classes</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="Some.html#">Some</a></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelConst%3Akotlin.String">topLevelConst</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#topLevelFun%28%29">topLevelFun</a>()</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="Some.html#"><span class="identifier">Some</span></a>.<a href="#%28p.Some%29.topLevelExtFun%28%29">topLevelExtFun</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level properties summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelVal%3Akotlin.String">topLevelVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelGetVal%3Akotlin.String">topLevelGetVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#%28p.Some%29.topLevelExtVal%3Akotlin.String">topLevelExtVal</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants</h2>
+ <div id="topLevelConst:kotlin.String">
+ <h3>topLevelConst</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">topLevelConst</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ <pre>Value: <code>&quot;&quot;</code></pre>
+ </div>
+ <h2>Top-level functions</h2>
+ <div id="topLevelFun()">
+ <h3>topLevelFun</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">topLevelFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <div id="(p.Some).topLevelExtFun()">
+ <h3>topLevelExtFun</h3>
+ <pre><span class="keyword">fun </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <h2>Top-level properties</h2>
+ <div id="topLevelVal:kotlin.String">
+ <h3>topLevelVal</h3>
+ <pre><span class="keyword">val </span><span class="identifier">topLevelVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ <div id="topLevelGetVal:kotlin.String">
+ <h3>topLevelGetVal</h3>
+ <pre><span class="keyword">val </span><span class="identifier">topLevelGetVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ <div id="(p.Some).topLevelExtVal:kotlin.String">
+ <h3>topLevelExtVal</h3>
+ <pre><span class="keyword">val </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.html b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
new file mode 100644
index 000000000..eca242c9c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
@@ -0,0 +1,63 @@
+<!-- File: /test/p/MyException.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>MyException</h1>
+ <pre><span class="keyword">class </span><span class="identifier">MyException</span>&nbsp;<span class="symbol">:</span>&nbsp;<span class="identifier">Exception</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.MyException</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="#">p.java.lang.Exception</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">Unit</span></td>
+ <td>
+ <div><code><span class="identifier">Exception</span>.<a href="package-summary.html#%28kotlin.Exception%29.ext%28%29">ext</a>()</code></div>
+ <p>Some docs...</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="&lt;init&gt;()">
+ <h3>&lt;init&gt;</h3>
+ <pre><span class="identifier">MyException</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.kt b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
new file mode 100644
index 000000000..0831dacb0
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
@@ -0,0 +1,13 @@
+package p
+
+// noStdlibLink set to true for that test
+
+/**
+ * Some docs...
+ */
+fun Exception.ext() {
+
+}
+
+
+class MyException: Exception() \ No newline at end of file
diff --git a/core/testdata/format/javaCodeInParam.java b/core/testdata/format/javaCodeInParam.java
new file mode 100644
index 000000000..73025fcc9
--- /dev/null
+++ b/core/testdata/format/javaCodeInParam.java
@@ -0,0 +1,5 @@
+/**
+ * @param T this is {@code some code} and other text
+ */
+class C<T> {
+}
diff --git a/core/testdata/format/javaCodeInParam.md b/core/testdata/format/javaCodeInParam.md
new file mode 100644
index 000000000..319c6d87e
--- /dev/null
+++ b/core/testdata/format/javaCodeInParam.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C<T : Any>`
+
+### Parameters
+
+`T` - this is `some code` and other text
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
diff --git a/core/testdata/format/javaCodeLiteralTags.java b/core/testdata/format/javaCodeLiteralTags.java
new file mode 100644
index 000000000..e71ddaa70
--- /dev/null
+++ b/core/testdata/format/javaCodeLiteralTags.java
@@ -0,0 +1,6 @@
+/**
+ * <p>{@code A<B>C}</p>
+ * <p>{@literal A<B>C}</p>
+ */
+class C {
+}
diff --git a/core/testdata/format/javaCodeLiteralTags.md b/core/testdata/format/javaCodeLiteralTags.md
new file mode 100644
index 000000000..b36be04d8
--- /dev/null
+++ b/core/testdata/format/javaCodeLiteralTags.md
@@ -0,0 +1,16 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+`A<B>C`
+
+
+
+A&lt;B&gt;C
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
+
diff --git a/core/testdata/format/javaDeprecated.html b/core/testdata/format/javaDeprecated.html
new file mode 100644
index 000000000..6d45fc9ff
--- /dev/null
+++ b/core/testdata/format/javaDeprecated.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<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/>
+<br/>
+<h1>foo</h1>
+<a name="Foo$foo()"></a>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><s><span class="identifier">foo</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<strong>Deprecated:</strong> use <code><a href="bar.html">bar</a></code> instead
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaDeprecated.java b/core/testdata/format/javaDeprecated.java
new file mode 100644
index 000000000..9694a4441
--- /dev/null
+++ b/core/testdata/format/javaDeprecated.java
@@ -0,0 +1,5 @@
+class Foo {
+ /** @deprecated use {@link #bar} instead */
+ public void foo() {}
+ public void bar() {}
+}
diff --git a/core/testdata/format/javaLinkTag.html b/core/testdata/format/javaLinkTag.html
new file mode 100644
index 000000000..9a0baaa31
--- /dev/null
+++ b/core/testdata/format/javaLinkTag.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaLinkTag.java b/core/testdata/format/javaLinkTag.java
new file mode 100644
index 000000000..84f761c6f
--- /dev/null
+++ b/core/testdata/format/javaLinkTag.java
@@ -0,0 +1,6 @@
+/**
+ * Call {@link #bar()} to do the job.
+ */
+class Foo {
+ public void bar()
+}
diff --git a/core/testdata/format/javaLinkTagWithLabel.html b/core/testdata/format/javaLinkTagWithLabel.html
new file mode 100644
index 000000000..51917f7ab
--- /dev/null
+++ b/core/testdata/format/javaLinkTagWithLabel.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaLinkTagWithLabel.java b/core/testdata/format/javaLinkTagWithLabel.java
new file mode 100644
index 000000000..1db5ad703
--- /dev/null
+++ b/core/testdata/format/javaLinkTagWithLabel.java
@@ -0,0 +1,6 @@
+/**
+ * Call {@link #bar() this wonderful method} to do the job.
+ */
+class Foo {
+ public void bar()
+}
diff --git a/core/testdata/format/javaSample.java b/core/testdata/format/javaSample.java
new file mode 100644
index 000000000..b9fb32a74
--- /dev/null
+++ b/core/testdata/format/javaSample.java
@@ -0,0 +1,5 @@
+/**
+ * {@sample memberWithModifiers.java }
+ */
+class C {
+}
diff --git a/core/testdata/format/javaSample.md b/core/testdata/format/javaSample.md
new file mode 100644
index 000000000..fa1757770
--- /dev/null
+++ b/core/testdata/format/javaSample.md
@@ -0,0 +1,41 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+```
+public abstract class Test {
+ /**
+ * Summary for Function
+ * @param name is String parameter
+ * @param value is int parameter
+ */
+ protected final void fn(String name, int value) {
+
+ }
+
+ protected void openFn() {}
+}
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>
+
+```
+public abstract class Test {
+ /**
+ * Summary for Function
+ * @param name is String parameter
+ * @param value is int parameter
+ */
+ protected final void fn(String name, int value) {
+
+ }
+
+ protected void openFn() {}
+}
+<br>```
+<br> |
+
diff --git a/core/testdata/format/javaSeeTag.html b/core/testdata/format/javaSeeTag.html
new file mode 100644
index 000000000..f8866dc2c
--- /dev/null
+++ b/core/testdata/format/javaSeeTag.html
@@ -0,0 +1,38 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p><strong>See Also</strong><br/>
+<a href="bar.html">#bar</a></p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaSeeTag.java b/core/testdata/format/javaSeeTag.java
new file mode 100644
index 000000000..94a24606d
--- /dev/null
+++ b/core/testdata/format/javaSeeTag.java
@@ -0,0 +1,6 @@
+/**
+ * @see #bar
+ */
+public class Foo {
+ public void bar() {}
+} \ No newline at end of file
diff --git a/core/testdata/format/javaSpaceInAuthor.java b/core/testdata/format/javaSpaceInAuthor.java
new file mode 100644
index 000000000..f980ae07c
--- /dev/null
+++ b/core/testdata/format/javaSpaceInAuthor.java
@@ -0,0 +1,5 @@
+/**
+ * @author Dmitry Jemerov
+ */
+class C {
+} \ No newline at end of file
diff --git a/core/testdata/format/javaSpaceInAuthor.md b/core/testdata/format/javaSpaceInAuthor.md
new file mode 100644
index 000000000..1d2251d0f
--- /dev/null
+++ b/core/testdata/format/javaSpaceInAuthor.md
@@ -0,0 +1,13 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+**Author**
+Dmitry Jemerov
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
diff --git a/core/testdata/format/javadocCodeMultiline.java b/core/testdata/format/javadocCodeMultiline.java
new file mode 100644
index 000000000..a8b8de699
--- /dev/null
+++ b/core/testdata/format/javadocCodeMultiline.java
@@ -0,0 +1,10 @@
+/**
+ * <pre>
+ * &lt;Button
+ * android:id="@+id/button_id"
+ * android:layout_height="wrap_content"
+ * android:layout_width="wrap_content"
+ * android:text="@string/self_destruct" /&gt;</pre>
+ */
+public class C {
+}
diff --git a/core/testdata/format/javadocCodeMultiline.md b/core/testdata/format/javadocCodeMultiline.md
new file mode 100644
index 000000000..35dd1ac91
--- /dev/null
+++ b/core/testdata/format/javadocCodeMultiline.md
@@ -0,0 +1,26 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`open class C`
+
+```
+<Button
+ android:id="@+id/button_id"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/self_destruct" />
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>
+
+```
+<Button
+ android:id="@+id/button_id"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/self_destruct" /><br>```
+<br> |
+
diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java
new file mode 100644
index 000000000..622116b28
--- /dev/null
+++ b/core/testdata/format/javadocHtml.java
@@ -0,0 +1,14 @@
+/**
+ * <b>Bold</b>
+ * <strong>Strong</strong>
+ * <i>Italic</i>
+ * <em>Emphasized</em>
+ * <p>Paragraph</p>
+ * <s>Strikethrough</s>
+ * <del>Deleted</del>
+ * <code>Code</code>
+ * <pre>Block code</pre>
+ * <ul><li>List Item</li></ul>
+ */
+public class C {
+}
diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md
new file mode 100644
index 000000000..a3c1baff7
--- /dev/null
+++ b/core/testdata/format/javadocHtml.md
@@ -0,0 +1,28 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`open class C`
+
+**Bold** **Strong** *Italic* *Emphasized*
+
+Paragraph
+
+ ~~Strikethrough~~ ~~Deleted~~ `Code`
+
+```
+Block code
+```
+
+ * List Item
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
+
+```
+Block code<br>```
+<br>
+* List Item
+<br> |
+
diff --git a/core/testdata/format/javadocOrderedList.java b/core/testdata/format/javadocOrderedList.java
new file mode 100644
index 000000000..c32d9032a
--- /dev/null
+++ b/core/testdata/format/javadocOrderedList.java
@@ -0,0 +1,8 @@
+/**
+ * <ol>
+ * <li>Rinse</li>
+ * <li>Repeat</li>
+ * </ol>
+ */
+public class Bar {
+}
diff --git a/core/testdata/format/javadocOrderedList.md b/core/testdata/format/javadocOrderedList.md
new file mode 100644
index 000000000..88d5f5b66
--- /dev/null
+++ b/core/testdata/format/javadocOrderedList.md
@@ -0,0 +1,17 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`open class Bar`
+
+ 1. Rinse
+ 2. Repeat
+
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>
+1. Rinse
+ 2. Repeat
+ <br> |
+
diff --git a/core/testdata/format/jdkLinks.kt b/core/testdata/format/jdkLinks.kt
new file mode 100644
index 000000000..660fb4ce4
--- /dev/null
+++ b/core/testdata/format/jdkLinks.kt
@@ -0,0 +1,7 @@
+/**
+ * This is a [ClassLoader] and I can get its [ClassLoader.getResource]
+ *
+ * You can print something to [java.lang.System.out] now!
+ */
+class C : ClassLoader {
+}
diff --git a/core/testdata/format/jdkLinks.md b/core/testdata/format/jdkLinks.md
new file mode 100644
index 000000000..7498171d7
--- /dev/null
+++ b/core/testdata/format/jdkLinks.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C : `[`ClassLoader`](http://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))
+
+You can print something to [java.lang.System.out](http://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)) |
+
diff --git a/core/testdata/format/linkWithLabel.html b/core/testdata/format/linkWithLabel.html
new file mode 100644
index 000000000..59bc6ddf6
--- /dev/null
+++ b/core/testdata/format/linkWithLabel.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/linkWithLabel.kt b/core/testdata/format/linkWithLabel.kt
new file mode 100644
index 000000000..4a85c505e
--- /dev/null
+++ b/core/testdata/format/linkWithLabel.kt
@@ -0,0 +1,6 @@
+/**
+ * Use [this method][Bar.foo] for best results.
+ */
+class Bar {
+ fun foo() {}
+}
diff --git a/core/testdata/format/linkWithStarProjection.html b/core/testdata/format/linkWithStarProjection.html
new file mode 100644
index 000000000..e1b6e098a
--- /dev/null
+++ b/core/testdata/format/linkWithStarProjection.html
@@ -0,0 +1,24 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>KClassLoader - test</title>
+</HEAD>
+<BODY>
+<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>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="KClassLoader$foo(kotlin.Enum(()))/c">c</span><span class="symbol">:</span>&nbsp;<span class="identifier">Enum</span><span class="symbol">&lt;</span><span class="identifier">*</span><span class="symbol">&gt;</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/linkWithStarProjection.kt b/core/testdata/format/linkWithStarProjection.kt
new file mode 100644
index 000000000..6da6c5958
--- /dev/null
+++ b/core/testdata/format/linkWithStarProjection.kt
@@ -0,0 +1,3 @@
+object KClassLoader {
+ fun foo(c: Enum<*>) { }
+}
diff --git a/core/testdata/format/linksInEmphasis.kt b/core/testdata/format/linksInEmphasis.kt
new file mode 100644
index 000000000..3e2017d25
--- /dev/null
+++ b/core/testdata/format/linksInEmphasis.kt
@@ -0,0 +1,13 @@
+/**
+ * An emphasised class.
+ *
+ * _This class [Bar] is awesome._
+ *
+ * _Even more awesomer is the function [Bar.foo]_
+ *
+ * _[Bar.hello] is also OK_
+ */
+class Bar {
+ fun foo() {}
+ fun hello() {}
+}
diff --git a/core/testdata/format/linksInEmphasis.md b/core/testdata/format/linksInEmphasis.md
new file mode 100644
index 000000000..d0ae70c87
--- /dev/null
+++ b/core/testdata/format/linksInEmphasis.md
@@ -0,0 +1,23 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+An emphasised class.
+
+*This class [Bar](./index.md) is awesome.*
+
+*Even more awesomer is the function [Bar.foo](foo.md)*
+
+*[Bar.hello](hello.md) is also OK*
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>An emphasised class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+
diff --git a/core/testdata/format/linksInHeaders.kt b/core/testdata/format/linksInHeaders.kt
new file mode 100644
index 000000000..18efd34b2
--- /dev/null
+++ b/core/testdata/format/linksInHeaders.kt
@@ -0,0 +1,24 @@
+/**
+ * Some class with really useless documentation.
+ *
+ * # Beer o'clock - time to go to the [Bar]
+ *
+ * ## But __is [it](isitbeeroclock.com)__ really?
+ *
+ * ### [Bar.hello] to the [Bar.world]!
+ *
+ * #### _Kotlin is amazing, [Bar.none]_
+ *
+ * ##### We need to go [Bar.deeper]
+ *
+ * ###### End of the [Bar.line] - we need to go back!
+ */
+class Bar {
+ fun foo() {}
+ fun hello() {}
+ fun world() {}
+ fun kotlin() {}
+ fun none() {}
+ fun deeper() {}
+ fun line() {}
+}
diff --git a/core/testdata/format/linksInHeaders.md b/core/testdata/format/linksInHeaders.md
new file mode 100644
index 000000000..1dc7d18b4
--- /dev/null
+++ b/core/testdata/format/linksInHeaders.md
@@ -0,0 +1,34 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Some class with really useless documentation.
+
+# Beer o'clock - time to go to the [Bar](./index.md)
+
+## But **is [it](isitbeeroclock.com)** really?
+
+### [Bar.hello](hello.md) to the [Bar.world](world.md)!
+
+#### *Kotlin is amazing, [Bar.none](none.md)*
+
+##### We need to go [Bar.deeper](deeper.md)
+
+###### End of the [Bar.line](line.md) - we need to go back!
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Some class with really useless documentation. |
+
+### Functions
+
+| [deeper](deeper.md) | `fun deeper(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+| [kotlin](kotlin.md) | `fun kotlin(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
+| [none](none.md) | `fun none(): Unit` |
+| [world](world.md) | `fun world(): Unit` |
+
diff --git a/core/testdata/format/linksInStrong.kt b/core/testdata/format/linksInStrong.kt
new file mode 100644
index 000000000..b9e295ecb
--- /dev/null
+++ b/core/testdata/format/linksInStrong.kt
@@ -0,0 +1,13 @@
+/**
+ * A strong class.
+ *
+ * __This class [Bar] is awesome.__
+ *
+ * __Even more awesomer is the function [Bar.foo]__
+ *
+ * __[Bar.hello] is also OK__
+ */
+class Bar {
+ fun foo() {}
+ fun hello() {}
+}
diff --git a/core/testdata/format/linksInStrong.md b/core/testdata/format/linksInStrong.md
new file mode 100644
index 000000000..5b44112d6
--- /dev/null
+++ b/core/testdata/format/linksInStrong.md
@@ -0,0 +1,23 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+A strong class.
+
+**This class [Bar](./index.md) is awesome.**
+
+**Even more awesomer is the function [Bar.foo](foo.md)**
+
+**[Bar.hello](hello.md) is also OK**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>A strong class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+
diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html
new file mode 100644
index 000000000..596cca73e
--- /dev/null
+++ b/core/testdata/format/markdownInLinks.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p><a href="http://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
new file mode 100644
index 000000000..67b6311f0
--- /dev/null
+++ b/core/testdata/format/markdownInLinks.kt
@@ -0,0 +1,4 @@
+/**
+ * [a**b**__d__ kas ](http://www.ibm.com)
+ */
+fun foo() {}
diff --git a/core/testdata/format/memberExtension.kt b/core/testdata/format/memberExtension.kt
new file mode 100644
index 000000000..955794d1e
--- /dev/null
+++ b/core/testdata/format/memberExtension.kt
@@ -0,0 +1,8 @@
+open class X
+
+class Foo : X
+
+class Bar {
+ fun X.y() = ""
+ fun Foo.x() = ""
+}
diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md
new file mode 100644
index 000000000..0ec1fda3e
--- /dev/null
+++ b/core/testdata/format/memberExtension.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo : `[`X`](../-x/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt
new file mode 100644
index 000000000..d7fbf924a
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt
@@ -0,0 +1,7 @@
+package pack
+
+class Some {
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt
new file mode 100644
index 000000000..57f36742e
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
new file mode 100644
index 000000000..37e943ad0
--- /dev/null
+++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md
@@ -0,0 +1,8 @@
+[test](../../../index.md) / [pack](../../index.md) / [Some](../index.md) / [magic](./magic.md)
+
+# magic
+
+`fun magic(): Unit`
+
+**Platform and version requirements:** JS
+
diff --git a/core/testdata/format/multiplatform/groupNode/js.kt b/core/testdata/format/multiplatform/groupNode/js.kt
new file mode 100644
index 000000000..045f3f0d6
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/groupNode/jvm.kt b/core/testdata/format/multiplatform/groupNode/jvm.kt
new file mode 100644
index 000000000..57f36742e
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.md b/core/testdata/format/multiplatform/groupNode/multiplatform.md
new file mode 100644
index 000000000..74d464c9b
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md
@@ -0,0 +1,20 @@
+[test](../../index.md) / [pack](../index.md) / [Some](./index.md)
+
+# Some
+
+`typealias Some = SomeCoolJvmClass`
+
+**Platform and version requirements:** JVM
+
+`class Some`
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+| [&lt;init&gt;](-some/-init-.md) | `Some()` |
+
+### Functions
+
+| [magic](-some/magic.md) | `fun magic(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
new file mode 100644
index 000000000..5708795e6
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
@@ -0,0 +1,13 @@
+[test](../index.md) / [pack](./index.md)
+
+## Package pack
+
+### Types
+
+| [Some](-some/index.md)<br>(JS) | `class Some` |
+| [SomeCoolJvmClass](-some-cool-jvm-class/index.md)<br>(JVM) | `class SomeCoolJvmClass` |
+
+### Type Aliases
+
+| [Some](-some/index.md)<br>(JVM) | `typealias Some = SomeCoolJvmClass` |
+
diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md
new file mode 100644
index 000000000..fca2aff4f
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/foo.md
@@ -0,0 +1,24 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+This is a foo.
+
+### Constructors
+
+| [&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` |
+
+### 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` |
+
diff --git a/core/testdata/format/multiplatform/implied/js.kt b/core/testdata/format/multiplatform/implied/js.kt
new file mode 100644
index 000000000..dd2de5bc1
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/js.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun js() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJs = "abc"
+}
diff --git a/core/testdata/format/multiplatform/implied/jvm.kt b/core/testdata/format/multiplatform/implied/jvm.kt
new file mode 100644
index 000000000..8d73ce25f
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/jvm.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun jvm() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/merge/js.kt b/core/testdata/format/multiplatform/merge/js.kt
new file mode 100644
index 000000000..bbf1dd7ce
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/js.kt
@@ -0,0 +1,7 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+}
diff --git a/core/testdata/format/multiplatform/merge/jvm.kt b/core/testdata/format/multiplatform/merge/jvm.kt
new file mode 100644
index 000000000..cb77273f2
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/jvm.kt
@@ -0,0 +1,8 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+
+}
diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md
new file mode 100644
index 000000000..ea78b5a36
--- /dev/null
+++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [foo](./index.md)
+
+## Package foo
+
+**Platform and version requirements:** JVM, JS
+
+### Types
+
+| [Foo](-foo/index.md)<br>(JVM, JS) | `class Foo`<br>This is a foo. |
+
diff --git a/core/testdata/format/multiplatform/mergeMembers/foo.md b/core/testdata/format/multiplatform/mergeMembers/foo.md
new file mode 100644
index 000000000..7f41b7d13
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/foo.md
@@ -0,0 +1,26 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM, JS
+
+This is a foo.
+
+### Constructors
+
+| [&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` |
+
+### 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` |
+
diff --git a/core/testdata/format/multiplatform/mergeMembers/js.kt b/core/testdata/format/multiplatform/mergeMembers/js.kt
new file mode 100644
index 000000000..dd2de5bc1
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/js.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun js() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJs = "abc"
+}
diff --git a/core/testdata/format/multiplatform/mergeMembers/jvm.kt b/core/testdata/format/multiplatform/mergeMembers/jvm.kt
new file mode 100644
index 000000000..8d73ce25f
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/jvm.kt
@@ -0,0 +1,16 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun bothJvmAndJs() {
+ }
+
+ fun jvm() {
+ }
+
+ val propJvmAndJs = 0
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md
new file mode 100644
index 000000000..a20b14cf8
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/foo.md
@@ -0,0 +1,22 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJvm](prop-jvm.md) | `val propJvm: String` |
+
+### Functions
+
+| [jvm](jvm.md) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/omitRedundant/js.kt b/core/testdata/format/multiplatform/omitRedundant/js.kt
new file mode 100644
index 000000000..d1b1429cb
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/js.kt
@@ -0,0 +1,2 @@
+package foo
+
diff --git a/core/testdata/format/multiplatform/omitRedundant/jvm.kt b/core/testdata/format/multiplatform/omitRedundant/jvm.kt
new file mode 100644
index 000000000..35e3c08d5
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+ fun jvm() {
+ }
+
+ val propJvm = "abc"
+}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt
new file mode 100644
index 000000000..86d092897
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt
@@ -0,0 +1,3 @@
+package foo.bar
+
+fun buz() {}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt
new file mode 100644
index 000000000..86d092897
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt
@@ -0,0 +1,3 @@
+package foo.bar
+
+fun buz() {}
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
new file mode 100644
index 000000000..6f45342b0
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md
@@ -0,0 +1,10 @@
+[test](./index.md)
+
+**Platform and version requirements:** JVM, JS
+
+### Packages
+
+| [foo.bar](foo.bar/index.md)<br>(JVM, JS) | |
+
+### Index
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
new file mode 100644
index 000000000..4ddfe2e3d
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [foo.bar](./index.md)
+
+## Package foo.bar
+
+**Platform and version requirements:** JVM, JS
+
+### Functions
+
+| [buz](buz.md)<br>(JVM, JS) | `fun buz(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt
new file mode 100644
index 000000000..27ab1b32e
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt
@@ -0,0 +1,5 @@
+package some
+
+fun String.buz(): Unit {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
new file mode 100644
index 000000000..f4186b6ec
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md
@@ -0,0 +1,10 @@
+[test](./index.md)
+
+**Platform and version requirements:** JVM
+
+### Packages
+
+| [some](some/index.md)<br>(JVM) | |
+
+### Index
+
diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
new file mode 100644
index 000000000..ff480b5a9
--- /dev/null
+++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [some](./index.md)
+
+## Package some
+
+**Platform and version requirements:** JVM
+
+### Extensions for External Classes
+
+| [kotlin.String](kotlin.-string/index.md) | |
+
diff --git a/core/testdata/format/multiplatform/simple/js.kt b/core/testdata/format/multiplatform/simple/js.kt
new file mode 100644
index 000000000..e6d66ffdc
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/js.kt
@@ -0,0 +1,7 @@
+package foo
+
+/**
+ * This is a bar.
+ */
+class Bar {
+}
diff --git a/core/testdata/format/multiplatform/simple/jvm.kt b/core/testdata/format/multiplatform/simple/jvm.kt
new file mode 100644
index 000000000..cb77273f2
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/jvm.kt
@@ -0,0 +1,8 @@
+package foo
+
+/**
+ * This is a foo.
+ */
+class Foo {
+
+}
diff --git a/core/testdata/format/multiplatform/simple/multiplatform.package.md b/core/testdata/format/multiplatform/simple/multiplatform.package.md
new file mode 100644
index 000000000..fad7e90d8
--- /dev/null
+++ b/core/testdata/format/multiplatform/simple/multiplatform.package.md
@@ -0,0 +1,9 @@
+[test](../index.md) / [foo](./index.md)
+
+## Package foo
+
+### 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. |
+
diff --git a/core/testdata/format/multipleTypeParameterConstraints.kt b/core/testdata/format/multipleTypeParameterConstraints.kt
new file mode 100644
index 000000000..9d085c3cf
--- /dev/null
+++ b/core/testdata/format/multipleTypeParameterConstraints.kt
@@ -0,0 +1,11 @@
+interface A {
+
+}
+
+interface B {
+
+}
+
+
+fun f<T> where T : A, T : B {
+}
diff --git a/core/testdata/format/multipleTypeParameterConstraints.md b/core/testdata/format/multipleTypeParameterConstraints.md
new file mode 100644
index 000000000..78586acae
--- /dev/null
+++ b/core/testdata/format/multipleTypeParameterConstraints.md
@@ -0,0 +1,18 @@
+<!-- File: test/-a.md -->
+[test](index.md) / [A](./-a.md)
+
+# A
+
+`interface A`
+<!-- File: test/-b.md -->
+[test](index.md) / [B](./-b.md)
+
+# B
+
+`interface B`
+<!-- File: test/f.md -->
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun <T> f(): Unit where T : `[`A`](-a.md)`, T : `[`B`](-b.md) \ No newline at end of file
diff --git a/core/testdata/format/nestedLists.kt b/core/testdata/format/nestedLists.kt
new file mode 100644
index 000000000..83217f8a5
--- /dev/null
+++ b/core/testdata/format/nestedLists.kt
@@ -0,0 +1,31 @@
+/**
+ * Usage instructions:
+ *
+ * - __Rinse__
+ * 1. Alter any rinse options _(optional)_
+ * - Recommended to [Bar.useSoap]
+ * - Optionally apply [Bar.elbowGrease] for best results
+ * 2. [Bar.rinse] to begin rinse
+ * 1. Thus you should call [Bar.rinse]
+ * 2. *Then* call [Bar.repeat]
+ * - Don't forget to use:
+ * - Soap
+ * - Elbow Grease
+ * 3. Finally, adjust soap usage [Bar.useSoap] as needed
+ * 3. Repeat with [Bar.repeat]
+ *
+ * - __Repeat__
+ * - Will use previously used rinse options
+ * - [Bar.rinse] must have been called once before
+ * - Can be repeated any number of times
+ * - Options include:
+ * - [Bar.useSoap]
+ * - [Bar.useElbowGrease]
+ */
+class Bar {
+ fun rinse() = Unit
+ fun repeat() = Unit
+
+ var useSoap = false
+ var useElbowGrease = false
+}
diff --git a/core/testdata/format/nestedLists.md b/core/testdata/format/nestedLists.md
new file mode 100644
index 000000000..fb25bc32e
--- /dev/null
+++ b/core/testdata/format/nestedLists.md
@@ -0,0 +1,43 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Usage instructions:
+
+* **Rinse**
+ 1. Alter any rinse options *(optional)*
+ * Recommended to [Bar.useSoap](use-soap.md)
+ * Optionally apply [Bar.elbowGrease](#) for best results
+ 2. [Bar.rinse](rinse.md) to begin rinse
+ 1. Thus you should call [Bar.rinse](rinse.md)
+ 2. *Then* call [Bar.repeat](repeat.md)
+ * Don't forget to use:
+ * Soap
+ * Elbow Grease
+ 3. Finally, adjust soap usage [Bar.useSoap](use-soap.md) as needed
+ 3. Repeat with [Bar.repeat](repeat.md)
+
+* **Repeat**
+ * Will use previously used rinse options
+ * [Bar.rinse](rinse.md) must have been called once before
+ * Can be repeated any number of times
+ * Options include:
+ * [Bar.useSoap](use-soap.md)
+ * [Bar.useElbowGrease](use-elbow-grease.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage instructions: |
+
+### Properties
+
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
+
+### Functions
+
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
+
diff --git a/core/testdata/format/newlineInTableCell.kt b/core/testdata/format/newlineInTableCell.kt
new file mode 100644
index 000000000..3e0616f05
--- /dev/null
+++ b/core/testdata/format/newlineInTableCell.kt
@@ -0,0 +1,6 @@
+/**
+ * There is `long long int` story
+ * full of
+ * new lines
+ */
+class A \ No newline at end of file
diff --git a/core/testdata/format/newlineInTableCell.package.md b/core/testdata/format/newlineInTableCell.package.md
new file mode 100644
index 000000000..53716db3a
--- /dev/null
+++ b/core/testdata/format/newlineInTableCell.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Types
+
+| [A](-a/index.md) | `class A`<br>There is `long long int` story full of new lines |
+
diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt b/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt
new file mode 100644
index 000000000..1f29e1107
--- /dev/null
+++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt
@@ -0,0 +1,13 @@
+
+class A
+class B
+
+
+internal typealias TA = A
+private typealias TB = B
+
+/**
+ * Correct ref [TA]
+ * Correct ref [TB]
+ */
+fun foo() {} \ No newline at end of file
diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
new file mode 100644
index 000000000..ca95093c6
--- /dev/null
+++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md
@@ -0,0 +1,9 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): Unit`
+
+Correct ref [TA](-a/index.md)
+Correct ref [TB](-b/index.md)
+
diff --git a/core/testdata/format/nullability.kt b/core/testdata/format/nullability.kt
new file mode 100644
index 000000000..d1d4545bf
--- /dev/null
+++ b/core/testdata/format/nullability.kt
@@ -0,0 +1,5 @@
+class C<T> {
+ fun foo(): Comparable<T>? {
+ return null
+ }
+}
diff --git a/core/testdata/format/nullability.md b/core/testdata/format/nullability.md
new file mode 100644
index 000000000..7b81c2554
--- /dev/null
+++ b/core/testdata/format/nullability.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Comparable<`[`T`](index.md#T)`>?` |
+
diff --git a/core/testdata/format/operatorOverloading.kt b/core/testdata/format/operatorOverloading.kt
new file mode 100644
index 000000000..6fe78e45f
--- /dev/null
+++ b/core/testdata/format/operatorOverloading.kt
@@ -0,0 +1,3 @@
+class C {
+ fun plus(other: C): C
+}
diff --git a/core/testdata/format/operatorOverloading.md b/core/testdata/format/operatorOverloading.md
new file mode 100644
index 000000000..0a4c87b64
--- /dev/null
+++ b/core/testdata/format/operatorOverloading.md
@@ -0,0 +1,5 @@
+[test](../index.md) / [C](index.md) / [plus](./plus.md)
+
+# plus
+
+`fun plus(other: `[`C`](index.md)`): `[`C`](index.md) \ No newline at end of file
diff --git a/core/testdata/format/orderedList.html b/core/testdata/format/orderedList.html
new file mode 100644
index 000000000..6f735bfdb
--- /dev/null
+++ b/core/testdata/format/orderedList.html
@@ -0,0 +1,30 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Usage instructions:</p>
+<ol><li>Rinse</li>
+<li>Repeat</li>
+</ol>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Usage instructions:</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/orderedList.kt b/core/testdata/format/orderedList.kt
new file mode 100644
index 000000000..03681c7a3
--- /dev/null
+++ b/core/testdata/format/orderedList.kt
@@ -0,0 +1,8 @@
+/**
+ * Usage instructions:
+ *
+ * 1. Rinse
+ * 1. Repeat
+ */
+class Bar {
+}
diff --git a/core/testdata/format/overloads.html b/core/testdata/format/overloads.html
new file mode 100644
index 000000000..feda82e4e
--- /dev/null
+++ b/core/testdata/format/overloads.html
@@ -0,0 +1,26 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>root package - test</title>
+</HEAD>
+<BODY>
+<a href="./index.html">test</a><br/>
+<br/>
+<h2>Package &lt;root&gt;</h2>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="f.html">f</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloads.kt b/core/testdata/format/overloads.kt
new file mode 100644
index 000000000..dcd2d097f
--- /dev/null
+++ b/core/testdata/format/overloads.kt
@@ -0,0 +1,5 @@
+/** Performs an action on x. */
+fun f(x: Int) { }
+
+/** Performs an action on x. */
+fun f(x: String) { }
diff --git a/core/testdata/format/overloadsWithDescription.html b/core/testdata/format/overloadsWithDescription.html
new file mode 100644
index 000000000..16b03f7e4
--- /dev/null
+++ b/core/testdata/format/overloadsWithDescription.html
@@ -0,0 +1,20 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f(kotlin.Int)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<a name="$f(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on <a href="f.html#$f(kotlin.Int)/x">x</a>.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the value to perform the action on.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloadsWithDescription.kt b/core/testdata/format/overloadsWithDescription.kt
new file mode 100644
index 000000000..740e642f9
--- /dev/null
+++ b/core/testdata/format/overloadsWithDescription.kt
@@ -0,0 +1,15 @@
+/**
+ * Performs an action on [x].
+ *
+ * This is a long description.
+ * @param x the value to perform the action on.
+ */
+fun f(x: Int) { }
+
+/**
+ * Performs an action on [x].
+ *
+ * This is a long description.
+ * @param x the value to perform the action on.
+ */
+fun f(x: String) { }
diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.html b/core/testdata/format/overloadsWithDifferentDescriptions.html
new file mode 100644
index 000000000..4c4f7f748
--- /dev/null
+++ b/core/testdata/format/overloadsWithDifferentDescriptions.html
@@ -0,0 +1,25 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f(kotlin.Int)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the int value to perform the action on.</p>
+<a name="$f(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the string value to perform the action on.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.kt b/core/testdata/format/overloadsWithDifferentDescriptions.kt
new file mode 100644
index 000000000..ad3169b0c
--- /dev/null
+++ b/core/testdata/format/overloadsWithDifferentDescriptions.kt
@@ -0,0 +1,15 @@
+/**
+ * Performs an action on x.
+ *
+ * This is a long description.
+ * @param x the int value to perform the action on.
+ */
+fun f(x: Int) { }
+
+/**
+ * Performs an action on x.
+ *
+ * This is a long description.
+ * @param x the string value to perform the action on.
+ */
+fun f(x: String) { }
diff --git a/core/testdata/format/overridingFunction.kt b/core/testdata/format/overridingFunction.kt
new file mode 100644
index 000000000..d7329489a
--- /dev/null
+++ b/core/testdata/format/overridingFunction.kt
@@ -0,0 +1,7 @@
+open class C() {
+ open fun f() {}
+}
+
+class D(): C() {
+ override fun f() {}
+}
diff --git a/core/testdata/format/overridingFunction.md b/core/testdata/format/overridingFunction.md
new file mode 100644
index 000000000..d0ec82fa0
--- /dev/null
+++ b/core/testdata/format/overridingFunction.md
@@ -0,0 +1,8 @@
+[test](../index.md) / [D](index.md) / [f](./f.md)
+
+# f
+
+`fun f(): Unit`
+
+Overrides [C.f](../-c/f.md)
+
diff --git a/core/testdata/format/paramTag.kt b/core/testdata/format/paramTag.kt
new file mode 100644
index 000000000..47e471f53
--- /dev/null
+++ b/core/testdata/format/paramTag.kt
@@ -0,0 +1,6 @@
+/**
+ * @param x A string
+ * @param y A number with a really long description that spans multiple lines and goes
+ * on and on and is very interesting to read
+ */
+fun f(x: String, y: Int) {}
diff --git a/core/testdata/format/paramTag.md b/core/testdata/format/paramTag.md
new file mode 100644
index 000000000..f39848887
--- /dev/null
+++ b/core/testdata/format/paramTag.md
@@ -0,0 +1,12 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f( x: String, y: Int): Unit`
+
+### Parameters
+
+`x` - A string
+
+`y` - A number with a really long description that spans multiple lines and goes
+ on and on and is very interesting to read \ No newline at end of file
diff --git a/core/testdata/format/parameterAnchor.html b/core/testdata/format/parameterAnchor.html
new file mode 100644
index 000000000..a4ae0997e
--- /dev/null
+++ b/core/testdata/format/parameterAnchor.html
@@ -0,0 +1,17 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>processFiles - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./process-files.html">processFiles</a><br/>
+<br/>
+<h1>processFiles</h1>
+<a name="$processFiles(kotlin.Function0((processFiles.T)))"></a>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">processFiles</span><span class="symbol">(</span><span class="identifier" id="$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">List</span><span class="symbol">&lt;</span><a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">&gt;</span></code>
+<p>Runs <a href="process-files.html#$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</a> for each file and collects its results into single list</p>
+<h3>Parameters</h3>
+<p><a name="processor"></a>
+<code>processor</code> - function to receive context for symbol resolution and file for processing</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/parameterAnchor.kt b/core/testdata/format/parameterAnchor.kt
new file mode 100644
index 000000000..ae36ee4c9
--- /dev/null
+++ b/core/testdata/format/parameterAnchor.kt
@@ -0,0 +1,6 @@
+/**
+ * Runs [processor] for each file and collects its results into single list
+ * @param processor function to receive context for symbol resolution and file for processing
+ */
+public fun processFiles<T>(processor: () -> T): List<T> {
+}
diff --git a/core/testdata/format/parenthesis.html b/core/testdata/format/parenthesis.html
new file mode 100644
index 000000000..c63154c12
--- /dev/null
+++ b/core/testdata/format/parenthesis.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>foo (bar)</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/parenthesis.kt b/core/testdata/format/parenthesis.kt
new file mode 100644
index 000000000..b906f64a9
--- /dev/null
+++ b/core/testdata/format/parenthesis.kt
@@ -0,0 +1,4 @@
+/**
+ * foo (bar)
+ */
+fun foo() {}
diff --git a/core/testdata/format/propertyVar.kt b/core/testdata/format/propertyVar.kt
new file mode 100644
index 000000000..88be1a7ae
--- /dev/null
+++ b/core/testdata/format/propertyVar.kt
@@ -0,0 +1 @@
+var x = 1 \ No newline at end of file
diff --git a/core/testdata/format/propertyVar.md b/core/testdata/format/propertyVar.md
new file mode 100644
index 000000000..887d25a5b
--- /dev/null
+++ b/core/testdata/format/propertyVar.md
@@ -0,0 +1,5 @@
+[test](index.md) / [x](./x.md)
+
+# x
+
+`var x: Int` \ No newline at end of file
diff --git a/core/testdata/format/qualifiedNameLink.kt b/core/testdata/format/qualifiedNameLink.kt
new file mode 100644
index 000000000..be82a9905
--- /dev/null
+++ b/core/testdata/format/qualifiedNameLink.kt
@@ -0,0 +1,6 @@
+/**
+ * See [kotlin.apply] for the docs
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/qualifiedNameLink.md b/core/testdata/format/qualifiedNameLink.md
new file mode 100644
index 000000000..92fa8f7ac
--- /dev/null
+++ b/core/testdata/format/qualifiedNameLink.md
@@ -0,0 +1,8 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`fun foo(): Unit`
+
+See [kotlin.apply](#) for the docs
+
diff --git a/core/testdata/format/receiverParameterTypeBound.kt b/core/testdata/format/receiverParameterTypeBound.kt
new file mode 100644
index 000000000..2b5f6f176
--- /dev/null
+++ b/core/testdata/format/receiverParameterTypeBound.kt
@@ -0,0 +1,5 @@
+open class Foo {
+}
+
+fun <T : Foo> T.xyzzy() {
+}
diff --git a/core/testdata/format/receiverParameterTypeBound.md b/core/testdata/format/receiverParameterTypeBound.md
new file mode 100644
index 000000000..978dc0f81
--- /dev/null
+++ b/core/testdata/format/receiverParameterTypeBound.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`open class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Extension Functions
+
+| [xyzzy](../xyzzy.md) | `fun <T : `[`Foo`](./index.md)`> `[`T`](../xyzzy.md#T)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/receiverReference.kt b/core/testdata/format/receiverReference.kt
new file mode 100644
index 000000000..3e6e2056d
--- /dev/null
+++ b/core/testdata/format/receiverReference.kt
@@ -0,0 +1,6 @@
+/**
+ * Prints [this]
+ */
+fun String.some() {
+ println(this)
+} \ No newline at end of file
diff --git a/core/testdata/format/receiverReference.md b/core/testdata/format/receiverReference.md
new file mode 100644
index 000000000..bdcce3228
--- /dev/null
+++ b/core/testdata/format/receiverReference.md
@@ -0,0 +1,6 @@
+[test](../index.md) / [kotlin.String](./index.md)
+
+### Extensions for kotlin.String
+
+| [some](some.md) | `fun String.some(): Unit`<br>Prints [this](some/-this-.md) |
+
diff --git a/core/testdata/format/referenceLink.kt b/core/testdata/format/referenceLink.kt
new file mode 100644
index 000000000..c6550f044
--- /dev/null
+++ b/core/testdata/format/referenceLink.kt
@@ -0,0 +1,16 @@
+package example
+
+/**
+ * It is link to [example other func][example]
+ *
+ * Sure, it is [example]
+ *
+ * [example]: example.someOtherFunc
+ */
+fun a() {
+
+}
+
+fun someOtherFunc() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/referenceLink.md b/core/testdata/format/referenceLink.md
new file mode 100644
index 000000000..ee910cbf1
--- /dev/null
+++ b/core/testdata/format/referenceLink.md
@@ -0,0 +1,17 @@
+<!-- File: test/example/a.md -->
+[test](../index.md) / [example](index.md) / [a](./a.md)
+
+# a
+
+`fun a(): Unit`
+
+It is link to [example other func](some-other-func.md)
+
+Sure, it is [example](some-other-func.md)
+
+<!-- File: test/example/some-other-func.md -->
+[test](../index.md) / [example](index.md) / [someOtherFunc](./some-other-func.md)
+
+# someOtherFunc
+
+`fun someOtherFunc(): Unit` \ No newline at end of file
diff --git a/core/testdata/format/reifiedTypeParameter.kt b/core/testdata/format/reifiedTypeParameter.kt
new file mode 100644
index 000000000..00fa1dc92
--- /dev/null
+++ b/core/testdata/format/reifiedTypeParameter.kt
@@ -0,0 +1,3 @@
+inline fun f<reified T>() {
+
+}
diff --git a/core/testdata/format/reifiedTypeParameter.md b/core/testdata/format/reifiedTypeParameter.md
new file mode 100644
index 000000000..40dbed7b8
--- /dev/null
+++ b/core/testdata/format/reifiedTypeParameter.md
@@ -0,0 +1,5 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`inline fun <reified T> f(): Unit` \ No newline at end of file
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt
new file mode 100644
index 000000000..84f78dfb1
--- /dev/null
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt
@@ -0,0 +1,3 @@
+fun (suspend () -> Unit).foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
new file mode 100644
index 000000000..4b5f3a64e
--- /dev/null
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
@@ -0,0 +1,6 @@
+[test](../index.md) / [kotlin.coroutines.SuspendFunction0](./index.md)
+
+### 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
new file mode 100644
index 000000000..fe1d031b0
--- /dev/null
+++ b/core/testdata/format/returnWithLink.html
@@ -0,0 +1,15 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="$foo(kotlin.String)/s1">s1</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code>
+<p><strong>Return</strong><br/>
+Returns <a href="foo.html#$foo(kotlin.String)/s1">s1</a> and does nothing else.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/returnWithLink.kt b/core/testdata/format/returnWithLink.kt
new file mode 100644
index 000000000..63d1770c3
--- /dev/null
+++ b/core/testdata/format/returnWithLink.kt
@@ -0,0 +1,4 @@
+/**
+ * @return Returns [s1] and does nothing else.
+ */
+fun foo(s1: String) = s1 \ No newline at end of file
diff --git a/core/testdata/format/sampleByFQName.kt b/core/testdata/format/sampleByFQName.kt
new file mode 100644
index 000000000..2c0af092d
--- /dev/null
+++ b/core/testdata/format/sampleByFQName.kt
@@ -0,0 +1,12 @@
+package test
+
+fun sample() {
+ println("sample")
+}
+
+/**
+ * @sample test.sample
+ */
+fun use() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/sampleByFQName.md b/core/testdata/format/sampleByFQName.md
new file mode 100644
index 000000000..7093179d1
--- /dev/null
+++ b/core/testdata/format/sampleByFQName.md
@@ -0,0 +1,17 @@
+<!-- File: test/test/sample.md -->
+[test](../index.md) / [test](index.md) / [sample](./sample.md)
+
+# sample
+
+`fun sample(): Unit`
+<!-- File: test/test/use.md -->
+[test](../index.md) / [test](index.md) / [use](./use.md)
+
+# use
+
+`fun use(): Unit`
+
+``` kotlin
+println("sample")
+```
+
diff --git a/core/testdata/format/sampleByShortName.kt b/core/testdata/format/sampleByShortName.kt
new file mode 100644
index 000000000..2e03310fb
--- /dev/null
+++ b/core/testdata/format/sampleByShortName.kt
@@ -0,0 +1,12 @@
+package test
+
+fun sample() {
+ println("sample")
+}
+
+/**
+ * @sample sample
+ */
+fun use() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/sampleByShortName.md b/core/testdata/format/sampleByShortName.md
new file mode 100644
index 000000000..7093179d1
--- /dev/null
+++ b/core/testdata/format/sampleByShortName.md
@@ -0,0 +1,17 @@
+<!-- File: test/test/sample.md -->
+[test](../index.md) / [test](index.md) / [sample](./sample.md)
+
+# sample
+
+`fun sample(): Unit`
+<!-- File: test/test/use.md -->
+[test](../index.md) / [test](index.md) / [use](./use.md)
+
+# use
+
+`fun use(): Unit`
+
+``` kotlin
+println("sample")
+```
+
diff --git a/core/testdata/format/see.html b/core/testdata/format/see.html
new file mode 100644
index 000000000..7483250b7
--- /dev/null
+++ b/core/testdata/format/see.html
@@ -0,0 +1,46 @@
+<!-- File: test/bar.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>bar - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./bar.html">bar</a><br/>
+<br/>
+<h1>bar</h1>
+<a name="$bar()"></a>
+<code><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>
+</BODY>
+</HTML>
+<!-- File: test/foo.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+</BODY>
+</HTML>
+<!-- File: test/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/>
+<br/>
+<h1>quux</h1>
+<a name="$quux()"></a>
+<code><span class="keyword">fun </span><span class="identifier">quux</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p><strong>See Also</strong><br/>
+<p><a href="foo.html">foo</a></p>
+<p><a href="bar.html">bar</a></p>
+</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/see.kt b/core/testdata/format/see.kt
new file mode 100644
index 000000000..a0b153b02
--- /dev/null
+++ b/core/testdata/format/see.kt
@@ -0,0 +1,12 @@
+/**
+ * @see foo
+ * @see bar
+ */
+fun quux() {
+}
+
+fun foo() {
+}
+
+fun bar() {
+} \ No newline at end of file
diff --git a/core/testdata/format/shadowedExtensionFunctions.kt b/core/testdata/format/shadowedExtensionFunctions.kt
new file mode 100644
index 000000000..64df1ecbd
--- /dev/null
+++ b/core/testdata/format/shadowedExtensionFunctions.kt
@@ -0,0 +1,18 @@
+open class Foo {
+}
+
+class Bar : Foo() {
+}
+
+fun Foo.xyzzy() {
+}
+
+fun Foo.shazam() {
+
+}
+
+fun Bar.xyzzy() {
+}
+
+fun Bar.shazam(i: Int) {
+}
diff --git a/core/testdata/format/shadowedExtensionFunctions.md b/core/testdata/format/shadowedExtensionFunctions.md
new file mode 100644
index 000000000..f900ecb2b
--- /dev/null
+++ b/core/testdata/format/shadowedExtensionFunctions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [shazam](../shazam.md) | `fun `[`Bar`](./index.md)`.shazam(i: Int): Unit`<br>`fun `[`Foo`](../-foo/index.md)`.shazam(): Unit` |
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html
new file mode 100644
index 000000000..32988de2a
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.html
@@ -0,0 +1,28 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Since1.1 - test</title>
+</HEAD>
+<BODY>
+<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>
+<td>
+<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Useful</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/sinceKotlin.kt b/core/testdata/format/sinceKotlin.kt
new file mode 100644
index 000000000..1025cf0d3
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Useful
+ */
+@SinceKotlin("1.1")
+class `Since1.1` \ No newline at end of file
diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md
new file mode 100644
index 000000000..df96db0af
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.md
@@ -0,0 +1,14 @@
+[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 |
+
diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md
new file mode 100644
index 000000000..eabf88d59
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.package.md
@@ -0,0 +1,10 @@
+[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 |
+
diff --git a/core/testdata/format/sinceKotlinWide.kt b/core/testdata/format/sinceKotlinWide.kt
new file mode 100644
index 000000000..fa1eb7de8
--- /dev/null
+++ b/core/testdata/format/sinceKotlinWide.kt
@@ -0,0 +1,11 @@
+/**
+ * Useful
+ */
+@SinceKotlin("1.1")
+class `Since1.1`
+
+/**
+ * Useful also
+ */
+@SinceKotlin("1.2")
+class `Since1.2` \ No newline at end of file
diff --git a/core/testdata/format/sinceKotlinWide.package.md b/core/testdata/format/sinceKotlinWide.package.md
new file mode 100644
index 000000000..58a5045ee
--- /dev/null
+++ b/core/testdata/format/sinceKotlinWide.package.md
@@ -0,0 +1,11 @@
+[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 |
+
diff --git a/core/testdata/format/starProjection.kt b/core/testdata/format/starProjection.kt
new file mode 100644
index 000000000..48d53e47d
--- /dev/null
+++ b/core/testdata/format/starProjection.kt
@@ -0,0 +1,3 @@
+public fun Iterable<*>.containsFoo(element: Any?): Boolean {
+ return false
+}
diff --git a/core/testdata/format/starProjection.md b/core/testdata/format/starProjection.md
new file mode 100644
index 000000000..5a53e5b9b
--- /dev/null
+++ b/core/testdata/format/starProjection.md
@@ -0,0 +1,6 @@
+[test](../index.md) / [kotlin.collections.Iterable](./index.md)
+
+### Extensions for kotlin.collections.Iterable
+
+| [containsFoo](contains-foo.md) | `fun Iterable<*>.containsFoo(element: Any?): Boolean` |
+
diff --git a/core/testdata/format/summarizeSignatures.kt b/core/testdata/format/summarizeSignatures.kt
new file mode 100644
index 000000000..1d875a501
--- /dev/null
+++ b/core/testdata/format/summarizeSignatures.kt
@@ -0,0 +1,20 @@
+package kotlin
+
+class Array<T>
+class IntArray
+class CharArray
+
+/**
+ * Returns true if foo.
+ */
+fun IntArray.foo(predicate: (Int) -> Boolean): Boolean = false
+
+/**
+ * Returns true if foo.
+ */
+fun CharArray.foo(predicate: (Char) -> Boolean): Boolean = false
+
+/**
+ * Returns true if foo.
+ */
+fun <T> Array<T>.foo(predicate: (T) -> Boolean): Boolean = false
diff --git a/core/testdata/format/summarizeSignatures.md b/core/testdata/format/summarizeSignatures.md
new file mode 100644
index 000000000..4f494166a
--- /dev/null
+++ b/core/testdata/format/summarizeSignatures.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [kotlin](./index.md)
+
+## Package kotlin
+
+### Types
+
+| [Array](-array/index.md) | `class Array<T>` |
+| [CharArray](-char-array/index.md) | `class CharArray` |
+| [IntArray](-int-array/index.md) | `class IntArray` |
+
+### Functions
+
+| [foo](foo.md) | `fun <T> any_array<T>.foo(predicate: (`[`T`](foo.md#T)`) -> Boolean): Boolean`<br>Returns true if foo. |
+
diff --git a/core/testdata/format/summarizeSignaturesProperty.kt b/core/testdata/format/summarizeSignaturesProperty.kt
new file mode 100644
index 000000000..fbbdd3288
--- /dev/null
+++ b/core/testdata/format/summarizeSignaturesProperty.kt
@@ -0,0 +1,20 @@
+package kotlin
+
+class Array<T>
+class IntArray
+class CharArray
+
+/**
+ * Returns true if foo.
+ */
+val IntArray.foo: Int = 0
+
+/**
+ * Returns true if foo.
+ */
+val CharArray.foo: Int = 0
+
+/**
+ * Returns true if foo.
+ */
+val <T> Array<T>.foo: Int = 0
diff --git a/core/testdata/format/summarizeSignaturesProperty.md b/core/testdata/format/summarizeSignaturesProperty.md
new file mode 100644
index 000000000..507ad6a5a
--- /dev/null
+++ b/core/testdata/format/summarizeSignaturesProperty.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [kotlin](./index.md)
+
+## Package kotlin
+
+### Types
+
+| [Array](-array/index.md) | `class Array<T>` |
+| [CharArray](-char-array/index.md) | `class CharArray` |
+| [IntArray](-int-array/index.md) | `class IntArray` |
+
+### Properties
+
+| [foo](foo.md) | `val <T> any_array<T>.foo: Int`<br>Returns true if foo. |
+
diff --git a/core/testdata/format/suspendParam.kt b/core/testdata/format/suspendParam.kt
new file mode 100644
index 000000000..ea3f56f99
--- /dev/null
+++ b/core/testdata/format/suspendParam.kt
@@ -0,0 +1,3 @@
+fun takesSuspendParam(func: suspend () -> Unit) {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md
new file mode 100644
index 000000000..ab116140d
--- /dev/null
+++ b/core/testdata/format/suspendParam.md
@@ -0,0 +1,5 @@
+[test](index.md) / [takesSuspendParam](./takes-suspend-param.md)
+
+# takesSuspendParam
+
+`fun takesSuspendParam(func: suspend () -> Unit): Unit` \ No newline at end of file
diff --git a/core/testdata/format/suspendParam.package.md b/core/testdata/format/suspendParam.package.md
new file mode 100644
index 000000000..92bd7ee7f
--- /dev/null
+++ b/core/testdata/format/suspendParam.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Functions
+
+| [takesSuspendParam](takes-suspend-param.md) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` |
+
diff --git a/core/testdata/format/throwsTag.kt b/core/testdata/format/throwsTag.kt
new file mode 100644
index 000000000..29a9c3f7a
--- /dev/null
+++ b/core/testdata/format/throwsTag.kt
@@ -0,0 +1,5 @@
+/**
+ * @throws IllegalArgumentException on Mondays
+ * @exception NullPointerException on Tuesdays
+ */
+fun f() {}
diff --git a/core/testdata/format/throwsTag.md b/core/testdata/format/throwsTag.md
new file mode 100644
index 000000000..70fba512f
--- /dev/null
+++ b/core/testdata/format/throwsTag.md
@@ -0,0 +1,11 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f(): Unit`
+
+### Exceptions
+
+`IllegalArgumentException` - on Mondays
+
+`NullPointerException` - on Tuesdays \ No newline at end of file
diff --git a/core/testdata/format/tokensInEmphasis.kt b/core/testdata/format/tokensInEmphasis.kt
new file mode 100644
index 000000000..39362b377
--- /dev/null
+++ b/core/testdata/format/tokensInEmphasis.kt
@@ -0,0 +1,10 @@
+/**
+ * Another emphasised class.
+ *
+ * _This class, [Bar] is just meh._
+ *
+ * _For a semicolon; [Bar.foo] is for you!._
+ */
+class Bar {
+ fun foo() = ";"
+}
diff --git a/core/testdata/format/tokensInEmphasis.md b/core/testdata/format/tokensInEmphasis.md
new file mode 100644
index 000000000..a68861de9
--- /dev/null
+++ b/core/testdata/format/tokensInEmphasis.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Another emphasised class.
+
+*This class, [Bar](./index.md) is just meh.*
+
+*For a semicolon; [Bar.foo](foo.md) is for you!.*
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Another emphasised class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): String` |
+
diff --git a/core/testdata/format/tokensInHeaders.kt b/core/testdata/format/tokensInHeaders.kt
new file mode 100644
index 000000000..df62b024f
--- /dev/null
+++ b/core/testdata/format/tokensInHeaders.kt
@@ -0,0 +1,27 @@
+/**
+ * Why did the token cross the road?
+ *
+ * # Because it was Beer o'clock @ [The.bar]
+ *
+ * ## But __waz *\[sic\]* [it](isitbeeroclock.com)__ really?
+ *
+ * ### [The.bar] has? [The.foo]est drinks ever!
+ *
+ * #### _[The.kotlinz] is [The.bestests], [Bar.none]_
+ *
+ * ##### So many lame code "puns" (in) [The.house]
+ *
+ * ###### End of the?? [Bar.line]! - we need to go back!
+ */
+class The {
+ object Bar {
+ fun none() {}
+ }
+
+ fun bar() {}
+ fun foo() {}
+ fun bestests() {}
+ fun kotlinz() {}
+ fun house() {}
+ fun line() {}
+}
diff --git a/core/testdata/format/tokensInHeaders.md b/core/testdata/format/tokensInHeaders.md
new file mode 100644
index 000000000..bd25492e2
--- /dev/null
+++ b/core/testdata/format/tokensInHeaders.md
@@ -0,0 +1,37 @@
+[test](../index.md) / [The](./index.md)
+
+# The
+
+`class The`
+
+Why did the token cross the road?
+
+# Because it was Beer o'clock @ [The.bar](bar.md)
+
+## But **waz *\[sic\]* [it](isitbeeroclock.com)** really?
+
+### [The.bar](bar.md) has? [The.foo](foo.md)est drinks ever!
+
+#### *[The.kotlinz](kotlinz.md) is [The.bestests](bestests.md), [Bar.none](-bar/none.md)*
+
+##### So many lame code "puns" (in) [The.house](house.md)
+
+###### End of the?? [Bar.line](#)! - we need to go back!
+
+### Types
+
+| [Bar](-bar/index.md) | `object Bar` |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `The()`<br>Why did the token cross the road? |
+
+### Functions
+
+| [bar](bar.md) | `fun bar(): Unit` |
+| [bestests](bestests.md) | `fun bestests(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [house](house.md) | `fun house(): Unit` |
+| [kotlinz](kotlinz.md) | `fun kotlinz(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
+
diff --git a/core/testdata/format/tokensInStrong.kt b/core/testdata/format/tokensInStrong.kt
new file mode 100644
index 000000000..596a9ae8d
--- /dev/null
+++ b/core/testdata/format/tokensInStrong.kt
@@ -0,0 +1,10 @@
+/**
+ * __YASC: [Yasc] Yet Another Strong Class__
+ *
+ * __This class, [Yasc] *is* just meh.__
+ *
+ * __For a semicolon; [Yasc.foo] is for you!.__
+ */
+class Yasc {
+ fun foo() = ";"
+}
diff --git a/core/testdata/format/tokensInStrong.md b/core/testdata/format/tokensInStrong.md
new file mode 100644
index 000000000..2781656cb
--- /dev/null
+++ b/core/testdata/format/tokensInStrong.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Yasc](./index.md)
+
+# Yasc
+
+`class Yasc`
+
+**YASC: [Yasc](./index.md) Yet Another Strong Class**
+
+**This class, [Yasc](./index.md) *is* just meh.**
+
+**For a semicolon; [Yasc.foo](foo.md) is for you!.**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Yasc()`<br>**YASC: [Yasc](./index.md) Yet Another Strong Class** |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): String` |
+
diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html
new file mode 100644
index 000000000..dacd0567c
--- /dev/null
+++ b/core/testdata/format/tripleBackticks.html
@@ -0,0 +1,16 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Description</p>
+<pre><code>code sample
+</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/tripleBackticks.kt b/core/testdata/format/tripleBackticks.kt
new file mode 100644
index 000000000..54dfa6d51
--- /dev/null
+++ b/core/testdata/format/tripleBackticks.kt
@@ -0,0 +1,7 @@
+/**
+ * Description
+ * ```
+ * code sample
+ * ```
+ */
+fun f() {}
diff --git a/core/testdata/format/typeAliases.kt b/core/testdata/format/typeAliases.kt
new file mode 100644
index 000000000..9657963e6
--- /dev/null
+++ b/core/testdata/format/typeAliases.kt
@@ -0,0 +1,27 @@
+
+class A
+class B
+class C<T>
+
+typealias D = A
+typealias E = D
+
+typealias F = (A) -> B
+
+typealias G = C<A>
+typealias H<T> = C<T>
+
+typealias I<T> = H<T>
+typealias J = H<A>
+
+typealias K = H<J>
+
+typealias L = (K, B) -> J
+
+/**
+ * Documented
+ */
+typealias M = A
+
+@Deprecated("!!!")
+typealias N = A \ No newline at end of file
diff --git a/core/testdata/format/typeAliases.md b/core/testdata/format/typeAliases.md
new file mode 100644
index 000000000..218c4848b
--- /dev/null
+++ b/core/testdata/format/typeAliases.md
@@ -0,0 +1,104 @@
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
+
+# A
+
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
+
+# B
+
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/-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)
+
+# E
+
+`typealias E = `[`D`](-d.md)
+<!-- File: test/-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)
+
+# G
+
+`typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-h.md -->
+[test](index.md) / [H](./-h.md)
+
+# H
+
+`typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>`
+<!-- File: test/-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)
+
+# J
+
+`typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-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)
+
+# L
+
+`typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md)
+<!-- File: test/-m.md -->
+[test](index.md) / [M](./-m.md)
+
+# M
+
+`typealias M = `[`A`](-a/index.md)
+
+Documented
+
+<!-- File: test/-n.md -->
+[test](index.md) / [N](./-n.md)
+
+# N
+
+`typealias ~~N~~ = `[`A`](-a/index.md)
+**Deprecated:** !!!
+
diff --git a/core/testdata/format/typeAliases.package.md b/core/testdata/format/typeAliases.package.md
new file mode 100644
index 000000000..199e91c2c
--- /dev/null
+++ b/core/testdata/format/typeAliases.package.md
@@ -0,0 +1,24 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Types
+
+| [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) |
+| [G](-g.md) | `typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>` |
+| [H](-h.md) | `typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>` |
+| [I](-i.md) | `typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>` |
+| [J](-j.md) | `typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>` |
+| [K](-k.md) | `typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>` |
+| [L](-l.md) | `typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md) |
+| [M](-m.md) | `typealias M = `[`A`](-a/index.md)<br>Documented |
+| [N](-n.md) | `typealias ~~N~~ = `[`A`](-a/index.md) |
+
diff --git a/core/testdata/format/typeLink.html b/core/testdata/format/typeLink.html
new file mode 100644
index 000000000..30af8a930
--- /dev/null
+++ b/core/testdata/format/typeLink.html
@@ -0,0 +1,24 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<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>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/typeLink.kt b/core/testdata/format/typeLink.kt
new file mode 100644
index 000000000..966e020e6
--- /dev/null
+++ b/core/testdata/format/typeLink.kt
@@ -0,0 +1,5 @@
+class Foo() {
+}
+
+class Bar(): Foo {
+}
diff --git a/core/testdata/format/typeParameterBounds.kt b/core/testdata/format/typeParameterBounds.kt
new file mode 100644
index 000000000..8604e3b92
--- /dev/null
+++ b/core/testdata/format/typeParameterBounds.kt
@@ -0,0 +1,7 @@
+
+/**
+ * generic function
+ * @param T the first type parameter
+ */
+public fun <T : R, R> generic() {
+} \ No newline at end of file
diff --git a/core/testdata/format/typeParameterBounds.md b/core/testdata/format/typeParameterBounds.md
new file mode 100644
index 000000000..cf03b3a76
--- /dev/null
+++ b/core/testdata/format/typeParameterBounds.md
@@ -0,0 +1,11 @@
+[test](index.md) / [generic](./generic.md)
+
+# generic
+
+`fun <T : `[`R`](generic.md#R)`, R> generic(): Unit`
+
+generic function
+
+### Parameters
+
+`T` - the first type parameter \ No newline at end of file
diff --git a/core/testdata/format/typeParameterReference.kt b/core/testdata/format/typeParameterReference.kt
new file mode 100644
index 000000000..f196112d3
--- /dev/null
+++ b/core/testdata/format/typeParameterReference.kt
@@ -0,0 +1,6 @@
+/**
+ * Correct ref to [T]
+ */
+fun <T> T.tt() {
+ println("T.tt")
+} \ No newline at end of file
diff --git a/core/testdata/format/typeParameterReference.md b/core/testdata/format/typeParameterReference.md
new file mode 100644
index 000000000..5001d321b
--- /dev/null
+++ b/core/testdata/format/typeParameterReference.md
@@ -0,0 +1,8 @@
+[test](index.md) / [tt](./tt.md)
+
+# tt
+
+`fun <T> `[`T`](tt.md#T)`.tt(): Unit`
+
+Correct ref to [T](tt.md#T)
+
diff --git a/core/testdata/format/typeParameterVariance.kt b/core/testdata/format/typeParameterVariance.kt
new file mode 100644
index 000000000..d45e7eb37
--- /dev/null
+++ b/core/testdata/format/typeParameterVariance.kt
@@ -0,0 +1,5 @@
+/**
+ * @param T the class parameter type
+ */
+class Foo<out T> {
+}
diff --git a/core/testdata/format/typeParameterVariance.md b/core/testdata/format/typeParameterVariance.md
new file mode 100644
index 000000000..b0615d43b
--- /dev/null
+++ b/core/testdata/format/typeParameterVariance.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo<out T>`
+
+### Parameters
+
+`T` - the class parameter type
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/typeProjectionVariance.kt b/core/testdata/format/typeProjectionVariance.kt
new file mode 100644
index 000000000..85ee344d0
--- /dev/null
+++ b/core/testdata/format/typeProjectionVariance.kt
@@ -0,0 +1 @@
+fun <T> Array<out T>.foo() {}
diff --git a/core/testdata/format/typeProjectionVariance.md b/core/testdata/format/typeProjectionVariance.md
new file mode 100644
index 000000000..d3a55c58b
--- /dev/null
+++ b/core/testdata/format/typeProjectionVariance.md
@@ -0,0 +1,6 @@
+[test](../index.md) / [kotlin.Array](./index.md)
+
+### Extensions for kotlin.Array
+
+| [foo](foo.md) | `fun <T> Array<out `[`T`](foo.md#T)`>.foo(): Unit` |
+
diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.html b/core/testdata/format/uninterpretedEmphasisCharacters.html
new file mode 100644
index 000000000..dd338f72a
--- /dev/null
+++ b/core/testdata/format/uninterpretedEmphasisCharacters.html
@@ -0,0 +1,15 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This is <em>emphasized text</em> but text_with_underscores has to preserve the underscores.
+Single stars embedded in a word like Embedded*Star have to be preserved as well.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.kt b/core/testdata/format/uninterpretedEmphasisCharacters.kt
new file mode 100644
index 000000000..711bb5674
--- /dev/null
+++ b/core/testdata/format/uninterpretedEmphasisCharacters.kt
@@ -0,0 +1,5 @@
+/**
+ * This is _emphasized text_ but text_with_underscores has to preserve the underscores.
+ * Single stars embedded in a word like Embedded*Star have to be preserved as well.
+ */
+fun foo() {} \ No newline at end of file
diff --git a/core/testdata/format/unorderedLists.kt b/core/testdata/format/unorderedLists.kt
new file mode 100644
index 000000000..a594b89b6
--- /dev/null
+++ b/core/testdata/format/unorderedLists.kt
@@ -0,0 +1,36 @@
+/**
+ * Usage summary:
+ *
+ * - Rinse
+ * - Repeat
+ *
+ * Usage instructions:
+ *
+ * - [Bar.rinse] to rinse
+ * - Alter any rinse options _(optional)_
+ * - To repeat; [Bar.repeat]
+ * - Can reconfigure options:
+ * - Soap
+ * - Elbow Grease
+ * - Bleach
+ *
+ * Rinse options:
+ *
+ * - [Bar.useSoap]
+ * - _recommended_
+ *
+ * - [Bar.useElbowGrease]
+ * - _warning: requires effort_
+ *
+ * - [Bar.useBleach]
+ * - __use with caution__
+ *
+ */
+class Bar {
+ fun rinse() = Unit
+ fun repeat() = Unit
+
+ var useSoap = false
+ var useElbowGrease = false
+ var useBleach = false
+}
diff --git a/core/testdata/format/unorderedLists.md b/core/testdata/format/unorderedLists.md
new file mode 100644
index 000000000..52ad9a71c
--- /dev/null
+++ b/core/testdata/format/unorderedLists.md
@@ -0,0 +1,47 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Usage summary:
+
+* Rinse
+* Repeat
+
+Usage instructions:
+
+* [Bar.rinse](rinse.md) to rinse
+* Alter any rinse options *(optional)*
+* To repeat; [Bar.repeat](repeat.md)
+ * Can reconfigure options:
+ * Soap
+ * Elbow Grease
+ * Bleach
+
+Rinse options:
+
+* [Bar.useSoap](use-soap.md)
+ * *recommended*
+
+* [Bar.useElbowGrease](use-elbow-grease.md)
+ * *warning: requires effort*
+
+* [Bar.useBleach](use-bleach.md)
+ * **use with caution**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage summary: |
+
+### Properties
+
+| [useBleach](use-bleach.md) | `var useBleach: Boolean` |
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
+
+### Functions
+
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
+
diff --git a/core/testdata/format/varargsFunction.kt b/core/testdata/format/varargsFunction.kt
new file mode 100644
index 000000000..deea127b1
--- /dev/null
+++ b/core/testdata/format/varargsFunction.kt
@@ -0,0 +1 @@
+fun f(vararg s: String) {}
diff --git a/core/testdata/format/varargsFunction.md b/core/testdata/format/varargsFunction.md
new file mode 100644
index 000000000..550202cce
--- /dev/null
+++ b/core/testdata/format/varargsFunction.md
@@ -0,0 +1,5 @@
+[test](index.md) / [f](./f.md)
+
+# f
+
+`fun f(vararg s: String): Unit` \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/jre7.kt b/core/testdata/format/website-html/dataTags/jre7.kt
new file mode 100644
index 000000000..d21b8d7b4
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/jre7.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jre7New() {}
+
+fun jre7() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/js.kt b/core/testdata/format/website-html/dataTags/js.kt
new file mode 100644
index 000000000..b22d70886
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/js.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jsNew() {}
+
+fun js() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/jvm.kt b/core/testdata/format/website-html/dataTags/jvm.kt
new file mode 100644
index 000000000..02d042261
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jvmNew() {}
+
+fun jvm() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/multiplatform.package.html b/core/testdata/format/website-html/dataTags/multiplatform.package.html
new file mode 100644
index 000000000..35453ab10
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/multiplatform.package.html
@@ -0,0 +1,71 @@
+<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">foo</a></div>
+<h2>Package foo</h2>
+<h3>Functions</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM" data-jre-version="JRE7"><td>
+<p><a href="jre7.html">jre7</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td>
+<p><a href="jre7-new.html">jre7New</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS"><td>
+<p><a href="js.html">js</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td>
+<p><a href="js-new.html">jsNew</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td>
+<p><a href="jvm.html">jvm</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td>
+<p><a href="jvm-new.html">jvmNew</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td>
+<p><a href="shared.html">shared</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td>
+<p><a href="shared-new.html">sharedNew</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/js.kt b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt
new file mode 100644
index 000000000..045f3f0d6
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt
new file mode 100644
index 000000000..57f36742e
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
new file mode 100644
index 000000000..3d34fc7e9
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
@@ -0,0 +1,38 @@
+<div class='api-docs-breadcrumbs'><a href="../../index.html">test</a> / <a href="../index.html">pack</a> / <a href="./index.html">Some</a></div>
+<h1>Some</h1>
+<div class="overload-group" data-platform="JVM"><div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+<p><strong>Platform and version requirements:</strong> JVM</p>
+</div>
+<div class="overload-group" data-platform="JS"><div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+<p><strong>Platform and version requirements:</strong> JS</p>
+<h3>Constructors</h3>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<p><a href="-some/-init-.html">&lt;init&gt;</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<p><a href="-some/magic.html">magic</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+</div>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
new file mode 100644
index 000000000..c8926a28e
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
@@ -0,0 +1,36 @@
+<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">pack</a></div>
+<h2>Package pack</h2>
+<h3>Types</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td>
+<p><a href="-some/index.html">Some</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td>
+<p><a href="-some-cool-jvm-class/index.html">SomeCoolJvmClass</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
+<h3>Type Aliases</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM"><td>
+<p><a href="-some/index.html">Some</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website-html/dropImport.html b/core/testdata/format/website-html/dropImport.html
new file mode 100644
index 000000000..e0fcb12bb
--- /dev/null
+++ b/core/testdata/format/website-html/dropImport.html
@@ -0,0 +1,11 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import some.*
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/dropImport.kt b/core/testdata/format/website-html/dropImport.kt
new file mode 100644
index 000000000..7b8d9f4e8
--- /dev/null
+++ b/core/testdata/format/website-html/dropImport.kt
@@ -0,0 +1,12 @@
+import samples.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/newLinesInImportList.html b/core/testdata/format/website-html/newLinesInImportList.html
new file mode 100644
index 000000000..b5a073252
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInImportList.html
@@ -0,0 +1,12 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import same.*
+import some.*
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInImportList.kt b/core/testdata/format/website-html/newLinesInImportList.kt
new file mode 100644
index 000000000..836d9f6f0
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInImportList.kt
@@ -0,0 +1,12 @@
+import same.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/newLinesInSamples.html b/core/testdata/format/website-html/newLinesInSamples.html
new file mode 100644
index 000000000..50f875da1
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInSamples.html
@@ -0,0 +1,19 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+val words = listOf("a", "abc", "ab", "def", "abcd")
+val byLength = words.groupBy { it.length }
+
+println(byLength.keys) // [1, 3, 2, 4]
+println(byLength.values) // [[a], [abc, def], [ab], [abcd]]
+
+val mutableByLength: MutableMap&lt;Int, MutableList&lt;String&gt;&gt; = words.groupByTo(mutableMapOf()) { it.length }
+// same content as in byLength map, but the map is mutable
+println("mutableByLength == byLength is ${mutableByLength == byLength}") // true
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInSamples.kt b/core/testdata/format/website-html/newLinesInSamples.kt
new file mode 100644
index 000000000..ee49aefc7
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInSamples.kt
@@ -0,0 +1,19 @@
+fun groupBySample() {
+ val words = listOf("a", "abc", "ab", "def", "abcd")
+ val byLength = words.groupBy { it.length }
+
+ assertPrints(byLength.keys, "[1, 3, 2, 4]")
+ assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]")
+
+ val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+ // same content as in byLength map, but the map is mutable
+ assertTrue(mutableByLength == byLength)
+}
+
+
+/**
+ * @sample groupBySample
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/overloadGroup.html b/core/testdata/format/website-html/overloadGroup.html
new file mode 100644
index 000000000..aaba9c96f
--- /dev/null
+++ b/core/testdata/format/website-html/overloadGroup.html
@@ -0,0 +1,16 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./magic.html">magic</a></div>
+<h1>magic</h1>
+<div class="overload-group"><a name="$magic(kotlin.String)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<h3>Parameters</h3>
+<p><a name="spell"></a>
+<code>spell</code> - The text of spell, often distributed on scrolls</p>
+<p><strong>Return</strong> Spell ID for future casts</p>
+</div>
+<div class="overload-group"><a name="$magic(kotlin.Int)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<h3>Parameters</h3>
+<p><a name="spell"></a>
+<code>spell</code> - Spell ID of previously casted spell</p>
+<p><strong>Return</strong> Spell ID for future casts</p>
+</div>
diff --git a/core/testdata/format/website-html/overloadGroup.kt b/core/testdata/format/website-html/overloadGroup.kt
new file mode 100644
index 000000000..5bc98e3d4
--- /dev/null
+++ b/core/testdata/format/website-html/overloadGroup.kt
@@ -0,0 +1,15 @@
+/**
+ * @param spell The text of spell, often distributed on scrolls
+ * @return Spell ID for future casts
+ */
+fun magic(spell: String): Int {
+
+}
+
+/**
+ * @param spell Spell ID of previously casted spell
+ * @return Spell ID for future casts
+ */
+fun magic(spell: Int): Int {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/returnTag.html b/core/testdata/format/website-html/returnTag.html
new file mode 100644
index 000000000..b68785032
--- /dev/null
+++ b/core/testdata/format/website-html/returnTag.html
@@ -0,0 +1,9 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./index-of.html">indexOf</a></div>
+<h1>indexOf</h1>
+<a name="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)"></a>
+<div class="signature"><code><span class="keyword">fun </span><a href="-foo/index.html"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.</p>
+<h3>Parameters</h3>
+<p><a name="ignoreCase"></a>
+<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.</p>
+<p><strong>Returns</strong> An index of the first occurrence of <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.</p>
diff --git a/core/testdata/format/website-html/returnTag.kt b/core/testdata/format/website-html/returnTag.kt
new file mode 100644
index 000000000..669c14f90
--- /dev/null
+++ b/core/testdata/format/website-html/returnTag.kt
@@ -0,0 +1,11 @@
+class Foo
+
+/**
+ * Returns the index within this string of the first occurrence of the specified character, starting from the specified [startIndex].
+ *
+ * @param ignoreCase `true` to ignore character case when matching a character. By default `false`.
+ * @returns An index of the first occurrence of [char] or -1 if none is found.
+ */
+fun Foo.indexOf(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int {
+ return -1
+}
diff --git a/core/testdata/format/website-html/sample.html b/core/testdata/format/website-html/sample.html
new file mode 100644
index 000000000..1fb26e41b
--- /dev/null
+++ b/core/testdata/format/website-html/sample.html
@@ -0,0 +1,19 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<div class="overload-group"><a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.</p>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+if (true) {
+ println(property)
+}
+//sampleEnd
+}</code></pre></div>
+</div>
+<div class="overload-group"><a name="$foo(kotlin.Int)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+</div>
diff --git a/core/testdata/format/website-html/sample.kt b/core/testdata/format/website-html/sample.kt
new file mode 100644
index 000000000..a664c2f52
--- /dev/null
+++ b/core/testdata/format/website-html/sample.kt
@@ -0,0 +1,16 @@
+/**
+ * Groups elements of the original sequence by the key returned by the given [keySelector] function
+ * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+ * @sample example1
+ */
+fun foo(): Int {
+ return 0
+}
+
+fun foo(i: Int): Int {
+ return 1
+}
+
+fun example1(node: String) = if (true) {
+ println(property)
+}
diff --git a/core/testdata/format/website-html/sampleWithAsserts.html b/core/testdata/format/website-html/sampleWithAsserts.html
new file mode 100644
index 000000000..e91232f5b
--- /dev/null
+++ b/core/testdata/format/website-html/sampleWithAsserts.html
@@ -0,0 +1,22 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./a.html">a</a></div>
+<h1>a</h1>
+<a name="$a()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import java.io.FileNotFoundException
+import java.io.File
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+println(a()) // Hello, Work
+println("a() == b() is ${a() == b()}") // true
+// A eq B
+println("a() == b() is ${a() == b()}") // true
+// readSomeFile(File("some.txt")) // reading file now will fail
+// readSomeFile(File("some.txt")) // will fail with FileNotFoundException
+
+fun indented() {
+ // A neq B
+ println("a() != b() is ${a() != b()}") // false
+}
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/sampleWithAsserts.kt b/core/testdata/format/website-html/sampleWithAsserts.kt
new file mode 100644
index 000000000..b3bce11d8
--- /dev/null
+++ b/core/testdata/format/website-html/sampleWithAsserts.kt
@@ -0,0 +1,32 @@
+import java.io.FileNotFoundException
+import java.io.File
+
+/**
+ * @sample sample
+ */
+fun a(): String {
+ return "Hello, Work"
+}
+
+fun b(): String {
+ return "Hello, Rest"
+}
+
+/**
+ * @throws FileNotFoundException every time
+ */
+fun readSomeFile(f: File) {
+ throw FileNotFoundException("BOOM")
+}
+
+fun sample() {
+ assertPrints(a(), "Hello, Work")
+ assertTrue(a() == b())
+ assertTrue(a() == b(), "A eq B")
+ assertFails("reading file now") { readSomeFile(File("some.txt")) }
+ assertFailsWith<FileNotFoundException> { readSomeFile(File("some.txt")) }
+
+ fun indented() {
+ assertFalse(a() != b(), "A neq B")
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/dropImport.kt b/core/testdata/format/website-samples/dropImport.kt
new file mode 100644
index 000000000..7b8d9f4e8
--- /dev/null
+++ b/core/testdata/format/website-samples/dropImport.kt
@@ -0,0 +1,12 @@
+import samples.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/dropImport.md b/core/testdata/format/website-samples/dropImport.md
new file mode 100644
index 000000000..1e05678b8
--- /dev/null
+++ b/core/testdata/format/website-samples/dropImport.md
@@ -0,0 +1,23 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+import some.*
+
+fun main(args: Array<String>) {
+//sampleStart
+
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website-samples/newLinesInImportList.kt b/core/testdata/format/website-samples/newLinesInImportList.kt
new file mode 100644
index 000000000..836d9f6f0
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInImportList.kt
@@ -0,0 +1,12 @@
+import same.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/newLinesInImportList.md b/core/testdata/format/website-samples/newLinesInImportList.md
new file mode 100644
index 000000000..27d796f80
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInImportList.md
@@ -0,0 +1,24 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+import same.*
+import some.*
+
+fun main(args: Array<String>) {
+//sampleStart
+
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website-samples/newLinesInSamples.kt b/core/testdata/format/website-samples/newLinesInSamples.kt
new file mode 100644
index 000000000..ee49aefc7
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInSamples.kt
@@ -0,0 +1,19 @@
+fun groupBySample() {
+ val words = listOf("a", "abc", "ab", "def", "abcd")
+ val byLength = words.groupBy { it.length }
+
+ assertPrints(byLength.keys, "[1, 3, 2, 4]")
+ assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]")
+
+ val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+ // same content as in byLength map, but the map is mutable
+ assertTrue(mutableByLength == byLength)
+}
+
+
+/**
+ * @sample groupBySample
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/newLinesInSamples.md b/core/testdata/format/website-samples/newLinesInSamples.md
new file mode 100644
index 000000000..5344b9838
--- /dev/null
+++ b/core/testdata/format/website-samples/newLinesInSamples.md
@@ -0,0 +1,31 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+
+
+fun main(args: Array<String>) {
+//sampleStart
+val words = listOf("a", "abc", "ab", "def", "abcd")
+val byLength = words.groupBy { it.length }
+
+println(byLength.keys) // [1, 3, 2, 4]
+println(byLength.values) // [[a], [abc, def], [ab], [abcd]]
+
+val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+// same content as in byLength map, but the map is mutable
+println("mutableByLength == byLength is ${mutableByLength == byLength}") // true
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website-samples/sample.kt b/core/testdata/format/website-samples/sample.kt
new file mode 100644
index 000000000..a664c2f52
--- /dev/null
+++ b/core/testdata/format/website-samples/sample.kt
@@ -0,0 +1,16 @@
+/**
+ * Groups elements of the original sequence by the key returned by the given [keySelector] function
+ * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+ * @sample example1
+ */
+fun foo(): Int {
+ return 0
+}
+
+fun foo(i: Int): Int {
+ return 1
+}
+
+fun example1(node: String) = if (true) {
+ println(property)
+}
diff --git a/core/testdata/format/website-samples/sample.md b/core/testdata/format/website-samples/sample.md
new file mode 100644
index 000000000..b29075a78
--- /dev/null
+++ b/core/testdata/format/website-samples/sample.md
@@ -0,0 +1,39 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+
+<div class="sample" markdown="1">
+
+``` kotlin
+
+
+fun main(args: Array<String>) {
+//sampleStart
+if (true) {
+ println(property)
+}
+//sampleEnd
+}
+```
+
+</div>
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+</div>
diff --git a/core/testdata/format/website-samples/sampleWithAsserts.kt b/core/testdata/format/website-samples/sampleWithAsserts.kt
new file mode 100644
index 000000000..bb9732d5c
--- /dev/null
+++ b/core/testdata/format/website-samples/sampleWithAsserts.kt
@@ -0,0 +1,15 @@
+/**
+ * @sample sample
+ */
+fun a(): String {
+ return "Hello, Work"
+}
+
+fun b(): String {
+ return "Hello, Rest"
+}
+
+fun sample() {
+ assertPrints(a(), "Hello, Work")
+ assertTrue(a() == b())
+} \ No newline at end of file
diff --git a/core/testdata/format/website-samples/sampleWithAsserts.md b/core/testdata/format/website-samples/sampleWithAsserts.md
new file mode 100644
index 000000000..c114468a4
--- /dev/null
+++ b/core/testdata/format/website-samples/sampleWithAsserts.md
@@ -0,0 +1,24 @@
+---
+title: a - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/a">a</a></div>
+
+# a
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div>
+<div class="sample" markdown="1">
+
+``` kotlin
+
+
+fun main(args: Array<String>) {
+//sampleStart
+println(a()) // Hello, Work
+println("a() == b() is ${a() == b()}") // true
+//sampleEnd
+}
+```
+
+</div>
diff --git a/core/testdata/format/website/dataTags/jre7.kt b/core/testdata/format/website/dataTags/jre7.kt
new file mode 100644
index 000000000..d21b8d7b4
--- /dev/null
+++ b/core/testdata/format/website/dataTags/jre7.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jre7New() {}
+
+fun jre7() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/js.kt b/core/testdata/format/website/dataTags/js.kt
new file mode 100644
index 000000000..b22d70886
--- /dev/null
+++ b/core/testdata/format/website/dataTags/js.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jsNew() {}
+
+fun js() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/jvm.kt b/core/testdata/format/website/dataTags/jvm.kt
new file mode 100644
index 000000000..02d042261
--- /dev/null
+++ b/core/testdata/format/website/dataTags/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jvmNew() {}
+
+fun jvm() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTags/multiplatform.package.md b/core/testdata/format/website/dataTags/multiplatform.package.md
new file mode 100644
index 000000000..1c7fbf669
--- /dev/null
+++ b/core/testdata/format/website/dataTags/multiplatform.package.md
@@ -0,0 +1,71 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">foo</a></div>
+
+## Package foo
+
+### Functions
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/jre7">jre7</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/jre7-new">jre7New</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS"><td markdown="1">
+<a href="test/foo/js">js</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td markdown="1">
+<a href="test/foo/js-new">jsNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td markdown="1">
+<a href="test/foo/jvm">jvm</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td markdown="1">
+<a href="test/foo/jvm-new">jvmNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/shared">shared</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1">
+<a href="test/foo/shared-new">sharedNew</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt
diff --git a/core/testdata/format/website/dataTagsInGroupNode/js.kt b/core/testdata/format/website/dataTagsInGroupNode/js.kt
new file mode 100644
index 000000000..045f3f0d6
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/website/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt
new file mode 100644
index 000000000..57f36742e
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md
new file mode 100644
index 000000000..78f6adf22
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md
@@ -0,0 +1,56 @@
+---
+title: pack.Some - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a> / <a href="test/pack/-some/index">Some</a></div>
+
+# Some
+
+<div class="overload-group" data-platform="JVM" markdown="1">
+
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+**Platform and version requirements:** JVM
+
+</div>
+
+<div class="overload-group" data-platform="JS" markdown="1">
+
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/-init-">&lt;init&gt;</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+### Functions
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/magic">magic</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+</div>
diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
new file mode 100644
index 000000000..a6e7d63bb
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
@@ -0,0 +1,43 @@
+---
+title: pack - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div>
+
+## Package pack
+
+### Types
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
+
+### Type Aliases
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website/overloadGroup.kt b/core/testdata/format/website/overloadGroup.kt
new file mode 100644
index 000000000..5bc98e3d4
--- /dev/null
+++ b/core/testdata/format/website/overloadGroup.kt
@@ -0,0 +1,15 @@
+/**
+ * @param spell The text of spell, often distributed on scrolls
+ * @return Spell ID for future casts
+ */
+fun magic(spell: String): Int {
+
+}
+
+/**
+ * @param spell Spell ID of previously casted spell
+ * @return Spell ID for future casts
+ */
+fun magic(spell: Int): Int {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website/overloadGroup.md b/core/testdata/format/website/overloadGroup.md
new file mode 100644
index 000000000..d81f86bfc
--- /dev/null
+++ b/core/testdata/format/website/overloadGroup.md
@@ -0,0 +1,34 @@
+---
+title: magic - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/magic">magic</a></div>
+
+# magic
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+### Parameters
+
+<code>spell</code> - The text of spell, often distributed on scrolls
+
+**Return**
+Spell ID for future casts
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+### Parameters
+
+<code>spell</code> - Spell ID of previously casted spell
+
+**Return**
+Spell ID for future casts
+
+</div>
diff --git a/core/testdata/format/website/returnTag.kt b/core/testdata/format/website/returnTag.kt
new file mode 100644
index 000000000..669c14f90
--- /dev/null
+++ b/core/testdata/format/website/returnTag.kt
@@ -0,0 +1,11 @@
+class Foo
+
+/**
+ * Returns the index within this string of the first occurrence of the specified character, starting from the specified [startIndex].
+ *
+ * @param ignoreCase `true` to ignore character case when matching a character. By default `false`.
+ * @returns An index of the first occurrence of [char] or -1 if none is found.
+ */
+fun Foo.indexOf(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int {
+ return -1
+}
diff --git a/core/testdata/format/website/returnTag.md b/core/testdata/format/website/returnTag.md
new file mode 100644
index 000000000..418babadb
--- /dev/null
+++ b/core/testdata/format/website/returnTag.md
@@ -0,0 +1,20 @@
+---
+title: indexOf - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/index-of">indexOf</a></div>
+
+# indexOf
+
+<div class="signature"><code><span class="keyword">fun </span><a href="test/-foo/index"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.
+
+### Parameters
+
+<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.
+
+**Returns**
+An index of the first occurrence of <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.
+
diff --git a/core/testdata/format/website/sample.kt b/core/testdata/format/website/sample.kt
new file mode 100644
index 000000000..a664c2f52
--- /dev/null
+++ b/core/testdata/format/website/sample.kt
@@ -0,0 +1,16 @@
+/**
+ * Groups elements of the original sequence by the key returned by the given [keySelector] function
+ * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+ * @sample example1
+ */
+fun foo(): Int {
+ return 0
+}
+
+fun foo(i: Int): Int {
+ return 1
+}
+
+fun example1(node: String) = if (true) {
+ println(property)
+}
diff --git a/core/testdata/format/website/sample.md b/core/testdata/format/website/sample.md
new file mode 100644
index 000000000..7c11790f2
--- /dev/null
+++ b/core/testdata/format/website/sample.md
@@ -0,0 +1,29 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+
+``` kotlin
+if (true) {
+ println(property)
+}
+```
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+</div>
diff --git a/core/testdata/functions/annotatedFunction.kt b/core/testdata/functions/annotatedFunction.kt
new file mode 100644
index 000000000..f7abbf6c3
--- /dev/null
+++ b/core/testdata/functions/annotatedFunction.kt
@@ -0,0 +1,2 @@
+@Strictfp fun f() {
+}
diff --git a/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt b/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt
new file mode 100644
index 000000000..e559713ab
--- /dev/null
+++ b/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt
@@ -0,0 +1,7 @@
+@Target(AnnotationTarget.VALUE_PARAMETER)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+public annotation class Fancy(val size: Int)
+
+
+@Fancy(1) fun f() {}
diff --git a/core/testdata/functions/function.kt b/core/testdata/functions/function.kt
new file mode 100644
index 000000000..3ed81dfa5
--- /dev/null
+++ b/core/testdata/functions/function.kt
@@ -0,0 +1,5 @@
+/**
+ * Function fn
+ */
+fun fn() {
+} \ No newline at end of file
diff --git a/core/testdata/functions/functionWithAnnotatedLambdaParam.kt b/core/testdata/functions/functionWithAnnotatedLambdaParam.kt
new file mode 100644
index 000000000..4136e03b3
--- /dev/null
+++ b/core/testdata/functions/functionWithAnnotatedLambdaParam.kt
@@ -0,0 +1,7 @@
+@Target(AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+public annotation class Fancy
+
+fun function(notInlined: @Fancy () -> Unit) {
+}
diff --git a/core/testdata/functions/functionWithAnnotatedParam.kt b/core/testdata/functions/functionWithAnnotatedParam.kt
new file mode 100644
index 000000000..f858e671f
--- /dev/null
+++ b/core/testdata/functions/functionWithAnnotatedParam.kt
@@ -0,0 +1,7 @@
+@Target(AnnotationTarget.VALUE_PARAMETER)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+public annotation class Fancy
+
+fun function(@Fancy notInlined: () -> Unit) {
+}
diff --git a/core/testdata/functions/functionWithDefaultParameter.kt b/core/testdata/functions/functionWithDefaultParameter.kt
new file mode 100644
index 000000000..3a3a102fb
--- /dev/null
+++ b/core/testdata/functions/functionWithDefaultParameter.kt
@@ -0,0 +1 @@
+fun f(x: String = "") {}
diff --git a/core/testdata/functions/functionWithNoinlineParam.kt b/core/testdata/functions/functionWithNoinlineParam.kt
new file mode 100644
index 000000000..640bec837
--- /dev/null
+++ b/core/testdata/functions/functionWithNoinlineParam.kt
@@ -0,0 +1,2 @@
+fun function(noinline notInlined: () -> Unit) {
+}
diff --git a/core/testdata/functions/functionWithNotDocumentedAnnotation.kt b/core/testdata/functions/functionWithNotDocumentedAnnotation.kt
new file mode 100644
index 000000000..3c7e2ff9b
--- /dev/null
+++ b/core/testdata/functions/functionWithNotDocumentedAnnotation.kt
@@ -0,0 +1,2 @@
+@Suppress("FOO") fun f() {
+}
diff --git a/core/testdata/functions/functionWithParams.kt b/core/testdata/functions/functionWithParams.kt
new file mode 100644
index 000000000..85c493689
--- /dev/null
+++ b/core/testdata/functions/functionWithParams.kt
@@ -0,0 +1,8 @@
+/**
+ * Multiline
+ *
+ * Function
+ * Documentation
+ */
+fun function(/** parameter */ x: Int) {
+} \ No newline at end of file
diff --git a/core/testdata/functions/functionWithReceiver.kt b/core/testdata/functions/functionWithReceiver.kt
new file mode 100644
index 000000000..c84732515
--- /dev/null
+++ b/core/testdata/functions/functionWithReceiver.kt
@@ -0,0 +1,11 @@
+/**
+ * Function with receiver
+ */
+fun String.fn() {
+}
+
+/**
+ * Function with receiver
+ */
+fun String.fn(x: Int) {
+}
diff --git a/core/testdata/functions/genericFunction.kt b/core/testdata/functions/genericFunction.kt
new file mode 100644
index 000000000..05a650707
--- /dev/null
+++ b/core/testdata/functions/genericFunction.kt
@@ -0,0 +1,5 @@
+
+/**
+ * generic function
+ */
+private fun <T> generic() {} \ No newline at end of file
diff --git a/core/testdata/functions/genericFunctionWithConstraints.kt b/core/testdata/functions/genericFunctionWithConstraints.kt
new file mode 100644
index 000000000..5f22f8c53
--- /dev/null
+++ b/core/testdata/functions/genericFunctionWithConstraints.kt
@@ -0,0 +1,6 @@
+
+/**
+ * generic function
+ */
+public fun <T : R, R> generic() {
+} \ No newline at end of file
diff --git a/core/testdata/functions/inlineFunction.kt b/core/testdata/functions/inlineFunction.kt
new file mode 100644
index 000000000..11c196721
--- /dev/null
+++ b/core/testdata/functions/inlineFunction.kt
@@ -0,0 +1,2 @@
+inline fun f() {
+}
diff --git a/core/testdata/functions/sinceKotlin.kt b/core/testdata/functions/sinceKotlin.kt
new file mode 100644
index 000000000..cdcd33570
--- /dev/null
+++ b/core/testdata/functions/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Quite useful [String]
+ */
+@SinceKotlin("1.1")
+fun `availableSince1.1`(): String = "1.1 rulezz" \ No newline at end of file
diff --git a/core/testdata/issues/errorClasses.kt b/core/testdata/issues/errorClasses.kt
new file mode 100644
index 000000000..9e966b3a4
--- /dev/null
+++ b/core/testdata/issues/errorClasses.kt
@@ -0,0 +1,20 @@
+
+class Test(var value: String) {
+ fun brokenApply(v: String) = apply { value = v }
+
+ fun brokenRun(v: String) = run {
+ value = v
+ this
+ }
+
+ fun brokenLet(v: String) = let {
+ it.value = v
+ it
+ }
+
+ fun brokenGenerics() = listOf("a", "b", "c")
+
+ fun working(v: String) = doSomething()
+
+ fun doSomething(): String = "Hello"
+} \ No newline at end of file
diff --git a/core/testdata/java/InheritorLinks.java b/core/testdata/java/InheritorLinks.java
new file mode 100644
index 000000000..31658265e
--- /dev/null
+++ b/core/testdata/java/InheritorLinks.java
@@ -0,0 +1,7 @@
+public class InheritorLinks {
+ public static class Foo {
+ }
+
+ public static class Bar extends Foo {
+ }
+}
diff --git a/core/testdata/java/InnerClass.java b/core/testdata/java/InnerClass.java
new file mode 100644
index 000000000..0302da9df
--- /dev/null
+++ b/core/testdata/java/InnerClass.java
@@ -0,0 +1,4 @@
+class InnerClass {
+ public class D {
+ }
+}
diff --git a/core/testdata/java/annotatedAnnotation.java b/core/testdata/java/annotatedAnnotation.java
new file mode 100644
index 000000000..6296e4002
--- /dev/null
+++ b/core/testdata/java/annotatedAnnotation.java
@@ -0,0 +1,6 @@
+import java.lang.annotation.*;
+
+@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
+public @interface Attribute {
+ String value() default "";
+}
diff --git a/core/testdata/java/arrayType.java b/core/testdata/java/arrayType.java
new file mode 100644
index 000000000..dc42a2077
--- /dev/null
+++ b/core/testdata/java/arrayType.java
@@ -0,0 +1,5 @@
+class Test {
+ public String[] arrayToString(int[] data) {
+ return null;
+ }
+}
diff --git a/core/testdata/java/constructors.java b/core/testdata/java/constructors.java
new file mode 100644
index 000000000..6f899d182
--- /dev/null
+++ b/core/testdata/java/constructors.java
@@ -0,0 +1,5 @@
+class Test {
+ public Test() {}
+
+ public Test(String s) {}
+}
diff --git a/core/testdata/java/deprecation.java b/core/testdata/java/deprecation.java
new file mode 100644
index 000000000..be8decd6e
--- /dev/null
+++ b/core/testdata/java/deprecation.java
@@ -0,0 +1,6 @@
+class C {
+ /**
+ * @deprecated This should no longer be used
+ */
+ void fn() {}
+} \ No newline at end of file
diff --git a/core/testdata/java/enumValues.java b/core/testdata/java/enumValues.java
new file mode 100644
index 000000000..d8dda934a
--- /dev/null
+++ b/core/testdata/java/enumValues.java
@@ -0,0 +1,3 @@
+enum E {
+ Foo
+} \ No newline at end of file
diff --git a/core/testdata/java/field.java b/core/testdata/java/field.java
new file mode 100644
index 000000000..d9ae75f90
--- /dev/null
+++ b/core/testdata/java/field.java
@@ -0,0 +1,4 @@
+class Test {
+ public int i;
+ public static final String s;
+}
diff --git a/core/testdata/java/hideAnnotation.java b/core/testdata/java/hideAnnotation.java
new file mode 100644
index 000000000..58b3ac452
--- /dev/null
+++ b/core/testdata/java/hideAnnotation.java
@@ -0,0 +1,27 @@
+public class ShownClass {
+
+ static int shownInt;
+
+ /**
+ * @hide
+ */
+ public static int hiddenInt;
+
+ public static void shownMethod() {
+
+ }
+
+ /**
+ * @hide
+ */
+ public static void hiddenMethod() {
+
+ }
+}
+
+/**
+ * @hide
+ */
+public class HiddenClass {
+
+}
diff --git a/core/testdata/java/javaLangObject.java b/core/testdata/java/javaLangObject.java
new file mode 100644
index 000000000..be3dd5708
--- /dev/null
+++ b/core/testdata/java/javaLangObject.java
@@ -0,0 +1,3 @@
+class Test {
+ public Object fn() { return null; }
+}
diff --git a/core/testdata/java/member.java b/core/testdata/java/member.java
new file mode 100644
index 000000000..d4f4b8d59
--- /dev/null
+++ b/core/testdata/java/member.java
@@ -0,0 +1,11 @@
+class Test {
+ /**
+ * Summary for Function
+ * @param name is String parameter
+ * @param value is int parameter
+ * @author yole
+ */
+ public void fn(String name, int value) {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/java/memberWithModifiers.java b/core/testdata/java/memberWithModifiers.java
new file mode 100644
index 000000000..ea645c218
--- /dev/null
+++ b/core/testdata/java/memberWithModifiers.java
@@ -0,0 +1,12 @@
+public abstract class Test {
+ /**
+ * Summary for Function
+ * @param name is String parameter
+ * @param value is int parameter
+ */
+ protected final void fn(String name, int value) {
+
+ }
+
+ protected void openFn() {}
+} \ No newline at end of file
diff --git a/core/testdata/java/staticMethod.java b/core/testdata/java/staticMethod.java
new file mode 100644
index 000000000..a2ecd7f11
--- /dev/null
+++ b/core/testdata/java/staticMethod.java
@@ -0,0 +1,4 @@
+class C {
+ public static void foo() {
+ }
+}
diff --git a/core/testdata/java/superClass.java b/core/testdata/java/superClass.java
new file mode 100644
index 000000000..31b5fa962
--- /dev/null
+++ b/core/testdata/java/superClass.java
@@ -0,0 +1,2 @@
+public class Foo extends Exception implements Cloneable {
+}
diff --git a/core/testdata/java/suppressTag.java b/core/testdata/java/suppressTag.java
new file mode 100644
index 000000000..c26194c84
--- /dev/null
+++ b/core/testdata/java/suppressTag.java
@@ -0,0 +1,10 @@
+class C {
+ public static void foo() {
+ }
+
+ /**
+ * @suppress
+ */
+ public static void bar() {
+ }
+}
diff --git a/core/testdata/java/typeParameter.java b/core/testdata/java/typeParameter.java
new file mode 100644
index 000000000..5a24b30ac
--- /dev/null
+++ b/core/testdata/java/typeParameter.java
@@ -0,0 +1,3 @@
+class Foo<T extends Comparable<T>> {
+ public <E> E foo();
+}
diff --git a/core/testdata/java/varargs.java b/core/testdata/java/varargs.java
new file mode 100644
index 000000000..d184564e8
--- /dev/null
+++ b/core/testdata/java/varargs.java
@@ -0,0 +1,3 @@
+class Foo {
+ public void bar(String... x);
+}
diff --git a/core/testdata/javadoc/blankLineInsideCodeBlock.kt b/core/testdata/javadoc/blankLineInsideCodeBlock.kt
new file mode 100644
index 000000000..9430f4d58
--- /dev/null
+++ b/core/testdata/javadoc/blankLineInsideCodeBlock.kt
@@ -0,0 +1,12 @@
+/**
+ * ```
+ * This is a test
+ * of Dokka's code blocks.
+ * Here is a blank line.
+ *
+ * The previous line was blank.
+ * ```
+ */
+fun u() {
+
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/bytearr.kt b/core/testdata/javadoc/bytearr.kt
new file mode 100644
index 000000000..84be1a706
--- /dev/null
+++ b/core/testdata/javadoc/bytearr.kt
@@ -0,0 +1,7 @@
+package foo
+
+class ByteArray {
+ fun foo(): IntArray {
+ return intArrayOf()
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/companionMethodReference.kt b/core/testdata/javadoc/companionMethodReference.kt
new file mode 100644
index 000000000..499e4492f
--- /dev/null
+++ b/core/testdata/javadoc/companionMethodReference.kt
@@ -0,0 +1,13 @@
+package foo
+
+
+/**
+ * Linking to [test]
+ */
+class TestClass {
+
+ companion object {
+
+ @JvmStatic fun test(arg: String) {}
+ }
+} \ 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 000000000..5a6cdd77c
--- /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/exception.kt b/core/testdata/javadoc/exception.kt
new file mode 100644
index 000000000..ec0a5bbb6
--- /dev/null
+++ b/core/testdata/javadoc/exception.kt
@@ -0,0 +1,5 @@
+package foo
+
+class MyException : Exception {
+ fun foo() = ""
+}
diff --git a/core/testdata/javadoc/internal.kt b/core/testdata/javadoc/internal.kt
new file mode 100644
index 000000000..a57ea3b41
--- /dev/null
+++ b/core/testdata/javadoc/internal.kt
@@ -0,0 +1,8 @@
+package foo
+
+data class Person internal constructor(
+ val name: String = "",
+ val age: Int = 0
+) {
+ constructor(age: Int): this("", age)
+}
diff --git a/core/testdata/javadoc/jvmname.kt b/core/testdata/javadoc/jvmname.kt
new file mode 100644
index 000000000..e4774cd67
--- /dev/null
+++ b/core/testdata/javadoc/jvmname.kt
@@ -0,0 +1,6 @@
+package foo
+
+class Apple {
+ @get:JvmName("_tree")
+ internal val source: Tree
+}
diff --git a/core/testdata/javadoc/kdocKeywordsOnMethod.kt b/core/testdata/javadoc/kdocKeywordsOnMethod.kt
new file mode 100644
index 000000000..df5bbbe0f
--- /dev/null
+++ b/core/testdata/javadoc/kdocKeywordsOnMethod.kt
@@ -0,0 +1,12 @@
+class FireException : Exception
+
+
+/**
+ * COMM
+ * @param a Some string
+ * @return value of a
+ * @throws FireException in case of fire
+ */
+@Throws(FireException::class)
+fun my(a: String): String = a
+
diff --git a/core/testdata/javadoc/obj.kt b/core/testdata/javadoc/obj.kt
new file mode 100644
index 000000000..1d10a4228
--- /dev/null
+++ b/core/testdata/javadoc/obj.kt
@@ -0,0 +1,7 @@
+package foo
+
+class O {
+ companion object {
+
+ }
+}
diff --git a/core/testdata/javadoc/paramlink.kt b/core/testdata/javadoc/paramlink.kt
new file mode 100644
index 000000000..48972a22d
--- /dev/null
+++ b/core/testdata/javadoc/paramlink.kt
@@ -0,0 +1,10 @@
+package demo
+
+/**
+ * You can [eat] it or cut it into slices using [cutIntoPieces]
+ */
+interface Apple {
+ fun eat()
+
+ fun cutIntoPieces(pieces: Int)
+}
diff --git a/core/testdata/javadoc/stringarr.kt b/core/testdata/javadoc/stringarr.kt
new file mode 100644
index 000000000..d6cd9dea3
--- /dev/null
+++ b/core/testdata/javadoc/stringarr.kt
@@ -0,0 +1,8 @@
+package foo
+
+class Foo {
+ companion object {
+ @JvmStatic fun main(args: Array<String>) {
+ }
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/suppress.kt b/core/testdata/javadoc/suppress.kt
new file mode 100644
index 000000000..90f6c131d
--- /dev/null
+++ b/core/testdata/javadoc/suppress.kt
@@ -0,0 +1,37 @@
+/**
+ * @suppress
+ */
+class Some {
+
+}
+
+
+/**
+ * @suppress
+ * @author me
+ * @see other
+ */
+class SomeAgain {
+
+}
+
+class Same {
+ /**
+ * @suppress
+ */
+ fun privateApi() {
+
+ }
+
+ /**
+ * @suppress
+ */
+ val privateForSomeReason = ""
+}
+
+/**
+ * @suppress
+ */
+interface Interface {
+
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/typealiases.kt b/core/testdata/javadoc/typealiases.kt
new file mode 100644
index 000000000..bb09bfada
--- /dev/null
+++ b/core/testdata/javadoc/typealiases.kt
@@ -0,0 +1,11 @@
+class A
+
+typealias B = A
+
+class C : B
+
+typealias D = (A) -> C
+
+fun some(d: D) {
+
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/types.kt b/core/testdata/javadoc/types.kt
new file mode 100644
index 000000000..55be60587
--- /dev/null
+++ b/core/testdata/javadoc/types.kt
@@ -0,0 +1,4 @@
+package foo
+
+fun foo(x: Int, o: Any): String {
+}
diff --git a/core/testdata/links/linkToConstantWithUnderscores.kt b/core/testdata/links/linkToConstantWithUnderscores.kt
new file mode 100644
index 000000000..57011bfa1
--- /dev/null
+++ b/core/testdata/links/linkToConstantWithUnderscores.kt
@@ -0,0 +1,8 @@
+/**
+ * This is link to [MY_CONSTANT_VALUE]
+ */
+class Foo {
+ companion object {
+ val MY_CONSTANT_VALUE = 0
+ }
+} \ No newline at end of file
diff --git a/core/testdata/links/linkToJDK.kt b/core/testdata/links/linkToJDK.kt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/core/testdata/links/linkToJDK.kt
diff --git a/core/testdata/links/linkToMember.kt b/core/testdata/links/linkToMember.kt
new file mode 100644
index 000000000..b60eaedbe
--- /dev/null
+++ b/core/testdata/links/linkToMember.kt
@@ -0,0 +1,6 @@
+/**
+ * This is link to [member]
+ */
+class Foo {
+ fun member() {}
+} \ No newline at end of file
diff --git a/core/testdata/links/linkToPackage.kt b/core/testdata/links/linkToPackage.kt
new file mode 100644
index 000000000..3dd866288
--- /dev/null
+++ b/core/testdata/links/linkToPackage.kt
@@ -0,0 +1,8 @@
+package test.magic
+
+/**
+ * Basic implementations of [Magic] are located in [test.magic] package
+ */
+abstract class Magic {
+
+}
diff --git a/core/testdata/links/linkToParam.kt b/core/testdata/links/linkToParam.kt
new file mode 100644
index 000000000..ca42a7428
--- /dev/null
+++ b/core/testdata/links/linkToParam.kt
@@ -0,0 +1,5 @@
+/**
+ * This is link to [param]
+ */
+fun Foo(param: String) {
+} \ No newline at end of file
diff --git a/core/testdata/links/linkToQualifiedMember.kt b/core/testdata/links/linkToQualifiedMember.kt
new file mode 100644
index 000000000..22c154fe8
--- /dev/null
+++ b/core/testdata/links/linkToQualifiedMember.kt
@@ -0,0 +1,6 @@
+/**
+ * This is link to [Foo.member]
+ */
+class Foo {
+ fun member() {}
+} \ No newline at end of file
diff --git a/core/testdata/links/linkToSelf.kt b/core/testdata/links/linkToSelf.kt
new file mode 100644
index 000000000..74395f0ff
--- /dev/null
+++ b/core/testdata/links/linkToSelf.kt
@@ -0,0 +1,6 @@
+/**
+ * This is link to [Foo]
+ */
+class Foo {
+
+} \ No newline at end of file
diff --git a/core/testdata/markdown/spec.txt b/core/testdata/markdown/spec.txt
new file mode 100644
index 000000000..fce879240
--- /dev/null
+++ b/core/testdata/markdown/spec.txt
@@ -0,0 +1,6150 @@
+---
+title: CommonMark Spec
+author:
+- John MacFarlane
+version: 2
+date: 2014-09-19
+...
+
+# Introduction
+
+## What is Markdown?
+
+Markdown is a plain text format for writing structured documents,
+based on conventions used for indicating formatting in email and
+usenet posts. It was developed in 2004 by John Gruber, who wrote
+the first Markdown-to-HTML converter in perl, and it soon became
+widely used in websites. By 2014 there were dozens of
+implementations in many languages. Some of them extended basic
+Markdown syntax with conventions for footnotes, definition lists,
+tables, and other constructs, and some allowed output not just in
+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)
+does not specify the syntax unambiguously. Here are some examples of
+questions it does not answer:
+
+1. How much indentation is needed for a sublist? The spec says that
+ continuation paragraphs need to be indented four spaces, but is
+ not fully explicit about sublists. It is natural to think that
+ they, too, must be indented four spaces, but `Markdown.pl` does
+ 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).)
+
+2. Is a blank line needed before a block quote or header?
+ Most implementations do not require the blank line. However,
+ this can lead to unexpected results in hard-wrapped text, and
+ 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).)
+
+3. Is a blank line needed before an indented code block?
+ (`Markdown.pl` requires it, but this is not mentioned in the
+ documentation, and some implementations do not require it.)
+
+ ``` markdown
+ paragraph
+ code?
+ ```
+
+4. What is the exact rule for determining when list items get
+ wrapped in `<p>` tags? Can a list be partially "loose" and partially
+ "tight"? What should we do with a list like this?
+
+ ``` markdown
+ 1. one
+
+ 2. two
+ 3. three
+ ```
+
+ Or this?
+
+ ``` markdown
+ 1. one
+ - a
+
+ - b
+ 2. two
+ ```
+
+ (There are some relevant comments by John Gruber
+ [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
+
+5. Can list markers be indented? Can ordered list markers be right-aligned?
+
+ ``` markdown
+ 8. item 1
+ 9. item 2
+ 10. item 2a
+ ```
+
+6. Is this one list with a horizontal rule in its second item,
+ or two lists separated by a horizontal rule?
+
+ ``` markdown
+ * a
+ * * * * *
+ * b
+ ```
+
+7. When list markers change from numbers to bullets, do we have
+ two lists or one? (The Markdown syntax description suggests two,
+ but the perl scripts and many other implementations produce one.)
+
+ ``` markdown
+ 1. fee
+ 2. fie
+ - foe
+ - fum
+ ```
+
+8. What are the precedence rules for the markers of inline structure?
+ For example, is the following a valid link, or does the code span
+ take precedence ?
+
+ ``` markdown
+ [a backtick (`)](/url) and [another backtick (`)](/url).
+ ```
+
+9. What are the precedence rules for markers of emphasis and strong
+ emphasis? For example, how should the following be parsed?
+
+ ``` markdown
+ *foo *bar* baz*
+ ```
+
+10. What are the precedence rules between block-level and inline-level
+ structure? For example, how should the following be parsed?
+
+ ``` markdown
+ - `a long code span can contain a hyphen like this
+ - and it can screw things up`
+ ```
+
+11. Can list items include headers? (`Markdown.pl` does not allow this,
+ but headers can occur in blockquotes.)
+
+ ``` markdown
+ - # Heading
+ ```
+
+12. Can link references be defined inside block quotes or list items?
+
+ ``` markdown
+ > Blockquote [foo].
+ >
+ > [foo]: /url
+ ```
+
+13. If there are multiple definitions for the same reference, which takes
+ precedence?
+
+ ``` markdown
+ [foo]: /url1
+ [foo]: /url2
+
+ [foo][]
+ ```
+
+In the absence of a spec, early implementers consulted `Markdown.pl`
+to resolve these ambiguities. But `Markdown.pl` was quite buggy, and
+gave manifestly bad results in many cases, so it was not a
+satisfactory replacement for a spec.
+
+Because there is no unambiguous spec, implementations have diverged
+considerably. As a result, users are often surprised to find that
+a document that renders one way on one system (say, a github wiki)
+renders differently on another (say, converting to docbook using
+pandoc). To make matters worse, because nothing in Markdown counts
+as a "syntax error," the divergence often isn't discovered right away.
+
+## About this document
+
+This document attempts to specify Markdown syntax unambiguously.
+It contains many examples with side-by-side Markdown and
+HTML. These are intended to double as conformance tests. An
+accompanying script `runtests.pl` can be used to run the tests
+against any Markdown program:
+
+ perl runtests.pl spec.txt PROGRAM
+
+Since this document describes how Markdown is to be parsed into
+an abstract syntax tree, it would have made sense to use an abstract
+representation of the syntax tree instead of HTML. But HTML is capable
+of representing the structural distinctions we need to make, and the
+choice of HTML for the tests makes it possible to run the tests against
+an implementation without writing an abstract syntax tree renderer.
+
+This document is generated from a text file, `spec.txt`, written
+in Markdown with a small extension for the side-by-side tests.
+The script `spec2md.pl` can be used to turn `spec.txt` into pandoc
+Markdown, which can then be converted into other formats.
+
+In the examples, the `→` character is used to represent tabs.
+
+# Preprocessing
+
+A [line](#line) <a id="line"></a>
+is a sequence of zero or more characters followed by a line
+ending (CR, LF, or CRLF) or by the end of
+file.
+
+This spec does not specify an encoding; it thinks of lines as composed
+of characters rather than bytes. A conforming parser may be limited
+to a certain encoding.
+
+Tabs in lines are expanded to spaces, with a tab stop of 4 characters:
+
+.
+→foo→baz→→bim
+.
+<pre><code>foo baz bim
+</code></pre>
+.
+
+.
+ a→a
+ ὐ→a
+.
+<pre><code>a a
+ὐ a
+</code></pre>
+.
+
+Line endings are replaced by newline characters (LF).
+
+A line containing no characters, or a line containing only spaces (after
+tab expansion), is called a [blank line](#blank-line).
+<a id="blank-line"></a>
+
+# Blocks and inlines
+
+We can think of a document as a sequence of [blocks](#block)<a
+id="block"></a>---structural elements like paragraphs, block quotations,
+lists, headers, rules, and code blocks. Blocks can contain other
+blocks, or they can contain [inline](#inline)<a id="inline"></a> content:
+words, spaces, links, emphasized text, images, and inline code.
+
+## Precedence
+
+Indicators of block structure always take precedence over indicators
+of inline structure. So, for example, the following is a list with
+two items, not a list with one item containing a code span:
+
+.
+- `one
+- two`
+.
+<ul>
+<li>`one</li>
+<li>two`</li>
+</ul>
+.
+
+This means that parsing can proceed in two steps: first, the block
+structure of the document can be discerned; second, text lines inside
+paragraphs, headers, and other block constructs can be parsed for inline
+structure. The second step requires information about link reference
+definitions that will be available only at the end of the first
+step. Note that the first step requires processing lines in sequence,
+but the second can be parallelized, since the inline parsing of
+one block element does not affect the inline parsing of any other.
+
+## Container blocks and leaf blocks
+
+We can divide blocks into two types:
+[container blocks](#container-block), <a id="container-block"></a>
+which can contain other blocks, and [leaf blocks](#leaf-block),
+<a id="leaf-block"></a> which cannot.
+
+# Leaf blocks
+
+This section describes the different kinds of leaf block that make up a
+Markdown document.
+
+## Horizontal rules
+
+A line consisting of 0-3 spaces of indentation, followed by a sequence
+of three or more matching `-`, `_`, or `*` characters, each followed
+optionally by any number of spaces, forms a [horizontal
+rule](#horizontal-rule). <a id="horizontal-rule"></a>
+
+.
+***
+---
+___
+.
+<hr />
+<hr />
+<hr />
+.
+
+Wrong characters:
+
+.
++++
+.
+<p>+++</p>
+.
+
+.
+===
+.
+<p>===</p>
+.
+
+Not enough characters:
+
+.
+--
+**
+__
+.
+<p>--
+**
+__</p>
+.
+
+One to three spaces indent are allowed:
+
+.
+ ***
+ ***
+ ***
+.
+<hr />
+<hr />
+<hr />
+.
+
+Four spaces is too many:
+
+.
+ ***
+.
+<pre><code>***
+</code></pre>
+.
+
+.
+Foo
+ ***
+.
+<p>Foo
+***</p>
+.
+
+More than three characters may be used:
+
+.
+_____________________________________
+.
+<hr />
+.
+
+Spaces are allowed between the characters:
+
+.
+ - - -
+.
+<hr />
+.
+
+.
+ ** * ** * ** * **
+.
+<hr />
+.
+
+.
+- - - -
+.
+<hr />
+.
+
+Spaces are allowed at the end:
+
+.
+- - - -
+.
+<hr />
+.
+
+However, no other characters may occur at the end or the
+beginning:
+
+.
+_ _ _ _ a
+
+a------
+.
+<p>_ _ _ _ a</p>
+<p>a------</p>
+.
+
+It is required that all of the non-space characters be the same.
+So, this is not a horizontal rule:
+
+.
+ *-*
+.
+<p><em>-</em></p>
+.
+
+Horizontal rules do not need blank lines before or after:
+
+.
+- foo
+***
+- bar
+.
+<ul>
+<li>foo</li>
+</ul>
+<hr />
+<ul>
+<li>bar</li>
+</ul>
+.
+
+Horizontal rules can interrupt a paragraph:
+
+.
+Foo
+***
+bar
+.
+<p>Foo</p>
+<hr />
+<p>bar</p>
+.
+
+Note, however, that this is a setext header, not a paragraph followed
+by a horizontal rule:
+
+.
+Foo
+---
+bar
+.
+<h2>Foo</h2>
+<p>bar</p>
+.
+
+When both a horizontal rule and a list item are possible
+interpretations of a line, the horizontal rule is preferred:
+
+.
+* Foo
+* * *
+* Bar
+.
+<ul>
+<li>Foo</li>
+</ul>
+<hr />
+<ul>
+<li>Bar</li>
+</ul>
+.
+
+If you want a horizontal rule in a list item, use a different bullet:
+
+.
+- Foo
+- * * *
+.
+<ul>
+<li>Foo</li>
+<li><hr /></li>
+</ul>
+.
+
+## ATX headers
+
+An [ATX header](#atx-header) <a id="atx-header"></a>
+consists of a string of characters, parsed as inline content, between an
+opening sequence of 1--6 unescaped `#` characters and an optional
+closing sequence of any number of `#` characters. The opening sequence
+of `#` characters cannot be followed directly by a nonspace character.
+The closing `#` characters may be followed by spaces only. The opening
+`#` character may be indented 0-3 spaces. The raw contents of the
+header are stripped of leading and trailing spaces before being parsed
+as inline content. The header level is equal to the number of `#`
+characters in the opening sequence.
+
+Simple headers:
+
+.
+# foo
+## foo
+### foo
+#### foo
+##### foo
+###### foo
+.
+<h1>foo</h1>
+<h2>foo</h2>
+<h3>foo</h3>
+<h4>foo</h4>
+<h5>foo</h5>
+<h6>foo</h6>
+.
+
+More than six `#` characters is not a header:
+
+.
+####### foo
+.
+<p>####### foo</p>
+.
+
+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
+prevent things like the following from being parsed as headers:
+
+.
+#5 bolt
+.
+<p>#5 bolt</p>
+.
+
+This is not a header, because the first `#` is escaped:
+
+.
+\## foo
+.
+<p>## foo</p>
+.
+
+Contents are parsed as inlines:
+
+.
+# foo *bar* \*baz\*
+.
+<h1>foo <em>bar</em> *baz*</h1>
+.
+
+Leading and trailing blanks are ignored in parsing inline content:
+
+.
+# foo
+.
+<h1>foo</h1>
+.
+
+One to three spaces indentation are allowed:
+
+.
+ ### foo
+ ## foo
+ # foo
+.
+<h3>foo</h3>
+<h2>foo</h2>
+<h1>foo</h1>
+.
+
+Four spaces are too much:
+
+.
+ # foo
+.
+<pre><code># foo
+</code></pre>
+.
+
+.
+foo
+ # bar
+.
+<p>foo
+# bar</p>
+.
+
+A closing sequence of `#` characters is optional:
+
+.
+## foo ##
+ ### bar ###
+.
+<h2>foo</h2>
+<h3>bar</h3>
+.
+
+It need not be the same length as the opening sequence:
+
+.
+# foo ##################################
+##### foo ##
+.
+<h1>foo</h1>
+<h5>foo</h5>
+.
+
+Spaces are allowed after the closing sequence:
+
+.
+### foo ###
+.
+<h3>foo</h3>
+.
+
+A sequence of `#` characters with a nonspace character following it
+is not a closing sequence, but counts as part of the contents of the
+header:
+
+.
+### foo ### b
+.
+<h3>foo ### b</h3>
+.
+
+Backslash-escaped `#` characters do not count as part
+of the closing sequence:
+
+.
+### foo \###
+## foo \#\##
+# foo \#
+.
+<h3>foo #</h3>
+<h2>foo ##</h2>
+<h1>foo #</h1>
+.
+
+ATX headers need not be separated from surrounding content by blank
+lines, and they can interrupt paragraphs:
+
+.
+****
+## foo
+****
+.
+<hr />
+<h2>foo</h2>
+<hr />
+.
+
+.
+Foo bar
+# baz
+Bar foo
+.
+<p>Foo bar</p>
+<h1>baz</h1>
+<p>Bar foo</p>
+.
+
+ATX headers can be empty:
+
+.
+##
+#
+### ###
+.
+<h2></h2>
+<h1></h1>
+<h3></h3>
+.
+
+## Setext headers
+
+A [setext header](#setext-header) <a id="setext-header"></a>
+consists of a line of text, containing at least one nonspace character,
+with no more than 3 spaces indentation, followed by a [setext header
+underline](#setext-header-underline). A [setext header
+underline](#setext-header-underline) <a id="setext-header-underline"></a>
+is a sequence of `=` characters or a sequence of `-` characters, with no
+more than 3 spaces indentation and any number of trailing
+spaces. The header is a level 1 header if `=` characters are used, and
+a level 2 header if `-` characters are used. The contents of the header
+are the result of parsing the first line as Markdown inline content.
+
+In general, a setext header need not be preceded or followed by a
+blank line. However, it cannot interrupt a paragraph, so when a
+setext header comes after a paragraph, a blank line is needed between
+them.
+
+Simple examples:
+
+.
+Foo *bar*
+=========
+
+Foo *bar*
+---------
+.
+<h1>Foo <em>bar</em></h1>
+<h2>Foo <em>bar</em></h2>
+.
+
+The underlining can be any length:
+
+.
+Foo
+-------------------------
+
+Foo
+=
+.
+<h2>Foo</h2>
+<h1>Foo</h1>
+.
+
+The header content can be indented up to three spaces, and need
+not line up with the underlining:
+
+.
+ Foo
+---
+
+ Foo
+-----
+
+ Foo
+ ===
+.
+<h2>Foo</h2>
+<h2>Foo</h2>
+<h1>Foo</h1>
+.
+
+Four spaces indent is too much:
+
+.
+ Foo
+ ---
+
+ Foo
+---
+.
+<pre><code>Foo
+---
+
+Foo
+</code></pre>
+<hr />
+.
+
+The setext header underline can be indented up to three spaces, and
+may have trailing spaces:
+
+.
+Foo
+ ----
+.
+<h2>Foo</h2>
+.
+
+Four spaces is too much:
+
+.
+Foo
+ ---
+.
+<p>Foo
+---</p>
+.
+
+The setext header underline cannot contain internal spaces:
+
+.
+Foo
+= =
+
+Foo
+--- -
+.
+<p>Foo
+= =</p>
+<p>Foo</p>
+<hr />
+.
+
+Trailing spaces in the content line do not cause a line break:
+
+.
+Foo
+-----
+.
+<h2>Foo</h2>
+.
+
+Nor does a backslash at the end:
+
+.
+Foo\
+----
+.
+<h2>Foo\</h2>
+.
+
+Since indicators of block structure take precedence over
+indicators of inline structure, the following are setext headers:
+
+.
+`Foo
+----
+`
+
+<a title="a lot
+---
+of dashes"/>
+.
+<h2>`Foo</h2>
+<p>`</p>
+<h2>&lt;a title=&quot;a lot</h2>
+<p>of dashes&quot;/&gt;</p>
+.
+
+The setext header underline cannot be a lazy line:
+
+.
+> Foo
+---
+.
+<blockquote>
+<p>Foo</p>
+</blockquote>
+<hr />
+.
+
+A setext header cannot interrupt a paragraph:
+
+.
+Foo
+Bar
+---
+
+Foo
+Bar
+===
+.
+<p>Foo
+Bar</p>
+<hr />
+<p>Foo
+Bar
+===</p>
+.
+
+But in general a blank line is not required before or after:
+
+.
+---
+Foo
+---
+Bar
+---
+Baz
+.
+<hr />
+<h2>Foo</h2>
+<h2>Bar</h2>
+<p>Baz</p>
+.
+
+Setext headers cannot be empty:
+
+.
+
+====
+.
+<p>====</p>
+.
+
+
+## Indented code blocks
+
+An [indented code block](#indented-code-block)
+<a id="indented-code-block"></a> is composed of one or more
+[indented chunks](#indented-chunk) separated by blank lines.
+An [indented chunk](#indented-chunk) <a id="indented-chunk"></a>
+is a sequence of non-blank lines, each indented four or more
+spaces. An indented code block cannot interrupt a paragraph, so
+if it occurs before or after a paragraph, there must be an
+intervening blank line. The contents of the code block are
+the literal contents of the lines, including trailing newlines,
+minus four spaces of indentation. An indented code block has no
+attributes.
+
+.
+ a simple
+ indented code block
+.
+<pre><code>a simple
+ indented code block
+</code></pre>
+.
+
+The contents are literal text, and do not get parsed as Markdown:
+
+.
+ <a/>
+ *hi*
+
+ - one
+.
+<pre><code>&lt;a/&gt;
+*hi*
+
+- one
+</code></pre>
+.
+
+Here we have three chunks separated by blank lines:
+
+.
+ chunk1
+
+ chunk2
+
+
+
+ chunk3
+.
+<pre><code>chunk1
+
+chunk2
+
+
+
+chunk3
+</code></pre>
+.
+
+Any initial spaces beyond four will be included in the content, even
+in interior blank lines:
+
+.
+ chunk1
+
+ chunk2
+.
+<pre><code>chunk1
+
+ chunk2
+</code></pre>
+.
+
+An indented code block cannot interrupt a paragraph. (This
+allows hanging indents and the like.)
+
+.
+Foo
+ bar
+
+.
+<p>Foo
+bar</p>
+.
+
+However, any non-blank line with fewer than four leading spaces ends
+the code block immediately. So a paragraph may occur immediately
+after indented code:
+
+.
+ foo
+bar
+.
+<pre><code>foo
+</code></pre>
+<p>bar</p>
+.
+
+And indented code can occur immediately before and after other kinds of
+blocks:
+
+.
+# Header
+ foo
+Header
+------
+ foo
+----
+.
+<h1>Header</h1>
+<pre><code>foo
+</code></pre>
+<h2>Header</h2>
+<pre><code>foo
+</code></pre>
+<hr />
+.
+
+The first line can be indented more than four spaces:
+
+.
+ foo
+ bar
+.
+<pre><code> foo
+bar
+</code></pre>
+.
+
+Blank lines preceding or following an indented code block
+are not included in it:
+
+.
+
+
+ foo
+
+
+.
+<pre><code>foo
+</code></pre>
+.
+
+Trailing spaces are included in the code block's content:
+
+.
+ foo
+.
+<pre><code>foo
+</code></pre>
+.
+
+
+## Fenced code blocks
+
+A [code fence](#code-fence) <a id="code-fence"></a> is a sequence
+of at least three consecutive backtick characters (`` ` ``) or
+tildes (`~`). (Tildes and backticks cannot be mixed.)
+A [fenced code block](#fenced-code-block) <a id="fenced-code-block"></a>
+begins with a code fence, indented no more than three spaces.
+
+The line with the opening code fence may optionally contain some text
+following the code fence; this is trimmed of leading and trailing
+spaces and called the [info string](#info-string).
+<a id="info-string"></a> The info string may not contain any backtick
+characters. (The reason for this restriction is that otherwise
+some inline code would be incorrectly interpreted as the
+beginning of a fenced code block.)
+
+The content of the code block consists of all subsequent lines, until
+a closing [code fence](#code-fence) of the same type as the code block
+began with (backticks or tildes), and with at least as many backticks
+or tildes as the opening code fence. If the leading code fence is
+indented N spaces, then up to N spaces of indentation are removed from
+each line of the content (if present). (If a content line is not
+indented, it is preserved unchanged. If it is indented less than N
+spaces, all of the indentation is removed.)
+
+The closing code fence may be indented up to three spaces, and may be
+followed only by spaces, which are ignored. If the end of the
+containing block (or document) is reached and no closing code fence
+has been found, the code block contains all of the lines after the
+opening code fence until the end of the containing block (or
+document). (An alternative spec would require backtracking in the
+event that a closing code fence is not found. But this makes parsing
+much less efficient, and there seems to be no real down side to the
+behavior described here.)
+
+A fenced code block may interrupt a paragraph, and does not require
+a blank line either before or after.
+
+The content of a code fence is treated as literal text, not parsed
+as inlines. The first word of the info string is typically used to
+specify the language of the code sample, and rendered in the `class`
+attribute of the `code` tag. However, this spec does not mandate any
+particular treatment of the info string.
+
+Here is a simple example with backticks:
+
+.
+```
+<
+ >
+```
+.
+<pre><code>&lt;
+ &gt;
+</code></pre>
+.
+
+With tildes:
+
+.
+~~~
+<
+ >
+~~~
+.
+<pre><code>&lt;
+ &gt;
+</code></pre>
+.
+
+The closing code fence must use the same character as the opening
+fence:
+
+.
+```
+aaa
+~~~
+```
+.
+<pre><code>aaa
+~~~
+</code></pre>
+.
+
+.
+~~~
+aaa
+```
+~~~
+.
+<pre><code>aaa
+```
+</code></pre>
+.
+
+The closing code fence must be at least as long as the opening fence:
+
+.
+````
+aaa
+```
+``````
+.
+<pre><code>aaa
+```
+</code></pre>
+.
+
+.
+~~~~
+aaa
+~~~
+~~~~
+.
+<pre><code>aaa
+~~~
+</code></pre>
+.
+
+Unclosed code blocks are closed by the end of the document:
+
+.
+```
+.
+<pre><code></code></pre>
+.
+
+.
+`````
+
+```
+aaa
+.
+<pre><code>
+```
+aaa
+</code></pre>
+.
+
+A code block can have all empty lines as its content:
+
+.
+```
+
+
+```
+.
+<pre><code>
+
+</code></pre>
+.
+
+A code block can be empty:
+
+.
+```
+```
+.
+<pre><code></code></pre>
+.
+
+Fences can be indented. If the opening fence is indented,
+content lines will have equivalent opening indentation removed,
+if present:
+
+.
+ ```
+ aaa
+aaa
+```
+.
+<pre><code>aaa
+aaa
+</code></pre>
+.
+
+.
+ ```
+aaa
+ aaa
+aaa
+ ```
+.
+<pre><code>aaa
+aaa
+aaa
+</code></pre>
+.
+
+.
+ ```
+ aaa
+ aaa
+ aaa
+ ```
+.
+<pre><code>aaa
+ aaa
+aaa
+</code></pre>
+.
+
+Four spaces indentation produces an indented code block:
+
+.
+ ```
+ aaa
+ ```
+.
+<pre><code>```
+aaa
+```
+</code></pre>
+.
+
+Code fences (opening and closing) cannot contain internal spaces:
+
+.
+``` ```
+aaa
+.
+<p><code></code>
+aaa</p>
+.
+
+.
+~~~~~~
+aaa
+~~~ ~~
+.
+<pre><code>aaa
+~~~ ~~
+</code></pre>
+.
+
+Fenced code blocks can interrupt paragraphs, and can be followed
+directly by paragraphs, without a blank line between:
+
+.
+foo
+```
+bar
+```
+baz
+.
+<p>foo</p>
+<pre><code>bar
+</code></pre>
+<p>baz</p>
+.
+
+Other blocks can also occur before and after fenced code blocks
+without an intervening blank line:
+
+.
+foo
+---
+~~~
+bar
+~~~
+# baz
+.
+<h2>foo</h2>
+<pre><code>bar
+</code></pre>
+<h1>baz</h1>
+.
+
+An [info string](#info-string) can be provided after the opening code fence.
+Opening and closing spaces will be stripped, and the first word, prefixed
+with `language-`, is used as the value for the `class` attribute of the
+`code` element within the enclosing `pre` element.
+
+.
+```ruby
+def foo(x)
+ return 3
+end
+```
+.
+<pre><code class="language-ruby">def foo(x)
+ return 3
+end
+</code></pre>
+.
+
+.
+~~~~ ruby startline=3 $%@#$
+def foo(x)
+ return 3
+end
+~~~~~~~
+.
+<pre><code class="language-ruby">def foo(x)
+ return 3
+end
+</code></pre>
+.
+
+.
+````;
+````
+.
+<pre><code class="language-;"></code></pre>
+.
+
+Info strings for backtick code blocks cannot contain backticks:
+
+.
+``` aa ```
+foo
+.
+<p><code>aa</code>
+foo</p>
+.
+
+Closing code fences cannot have info strings:
+
+.
+```
+``` aaa
+```
+.
+<pre><code>``` aaa
+</code></pre>
+.
+
+
+## HTML blocks
+
+An [HTML block tag](#html-block-tag) <a id="html-block-tag"></a> is
+an [open tag](#open-tag) or [closing tag](#closing-tag) whose tag
+name is one of the following (case-insensitive):
+`article`, `header`, `aside`, `hgroup`, `blockquote`, `hr`, `iframe`,
+`body`, `li`, `map`, `button`, `object`, `canvas`, `ol`, `caption`,
+`output`, `col`, `p`, `colgroup`, `pre`, `dd`, `progress`, `div`,
+`section`, `dl`, `table`, `td`, `dt`, `tbody`, `embed`, `textarea`,
+`fieldset`, `tfoot`, `figcaption`, `th`, `figure`, `thead`, `footer`,
+`footer`, `tr`, `form`, `ul`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`,
+`video`, `script`, `style`.
+
+An [HTML block](#html-block) <a id="html-block"></a> begins with an
+[HTML block tag](#html-block-tag), [HTML comment](#html-comment),
+[processing instruction](#processing-instruction),
+[declaration](#declaration), or [CDATA section](#cdata-section).
+It ends when a [blank line](#blank-line) or the end of the
+input is encountered. The initial line may be indented up to three
+spaces, and subsequent lines may have any indentation. The contents
+of the HTML block are interpreted as raw HTML, and will not be escaped
+in HTML output.
+
+Some simple examples:
+
+.
+<table>
+ <tr>
+ <td>
+ hi
+ </td>
+ </tr>
+</table>
+
+okay.
+.
+<table>
+ <tr>
+ <td>
+ hi
+ </td>
+ </tr>
+</table>
+<p>okay.</p>
+.
+
+.
+ <div>
+ *hello*
+ <foo><a>
+.
+ <div>
+ *hello*
+ <foo><a>
+.
+
+Here we have two code blocks with a Markdown paragraph between them:
+
+.
+<DIV CLASS="foo">
+
+*Markdown*
+
+</DIV>
+.
+<DIV CLASS="foo">
+<p><em>Markdown</em></p>
+</DIV>
+.
+
+In the following example, what looks like a Markdown code block
+is actually part of the HTML block, which continues until a blank
+line or the end of the document is reached:
+
+.
+<div></div>
+``` c
+int x = 33;
+```
+.
+<div></div>
+``` c
+int x = 33;
+```
+.
+
+A comment:
+
+.
+<!-- Foo
+bar
+ baz -->
+.
+<!-- Foo
+bar
+ baz -->
+.
+
+A processing instruction:
+
+.
+<?php
+ echo 'foo'
+?>
+.
+<?php
+ echo 'foo'
+?>
+.
+
+CDATA:
+
+.
+<![CDATA[
+function matchwo(a,b)
+{
+if (a < b && a < 0) then
+ {
+ return 1;
+ }
+else
+ {
+ return 0;
+ }
+}
+]]>
+.
+<![CDATA[
+function matchwo(a,b)
+{
+if (a < b && a < 0) then
+ {
+ return 1;
+ }
+else
+ {
+ return 0;
+ }
+}
+]]>
+.
+
+The opening tag can be indented 1-3 spaces, but not 4:
+
+.
+ <!-- foo -->
+
+ <!-- foo -->
+.
+ <!-- foo -->
+<pre><code>&lt;!-- foo --&gt;
+</code></pre>
+.
+
+An HTML block can interrupt a paragraph, and need not be preceded
+by a blank line.
+
+.
+Foo
+<div>
+bar
+</div>
+.
+<p>Foo</p>
+<div>
+bar
+</div>
+.
+
+However, a following blank line is always needed, except at the end of
+a document:
+
+.
+<div>
+bar
+</div>
+*foo*
+.
+<div>
+bar
+</div>
+*foo*
+.
+
+An incomplete HTML block tag may also start an HTML block:
+
+.
+<div class
+foo
+.
+<div class
+foo
+.
+
+This rule differs from John Gruber's original Markdown syntax
+specification, which says:
+
+> The only restrictions are that block-level HTML elements —
+> e.g. `<div>`, `<table>`, `<pre>`, `<p>`, etc. — must be separated from
+> surrounding content by blank lines, and the start and end tags of the
+> block should not be indented with tabs or spaces.
+
+In some ways Gruber's rule is more restrictive than the one given
+here:
+
+- It requires that an HTML block be preceded by a blank line.
+- It does not allow the start tag to be indented.
+- It requires a matching end tag, which it also does not allow to
+ be indented.
+
+Indeed, most Markdown implementations, including some of Gruber's
+own perl implementations, do not impose these restrictions.
+
+There is one respect, however, in which Gruber's rule is more liberal
+than the one given here, since it allows blank lines to occur inside
+an HTML block. There are two reasons for disallowing them here.
+First, it removes the need to parse balanced tags, which is
+expensive and can require backtracking from the end of the document
+if no matching end tag is found. Second, it provides a very simple
+and flexible way of including Markdown content inside HTML tags:
+simply separate the Markdown from the HTML using blank lines:
+
+.
+<div>
+
+*Emphasized* text.
+
+</div>
+.
+<div>
+<p><em>Emphasized</em> text.</p>
+</div>
+.
+
+Compare:
+
+.
+<div>
+*Emphasized* text.
+</div>
+.
+<div>
+*Emphasized* text.
+</div>
+.
+
+Some Markdown implementations have adopted a convention of
+interpreting content inside tags as text if the open tag has
+the attribute `markdown=1`. The rule given above seems a simpler and
+more elegant way of achieving the same expressive power, which is also
+much simpler to parse.
+
+The main potential drawback is that one can no longer paste HTML
+blocks into Markdown documents with 100% reliability. However,
+*in most cases* this will work fine, because the blank lines in
+HTML are usually followed by HTML block tags. For example:
+
+.
+<table>
+
+<tr>
+
+<td>
+Hi
+</td>
+
+</tr>
+
+</table>
+.
+<table>
+<tr>
+<td>
+Hi
+</td>
+</tr>
+</table>
+.
+
+Moreover, blank lines are usually not necessary and can be
+deleted. The exception is inside `<pre>` tags; here, one can
+replace the blank lines with `&#10;` entities.
+
+So there is no important loss of expressive power with the new rule.
+
+## Link reference definitions
+
+A [link reference definition](#link-reference-definition)
+<a id="link-reference-definition"></a> consists of a [link
+label](#link-label), indented up to three spaces, followed
+by a colon (`:`), optional blank space (including up to one
+newline), a [link destination](#link-destination), optional
+blank space (including up to one newline), and an optional [link
+title](#link-title), which if it is present must be separated
+from the [link destination](#link-destination) by whitespace.
+No further non-space characters may occur on the line.
+
+A [link reference-definition](#link-reference-definition)
+does not correspond to a structural element of a document. Instead, it
+defines a label which can be used in [reference links](#reference-link)
+and reference-style [images](#image) elsewhere in the document. [Link
+reference definitions] can come either before or after the links that use
+them.
+
+.
+[foo]: /url "title"
+
+[foo]
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+ [foo]:
+ /url
+ 'the title'
+
+[foo]
+.
+<p><a href="/url" title="the title">foo</a></p>
+.
+
+.
+[Foo*bar\]]:my_(url) 'title (with parens)'
+
+[Foo*bar\]]
+.
+<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
+.
+
+.
+[Foo bar]:
+<my url>
+'title'
+
+[Foo bar]
+.
+<p><a href="my%20url" title="title">Foo bar</a></p>
+.
+
+The title may be omitted:
+
+.
+[foo]:
+/url
+
+[foo]
+.
+<p><a href="/url">foo</a></p>
+.
+
+The link destination may not be omitted:
+
+.
+[foo]:
+
+[foo]
+.
+<p>[foo]:</p>
+<p>[foo]</p>
+.
+
+A link can come before its corresponding definition:
+
+.
+[foo]
+
+[foo]: url
+.
+<p><a href="url">foo</a></p>
+.
+
+If there are several matching definitions, the first one takes
+precedence:
+
+.
+[foo]
+
+[foo]: first
+[foo]: second
+.
+<p><a href="first">foo</a></p>
+.
+
+As noted in the section on [Links], matching of labels is
+case-insensitive (see [matches](#matches)).
+
+.
+[FOO]: /url
+
+[Foo]
+.
+<p><a href="/url">Foo</a></p>
+.
+
+.
+[ΑΓΩ]: /φου
+
+[αγω]
+.
+<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>
+.
+
+Here is a link reference definition with no corresponding link.
+It contributes nothing to the document.
+
+.
+[foo]: /url
+.
+.
+
+This is not a link reference definition, because there are
+non-space characters after the title:
+
+.
+[foo]: /url "title" ok
+.
+<p>[foo]: /url &quot;title&quot; ok</p>
+.
+
+This is not a link reference definition, because it is indented
+four spaces:
+
+.
+ [foo]: /url "title"
+
+[foo]
+.
+<pre><code>[foo]: /url &quot;title&quot;
+</code></pre>
+<p>[foo]</p>
+.
+
+This is not a link reference definition, because it occurs inside
+a code block:
+
+.
+```
+[foo]: /url
+```
+
+[foo]
+.
+<pre><code>[foo]: /url
+</code></pre>
+<p>[foo]</p>
+.
+
+A [link reference definition](#link-reference-definition) cannot
+interrupt a paragraph.
+
+.
+Foo
+[bar]: /baz
+
+[bar]
+.
+<p>Foo
+[bar]: /baz</p>
+<p>[bar]</p>
+.
+
+However, it can directly follow other block elements, such as headers
+and horizontal rules, and it need not be followed by a blank line.
+
+.
+# [Foo]
+[foo]: /url
+> bar
+.
+<h1><a href="/url">Foo</a></h1>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+Several [link references](#link-reference) can occur one after another,
+without intervening blank lines.
+
+.
+[foo]: /foo-url "foo"
+[bar]: /bar-url
+ "bar"
+[baz]: /baz-url
+
+[foo],
+[bar],
+[baz]
+.
+<p><a href="/foo-url" title="foo">foo</a>,
+<a href="/bar-url" title="bar">bar</a>,
+<a href="/baz-url">baz</a></p>
+.
+
+[Link reference definitions](#link-reference-definition) can occur
+inside block containers, like lists and block quotations. They
+affect the entire document, not just the container in which they
+are defined:
+
+.
+[foo]
+
+> [foo]: /url
+.
+<p><a href="/url">foo</a></p>
+<blockquote>
+</blockquote>
+.
+
+
+## Paragraphs
+
+A sequence of non-blank lines that cannot be interpreted as other
+kinds of blocks forms a [paragraph](#paragraph).<a id="paragraph"></a>
+The contents of the paragraph are the result of parsing the
+paragraph's raw content as inlines. The paragraph's raw content
+is formed by concatenating the lines and removing initial and final
+spaces.
+
+A simple example with two paragraphs:
+
+.
+aaa
+
+bbb
+.
+<p>aaa</p>
+<p>bbb</p>
+.
+
+Paragraphs can contain multiple lines, but no blank lines:
+
+.
+aaa
+bbb
+
+ccc
+ddd
+.
+<p>aaa
+bbb</p>
+<p>ccc
+ddd</p>
+.
+
+Multiple blank lines between paragraph have no effect:
+
+.
+aaa
+
+
+bbb
+.
+<p>aaa</p>
+<p>bbb</p>
+.
+
+Leading spaces are skipped:
+
+.
+ aaa
+ bbb
+.
+<p>aaa
+bbb</p>
+.
+
+Lines after the first may be indented any amount, since indented
+code blocks cannot interrupt paragraphs.
+
+.
+aaa
+ bbb
+ ccc
+.
+<p>aaa
+bbb
+ccc</p>
+.
+
+However, the first line may be indented at most three spaces,
+or an indented code block will be triggered:
+
+.
+ aaa
+bbb
+.
+<p>aaa
+bbb</p>
+.
+
+.
+ aaa
+bbb
+.
+<pre><code>aaa
+</code></pre>
+<p>bbb</p>
+.
+
+Final spaces are stripped before inline parsing, so a paragraph
+that ends with two or more spaces will not end with a hard line
+break:
+
+.
+aaa
+bbb
+.
+<p>aaa<br />
+bbb</p>
+.
+
+## Blank lines
+
+[Blank lines](#blank-line) between block-level elements are ignored,
+except for the role they play in determining whether a [list](#list)
+is [tight](#tight) or [loose](#loose).
+
+Blank lines at the beginning and end of the document are also ignored.
+
+.
+
+
+aaa
+
+
+# aaa
+
+
+.
+<p>aaa</p>
+<h1>aaa</h1>
+.
+
+
+# Container blocks
+
+A [container block](#container-block) is a block that has other
+blocks as its contents. There are two basic kinds of container blocks:
+[block quotes](#block-quote) and [list items](#list-item).
+[Lists](#list) are meta-containers for [list items](#list-item).
+
+We define the syntax for container blocks recursively. The general
+form of the definition is:
+
+> If X is a sequence of blocks, then the result of
+> transforming X in such-and-such a way is a container of type Y
+> with these blocks as its content.
+
+So, we explain what counts as a block quote or list item by explaining
+how these can be *generated* from their contents. This should suffice
+to define the syntax, although it does not give a recipe for *parsing*
+these constructions. (A recipe is provided below in the section entitled
+[A parsing strategy](#appendix-a-a-parsing-strategy).)
+
+## Block quotes
+
+A [block quote marker](#block-quote-marker) <a id="block-quote-marker"></a>
+consists of 0-3 spaces of initial indent, plus (a) the character `>` together
+with a following space, or (b) a single character `>` not followed by a space.
+
+The following rules define [block quotes](#block-quote):
+<a id="block-quote"></a>
+
+1. **Basic case.** If a string of lines *Ls* constitute a sequence
+ of blocks *Bs*, then the result of appending a [block quote
+ marker](#block-quote-marker) to the beginning of each line in *Ls*
+ is a [block quote](#block-quote) containing *Bs*.
+
+2. **Laziness.** If a string of lines *Ls* constitute a [block
+ quote](#block-quote) with contents *Bs*, then the result of deleting
+ the initial [block quote marker](#block-quote-marker) from one or
+ more lines in which the next non-space character after the [block
+ quote marker](#block-quote-marker) is [paragraph continuation
+ text](#paragraph-continuation-text) is a block quote with *Bs* as
+ its content. <a id="paragraph-continuation-text"></a>
+ [Paragraph continuation text](#paragraph-continuation-text) is text
+ that will be parsed as part of the content of a paragraph, but does
+ not occur at the beginning of the paragraph.
+
+3. **Consecutiveness.** A document cannot contain two [block
+ quotes](#block-quote) in a row unless there is a [blank
+ line](#blank-line) between them.
+
+Nothing else counts as a [block quote](#block-quote).
+
+Here is a simple example:
+
+.
+> # Foo
+> bar
+> baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+The spaces after the `>` characters can be omitted:
+
+.
+># Foo
+>bar
+> baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+The `>` characters can be indented 1-3 spaces:
+
+.
+ > # Foo
+ > bar
+ > baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+Four spaces gives us a code block:
+
+.
+ > # Foo
+ > bar
+ > baz
+.
+<pre><code>&gt; # Foo
+&gt; bar
+&gt; baz
+</code></pre>
+.
+
+The Laziness clause allows us to omit the `>` before a
+paragraph continuation line:
+
+.
+> # Foo
+> bar
+baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+A block quote can contain some lazy and some non-lazy
+continuation lines:
+
+.
+> bar
+baz
+> foo
+.
+<blockquote>
+<p>bar
+baz
+foo</p>
+</blockquote>
+.
+
+Laziness only applies to lines that are continuations of
+paragraphs. Lines containing characters or indentation that indicate
+block structure cannot be lazy.
+
+.
+> foo
+---
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+<hr />
+.
+
+.
+> - foo
+- bar
+.
+<blockquote>
+<ul>
+<li>foo</li>
+</ul>
+</blockquote>
+<ul>
+<li>bar</li>
+</ul>
+.
+
+.
+> foo
+ bar
+.
+<blockquote>
+<pre><code>foo
+</code></pre>
+</blockquote>
+<pre><code>bar
+</code></pre>
+.
+
+.
+> ```
+foo
+```
+.
+<blockquote>
+<pre><code></code></pre>
+</blockquote>
+<p>foo</p>
+<pre><code></code></pre>
+.
+
+A block quote can be empty:
+
+.
+>
+.
+<blockquote>
+</blockquote>
+.
+
+.
+>
+>
+>
+.
+<blockquote>
+</blockquote>
+.
+
+A block quote can have initial or final blank lines:
+
+.
+>
+> foo
+>
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+.
+
+A blank line always separates block quotes:
+
+.
+> foo
+
+> bar
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+(Most current Markdown implementations, including John Gruber's
+original `Markdown.pl`, will parse this example as a single block quote
+with two paragraphs. But it seems better to allow the author to decide
+whether two block quotes or one are wanted.)
+
+Consecutiveness means that if we put these block quotes together,
+we get a single block quote:
+
+.
+> foo
+> bar
+.
+<blockquote>
+<p>foo
+bar</p>
+</blockquote>
+.
+
+To get a block quote with two paragraphs, use:
+
+.
+> foo
+>
+> bar
+.
+<blockquote>
+<p>foo</p>
+<p>bar</p>
+</blockquote>
+.
+
+Block quotes can interrupt paragraphs:
+
+.
+foo
+> bar
+.
+<p>foo</p>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+In general, blank lines are not needed before or after block
+quotes:
+
+.
+> aaa
+***
+> bbb
+.
+<blockquote>
+<p>aaa</p>
+</blockquote>
+<hr />
+<blockquote>
+<p>bbb</p>
+</blockquote>
+.
+
+However, because of laziness, a blank line is needed between
+a block quote and a following paragraph:
+
+.
+> bar
+baz
+.
+<blockquote>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+.
+> bar
+
+baz
+.
+<blockquote>
+<p>bar</p>
+</blockquote>
+<p>baz</p>
+.
+
+.
+> bar
+>
+baz
+.
+<blockquote>
+<p>bar</p>
+</blockquote>
+<p>baz</p>
+.
+
+It is a consequence of the Laziness rule that any number
+of initial `>`s may be omitted on a continuation line of a
+nested block quote:
+
+.
+> > > foo
+bar
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>foo
+bar</p>
+</blockquote>
+</blockquote>
+</blockquote>
+.
+
+.
+>>> foo
+> bar
+>>baz
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>foo
+bar
+baz</p>
+</blockquote>
+</blockquote>
+</blockquote>
+.
+
+When including an indented code block in a block quote,
+remember that the [block quote marker](#block-quote-marker) includes
+both the `>` and a following space. So *five spaces* are needed after
+the `>`:
+
+.
+> code
+
+> not code
+.
+<blockquote>
+<pre><code>code
+</code></pre>
+</blockquote>
+<blockquote>
+<p>not code</p>
+</blockquote>
+.
+
+
+## List items
+
+A [list marker](#list-marker) <a id="list-marker"></a> is a
+[bullet list marker](#bullet-list-marker) or an [ordered list
+marker](#ordered-list-marker).
+
+A [bullet list marker](#bullet-list-marker) <a id="bullet-list-marker"></a>
+is a `-`, `+`, or `*` character.
+
+An [ordered list marker](#ordered-list-marker) <a id="ordered-list-marker"></a>
+is a sequence of one of more digits (`0-9`), followed by either a
+`.` character or a `)` character.
+
+The following rules define [list items](#list-item):
+
+1. **Basic case.** If a sequence of lines *Ls* constitute a sequence of
+ blocks *Bs* starting with a non-space character and not separated
+ from each other by more than one blank line, and *M* is a list
+ marker *M* of width *W* followed by 0 < *N* < 5 spaces, then the result
+ of prepending *M* and the following spaces to the first line of
+ *Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
+ list item with *Bs* as its contents. The type of the list item
+ (bullet or ordered) is determined by the type of its list marker.
+ If the list item is ordered, then it is also assigned a start
+ number, based on the ordered list marker.
+
+For example, let *Ls* be the lines
+
+.
+A paragraph
+with two lines.
+
+ indented code
+
+> A block quote.
+.
+<p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote>
+.
+
+And let *M* be the marker `1.`, and *N* = 2. Then rule #1 says
+that the following is an ordered list item with start number 1,
+and the same contents as *Ls*:
+
+.
+1. A paragraph
+ with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+The most important thing to notice is that the position of
+the text after the list marker determines how much indentation
+is needed in subsequent blocks in the list item. If the list
+marker takes up two spaces, and there are three spaces between
+the list marker and the next nonspace character, then blocks
+must be indented five spaces in order to fall under the list
+item.
+
+Here are some examples showing how far content must be indented to be
+put under the list item:
+
+.
+- one
+
+ two
+.
+<ul>
+<li>one</li>
+</ul>
+<p>two</p>
+.
+
+.
+- one
+
+ two
+.
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+.
+
+.
+ - one
+
+ two
+.
+<ul>
+<li>one</li>
+</ul>
+<pre><code> two
+</code></pre>
+.
+
+.
+ - one
+
+ two
+.
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+.
+
+It is tempting to think of this in terms of columns: the continuation
+blocks must be indented at least to the column of the first nonspace
+character after the list marker. However, that is not quite right.
+The spaces after the list marker determine how much relative indentation
+is needed. Which column this indentation reaches will depend on
+how the list item is embedded in other constructions, as shown by
+this example:
+
+.
+ > > 1. one
+>>
+>> two
+.
+<blockquote>
+<blockquote>
+<ol>
+<li><p>one</p>
+<p>two</p></li>
+</ol>
+</blockquote>
+</blockquote>
+.
+
+Here `two` occurs in the same column as the list marker `1.`,
+but is actually contained in the list item, because there is
+sufficent indentation after the last containing blockquote marker.
+
+The converse is also possible. In the following example, the word `two`
+occurs far to the right of the initial text of the list item, `one`, but
+it is not considered part of the list item, because it is not indented
+far enough past the blockquote marker:
+
+.
+>>- one
+>>
+ > > two
+.
+<blockquote>
+<blockquote>
+<ul>
+<li>one</li>
+</ul>
+<p>two</p>
+</blockquote>
+</blockquote>
+.
+
+A list item may not contain blocks that are separated by more than
+one blank line. Thus, two blank lines will end a list, unless the
+two blanks are contained in a [fenced code block](#fenced-code-block).
+
+.
+- foo
+
+ bar
+
+- foo
+
+
+ bar
+
+- ```
+ foo
+
+
+ bar
+ ```
+.
+<ul>
+<li><p>foo</p>
+<p>bar</p></li>
+<li><p>foo</p></li>
+</ul>
+<p>bar</p>
+<ul>
+<li><pre><code>foo
+
+
+bar
+</code></pre></li>
+</ul>
+.
+
+A list item may contain any kind of block:
+
+.
+1. foo
+
+ ```
+ bar
+ ```
+
+ baz
+
+ > bam
+.
+<ol>
+<li><p>foo</p>
+<pre><code>bar
+</code></pre>
+<p>baz</p>
+<blockquote>
+<p>bam</p>
+</blockquote></li>
+</ol>
+.
+
+2. **Item starting with indented code.** If a sequence of lines *Ls*
+ constitute a sequence of blocks *Bs* starting with an indented code
+ block and not separated from each other by more than one blank line,
+ and *M* is a list marker *M* of width *W* followed by
+ one space, then the result of prepending *M* and the following
+ space to the first line of *Ls*, and indenting subsequent lines of
+ *Ls* by *W + 1* spaces, is a list item with *Bs* as its contents.
+ If a line is empty, then it need not be indented. The type of the
+ list item (bullet or ordered) is determined by the type of its list
+ marker. If the list item is ordered, then it is also assigned a
+ start number, based on the ordered list marker.
+
+An indented code block will have to be indented four spaces beyond
+the edge of the region where text will be included in the list item.
+In the following case that is 6 spaces:
+
+.
+- foo
+
+ bar
+.
+<ul>
+<li><p>foo</p>
+<pre><code>bar
+</code></pre></li>
+</ul>
+.
+
+And in this case it is 11 spaces:
+
+.
+ 10. foo
+
+ bar
+.
+<ol start="10">
+<li><p>foo</p>
+<pre><code>bar
+</code></pre></li>
+</ol>
+.
+
+If the *first* block in the list item is an indented code block,
+then by rule #2, the contents must be indented *one* space after the
+list marker:
+
+.
+ indented code
+
+paragraph
+
+ more code
+.
+<pre><code>indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre>
+.
+
+.
+1. indented code
+
+ paragraph
+
+ more code
+.
+<ol>
+<li><pre><code>indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre></li>
+</ol>
+.
+
+Note that an additional space indent is interpreted as space
+inside the code block:
+
+.
+1. indented code
+
+ paragraph
+
+ more code
+.
+<ol>
+<li><pre><code> indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre></li>
+</ol>
+.
+
+Note that rules #1 and #2 only apply to two cases: (a) cases
+in which the lines to be included in a list item begin with a nonspace
+character, and (b) cases in which they begin with an indented code
+block. In a case like the following, where the first block begins with
+a three-space indent, the rules do not allow us to form a list item by
+indenting the whole thing and prepending a list marker:
+
+.
+ foo
+
+bar
+.
+<p>foo</p>
+<p>bar</p>
+.
+
+.
+- foo
+
+ bar
+.
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+.
+
+This is not a significant restriction, because when a block begins
+with 1-3 spaces indent, the indentation can always be removed without
+a change in interpretation, allowing rule #1 to be applied. So, in
+the above case:
+
+.
+- foo
+
+ bar
+.
+<ul>
+<li><p>foo</p>
+<p>bar</p></li>
+</ul>
+.
+
+
+3. **Indentation.** If a sequence of lines *Ls* constitutes a list item
+ according to rule #1 or #2, then the result of indenting each line
+ of *L* by 1-3 spaces (the same for each line) also constitutes a
+ list item with the same contents and attributes. If a line is
+ empty, then it need not be indented.
+
+Indented one space:
+
+.
+ 1. A paragraph
+ with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indented two spaces:
+
+.
+ 1. A paragraph
+ with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indented three spaces:
+
+.
+ 1. A paragraph
+ with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Four spaces indent gives a code block:
+
+.
+ 1. A paragraph
+ with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<pre><code>1. A paragraph
+ with two lines.
+
+ indented code
+
+ &gt; A block quote.
+</code></pre>
+.
+
+
+4. **Laziness.** If a string of lines *Ls* constitute a [list
+ item](#list-item) with contents *Bs*, then the result of deleting
+ some or all of the indentation from one or more lines in which the
+ next non-space character after the indentation is
+ [paragraph continuation text](#paragraph-continuation-text) is a
+ list item with the same contents and attributes.
+
+Here is an example with lazy continuation lines:
+
+.
+ 1. A paragraph
+with two lines.
+
+ indented code
+
+ > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indentation can be partially deleted:
+
+.
+ 1. A paragraph
+ with two lines.
+.
+<ol>
+<li>A paragraph
+with two lines.</li>
+</ol>
+.
+
+These examples show how laziness can work in nested structures:
+
+.
+> 1. > Blockquote
+continued here.
+.
+<blockquote>
+<ol>
+<li><blockquote>
+<p>Blockquote
+continued here.</p>
+</blockquote></li>
+</ol>
+</blockquote>
+.
+
+.
+> 1. > Blockquote
+> continued here.
+.
+<blockquote>
+<ol>
+<li><blockquote>
+<p>Blockquote
+continued here.</p>
+</blockquote></li>
+</ol>
+</blockquote>
+.
+
+
+5. **That's all.** Nothing that is not counted as a list item by rules
+ #1--4 counts as a [list item](#list-item).
+
+The rules for sublists follow from the general rules above. A sublist
+must be indented the same number of spaces a paragraph would need to be
+in order to be included in the list item.
+
+So, in this case we need two spaces indent:
+
+.
+- foo
+ - bar
+ - baz
+.
+<ul>
+<li>foo
+<ul>
+<li>bar
+<ul>
+<li>baz</li>
+</ul></li>
+</ul></li>
+</ul>
+.
+
+One is not enough:
+
+.
+- foo
+ - bar
+ - baz
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+<li>baz</li>
+</ul>
+.
+
+Here we need four, because the list marker is wider:
+
+.
+10) foo
+ - bar
+.
+<ol start="10">
+<li>foo
+<ul>
+<li>bar</li>
+</ul></li>
+</ol>
+.
+
+Three is not enough:
+
+.
+10) foo
+ - bar
+.
+<ol start="10">
+<li>foo</li>
+</ol>
+<ul>
+<li>bar</li>
+</ul>
+.
+
+A list may be the first block in a list item:
+
+.
+- - foo
+.
+<ul>
+<li><ul>
+<li>foo</li>
+</ul></li>
+</ul>
+.
+
+.
+1. - 2. foo
+.
+<ol>
+<li><ul>
+<li><ol start="2">
+<li>foo</li>
+</ol></li>
+</ul></li>
+</ol>
+.
+
+A list item may be empty:
+
+.
+- foo
+-
+- bar
+.
+<ul>
+<li>foo</li>
+<li></li>
+<li>bar</li>
+</ul>
+.
+
+.
+-
+.
+<ul>
+<li></li>
+</ul>
+.
+
+### Motivation
+
+John Gruber's Markdown spec says the following about list items:
+
+1. "List markers typically start at the left margin, but may be indented
+ by up to three spaces. List markers must be followed by one or more
+ spaces or a tab."
+
+2. "To make lists look nice, you can wrap items with hanging indents....
+ But if you don't want to, you don't have to."
+
+3. "List items may consist of multiple paragraphs. Each subsequent
+ paragraph in a list item must be indented by either 4 spaces or one
+ tab."
+
+4. "It looks nice if you indent every line of the subsequent paragraphs,
+ but here again, Markdown will allow you to be lazy."
+
+5. "To put a blockquote within a list item, the blockquote's `>`
+ delimiters need to be indented."
+
+6. "To put a code block within a list item, the code block needs to be
+ indented twice — 8 spaces or two tabs."
+
+These rules specify that a paragraph under a list item must be indented
+four spaces (presumably, from the left margin, rather than the start of
+the list marker, but this is not said), and that code under a list item
+must be indented eight spaces instead of the usual four. They also say
+that a block quote must be indented, but not by how much; however, the
+example given has four spaces indentation. Although nothing is said
+about other kinds of block-level content, it is certainly reasonable to
+infer that *all* block elements under a list item, including other
+lists, must be indented four spaces. This principle has been called the
+*four-space rule*.
+
+The four-space rule is clear and principled, and if the reference
+implementation `Markdown.pl` had followed it, it probably would have
+become the standard. However, `Markdown.pl` allowed paragraphs and
+sublists to start with only two spaces indentation, at least on the
+outer level. Worse, its behavior was inconsistent: a sublist of an
+outer-level list needed two spaces indentation, but a sublist of this
+sublist needed three spaces. It is not surprising, then, that different
+implementations of Markdown have developed very different rules for
+determining what comes under a list item. (Pandoc and python-Markdown,
+for example, stuck with Gruber's syntax description and the four-space
+rule, while discount, redcarpet, marked, PHP Markdown, and others
+followed `Markdown.pl`'s behavior more closely.)
+
+Unfortunately, given the divergences between implementations, there
+is no way to give a spec for list items that will be guaranteed not
+to break any existing documents. However, the spec given here should
+correctly handle lists formatted with either the four-space rule or
+the more forgiving `Markdown.pl` behavior, provided they are laid out
+in a way that is natural for a human to read.
+
+The strategy here is to let the width and indentation of the list marker
+determine the indentation necessary for blocks to fall under the list
+item, rather than having a fixed and arbitrary number. The writer can
+think of the body of the list item as a unit which gets indented to the
+right enough to fit the list marker (and any indentation on the list
+marker). (The laziness rule, #4, then allows continuation lines to be
+unindented if needed.)
+
+This rule is superior, we claim, to any rule requiring a fixed level of
+indentation from the margin. The four-space rule is clear but
+unnatural. It is quite unintuitive that
+
+``` markdown
+- foo
+
+ bar
+
+ - baz
+```
+
+should be parsed as two lists with an intervening paragraph,
+
+``` html
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul>
+```
+
+as the four-space rule demands, rather than a single list,
+
+``` html
+<ul>
+<li><p>foo</p>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul></li>
+</ul>
+```
+
+The choice of four spaces is arbitrary. It can be learned, but it is
+not likely to be guessed, and it trips up beginners regularly.
+
+Would it help to adopt a two-space rule? The problem is that such
+a rule, together with the rule allowing 1--3 spaces indentation of the
+initial list marker, allows text that is indented *less than* the
+original list marker to be included in the list item. For example,
+`Markdown.pl` parses
+
+``` markdown
+ - one
+
+ two
+```
+
+as a single list item, with `two` a continuation paragraph:
+
+``` html
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+```
+
+and similarly
+
+``` markdown
+> - one
+>
+> two
+```
+
+as
+
+``` html
+<blockquote>
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+</blockquote>
+```
+
+This is extremely unintuitive.
+
+Rather than requiring a fixed indent from the margin, we could require
+a fixed indent (say, two spaces, or even one space) from the list marker (which
+may itself be indented). This proposal would remove the last anomaly
+discussed. Unlike the spec presented above, it would count the following
+as a list item with a subparagraph, even though the paragraph `bar`
+is not indented as far as the first paragraph `foo`:
+
+``` markdown
+ 10. foo
+
+ bar
+```
+
+Arguably this text does read like a list item with `bar` as a subparagraph,
+which may count in favor of the proposal. However, on this proposal indented
+code would have to be indented six spaces after the list marker. And this
+would break a lot of existing Markdown, which has the pattern:
+
+``` markdown
+1. foo
+
+ indented code
+```
+
+where the code is indented eight spaces. The spec above, by contrast, will
+parse this text as expected, since the code block's indentation is measured
+from the beginning of `foo`.
+
+The one case that needs special treatment is a list item that *starts*
+with indented code. How much indentation is required in that case, since
+we don't have a "first paragraph" to measure from? Rule #2 simply stipulates
+that in such cases, we require one space indentation from the list marker
+(and then the normal four spaces for the indented code). This will match the
+four-space rule in cases where the list marker plus its initial indentation
+takes four spaces (a common case), but diverge in other cases.
+
+## Lists
+
+A [list](#list) <a id="list"></a> is a sequence of one or more
+list items [of the same type](#of-the-same-type). The list items
+may be separated by single [blank lines](#blank-line), but two
+blank lines end all containing lists.
+
+Two list items are [of the same type](#of-the-same-type)
+<a id="of-the-same-type"></a> if they begin with a [list
+marker](#list-marker) of the same type. Two list markers are of the
+same type if (a) they are bullet list markers using the same character
+(`-`, `+`, or `*`) or (b) they are ordered list numbers with the same
+delimiter (either `.` or `)`).
+
+A list is an [ordered list](#ordered-list) <a id="ordered-list"></a>
+if its constituent list items begin with
+[ordered list markers](#ordered-list-marker), and a [bullet
+list](#bullet-list) <a id="bullet-list"></a> if its constituent list
+items begin with [bullet list markers](#bullet-list-marker).
+
+The [start number](#start-number) <a id="start-number"></a>
+of an [ordered list](#ordered-list) is determined by the list number of
+its initial list item. The numbers of subsequent list items are
+disregarded.
+
+A list is [loose](#loose) if it any of its constituent list items are
+separated by blank lines, or if any of its constituent list items
+directly contain two block-level elements with a blank line between
+them. Otherwise a list is [tight](#tight). (The difference in HTML output
+is that paragraphs in a loose with are wrapped in `<p>` tags, while
+paragraphs in a tight list are not.)
+
+Changing the bullet or ordered list delimiter starts a new list:
+
+.
+- foo
+- bar
++ baz
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+</ul>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+.
+1. foo
+2. bar
+3) baz
+.
+<ol>
+<li>foo</li>
+<li>bar</li>
+</ol>
+<ol start="3">
+<li>baz</li>
+</ol>
+.
+
+There can be blank lines between items, but two blank lines end
+a list:
+
+.
+- foo
+
+- bar
+
+
+- baz
+.
+<ul>
+<li><p>foo</p></li>
+<li><p>bar</p></li>
+</ul>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+As illustrated above in the section on [list items](#list-item),
+two blank lines between blocks *within* a list item will also end a
+list:
+
+.
+- foo
+
+
+ bar
+- baz
+.
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+Indeed, two blank lines will end *all* containing lists:
+
+.
+- foo
+ - bar
+ - baz
+
+
+ bim
+.
+<ul>
+<li>foo
+<ul>
+<li>bar
+<ul>
+<li>baz</li>
+</ul></li>
+</ul></li>
+</ul>
+<pre><code> bim
+</code></pre>
+.
+
+Thus, two blank lines can be used to separate consecutive lists of
+the same type, or to separate a list from an indented code block
+that would otherwise be parsed as a subparagraph of the final list
+item:
+
+.
+- foo
+- bar
+
+
+- baz
+- bim
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+</ul>
+<ul>
+<li>baz</li>
+<li>bim</li>
+</ul>
+.
+
+.
+- foo
+
+ notcode
+
+- foo
+
+
+ code
+.
+<ul>
+<li><p>foo</p>
+<p>notcode</p></li>
+<li><p>foo</p></li>
+</ul>
+<pre><code>code
+</code></pre>
+.
+
+List items need not be indented to the same level. The following
+list items will be treated as items at the same list level,
+since none is indented enough to belong to the previous list
+item:
+
+.
+- a
+ - b
+ - c
+ - d
+ - e
+ - f
+- g
+.
+<ul>
+<li>a</li>
+<li>b</li>
+<li>c</li>
+<li>d</li>
+<li>e</li>
+<li>f</li>
+<li>g</li>
+</ul>
+.
+
+This is a loose list, because there is a blank line between
+two of the list items:
+
+.
+- a
+- b
+
+- c
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p></li>
+<li><p>c</p></li>
+</ul>
+.
+
+So is this, with a empty second item:
+
+.
+* a
+*
+
+* c
+.
+<ul>
+<li><p>a</p></li>
+<li></li>
+<li><p>c</p></li>
+</ul>
+.
+
+These are loose lists, even though there is no space between the items,
+because one of the items directly contains two block-level elements
+with a blank line between them:
+
+.
+- a
+- b
+
+ c
+- d
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p>
+<p>c</p></li>
+<li><p>d</p></li>
+</ul>
+.
+
+.
+- a
+- b
+
+ [ref]: /url
+- d
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p></li>
+<li><p>d</p></li>
+</ul>
+.
+
+This is a tight list, because the blank lines are in a code block:
+
+.
+- a
+- ```
+ b
+
+
+ ```
+- c
+.
+<ul>
+<li>a</li>
+<li><pre><code>b
+
+
+</code></pre></li>
+<li>c</li>
+</ul>
+.
+
+This is a tight list, because the blank line is between two
+paragraphs of a sublist. So the inner list is loose while
+the other list is tight:
+
+.
+- a
+ - b
+
+ c
+- d
+.
+<ul>
+<li>a
+<ul>
+<li><p>b</p>
+<p>c</p></li>
+</ul></li>
+<li>d</li>
+</ul>
+.
+
+This is a tight list, because the blank line is inside the
+block quote:
+
+.
+* a
+ > b
+ >
+* c
+.
+<ul>
+<li>a
+<blockquote>
+<p>b</p>
+</blockquote></li>
+<li>c</li>
+</ul>
+.
+
+This list is tight, because the consecutive block elements
+are not separated by blank lines:
+
+.
+- a
+ > b
+ ```
+ c
+ ```
+- d
+.
+<ul>
+<li>a
+<blockquote>
+<p>b</p>
+</blockquote>
+<pre><code>c
+</code></pre></li>
+<li>d</li>
+</ul>
+.
+
+A single-paragraph list is tight:
+
+.
+- a
+.
+<ul>
+<li>a</li>
+</ul>
+.
+
+.
+- a
+ - b
+.
+<ul>
+<li>a
+<ul>
+<li>b</li>
+</ul></li>
+</ul>
+.
+
+Here the outer list is loose, the inner list tight:
+
+.
+* foo
+ * bar
+
+ baz
+.
+<ul>
+<li><p>foo</p>
+<ul>
+<li>bar</li>
+</ul>
+<p>baz</p></li>
+</ul>
+.
+
+.
+- a
+ - b
+ - c
+
+- d
+ - e
+ - f
+.
+<ul>
+<li><p>a</p>
+<ul>
+<li>b</li>
+<li>c</li>
+</ul></li>
+<li><p>d</p>
+<ul>
+<li>e</li>
+<li>f</li>
+</ul></li>
+</ul>
+.
+
+# Inlines
+
+Inlines are parsed sequentially from the beginning of the character
+stream to the end (left to right, in left-to-right languages).
+Thus, for example, in
+
+.
+`hi`lo`
+.
+<p><code>hi</code>lo`</p>
+.
+
+`hi` is parsed as code, leaving the backtick at the end as a literal
+backtick.
+
+## Backslash escapes
+
+Any ASCII punctuation character may be backslash-escaped:
+
+.
+\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
+.
+<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
+.
+
+Backslashes before other characters are treated as literal
+backslashes:
+
+.
+\→\A\a\ \3\φ\«
+.
+<p>\ \A\a\ \3\φ\«</p>
+.
+
+Escaped characters are treated as regular characters and do
+not have their usual Markdown meanings:
+
+.
+\*not emphasized*
+\<br/> not a tag
+\[not a link](/foo)
+\`not code`
+1\. not a list
+\* not a list
+\# not a header
+\[foo]: /url "not a reference"
+.
+<p>*not emphasized*
+&lt;br/&gt; not a tag
+[not a link](/foo)
+`not code`
+1. not a list
+* not a list
+# not a header
+[foo]: /url &quot;not a reference&quot;</p>
+.
+
+If a backslash is itself escaped, the following character is not:
+
+.
+\\*emphasis*
+.
+<p>\<em>emphasis</em></p>
+.
+
+A backslash at the end of the line is a hard line break:
+
+.
+foo\
+bar
+.
+<p>foo<br />
+bar</p>
+.
+
+Backslash escapes do not work in code blocks, code spans, autolinks, or
+raw HTML:
+
+.
+`` \[\` ``
+.
+<p><code>\[\`</code></p>
+.
+
+.
+ \[\]
+.
+<pre><code>\[\]
+</code></pre>
+.
+
+.
+~~~
+\[\]
+~~~
+.
+<pre><code>\[\]
+</code></pre>
+.
+
+.
+<http://google.com?find=\*>
+.
+<p><a href="http://google.com?find=%5C*">http://google.com?find=\*</a></p>
+.
+
+.
+<a href="/bar\/)">
+.
+<p><a href="/bar\/)"></p>
+.
+
+But they work in all other contexts, including URLs and link titles,
+link references, and info strings in [fenced code
+blocks](#fenced-code-block):
+
+.
+[foo](/bar\* "ti\*tle")
+.
+<p><a href="/bar*" title="ti*tle">foo</a></p>
+.
+
+.
+[foo]
+
+[foo]: /bar\* "ti\*tle"
+.
+<p><a href="/bar*" title="ti*tle">foo</a></p>
+.
+
+.
+``` foo\+bar
+foo
+```
+.
+<pre><code class="language-foo+bar">foo
+</code></pre>
+.
+
+
+## Entities
+
+With the goal of making this standard as HTML-agnostic as possible, all HTML valid HTML Entities in any
+context are recognized as such and converted into their actual values (i.e. the UTF8 characters representing
+the entity itself) before they are stored in the AST.
+
+This allows implementations that target HTML output to trivially escape the entities when generating HTML,
+and simplifies the job of implementations targetting other languages, as these will only need to handle the
+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)
+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
+named entities that exist, with the exception of `"` (`&quot;`), `&` (`&amp;`), `<` (`&lt;`) and `>` (`&gt;`),
+which always need to be written as entities for security reasons.
+
+.
+&nbsp; &amp; &copy; &AElig; &Dcaron; &frac34; &HilbertSpace; &DifferentialD; &ClockwiseContourIntegral;
+.
+<p>  &amp; © Æ Ď ¾ ℋ ⅆ ∲</p>
+.
+
+[Decimal entities](#decimal-entities) <a id="decimal-entities"></a>
+consist of `&#` + a string of 1--8 arabic digits + `;`. Again, these entities need to be recognised
+and tranformed into their corresponding UTF8 codepoints. Invalid Unicode codepoints will be written
+as the "unknown codepoint" character (`0xFFFD`)
+
+.
+&#35; &#1234; &#992; &#98765432;
+.
+<p># Ӓ Ϡ �</p>
+.
+
+[Hexadecimal entities](#hexadecimal-entities) <a id="hexadecimal-entities"></a>
+consist of `&#` + either `X` or `x` + a string of 1-8 hexadecimal digits
++ `;`. They will also be parsed and turned into their corresponding UTF8 values in the AST.
+
+.
+&#X22; &#XD06; &#xcab;
+.
+<p>&quot; ആ ಫ</p>
+.
+
+Here are some nonentities:
+
+.
+&nbsp &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
+.
+<p>&amp;nbsp &amp;x; &amp;#; &amp;#x; &amp;ThisIsWayTooLongToBeAnEntityIsntIt; &amp;hi?;</p>
+.
+
+Although HTML5 does accept some entities without a trailing semicolon
+(such as `&copy`), these are not recognized as entities here, because it makes the grammar too ambiguous:
+
+.
+&copy
+.
+<p>&amp;copy</p>
+.
+
+Strings that are not on the list of HTML5 named entities are not recognized as entities either:
+
+.
+&MadeUpEntity;
+.
+<p>&amp;MadeUpEntity;</p>
+.
+
+Entities are recognized in any context besides code spans or
+code blocks, including raw HTML, URLs, [link titles](#link-title), and
+[fenced code block](#fenced-code-block) info strings:
+
+.
+<a href="&ouml;&ouml;.html">
+.
+<p><a href="&ouml;&ouml;.html"></p>
+.
+
+.
+[foo](/f&ouml;&ouml; "f&ouml;&ouml;")
+.
+<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
+.
+
+.
+[foo]
+
+[foo]: /f&ouml;&ouml; "f&ouml;&ouml;"
+.
+<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
+.
+
+.
+``` f&ouml;&ouml;
+foo
+```
+.
+<pre><code class="language-föö">foo
+</code></pre>
+.
+
+Entities are treated as literal text in code spans and code blocks:
+
+.
+`f&ouml;&ouml;`
+.
+<p><code>f&amp;ouml;&amp;ouml;</code></p>
+.
+
+.
+ f&ouml;f&ouml;
+.
+<pre><code>f&amp;ouml;f&amp;ouml;
+</code></pre>
+.
+
+## Code span
+
+A [backtick string](#backtick-string) <a id="backtick-string"></a>
+is a string of one or more backtick characters (`` ` ``) that is neither
+preceded nor followed by a backtick.
+
+A code span begins with a backtick string and ends with a backtick
+string of equal length. The contents of the code span are the
+characters between the two backtick strings, with leading and trailing
+spaces and newlines removed, and consecutive spaces and newlines
+collapsed to single spaces.
+
+This is a simple code span:
+
+.
+`foo`
+.
+<p><code>foo</code></p>
+.
+
+Here two backticks are used, because the code contains a backtick.
+This example also illustrates stripping of leading and trailing spaces:
+
+.
+`` foo ` bar ``
+.
+<p><code>foo ` bar</code></p>
+.
+
+This example shows the motivation for stripping leading and trailing
+spaces:
+
+.
+` `` `
+.
+<p><code>``</code></p>
+.
+
+Newlines are treated like spaces:
+
+.
+``
+foo
+``
+.
+<p><code>foo</code></p>
+.
+
+Interior spaces and newlines are collapsed into single spaces, just
+as they would be by a browser:
+
+.
+`foo bar
+ baz`
+.
+<p><code>foo bar baz</code></p>
+.
+
+Q: Why not just leave the spaces, since browsers will collapse them
+anyway? A: Because we might be targeting a non-HTML format, and we
+shouldn't rely on HTML-specific rendering assumptions.
+
+(Existing implementations differ in their treatment of internal
+spaces and newlines. Some, including `Markdown.pl` and
+`showdown`, convert an internal newline into a `<br />` tag.
+But this makes things difficult for those who like to hard-wrap
+their paragraphs, since a line break in the midst of a code
+span will cause an unintended line break in the output. Others
+just leave internal spaces as they are, which is fine if only
+HTML is being targeted.)
+
+.
+`foo `` bar`
+.
+<p><code>foo `` bar</code></p>
+.
+
+Note that backslash escapes do not work in code spans. All backslashes
+are treated literally:
+
+.
+`foo\`bar`
+.
+<p><code>foo\</code>bar`</p>
+.
+
+Backslash escapes are never needed, because one can always choose a
+string of *n* backtick characters as delimiters, where the code does
+not contain any strings of exactly *n* backtick characters.
+
+Code span backticks have higher precedence than any other inline
+constructs except HTML tags and autolinks. Thus, for example, this is
+not parsed as emphasized text, since the second `*` is part of a code
+span:
+
+.
+*foo`*`
+.
+<p>*foo<code>*</code></p>
+.
+
+And this is not parsed as a link:
+
+.
+[not a `link](/foo`)
+.
+<p>[not a <code>link](/foo</code>)</p>
+.
+
+But this is a link:
+
+.
+<http://foo.bar.`baz>`
+.
+<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
+.
+
+And this is an HTML tag:
+
+.
+<a href="`">`
+.
+<p><a href="`">`</p>
+.
+
+When a backtick string is not closed by a matching backtick string,
+we just have literal backticks:
+
+.
+```foo``
+.
+<p>```foo``</p>
+.
+
+.
+`foo
+.
+<p>`foo</p>
+.
+
+## Emphasis and strong emphasis
+
+John Gruber's original [Markdown syntax
+description](http://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
+> `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML `<strong>`
+> tag.
+
+This is enough for most users, but these rules leave much undecided,
+especially when it comes to nested emphasis. The original
+`Markdown.pl` test suite makes it clear that triple `***` and
+`___` delimiters can be used for strong emphasis, and most
+implementations have also allowed the following patterns:
+
+``` markdown
+***strong emph***
+***strong** in emph*
+***emph* in strong**
+**in strong *emph***
+*in emph **strong***
+```
+
+The following patterns are less widely supported, but the intent
+is clear and they are useful (especially in contexts like bibliography
+entries):
+
+``` markdown
+*emph *with emph* in it*
+**strong **with strong** in it**
+```
+
+Many implementations have also restricted intraword emphasis to
+the `*` forms, to avoid unwanted emphasis in words containing
+internal underscores. (It is best practice to put these in code
+spans, but users often do not.)
+
+``` markdown
+internal emphasis: foo*bar*baz
+no emphasis: foo_bar_baz
+```
+
+The following rules capture all of these patterns, while allowing
+for efficient parsing strategies that do not backtrack:
+
+1. A single `*` character [can open emphasis](#can-open-emphasis)
+ <a id="can-open-emphasis"></a> iff
+
+ (a) it is not part of a sequence of four or more unescaped `*`s,
+ (b) it is not followed by whitespace, and
+ (c) either it is not followed by a `*` character or it is
+ followed immediately by strong emphasis.
+
+2. A single `_` character [can open emphasis](#can-open-emphasis) iff
+
+ (a) it is not part of a sequence of four or more unescaped `_`s,
+ (b) it is not followed by whitespace,
+ (c) it is not preceded by an ASCII alphanumeric character, and
+ (d) either it is not followed by a `_` character or it is
+ followed immediately by strong emphasis.
+
+3. A single `*` character [can close emphasis](#can-close-emphasis)
+ <a id="can-close-emphasis"></a> iff
+
+ (a) it is not part of a sequence of four or more unescaped `*`s, and
+ (b) it is not preceded by whitespace.
+
+4. A single `_` character [can close emphasis](#can-close-emphasis) iff
+
+ (a) it is not part of a sequence of four or more unescaped `_`s,
+ (b) it is not preceded by whitespace, and
+ (c) it is not followed by an ASCII alphanumeric character.
+
+5. A double `**` [can open strong emphasis](#can-open-strong-emphasis)
+ <a id="can-open-strong-emphasis" ></a> iff
+
+ (a) it is not part of a sequence of four or more unescaped `*`s,
+ (b) it is not followed by whitespace, and
+ (c) either it is not followed by a `*` character or it is
+ followed immediately by emphasis.
+
+6. A double `__` [can open strong emphasis](#can-open-strong-emphasis)
+ iff
+
+ (a) it is not part of a sequence of four or more unescaped `_`s,
+ (b) it is not followed by whitespace, and
+ (c) it is not preceded by an ASCII alphanumeric character, and
+ (d) either it is not followed by a `_` character or it is
+ followed immediately by emphasis.
+
+7. A double `**` [can close strong emphasis](#can-close-strong-emphasis)
+ <a id="can-close-strong-emphasis" ></a> iff
+
+ (a) it is not part of a sequence of four or more unescaped `*`s, and
+ (b) it is not preceded by whitespace.
+
+8. A double `__` [can close strong emphasis](#can-close-strong-emphasis)
+ iff
+
+ (a) it is not part of a sequence of four or more unescaped `_`s,
+ (b) it is not preceded by whitespace, and
+ (c) it is not followed by an ASCII alphanumeric character.
+
+9. Emphasis begins with a delimiter that [can open
+ emphasis](#can-open-emphasis) and includes inlines parsed
+ sequentially until a delimiter that [can close
+ emphasis](#can-close-emphasis), and that uses the same
+ character (`_` or `*`) as the opening delimiter, is reached.
+
+10. Strong emphasis begins with a delimiter that [can open strong
+ emphasis](#can-open-strong-emphasis) and includes inlines parsed
+ sequentially until a delimiter that [can close strong
+ emphasis](#can-close-strong-emphasis), and that uses the
+ same character (`_` or `*`) as the opening delimiter, is reached.
+
+These rules can be illustrated through a series of examples.
+
+Simple emphasis:
+
+.
+*foo bar*
+.
+<p><em>foo bar</em></p>
+.
+
+.
+_foo bar_
+.
+<p><em>foo bar</em></p>
+.
+
+Simple strong emphasis:
+
+.
+**foo bar**
+.
+<p><strong>foo bar</strong></p>
+.
+
+.
+__foo bar__
+.
+<p><strong>foo bar</strong></p>
+.
+
+Emphasis can continue over line breaks:
+
+.
+*foo
+bar*
+.
+<p><em>foo
+bar</em></p>
+.
+
+.
+_foo
+bar_
+.
+<p><em>foo
+bar</em></p>
+.
+
+.
+**foo
+bar**
+.
+<p><strong>foo
+bar</strong></p>
+.
+
+.
+__foo
+bar__
+.
+<p><strong>foo
+bar</strong></p>
+.
+
+Emphasis can contain other inline constructs:
+
+.
+*foo [bar](/url)*
+.
+<p><em>foo <a href="/url">bar</a></em></p>
+.
+
+.
+_foo [bar](/url)_
+.
+<p><em>foo <a href="/url">bar</a></em></p>
+.
+
+.
+**foo [bar](/url)**
+.
+<p><strong>foo <a href="/url">bar</a></strong></p>
+.
+
+.
+__foo [bar](/url)__
+.
+<p><strong>foo <a href="/url">bar</a></strong></p>
+.
+
+Symbols contained in other inline constructs will not
+close emphasis:
+
+.
+*foo [bar*](/url)
+.
+<p>*foo <a href="/url">bar*</a></p>
+.
+
+.
+_foo [bar_](/url)
+.
+<p>_foo <a href="/url">bar_</a></p>
+.
+
+.
+**<a href="**">
+.
+<p>**<a href="**"></p>
+.
+
+.
+__<a href="__">
+.
+<p>__<a href="__"></p>
+.
+
+.
+*a `*`*
+.
+<p><em>a <code>*</code></em></p>
+.
+
+.
+_a `_`_
+.
+<p><em>a <code>_</code></em></p>
+.
+
+.
+**a<http://foo.bar?q=**>
+.
+<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p>
+.
+
+.
+__a<http://foo.bar?q=__>
+.
+<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p>
+.
+
+This is not emphasis, because the opening delimiter is
+followed by white space:
+
+.
+and * foo bar*
+.
+<p>and * foo bar*</p>
+.
+
+.
+_ foo bar_
+.
+<p>_ foo bar_</p>
+.
+
+.
+and ** foo bar**
+.
+<p>and ** foo bar**</p>
+.
+
+.
+__ foo bar__
+.
+<p>__ foo bar__</p>
+.
+
+This is not emphasis, because the closing delimiter is
+preceded by white space:
+
+.
+and *foo bar *
+.
+<p>and *foo bar *</p>
+.
+
+.
+and _foo bar _
+.
+<p>and _foo bar _</p>
+.
+
+.
+and **foo bar **
+.
+<p>and **foo bar **</p>
+.
+
+.
+and __foo bar __
+.
+<p>and __foo bar __</p>
+.
+
+The rules imply that a sequence of four or more unescaped `*` or
+`_` characters will always be parsed as a literal string:
+
+.
+****hi****
+.
+<p>****hi****</p>
+.
+
+.
+_____hi_____
+.
+<p>_____hi_____</p>
+.
+
+.
+Sign here: _________
+.
+<p>Sign here: _________</p>
+.
+
+The rules also imply that there can be no empty emphasis or strong
+emphasis:
+
+.
+** is not an empty emphasis
+.
+<p>** is not an empty emphasis</p>
+.
+
+.
+**** is not an empty strong emphasis
+.
+<p>**** is not an empty strong emphasis</p>
+.
+
+To include `*` or `_` in emphasized sections, use backslash escapes
+or code spans:
+
+.
+*here is a \**
+.
+<p><em>here is a *</em></p>
+.
+
+.
+__this is a double underscore (`__`)__
+.
+<p><strong>this is a double underscore (<code>__</code>)</strong></p>
+.
+
+`*` delimiters allow intra-word emphasis; `_` delimiters do not:
+
+.
+foo*bar*baz
+.
+<p>foo<em>bar</em>baz</p>
+.
+
+.
+foo_bar_baz
+.
+<p>foo_bar_baz</p>
+.
+
+.
+foo__bar__baz
+.
+<p>foo__bar__baz</p>
+.
+
+.
+_foo_bar_baz_
+.
+<p><em>foo_bar_baz</em></p>
+.
+
+.
+11*15*32
+.
+<p>11<em>15</em>32</p>
+.
+
+.
+11_15_32
+.
+<p>11_15_32</p>
+.
+
+Internal underscores will be ignored in underscore-delimited
+emphasis:
+
+.
+_foo_bar_baz_
+.
+<p><em>foo_bar_baz</em></p>
+.
+
+.
+__foo__bar__baz__
+.
+<p><strong>foo__bar__baz</strong></p>
+.
+
+The rules are sufficient for the following nesting patterns:
+
+.
+***foo bar***
+.
+<p><strong><em>foo bar</em></strong></p>
+.
+
+.
+___foo bar___
+.
+<p><strong><em>foo bar</em></strong></p>
+.
+
+.
+***foo** bar*
+.
+<p><em><strong>foo</strong> bar</em></p>
+.
+
+.
+___foo__ bar_
+.
+<p><em><strong>foo</strong> bar</em></p>
+.
+
+.
+***foo* bar**
+.
+<p><strong><em>foo</em> bar</strong></p>
+.
+
+.
+___foo_ bar__
+.
+<p><strong><em>foo</em> bar</strong></p>
+.
+
+.
+*foo **bar***
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+_foo __bar___
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+**foo *bar***
+.
+<p><strong>foo <em>bar</em></strong></p>
+.
+
+.
+__foo _bar___
+.
+<p><strong>foo <em>bar</em></strong></p>
+.
+
+.
+*foo **bar***
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+_foo __bar___
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+*foo *bar* baz*
+.
+<p><em>foo <em>bar</em> baz</em></p>
+.
+
+.
+_foo _bar_ baz_
+.
+<p><em>foo <em>bar</em> baz</em></p>
+.
+
+.
+**foo **bar** baz**
+.
+<p><strong>foo <strong>bar</strong> baz</strong></p>
+.
+
+.
+__foo __bar__ baz__
+.
+<p><strong>foo <strong>bar</strong> baz</strong></p>
+.
+
+.
+*foo **bar** baz*
+.
+<p><em>foo <strong>bar</strong> baz</em></p>
+.
+
+.
+_foo __bar__ baz_
+.
+<p><em>foo <strong>bar</strong> baz</em></p>
+.
+
+.
+**foo *bar* baz**
+.
+<p><strong>foo <em>bar</em> baz</strong></p>
+.
+
+.
+__foo _bar_ baz__
+.
+<p><strong>foo <em>bar</em> baz</strong></p>
+.
+
+Note that you cannot nest emphasis directly inside emphasis
+using the same delimeter, or strong emphasis directly inside
+strong emphasis:
+
+.
+**foo**
+.
+<p><strong>foo</strong></p>
+.
+
+.
+****foo****
+.
+<p>****foo****</p>
+.
+
+For these nestings, you need to switch delimiters:
+
+.
+*_foo_*
+.
+<p><em><em>foo</em></em></p>
+.
+
+.
+**__foo__**
+.
+<p><strong><strong>foo</strong></strong></p>
+.
+
+Note that a `*` followed by a `*` can close emphasis, and
+a `**` followed by a `*` can close strong emphasis (and
+similarly for `_` and `__`):
+
+.
+*foo**
+.
+<p><em>foo</em>*</p>
+.
+
+.
+*foo *bar**
+.
+<p><em>foo <em>bar</em></em></p>
+.
+
+.
+**foo***
+.
+<p><strong>foo</strong>*</p>
+.
+
+.
+***foo* bar***
+.
+<p><strong><em>foo</em> bar</strong>*</p>
+.
+
+.
+***foo** bar***
+.
+<p><em><strong>foo</strong> bar</em>**</p>
+.
+
+The following contains no strong emphasis, because the opening
+delimiter is closed by the first `*` before `bar`:
+
+.
+*foo**bar***
+.
+<p><em>foo</em><em>bar</em>**</p>
+.
+
+However, a string of four or more `****` can never close emphasis:
+
+.
+*foo****
+.
+<p>*foo****</p>
+.
+
+Note that there are some asymmetries here:
+
+.
+*foo**
+
+**foo*
+.
+<p><em>foo</em>*</p>
+<p>**foo*</p>
+.
+
+.
+*foo *bar**
+
+**foo* bar*
+.
+<p><em>foo <em>bar</em></em></p>
+<p>**foo* bar*</p>
+.
+
+More cases with mismatched delimiters:
+
+.
+**foo* bar*
+.
+<p>**foo* bar*</p>
+.
+
+.
+*bar***
+.
+<p><em>bar</em>**</p>
+.
+
+.
+***foo*
+.
+<p>***foo*</p>
+.
+
+.
+**bar***
+.
+<p><strong>bar</strong>*</p>
+.
+
+.
+***foo**
+.
+<p>***foo**</p>
+.
+
+.
+***foo *bar*
+.
+<p>***foo <em>bar</em></p>
+.
+
+## Links
+
+A link contains a [link label](#link-label) (the visible text),
+a [destination](#destination) (the URI that is the link destination),
+and optionally a [link title](#link-title). There are two basic kinds
+of links in Markdown. In [inline links](#inline-links) the destination
+and title are given immediately after the label. In [reference
+links](#reference-links) the destination and title are defined elsewhere
+in the document.
+
+A [link label](#link-label) <a id="link-label"></a> consists of
+
+- an opening `[`, followed by
+- zero or more backtick code spans, autolinks, HTML tags, link labels,
+ backslash-escaped ASCII punctuation characters, or non-`]` characters,
+ followed by
+- a closing `]`.
+
+These rules are motivated by the following intuitive ideas:
+
+- A link label is a container for inline elements.
+- The square brackets bind more tightly than emphasis markers,
+ but less tightly than `<>` or `` ` ``.
+- Link labels may contain material in matching square brackets.
+
+A [link destination](#link-destination) <a id="link-destination"></a>
+consists of either
+
+- a sequence of zero or more characters between an opening `<` and a
+ closing `>` that contains no line breaks or unescaped `<` or `>`
+ characters, or
+
+- a nonempty sequence of characters that does not include
+ ASCII space or control characters, and includes parentheses
+ only if (a) they are backslash-escaped or (b) they are part of
+ a balanced pair of unescaped parentheses that is not itself
+ inside a balanced pair of unescaped paretheses.
+
+A [link title](#link-title) <a id="link-title"></a> consists of either
+
+- a sequence of zero or more characters between straight double-quote
+ characters (`"`), including a `"` character only if it is
+ backslash-escaped, or
+
+- a sequence of zero or more characters between straight single-quote
+ characters (`'`), including a `'` character only if it is
+ backslash-escaped, or
+
+- a sequence of zero or more characters between matching parentheses
+ (`(...)`), including a `)` character only if it is backslash-escaped.
+
+An [inline link](#inline-link) <a id="inline-link"></a>
+consists of a [link label](#link-label) followed immediately
+by a left parenthesis `(`, optional whitespace,
+an optional [link destination](#link-destination),
+an optional [link title](#link-title) separated from the link
+destination by whitespace, optional whitespace, and a right
+parenthesis `)`. The link's text consists of the label (excluding
+the enclosing square brackets) parsed as inlines. The link's
+URI consists of the link destination, excluding enclosing `<...>` if
+present, with backslash-escapes in effect as described above. The
+link's title consists of the link title, excluding its enclosing
+delimiters, with backslash-escapes in effect as described above.
+
+Here is a simple inline link:
+
+.
+[link](/uri "title")
+.
+<p><a href="/uri" title="title">link</a></p>
+.
+
+The title may be omitted:
+
+.
+[link](/uri)
+.
+<p><a href="/uri">link</a></p>
+.
+
+Both the title and the destination may be omitted:
+
+.
+[link]()
+.
+<p><a href="">link</a></p>
+.
+
+.
+[link](<>)
+.
+<p><a href="">link</a></p>
+.
+
+
+If the destination contains spaces, it must be enclosed in pointy
+braces:
+
+.
+[link](/my uri)
+.
+<p>[link](/my uri)</p>
+.
+
+.
+[link](</my uri>)
+.
+<p><a href="/my%20uri">link</a></p>
+.
+
+The destination cannot contain line breaks, even with pointy braces:
+
+.
+[link](foo
+bar)
+.
+<p>[link](foo
+bar)</p>
+.
+
+One level of balanced parentheses is allowed without escaping:
+
+.
+[link]((foo)and(bar))
+.
+<p><a href="(foo)and(bar)">link</a></p>
+.
+
+However, if you have parentheses within parentheses, you need to escape
+or use the `<...>` form:
+
+.
+[link](foo(and(bar)))
+.
+<p>[link](foo(and(bar)))</p>
+.
+
+.
+[link](foo(and\(bar\)))
+.
+<p><a href="foo(and(bar))">link</a></p>
+.
+
+.
+[link](<foo(and(bar))>)
+.
+<p><a href="foo(and(bar))">link</a></p>
+.
+
+Parentheses and other symbols can also be escaped, as usual
+in Markdown:
+
+.
+[link](foo\)\:)
+.
+<p><a href="foo):">link</a></p>
+.
+
+URL-escaping and should be left alone inside the destination, as all URL-escaped characters
+are also valid URL characters. HTML entities in the destination will be parsed into their UTF8
+codepoints, as usual, and optionally URL-escaped when written as HTML.
+
+.
+[link](foo%20b&auml;)
+.
+<p><a href="foo%20b%C3%A4">link</a></p>
+.
+
+Note that, because titles can often be parsed as destinations,
+if you try to omit the destination and keep the title, you'll
+get unexpected results:
+
+.
+[link]("title")
+.
+<p><a href="%22title%22">link</a></p>
+.
+
+Titles may be in single quotes, double quotes, or parentheses:
+
+.
+[link](/url "title")
+[link](/url 'title')
+[link](/url (title))
+.
+<p><a href="/url" title="title">link</a>
+<a href="/url" title="title">link</a>
+<a href="/url" title="title">link</a></p>
+.
+
+Backslash escapes and entities may be used in titles:
+
+.
+[link](/url "title \"&quot;")
+.
+<p><a href="/url" title="title &quot;&quot;">link</a></p>
+.
+
+Nested balanced quotes are not allowed without escaping:
+
+.
+[link](/url "title "and" title")
+.
+<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>
+.
+
+But it is easy to work around this by using a different quote type:
+
+.
+[link](/url 'title "and" title')
+.
+<p><a href="/url" title="title &quot;and&quot; title">link</a></p>
+.
+
+(Note: `Markdown.pl` did allow double quotes inside a double-quoted
+title, and its test suite included a test demonstrating this.
+But it is hard to see a good rationale for the extra complexity this
+brings, since there are already many ways---backslash escaping,
+entities, or using a different quote type for the enclosing title---to
+write titles containing double quotes. `Markdown.pl`'s handling of
+titles has a number of other strange features. For example, it allows
+single-quoted titles in inline links, but not reference links. And, in
+reference links but not inline links, it allows a title to begin with
+`"` and end with `)`. `Markdown.pl` 1.0.1 even allows titles with no closing
+quotation mark, though 1.0.2b8 does not. It seems preferable to adopt
+a simple, rational rule that works the same way in inline links and
+link reference definitions.)
+
+Whitespace is allowed around the destination and title:
+
+.
+[link]( /uri
+ "title" )
+.
+<p><a href="/uri" title="title">link</a></p>
+.
+
+But it is not allowed between the link label and the
+following parenthesis:
+
+.
+[link] (/uri)
+.
+<p>[link] (/uri)</p>
+.
+
+Note that this is not a link, because the closing `]` occurs in
+an HTML tag:
+
+.
+[foo <bar attr="](baz)">
+.
+<p>[foo <bar attr="](baz)"></p>
+.
+
+
+There are three kinds of [reference links](#reference-link):
+<a id="reference-link"></a>
+
+A [full reference link](#full-reference-link) <a id="full-reference-link"></a>
+consists of a [link label](#link-label), optional whitespace, and
+another [link label](#link-label) that [matches](#matches) a
+[link reference definition](#link-reference-definition) elsewhere in the
+document.
+
+One label [matches](#matches) <a id="matches"></a>
+another just in case their normalized forms are equal. To normalize a
+label, perform the *unicode case fold* and collapse consecutive internal
+whitespace to a single space. If there are multiple matching reference
+link definitions, the one that comes first in the document is used. (It
+is desirable in such cases to emit a warning.)
+
+The contents of the first link label are parsed as inlines, which are
+used as the link's text. The link's URI and title are provided by the
+matching [link reference definition](#link-reference-definition).
+
+Here is a simple example:
+
+.
+[foo][bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+The first label can contain inline content:
+
+.
+[*foo\!*][bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo!</em></a></p>
+.
+
+Matching is case-insensitive:
+
+.
+[foo][BaR]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+Unicode case fold is used:
+
+.
+[Толпой][Толпой] is a Russian word.
+
+[ТОЛПОЙ]: /url
+.
+<p><a href="/url">Толпой</a> is a Russian word.</p>
+.
+
+Consecutive internal whitespace is treated as one space for
+purposes of determining matching:
+
+.
+[Foo
+ bar]: /url
+
+[Baz][Foo bar]
+.
+<p><a href="/url">Baz</a></p>
+.
+
+There can be whitespace between the two labels:
+
+.
+[foo] [bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[foo]
+[bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+When there are multiple matching [link reference
+definitions](#link-reference-definition), the first is used:
+
+.
+[foo]: /url1
+
+[foo]: /url2
+
+[bar][foo]
+.
+<p><a href="/url1">bar</a></p>
+.
+
+Note that matching is performed on normalized strings, not parsed
+inline content. So the following does not match, even though the
+labels define equivalent inline content:
+
+.
+[bar][foo\!]
+
+[foo!]: /url
+.
+<p>[bar][foo!]</p>
+.
+
+A [collapsed reference link](#collapsed-reference-link)
+<a id="collapsed-reference-link"></a> consists of a [link
+label](#link-label) that [matches](#matches) a [link reference
+definition](#link-reference-definition) elsewhere in the
+document, optional whitespace, and the string `[]`. The contents of the
+first link label are parsed as inlines, which are used as the link's
+text. The link's URI and title are provided by the matching reference
+link definition. Thus, `[foo][]` is equivalent to `[foo][foo]`.
+
+.
+[foo][]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[*foo* bar][]
+
+[*foo* bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo</em> bar</a></p>
+.
+
+The link labels are case-insensitive:
+
+.
+[Foo][]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">Foo</a></p>
+.
+
+
+As with full reference links, whitespace is allowed
+between the two sets of brackets:
+
+.
+[foo]
+[]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+A [shortcut reference link](#shortcut-reference-link)
+<a id="shortcut-reference-link"></a> consists of a [link
+label](#link-label) that [matches](#matches) a [link reference
+definition](#link-reference-definition) elsewhere in the
+document and is not followed by `[]` or a link label.
+The contents of the first link label are parsed as inlines,
+which are used as the link's text. the link's URI and title
+are provided by the matching link reference definition.
+Thus, `[foo]` is equivalent to `[foo][]`.
+
+.
+[foo]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[*foo* bar]
+
+[*foo* bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo</em> bar</a></p>
+.
+
+.
+[[*foo* bar]]
+
+[*foo* bar]: /url "title"
+.
+<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
+.
+
+The link labels are case-insensitive:
+
+.
+[Foo]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">Foo</a></p>
+.
+
+If you just want bracketed text, you can backslash-escape the
+opening bracket to avoid links:
+
+.
+\[foo]
+
+[foo]: /url "title"
+.
+<p>[foo]</p>
+.
+
+Note that this is a link, because link labels bind more tightly
+than emphasis:
+
+.
+[foo*]: /url
+
+*[foo*]
+.
+<p>*<a href="/url">foo*</a></p>
+.
+
+However, this is not, because link labels bind less
+tightly than code backticks:
+
+.
+[foo`]: /url
+
+[foo`]`
+.
+<p>[foo<code>]</code></p>
+.
+
+Link labels can contain matched square brackets:
+
+.
+[[[foo]]]
+
+[[[foo]]]: /url
+.
+<p><a href="/url">[[foo]]</a></p>
+.
+
+.
+[[[foo]]]
+
+[[[foo]]]: /url1
+[foo]: /url2
+.
+<p><a href="/url1">[[foo]]</a></p>
+.
+
+For non-matching brackets, use backslash escapes:
+
+.
+[\[foo]
+
+[\[foo]: /url
+.
+<p><a href="/url">[foo</a></p>
+.
+
+Full references take precedence over shortcut references:
+
+.
+[foo][bar]
+
+[foo]: /url1
+[bar]: /url2
+.
+<p><a href="/url2">foo</a></p>
+.
+
+In the following case `[bar][baz]` is parsed as a reference,
+`[foo]` as normal text:
+
+.
+[foo][bar][baz]
+
+[baz]: /url
+.
+<p>[foo]<a href="/url">bar</a></p>
+.
+
+Here, though, `[foo][bar]` is parsed as a reference, since
+`[bar]` is defined:
+
+.
+[foo][bar][baz]
+
+[baz]: /url1
+[bar]: /url2
+.
+<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
+.
+
+Here `[foo]` is not parsed as a shortcut reference, because it
+is followed by a link label (even though `[bar]` is not defined):
+
+.
+[foo][bar][baz]
+
+[baz]: /url1
+[foo]: /url2
+.
+<p>[foo]<a href="/url1">bar</a></p>
+.
+
+
+## Images
+
+An (unescaped) exclamation mark (`!`) followed by a reference or
+inline link will be parsed as an image. The link label will be
+used as the image's alt text, and the link title, if any, will
+be used as the image's title.
+
+.
+![foo](/url "title")
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![foo *bar*]
+
+[foo *bar*]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo *bar*][]
+
+[foo *bar*]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo *bar*][foobar]
+
+[FOOBAR]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo](train.jpg)
+.
+<p><img src="train.jpg" alt="foo" /></p>
+.
+
+.
+My ![foo bar](/path/to/train.jpg "title" )
+.
+<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
+.
+
+.
+![foo](<url>)
+.
+<p><img src="url" alt="foo" /></p>
+.
+
+.
+![](/url)
+.
+<p><img src="/url" alt="" /></p>
+.
+
+Reference-style:
+
+.
+![foo] [bar]
+
+[bar]: /url
+.
+<p><img src="/url" alt="foo" /></p>
+.
+
+.
+![foo] [bar]
+
+[BAR]: /url
+.
+<p><img src="/url" alt="foo" /></p>
+.
+
+Collapsed:
+
+.
+![foo][]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![*foo* bar][]
+
+[*foo* bar]: /url "title"
+.
+<p><img src="/url" alt="&lt;em&gt;foo&lt;/em&gt; bar" title="title" /></p>
+.
+
+The labels are case-insensitive:
+
+.
+![Foo][]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="Foo" title="title" /></p>
+.
+
+As with full reference links, whitespace is allowed
+between the two sets of brackets:
+
+.
+![foo]
+[]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+Shortcut:
+
+.
+![foo]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![*foo* bar]
+
+[*foo* bar]: /url "title"
+.
+<p><img src="/url" alt="&lt;em&gt;foo&lt;/em&gt; bar" title="title" /></p>
+.
+
+.
+![[foo]]
+
+[[foo]]: /url "title"
+.
+<p><img src="/url" alt="[foo]" title="title" /></p>
+.
+
+The link labels are case-insensitive:
+
+.
+![Foo]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="Foo" title="title" /></p>
+.
+
+If you just want bracketed text, you can backslash-escape the
+opening `!` and `[`:
+
+.
+\!\[foo]
+
+[foo]: /url "title"
+.
+<p>![foo]</p>
+.
+
+If you want a link after a literal `!`, backslash-escape the
+`!`:
+
+.
+\![foo]
+
+[foo]: /url "title"
+.
+<p>!<a href="/url" title="title">foo</a></p>
+.
+
+## Autolinks
+
+Autolinks are absolute URIs and email addresses inside `<` and `>`.
+They are parsed as links, with the URL or email address as the link
+label.
+
+A [URI autolink](#uri-autolink) <a id="uri-autolink"></a>
+consists of `<`, followed by an [absolute
+URI](#absolute-uri) not containing `<`, followed by `>`. It is parsed
+as a link to the URI, with the URI as the link's label.
+
+An [absolute URI](#absolute-uri), <a id="absolute-uri"></a>
+for these purposes, consists of a [scheme](#scheme) followed by a colon (`:`)
+followed by zero or more characters other than ASCII whitespace and
+control characters, `<`, and `>`. If the URI includes these characters,
+you must use percent-encoding (e.g. `%20` for a space).
+
+The following [schemes](#scheme) <a id="scheme"></a>
+are recognized (case-insensitive):
+`coap`, `doi`, `javascript`, `aaa`, `aaas`, `about`, `acap`, `cap`,
+`cid`, `crid`, `data`, `dav`, `dict`, `dns`, `file`, `ftp`, `geo`, `go`,
+`gopher`, `h323`, `http`, `https`, `iax`, `icap`, `im`, `imap`, `info`,
+`ipp`, `iris`, `iris.beep`, `iris.xpc`, `iris.xpcs`, `iris.lwz`, `ldap`,
+`mailto`, `mid`, `msrp`, `msrps`, `mtqp`, `mupdate`, `news`, `nfs`,
+`ni`, `nih`, `nntp`, `opaquelocktoken`, `pop`, `pres`, `rtsp`,
+`service`, `session`, `shttp`, `sieve`, `sip`, `sips`, `sms`, `snmp`,`
+soap.beep`, `soap.beeps`, `tag`, `tel`, `telnet`, `tftp`, `thismessage`,
+`tn3270`, `tip`, `tv`, `urn`, `vemmi`, `ws`, `wss`, `xcon`,
+`xcon-userid`, `xmlrpc.beep`, `xmlrpc.beeps`, `xmpp`, `z39.50r`,
+`z39.50s`, `adiumxtra`, `afp`, `afs`, `aim`, `apt`,` attachment`, `aw`,
+`beshare`, `bitcoin`, `bolo`, `callto`, `chrome`,` chrome-extension`,
+`com-eventbrite-attendee`, `content`, `cvs`,` dlna-playsingle`,
+`dlna-playcontainer`, `dtn`, `dvb`, `ed2k`, `facetime`, `feed`,
+`finger`, `fish`, `gg`, `git`, `gizmoproject`, `gtalk`, `hcp`, `icon`,
+`ipn`, `irc`, `irc6`, `ircs`, `itms`, `jar`, `jms`, `keyparc`, `lastfm`,
+`ldaps`, `magnet`, `maps`, `market`,` message`, `mms`, `ms-help`,
+`msnim`, `mumble`, `mvn`, `notes`, `oid`, `palm`, `paparazzi`,
+`platform`, `proxy`, `psyc`, `query`, `res`, `resource`, `rmi`, `rsync`,
+`rtmp`, `secondlife`, `sftp`, `sgn`, `skype`, `smb`, `soldat`,
+`spotify`, `ssh`, `steam`, `svn`, `teamspeak`, `things`, `udp`,
+`unreal`, `ut2004`, `ventrilo`, `view-source`, `webcal`, `wtai`,
+`wyciwyg`, `xfire`, `xri`, `ymsgr`.
+
+Here are some valid autolinks:
+
+.
+<http://foo.bar.baz>
+.
+<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
+.
+
+.
+<http://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>
+.
+
+.
+<irc://foo.bar:2233/baz>
+.
+<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
+.
+
+Uppercase is also fine:
+
+.
+<MAILTO:FOO@BAR.BAZ>
+.
+<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
+.
+
+Spaces are not allowed in autolinks:
+
+.
+<http://foo.bar/baz bim>
+.
+<p>&lt;http://foo.bar/baz bim&gt;</p>
+.
+
+An [email autolink](#email-autolink) <a id="email-autolink"></a>
+consists of `<`, followed by an [email address](#email-address),
+followed by `>`. The link's label is the email address,
+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):
+
+ /^[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])?)*$/
+
+Examples of email autolinks:
+
+.
+<foo@bar.baz.com>
+.
+<p><a href="mailto:foo@bar.baz.com">foo@bar.baz.com</a></p>
+.
+
+.
+<foo+special@Bar.baz-bar0.com>
+.
+<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
+.
+
+These are not autolinks:
+
+.
+<>
+.
+<p>&lt;&gt;</p>
+.
+
+.
+<heck://bing.bong>
+.
+<p>&lt;heck://bing.bong&gt;</p>
+.
+
+.
+< http://foo.bar >
+.
+<p>&lt; http://foo.bar &gt;</p>
+.
+
+.
+<foo.bar.baz>
+.
+<p>&lt;foo.bar.baz&gt;</p>
+.
+
+.
+<localhost:5001/foo>
+.
+<p>&lt;localhost:5001/foo&gt;</p>
+.
+
+.
+http://google.com
+.
+<p>http://google.com</p>
+.
+
+.
+foo@bar.baz.com
+.
+<p>foo@bar.baz.com</p>
+.
+
+## Raw HTML
+
+Text between `<` and `>` that looks like an HTML tag is parsed as a
+raw HTML tag and will be rendered in HTML without escaping.
+Tag and attribute names are not limited to current HTML tags,
+so custom tags (and even, say, DocBook tags) may be used.
+
+Here is the grammar for tags:
+
+A [tag name](#tag-name) <a id="tag-name"></a> consists of an ASCII letter
+followed by zero or more ASCII letters or digits.
+
+An [attribute](#attribute) <a id="attribute"></a> consists of whitespace,
+an **attribute name**, and an optional **attribute value
+specification**.
+
+An [attribute name](#attribute-name) <a id="attribute-name"></a>
+consists of an ASCII letter, `_`, or `:`, followed by zero or more ASCII
+letters, digits, `_`, `.`, `:`, or `-`. (Note: This is the XML
+specification restricted to ASCII. HTML5 is laxer.)
+
+An [attribute value specification](#attribute-value-specification)
+<a id="attribute-value-specification"></a> consists of optional whitespace,
+a `=` character, optional whitespace, and an [attribute
+value](#attribute-value).
+
+An [attribute value](#attribute-value) <a id="attribute-value"></a>
+consists of an [unquoted attribute value](#unquoted-attribute-value),
+a [single-quoted attribute value](#single-quoted-attribute-value),
+or a [double-quoted attribute value](#double-quoted-attribute-value).
+
+An [unquoted attribute value](#unquoted-attribute-value)
+<a id="unquoted-attribute-value"></a> is a nonempty string of characters not
+including spaces, `"`, `'`, `=`, `<`, `>`, or `` ` ``.
+
+A [single-quoted attribute value](#single-quoted-attribute-value)
+<a id="single-quoted-attribute-value"></a> consists of `'`, zero or more
+characters not including `'`, and a final `'`.
+
+A [double-quoted attribute value](#double-quoted-attribute-value)
+<a id="double-quoted-attribute-value"></a> consists of `"`, zero or more
+characters not including `"`, and a final `"`.
+
+An [open tag](#open-tag) <a id="open-tag"></a> consists of a `<` character,
+a [tag name](#tag-name), zero or more [attributes](#attribute),
+optional whitespace, an optional `/` character, and a `>` character.
+
+A [closing tag](#closing-tag) <a id="closing-tag"></a> consists of the
+string `</`, a [tag name](#tag-name), optional whitespace, and the
+character `>`.
+
+An [HTML comment](#html-comment) <a id="html-comment"></a> consists of the
+string `<!--`, a string of characters not including the string `--`, and
+the string `-->`.
+
+A [processing instruction](#processing-instruction)
+<a id="processing-instruction"></a> consists of the string `<?`, a string
+of characters not including the string `?>`, and the string
+`?>`.
+
+A [declaration](#declaration) <a id="declaration"></a> consists of the
+string `<!`, a name consisting of one or more uppercase ASCII letters,
+whitespace, a string of characters not including the character `>`, and
+the character `>`.
+
+A [CDATA section](#cdata-section) <a id="cdata-section"></a> consists of
+the string `<![CDATA[`, a string of characters not including the string
+`]]>`, and the string `]]>`.
+
+An [HTML tag](#html-tag) <a id="html-tag"></a> consists of an [open
+tag](#open-tag), a [closing tag](#closing-tag), an [HTML
+comment](#html-comment), a [processing
+instruction](#processing-instruction), an [element type
+declaration](#element-type-declaration), or a [CDATA
+section](#cdata-section).
+
+Here are some simple open tags:
+
+.
+<a><bab><c2c>
+.
+<p><a><bab><c2c></p>
+.
+
+Empty elements:
+
+.
+<a/><b2/>
+.
+<p><a/><b2/></p>
+.
+
+Whitespace is allowed:
+
+.
+<a /><b2
+data="foo" >
+.
+<p><a /><b2
+data="foo" ></p>
+.
+
+With attributes:
+
+.
+<a foo="bar" bam = 'baz <em>"</em>'
+_boolean zoop:33=zoop:33 />
+.
+<p><a foo="bar" bam = 'baz <em>"</em>'
+_boolean zoop:33=zoop:33 /></p>
+.
+
+Illegal tag names, not parsed as HTML:
+
+.
+<33> <__>
+.
+<p>&lt;33&gt; &lt;__&gt;</p>
+.
+
+Illegal attribute names:
+
+.
+<a h*#ref="hi">
+.
+<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>
+.
+
+Illegal attribute values:
+
+.
+<a href="hi'> <a href=hi'>
+.
+<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>
+.
+
+Illegal whitespace:
+
+.
+< a><
+foo><bar/ >
+.
+<p>&lt; a&gt;&lt;
+foo&gt;&lt;bar/ &gt;</p>
+.
+
+Missing whitespace:
+
+.
+<a href='bar'title=title>
+.
+<p>&lt;a href='bar'title=title&gt;</p>
+.
+
+Closing tags:
+
+.
+</a>
+</foo >
+.
+<p></a>
+</foo ></p>
+.
+
+Illegal attributes in closing tag:
+
+.
+</a href="foo">
+.
+<p>&lt;/a href=&quot;foo&quot;&gt;</p>
+.
+
+Comments:
+
+.
+foo <!-- this is a
+comment - with hyphen -->
+.
+<p>foo <!-- this is a
+comment - with hyphen --></p>
+.
+
+.
+foo <!-- not a comment -- two hyphens -->
+.
+<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
+.
+
+Processing instructions:
+
+.
+foo <?php echo $a; ?>
+.
+<p>foo <?php echo $a; ?></p>
+.
+
+Declarations:
+
+.
+foo <!ELEMENT br EMPTY>
+.
+<p>foo <!ELEMENT br EMPTY></p>
+.
+
+CDATA sections:
+
+.
+foo <![CDATA[>&<]]>
+.
+<p>foo <![CDATA[>&<]]></p>
+.
+
+Entities are preserved in HTML attributes:
+
+.
+<a href="&ouml;">
+.
+<p><a href="&ouml;"></p>
+.
+
+Backslash escapes do not work in HTML attributes:
+
+.
+<a href="\*">
+.
+<p><a href="\*"></p>
+.
+
+.
+<a href="\"">
+.
+<p>&lt;a href=&quot;&quot;&quot;&gt;</p>
+.
+
+## Hard line breaks
+
+A line break (not in a code span or HTML tag) that is preceded
+by two or more spaces is parsed as a linebreak (rendered
+in HTML as a `<br />` tag):
+
+.
+foo
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+For a more visible alternative, a backslash before the newline may be
+used instead of two spaces:
+
+.
+foo\
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+More than two spaces can be used:
+
+.
+foo
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+Leading spaces at the beginning of the next line are ignored:
+
+.
+foo
+ bar
+.
+<p>foo<br />
+bar</p>
+.
+
+.
+foo\
+ bar
+.
+<p>foo<br />
+bar</p>
+.
+
+Line breaks can occur inside emphasis, links, and other constructs
+that allow inline content:
+
+.
+*foo
+bar*
+.
+<p><em>foo<br />
+bar</em></p>
+.
+
+.
+*foo\
+bar*
+.
+<p><em>foo<br />
+bar</em></p>
+.
+
+Line breaks do not occur inside code spans
+
+.
+`code
+span`
+.
+<p><code>code span</code></p>
+.
+
+.
+`code\
+span`
+.
+<p><code>code\ span</code></p>
+.
+
+or HTML tags:
+
+.
+<a href="foo
+bar">
+.
+<p><a href="foo
+bar"></p>
+.
+
+.
+<a href="foo\
+bar">
+.
+<p><a href="foo\
+bar"></p>
+.
+
+## Soft line breaks
+
+A regular line break (not in a code span or HTML tag) that is not
+preceded by two or more spaces is parsed as a softbreak. (A
+softbreak may be rendered in HTML either as a newline or as a space.
+The result will be the same in browsers. In the examples here, a
+newline will be used.)
+
+.
+foo
+baz
+.
+<p>foo
+baz</p>
+.
+
+Spaces at the end of the line and beginning of the next line are
+removed:
+
+.
+foo
+ baz
+.
+<p>foo
+baz</p>
+.
+
+A conforming parser may render a soft line break in HTML either as a
+line break or as a space.
+
+A renderer may also provide an option to render soft line breaks
+as hard line breaks.
+
+## Strings
+
+Any characters not given an interpretation by the above rules will
+be parsed as string content.
+
+.
+hello $.;'there
+.
+<p>hello $.;'there</p>
+.
+
+.
+Foo χρῆν
+.
+<p>Foo χρῆν</p>
+.
+
+Internal spaces are preserved verbatim:
+
+.
+Multiple spaces
+.
+<p>Multiple spaces</p>
+.
+
+<!-- END TESTS -->
+
+# Appendix A: A parsing strategy {-}
+
+## Overview {-}
+
+Parsing has two phases:
+
+1. In the first phase, lines of input are consumed and the block
+structure of the document---its division into paragraphs, block quotes,
+list items, and so on---is constructed. Text is assigned to these
+blocks but not parsed. Link reference definitions are parsed and a
+map of links is constructed.
+
+2. In the second phase, the raw text contents of paragraphs and headers
+are parsed into sequences of Markdown inline elements (strings,
+code spans, links, emphasis, and so on), using the map of link
+references constructed in phase 1.
+
+## The document tree {-}
+
+At each point in processing, the document is represented as a tree of
+**blocks**. The root of the tree is a `document` block. The `document`
+may have any number of other blocks as **children**. These children
+may, in turn, have other blocks as children. The last child of a block
+is normally considered **open**, meaning that subsequent lines of input
+can alter its contents. (Blocks that are not open are **closed**.)
+Here, for example, is a possible document tree, with the open blocks
+marked by arrows:
+
+``` tree
+-> document
+ -> block_quote
+ paragraph
+ "Lorem ipsum dolor\nsit amet."
+ -> list (type=bullet tight=true bullet_char=-)
+ list_item
+ paragraph
+ "Qui *quodsi iracundia*"
+ -> list_item
+ -> paragraph
+ "aliquando id"
+```
+
+## How source lines alter the document tree {-}
+
+Each line that is processed has an effect on this tree. The line is
+analyzed and, depending on its contents, the document may be altered
+in one or more of the following ways:
+
+1. One or more open blocks may be closed.
+2. One or more new blocks may be created as children of the
+ last open block.
+3. Text may be added to the last (deepest) open block remaining
+ on the tree.
+
+Once a line has been incorporated into the tree in this way,
+it can be discarded, so input can be read in a stream.
+
+We can see how this works by considering how the tree above is
+generated by four lines of Markdown:
+
+``` markdown
+> Lorem ipsum dolor
+sit amet.
+> - Qui *quodsi iracundia*
+> - aliquando id
+```
+
+At the outset, our document model is just
+
+``` tree
+-> document
+```
+
+The first line of our text,
+
+``` markdown
+> Lorem ipsum dolor
+```
+
+causes a `block_quote` block to be created as a child of our
+open `document` block, and a `paragraph` block as a child of
+the `block_quote`. Then the text is added to the last open
+block, the `paragraph`:
+
+``` tree
+-> document
+ -> block_quote
+ -> paragraph
+ "Lorem ipsum dolor"
+```
+
+The next line,
+
+``` markdown
+sit amet.
+```
+
+is a "lazy continuation" of the open `paragraph`, so it gets added
+to the paragraph's text:
+
+``` tree
+-> document
+ -> block_quote
+ -> paragraph
+ "Lorem ipsum dolor\nsit amet."
+```
+
+The third line,
+
+``` markdown
+> - Qui *quodsi iracundia*
+```
+
+causes the `paragraph` block to be closed, and a new `list` block
+opened as a child of the `block_quote`. A `list_item` is also
+added as a child of the `list`, and a `paragraph` as a child of
+the `list_item`. The text is then added to the new `paragraph`:
+
+``` tree
+-> document
+ -> block_quote
+ paragraph
+ "Lorem ipsum dolor\nsit amet."
+ -> list (type=bullet tight=true bullet_char=-)
+ -> list_item
+ -> paragraph
+ "Qui *quodsi iracundia*"
+```
+
+The fourth line,
+
+``` markdown
+> - aliquando id
+```
+
+causes the `list_item` (and its child the `paragraph`) to be closed,
+and a new `list_item` opened up as child of the `list`. A `paragraph`
+is added as a child of the new `list_item`, to contain the text.
+We thus obtain the final tree:
+
+``` tree
+-> document
+ -> block_quote
+ paragraph
+ "Lorem ipsum dolor\nsit amet."
+ -> list (type=bullet tight=true bullet_char=-)
+ list_item
+ paragraph
+ "Qui *quodsi iracundia*"
+ -> list_item
+ -> paragraph
+ "aliquando id"
+```
+
+## From block structure to the final document {-}
+
+Once all of the input has been parsed, all open blocks are closed.
+
+We then "walk the tree," visiting every node, and parse raw
+string contents of paragraphs and headers as inlines. At this
+point we have seen all the link reference definitions, so we can
+resolve reference links as we go.
+
+``` tree
+document
+ block_quote
+ paragraph
+ str "Lorem ipsum dolor"
+ softbreak
+ str "sit amet."
+ list (type=bullet tight=true bullet_char=-)
+ list_item
+ paragraph
+ str "Qui "
+ emph
+ str "quodsi iracundia"
+ list_item
+ paragraph
+ str "aliquando id"
+```
+
+Notice how the newline in the first paragraph has been parsed as
+a `softbreak`, and the asterisks in the first list item have become
+an `emph`.
+
+The document can be rendered as HTML, or in any other format, given
+an appropriate renderer.
+
+
diff --git a/core/testdata/packagedocs/referenceLinks.kotlin.md b/core/testdata/packagedocs/referenceLinks.kotlin.md
new file mode 100644
index 000000000..ac7e4b48a
--- /dev/null
+++ b/core/testdata/packagedocs/referenceLinks.kotlin.md
@@ -0,0 +1,7 @@
+
+
+Core functions and types
+See [ref](http://example.com)
+Also, [example](http://example.com)
+
+ \ No newline at end of file
diff --git a/core/testdata/packagedocs/referenceLinks.md b/core/testdata/packagedocs/referenceLinks.md
new file mode 100644
index 000000000..7583ee9d7
--- /dev/null
+++ b/core/testdata/packagedocs/referenceLinks.md
@@ -0,0 +1,17 @@
+# Module refLinks
+
+## Kotlin Standard Library
+
+The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections,
+text and files.
+See [ref]
+Also, [example][ref]
+
+# Package kotlin
+
+Core functions and types
+See [ref]
+Also, [example][ref]
+
+<!-- Refs -->
+[ref]: http://example.com
diff --git a/core/testdata/packagedocs/referenceLinks.module.md b/core/testdata/packagedocs/referenceLinks.module.md
new file mode 100644
index 000000000..ddbdbe2f8
--- /dev/null
+++ b/core/testdata/packagedocs/referenceLinks.module.md
@@ -0,0 +1,9 @@
+
+
+## Kotlin Standard Library
+
+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)
+
diff --git a/core/testdata/packagedocs/stdlib.md b/core/testdata/packagedocs/stdlib.md
new file mode 100644
index 000000000..5d7432b50
--- /dev/null
+++ b/core/testdata/packagedocs/stdlib.md
@@ -0,0 +1,11 @@
+# Module stdlib
+
+## Kotlin Standard Library
+
+The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections,
+text and files.
+
+# Package kotlin
+
+Core functions and types
+
diff --git a/core/testdata/packages/classInPackage.kt b/core/testdata/packages/classInPackage.kt
new file mode 100644
index 000000000..b22273afb
--- /dev/null
+++ b/core/testdata/packages/classInPackage.kt
@@ -0,0 +1,3 @@
+package simple.name
+
+class Foo {}
diff --git a/core/testdata/packages/dottedNamePackage.kt b/core/testdata/packages/dottedNamePackage.kt
new file mode 100644
index 000000000..386193104
--- /dev/null
+++ b/core/testdata/packages/dottedNamePackage.kt
@@ -0,0 +1 @@
+package dot.name \ No newline at end of file
diff --git a/core/testdata/packages/rootPackage.kt b/core/testdata/packages/rootPackage.kt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/core/testdata/packages/rootPackage.kt
diff --git a/core/testdata/packages/simpleNamePackage.kt b/core/testdata/packages/simpleNamePackage.kt
new file mode 100644
index 000000000..2c29f4c70
--- /dev/null
+++ b/core/testdata/packages/simpleNamePackage.kt
@@ -0,0 +1 @@
+package simple
diff --git a/core/testdata/packages/simpleNamePackage2.kt b/core/testdata/packages/simpleNamePackage2.kt
new file mode 100644
index 000000000..2c29f4c70
--- /dev/null
+++ b/core/testdata/packages/simpleNamePackage2.kt
@@ -0,0 +1 @@
+package simple
diff --git a/core/testdata/properties/annotatedProperty.kt b/core/testdata/properties/annotatedProperty.kt
new file mode 100644
index 000000000..3c12691b1
--- /dev/null
+++ b/core/testdata/properties/annotatedProperty.kt
@@ -0,0 +1 @@
+@Strictfp var property = "test" \ No newline at end of file
diff --git a/core/testdata/properties/propertyOverride.kt b/core/testdata/properties/propertyOverride.kt
new file mode 100644
index 000000000..625d1da0b
--- /dev/null
+++ b/core/testdata/properties/propertyOverride.kt
@@ -0,0 +1,7 @@
+open class Foo() {
+ open val xyzzy: Int get() = 0
+}
+
+class Bar(): Foo() {
+ override val xyzzy: Int get() = 1
+}
diff --git a/core/testdata/properties/propertyWithReceiver.kt b/core/testdata/properties/propertyWithReceiver.kt
new file mode 100644
index 000000000..e282f6bd9
--- /dev/null
+++ b/core/testdata/properties/propertyWithReceiver.kt
@@ -0,0 +1,2 @@
+val String.foobar: Int
+ get() = size() * 2
diff --git a/core/testdata/properties/sinceKotlin.kt b/core/testdata/properties/sinceKotlin.kt
new file mode 100644
index 000000000..e96f2349f
--- /dev/null
+++ b/core/testdata/properties/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Quite useful [String]
+ */
+@SinceKotlin("1.1")
+val `availableSince1.1`: String = "1.1 rulezz" \ No newline at end of file
diff --git a/core/testdata/properties/valueProperty.kt b/core/testdata/properties/valueProperty.kt
new file mode 100644
index 000000000..b87cce575
--- /dev/null
+++ b/core/testdata/properties/valueProperty.kt
@@ -0,0 +1 @@
+val property = "test" \ No newline at end of file
diff --git a/core/testdata/properties/valuePropertyWithGetter.kt b/core/testdata/properties/valuePropertyWithGetter.kt
new file mode 100644
index 000000000..64d3848c5
--- /dev/null
+++ b/core/testdata/properties/valuePropertyWithGetter.kt
@@ -0,0 +1,2 @@
+val property: String
+ get() = "test" \ No newline at end of file
diff --git a/core/testdata/properties/variableProperty.kt b/core/testdata/properties/variableProperty.kt
new file mode 100644
index 000000000..54ab45959
--- /dev/null
+++ b/core/testdata/properties/variableProperty.kt
@@ -0,0 +1 @@
+var property = "test" \ No newline at end of file
diff --git a/core/testdata/properties/variablePropertyWithAccessors.kt b/core/testdata/properties/variablePropertyWithAccessors.kt
new file mode 100644
index 000000000..152fb7d0a
--- /dev/null
+++ b/core/testdata/properties/variablePropertyWithAccessors.kt
@@ -0,0 +1,4 @@
+var property: String
+ get() = "test"
+ set(value) {
+ } \ No newline at end of file
diff --git a/core/testdata/typealias/asTypeBoundWithVariance.kt b/core/testdata/typealias/asTypeBoundWithVariance.kt
new file mode 100644
index 000000000..1aef84d69
--- /dev/null
+++ b/core/testdata/typealias/asTypeBoundWithVariance.kt
@@ -0,0 +1,7 @@
+package _typealias.astypebound
+class A
+
+typealias B = A
+
+class C<out T : B>
+class D<in T : B> \ No newline at end of file
diff --git a/core/testdata/typealias/chain.kt b/core/testdata/typealias/chain.kt
new file mode 100644
index 000000000..520be5535
--- /dev/null
+++ b/core/testdata/typealias/chain.kt
@@ -0,0 +1,8 @@
+package _typealias.chain
+
+class A
+
+typealias B = A
+
+typealias C = B
+
diff --git a/core/testdata/typealias/deprecated.kt b/core/testdata/typealias/deprecated.kt
new file mode 100644
index 000000000..b53d3a206
--- /dev/null
+++ b/core/testdata/typealias/deprecated.kt
@@ -0,0 +1,7 @@
+package _typealias.deprecated
+
+class Lol
+
+@Deprecated("Not mainstream now")
+typealias Kek = Lol
+
diff --git a/core/testdata/typealias/documented.kt b/core/testdata/typealias/documented.kt
new file mode 100644
index 000000000..3ca110e5f
--- /dev/null
+++ b/core/testdata/typealias/documented.kt
@@ -0,0 +1,9 @@
+package _typealias.documented
+
+class A
+
+/**
+ * Just typealias
+ */
+typealias B = A
+
diff --git a/core/testdata/typealias/functional.kt b/core/testdata/typealias/functional.kt
new file mode 100644
index 000000000..dadafa5e4
--- /dev/null
+++ b/core/testdata/typealias/functional.kt
@@ -0,0 +1,10 @@
+package _typealias.functional
+
+class A
+class B
+
+typealias Spell = (A) -> B
+
+fun magic(spell: Spell) {
+
+} \ No newline at end of file
diff --git a/core/testdata/typealias/generic.kt b/core/testdata/typealias/generic.kt
new file mode 100644
index 000000000..43bc0e23f
--- /dev/null
+++ b/core/testdata/typealias/generic.kt
@@ -0,0 +1,7 @@
+package _typealias.generic
+
+interface A<T>
+
+typealias B = A<Any>
+
+typealias C<T> = A<T> \ No newline at end of file
diff --git a/core/testdata/typealias/inheritanceFromTypeAlias.kt b/core/testdata/typealias/inheritanceFromTypeAlias.kt
new file mode 100644
index 000000000..f929ecd09
--- /dev/null
+++ b/core/testdata/typealias/inheritanceFromTypeAlias.kt
@@ -0,0 +1,7 @@
+package _typealias.inheritance
+
+open class Some
+
+typealias Same = Some
+
+class My : Same \ No newline at end of file
diff --git a/core/testdata/typealias/simple.kt b/core/testdata/typealias/simple.kt
new file mode 100644
index 000000000..d688a84d1
--- /dev/null
+++ b/core/testdata/typealias/simple.kt
@@ -0,0 +1,5 @@
+package _typealias.simple
+
+class A
+
+typealias B = A \ No newline at end of file
diff --git a/core/testdata/typealias/sinceKotlin.kt b/core/testdata/typealias/sinceKotlin.kt
new file mode 100644
index 000000000..5b76f63ad
--- /dev/null
+++ b/core/testdata/typealias/sinceKotlin.kt
@@ -0,0 +1,5 @@
+/**
+ * Documentation
+ */
+@SinceKotlin("1.1")
+typealias `Since 1.1` = String \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..2117d734f
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,22 @@
+dokka_version=0.9.17-g014
+dokka_publication_channel=dokka
+
+#Kotlin compiler and plugin
+bundled_kotlin_compiler_version=1.3.61
+kotlin_version=1.3.61
+kotlin_plugin_version=1.3.61-release-180
+idea_version=192.5728.98
+kotlin_for_gradle_runtime_version=1.3.61
+
+ant_version=1.9.6
+
+#Maven plugin dependencies
+maven_version=3.5.0
+maven_archiver_version=2.5
+plexus_utils_version=3.0.22
+plexus_archiver_version=3.4
+maven_plugin_tools_version=3.5
+
+#For CI
+mvn=mvn
+re
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..736fb7d3f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..bd24854fe
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 000000000..cccdd3d51
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..f9553162f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/integration/build.gradle b/integration/build.gradle
new file mode 100644
index 000000000..24d59edf0
--- /dev/null
+++ b/integration/build.gradle
@@ -0,0 +1,26 @@
+buildscript {
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+apply plugin: 'kotlin'
+
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ languageVersion = "1.2"
+ apiVersion = "1.1"
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version
+ compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version
+ compile('com.github.yole:jkid:8fc7f12e1a') {
+ transitive = false
+ }
+} \ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt
new file mode 100644
index 000000000..b78eb9c65
--- /dev/null
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt
@@ -0,0 +1,10 @@
+package org.jetbrains.dokka
+
+import java.util.function.BiConsumer
+
+interface DokkaBootstrap {
+
+ fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String)
+
+ fun generate()
+} \ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt b/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt
new file mode 100644
index 000000000..1984a3e5a
--- /dev/null
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt
@@ -0,0 +1,72 @@
+package org.jetbrains.dokka
+
+import kotlin.reflect.*
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.jvm.isAccessible
+
+object ReflectDsl {
+
+ class CallOrPropAccess(private val receiver: Any?,
+ private val clz: KClass<*>,
+ private val selector: String) {
+
+ @Suppress("UNCHECKED_CAST")
+ operator fun <T : Any?> invoke(vararg a: Any?): T {
+ return func!!.call(receiver, *a) as T
+ }
+
+ operator fun get(s: String): CallOrPropAccess {
+ return v<Any?>()!![s]
+ }
+
+ val func: KFunction<*>? by lazy { clz.memberFunctions.find { it.name == selector } }
+ val prop: KProperty<*>? by lazy { clz.memberProperties.find { it.name == selector } }
+
+ fun takeIfIsFunc(): CallOrPropAccess? = if (func != null) this else null
+
+ fun takeIfIsProp(): CallOrPropAccess? = if (prop != null) this else null
+
+ @Suppress("UNCHECKED_CAST")
+ fun <T : Any?> v(): T {
+ val prop = prop!!
+ return try {
+ prop.getter.apply { isAccessible = true }.call(receiver) as T
+ } catch (e: KotlinNullPointerException) {
+ // Hack around kotlin-reflect bug KT-18480
+ val jclass = clz.java
+ val customGetterName = prop.getter.name
+ val getterName = if (customGetterName.startsWith("<")) "get" + prop.name.capitalize() else customGetterName
+ val getter = jclass.getDeclaredMethod(getterName)
+ getter.isAccessible = true
+
+ getter.invoke(receiver) as T
+
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ fun v(x: Any?) {
+ (prop as KMutableProperty).setter.apply { isAccessible = true }.call(receiver, x)
+ }
+
+
+ }
+
+ operator fun Any.get(s: String): CallOrPropAccess {
+ val clz = this.javaClass.kotlin
+ return CallOrPropAccess(this, clz, s)
+ }
+
+ operator fun Any.get(s: String, clz: Class<*>): CallOrPropAccess {
+ val kclz = clz.kotlin
+ return CallOrPropAccess(this, kclz, s)
+ }
+
+ operator fun Any.get(s: String, clz: KClass<*>): CallOrPropAccess {
+ return CallOrPropAccess(this, clz, s)
+ }
+
+ inline infix fun Any.isInstance(clz: Class<*>?): Boolean = clz != null && clz.isAssignableFrom(this.javaClass)
+ inline infix fun Any.isNotInstance(clz: Class<*>?): Boolean = !(this isInstance clz)
+} \ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
new file mode 100644
index 000000000..9eacf5ec6
--- /dev/null
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
@@ -0,0 +1,122 @@
+package org.jetbrains.dokka
+
+import ru.yole.jkid.CustomSerializer
+import ru.yole.jkid.ValueSerializer
+import ru.yole.jkid.deserialization.JKidException
+import java.io.Serializable
+import java.net.URL
+
+
+class UrlSerializer : ValueSerializer<URL?> {
+ override fun fromJsonValue(jsonValue: Any?): URL? {
+ if (jsonValue !is String?)
+ throw JKidException("Expected string representation of URL, got: $jsonValue")
+ return jsonValue?.let { URL(jsonValue) }
+ }
+
+ override fun toJsonValue(value: URL?): Any? = value?.toExternalForm()
+}
+
+interface DokkaConfiguration {
+ val moduleName: String
+ val classpath: List<String>
+ val sourceRoots: List<SourceRoot>
+ val samples: List<String>
+ val includes: List<String>
+ val outputDir: String
+ val format: String
+ val includeNonPublic: Boolean
+ val includeRootPackage: Boolean
+ val reportUndocumented: Boolean
+ val skipEmptyPackages: Boolean
+ val skipDeprecated: Boolean
+ val jdkVersion: Int
+ val generateClassIndexPage: Boolean
+ val generatePackageIndexPage: Boolean
+ val sourceLinks: List<SourceLinkDefinition>
+ val impliedPlatforms: List<String>
+ val perPackageOptions: List<PackageOptions>
+ val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>
+ val languageVersion: String?
+ val apiVersion: String?
+ val noStdlibLink: Boolean
+ val noJdkLink: Boolean
+ val cacheRoot: String?
+ val suppressedFiles: List<String>
+ val collectInheritedExtensionsFromLibraries: Boolean
+ val outlineRoot: String
+ val dacRoot: String
+
+ interface SourceRoot {
+ val path: String
+ val platforms: List<String>
+ }
+
+ interface SourceLinkDefinition {
+ val path: String
+ val url: String
+ val lineSuffix: String?
+ }
+
+ interface PackageOptions {
+ val prefix: String
+ val includeNonPublic: Boolean
+ val reportUndocumented: Boolean
+ val skipDeprecated: Boolean
+ val suppress: Boolean
+ }
+
+ interface ExternalDocumentationLink {
+ @CustomSerializer(UrlSerializer::class) val url: URL
+ @CustomSerializer(UrlSerializer::class) val packageListUrl: URL
+
+ open class Builder(open var url: URL? = null,
+ open var packageListUrl: URL? = null) {
+
+ constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) })
+
+ fun build(): DokkaConfiguration.ExternalDocumentationLink =
+ if (packageListUrl != null && url != null)
+ ExternalDocumentationLinkImpl(url!!, packageListUrl!!)
+ else if (url != null)
+ ExternalDocumentationLinkImpl(url!!, URL(url!!, "package-list"))
+ else
+ throw IllegalArgumentException("url or url && packageListUrl must not be null for external documentation link")
+ }
+ }
+}
+
+data class SerializeOnlyDokkaConfiguration(
+ override val moduleName: String,
+ override val classpath: List<String>,
+ override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
+ 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 generateClassIndexPage: Boolean,
+ override val generatePackageIndexPage: Boolean,
+ override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
+ override val impliedPlatforms: List<String>,
+ override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
+ override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
+ override val noStdlibLink: Boolean,
+ override val noJdkLink: Boolean,
+ override val cacheRoot: String?,
+ override val suppressedFiles: List<String>,
+ override val languageVersion: String?,
+ override val apiVersion: String?,
+ override val collectInheritedExtensionsFromLibraries: Boolean,
+ override val outlineRoot: String,
+ override val dacRoot: String
+) : DokkaConfiguration
+
+
+data class ExternalDocumentationLinkImpl(@CustomSerializer(UrlSerializer::class) override val url: URL,
+ @CustomSerializer(UrlSerializer::class) override val packageListUrl: URL) : Serializable, DokkaConfiguration.ExternalDocumentationLink \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar
deleted file mode 100644
index 26ab40c6f..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5
deleted file mode 100644
index 10df6b20e..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-8ace5e5ab08ad920213fb38f7ef81a9c \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1
deleted file mode 100644
index 8457be521..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-25d19744222bcd2add5c5d9029db4820355c097a \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar
deleted file mode 100644
index 00ceebcca..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5
deleted file mode 100644
index 5736f3f12..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-9a0e3701cd3af6d83cd4c908278df7a8 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1
deleted file mode 100644
index 5eb6136e3..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cdf18a8d0a20a0c6fa76a2fb5788d68fff9e7335 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom
deleted file mode 100644
index f00c61277..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-android-gradle-plugin</artifactId>
- <version>0.9.17-g001</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g001</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5
deleted file mode 100644
index 95d2b0761..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-dd11486f187fd3d4bb6b72790b9d37bc \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1
deleted file mode 100644
index 3b3881501..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-16b7f1c0e34a5b8b16d36afe90f81786fc49ff2a \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar
deleted file mode 100644
index dbb67198d..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5
deleted file mode 100644
index 86c5ed506..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-5486aa0ab4976a5ff8dcb15d4ee2f7d1 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1
deleted file mode 100644
index ae375f522..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c8f65513ff029530d2e680310bfc7358765e4bfe \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar
deleted file mode 100644
index 0b8d0a1f9..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5
deleted file mode 100644
index 61ad006bd..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-16c1b1350e5d416c790c0d778e7cef46 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1
deleted file mode 100644
index 831005de9..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2317a2a7351c48f759ac6194a32a66dc7b0cadea \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom
deleted file mode 100644
index 95b493e58..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-android-gradle-plugin</artifactId>
- <version>0.9.17-g002</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g002</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5
deleted file mode 100644
index 9fd980a80..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-b1215e8f6e57314dafc6c145e74f351d \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1
deleted file mode 100644
index d32d53808..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-957e110ad89134389355af585988d464a8525614 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar
deleted file mode 100644
index e497d725b..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5
deleted file mode 100644
index bd34722d7..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-6e8becd2d093a3525324b965df802428 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1
deleted file mode 100644
index ec2207666..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2526367fbadfdf2f968b344f74a310e313a76772 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar
deleted file mode 100644
index 7047ab158..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5
deleted file mode 100644
index 2772e427f..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-9a70b8755a3e6240c61e802956a6d9b3 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1
deleted file mode 100644
index 19b522a64..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6f22b8e132e92691a8babdaae182ad8686c3070b \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom
deleted file mode 100644
index 9dd716383..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-android-gradle-plugin</artifactId>
- <version>0.9.17-g20190326</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g20190326</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5
deleted file mode 100644
index 6dccae919..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-a2e2583a875ffcfa13d071cbdf775e45 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1
deleted file mode 100644
index 131bd66b1..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-aaed6c55109462aa32d10619e3c9f4d549fc1102 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml
deleted file mode 100644
index 96198ecdb..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-android-gradle-plugin</artifactId>
- <versioning>
- <release>0.9.17-g002</release>
- <versions>
- <version>0.9.17-g001</version>
- <version>0.9.17-g002</version>
- </versions>
- <lastUpdated>20190426203004</lastUpdated>
- </versioning>
-</metadata>
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5
deleted file mode 100644
index 193b2a256..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-44af56929dc8cb7f77b27048a7c70af6 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1
deleted file mode 100644
index 0fc74b899..000000000
--- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fd61be9ee19d3261be71750198b244f5de95a27a \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar
deleted file mode 100644
index 7f5be648b..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5
deleted file mode 100644
index a3d8376f4..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-c0b51fe955e9dec5c2ccd65e32129394 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1
deleted file mode 100644
index 036da77ea..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9fff93a4b0c962e4c35e44d647cb7585025966e2 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom
deleted file mode 100644
index a76869b64..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g001</version>
- <dependencies/>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5
deleted file mode 100644
index afa61ea38..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-08372135fd740612250978da64378304 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1
deleted file mode 100644
index 47fb2e13c..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-ec6f8732761fa40cef30a61564544650999cddad \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar
deleted file mode 100644
index b4912535a..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5
deleted file mode 100644
index b73763fbd..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-723f6e53434c3f246ef246b9d54546ec \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1
deleted file mode 100644
index 92c579151..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cbbc1abbb490506c288b04bef4a474f66c14248d \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom
deleted file mode 100644
index 4874d56fe..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g002</version>
- <dependencies/>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5
deleted file mode 100644
index b58616e9b..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-1d3438e4bda3d0462a188e22925c2cf9 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1
deleted file mode 100644
index 036f12e8f..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5696c0c820310f2bd06031e539fe45db6c049fe6 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar
deleted file mode 100644
index ca4e5ba38..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5
deleted file mode 100644
index b5615a67d..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-74bfd2075d61e4e1ea72b3d0bed67af7 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1
deleted file mode 100644
index b573931d0..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b86602c25c8af1eabaf684c5a3de6de9d3872741 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom
deleted file mode 100644
index 23850c8ec..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g20190326</version>
- <dependencies/>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5
deleted file mode 100644
index e272866a4..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-707d062402bd7a0c30f9cd733b74dbbf \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1
deleted file mode 100644
index ee721350a..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-be9593d35a7a4a01dbb6fcc5a8892b84834bb893 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml
deleted file mode 100644
index ade0f21c2..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <versioning>
- <release>0.9.17-g002</release>
- <versions>
- <version>0.9.17-g001</version>
- <version>0.9.17-g002</version>
- </versions>
- <lastUpdated>20190426203021</lastUpdated>
- </versioning>
-</metadata>
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5
deleted file mode 100644
index ee1dcd92e..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-19f5a9e35cb5a2f976732d2c6fdce072 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1
deleted file mode 100644
index b254d0895..000000000
--- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a3b637586a30b734c693dddd32b6dfb539a9bf20 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar
deleted file mode 100644
index 7b9c5c9d8..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5
deleted file mode 100644
index 5c3f32d12..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-3d825d4d14f6305a023289c2ff495d2b \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1
deleted file mode 100644
index c63a877d5..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0bb166c615756d749781d4338bfdec813db76567 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar
deleted file mode 100644
index 80e198636..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5
deleted file mode 100644
index 3131604a0..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-932e2c32ecbd0e9f0c79737d529c6c62 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1
deleted file mode 100644
index 63e5fb1a7..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-88dd53edbec4b233709a74c76f9124769fd803b9 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom
deleted file mode 100644
index ec009cdaa..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g001</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-stdlib</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-reflect</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5
deleted file mode 100644
index 431abfb94..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-a88943835bcd6e862d353fd5dd11e019 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1
deleted file mode 100644
index 91b2f9449..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-41a055d22c61aebf9f7c31b5426784441826d685 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar
deleted file mode 100644
index cd6250be5..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5
deleted file mode 100644
index 3ec5d5f19..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-a09db7759638bb894b986015ca0c9169 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1
deleted file mode 100644
index 2a8c3e1c3..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-faae7e10f5d63640ac1e79dd80bafc53ed5d9d11 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar
deleted file mode 100644
index 516fce5e7..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5
deleted file mode 100644
index a95403c11..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-8679d9b687e2cdec9c1f052b3d63e3a8 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1
deleted file mode 100644
index ae1c25413..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-96dfd696c5e565840b10c30be31a64dc9275df28 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom
deleted file mode 100644
index 125e1552c..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g002</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-stdlib</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-reflect</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5
deleted file mode 100644
index baeb693e0..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-1cd7c4c2bc752eb14251473cae7dc37d \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1
deleted file mode 100644
index 592845b2e..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4266039913bb663843eb33b5195f2bff8878b602 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar
deleted file mode 100644
index 393581ad5..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5
deleted file mode 100644
index 7b50864d9..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-3e87fce121909b7173f16fb9346afeb2 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1
deleted file mode 100644
index 8eec44da4..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a835d1ef0bf4e54991c9463e3cbea34c850616b3 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar
deleted file mode 100644
index 6ec858dd8..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5
deleted file mode 100644
index eb6d174a1..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-d34bf35dae1cb45e9a0ae493ffe6fc0e \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1
deleted file mode 100644
index 080780e55..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7ca1fe874dc743a98575e0b49c5a37d552d5f482 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom
deleted file mode 100644
index 8c86f20f8..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <version>0.9.17-g20190326</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-stdlib</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.jetbrains.kotlin</groupId>
- <artifactId>kotlin-reflect</artifactId>
- <version>1.1.60</version>
- <scope>runtime</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5
deleted file mode 100644
index db144612b..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-a01c99c82aa71ff958d90428edd840b7 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1
deleted file mode 100644
index bdbd81570..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fd114549ed5130489609230f1c2b1eacd1cd4f62 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml
deleted file mode 100644
index d6b334d92..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-gradle-plugin</artifactId>
- <versioning>
- <release>0.9.17-g002</release>
- <versions>
- <version>0.9.17-g001</version>
- <version>0.9.17-g002</version>
- </versions>
- <lastUpdated>20190426203021</lastUpdated>
- </versioning>
-</metadata>
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5
deleted file mode 100644
index bf2a8a50d..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-5c37b739b4a301ae5c4279cc220773e3 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1
deleted file mode 100644
index 4380925d3..000000000
--- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3ed1dce91921552fdacaf7ead753200532693cad \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar
deleted file mode 100644
index f178b2da6..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5
deleted file mode 100644
index 8ee52efde..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-9036720995e5d8c15f77ae2f8340484c \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1
deleted file mode 100644
index 277844714..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-add6e40581be0278abbd9daf156a52c3c783662e \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar
deleted file mode 100644
index ec7a671da..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5
deleted file mode 100644
index eeaa4e7da..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-7161ca58f1b468b2784a192cb3a0a07a \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1
deleted file mode 100644
index 8cc09184a..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b0c00e2c8aa4566ff19632eb893ec38d8a23a36d \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom
deleted file mode 100644
index 710e33ea0..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-maven-plugin</artifactId>
- <version>0.9.17-g001</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g001</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-core</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-model</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-plugin-api</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-archiver</artifactId>
- <version>2.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-utils</artifactId>
- <version>3.0.22</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-archiver</artifactId>
- <version>3.4</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven.plugin-tools</groupId>
- <artifactId>maven-plugin-annotations</artifactId>
- <version>3.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.github.olivergondza</groupId>
- <artifactId>maven-jdk-tools-wrapper</artifactId>
- <version>0.1</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5
deleted file mode 100644
index 308e20fc5..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-ffce86cc8e9d0a08103418b9f1621217 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1
deleted file mode 100644
index a1ccd5f70..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-96ecac18be7e6e9356def15951c7018c82b141cc \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar
deleted file mode 100644
index 89f439d16..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5
deleted file mode 100644
index e6b7b106d..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-d0cfa37683b71cebe36a9ddcf4b252b0 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1
deleted file mode 100644
index c2e0928b4..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-960cdd9df9bfe11653c45186ee660e42d793f506 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar
deleted file mode 100644
index c22827274..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5
deleted file mode 100644
index 5c495b966..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-c91e19b5fbacb31dc3db20dfb5741011 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1
deleted file mode 100644
index d905e6fcd..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-820dfe4fb5bbf9e2176ade510230cf72649f0473 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom
deleted file mode 100644
index 7fad14a11..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-maven-plugin</artifactId>
- <version>0.9.17-g002</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g002</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-core</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-model</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-plugin-api</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-archiver</artifactId>
- <version>2.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-utils</artifactId>
- <version>3.0.22</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-archiver</artifactId>
- <version>3.4</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven.plugin-tools</groupId>
- <artifactId>maven-plugin-annotations</artifactId>
- <version>3.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.github.olivergondza</groupId>
- <artifactId>maven-jdk-tools-wrapper</artifactId>
- <version>0.1</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5
deleted file mode 100644
index f981b25e3..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-01340b7038e1b92680be03d55f31aa90 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1
deleted file mode 100644
index 4be27f05d..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bd28aee1f70e6ae4e94756a521a297735471bccf \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar
deleted file mode 100644
index de270a9d4..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5
deleted file mode 100644
index 2936c5d7d..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-acfe18028be05579b8a7f1981e37b716 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1
deleted file mode 100644
index c9ce7bb6a..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6da950a8b364820e0d563f2731bc838e54955267 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar
deleted file mode 100644
index 647031bff..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar
+++ /dev/null
Binary files differ
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5
deleted file mode 100644
index 68da64848..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-3e63f51357d560c3cddefc6cfe6c884d \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1
deleted file mode 100644
index ee0cd3ae5..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d0a449a89d6286dab97401370d0f531cf00a5f47 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom
deleted file mode 100644
index 1e197b851..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-maven-plugin</artifactId>
- <version>0.9.17-g20190326</version>
- <dependencies>
- <dependency>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-fatjar</artifactId>
- <version>0.9.17-g20190326</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-core</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-model</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-plugin-api</artifactId>
- <version>3.5.0</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-archiver</artifactId>
- <version>2.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-utils</artifactId>
- <version>3.0.22</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-archiver</artifactId>
- <version>3.4</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven.plugin-tools</groupId>
- <artifactId>maven-plugin-annotations</artifactId>
- <version>3.5</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.github.olivergondza</groupId>
- <artifactId>maven-jdk-tools-wrapper</artifactId>
- <version>0.1</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</project>
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5
deleted file mode 100644
index f5a6cc764..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5
+++ /dev/null
@@ -1 +0,0 @@
-12ca67b1a18088cc21b421d2b063d369 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1
deleted file mode 100644
index a1c9f269a..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7eb7fa4d959e31cb8cb53e72e32302b40942b919 \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml
deleted file mode 100644
index 67cd845e3..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<metadata>
- <groupId>org.jetbrains.dokka</groupId>
- <artifactId>dokka-maven-plugin</artifactId>
- <versioning>
- <release>0.9.17-g002</release>
- <versions>
- <version>0.9.17-g001</version>
- <version>0.9.17-g002</version>
- </versions>
- <lastUpdated>20190426203024</lastUpdated>
- </versioning>
-</metadata>
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5
deleted file mode 100644
index 0621c34dd..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5
+++ /dev/null
@@ -1 +0,0 @@
-2237ba14abe5abe9aeb68717818ca30f \ No newline at end of file
diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1
deleted file mode 100644
index fea4ccee9..000000000
--- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4311edf2ba014d211159a400af0db5a96091aa97 \ No newline at end of file
diff --git a/package-list b/package-list
new file mode 100644
index 000000000..7cd2ff10c
--- /dev/null
+++ b/package-list
@@ -0,0 +1,217 @@
+$dokka.format:kotlin-website-html
+$dokka.linkExtension:html
+$dokka.location:kotlin$and(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/and.html
+$dokka.location:kotlin$dec(java.math.BigDecimal)kotlin/java.math.-big-decimal/dec.html
+$dokka.location:kotlin$dec(java.math.BigInteger)kotlin/java.math.-big-integer/dec.html
+$dokka.location:kotlin$div(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/div.html
+$dokka.location:kotlin$div(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/div.html
+$dokka.location:kotlin$inc(java.math.BigDecimal)kotlin/java.math.-big-decimal/inc.html
+$dokka.location:kotlin$inc(java.math.BigInteger)kotlin/java.math.-big-integer/inc.html
+$dokka.location:kotlin$inv(java.math.BigInteger)kotlin/java.math.-big-integer/inv.html
+$dokka.location:kotlin$minus(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/minus.html
+$dokka.location:kotlin$minus(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/minus.html
+$dokka.location:kotlin$mod(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/mod.html
+$dokka.location:kotlin$or(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/or.html
+$dokka.location:kotlin$plus(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/plus.html
+$dokka.location:kotlin$plus(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/plus.html
+$dokka.location:kotlin$rem(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/rem.html
+$dokka.location:kotlin$rem(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/rem.html
+$dokka.location:kotlin$shl(java.math.BigInteger, kotlin.Int)kotlin/java.math.-big-integer/shl.html
+$dokka.location:kotlin$shr(java.math.BigInteger, kotlin.Int)kotlin/java.math.-big-integer/shr.html
+$dokka.location:kotlin$times(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/times.html
+$dokka.location:kotlin$times(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/times.html
+$dokka.location:kotlin$toBigDecimal(java.math.BigInteger)kotlin/java.math.-big-integer/to-big-decimal.html
+$dokka.location:kotlin$toBigDecimal(java.math.BigInteger, kotlin.Int, java.math.MathContext)kotlin/java.math.-big-integer/to-big-decimal.html
+$dokka.location:kotlin$unaryMinus(java.math.BigDecimal)kotlin/java.math.-big-decimal/unary-minus.html
+$dokka.location:kotlin$unaryMinus(java.math.BigInteger)kotlin/java.math.-big-integer/unary-minus.html
+$dokka.location:kotlin$xor(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/xor.html
+$dokka.location:kotlin.ArithmeticExceptionkotlin/-arithmetic-exception/index.html
+$dokka.location:kotlin.AssertionErrorkotlin/-assertion-error/index.html
+$dokka.location:kotlin.ClassCastExceptionkotlin/-class-cast-exception/index.html
+$dokka.location:kotlin.Comparatorkotlin/-comparator/index.html
+$dokka.location:kotlin.ConcurrentModificationExceptionkotlin/-concurrent-modification-exception/index.html
+$dokka.location:kotlin.Errorkotlin/-error/index.html
+$dokka.location:kotlin.Exceptionkotlin/-exception/index.html
+$dokka.location:kotlin.IllegalArgumentExceptionkotlin/-illegal-argument-exception/index.html
+$dokka.location:kotlin.IllegalStateExceptionkotlin/-illegal-state-exception/index.html
+$dokka.location:kotlin.IndexOutOfBoundsExceptionkotlin/-index-out-of-bounds-exception/index.html
+$dokka.location:kotlin.NoSuchElementExceptionkotlin/-no-such-element-exception/index.html
+$dokka.location:kotlin.NullPointerExceptionkotlin/-null-pointer-exception/index.html
+$dokka.location:kotlin.NumberFormatExceptionkotlin/-number-format-exception/index.html
+$dokka.location:kotlin.RuntimeExceptionkotlin/-runtime-exception/index.html
+$dokka.location:kotlin.Synchronizedkotlin/-synchronized/index.html
+$dokka.location:kotlin.UnsupportedOperationExceptionkotlin/-unsupported-operation-exception/index.html
+$dokka.location:kotlin.Volatilekotlin/-volatile/index.html
+$dokka.location:kotlin.collections$getOrPut(java.util.concurrent.ConcurrentMap((kotlin.collections.getOrPut.K, kotlin.collections.getOrPut.V)), kotlin.collections.getOrPut.K, kotlin.Function0((kotlin.collections.getOrPut.V)))kotlin.collections/java.util.concurrent.-concurrent-map/get-or-put.html
+$dokka.location:kotlin.collections$iterator(java.util.Enumeration((kotlin.collections.iterator.T)))kotlin.collections/java.util.-enumeration/iterator.html
+$dokka.location:kotlin.collections$toList(java.util.Enumeration((kotlin.collections.toList.T)))kotlin.collections/java.util.-enumeration/to-list.html
+$dokka.location:kotlin.collections.ArrayListkotlin.collections/-array-list/index.html
+$dokka.location:kotlin.collections.HashMapkotlin.collections/-hash-map/index.html
+$dokka.location:kotlin.collections.HashSetkotlin.collections/-hash-set/index.html
+$dokka.location:kotlin.collections.LinkedHashMapkotlin.collections/-linked-hash-map/index.html
+$dokka.location:kotlin.collections.LinkedHashSetkotlin.collections/-linked-hash-set/index.html
+$dokka.location:kotlin.concurrent$getOrSet(java.lang.ThreadLocal((kotlin.concurrent.getOrSet.T)), kotlin.Function0((kotlin.concurrent.getOrSet.T)))kotlin.concurrent/java.lang.-thread-local/get-or-set.html
+$dokka.location:kotlin.concurrent$read(java.util.concurrent.locks.ReentrantReadWriteLock, kotlin.Function0((kotlin.concurrent.read.T)))kotlin.concurrent/java.util.concurrent.locks.-reentrant-read-write-lock/read.html
+$dokka.location:kotlin.concurrent$schedule(java.util.Timer, java.util.Date, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html
+$dokka.location:kotlin.concurrent$schedule(java.util.Timer, java.util.Date, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html
+$dokka.location:kotlin.concurrent$schedule(java.util.Timer, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html
+$dokka.location:kotlin.concurrent$schedule(java.util.Timer, kotlin.Long, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html
+$dokka.location:kotlin.concurrent$scheduleAtFixedRate(java.util.Timer, java.util.Date, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule-at-fixed-rate.html
+$dokka.location:kotlin.concurrent$scheduleAtFixedRate(java.util.Timer, kotlin.Long, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule-at-fixed-rate.html
+$dokka.location:kotlin.concurrent$withLock(java.util.concurrent.locks.Lock, kotlin.Function0((kotlin.concurrent.withLock.T)))kotlin.concurrent/java.util.concurrent.locks.-lock/with-lock.html
+$dokka.location:kotlin.concurrent$write(java.util.concurrent.locks.ReentrantReadWriteLock, kotlin.Function0((kotlin.concurrent.write.T)))kotlin.concurrent/java.util.concurrent.locks.-reentrant-read-write-lock/write.html
+$dokka.location:kotlin.io$appendBytes(java.io.File, kotlin.ByteArray)kotlin.io/java.io.-file/append-bytes.html
+$dokka.location:kotlin.io$appendText(java.io.File, kotlin.String, java.nio.charset.Charset)kotlin.io/java.io.-file/append-text.html
+$dokka.location:kotlin.io$buffered(java.io.InputStream, kotlin.Int)kotlin.io/java.io.-input-stream/buffered.html
+$dokka.location:kotlin.io$buffered(java.io.OutputStream, kotlin.Int)kotlin.io/java.io.-output-stream/buffered.html
+$dokka.location:kotlin.io$buffered(java.io.Reader, kotlin.Int)kotlin.io/java.io.-reader/buffered.html
+$dokka.location:kotlin.io$buffered(java.io.Writer, kotlin.Int)kotlin.io/java.io.-writer/buffered.html
+$dokka.location:kotlin.io$bufferedReader(java.io.File, java.nio.charset.Charset, kotlin.Int)kotlin.io/java.io.-file/buffered-reader.html
+$dokka.location:kotlin.io$bufferedReader(java.io.InputStream, java.nio.charset.Charset)kotlin.io/java.io.-input-stream/buffered-reader.html
+$dokka.location:kotlin.io$bufferedWriter(java.io.File, java.nio.charset.Charset, kotlin.Int)kotlin.io/java.io.-file/buffered-writer.html
+$dokka.location:kotlin.io$bufferedWriter(java.io.OutputStream, java.nio.charset.Charset)kotlin.io/java.io.-output-stream/buffered-writer.html
+$dokka.location:kotlin.io$copyRecursively(java.io.File, java.io.File, kotlin.Boolean, kotlin.Function2((java.io.File, java.io.IOException, kotlin.io.OnErrorAction)))kotlin.io/java.io.-file/copy-recursively.html
+$dokka.location:kotlin.io$copyTo(java.io.File, java.io.File, kotlin.Boolean, kotlin.Int)kotlin.io/java.io.-file/copy-to.html
+$dokka.location:kotlin.io$copyTo(java.io.InputStream, java.io.OutputStream, kotlin.Int)kotlin.io/java.io.-input-stream/copy-to.html
+$dokka.location:kotlin.io$copyTo(java.io.Reader, java.io.Writer, kotlin.Int)kotlin.io/java.io.-reader/copy-to.html
+$dokka.location:kotlin.io$deleteRecursively(java.io.File)kotlin.io/java.io.-file/delete-recursively.html
+$dokka.location:kotlin.io$endsWith(java.io.File, java.io.File)kotlin.io/java.io.-file/ends-with.html
+$dokka.location:kotlin.io$endsWith(java.io.File, kotlin.String)kotlin.io/java.io.-file/ends-with.html
+$dokka.location:kotlin.io$extension#java.io.Filekotlin.io/java.io.-file/extension.html
+$dokka.location:kotlin.io$forEachBlock(java.io.File, kotlin.Function2((kotlin.ByteArray, kotlin.Int, kotlin.Unit)))kotlin.io/java.io.-file/for-each-block.html
+$dokka.location:kotlin.io$forEachBlock(java.io.File, kotlin.Int, kotlin.Function2((kotlin.ByteArray, kotlin.Int, kotlin.Unit)))kotlin.io/java.io.-file/for-each-block.html
+$dokka.location:kotlin.io$forEachLine(java.io.File, java.nio.charset.Charset, kotlin.Function1((kotlin.String, kotlin.Unit)))kotlin.io/java.io.-file/for-each-line.html
+$dokka.location:kotlin.io$forEachLine(java.io.Reader, kotlin.Function1((kotlin.String, kotlin.Unit)))kotlin.io/java.io.-reader/for-each-line.html
+$dokka.location:kotlin.io$inputStream(java.io.File)kotlin.io/java.io.-file/input-stream.html
+$dokka.location:kotlin.io$invariantSeparatorsPath#java.io.Filekotlin.io/java.io.-file/invariant-separators-path.html
+$dokka.location:kotlin.io$isRooted#java.io.Filekotlin.io/java.io.-file/is-rooted.html
+$dokka.location:kotlin.io$iterator(java.io.BufferedInputStream)kotlin.io/java.io.-buffered-input-stream/iterator.html
+$dokka.location:kotlin.io$lineSequence(java.io.BufferedReader)kotlin.io/java.io.-buffered-reader/line-sequence.html
+$dokka.location:kotlin.io$nameWithoutExtension#java.io.Filekotlin.io/java.io.-file/name-without-extension.html
+$dokka.location:kotlin.io$normalize(java.io.File)kotlin.io/java.io.-file/normalize.html
+$dokka.location:kotlin.io$outputStream(java.io.File)kotlin.io/java.io.-file/output-stream.html
+$dokka.location:kotlin.io$printWriter(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/print-writer.html
+$dokka.location:kotlin.io$readBytes(java.io.File)kotlin.io/java.io.-file/read-bytes.html
+$dokka.location:kotlin.io$readBytes(java.io.InputStream)kotlin.io/java.io.-input-stream/read-bytes.html
+$dokka.location:kotlin.io$readBytes(java.io.InputStream, kotlin.Int)kotlin.io/java.io.-input-stream/read-bytes.html
+$dokka.location:kotlin.io$readBytes(java.net.URL)kotlin.io/java.net.-u-r-l/read-bytes.html
+$dokka.location:kotlin.io$readLines(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/read-lines.html
+$dokka.location:kotlin.io$readLines(java.io.Reader)kotlin.io/java.io.-reader/read-lines.html
+$dokka.location:kotlin.io$readText(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/read-text.html
+$dokka.location:kotlin.io$readText(java.io.Reader)kotlin.io/java.io.-reader/read-text.html
+$dokka.location:kotlin.io$readText(java.net.URL, java.nio.charset.Charset)kotlin.io/java.net.-u-r-l/read-text.html
+$dokka.location:kotlin.io$reader(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/reader.html
+$dokka.location:kotlin.io$reader(java.io.InputStream, java.nio.charset.Charset)kotlin.io/java.io.-input-stream/reader.html
+$dokka.location:kotlin.io$relativeTo(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to.html
+$dokka.location:kotlin.io$relativeToOrNull(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to-or-null.html
+$dokka.location:kotlin.io$relativeToOrSelf(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to-or-self.html
+$dokka.location:kotlin.io$resolve(java.io.File, java.io.File)kotlin.io/java.io.-file/resolve.html
+$dokka.location:kotlin.io$resolve(java.io.File, kotlin.String)kotlin.io/java.io.-file/resolve.html
+$dokka.location:kotlin.io$resolveSibling(java.io.File, java.io.File)kotlin.io/java.io.-file/resolve-sibling.html
+$dokka.location:kotlin.io$resolveSibling(java.io.File, kotlin.String)kotlin.io/java.io.-file/resolve-sibling.html
+$dokka.location:kotlin.io$startsWith(java.io.File, java.io.File)kotlin.io/java.io.-file/starts-with.html
+$dokka.location:kotlin.io$startsWith(java.io.File, kotlin.String)kotlin.io/java.io.-file/starts-with.html
+$dokka.location:kotlin.io$toRelativeString(java.io.File, java.io.File)kotlin.io/java.io.-file/to-relative-string.html
+$dokka.location:kotlin.io$useLines(java.io.File, java.nio.charset.Charset, kotlin.Function1((kotlin.sequences.Sequence((kotlin.String)), kotlin.io.useLines.T)))kotlin.io/java.io.-file/use-lines.html
+$dokka.location:kotlin.io$useLines(java.io.Reader, kotlin.Function1((kotlin.sequences.Sequence((kotlin.String)), kotlin.io.useLines.T)))kotlin.io/java.io.-reader/use-lines.html
+$dokka.location:kotlin.io$walk(java.io.File, kotlin.io.FileWalkDirection)kotlin.io/java.io.-file/walk.html
+$dokka.location:kotlin.io$walkBottomUp(java.io.File)kotlin.io/java.io.-file/walk-bottom-up.html
+$dokka.location:kotlin.io$walkTopDown(java.io.File)kotlin.io/java.io.-file/walk-top-down.html
+$dokka.location:kotlin.io$writeBytes(java.io.File, kotlin.ByteArray)kotlin.io/java.io.-file/write-bytes.html
+$dokka.location:kotlin.io$writeText(java.io.File, kotlin.String, java.nio.charset.Charset)kotlin.io/java.io.-file/write-text.html
+$dokka.location:kotlin.io$writer(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/writer.html
+$dokka.location:kotlin.io$writer(java.io.OutputStream, java.nio.charset.Charset)kotlin.io/java.io.-output-stream/writer.html
+$dokka.location:kotlin.jvm$kotlin#java.lang.Class((kotlin.jvm.kotlin.T))kotlin.jvm/java.lang.-class/kotlin.html
+$dokka.location:kotlin.random$asKotlinRandom(java.util.Random)kotlin.random/java.util.-random/as-kotlin-random.html
+$dokka.location:kotlin.reflect.KAnnotatedElementkotlin.reflect/-k-annotated-element/index.html
+$dokka.location:kotlin.reflect.KDeclarationContainerkotlin.reflect/-k-declaration-container/index.html
+$dokka.location:kotlin.reflect.KFunctionkotlin.reflect/-k-function/index.html
+$dokka.location:kotlin.reflect.KMutablePropertykotlin.reflect/-k-mutable-property/index.html
+$dokka.location:kotlin.reflect.KPropertykotlin.reflect/-k-property/index.html
+$dokka.location:kotlin.reflect.jvm$kotlinFunction#java.lang.reflect.Constructor((kotlin.reflect.jvm.kotlinFunction.T))kotlin.reflect.jvm/java.lang.reflect.-constructor/kotlin-function.html
+$dokka.location:kotlin.reflect.jvm$kotlinFunction#java.lang.reflect.Methodkotlin.reflect.jvm/java.lang.reflect.-method/kotlin-function.html
+$dokka.location:kotlin.reflect.jvm$kotlinProperty#java.lang.reflect.Fieldkotlin.reflect.jvm/java.lang.reflect.-field/kotlin-property.html
+$dokka.location:kotlin.sequences$asSequence(java.util.Enumeration((kotlin.sequences.asSequence.T)))kotlin.sequences/java.util.-enumeration/as-sequence.html
+$dokka.location:kotlin.streams$asSequence(java.util.stream.DoubleStream)kotlin.streams/java.util.stream.-double-stream/as-sequence.html
+$dokka.location:kotlin.streams$asSequence(java.util.stream.IntStream)kotlin.streams/java.util.stream.-int-stream/as-sequence.html
+$dokka.location:kotlin.streams$asSequence(java.util.stream.LongStream)kotlin.streams/java.util.stream.-long-stream/as-sequence.html
+$dokka.location:kotlin.streams$asSequence(java.util.stream.Stream((kotlin.streams.asSequence.T)))kotlin.streams/java.util.stream.-stream/as-sequence.html
+$dokka.location:kotlin.streams$asStream(kotlin.sequences.Sequence((kotlin.streams.asStream.T)))kotlin.streams/kotlin.sequences.-sequence/as-stream.html
+$dokka.location:kotlin.streams$toList(java.util.stream.DoubleStream)kotlin.streams/java.util.stream.-double-stream/to-list.html
+$dokka.location:kotlin.streams$toList(java.util.stream.IntStream)kotlin.streams/java.util.stream.-int-stream/to-list.html
+$dokka.location:kotlin.streams$toList(java.util.stream.LongStream)kotlin.streams/java.util.stream.-long-stream/to-list.html
+$dokka.location:kotlin.streams$toList(java.util.stream.Stream((kotlin.streams.toList.T)))kotlin.streams/java.util.stream.-stream/to-list.html
+$dokka.location:kotlin.text$appendln(java.lang.Appendable)kotlin.text/java.lang.-appendable/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.Appendable, kotlin.Char)kotlin.text/java.lang.-appendable/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.Appendable, kotlin.CharSequence)kotlin.text/java.lang.-appendable/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, java.lang.StringBuffer)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Any)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Boolean)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Byte)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Char)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.CharArray)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.CharSequence)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Double)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Float)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Int)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Long)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Short)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.String)kotlin.text/java.lang.-string-builder/appendln.html
+$dokka.location:kotlin.text$clear(java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/clear.html
+$dokka.location:kotlin.text$set(java.lang.StringBuilder, kotlin.Int, kotlin.Char)kotlin.text/java.lang.-string-builder/set.html
+$dokka.location:kotlin.text$toRegex(java.util.regex.Pattern)kotlin.text/java.util.regex.-pattern/to-regex.html
+$dokka.location:kotlin.text.Appendablekotlin.text/-appendable/index.html
+$dokka.location:kotlin.text.CharacterCodingExceptionkotlin.text/-character-coding-exception/index.html
+$dokka.location:kotlin.text.StringBuilderkotlin.text/-string-builder/index.html
+kotlin
+kotlin.annotation
+kotlin.browser
+kotlin.collections
+kotlin.comparisons
+kotlin.concurrent
+kotlin.contracts
+kotlin.coroutines
+kotlin.coroutines.experimental
+kotlin.coroutines.experimental.intrinsics
+kotlin.coroutines.intrinsics
+kotlin.dom
+kotlin.experimental
+kotlin.io
+kotlin.js
+kotlin.jvm
+kotlin.math
+kotlin.native
+kotlin.native.concurrent
+kotlin.native.ref
+kotlin.properties
+kotlin.random
+kotlin.ranges
+kotlin.reflect
+kotlin.reflect.full
+kotlin.reflect.jvm
+kotlin.sequences
+kotlin.streams
+kotlin.system
+kotlin.text
+kotlinx.cinterop
+kotlinx.cinterop.internal
+kotlinx.wasm.jsinterop
+org.khronos.webgl
+org.w3c.css.masking
+org.w3c.dom
+org.w3c.dom.clipboard
+org.w3c.dom.css
+org.w3c.dom.events
+org.w3c.dom.mediacapture
+org.w3c.dom.parsing
+org.w3c.dom.pointerevents
+org.w3c.dom.svg
+org.w3c.dom.url
+org.w3c.fetch
+org.w3c.files
+org.w3c.notifications
+org.w3c.performance
+org.w3c.workers
+org.w3c.xhr
diff --git a/runners/android-gradle-plugin/build.gradle b/runners/android-gradle-plugin/build.gradle
new file mode 100644
index 000000000..22403dc63
--- /dev/null
+++ b/runners/android-gradle-plugin/build.gradle
@@ -0,0 +1,102 @@
+import com.gradle.publish.DependenciesBuilder
+import org.jetbrains.CorrectShadowPublishing
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+
+
+apply plugin: 'com.github.johnrengelman.shadow'
+apply plugin: "com.gradle.plugin-publish"
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ freeCompilerArgs += "-Xjsr305=strict"
+ languageVersion = "1.2"
+ apiVersion = "1.1"
+ jvmTarget = "1.8"
+ }
+}
+
+repositories {
+ jcenter()
+}
+
+dependencies {
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+
+ shadow project(path: ':runners:gradle-plugin', configuration: 'shadow')
+ compileOnly project(':integration')
+
+ compileOnly gradleApi()
+ compileOnly localGroovy()
+}
+
+task sourceJar(type: Jar) {
+ from sourceSets.main.allSource
+}
+
+processResources {
+ inputs.property("dokka_version", dokka_version)
+ eachFile {
+ if (it.name == "org.jetbrains.dokka-android.properties") {
+ it.filter { line ->
+ line.replace("<version>", dokka_version)
+ }
+ }
+ }
+}
+
+shadowJar {
+ baseName = 'dokka-android-gradle-plugin'
+ classifier = ''
+}
+
+apply plugin: 'maven-publish'
+
+publishing {
+ publications {
+ dokkaAndroidGradlePlugin(MavenPublication) { MavenPublication publication ->
+ artifactId = 'dokka-android-gradle-plugin'
+
+ artifact sourceJar {
+ classifier "sources"
+ }
+
+ CorrectShadowPublishing.configure(publication, project)
+ }
+ }
+}
+
+bintrayPublication(project, ['dokkaAndroidGradlePlugin'])
+
+configurations.archives.artifacts.clear()
+artifacts {
+ archives shadowJar
+}
+
+pluginBundle {
+ website = 'http://www.kotlinlang.org/'
+ vcsUrl = 'https://github.com/kotlin/dokka.git'
+ description = 'Dokka, the Kotlin documentation tool'
+ tags = ['dokka', 'kotlin', 'kdoc', 'android']
+
+ plugins {
+ dokkaAndroidGradlePlugin {
+ id = 'org.jetbrains.dokka-android'
+ displayName = 'Dokka Android plugin'
+ }
+ }
+
+ withDependencies { List<Dependency> list ->
+ list.clear()
+ def builder = new DependenciesBuilder()
+ builder.addUniqueScopedDependencies(list, configurations.shadow, "compile")
+ }
+
+ mavenCoordinates {
+ groupId = "org.jetbrains.dokka"
+ artifactId = "dokka-android-gradle-plugin"
+ }
+}
diff --git a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
new file mode 100644
index 000000000..bd2e88c23
--- /dev/null
+++ b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
@@ -0,0 +1,39 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.Input
+import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder
+import java.io.File
+
+open class DokkaAndroidPlugin : Plugin<Project> {
+ override fun apply(project: Project) {
+ DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties"))
+ project.tasks.create("dokka", DokkaAndroidTask::class.java).apply {
+ moduleName = project.name
+ outputDirectory = File(project.buildDir, "dokka").absolutePath
+ }
+ }
+}
+
+private val ANDROID_REFERENCE_URL = Builder("https://developer.android.com/reference/").build()
+
+open class DokkaAndroidTask : DokkaTask() {
+
+ @Input var noAndroidSdkLink: Boolean = false
+
+ override fun collectSuppressedFiles(sourceRoots: List<SourceRoot>): List<String> {
+ val generatedRoot = project.buildDir.resolve("generated").absoluteFile
+ return sourceRoots
+ .map { File(it.path) }
+ .filter { it.startsWith(generatedRoot) }
+ .flatMap { it.walk().toList() }
+ .map { it.absolutePath }
+ }
+
+ init {
+ project.afterEvaluate {
+ if (!noAndroidSdkLink) externalDocumentationLinks.add(ANDROID_REFERENCE_URL)
+ }
+ }
+}
diff --git a/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties b/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties
new file mode 100644
index 000000000..b204da7b3
--- /dev/null
+++ b/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties
@@ -0,0 +1,2 @@
+implementation-class=org.jetbrains.dokka.gradle.DokkaAndroidPlugin
+dokka-version=<version> \ No newline at end of file
diff --git a/runners/ant/build.gradle b/runners/ant/build.gradle
new file mode 100644
index 000000000..e7dcd441e
--- /dev/null
+++ b/runners/ant/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'kotlin'
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ freeCompilerArgs += "-Xjsr305=strict"
+ languageVersion = "1.2"
+ apiVersion = languageVersion
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ compile project(":core")
+ compileOnly group: 'org.apache.ant', name: 'ant', version: ant_version
+}
+
diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt
new file mode 100644
index 000000000..20cf8c42f
--- /dev/null
+++ b/runners/ant/src/main/kotlin/ant/dokka.kt
@@ -0,0 +1,152 @@
+package org.jetbrains.dokka.ant
+
+import org.apache.tools.ant.BuildException
+import org.apache.tools.ant.Project
+import org.apache.tools.ant.Task
+import org.apache.tools.ant.types.Path
+import org.apache.tools.ant.types.Reference
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
+import java.io.File
+
+class AntLogger(val task: Task): DokkaLogger {
+ override fun info(message: String) = task.log(message, Project.MSG_INFO)
+ override fun warn(message: String) = task.log(message, Project.MSG_WARN)
+ override fun error(message: String) = task.log(message, Project.MSG_ERR)
+}
+
+class AntSourceLinkDefinition(var path: String? = null, var url: String? = null, var lineSuffix: String? = null)
+
+class AntSourceRoot(var path: String? = null, var platforms: String? = null) {
+ fun toSourceRoot(): SourceRootImpl? = path?.let {
+ path ->
+ SourceRootImpl(path, platforms?.split(',').orEmpty())
+ }
+}
+
+class AntPackageOptions(
+ override var prefix: String = "",
+ override var includeNonPublic: Boolean = false,
+ override var reportUndocumented: Boolean = true,
+ override var skipDeprecated: Boolean = false,
+ override var suppress: Boolean = false) : DokkaConfiguration.PackageOptions
+
+
+class DokkaAntTask: Task() {
+ var moduleName: String? = null
+ var outputDir: String? = null
+ var outputFormat: String = "html"
+ var impliedPlatforms: String = ""
+ var jdkVersion: Int = 6
+
+ var noStdlibLink: Boolean = false
+ var noJdkLink: Boolean = false
+
+ var skipDeprecated: Boolean = false
+
+ var cacheRoot: String? = null
+
+ var languageVersion: String? = null
+ var apiVersion: String? = null
+
+ var generateClassIndexPage: Boolean = true
+ var generatePackageIndexPage: Boolean = true
+ var outlineRoot: String = ""
+ var dacRoot: String = ""
+
+ val compileClasspath: Path by lazy { Path(getProject()) }
+ val sourcePath: Path by lazy { Path(getProject()) }
+ val samplesPath: Path by lazy { Path(getProject()) }
+ val includesPath: Path by lazy { Path(getProject()) }
+
+ val antSourceLinks: MutableList<AntSourceLinkDefinition> = arrayListOf()
+ val antSourceRoots: MutableList<AntSourceRoot> = arrayListOf()
+ val antPackageOptions: MutableList<AntPackageOptions> = arrayListOf()
+ val antExternalDocumentationLinks = mutableListOf<ExternalDocumentationLink.Builder>()
+
+ fun setClasspath(classpath: Path) {
+ compileClasspath.append(classpath)
+ }
+
+ fun setClasspathRef(ref: Reference) {
+ compileClasspath.createPath().refid = ref
+ }
+
+ fun setSrc(src: Path) {
+ sourcePath.append(src)
+ }
+
+ fun setSrcRef(ref: Reference) {
+ sourcePath.createPath().refid = ref
+ }
+
+ fun setSamples(samples: Path) {
+ samplesPath.append(samples)
+ }
+
+ fun setSamplesRef(ref: Reference) {
+ samplesPath.createPath().refid = ref
+ }
+
+ fun setInclude(include: Path) {
+ includesPath.append(include)
+ }
+
+ fun createSourceLink(): AntSourceLinkDefinition {
+ val def = AntSourceLinkDefinition()
+ antSourceLinks.add(def)
+ return def
+ }
+
+ fun createSourceRoot(): AntSourceRoot = AntSourceRoot().apply { antSourceRoots.add(this) }
+
+ fun createPackageOptions(): AntPackageOptions = AntPackageOptions().apply { antPackageOptions.add(this) }
+
+ fun createExternalDocumentationLink() = ExternalDocumentationLink.Builder().apply { antExternalDocumentationLinks.add(this) }
+
+ override fun execute() {
+ if (sourcePath.list().isEmpty() && antSourceRoots.isEmpty()) {
+ throw BuildException("At least one source path needs to be specified")
+ }
+ if (moduleName == null) {
+ throw BuildException("Module name needs to be specified")
+ }
+ if (outputDir == null) {
+ throw BuildException("Output directory needs to be specified")
+ }
+ val sourceLinks = antSourceLinks.map {
+ val path = it.path ?: throw BuildException("'path' attribute of a <sourceLink> element is required")
+ val url = it.url ?: throw BuildException("'url' attribute of a <sourceLink> element is required")
+ SourceLinkDefinitionImpl(File(path).canonicalFile.absolutePath, url, it.lineSuffix)
+ }
+
+ val generator = DokkaGenerator(
+ AntLogger(this),
+ compileClasspath.list().toList(),
+ sourcePath.list().map { SourceRootImpl(it) } + antSourceRoots.mapNotNull { it.toSourceRoot() },
+ samplesPath.list().toList(),
+ includesPath.list().toList(),
+ moduleName!!,
+ DocumentationOptions(
+ outputDir!!,
+ outputFormat,
+ skipDeprecated = skipDeprecated,
+ sourceLinks = sourceLinks,
+ jdkVersion = jdkVersion,
+ impliedPlatforms = impliedPlatforms.split(','),
+ perPackageOptions = antPackageOptions,
+ externalDocumentationLinks = antExternalDocumentationLinks.map { it.build() },
+ noStdlibLink = noStdlibLink,
+ noJdkLink = noJdkLink,
+ cacheRoot = cacheRoot,
+ languageVersion = languageVersion,
+ apiVersion = apiVersion,
+ generatePackageIndexPage = generatePackageIndexPage,
+ generateClassIndexPage = generateClassIndexPage,
+ outlineRoot = outlineRoot,
+ dacRoot = dacRoot
+ )
+ )
+ generator.generate()
+ }
+} \ No newline at end of file
diff --git a/runners/ant/src/main/resources/dokka-antlib.xml b/runners/ant/src/main/resources/dokka-antlib.xml
new file mode 100644
index 000000000..9c3373d52
--- /dev/null
+++ b/runners/ant/src/main/resources/dokka-antlib.xml
@@ -0,0 +1,3 @@
+<antlib>
+ <taskdef name="dokka" classname="org.jetbrains.dokka.ant.DokkaAntTask"/>
+</antlib>
diff --git a/runners/build.gradle b/runners/build.gradle
new file mode 100644
index 000000000..23d232d2e
--- /dev/null
+++ b/runners/build.gradle
@@ -0,0 +1,7 @@
+subprojects {
+ buildscript {
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+ }
+} \ No newline at end of file
diff --git a/runners/cli/build.gradle b/runners/cli/build.gradle
new file mode 100644
index 000000000..7f733140e
--- /dev/null
+++ b/runners/cli/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'kotlin'
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ languageVersion = "1.2"
+ apiVersion = languageVersion
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ compile project(":core")
+ compile "com.github.spullara.cli-parser:cli-parser:1.1.1"
+}
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
new file mode 100644
index 000000000..37be6e289
--- /dev/null
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -0,0 +1,221 @@
+package org.jetbrains.dokka
+
+
+import com.sampullara.cli.Args
+import com.sampullara.cli.Argument
+import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
+import org.jetbrains.dokka.Utilities.DownloadSamples
+
+import java.io.File
+import java.net.MalformedURLException
+import java.net.URL
+import java.net.URLClassLoader
+
+class DokkaArguments {
+ @set:Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)")
+ var src: String = ""
+
+ @set:Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code")
+ var srcLink: String = ""
+
+ @set:Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)")
+ var include: String = ""
+
+ @set:Argument(value = "samples", description = "Source root for samples")
+ var samples: String = ""
+
+ @set:Argument(value = "useSamplesURL", description = "Download samples from URL into root folder")
+ var useSamplesURL: Boolean = false
+
+ @set:Argument(value = "output", description = "Output directory path")
+ var outputDir: String = "out/doc/"
+
+ @set:Argument(value = "format", description = "Output format (text, html, markdown, jekyll, kotlin-website)")
+ var outputFormat: String = "html"
+
+ @set:Argument(value = "module", description = "Name of the documentation module")
+ var moduleName: String = ""
+
+ @set:Argument(value = "classpath", description = "Classpath for symbol resolution")
+ var classpath: String = ""
+
+ @set:Argument(value = "nodeprecated", description = "Exclude deprecated members from documentation")
+ var nodeprecated: Boolean = false
+
+ @set:Argument(value = "jdkVersion", description = "Version of JDK to use for linking to JDK JavaDoc")
+ var jdkVersion: Int = 6
+
+ @set:Argument(value = "impliedPlatforms", description = "List of implied platforms (comma-separated)")
+ var impliedPlatforms: String = ""
+
+ @set:Argument(value = "packageOptions", description = "List of package options in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" ")
+ var packageOptions: String = ""
+
+ @set:Argument(value = "links", description = "External documentation links in format url^packageListUrl^^url2...")
+ var links: String = ""
+
+ @set:Argument(value = "noStdlibLink", description = "Disable documentation link to stdlib")
+ var noStdlibLink: Boolean = false
+
+ @set:Argument(value = "noJdkLink", description = "Disable documentation link to jdk")
+ var noJdkLink: Boolean = false
+
+ @set:Argument(value = "cacheRoot", description = "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled")
+ var cacheRoot: String? = null
+
+ @set:Argument(value = "languageVersion", description = "Language Version to pass to Kotlin Analysis")
+ var languageVersion: String? = null
+
+ @set:Argument(value = "apiVersion", description = "Kotlin Api Version to pass to Kotlin Analysis")
+ var apiVersion: String? = null
+
+ @set:Argument(value = "collectInheritedExtensionsFromLibraries", description = "Search for applicable extensions in libraries")
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+
+ @set:Argument(value = "generateClassIndexPage", description = "Generate classes.html index page. [Deprecated]: use noGenerateClassIndexPage instead.")
+ @Deprecated("There is no way to set this to false.", replaceWith = ReplaceWith(
+ expression ="noGenerateClassIndexPage"))
+ var generateClassIndexPage: Boolean = true
+
+ @set:Argument(value = "generatePackageIndexPage", description = "Generate packages.html index page. [Deprecated]: use noGeneratePackageIndexPage instead.")
+ @Deprecated("There is no way to set this to false.", replaceWith = ReplaceWith(
+ expression ="noGeneratePackageIndexPage"))
+ var generatePackageIndexPage: Boolean = true
+
+ @set:Argument(value = "noGenerateClassIndexPage", description = "Disable classes.html index page.")
+ var noGenerateClassIndexPage: Boolean = false
+
+ @set:Argument(value = "noGeneratePackageIndexPage", description = "Disable packages.html index page.")
+ var noGeneratePackageIndexPage: Boolean = false
+
+ @set:Argument(value = "outlineRoot", description = "Relative root directory the outline files. I.e. androidx/core/")
+ var outlineRoot: String = ""
+
+ @set:Argument(value = "dacRoot", description = "Root directory in DAC of this library. I.e: /reference/kotlin")
+ var dacRoot: String = ""
+}
+
+
+object MainKt {
+
+ fun parseLinks(links: String): List<ExternalDocumentationLink> {
+ val (parsedLinks, parsedOfflineLinks) = links.split("^^")
+ .map { it.split("^").map { it.trim() }.filter { it.isNotBlank() } }
+ .filter { it.isNotEmpty() }
+ .partition { it.size == 1 }
+
+ return parsedLinks.map { (root) -> ExternalDocumentationLink.Builder(root).build() } +
+ parsedOfflineLinks.map { (root, packageList) ->
+ val rootUrl = URL(root)
+ val packageListUrl =
+ try {
+ URL(packageList)
+ } catch (ex: MalformedURLException) {
+ File(packageList).toURI().toURL()
+ }
+ ExternalDocumentationLink.Builder(rootUrl, packageListUrl).build()
+ }
+ }
+
+ @JvmStatic
+ fun entry(args: Array<String>) {
+ val arguments = DokkaArguments()
+ val freeArgs: List<String> = Args.parse(arguments, args) ?: listOf()
+ val sources = if (arguments.src.isNotEmpty()) arguments.src.split(File.pathSeparatorChar).toList() + freeArgs else freeArgs
+ val samples = if (arguments.samples.isNotEmpty()) arguments.samples.split(File.pathSeparatorChar).toList() else listOf()
+ val includes = if (arguments.include.isNotEmpty()) arguments.include.split(File.pathSeparatorChar).toList() else listOf()
+
+ val sourceLinks = if (arguments.srcLink.isNotEmpty() && arguments.srcLink.contains("="))
+ listOf(SourceLinkDefinitionImpl.parseSourceLinkDefinition(arguments.srcLink))
+ else {
+ if (arguments.srcLink.isNotEmpty()) {
+ println("Warning: Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.")
+ }
+ listOf()
+ }
+
+ val classPath = arguments.classpath.split(File.pathSeparatorChar).toList()
+
+ if (arguments.useSamplesURL) DownloadSamples.downloadSamples()
+
+ val documentationOptions = DocumentationOptions(
+ arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
+ arguments.outputFormat,
+ skipDeprecated = arguments.nodeprecated,
+ sourceLinks = sourceLinks,
+ impliedPlatforms = arguments.impliedPlatforms.split(','),
+ perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
+ jdkVersion = arguments.jdkVersion,
+ externalDocumentationLinks = parseLinks(arguments.links),
+ noStdlibLink = arguments.noStdlibLink,
+ cacheRoot = arguments.cacheRoot,
+ languageVersion = arguments.languageVersion,
+ apiVersion = arguments.apiVersion,
+ collectInheritedExtensionsFromLibraries = arguments.collectInheritedExtensionsFromLibraries,
+ generateClassIndexPage = !arguments.noGenerateClassIndexPage,
+ generatePackageIndexPage = !arguments.noGeneratePackageIndexPage,
+ outlineRoot = arguments.outlineRoot,
+ dacRoot = arguments.dacRoot,
+ noJdkLink = arguments.noJdkLink
+ )
+
+ val generator = DokkaGenerator(
+ DokkaConsoleLogger,
+ classPath,
+ sources.map(SourceRootImpl.Companion::parseSourceRoot),
+ samples,
+ includes,
+ arguments.moduleName,
+ documentationOptions)
+
+ generator.generate()
+ DokkaConsoleLogger.report()
+ }
+
+ fun findToolsJar(): File {
+ val javaHome = System.getProperty("java.home")
+ val default = File(javaHome, "../lib/tools.jar")
+ val mac = File(javaHome, "../Classes/classes.jar")
+ when {
+ default.exists() -> return default
+ mac.exists() -> return mac
+ else -> {
+ throw Exception("tools.jar not found, please check it, also you can provide it manually, using -cp")
+ }
+ }
+ }
+
+ fun createClassLoaderWithTools(): ClassLoader {
+ val toolsJar = findToolsJar().canonicalFile.toURI().toURL()
+ val originalUrls = (javaClass.classLoader as? URLClassLoader)?.urLs
+ val dokkaJar = javaClass.protectionDomain.codeSource.location
+ val urls = if (originalUrls != null) arrayOf(toolsJar, *originalUrls) else arrayOf(toolsJar, dokkaJar)
+ return URLClassLoader(urls, ClassLoader.getSystemClassLoader().parent)
+ }
+
+ fun startWithToolsJar(args: Array<String>) {
+ try {
+ javaClass.classLoader.loadClass("com.sun.tools.doclets.formats.html.HtmlDoclet")
+ entry(args)
+ } catch (e: ClassNotFoundException) {
+ val classLoader = createClassLoaderWithTools()
+ classLoader.loadClass("org.jetbrains.dokka.MainKt")
+ .methods.find { it.name == "entry" }!!
+ .invoke(null, args)
+ }
+ }
+
+ @JvmStatic
+ fun main(args: Array<String>) {
+ val arguments = DokkaArguments()
+ Args.parse(arguments, args)
+
+ if (arguments.outputFormat == "javadoc")
+ startWithToolsJar(args)
+ else
+ entry(args)
+ }
+}
+
+
+
diff --git a/runners/fatjar/build.gradle b/runners/fatjar/build.gradle
new file mode 100644
index 000000000..4ce0416ca
--- /dev/null
+++ b/runners/fatjar/build.gradle
@@ -0,0 +1,50 @@
+import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
+import org.jetbrains.PluginXmlTransformer
+
+apply plugin: 'java'
+apply plugin: 'com.github.johnrengelman.shadow'
+
+dependencies {
+ compile project(":runners:cli")
+ compile project(":runners:ant")
+}
+
+jar {
+ manifest {
+ attributes 'Main-Class': 'org.jetbrains.dokka.MainKt'
+ }
+}
+
+shadowJar {
+ baseName = 'dokka-fatjar'
+ classifier = ''
+
+ configurations {
+ exclude compileOnly
+ }
+
+ transform(ServiceFileTransformer)
+ transform(PluginXmlTransformer)
+
+ exclude 'colorScheme/**'
+ exclude 'fileTemplates/**'
+ exclude 'inspectionDescriptions/**'
+ exclude 'intentionDescriptions/**'
+
+ exclude 'src/**'
+
+ relocate('kotlin.reflect.full', 'kotlin.reflect')
+}
+
+apply plugin: 'maven-publish'
+
+publishing {
+ publications {
+ dokkaFatJar(MavenPublication) { publication ->
+ artifactId = 'dokka-fatjar'
+ project.shadow.component(publication)
+ }
+ }
+}
+
+bintrayPublication(project, ["dokkaFatJar"]) \ No newline at end of file
diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-license b/runners/gradle-integration-tests/android-licenses/android-sdk-license
new file mode 100644
index 000000000..c311cf48c
--- /dev/null
+++ b/runners/gradle-integration-tests/android-licenses/android-sdk-license
@@ -0,0 +1,2 @@
+
+d56f5187479451eabf01fb78af6dfcb131a6481e \ No newline at end of file
diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license b/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license
new file mode 100644
index 000000000..da4552d2c
--- /dev/null
+++ b/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license
@@ -0,0 +1,2 @@
+
+84831b9409646a918e30573bab4c9c91346d8abd \ No newline at end of file
diff --git a/runners/gradle-integration-tests/build.gradle b/runners/gradle-integration-tests/build.gradle
new file mode 100644
index 000000000..a681c82e3
--- /dev/null
+++ b/runners/gradle-integration-tests/build.gradle
@@ -0,0 +1,59 @@
+
+
+apply plugin: 'kotlin'
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ freeCompilerArgs += "-Xjsr305=strict"
+ languageVersion = "1.2"
+ apiVersion = "1.0"
+ jvmTarget = "1.8"
+ }
+}
+
+configurations {
+ dokkaPlugin
+ dokkaAndroidPlugin
+ dokkaFatJar
+}
+
+dependencies {
+
+ testCompileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version
+ testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_for_gradle_runtime_version
+ testCompile ideaRT()
+
+ dokkaPlugin project(path: ':runners:gradle-plugin', configuration: 'shadow')
+ dokkaAndroidPlugin project(path: ':runners:android-gradle-plugin', configuration: 'shadow')
+ dokkaFatJar project(path: ":runners:fatjar", configuration: 'shadow')
+
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+ testCompile gradleTestKit()
+}
+
+
+
+task createClasspathManifest {
+ def outputDir = file("$buildDir/$name")
+
+ inputs.files(configurations.dokkaPlugin + configurations.dokkaAndroidPlugin + configurations.dokkaFatJar)
+ outputs.dir outputDir
+
+ doLast {
+ outputDir.mkdirs()
+ file("$outputDir/dokka-plugin-classpath.txt").text = configurations.dokkaPlugin.join("\n")
+ file("$outputDir/android-dokka-plugin-classpath.txt").text = configurations.dokkaAndroidPlugin.join("\n")
+ file("$outputDir/fatjar.txt").text = configurations.dokkaFatJar.join("\n")
+ }
+}
+
+
+createClasspathManifest.mustRunAfter project(":runners:fatjar").shadowJar
+testClasses.dependsOn project(":runners:fatjar").shadowJar
+testClasses.dependsOn createClasspathManifest
+
+test {
+ systemProperty "android.licenses.overwrite", project.findProperty("android.licenses.overwrite") ?: ""
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt
new file mode 100644
index 000000000..c3fe2ea95
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt
@@ -0,0 +1,51 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import kotlin.test.assertEquals
+
+abstract class AbstractAndroidAppTest(val testDataRootPath: String) : AbstractDokkaAndroidGradleTest() {
+
+ fun prepareTestData() {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.resolve("app").copy(tmpRoot.resolve("app"))
+ testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle"))
+ testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle"))
+
+ androidLocalProperties?.copy(tmpRoot.resolve("local.properties"))
+ }
+
+
+ data class AndroidPluginParams(val pluginVersion: String, val buildToolsVersion: String, val compileSdk: Int) {
+ fun asArguments(): List<String> = listOf(
+ "-Pabt_plugin_version=$pluginVersion",
+ "-Pabt_version=$buildToolsVersion",
+ "-Psdk_version=$compileSdk"
+ )
+ }
+
+
+ protected fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) {
+ prepareTestData()
+
+ val result = configure(gradleVersion, kotlinVersion,
+ arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments())
+ .build()
+
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome)
+
+ val docsOutput = "app/build/dokka"
+
+ checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput)
+
+ checkNoErrorClasses(docsOutput)
+ checkNoUnresolvedLinks(docsOutput)
+
+ checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>",
+ """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""")
+ }
+
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt
new file mode 100644
index 000000000..06753342b
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt
@@ -0,0 +1,45 @@
+package org.jetbrains.dokka.gradle
+
+import org.junit.BeforeClass
+import java.io.File
+
+abstract class AbstractDokkaAndroidGradleTest : AbstractDokkaGradleTest() {
+
+ override val pluginClasspath: List<File> = androidPluginClasspathData.toFile().readLines().map { File(it) }
+
+ companion object {
+
+ @JvmStatic
+ @BeforeClass
+ fun acceptAndroidSdkLicenses() {
+ val sdkDir = androidLocalProperties?.toFile()?.let {
+ val lines = it.readLines().map { it.trim() }
+ val sdkDirLine = lines.firstOrNull { "sdk.dir" in it }
+ sdkDirLine?.substringAfter("=")?.trim()
+ } ?: System.getenv("ANDROID_HOME")
+
+ if (sdkDir == null || sdkDir.isEmpty()) {
+ error("Android SDK home not set, " +
+ "try setting \$ANDROID_HOME " +
+ "or sdk.dir in runners/gradle-integration-tests/testData/android.local.properties")
+ }
+ val sdkDirFile = File(sdkDir)
+ if (!sdkDirFile.exists()) error("\$ANDROID_HOME and android.local.properties points to non-existing location")
+ val sdkLicensesDir = sdkDirFile.resolve("licenses")
+
+ val acceptedLicenses = File("android-licenses")
+ acceptedLicenses.listFiles().forEach { licenseFile ->
+ val target = sdkLicensesDir.resolve(licenseFile.name)
+ if(!target.exists() || target.readText() != licenseFile.readText()) {
+ val overwrite = System.getProperty("android.licenses.overwrite", "false").toBoolean()
+ if (!target.exists() || overwrite) {
+ licenseFile.copyTo(target, true)
+ println("Accepted ${licenseFile.name}, by copying $licenseFile to $target")
+ }
+ }
+
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt
new file mode 100644
index 000000000..255138a99
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt
@@ -0,0 +1,108 @@
+package org.jetbrains.dokka.gradle
+
+
+import com.intellij.rt.execution.junit.FileComparisonFailure
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Paths
+
+
+val testDataFolder = Paths.get("testData")
+
+val pluginClasspathData = Paths.get("build", "createClasspathManifest", "dokka-plugin-classpath.txt")
+val androidPluginClasspathData = pluginClasspathData.resolveSibling("android-dokka-plugin-classpath.txt")
+
+val dokkaFatJarPathData = pluginClasspathData.resolveSibling("fatjar.txt")
+
+val androidLocalProperties = testDataFolder.resolve("android.local.properties").let { if (Files.exists(it)) it else null }
+
+abstract class AbstractDokkaGradleTest {
+ @get:Rule val testProjectDir = TemporaryFolder()
+
+ open val pluginClasspath: List<File> = pluginClasspathData.toFile().readLines().map { File(it) }
+
+ fun checkOutputStructure(expected: String, actualSubpath: String) {
+ val expectedPath = testDataFolder.resolve(expected)
+ val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize()
+
+ assertEqualsIgnoringSeparators(expectedPath.toFile(), buildString {
+ actualPath.toFile().writeStructure(this, File(actualPath.toFile(), "."))
+ })
+ }
+
+ fun checkNoErrorClasses(actualSubpath: String, extension: String = "html", errorClassMarker: String = "ERROR CLASS") {
+ val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize()
+ var checked = 0
+ Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach {
+ val text = it.toFile().readText()
+
+ val noErrorClasses = text.replace(errorClassMarker, "?!")
+
+ if (noErrorClasses != text) {
+ throw FileComparisonFailure("", noErrorClasses, text, null)
+ }
+
+ checked++
+ }
+ println("$checked files checked for error classes")
+ }
+
+ fun checkNoUnresolvedLinks(actualSubpath: String, extension: String = "html", marker: Regex = "[\"']#[\"']".toRegex()) {
+ val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize()
+ var checked = 0
+ Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach {
+ val text = it.toFile().readText()
+
+ val noErrorClasses = text.replace(marker, "?!")
+
+ if (noErrorClasses != text) {
+ throw FileComparisonFailure("", noErrorClasses, text, null)
+ }
+
+ checked++
+ }
+ println("$checked files checked for unresolved links")
+ }
+
+ fun checkExternalLink(actualSubpath: String, linkBody: String, fullLink: String, extension: String = "html") {
+ val match = "!!match!!"
+ val notMatch = "!!not-match!!"
+
+ val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize()
+ var checked = 0
+ var totalEntries = 0
+ Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach {
+ val text = it.toFile().readText()
+
+ val textWithoutMatches = text.replace(fullLink, match)
+
+ val textWithoutNonMatches = textWithoutMatches.replace(linkBody, notMatch)
+
+ if (textWithoutNonMatches != textWithoutMatches) {
+
+ val expected = textWithoutNonMatches.replace(notMatch, fullLink).replace(match, fullLink)
+ val actual = textWithoutMatches.replace(match, fullLink)
+
+ throw FileComparisonFailure("", expected, actual, null)
+ }
+ if (text != textWithoutMatches)
+ totalEntries++
+
+ checked++
+ }
+ println("$checked files checked for valid external links '$linkBody', found $totalEntries links")
+ }
+
+ fun configure(gradleVersion: String = "3.5", kotlinVersion: String = "1.1.2", arguments: Array<String>): GradleRunner {
+ val fatjar = dokkaFatJarPathData.toFile().readText()
+
+ return GradleRunner.create().withProjectDir(testProjectDir.root)
+ .withArguments("-Pdokka_fatjar=$fatjar", "-Ptest_kotlin_version=$kotlinVersion", *arguments)
+ .withPluginClasspath(pluginClasspath)
+ .withGradleVersion(gradleVersion)
+ .withDebug(true)
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt
new file mode 100644
index 000000000..0ac4cbd96
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka.gradle
+
+import org.junit.Ignore
+import org.junit.Test
+
+class AndroidAppTest : AbstractAndroidAppTest("androidApp") {
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() {
+ doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() {
+ doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() {
+ doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() {
+ doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27))
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt
new file mode 100644
index 000000000..cdd3fa848
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt
@@ -0,0 +1,52 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Ignore
+import org.junit.Test
+import java.nio.file.Path
+import java.nio.file.Paths
+import kotlin.test.assertEquals
+
+class AndroidLibDependsOnJavaLibTest: AbstractDokkaAndroidGradleTest() {
+
+ private val testDataRootPath = "androidLibDependsOnJavaLib"
+
+ private fun prepareTestData() {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.copy(tmpRoot)
+
+ androidLocalProperties?.copy(tmpRoot.resolve("local.properties"))
+ }
+
+
+ private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AbstractAndroidAppTest.AndroidPluginParams) {
+ prepareTestData()
+
+ val result = configure(gradleVersion, kotlinVersion,
+ arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments())
+ .build()
+
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":lib:dokka")?.outcome)
+
+ val docsOutput = "lib/build/dokka"
+
+ checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput)
+
+ checkNoErrorClasses(docsOutput)
+ checkNoUnresolvedLinks(docsOutput)
+
+ checkExternalLink(docsOutput, "<span class=\"identifier\">LibClz</span>",
+ """<a href="https://example.com/example/jlib/LibClz.html"><span class="identifier">LibClz</span></a>""")
+ }
+
+ // TODO: add test back
+ @Ignore
+ @Test
+ fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() {
+ doTest("4.5", "1.2.20", AbstractAndroidAppTest.AndroidPluginParams("3.0.1", "27.0.0", 27))
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt
new file mode 100644
index 000000000..b0257f8bf
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt
@@ -0,0 +1,73 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import org.jetbrains.dokka.gradle.AbstractAndroidAppTest.AndroidPluginParams
+import org.junit.Ignore
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class AndroidMultiFlavourAppTest : AbstractDokkaAndroidGradleTest() {
+
+ fun prepareTestData(testDataRootPath: String) {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.resolve("app").copy(tmpRoot.resolve("app"))
+ testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle"))
+ testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle"))
+
+ androidLocalProperties?.copy(tmpRoot.resolve("local.properties"))
+ }
+
+ private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) {
+ prepareTestData("androidMultiFlavourApp")
+
+ val result = configure(gradleVersion, kotlinVersion,
+ arguments = arrayOf("dokka", "dokkaFullFlavourOnly", "--stacktrace") + androidPluginParams.asArguments())
+ .build()
+
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome)
+ assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokkaFullFlavourOnly")?.outcome)
+
+ val docsOutput = "app/build/dokka"
+
+ checkOutputStructure("androidMultiFlavourApp/fileTree.txt", docsOutput)
+
+ checkNoErrorClasses(docsOutput)
+ checkNoUnresolvedLinks(docsOutput)
+
+ checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>",
+ """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""")
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() {
+ doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() {
+ doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() {
+ doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24))
+ }
+
+ // TODO FIXME
+ @Ignore
+ @Test
+ fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() {
+ doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27))
+ }
+
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt
new file mode 100644
index 000000000..ebaf16538
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt
@@ -0,0 +1,55 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class BasicTest : AbstractDokkaGradleTest() {
+
+ fun prepareTestData(testDataRootPath: String) {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.resolve("src").copy(tmpRoot.resolve("src"))
+ testDataRoot.resolve("classDir").copy(tmpRoot.resolve("classDir"))
+ testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle"))
+ testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle"))
+ }
+
+ private fun doTest(gradleVersion: String, kotlinVersion: String) {
+
+ prepareTestData("basic")
+
+ val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build()
+
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome)
+
+ val docsOutput = "build/dokka"
+
+ checkOutputStructure("basic/fileTree.txt", docsOutput)
+
+ checkNoErrorClasses(docsOutput)
+ checkNoUnresolvedLinks(docsOutput)
+
+ checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>",
+ """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""")
+ }
+
+ @Test fun `test kotlin 1_1_2 and gradle 3_5`() {
+ doTest("3.5", "1.1.2")
+ }
+
+ @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() {
+ doTest("2.14.1", "1.0.7")
+ }
+
+ @Test fun `test kotlin 1_1_2 and gradle 4_0`() {
+ doTest("4.0", "1.1.2")
+ }
+
+ @Test fun `test kotlin 1_2_20 and gradle 4_5`() {
+ doTest("4.5", "1.2.20")
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt
new file mode 100644
index 000000000..18c20ed20
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka.gradle
+
+import org.junit.Test
+
+// TODO: add tests back
+class JavadocRSuppressionTest : AbstractAndroidAppTest("androidAppJavadoc") {
+// @Test
+// fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() {
+// doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25))
+// }
+//
+// @Test
+// fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() {
+// doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24))
+// }
+//
+// @Test
+// fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() {
+// doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24))
+// }
+//
+// @Test fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() {
+// doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27))
+// }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt
new file mode 100644
index 000000000..9458528cd
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt
@@ -0,0 +1,57 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class MultiProjectSingleOutTest : AbstractDokkaGradleTest() {
+
+ fun prepareTestData(testDataRootPath: String) {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.apply {
+ resolve("build.gradle").copy(tmpRoot.resolve("build.gradle"))
+ resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle"))
+ resolve("subA").copy(tmpRoot.resolve("subA"))
+ resolve("subB").copy(tmpRoot.resolve("subB"))
+ }
+ }
+
+ private fun doTest(gradleVersion: String, kotlinVersion: String) {
+
+ prepareTestData("multiProjectSingleOut")
+
+ val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build()
+
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome)
+
+ val docsOutput = "build/dokka"
+
+ checkOutputStructure("multiProjectSingleOut/fileTree.txt", docsOutput)
+
+ checkNoErrorClasses(docsOutput)
+ checkNoUnresolvedLinks(docsOutput)
+
+ checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>",
+ """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""")
+ }
+
+ @Test fun `test kotlin 1_1_2 and gradle 3_5`() {
+ doTest("3.5", "1.1.2")
+ }
+
+ @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() {
+ doTest("2.14.1", "1.0.7")
+ }
+
+ @Test fun `test kotlin 1_1_2 and gradle 4_0`() {
+ doTest("4.0", "1.1.2")
+ }
+
+ @Test fun `test kotlin 1_2_20 and gradle 4_5`() {
+ doTest("4.5", "1.2.20")
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt
new file mode 100644
index 000000000..f712998c1
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt
@@ -0,0 +1,74 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Test
+import java.nio.file.Path
+import kotlin.test.assertEquals
+
+class RebuildAfterSourceChangeTest : AbstractDokkaGradleTest() {
+
+ fun prepareTestData(testDataRootPath: String): Pair<Path, Path> {
+ val testDataRoot = testDataFolder.resolve(testDataRootPath)
+ val tmpRoot = testProjectDir.root.toPath()
+
+ testDataRoot.resolve("src").copy(tmpRoot.resolve("src"))
+ testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle"))
+ testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle"))
+
+ return testDataRoot to tmpRoot
+ }
+
+ private fun doTest(gradleVersion: String, kotlinVersion: String) {
+
+ val (testDataRoot, tmpRoot) = prepareTestData("sourcesChange")
+ val docsOutput = "build/dokka"
+
+ configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result ->
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome)
+ }
+
+
+ configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result ->
+ println(result.output)
+
+ assertEquals(TaskOutcome.UP_TO_DATE, result.task(":dokka")?.outcome)
+ }
+
+ checkOutputStructure("sourcesChange/fileTree.txt", docsOutput)
+
+ testDataRoot.resolve("src1").copy(tmpRoot.resolve("src"))
+
+ configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result ->
+ println(result.output)
+
+ assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome)
+ }
+
+
+ checkOutputStructure("sourcesChange/fileTree1.txt", docsOutput)
+
+ }
+
+
+ @Test
+ fun `test kotlin 1_1_2 and gradle 3_5`() {
+ doTest("3.5", "1.1.2")
+ }
+
+ @Test
+ fun `test kotlin 1_0_7 and gradle 2_14_1`() {
+ doTest("2.14.1", "1.0.7")
+ }
+
+ @Test
+ fun `test kotlin 1_1_2 and gradle 4_0`() {
+ doTest("4.0", "1.1.2")
+ }
+
+ @Test
+ fun `test kotlin 1_2_20 and gradle 4_5`() {
+ doTest("4.5", "1.2.20")
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt
new file mode 100644
index 000000000..6f17af228
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt
@@ -0,0 +1,56 @@
+package org.jetbrains.dokka.gradle
+
+import com.intellij.rt.execution.junit.FileComparisonFailure
+import java.io.File
+import java.io.IOException
+import java.nio.file.*
+import java.nio.file.attribute.BasicFileAttributes
+
+
+fun File.writeStructure(builder: StringBuilder, relativeTo: File = this, spaces: Int = 0) {
+ builder.append(" ".repeat(spaces))
+ val out = if (this != relativeTo) this.relativeTo(relativeTo) else this
+
+ builder.append(out)
+ if (this.isDirectory) {
+ builder.appendln("/")
+ this.listFiles().sortedBy { it.name }.forEach { it.writeStructure(builder, this, spaces + 4) }
+ } else {
+ builder.appendln()
+ }
+}
+
+fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) {
+ if (!expectedFile.exists()) expectedFile.createNewFile()
+ val expectedText = expectedFile.readText().replace("\r\n", "\n")
+ val actualText = output.replace("\r\n", "\n")
+
+ if (expectedText != actualText)
+ throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath)
+}
+
+class CopyFileVisitor(private var sourcePath: Path?, private val targetPath: Path) : SimpleFileVisitor<Path>() {
+
+ @Throws(IOException::class)
+ override fun preVisitDirectory(dir: Path,
+ attrs: BasicFileAttributes): FileVisitResult {
+ if (sourcePath == null) {
+ sourcePath = dir
+ } else {
+ Files.createDirectories(targetPath.resolve(sourcePath?.relativize(dir)))
+ }
+ return FileVisitResult.CONTINUE
+ }
+
+ @Throws(IOException::class)
+ override fun visitFile(file: Path,
+ attrs: BasicFileAttributes): FileVisitResult {
+ Files.copy(file, targetPath.resolve(sourcePath?.relativize(file)), StandardCopyOption.REPLACE_EXISTING)
+ return FileVisitResult.CONTINUE
+ }
+}
+
+fun Path.copy(to: Path) {
+ Files.walkFileTree(this, CopyFileVisitor(this, to))
+}
+
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidApp/app/build.gradle
new file mode 100644
index 000000000..1555de9fc
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/build.gradle
@@ -0,0 +1,50 @@
+buildscript {
+ repositories {
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka-android'
+}
+
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'org.jetbrains.dokka-android'
+
+android {
+ compileSdkVersion Integer.parseInt(sdk_version)
+ buildToolsVersion abt_version
+
+ defaultConfig {
+ applicationId "org.example.kotlin.mixed"
+ minSdkVersion 14
+ targetSdkVersion Integer.parseInt(sdk_version)
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version"
+}
+
+
+dokka {
+ dokkaFatJar = new File(dokka_fatjar)
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b4e1a892b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.example.kotlin.mixed" >
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+
+ <activity
+ android:name=".JavaActivity"
+ android:label="@string/title_activity_main_activity1" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".KotlinActivity"
+ android:label="@string/title_activity_main_activity2" />
+
+ </application>
+
+</manifest>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
new file mode 100644
index 000000000..3668c594f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
@@ -0,0 +1,34 @@
+package org.example.kotlin.mixed;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.Menu;
+import android.view.View;
+import android.widget.Button;
+
+public class JavaActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Button next = (Button) findViewById(R.id.Button01);
+ next.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ Intent myIntent = new Intent(view.getContext(), KotlinActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+ });
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+}
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
new file mode 100644
index 000000000..ca2f27b02
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main2)
+
+ val next = findViewById(R.id.Button02) as Button
+ next.setOnClickListener {
+ val intent: Intent = Intent()
+ setResult(RESULT_OK, intent)
+ finish()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.main_activity2, menu)
+ return true
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..ede57c390
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 1" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button01"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml
new file mode 100644
index 000000000..d707536a9
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 2" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button02"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..47c822467
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d8f08bc2a
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">AndroidSample</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="title_activity_main_activity1">JavaActivity</string>
+ <string name="title_activity_main_activity2">KotlinActivity</string>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidApp/build.gradle b/runners/gradle-integration-tests/testData/androidApp/build.gradle
new file mode 100644
index 000000000..59477b520
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url 'https://maven.google.com' }
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$abt_plugin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt
new file mode 100644
index 000000000..3827b69e8
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt
@@ -0,0 +1,19 @@
+/
+ app/
+ alltypes/
+ index.html
+ index-outline.html
+ index.html
+ org.example.kotlin.mixed/
+ -java-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ -kotlin-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ on-create.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/androidApp/settings.gradle b/runners/gradle-integration-tests/testData/androidApp/settings.gradle
new file mode 100644
index 000000000..1feb28670
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name = "androidApp"
+
+include ':app' \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle
new file mode 100644
index 000000000..6a053a5e6
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle
@@ -0,0 +1,50 @@
+buildscript {
+ repositories {
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka-android'
+}
+
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'org.jetbrains.dokka-android'
+
+android {
+ compileSdkVersion Integer.parseInt(sdk_version)
+ buildToolsVersion abt_version
+
+ defaultConfig {
+ applicationId "org.example.kotlin.mixed"
+ minSdkVersion 14
+ targetSdkVersion Integer.parseInt(sdk_version)
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version"
+}
+
+
+dokka {
+ outputFormat = "javadoc"
+ dokkaFatJar = new File(dokka_fatjar)
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b4e1a892b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.example.kotlin.mixed" >
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+
+ <activity
+ android:name=".JavaActivity"
+ android:label="@string/title_activity_main_activity1" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".KotlinActivity"
+ android:label="@string/title_activity_main_activity2" />
+
+ </application>
+
+</manifest>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
new file mode 100644
index 000000000..3668c594f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
@@ -0,0 +1,34 @@
+package org.example.kotlin.mixed;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.Menu;
+import android.view.View;
+import android.widget.Button;
+
+public class JavaActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Button next = (Button) findViewById(R.id.Button01);
+ next.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ Intent myIntent = new Intent(view.getContext(), KotlinActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+ });
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+}
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
new file mode 100644
index 000000000..ca2f27b02
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main2)
+
+ val next = findViewById(R.id.Button02) as Button
+ next.setOnClickListener {
+ val intent: Intent = Intent()
+ setResult(RESULT_OK, intent)
+ finish()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.main_activity2, menu)
+ return true
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..ede57c390
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 1" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button01"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml
new file mode 100644
index 000000000..d707536a9
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 2" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button02"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..47c822467
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d8f08bc2a
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">AndroidSample</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="title_activity_main_activity1">JavaActivity</string>
+ <string name="title_activity_main_activity2">KotlinActivity</string>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle
new file mode 100644
index 000000000..59477b520
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url 'https://maven.google.com' }
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$abt_plugin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt b/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt
new file mode 100644
index 000000000..c5e79eba5
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt
@@ -0,0 +1,21 @@
+/
+ allclasses-frame.html
+ allclasses-noframe.html
+ constant-values.html
+ deprecated-list.html
+ help-doc.html
+ index-all.html
+ index.html
+ org/
+ example/
+ kotlin/
+ mixed/
+ JavaActivity.html
+ KotlinActivity.html
+ package-frame.html
+ package-summary.html
+ package-tree.html
+ overview-tree.html
+ package-list
+ script.js
+ stylesheet.css
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle
new file mode 100644
index 000000000..a4e67fea0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name = "androidAppJavadoc"
+
+include ':app' \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle
new file mode 100644
index 000000000..736668ab1
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle
@@ -0,0 +1,20 @@
+subprojects {
+ buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url 'https://maven.google.com' }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+
+ }
+
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url 'https://maven.google.com' }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt
new file mode 100644
index 000000000..6c96a01ce
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt
@@ -0,0 +1,14 @@
+/
+ lib/
+ alltypes/
+ index.html
+ example/
+ -lib-clz-use/
+ -init-.html
+ f.html
+ index.html
+ index.html
+ index-outline.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle
new file mode 100644
index 000000000..bbfeb03c2
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle
@@ -0,0 +1 @@
+apply plugin: 'java'
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java
new file mode 100644
index 000000000..1d9a6fb22
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java
@@ -0,0 +1,5 @@
+package example.jlib;
+
+public class LibClz {
+
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle
new file mode 100644
index 000000000..0f27d365c
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle
@@ -0,0 +1,39 @@
+buildscript {
+ dependencies {
+ classpath "com.android.tools.build:gradle:$abt_plugin_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+
+plugins {
+ id 'org.jetbrains.dokka-android'
+}
+
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'org.jetbrains.dokka-android'
+
+
+android {
+ compileSdkVersion Integer.parseInt(sdk_version)
+ buildToolsVersion abt_version
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ api(project(":jlib"))
+}
+
+dokka {
+ dokkaFatJar = new File(dokka_fatjar)
+
+ externalDocumentationLink {
+ url = new URL("https://example.com")
+ packageListUrl = file("$rootDir/package-list").toURI().toURL()
+ }
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..267f6efd1
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="example">
+</manifest>
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt
new file mode 100644
index 000000000..d034a3a92
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt
@@ -0,0 +1,13 @@
+package example
+
+import example.jlib.LibClz
+
+/**
+ * Uses jlib
+ */
+class LibClzUse {
+ /**
+ * Returns LibClz
+ */
+ fun f(): LibClz = LibClz()
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list
new file mode 100644
index 000000000..bf76058e1
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list
@@ -0,0 +1 @@
+example.jlib \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle
new file mode 100644
index 000000000..5b4250a03
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle
@@ -0,0 +1,5 @@
+rootProject.name = "androidLibDependsOnJavaLib"
+
+
+include(":lib")
+include(":jlib") \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle
new file mode 100644
index 000000000..ee68ba6d6
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle
@@ -0,0 +1,75 @@
+buildscript {
+ repositories {
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka-android'
+}
+
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'org.jetbrains.dokka-android'
+
+android {
+ compileSdkVersion Integer.parseInt(sdk_version)
+ buildToolsVersion abt_version
+
+ defaultConfig {
+ applicationId "org.example.kotlin.mixed"
+ minSdkVersion 14
+ targetSdkVersion Integer.parseInt(sdk_version)
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+
+ flavorDimensions "mode"
+ productFlavors {
+ free {
+ dimension "mode"
+ applicationIdSuffix ".free"
+ versionNameSuffix "-free"
+ }
+ full {
+ dimension "mode"
+ applicationIdSuffix ".full"
+ versionNameSuffix "-full"
+ }
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ free.java.srcDirs += 'src/free/kotlin'
+ }
+}
+
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version"
+}
+
+
+dokka {
+ outputDirectory = "$buildDir/dokka/all"
+ dokkaFatJar = new File(dokka_fatjar)
+}
+
+task dokkaFullFlavourOnly(type: org.jetbrains.dokka.gradle.DokkaAndroidTask) {
+ kotlinTasks {
+ ["compileFullReleaseKotlin"]
+ }
+ dokkaFatJar = new File(dokka_fatjar)
+ outputDirectory = "$buildDir/dokka/fullOnly"
+ moduleName = "full"
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml
new file mode 100644
index 000000000..3ecbcd3a4
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application>
+ <activity
+ android:name="org.example.kotlin.mixed.free.AdActivity"
+ android:label="@string/title_activity_ad"
+ android:theme="@style/AppTheme"></activity>
+ </application>
+</manifest>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt
new file mode 100644
index 000000000..b0b980fda
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt
@@ -0,0 +1,14 @@
+package org.example.kotlin.mixed.free
+
+import android.os.Bundle
+import android.app.Activity
+import org.example.kotlin.mixed.R
+
+class AdActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_ad)
+ }
+
+}
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml
new file mode 100644
index 000000000..e6443d053
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".free.AdActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Advertisment" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button02"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml
new file mode 100644
index 000000000..bbdf2d067
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="title_activity_ad">AdActivity</string>
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b4e1a892b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.example.kotlin.mixed" >
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+
+ <activity
+ android:name=".JavaActivity"
+ android:label="@string/title_activity_main_activity1" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".KotlinActivity"
+ android:label="@string/title_activity_main_activity2" />
+
+ </application>
+
+</manifest>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
new file mode 100644
index 000000000..3668c594f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java
@@ -0,0 +1,34 @@
+package org.example.kotlin.mixed;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.app.Activity;
+import android.view.Menu;
+import android.view.View;
+import android.widget.Button;
+
+public class JavaActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Button next = (Button) findViewById(R.id.Button01);
+ next.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ Intent myIntent = new Intent(view.getContext(), KotlinActivity.class);
+ startActivityForResult(myIntent, 0);
+ }
+ });
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+}
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
new file mode 100644
index 000000000..ca2f27b02
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main2)
+
+ val next = findViewById(R.id.Button02) as Button
+ next.setOnClickListener {
+ val intent: Intent = Intent()
+ setResult(RESULT_OK, intent)
+ finish()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.main_activity2, menu)
+ return true
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..ede57c390
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 1" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button01"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml
new file mode 100644
index 000000000..d707536a9
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml
@@ -0,0 +1,24 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Activity 2" />
+
+ <Button android:text="Next"
+ android:id="@+id/Button02"
+ android:layout_width="250px"
+ android:textSize="18px"
+ android:layout_height="55px">
+ </Button>
+
+</LinearLayout>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml
new file mode 100644
index 000000000..f3b10b6c0
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml
@@ -0,0 +1,6 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..47c822467
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d8f08bc2a
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">AndroidSample</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="title_activity_main_activity1">JavaActivity</string>
+ <string name="title_activity_main_activity2">KotlinActivity</string>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle
new file mode 100644
index 000000000..59477b520
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url 'https://maven.google.com' }
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$abt_plugin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt
new file mode 100644
index 000000000..5e969d8b9
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt
@@ -0,0 +1,45 @@
+/
+ all/
+ app/
+ alltypes/
+ index.html
+ index-outline.html
+ index.html
+ org.example.kotlin.mixed/
+ -java-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ -kotlin-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ on-create.html
+ index.html
+ org.example.kotlin.mixed.free/
+ -ad-activity/
+ -init-.html
+ index.html
+ on-create.html
+ index.html
+ package-list
+ style.css
+ fullOnly/
+ full/
+ alltypes/
+ index.html
+ index-outline.html
+ index.html
+ org.example.kotlin.mixed/
+ -java-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ -kotlin-activity/
+ -init-.html
+ index.html
+ on-create-options-menu.html
+ on-create.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle
new file mode 100644
index 000000000..1feb28670
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name = "androidApp"
+
+include ':app' \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/basic/build.gradle b/runners/gradle-integration-tests/testData/basic/build.gradle
new file mode 100644
index 000000000..4a259f500
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/build.gradle
@@ -0,0 +1,40 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka'
+}
+
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+
+repositories {
+ mavenCentral()
+ jcenter()
+ maven {
+ url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
+ }
+ maven {
+ url "https://dl.bintray.com/kotlin/kotlin-dev"
+ }
+}
+
+dependencies {
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version
+}
+
+
+dokka {
+ dokkaFatJar = new File(dokka_fatjar)
+ classpath += files("$projectDir/classDir")
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class
new file mode 100644
index 000000000..ccfff3007
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/basic/fileTree.txt b/runners/gradle-integration-tests/testData/basic/fileTree.txt
new file mode 100644
index 000000000..2ceae3715
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/fileTree.txt
@@ -0,0 +1,33 @@
+/
+ basic/
+ alltypes/
+ index.html
+ demo/
+ -a/
+ -init-.html
+ index.html
+ p.html
+ -greeter/
+ -init-.html
+ greet.html
+ index.html
+ name.html
+ -some-interface.html
+ -some-sub-type/
+ -init-.html
+ index.html
+ -some-type/
+ -init-.html
+ index.html
+ constructor.html
+ index.html
+ main.html
+ p1.-my-binary-class/
+ index.html
+ test.html
+ str.html
+ x.html
+ index-outline.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/basic/settings.gradle b/runners/gradle-integration-tests/testData/basic/settings.gradle
new file mode 100644
index 000000000..c36a146ca
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "basic" \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt
new file mode 100644
index 000000000..3d7bcb51d
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt
@@ -0,0 +1,45 @@
+package demo
+
+import p1.MyBinaryClass
+
+/**
+ * This class supports greeting people by name.
+ *
+ * @property name The name of the person to be greeted.
+ */
+class Greeter(val name: String) {
+
+ /**
+ * Prints the greeting to the standard output.
+ */
+ fun greet() {
+ println("Hello $name!")
+ }
+}
+
+fun main(args: Array<String>) {
+ Greeter(args[0]).greet()
+}
+
+val str = "Hello! ".repeat(4)
+val x: (a: String, b: Int) -> Int = { a, b -> 0 }
+
+interface SomeInterface
+private class SomeImpl : SomeInterface
+
+fun SomeInterface.constructor(): SomeInterface {
+ return SomeImpl()
+}
+
+open class SomeType
+class SomeSubType : SomeType()
+
+fun SomeType.constructor(): SomeType {
+ return SomeSubType()
+}
+
+
+annotation class A(val p: String)
+
+val MyBinaryClass.test get() = s()
+
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle
new file mode 100644
index 000000000..68d93e30c
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle
@@ -0,0 +1,32 @@
+plugins {
+ id 'org.jetbrains.dokka'
+}
+
+subprojects {
+ buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+ }
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+}
+
+apply plugin: 'org.jetbrains.dokka'
+
+dokka {
+ kotlinTasks {
+ [":subA:compileKotlin", ":subB:compileKotlin"]
+ }
+ dokkaFatJar = new File(dokka_fatjar)
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt
new file mode 100644
index 000000000..5624fca66
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt
@@ -0,0 +1,33 @@
+/
+ multi-project-root/
+ alltypes/
+ index.html
+ index-outline.html
+ index.html
+ package-list
+ s1/
+ -my-class/
+ -init-.html
+ index.html
+ otherworks.html
+ -super/
+ -init-.html
+ bar.html
+ foo.html
+ index.html
+ index.html
+ some-cool-thing.html
+ s2/
+ -cooler/
+ -init-.html
+ a.html
+ coolest.html
+ index.html
+ my-class.html
+ -superful/
+ -init-.html
+ bar.html
+ index.html
+ index.html
+ main.html
+ style.css
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle
new file mode 100644
index 000000000..283cc526b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name = "multiProjectRoot"
+
+include 'subA', 'subB' \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle
new file mode 100644
index 000000000..0600411e2
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle
@@ -0,0 +1,6 @@
+apply plugin: 'kotlin'
+
+dependencies {
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version
+}
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt
new file mode 100644
index 000000000..126d7f3e2
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt
@@ -0,0 +1,31 @@
+package s1
+
+/**
+ * Coolest one
+ */
+fun someCoolThing(s: String) = s.repeat(2)
+
+/**
+ * Just a class
+ */
+class MyClass {
+ /**
+ * Ultimate answer to all questions
+ */
+ fun otherworks(): Int = 42
+}
+
+/**
+ * Just a SUPER class
+ */
+open class Super {
+ /**
+ * Same as [MyClass.otherworks]
+ */
+ fun foo(i: Int = 21) = i * 2
+
+ /**
+ * magic
+ */
+ open fun bar() = foo()
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle
new file mode 100644
index 000000000..7b8ff9f3c
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle
@@ -0,0 +1,7 @@
+apply plugin: 'kotlin'
+
+dependencies {
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version
+ compile project(":subA")
+}
diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt
new file mode 100644
index 000000000..8a87590ab
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt
@@ -0,0 +1,31 @@
+package s2
+
+import s1.Super
+import s1.MyClass
+import s1.someCoolThing
+
+/**
+ * Just an entry-point
+ */
+fun main(args: Array<String>) {
+
+}
+
+/**
+ * Take a glass of hot water
+ */
+class Cooler {
+ val myClass = MyClass()
+ val a = myClass.otherworks()
+ val coolest = someCoolThing()
+}
+
+/**
+ * Powerful
+ */
+class Superful : Super() {
+ /**
+ * Overriden magic
+ */
+ override fun bar() = foo(20) * 2
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/build.gradle b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle
new file mode 100644
index 000000000..bc20e1cf8
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle
@@ -0,0 +1,39 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ jcenter()
+ maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka'
+}
+
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+
+repositories {
+ mavenCentral()
+ jcenter()
+ maven {
+ url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
+ }
+ maven {
+ url "https://dl.bintray.com/kotlin/kotlin-dev"
+ }
+}
+
+dependencies {
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version
+ compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version
+}
+
+
+dokka {
+ dokkaFatJar = new File(dokka_fatjar)
+} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt
new file mode 100644
index 000000000..09f3724be
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt
@@ -0,0 +1,10 @@
+/
+ sources-change/
+ alltypes.html
+ demo/
+ hello.html
+ index.html
+ index-outline.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt
new file mode 100644
index 000000000..eeb377f7d
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt
@@ -0,0 +1,11 @@
+/
+ sources-change/
+ alltypes.html
+ demo/
+ hello.html
+ index.html
+ world.html
+ index-outline.html
+ index.html
+ package-list
+ style.css
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle b/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle
new file mode 100644
index 000000000..3fb032bfa
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "sourcesChange" \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt
new file mode 100644
index 000000000..c54dea508
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt
@@ -0,0 +1,6 @@
+package demo
+
+/**
+ * @return Hello
+ */
+fun hello(): String = "Hello" \ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt
new file mode 100644
index 000000000..53f22ff5c
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt
@@ -0,0 +1,11 @@
+package demo
+
+/**
+ * @return Hello
+ */
+fun hello(): String = "Hello"
+
+/**
+ * @return World
+ */
+fun world(): String = "World" \ No newline at end of file
diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle
new file mode 100644
index 000000000..4742dda5d
--- /dev/null
+++ b/runners/gradle-plugin/build.gradle
@@ -0,0 +1,121 @@
+import com.gradle.publish.DependenciesBuilder
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+
+
+apply plugin: 'com.github.johnrengelman.shadow'
+apply plugin: "com.gradle.plugin-publish"
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ freeCompilerArgs += "-Xjsr305=strict"
+ languageVersion = "1.2"
+ apiVersion = "1.1"
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ testCompile group: 'junit', name: 'junit', version: '4.12'
+
+ shadow group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version
+ shadow group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version
+
+ compile project(":integration")
+
+ compileOnly gradleApi()
+ compileOnly localGroovy()
+}
+
+task sourceJar(type: Jar) {
+ from sourceSets.main.allSource
+}
+
+processResources {
+ inputs.property("dokka_version", dokka_version)
+ eachFile {
+ if (it.name == "org.jetbrains.dokka.properties") {
+ it.filter { line ->
+ line.replace("<version>", dokka_version)
+ }
+ }
+ }
+}
+
+shadowJar {
+ baseName = 'dokka-gradle-plugin'
+ classifier = ''
+}
+
+apply plugin: 'maven-publish'
+
+publishing {
+ publications {
+ dokkaGradlePlugin(MavenPublication) { publication ->
+
+ artifactId = 'dokka-gradle-plugin'
+
+ artifact sourceJar {
+ classifier "sources"
+ }
+
+ project.shadow.component(publication)
+ publication.pom { pom ->
+ // Add dokka-fatjar as a runtime dependency.
+ // This is a workaround until the Shadow jar can put project dependencies into the .pom: https://github.com/johnrengelman/shadow/commit/da82b37522b349aff414f571d2037682acd84f27
+ pom.withXml { xml ->
+ def node = xml.asNode()
+ def deps = null
+ node.children().each { child ->
+ if (child.name().toString() == "dependencies") {
+ deps = child
+ }
+ }
+ if (deps == null) {
+ deps = node.appendNode("dependencies")
+ }
+ def dep = deps.appendNode("dependency")
+ dep.appendNode("groupId", "org.jetbrains.dokka")
+ dep.appendNode("artifactId", "dokka-fatjar")
+ dep.appendNode("version", dokka_version)
+ dep.appendNode("scope", "runtime")
+ }
+ }
+ }
+ }
+}
+
+bintrayPublication(project, ['dokkaGradlePlugin'])
+
+configurations.archives.artifacts.clear()
+artifacts {
+ archives shadowJar
+}
+
+pluginBundle {
+ website = 'http://www.kotlinlang.org/'
+ vcsUrl = 'https://github.com/kotlin/dokka.git'
+ description = 'Dokka, the Kotlin documentation tool'
+ tags = ['dokka', 'kotlin', 'kdoc']
+
+ plugins {
+ dokkaGradlePlugin {
+ id = 'org.jetbrains.dokka'
+ displayName = 'Dokka plugin'
+ }
+ }
+
+ withDependencies { List<Dependency> list ->
+ list.clear()
+ def builder = new DependenciesBuilder()
+ builder.addUniqueScopedDependencies(list, configurations.shadow, "compile")
+ }
+
+ mavenCoordinates {
+ groupId = "org.jetbrains.dokka"
+ artifactId = "dokka-gradle-plugin"
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt b/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt
new file mode 100644
index 000000000..7bdf2f9d1
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt
@@ -0,0 +1,46 @@
+package org.jetbrains.dokka
+
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+
+
+/**
+ * Warning! Hard reflection magic used here.
+ *
+ * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm,
+ * to create access proxy for [delegate] into [targetClassLoader].
+ */
+@Suppress("UNCHECKED_CAST")
+inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, delegate: Any): T =
+ automagicProxy(targetClassLoader, T::class.java, delegate) as T
+
+
+/**
+ * Warning! Hard reflection magic used here.
+ *
+ * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm,
+ * to create access proxy for [delegate] into [targetClassLoader].
+ *
+ */
+fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any =
+ Proxy.newProxyInstance(
+ targetClassLoader,
+ arrayOf(targetType),
+ DelegatedInvocationHandler(delegate)
+ )
+
+class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler {
+
+ @Throws(Throwable::class)
+ override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any? {
+ val delegateMethod = delegate.javaClass.getMethod(method.name, *method.parameterTypes)
+ try {
+ delegateMethod.isAccessible = true
+ return delegateMethod.invoke(delegate, *(args ?: emptyArray()))
+ } catch (ex: InvocationTargetException) {
+ throw ex.targetException
+ }
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
new file mode 100644
index 000000000..9555fa828
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -0,0 +1,478 @@
+package org.jetbrains.dokka.gradle
+
+import groovy.lang.Closure
+import org.gradle.api.DefaultTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.file.FileCollection
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.*
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.compile.AbstractCompile
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.ReflectDsl.isNotInstance
+import org.jetbrains.dokka.gradle.ClassloaderContainer.fatJarClassLoader
+import org.jetbrains.dokka.gradle.DokkaVersion.version
+import ru.yole.jkid.JsonExclude
+import ru.yole.jkid.serialization.serialize
+import java.io.File
+import java.io.InputStream
+import java.io.Serializable
+import java.net.URLClassLoader
+import java.util.*
+import java.util.concurrent.Callable
+import java.util.function.BiConsumer
+
+open class DokkaPlugin : Plugin<Project> {
+
+ override fun apply(project: Project) {
+ DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties"))
+ project.tasks.create("dokka", DokkaTask::class.java).apply {
+ moduleName = project.name
+ outputDirectory = File(project.buildDir, "dokka").absolutePath
+ }
+ }
+}
+
+object DokkaVersion {
+ var version: String? = null
+
+ fun loadFrom(stream: InputStream) {
+ version = Properties().apply {
+ load(stream)
+ }.getProperty("dokka-version")
+ }
+}
+
+
+object ClassloaderContainer {
+ @JvmField
+ var fatJarClassLoader: ClassLoader? = null
+}
+
+const val `deprecationMessage reportNotDocumented` = "Will be removed in 0.9.17, see dokka#243"
+
+open class DokkaTask : DefaultTask() {
+
+ fun defaultKotlinTasks() = with(ReflectDsl) {
+ val abstractKotlinCompileClz = try {
+ project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
+ } catch (cnfe: ClassNotFoundException) {
+ logger.warn("$ABSTRACT_KOTLIN_COMPILE class not found, default kotlin tasks ignored")
+ return@with emptyList<Task>()
+ }
+
+ return@with project.tasks.filter { it isInstance abstractKotlinCompileClz }.filter { "Test" !in it.name }
+ }
+
+ init {
+ group = JavaBasePlugin.DOCUMENTATION_GROUP
+ description = "Generates dokka documentation for Kotlin"
+
+ @Suppress("LeakingThis")
+ dependsOn(Callable { kotlinTasks.map { it.taskDependencies } })
+ }
+
+ @Input
+ var moduleName: String = ""
+ @Input
+ var outputFormat: String = "html"
+ @get:Internal // handled by getOutputDirectoryAsFile
+ var outputDirectory: String = ""
+
+
+ @Deprecated("Going to be removed in 0.9.16, use classpath + sourceDirs instead if kotlinTasks is not suitable for you")
+ @Input var processConfigurations: List<Any?> = emptyList()
+
+ @InputFiles var classpath: Iterable<File> = arrayListOf()
+
+ @Input
+ var includes: List<Any?> = arrayListOf()
+ @Input
+ var linkMappings: ArrayList<LinkMapping> = arrayListOf()
+ @Input
+ var samples: List<Any?> = arrayListOf()
+ @Input
+ var jdkVersion: Int = 6
+
+ @Input
+ var generateClassIndexPage = true
+
+ @Input
+ var generatePackageIndexPage = true
+
+ @Input
+ var sourceDirs: Iterable<File> = emptyList()
+
+ @Input
+ var sourceRoots: MutableList<SourceRoot> = arrayListOf()
+
+ @Input
+ var dokkaFatJar: Any = "org.jetbrains.dokka:dokka-fatjar:$version"
+
+ @Input var includeNonPublic = false
+ @Input var skipDeprecated = false
+ @Input var skipEmptyPackages = true
+
+ @Input var outlineRoot: String = ""
+ @Input var dacRoot: String = ""
+
+ @get:Input
+ @Deprecated(`deprecationMessage reportNotDocumented`, replaceWith = ReplaceWith("reportUndocumented"))
+ var reportNotDocumented
+ get() = reportUndocumented
+ set(value) {
+ logger.warn("Dokka: reportNotDocumented is deprecated and " + `deprecationMessage reportNotDocumented`.decapitalize())
+ reportUndocumented = value
+ }
+
+ @Input var reportUndocumented = true
+ @Input var perPackageOptions: MutableList<PackageOptions> = arrayListOf()
+ @Input var impliedPlatforms: MutableList<String> = arrayListOf()
+
+ @Input var externalDocumentationLinks = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
+
+ @Input var noStdlibLink: Boolean = false
+
+ @Input
+ var noJdkLink: Boolean = false
+
+ @Optional @Input
+ var cacheRoot: String? = null
+
+
+ @Optional @Input
+ var languageVersion: String? = null
+
+ @Optional @Input
+ var apiVersion: String? = null
+
+ @Input
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+
+ @get:Internal
+ internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
+
+
+ private var kotlinTasksConfigurator: () -> List<Any?>? = { defaultKotlinTasks() }
+ private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks() }
+
+ fun kotlinTasks(closure: Closure<Any?>) {
+ kotlinTasksConfigurator = { closure.call() as? List<Any?> }
+ }
+
+ fun linkMapping(closure: Closure<Unit>) {
+ val mapping = LinkMapping()
+ closure.delegate = mapping
+ closure.call()
+
+ if (mapping.path.isEmpty()) {
+ throw IllegalArgumentException("Link mapping should have dir")
+ }
+ if (mapping.url.isEmpty()) {
+ throw IllegalArgumentException("Link mapping should have url")
+ }
+
+ linkMappings.add(mapping)
+ }
+
+ fun sourceRoot(closure: Closure<Unit>) {
+ val sourceRoot = SourceRoot()
+ closure.delegate = sourceRoot
+ closure.call()
+ sourceRoots.add(sourceRoot)
+ }
+
+ fun packageOptions(closure: Closure<Unit>) {
+ val packageOptions = PackageOptions()
+ closure.delegate = packageOptions
+ closure.call()
+ perPackageOptions.add(packageOptions)
+ }
+
+ fun externalDocumentationLink(closure: Closure<Unit>) {
+ val builder = DokkaConfiguration.ExternalDocumentationLink.Builder()
+ closure.delegate = builder
+ closure.call()
+ externalDocumentationLinks.add(builder.build())
+ }
+
+ fun tryResolveFatJar(project: Project): File {
+ return try {
+ val dependency = project.buildscript.dependencies.create(dokkaFatJar)
+ val configuration = project.buildscript.configurations.detachedConfiguration(dependency)
+ configuration.description = "Dokka main jar"
+ configuration.resolve().first()
+ } catch (e: Exception) {
+ project.parent?.let { tryResolveFatJar(it) } ?: throw e
+ }
+ }
+
+ fun loadFatJar() {
+ if (fatJarClassLoader == null) {
+ val fatjar = if (dokkaFatJar is File)
+ dokkaFatJar as File
+ else
+ tryResolveFatJar(project)
+ fatJarClassLoader = URLClassLoader(arrayOf(fatjar.toURI().toURL()), ClassLoader.getSystemClassLoader().parent)
+ }
+ }
+
+ internal data class ClasspathAndSourceRoots(val classpathFileCollection: FileCollection, val sourceRoots: List<File>) : Serializable
+
+ private fun extractKotlinCompileTasks(): List<Task> {
+ val inputList = (kotlinTasksConfigurator.invoke() ?: emptyList()).filterNotNull()
+ val (paths, other) = inputList.partition { it is String }
+
+ val taskContainer = project.tasks
+
+ val tasksByPath = paths.map { taskContainer.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") }
+
+ other
+ .filter { it !is Task || it isNotInstance getAbstractKotlinCompileFor(it) }
+ .forEach { throw IllegalArgumentException("Illegal entry in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE or String, but was $it") }
+
+ tasksByPath
+ .filter { it == null || it isNotInstance getAbstractKotlinCompileFor(it) }
+ .forEach { throw IllegalArgumentException("Illegal task path in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE, but was $it") }
+
+
+ return (tasksByPath + other) as List<Task>
+ }
+
+ private fun extractClasspathAndSourceRootsFromKotlinTasks(): ClasspathAndSourceRoots {
+
+ val allTasks = kotlinTasks
+
+ val allClasspath = mutableSetOf<File>()
+ var allClasspathFileCollection: FileCollection = project.files()
+ val allSourceRoots = mutableSetOf<File>()
+
+ allTasks.forEach {
+
+ logger.debug("Dokka found AbstractKotlinCompile task: $it")
+ with(ReflectDsl) {
+ val taskSourceRoots: List<File> = it["sourceRootsContainer"]["sourceRoots"].v()
+
+ val abstractKotlinCompileClz = getAbstractKotlinCompileFor(it)!!
+
+ val taskClasspath: Iterable<File> =
+ (it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke()
+ ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v()
+ ?: it["getClasspath", abstractKotlinCompileClz]())
+
+ if (taskClasspath is FileCollection) {
+ allClasspathFileCollection += taskClasspath
+ } else {
+ allClasspath += taskClasspath
+ }
+ allSourceRoots += taskSourceRoots.filter { it.exists() }
+ }
+ }
+
+ return ClasspathAndSourceRoots(allClasspathFileCollection + project.files(allClasspath), allSourceRoots.toList())
+ }
+
+ private fun Iterable<File>.toSourceRoots(): List<SourceRoot> = this.filter { it.exists() }.map { SourceRoot().apply { path = it.path } }
+
+ protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>): List<String> = emptyList()
+
+ @TaskAction
+ fun generate() {
+ val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false"
+ System.setProperty(COLORS_ENABLED_PROPERTY, "false")
+ try {
+ loadFatJar()
+
+ val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
+
+ val project = project
+ val sourceRoots = collectSourceRoots() + tasksSourceRoots.toSourceRoots()
+
+ if (sourceRoots.isEmpty()) {
+ logger.warn("No source directories found: skipping dokka generation")
+ return
+ }
+
+ val fullClasspath = collectClasspathFromOldSources() + tasksClasspath + classpath
+
+ val bootstrapClass = fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl")
+
+ val bootstrapInstance = bootstrapClass.constructors.first().newInstance()
+
+ val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
+
+ val configuration = SerializeOnlyDokkaConfiguration(
+ moduleName,
+ fullClasspath.map { it.absolutePath },
+ sourceRoots,
+ samples.filterNotNull().map { project.file(it).absolutePath },
+ includes.filterNotNull().map { project.file(it).absolutePath },
+ outputDirectory,
+ outputFormat,
+ includeNonPublic,
+ false,
+ reportUndocumented,
+ skipEmptyPackages,
+ skipDeprecated,
+ jdkVersion,
+ generateClassIndexPage,
+ generatePackageIndexPage,
+ linkMappings,
+ impliedPlatforms,
+ perPackageOptions,
+ externalDocumentationLinks,
+ noStdlibLink,
+ noJdkLink,
+ cacheRoot,
+ collectSuppressedFiles(sourceRoots),
+ languageVersion,
+ apiVersion,
+ collectInheritedExtensionsFromLibraries,
+ outlineRoot,
+ dacRoot)
+
+
+ bootstrapProxy.configure(
+ BiConsumer { level, message ->
+ when (level) {
+ "info" -> logger.info(message)
+ "warn" -> logger.warn(message)
+ "error" -> logger.error(message)
+ }
+ },
+ serialize(configuration)
+ )
+
+ bootstrapProxy.generate()
+
+ } finally {
+ System.setProperty(COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore)
+ }
+ }
+
+ private fun collectClasspathFromOldSources(): List<File> {
+
+ val allConfigurations = project.configurations
+
+ val fromConfigurations =
+ processConfigurations.flatMap { allConfigurations.getByName(it.toString()) }
+
+ return fromConfigurations
+ }
+
+ private fun collectSourceRoots(): List<SourceRoot> {
+ val sourceDirs = if (sourceDirs.any()) {
+ logger.info("Dokka: Taking source directories provided by the user")
+ sourceDirs.toSet()
+ } else if (kotlinTasks.isEmpty()) {
+ project.convention.findPlugin(JavaPluginConvention::class.java)?.let { javaPluginConvention ->
+ logger.info("Dokka: Taking source directories from default java plugin")
+ val sourceSets = javaPluginConvention.sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)
+ sourceSets?.allSource?.srcDirs
+ }
+ } else {
+ emptySet()
+ }
+
+ return sourceRoots + (sourceDirs?.toSourceRoots() ?: emptyList())
+ }
+
+
+ @Classpath
+ fun getInputClasspath(): FileCollection {
+ val (classpathFileCollection) = extractClasspathAndSourceRootsFromKotlinTasks()
+ return project.files(collectClasspathFromOldSources() + classpath) + classpathFileCollection
+ }
+
+ @InputFiles
+ fun getInputFiles(): FileCollection {
+ val (_, tasksSourceRoots) = extractClasspathAndSourceRootsFromKotlinTasks()
+ return project.files(tasksSourceRoots.map { project.fileTree(it) }) +
+ project.files(collectSourceRoots().map { project.fileTree(File(it.path)) }) +
+ project.files(includes) +
+ project.files(samples.filterNotNull().map { project.fileTree(it) })
+ }
+
+ @OutputDirectory
+ fun getOutputDirectoryAsFile(): File = project.file(outputDirectory)
+
+ companion object {
+ const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled"
+ const val ABSTRACT_KOTLIN_COMPILE = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile"
+
+ private fun getAbstractKotlinCompileFor(task: Task) = try {
+ task.project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
+ } catch (e: ClassNotFoundException) {
+ null
+ }
+ }
+}
+
+class SourceRoot : DokkaConfiguration.SourceRoot, Serializable {
+ override var path: String = ""
+ set(value) {
+ field = File(value).absolutePath
+ }
+
+ override var platforms: List<String> = arrayListOf()
+
+ override fun toString(): String {
+ return "${platforms.joinToString()}::$path"
+ }
+}
+
+open class LinkMapping : Serializable, DokkaConfiguration.SourceLinkDefinition {
+ @JsonExclude
+ var dir: String
+ get() = path
+ set(value) {
+ path = value
+ }
+
+ override var path: String = ""
+ override var url: String = ""
+
+ @JsonExclude
+ var suffix: String?
+ get() = lineSuffix
+ set(value) {
+ lineSuffix = value
+ }
+
+ override var lineSuffix: String? = null
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other?.javaClass != javaClass) return false
+
+ other as LinkMapping
+
+ if (path != other.path) return false
+ if (url != other.url) return false
+ if (lineSuffix != other.lineSuffix) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = path.hashCode()
+ result = 31 * result + url.hashCode()
+ result = 31 * result + (lineSuffix?.hashCode() ?: 0)
+ return result
+ }
+
+ companion object {
+ const val serialVersionUID: Long = -8133501684312445981L
+ }
+}
+
+class PackageOptions : Serializable, DokkaConfiguration.PackageOptions {
+ override var prefix: String = ""
+ override var includeNonPublic: Boolean = false
+ override var reportUndocumented: Boolean = true
+ override var skipDeprecated: Boolean = false
+ override var suppress: Boolean = false
+}
diff --git a/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties b/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties
new file mode 100644
index 000000000..068bd3522
--- /dev/null
+++ b/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties
@@ -0,0 +1,2 @@
+implementation-class=org.jetbrains.dokka.gradle.DokkaPlugin
+dokka-version=<version> \ No newline at end of file
diff --git a/runners/maven-plugin/build.gradle b/runners/maven-plugin/build.gradle
new file mode 100644
index 000000000..acd3415d7
--- /dev/null
+++ b/runners/maven-plugin/build.gradle
@@ -0,0 +1,112 @@
+import groovy.io.FileType
+import org.jetbrains.CorrectShadowPublishing
+import org.jetbrains.CrossPlatformExec
+
+import java.nio.file.Files
+import java.nio.file.StandardCopyOption
+
+apply plugin: 'kotlin'
+apply plugin: 'com.github.johnrengelman.shadow'
+
+sourceCompatibility = 1.8
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ freeCompilerArgs += "-Xjsr305=strict"
+ languageVersion = "1.2"
+ apiVersion = languageVersion
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ shadow project(":runners:fatjar")
+ shadow "org.apache.maven:maven-core:$maven_version"
+ shadow "org.apache.maven:maven-model:$maven_version"
+ shadow "org.apache.maven:maven-plugin-api:$maven_version"
+ shadow "org.apache.maven:maven-archiver:$maven_archiver_version"
+ shadow "org.codehaus.plexus:plexus-utils:$plexus_utils_version"
+ shadow "org.codehaus.plexus:plexus-archiver:$plexus_archiver_version"
+ shadow "org.apache.maven.plugin-tools:maven-plugin-annotations:$maven_plugin_tools_version"
+ shadow "com.github.olivergondza:maven-jdk-tools-wrapper:0.1"
+}
+
+task generatePom() {
+ inputs.file(new File(projectDir, "pom.tpl.xml"))
+ inputs.property("dokka_version", dokka_version)
+ inputs.property("maven_version", maven_version)
+ inputs.property("maven_plugin_tools_version", maven_plugin_tools_version)
+ outputs.file(new File(buildDir, "pom.xml"))
+ doLast {
+ final pomTemplate = new File(projectDir, "pom.tpl.xml")
+ final pom = new File(buildDir, "pom.xml")
+ pom.text = pomTemplate.text.replace("<version>dokka_version</version>", "<version>$dokka_version</version>")
+ .replace("<maven.version></maven.version>", "<maven.version>$maven_version</maven.version>")
+ .replace("<version>maven-plugin-plugin</version>", "<version>$maven_plugin_tools_version</version>")
+ }
+}
+
+task mergeClassOutputs doLast {
+ def sourceDir = new File(buildDir, "classes/kotlin")
+ def targetDir = new File(buildDir, "classes/java")
+
+ sourceDir.eachFileRecurse FileType.ANY, {
+ def filePath = it.toPath()
+ def targetFilePath = targetDir.toPath().resolve(sourceDir.toPath().relativize(filePath))
+ if (it.isFile()) {
+ Files.move(filePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING)
+ } else if (it.isDirectory()) {
+ targetFilePath.toFile().mkdirs()
+ }
+ }
+}
+
+task pluginDescriptor(type: CrossPlatformExec) {
+ workingDir buildDir
+ commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:descriptor'
+
+ dependsOn mergeClassOutputs
+}
+
+task helpMojo(type: CrossPlatformExec) {
+ workingDir buildDir
+ commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:helpmojo'
+
+ dependsOn mergeClassOutputs
+}
+
+helpMojo.dependsOn generatePom
+sourceSets.main.java.srcDir("$buildDir/generated-sources/plugin")
+compileJava.dependsOn helpMojo
+
+pluginDescriptor.dependsOn generatePom
+
+shadowJar {
+ baseName = 'dokka-maven-plugin'
+ classifier = ''
+}
+
+shadowJar.dependsOn pluginDescriptor
+
+
+task sourceJar(type: Jar) {
+ from sourceSets.main.allSource
+}
+
+apply plugin: 'maven-publish'
+
+publishing {
+ publications {
+ dokkaMavenPlugin(MavenPublication) { MavenPublication publication ->
+ artifactId = 'dokka-maven-plugin'
+
+ artifact sourceJar {
+ classifier "sources"
+ }
+
+ CorrectShadowPublishing.configure(publication, project)
+ }
+ }
+}
+
+bintrayPublication(project, ['dokkaMavenPlugin'])
diff --git a/runners/maven-plugin/pom.tpl.xml b/runners/maven-plugin/pom.tpl.xml
new file mode 100644
index 000000000..c5883c6a6
--- /dev/null
+++ b/runners/maven-plugin/pom.tpl.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.jetbrains.dokka</groupId>
+ <artifactId>dokka-maven-plugin</artifactId>
+ <version>dokka_version</version>
+ <packaging>maven-plugin</packaging>
+ <properties>
+ <maven.version></maven.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-plugin-plugin</artifactId>
+ <version>maven-plugin-plugin</version>
+ <configuration>
+ <helpPackageName>org.jetbrains.dokka.maven</helpPackageName>
+ </configuration>
+ </plugin>
+ </plugins>
+ <directory>./</directory>
+ <outputDirectory>./classes/java/main</outputDirectory>
+ </build>
+</project>
diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
new file mode 100644
index 000000000..dcb9ac2c2
--- /dev/null
+++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
@@ -0,0 +1,252 @@
+package org.jetbrains.dokka.maven
+
+import org.apache.maven.archiver.MavenArchiveConfiguration
+import org.apache.maven.archiver.MavenArchiver
+import org.apache.maven.execution.MavenSession
+import org.apache.maven.plugin.AbstractMojo
+import org.apache.maven.plugins.annotations.*
+import org.apache.maven.project.MavenProject
+import org.apache.maven.project.MavenProjectHelper
+import org.codehaus.plexus.archiver.Archiver
+import org.codehaus.plexus.archiver.jar.JarArchiver
+import org.jetbrains.dokka.*
+import java.io.File
+import java.net.URL
+
+class SourceLinkMapItem {
+ @Parameter(name = "dir", required = true)
+ var dir: String = ""
+
+ @Parameter(name = "url", required = true)
+ var url: String = ""
+
+ @Parameter(name = "urlSuffix")
+ var urlSuffix: String? = null
+}
+
+class ExternalDocumentationLinkBuilder : DokkaConfiguration.ExternalDocumentationLink.Builder() {
+
+ @Parameter(name = "url", required = true)
+ override var url: URL? = null
+ @Parameter(name = "packageListUrl", required = true)
+ override var packageListUrl: URL? = null
+}
+
+abstract class AbstractDokkaMojo : AbstractMojo() {
+ class SourceRoot : DokkaConfiguration.SourceRoot {
+ @Parameter(required = true)
+ override var path: String = ""
+
+ @Parameter
+ override var platforms: List<String> = emptyList()
+ }
+
+ class PackageOptions : DokkaConfiguration.PackageOptions {
+ @Parameter
+ override var prefix: String = ""
+ @Parameter
+ override var includeNonPublic: Boolean = false
+ @Parameter
+ override var reportUndocumented: Boolean = true
+ @Parameter
+ override var skipDeprecated: Boolean = false
+ @Parameter
+ override var suppress: Boolean = false
+ }
+
+ @Parameter(required = true, defaultValue = "\${project.compileSourceRoots}")
+ var sourceDirectories: List<String> = emptyList()
+
+ @Parameter
+ var sourceRoots: List<SourceRoot> = emptyList()
+
+ @Parameter
+ var samplesDirs: List<String> = emptyList()
+
+ @Parameter
+ @Deprecated("Use <includes> instead")
+ var includeDirs: List<String> = emptyList()
+
+ @Parameter
+ var includes: List<String> = emptyList()
+
+ @Parameter(required = true, defaultValue = "\${project.compileClasspathElements}")
+ var classpath: List<String> = emptyList()
+
+ @Parameter
+ var sourceLinks: Array<SourceLinkMapItem> = emptyArray()
+
+ @Parameter(required = true, defaultValue = "\${project.artifactId}")
+ var moduleName: String = ""
+
+ @Parameter(required = false, defaultValue = "false")
+ var skip: Boolean = false
+
+ @Parameter(required = false, defaultValue = "6")
+ var jdkVersion: Int = 6
+
+ @Parameter
+ var skipDeprecated = false
+ @Parameter
+ var skipEmptyPackages = true
+ @Parameter
+ var reportNotDocumented = true
+
+ @Parameter
+ var impliedPlatforms: List<String> = emptyList()
+
+ @Parameter
+ var perPackageOptions: List<PackageOptions> = emptyList()
+
+ @Parameter
+ var externalDocumentationLinks: List<ExternalDocumentationLinkBuilder> = emptyList()
+
+ @Parameter(defaultValue = "false")
+ var noStdlibLink: Boolean = false
+
+ @Parameter(defaultValue = "false")
+ var noJdkLink: Boolean = false
+
+ @Parameter
+ var cacheRoot: String? = null
+
+ @Parameter
+ var languageVersion: String? = null
+
+ @Parameter
+ var apiVersion: String? = null
+
+ protected abstract fun getOutDir(): String
+ protected abstract fun getOutFormat(): String
+
+ override fun execute() {
+ if (skip) {
+ log.info("Dokka skip parameter is true so no dokka output will be produced")
+ return
+ }
+
+ val gen = DokkaGenerator(
+ MavenDokkaLogger(log),
+ classpath,
+ sourceDirectories.map { SourceRootImpl(it) } + sourceRoots,
+ samplesDirs,
+ includeDirs + includes,
+ moduleName,
+ DocumentationOptions(getOutDir(), getOutFormat(),
+ sourceLinks = sourceLinks.map { SourceLinkDefinitionImpl(it.dir, it.url, it.urlSuffix) },
+ jdkVersion = jdkVersion,
+ skipDeprecated = skipDeprecated,
+ skipEmptyPackages = skipEmptyPackages,
+ reportUndocumented = reportNotDocumented,
+ impliedPlatforms = impliedPlatforms,
+ perPackageOptions = perPackageOptions,
+ externalDocumentationLinks = externalDocumentationLinks.map { it.build() },
+ noStdlibLink = noStdlibLink,
+ noJdkLink = noJdkLink,
+ cacheRoot = cacheRoot,
+ languageVersion = languageVersion,
+ apiVersion = apiVersion
+ )
+ )
+
+ gen.generate()
+ }
+}
+
+@Mojo(name = "dokka", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
+class DokkaMojo : AbstractDokkaMojo() {
+ @Parameter(required = true, defaultValue = "html")
+ var outputFormat: String = "html"
+
+ @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokka")
+ var outputDir: String = ""
+
+ override fun getOutFormat() = outputFormat
+ override fun getOutDir() = outputDir
+}
+
+@Mojo(name = "javadoc", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
+class DokkaJavadocMojo : AbstractDokkaMojo() {
+ @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadoc")
+ var outputDir: String = ""
+
+ override fun getOutFormat() = "javadoc"
+ override fun getOutDir() = outputDir
+}
+
+@Mojo(name = "javadocJar", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
+class DokkaJavadocJarMojo : AbstractDokkaMojo() {
+ @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadocJar")
+ var outputDir: String = ""
+
+ /**
+ * Specifies the directory where the generated jar file will be put.
+ */
+ @Parameter(property = "project.build.directory")
+ private var jarOutputDirectory: String? = null
+
+ /**
+ * Specifies the filename that will be used for the generated jar file. Please note that `-javadoc`
+ * or `-test-javadoc` will be appended to the file name.
+ */
+ @Parameter(property = "project.build.finalName")
+ private var finalName: String? = null
+
+ /**
+ * Specifies whether to attach the generated artifact to the project helper.
+ */
+ @Parameter(property = "attach", defaultValue = "true")
+ private val attach: Boolean = false
+
+ /**
+ * The archive configuration to use.
+ * See [Maven Archiver Reference](http://maven.apache.org/shared/maven-archiver/index.html)
+ */
+ @Parameter
+ private val archive = MavenArchiveConfiguration()
+
+ @Parameter(property = "maven.javadoc.classifier", defaultValue = "javadoc", required = true)
+ private var classifier: String? = null
+
+ @Parameter(defaultValue = "\${session}", readonly = true, required = true)
+ protected var session: MavenSession? = null
+
+ @Parameter(defaultValue = "\${project}", readonly = true, required = true)
+ protected var project: MavenProject? = null
+
+ @Component
+ private var projectHelper: MavenProjectHelper? = null
+
+ @Component(role = Archiver::class, hint = "jar")
+ private var jarArchiver: JarArchiver? = null
+
+ override fun getOutFormat() = "javadoc"
+ override fun getOutDir() = outputDir
+
+ override fun execute() {
+ super.execute()
+ if(!File(outputDir).exists()) {
+ log.warn("No javadoc generated so no javadoc jar will be generated")
+ return
+ }
+ val outputFile = generateArchive("$finalName-$classifier.jar")
+ if (attach) {
+ projectHelper?.attachArtifact(project, "javadoc", classifier, outputFile)
+ }
+ }
+
+ private fun generateArchive(jarFileName: String): File {
+ val javadocJar = File(jarOutputDirectory, jarFileName)
+
+ val archiver = MavenArchiver()
+ archiver.setArchiver(jarArchiver)
+ archiver.setOutputFile(javadocJar)
+ archiver.archiver.addDirectory(File(outputDir), arrayOf("**/**"), arrayOf())
+
+ archive.setAddMavenDescriptor(false)
+ archiver.createArchive(session, project, archive)
+
+ return javadocJar
+ }
+}
+
diff --git a/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt
new file mode 100644
index 000000000..a535c8076
--- /dev/null
+++ b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt
@@ -0,0 +1,18 @@
+package org.jetbrains.dokka.maven
+
+import org.apache.maven.plugin.logging.Log
+import org.jetbrains.dokka.DokkaLogger
+
+class MavenDokkaLogger(val log: Log) : DokkaLogger {
+ override fun error(message: String) {
+ log.error(message)
+ }
+
+ override fun info(message: String) {
+ log.info(message)
+ }
+
+ override fun warn(message: String) {
+ log.warn(message)
+ }
+} \ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 000000000..712f03744
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,12 @@
+rootProject.name = "dokka"
+
+include 'core',
+ 'integration',
+ 'runners:fatjar',
+ 'runners:ant',
+ 'runners:cli',
+ // Google's fork does not use maven-plugin. Removing it from build for convenience.
+ // 'runners:maven-plugin',
+ 'runners:gradle-plugin',
+ 'runners:android-gradle-plugin',
+ 'runners:gradle-integration-tests'
diff --git a/test/playground.kt b/test/playground.kt
new file mode 100644
index 000000000..5206e10e3
--- /dev/null
+++ b/test/playground.kt
@@ -0,0 +1,99 @@
+// this file is not included in sources or tests, you can play with it for debug purposes
+// Console run configuration will analyse it and provide lots of debug output
+package dokka.playground
+
+fun topLevelFunction() {
+}
+
+val topLevelConstantValue = "Hello"
+
+val topLevelValue: String
+ get() = "Bye bye"
+
+var topLevelVariable: String
+ get() = "Modify me!"
+ set(value) {
+ }
+
+/**
+ * This is a class
+ */
+class Class {
+ fun memberFunction() {
+ }
+
+ val memberValue = "Member"
+}
+
+/**
+ * This is a class with constructor and space after doc
+ */
+
+class ClassWithConstructor(
+ /** Doc at parameter */ val name: Class)
+
+/**
+ * This is data class with constructor and two properties
+ * Also look at [Employee]
+ *
+ * $name Person's name
+ * $age Person's age
+ *
+ */
+data class Person(val name: ClassWithConstructor, val age: Int) {}
+
+data class Employee(val name: ClassWithConstructor, val age: Int) {}
+
+object Object {
+ throws(javaClass<IllegalArgumentException>())
+ fun objectFunction() {
+ }
+
+ val objectValue: String
+ /** one line getter doc */
+ get() = "Member"
+
+ public val String.valueWithReceiver: Int
+ get() = 1
+
+}
+
+enum class Color(r: Int, g: Int, b: Int) {
+ Red : Color(100,0,0)
+ Green : Color(0,100,0)
+ Blue : Color(0,0,100)
+}
+
+class OuterClass {
+
+ /**
+ * $T type of the item
+ */
+ class NestedClass<T> {
+ fun nestedClassFunction(item: T) {
+ }
+
+ fun String.functionWithReceiver(): Int = 1
+
+ }
+
+ inner class InnerClass {
+ open fun innerClassFunction<
+ /** doc for R1 type param */
+ R1,
+ /** doc for R2 type param */
+ R2
+ >() {
+ }
+ }
+
+ object NestedObject {
+ protected open fun nestedObjectFunction() {
+ }
+ }
+}
+
+trait Interface {
+ fun worker()
+ val extra: String
+} \ No newline at end of file