aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2019-03-18 22:05:22 +0300
committerSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2019-03-18 22:05:22 +0300
commitfd2f36301e95f8d100e1c2b942f1590144d8c6c2 (patch)
treedcbf109f14de5301848c29e9721c8e0af6881b3e
parentf2359e05347708e625d5a6b339010cda2adc30de (diff)
parent4b22ebab09ce3b934443d063df6b905e5347c390 (diff)
downloaddokka-fd2f36301e95f8d100e1c2b942f1590144d8c6c2.tar.gz
dokka-fd2f36301e95f8d100e1c2b942f1590144d8c6c2.tar.bz2
dokka-fd2f36301e95f8d100e1c2b942f1590144d8c6c2.zip
Merge branch 'dev'
-rw-r--r--.gitignore2
-rw-r--r--README.md78
-rw-r--r--build.gradle30
-rw-r--r--buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy4
-rw-r--r--core/build.gradle2
-rw-r--r--core/src/main/kotlin/Analysis/AnalysisEnvironment.kt76
-rw-r--r--core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt4
-rw-r--r--core/src/main/kotlin/Analysis/JavaResolveExtension.kt131
-rw-r--r--core/src/main/kotlin/DokkaBootstrapImpl.kt38
-rw-r--r--core/src/main/kotlin/Formats/AnalysisComponents.kt14
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt117
-rw-r--r--core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt3
-rw-r--r--core/src/main/kotlin/Formats/OutlineService.kt2
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt6
-rw-r--r--core/src/main/kotlin/Generation/DokkaGenerator.kt20
-rw-r--r--core/src/main/kotlin/Generation/configurationImpl.kt52
-rw-r--r--core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt87
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt353
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt9
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt32
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt377
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt61
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt22
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt5
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt25
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt217
-rw-r--r--core/src/main/kotlin/Languages/CommonLanguageService.kt84
-rw-r--r--core/src/main/kotlin/Languages/NewJavaLanguageService.kt197
-rw-r--r--core/src/main/kotlin/Model/Content.kt38
-rw-r--r--core/src/main/kotlin/Model/DescriptorSignatureProvider.kt7
-rw-r--r--core/src/main/kotlin/Model/DocumentationNode.kt86
-rw-r--r--core/src/main/kotlin/Model/DocumentationReference.kt5
-rw-r--r--core/src/main/kotlin/Model/ElementSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Model/PackageDocs.kt74
-rw-r--r--core/src/main/kotlin/Model/SourceLinks.kt38
-rw-r--r--core/src/main/kotlin/Utilities/DokkaModules.kt6
-rw-r--r--core/src/main/kotlin/Utilities/Html.kt4
-rw-r--r--core/src/main/kotlin/Utilities/ServiceLocator.kt16
-rw-r--r--core/src/main/kotlin/Utilities/Uri.kt40
-rw-r--r--core/src/main/kotlin/javadoc/docbase.kt49
-rw-r--r--core/src/main/kotlin/javadoc/tags.kt2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties2
-rw-r--r--core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties2
-rw-r--r--core/src/test/kotlin/NodeSelect.kt90
-rw-r--r--core/src/test/kotlin/TestAPI.kt95
-rw-r--r--core/src/test/kotlin/format/HtmlFormatTest.kt6
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt1
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt1
-rw-r--r--core/src/test/kotlin/format/MarkdownFormatTest.kt21
-rw-r--r--core/src/test/kotlin/format/PackageDocsTest.kt33
-rw-r--r--core/src/test/kotlin/javadoc/JavadocTest.kt112
-rw-r--r--core/src/test/kotlin/model/FunctionTest.kt27
-rw-r--r--core/src/test/kotlin/model/JavaTest.kt8
-rw-r--r--core/src/test/kotlin/model/KotlinAsJavaTest.kt20
-rw-r--r--core/src/test/kotlin/model/PackageTest.kt14
-rw-r--r--core/src/test/kotlin/model/PropertyTest.kt2
-rw-r--r--core/src/test/kotlin/model/SourceLinksErrorTest.kt34
-rw-r--r--core/src/test/kotlin/model/SourceLinksTest.kt74
-rw-r--r--core/testdata/format/JavaSupertype.html4
-rw-r--r--core/testdata/format/enumRef.md2
-rw-r--r--core/testdata/format/externalReferenceLink.kt2
-rw-r--r--core/testdata/format/externalReferenceLink.md4
-rw-r--r--core/testdata/format/inheritedLink.md2
-rw-r--r--core/testdata/format/inlineSuspendFunction.kt6
-rw-r--r--core/testdata/format/inlineSuspendFunction.md8
-rw-r--r--core/testdata/format/javaCodeInParam.java10
-rw-r--r--core/testdata/format/javaCodeInParam.md13
-rw-r--r--core/testdata/format/javaCodeLiteralTags.md2
-rw-r--r--core/testdata/format/javadocHtml.java12
-rw-r--r--core/testdata/format/javadocHtml.md22
-rw-r--r--core/testdata/format/jdkLinks.md8
-rw-r--r--core/testdata/format/markdownInLinks.html2
-rw-r--r--core/testdata/format/markdownInLinks.kt2
-rw-r--r--core/testdata/format/nullableTypeParameterFunction.kt8
-rw-r--r--core/testdata/format/nullableTypeParameterFunction.md18
-rw-r--r--core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md4
-rw-r--r--core/testdata/format/suspendInlineFunction.kt6
-rw-r--r--core/testdata/format/suspendInlineFunction.md8
-rw-r--r--core/testdata/functions/inlineSuspendFunction.kt2
-rw-r--r--core/testdata/functions/suspendFunction.kt2
-rw-r--r--core/testdata/functions/suspendInlineFunction.kt2
-rw-r--r--core/testdata/java/constants.java5
-rw-r--r--core/testdata/javadoc/argumentReference.kt4
-rw-r--r--core/testdata/javadoc/constructorParameters.kt14
-rw-r--r--core/testdata/javadoc/defaultNoArgConstructor.kt12
-rw-r--r--core/testdata/javadoc/deprecated.java28
-rw-r--r--core/testdata/javadoc/noArgConstructor.kt12
-rw-r--r--core/testdata/javadoc/vararg.kt3
-rw-r--r--core/testdata/javadoc/visibilityModifiers.kt15
-rw-r--r--core/testdata/markdown/spec.txt52
-rw-r--r--core/testdata/packagedocs/referenceLinks.kotlin.md5
-rw-r--r--core/testdata/packagedocs/referenceLinks.md2
-rw-r--r--core/testdata/packagedocs/referenceLinks.module.md4
-rw-r--r--core/testdata/properties/annotatedProperty.kt2
-rw-r--r--core/testdata/sourceLinks/dummy.kt6
-rw-r--r--gradle.properties9
-rw-r--r--integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt50
-rw-r--r--runners/android-gradle-plugin/build.gradle2
-rw-r--r--runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt1
-rw-r--r--runners/ant/src/main/kotlin/ant/dokka.kt6
-rw-r--r--runners/cli/src/main/kotlin/cli/main.kt32
-rw-r--r--runners/gradle-integration-tests/build.gradle1
-rw-r--r--runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt36
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/androidApp/fileTree.txt1
-rw-r--r--runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt2
-rw-r--r--runners/gradle-integration-tests/testData/basic/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/sourcesChange/build.gradle4
-rw-r--r--runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle84
-rw-r--r--runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle1
-rw-r--r--runners/gradle-plugin/build.gradle2
-rw-r--r--runners/gradle-plugin/src/main/kotlin/main.kt128
-rw-r--r--runners/maven-plugin/build.gradle98
-rw-r--r--runners/maven-plugin/src/main/kotlin/DokkaMojo.kt13
-rw-r--r--settings.gradle2
121 files changed, 3026 insertions, 788 deletions
diff --git a/.gitignore b/.gitignore
index fe5edc3b..59243df5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,7 @@ buildNumber.properties
*.war
*.ear
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+# virtual machine crash logs, see https://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
diff --git a/README.md b/README.md
index 69442dc9..34b01f54 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-dokka [![official JetBrains project](http://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
+dokka [![official JetBrains project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
[![TeamCity (build status)](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/Kotlin_Dokka_DokkaAntMavenGradle.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_Dokka_DokkaAntMavenGradle&branch_KotlinTools_Dokka=%3Cdefault%3E&tab=buildTypeStatusDiv) [ ![Download](https://api.bintray.com/packages/kotlin/dokka/dokka/images/download.svg) ](https://bintray.com/kotlin/dokka/dokka/_latestVersion)
=====
@@ -26,7 +26,7 @@ apply plugin: 'org.jetbrains.dokka'
```
The plugin adds a task named "dokka" to the project.
-
+
Minimal dokka configuration:
```groovy
@@ -37,7 +37,7 @@ dokka {
```
[Output formats](#output_formats)
-
+
The available configuration options are shown below:
```groovy
@@ -52,7 +52,7 @@ dokka {
}
// List of files with module and package documentation
- // http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation
+ // https://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)
@@ -100,19 +100,22 @@ dokka {
// If provided, Dokka generates "source" links for each declaration.
// Repeat for multiple mappings
linkMapping {
- // Source directory
- dir = "src/main/kotlin"
+ // Unix based directory relative path to the root of the project (where you execute gradle respectively).
+ dir = "src/main/kotlin" // or simply "./"
// 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"
+ url = "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin" //remove src/main/kotlin if you use "./" above
// 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
+ // Disable linking to online kotlin-stdlib documentation
noStdlibLink = false
+ // Disable linking to online JDK documentation
+ noJdkLink = false
+
// Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka)
// Repeat for multiple links
externalDocumentationLink {
@@ -157,6 +160,28 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
Please see the [Dokka Gradle example project](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/dokka-gradle-example) for an example.
+#### Dokka Runtime
+If you are using Gradle plugin and you want to change the version of Dokka, you can do it by setting `dokkaRuntime`:
+
+```groovy
+buildscript {
+ ...
+}
+
+apply plugin: 'org.jetbrains.dokka'
+
+repositories {
+ jcenter()
+}
+
+dependencies {
+ dokkaRuntime "org.jetbrains.dokka:dokka-fatjar:0.9.18"
+}
+```
+
+#### FAQ
+Please see the [FAQ](https://github.com/Kotlin/dokka/wiki/faq).
+
#### Android
If you are using Android there is a separate Gradle plugin. Just make sure you apply the plugin after
@@ -250,7 +275,7 @@ The available configuration options are shown below:
<cacheRoot>default</cacheRoot>
<!-- List of '.md' files with package and module docs -->
- <!-- http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation -->
+ <!-- https://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation -->
<includes>
<include>packages.md</include>
<include>extra.md</include>
@@ -263,7 +288,7 @@ The available configuration options are shown below:
<!-- 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 -->
@@ -297,15 +322,18 @@ The available configuration options are shown below:
<!-- 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>
+ <url>https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin</url> <!-- //remove src/main/kotlin if you use "./" above -->
<!--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 -->
+ <!-- Disable linking to online kotlin-stdlib documentation -->
<noStdlibLink>false</noStdlibLink>
+ <!-- Disable linking to online JDK documentation -->
+ <noJdkLink>false</noJdkLink>
+
<!-- Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka) -->
<externalDocumentationLinks>
<link>
@@ -365,11 +393,12 @@ The Ant task supports the following attributes:
* `<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
+ * `noStdlibLink` - disable linking to online kotlin-stdlib documentation
+ * `noJdkLink` - disable linking to online JDK documentation
* `<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
@@ -387,20 +416,21 @@ Dokka supports the following command line arguments:
* `-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
+ * `-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 linking to online kotlin-stdlib documentation
+ * `-noJdkLink` - disable linking to online JDK documentation
+ * `-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
+ * `html` - minimalistic html format used by default, Java classes are translated to Kotlin
+ * `javadoc` - looks like normal Javadoc, Kotlin classes are translated to Java
+ * `html-as-java` - looks like `html`, but Kotlin classes are translated to Java
+ * `markdown` - markdown structured as `html`, Java classes are translated to Kotlin
+ * `gfm` - GitHub flavored markdown
* `jekyll` - Jekyll compatible markdown
* `kotlin-website*` - internal format used for documentation on [kotlinlang.org](https://kotlinlang.org)
diff --git a/build.gradle b/build.gradle
index 35ec271e..0b5d5dbc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ allprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
maven { url "https://plugins.gradle.org/m2/" }
ivy(repo)
@@ -29,7 +29,8 @@ allprojects {
repositories {
mavenCentral()
mavenLocal()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap" }
+ maven { url "https://dl.bintray.com/jetbrains/markdown" }
+ maven { url "https://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" }
@@ -99,4 +100,29 @@ 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/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy
index 3e1d2106..58cfdcf7 100644
--- a/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy
+++ b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy
@@ -35,5 +35,5 @@ private static void addDependency(Node dependenciesNode, dep) {
dependencyNode.appendNode('groupId', dep.group)
dependencyNode.appendNode('artifactId', dep.name)
dependencyNode.appendNode('version', dep.version)
- dependencyNode.appendNode('scope', 'runtime')
-} \ No newline at end of file
+ dependencyNode.appendNode('scope', 'compile')
+}
diff --git a/core/build.gradle b/core/build.gradle
index a87d3c97..a4538471 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -29,7 +29,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version"
compile "teamcity:kotlin-ide-common:$bundled_kotlin_compiler_version"
- compile "teamcity:markdown:$markdownVersion"
+ compile "org.jetbrains:markdown:$markdownVersion"
compile intellijCoreAnalysis()
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
index 5522d4f0..8ad7b8bb 100644
--- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -18,8 +18,12 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.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
@@ -29,13 +33,13 @@ import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.container.getService
+import org.jetbrains.kotlin.container.tryGetService
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.name.Name
-import org.jetbrains.kotlin.platform.JvmBuiltIns
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
@@ -72,7 +76,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
val projectComponentManager = environment.project as MockComponentManager
val projectFileIndex = CoreProjectFileIndex(environment.project,
- environment.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
+ environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
val moduleManager = object : CoreModuleManager(environment.project, this) {
override fun getModules(): Array<out Module> = arrayOf(projectFileIndex.module)
@@ -98,7 +102,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
}
- fun createResolutionFacade(environment: KotlinCoreEnvironment): DokkaResolutionFacade {
+ fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair<DokkaResolutionFacade, DokkaResolutionFacade> {
val projectContext = ProjectContext(environment.project)
val sourceFiles = environment.getSourceFiles()
@@ -131,43 +135,49 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
}
val resolverForProject = ResolverForProjectImpl(
- "Dokka",
- projectContext,
- listOf(library, module),
- { JvmAnalyzerFacade },
- {
- when (it) {
- library -> ModuleContent(emptyList(), GlobalSearchScope.notScope(sourcesScope))
- module -> ModuleContent(sourceFiles, sourcesScope)
- else -> throw IllegalArgumentException("Unexpected module info")
+ "Dokka",
+ projectContext,
+ listOf(library, module),
+ {
+ when (it) {
+ library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
+ module -> ModuleContent(it, emptyList(), sourcesScope)
+ else -> throw IllegalArgumentException("Unexpected module info")
+ }
+ },
+ {
+ JvmPlatform.multiTargetPlatform
+ },
+ LanguageSettingsProvider.Default /* TODO: Fix this */,
+ { JvmAnalyzerFacade },
+ {
+ JvmPlatformParameters ({ content ->
+ JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
+ addRoots(javaRoots, messageCollector)
}
- },
- JvmPlatformParameters {
+ }, {
val file = (it as JavaClassImpl).psi.containingFile.virtualFile
if (file in sourcesScope)
module
else
library
- },
- CompilerEnvironment,
- packagePartProviderFactory = { info, content ->
- JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
- addRoots(javaRoots)
- }
- },
- builtIns = builtIns,
- modulePlatforms = { JvmPlatform.multiTargetPlatform }
+ })
+ },
+ CompilerEnvironment,
+ builtIns = builtIns
)
- resolverForProject.resolverForModule(library) // Required before module to initialize library properly
+ 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
+ return created to libraryResolutionFacade
}
fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) {
@@ -202,7 +212,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
* List of source roots for this environment.
*/
val sources: List<String>
- get() = configuration.get(JVMConfigurationKeys.CONTENT_ROOTS)
+ get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
?.filterIsInstance<KotlinSourceRoot>()
?.map { it.path } ?: emptyList()
@@ -221,7 +231,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
}
fun addRoots(list: List<ContentRoot>) {
- configuration.addAll(JVMConfigurationKeys.CONTENT_ROOTS, list)
+ configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list)
}
/**
@@ -234,15 +244,19 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable {
fun contentRootFromPath(path: String): ContentRoot {
val file = File(path)
- return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path)
+ return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false)
}
class DokkaResolutionFacade(override val project: Project,
override val moduleDescriptor: ModuleDescriptor,
val resolverForModule: ResolverForModule) : ResolutionFacade {
+ override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult {
+ throw UnsupportedOperationException()
+ }
+
override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? {
- return null
+ return resolverForModule.componentProvider.tryGetService(serviceClass)
}
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
@@ -295,10 +309,6 @@ class DokkaResolutionFacade(override val project: Project,
throw UnsupportedOperationException()
}
- override fun analyzeFullyAndGetResult(elements: Collection<KtElement>): AnalysisResult {
- throw UnsupportedOperationException()
- }
-
override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T {
throw UnsupportedOperationException()
}
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
index 4f6a7c76..319d85b1 100644
--- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
+++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
@@ -20,10 +20,10 @@ import com.intellij.openapi.vfs.VirtualFileFilter
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.messages.MessageBus
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
+import org.jetbrains.kotlin.cli.common.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
-import org.jetbrains.kotlin.config.ContentRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.picocontainer.PicoContainer
import java.io.File
diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
new file mode 100644
index 00000000..4dc6b366
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("JavaResolutionUtils")
+
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import org.jetbrains.kotlin.asJava.classes.KtLightClass
+import org.jetbrains.kotlin.asJava.unwrapped
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.*
+import org.jetbrains.kotlin.load.java.structure.impl.*
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
+import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+
+// TODO: Remove that file
+
+@JvmOverloads
+fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ val method = originalElement as? PsiMethod ?: return null
+ if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null
+ val resolver = method.getJavaDescriptorResolver(resolutionFacade)
+ return when {
+ method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method))
+ else -> resolver?.resolveMethod(JavaMethodImpl(method))
+ }
+}
+
+@JvmOverloads
+fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? {
+ val psiClass = originalElement as? PsiClass ?: return null
+ return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass))
+}
+
+@JvmOverloads
+fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? {
+ val field = originalElement as? PsiField ?: return null
+ return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field))
+}
+
+@JvmOverloads
+fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ return when (this) {
+ is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade)
+ is PsiClass -> getJavaClassDescriptor(resolutionFacade)
+ is PsiMethod -> getJavaMethodDescriptor(resolutionFacade)
+ is PsiField -> getJavaFieldDescriptor(resolutionFacade)
+ else -> null
+ }
+}
+
+@JvmOverloads
+fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+ val callable = unwrapped
+ return when (callable) {
+ is PsiMember -> getJavaMemberDescriptor(resolutionFacade)
+ is KtDeclaration -> {
+ val descriptor = resolutionFacade.resolveToDescriptor(callable)
+ if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor
+ }
+ else -> null
+ }
+}
+
+private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? {
+ return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java)
+}
+
+private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? {
+ return getContainingScope(method)
+ ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES)
+ ?.filterIsInstance<DeclarationDescriptorWithSource>()
+ ?.findByJavaElement(method)
+}
+
+private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? {
+ return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor)
+}
+
+private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? {
+ return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field)
+}
+
+private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? {
+ val containingClass = resolveClass(member.containingClass)
+ return if (member.isStatic)
+ containingClass?.staticScope
+ else
+ containingClass?.defaultType?.memberScope
+}
+
+private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElement(javaElement: JavaElement): T? {
+ return firstOrNull { member ->
+ val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement
+ when {
+ memberJavaElement == javaElement ->
+ true
+ memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> ->
+ memberJavaElement.psi.isEquivalentTo(javaElement.psi)
+ else ->
+ false
+ }
+ }
+}
+
+fun PsiElement.javaResolutionFacade() =
+ KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform)!!
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index aeaca8be..e18ab6cf 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -52,24 +52,26 @@ class DokkaBootstrapImpl : DokkaBootstrap {
includes,
moduleName,
DocumentationOptions(
- outputDir,
- format,
- includeNonPublic,
- includeRootPackage,
- reportUndocumented,
- skipEmptyPackages,
- skipDeprecated,
- jdkVersion,
- generateIndexPages,
- sourceLinks,
- impliedPlatforms,
- perPackageOptions,
- externalDocumentationLinks,
- noStdlibLink,
- languageVersion,
- apiVersion,
- cacheRoot,
- suppressedFiles.map { File(it) }.toSet()
+ outputDir = outputDir,
+ outputFormat = format,
+ includeNonPublic = includeNonPublic,
+ includeRootPackage = includeRootPackage,
+ reportUndocumented = reportUndocumented,
+ skipEmptyPackages = skipEmptyPackages,
+ skipDeprecated = skipDeprecated,
+ jdkVersion = jdkVersion,
+ generateIndexPages = generateIndexPages,
+ 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
)
)
}
diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt
index c4d97dbb..d78d4a0c 100644
--- a/core/src/main/kotlin/Formats/AnalysisComponents.kt
+++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt
@@ -2,9 +2,9 @@ package org.jetbrains.dokka.Formats
import com.google.inject.Binder
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Kotlin.KotlinAsJavaDescriptorSignatureProvider
-import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
+import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider
+import org.jetbrains.dokka.KotlinElementSignatureProvider
+import org.jetbrains.dokka.ElementSignatureProvider
import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.dokka.Utilities.bind
@@ -16,12 +16,12 @@ interface DefaultAnalysisComponentServices {
val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder>
val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder>
val sampleProcessingService: KClass<out SampleProcessingService>
- val descriptorSignatureProvider: KClass<out DescriptorSignatureProvider>
+ val elementSignatureProvider: KClass<out ElementSignatureProvider>
}
interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices {
override fun configureAnalysis(binder: Binder): Unit = with(binder) {
- bind<DescriptorSignatureProvider>() toType descriptorSignatureProvider
+ bind<ElementSignatureProvider>() toType elementSignatureProvider
bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass
bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass
bind<SampleProcessingService>() toType sampleProcessingService
@@ -33,7 +33,7 @@ object KotlinAsJava : DefaultAnalysisComponentServices {
override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class
override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinAsJavaDescriptorSignatureProvider::class
+ override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class
}
@@ -41,5 +41,5 @@ object KotlinAsKotlin : DefaultAnalysisComponentServices {
override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
new file mode 100644
index 00000000..09bb2602
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt
@@ -0,0 +1,117 @@
+package org.jetbrains.dokka.Formats
+
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
+import org.jetbrains.kotlin.types.KotlinType
+
+class JavaLayoutHtmlPackageListService: PackageListService {
+
+ private fun StringBuilder.appendParam(name: String, value: String) {
+ append(DOKKA_PARAM_PREFIX)
+ append(name)
+ append(":")
+ appendln(value)
+ }
+
+ override fun formatPackageList(module: DocumentationModule): String {
+ val packages = module.members(NodeKind.Package).map { it.name }
+
+ return buildString {
+ appendParam("format", "java-layout-html")
+ appendParam("mode", "kotlin")
+ for (p in packages) {
+ appendln(p)
+ }
+ }
+ }
+
+}
+
+class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ private fun getContainerPath(symbol: DeclarationDescriptor): String? {
+ return when (symbol) {
+ is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/"
+ is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html"
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor =
+ generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first()
+
+ private fun ClassifierDescriptor.nameWithOuter(): String =
+ generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor }
+ .toList().asReversed().joinToString(".") { it.name.asString() }
+
+ private fun getPagePath(symbol: DeclarationDescriptor): String? {
+ return when (symbol) {
+ is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html"
+ is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded()
+ is ClassifierDescriptor -> getContainerPath(symbol) + "#"
+ is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded()
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.signatureForAnchor(): String? {
+
+ fun ReceiverParameterDescriptor.extractReceiverName(): String {
+ var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!!
+ if (receiverClass.isCompanionObject()) {
+ receiverClass = receiverClass.containingDeclaration!!
+ } else if (receiverClass is TypeParameterDescriptor) {
+ val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor
+ if (upperBoundClass != null) {
+ receiverClass = upperBoundClass
+ }
+ }
+
+ return receiverClass.name.asString()
+ }
+
+ fun KotlinType.qualifiedNameForSignature(): String {
+ val desc = constructor.declarationDescriptor
+ return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>"
+ }
+
+ fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) {
+ if (desc.containingDeclaration.isCompanionObject()) {
+ append("Companion.")
+ }
+ desc.extensionReceiverParameter?.let {
+ append("(")
+ append(it.extractReceiverName())
+ append(").")
+ }
+ }
+
+ return when(this) {
+ is EnumEntrySyntheticClassDescriptor -> buildString {
+ append("ENUM_VALUE:")
+ append(name.asString())
+ }
+ is FunctionDescriptor -> buildString {
+ appendReceiverAndCompanion(this@signatureForAnchor)
+ append(name.asString())
+ valueParameters.joinTo(this, prefix = "(", postfix = ")") {
+ it.type.qualifiedNameForSignature()
+ }
+ }
+ is PropertyDescriptor -> buildString {
+ appendReceiverAndCompanion(this@signatureForAnchor)
+ append(name.asString())
+ append(":")
+ append(returnType?.qualifiedNameForSignature())
+ }
+ else -> null
+ }
+ }
+
+ private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded()
+
+ override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol)
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
index f73cd23e..885cdf6c 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
@@ -6,7 +6,6 @@ import kotlinx.html.li
import kotlinx.html.stream.appendHTML
import kotlinx.html.ul
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.Kotlin.KotlinDescriptorSignatureProvider
import org.jetbrains.dokka.Samples.DefaultSampleProcessingService
import org.jetbrains.dokka.Utilities.bind
import org.jetbrains.dokka.Utilities.toType
@@ -17,7 +16,7 @@ class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponen
override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class
override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class
override val sampleProcessingService = DefaultSampleProcessingService::class
- override val descriptorSignatureProvider = KotlinDescriptorSignatureProvider::class
+ override val elementSignatureProvider = KotlinElementSignatureProvider::class
override fun configureOutput(binder: Binder): Unit = with(binder) {
bind<Generator>() toType generatorServiceClass
diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt
index 3c31ba57..958e93af 100644
--- a/core/src/main/kotlin/Formats/OutlineService.kt
+++ b/core/src/main/kotlin/Formats/OutlineService.kt
@@ -16,7 +16,7 @@ interface OutlineFormatService {
for (node in nodes) {
appendOutlineHeader(location, node, to)
if (node.members.any()) {
- val sortedMembers = node.members.sortedBy { it.name }
+ val sortedMembers = node.members.sortedBy { it.name.toLowerCase() }
appendOutlineLevel(to) {
appendOutline(location, to, sortedMembers)
}
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 952e14cf..410de281 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -152,6 +152,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ is NodeRenderContent -> {
+ val node = content.node
+ appendContent(languageService.render(node, content.mode))
+ }
is ContentNodeLink -> {
val node = content.node
val linkTo = if (node != null) locationHref(location, node) else "#"
@@ -570,7 +574,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendHeader(3) { appendText(caption) }
- val children = if (sortMembers) members.sortedBy { it.name } else members
+ val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members
val membersMap = children.groupBy { link(node, it) }
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 09e5cedf..a5279772 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -10,13 +10,13 @@ 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.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
@@ -157,9 +157,13 @@ fun buildDocumentationModule(injector: Injector,
}
}
+ parseJavaPackageDocs(packageDocs, coreEnvironment)
+
with(injector.getInstance(DocumentationBuilder::class.java)) {
documentationModule.appendFragments(fragments, packageDocs.packageContent,
injector.getInstance(PackageDocumentationBuilder::class.java))
+
+ propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
}
val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
@@ -168,9 +172,21 @@ fun buildDocumentationModule(injector: Injector,
}
}
+fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) {
+ val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
+ ?.filterIsInstance<JavaSourceRoot>()
+ ?.map { it.file }
+ ?: listOf()
+ contentRoots.forEach { root ->
+ root.walkTopDown().filter { it.name == "overview.html" }.forEach {
+ packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", "."))
+ }
+ }
+}
+
fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> {
- val sourceRoots = configuration.get(JVMConfigurationKeys.CONTENT_ROOTS)
+ val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
?.filterIsInstance<JavaSourceRoot>()
?.map { it.file }
?: listOf()
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 34d4154e..eecf122e 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -11,9 +11,9 @@ data class SourceLinkDefinitionImpl(override val path: String,
companion object {
fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
val (path, urlAndLine) = srcLink.split('=')
- return SourceLinkDefinitionImpl(File(path).absolutePath,
+ return SourceLinkDefinitionImpl(File(path).canonicalPath,
urlAndLine.substringBefore("#"),
- urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
+ urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" })
}
}
}
@@ -36,27 +36,29 @@ data class PackageOptionsImpl(override val prefix: String,
override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
data class DokkaConfigurationImpl(
- override val moduleName: String,
- override val classpath: List<String>,
- override val sourceRoots: List<SourceRootImpl>,
- override val samples: List<String>,
- override val includes: List<String>,
- override val outputDir: String,
- override val format: String,
- override val includeNonPublic: Boolean,
- override val includeRootPackage: Boolean,
- override val reportUndocumented: Boolean,
- override val skipEmptyPackages: Boolean,
- override val skipDeprecated: Boolean,
- override val jdkVersion: Int,
- override val generateIndexPages: Boolean,
- override val sourceLinks: List<SourceLinkDefinitionImpl>,
- override val impliedPlatforms: List<String>,
- override val perPackageOptions: List<PackageOptionsImpl>,
- override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
- override val noStdlibLink: Boolean,
- override val cacheRoot: String?,
- override val suppressedFiles: List<String>,
- override val languageVersion: String?,
- override val apiVersion: String?
+ override val moduleName: String,
+ override val classpath: List<String>,
+ override val sourceRoots: List<SourceRootImpl>,
+ override val samples: List<String>,
+ override val includes: List<String>,
+ override val outputDir: String,
+ override val format: String,
+ override val includeNonPublic: Boolean,
+ override val includeRootPackage: Boolean,
+ override val reportUndocumented: Boolean,
+ override val skipEmptyPackages: Boolean,
+ override val skipDeprecated: Boolean,
+ override val jdkVersion: Int,
+ override val generateIndexPages: Boolean,
+ override val sourceLinks: List<SourceLinkDefinitionImpl>,
+ override val impliedPlatforms: List<String>,
+ override val perPackageOptions: List<PackageOptionsImpl>,
+ override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
+ override val noStdlibLink: Boolean,
+ override val noJdkLink: Boolean,
+ override val cacheRoot: String?,
+ override val suppressedFiles: List<String>,
+ override val languageVersion: String?,
+ override val apiVersion: String?,
+ override val collectInheritedExtensionsFromLibraries: Boolean
) : DokkaConfiguration \ No newline at end of file
diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
index cf2b0514..f1f170d7 100644
--- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -1,9 +1,12 @@
package org.jetbrains.dokka
import com.google.inject.Inject
+import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
@@ -14,14 +17,23 @@ 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(",") + ")"
+ methodSignature(element)
+ is PsiParameter -> {
+ val method = (element.parent.parent as PsiMethod)
+ methodSignature(method)
+ }
else -> null
}
+private fun methodSignature(method: PsiMethod): String {
+ return method.containingClass!!.qualifiedName + "$" + method.name + "(" +
+ method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")"
+}
+
private fun PsiType.typeSignature(): String = when(this) {
is PsiArrayType -> "Array((${componentType.typeSignature()}))"
is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize()
@@ -45,10 +57,16 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
private val refGraph: NodeReferenceGraph
private val docParser: JavaDocumentationParser
- @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) {
+ @Inject constructor(
+ options: DocumentationOptions,
+ refGraph: NodeReferenceGraph,
+ logger: DokkaLogger,
+ signatureProvider: ElementSignatureProvider,
+ externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+ ) {
this.options = options
this.refGraph = refGraph
- this.docParser = JavadocParser(refGraph, logger)
+ this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver)
}
constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) {
@@ -61,7 +79,7 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
if (skipFile(file) || file.classes.all { skipElement(it) }) {
return
}
- val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap(), refGraph)
+ val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph)
appendClasses(packageNode, file.classes)
}
@@ -92,9 +110,11 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun nodeForElement(element: PsiNamedElement,
kind: NodeKind,
- name: String = element.name ?: "<anonymous>"): DocumentationNode {
+ name: String = element.name ?: "<anonymous>",
+ register: Boolean = false): DocumentationNode {
val (docComment, deprecatedContent) = docParser.parseDocumentation(element)
val node = DocumentationNode(name, docComment, kind)
+ if (register) register(element, node)
if (element is PsiModifierListOwner) {
node.appendModifiers(element)
val modifierList = element.modifierList
@@ -139,11 +159,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
hasSuppressDocTag(element) ||
skipElementBySuppressedFiles(element)
- private fun skipElementByVisibility(element: Any): Boolean = element is PsiModifierListOwner &&
- !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) &&
- (element.hasModifierProperty(PsiModifier.PRIVATE) ||
- element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
- element.isInternal())
+ private fun skipElementByVisibility(element: Any): Boolean =
+ element is PsiModifierListOwner &&
+ element !is PsiParameter &&
+ !(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 && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles
@@ -161,13 +183,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun PsiClass.build(): DocumentationNode {
val kind = when {
+ isAnnotationType -> NodeKind.AnnotationClass
isInterface -> NodeKind.Interface
isEnum -> NodeKind.Enum
- isAnnotationType -> NodeKind.AnnotationClass
isException() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForElement(this, kind)
+ val node = nodeForElement(this, kind, register = isAnnotationType)
superTypes.filter { !ignoreSupertype(it) }.forEach {
node.appendType(it, NodeKind.Supertype)
val superClass = it.resolve()
@@ -200,11 +222,28 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
fun PsiField.build(): DocumentationNode {
val node = nodeForElement(this, nodeKind())
node.appendType(type)
- node.appendModifiers(this)
+
+ node.appendConstantValueIfAny(this)
register(this, node)
return node
}
+ private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) {
+ val modifierList = field.modifierList ?: return
+ val initializer = field.initializer ?: return
+ if (modifierList.hasExplicitModifier(PsiModifier.FINAL) &&
+ modifierList.hasExplicitModifier(PsiModifier.STATIC)) {
+ val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false)
+ val text = when(value) {
+ null -> return // No value found
+ is String ->
+ "\"" + StringUtil.escapeStringCharacters(value) + "\""
+ else -> value.toString()
+ }
+ append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail)
+ }
+ }
+
private fun PsiField.nodeKind(): NodeKind = when {
this is PsiEnumConstant -> NodeKind.EnumItem
else -> NodeKind.Field
@@ -274,8 +313,26 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder {
return node
}
+ private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode {
+ val existing = refGraph.lookup(getSignature(psiClass)!!)
+ if (existing != null) return existing
+ val new = psiClass.build()
+ val packageNode = findOrCreatePackageNode(null, (psiClass.parent as PsiJavaFile).packageName, emptyMap(), refGraph)
+ packageNode.append(new, RefKind.Member)
+ return new
+ }
+
fun PsiAnnotation.build(): DocumentationNode {
- val node = DocumentationNode(nameReferenceElement?.text ?: "<?>", Content.Empty, NodeKind.Annotation)
+
+ val original = when (this) {
+ is KtLightAbstractAnnotation -> clsDelegate
+ else -> this
+ }
+ val node = DocumentationNode(qualifiedName?.substringAfterLast(".") ?: "<?>", Content.Empty, NodeKind.Annotation)
+ val psiClass = original.nameReferenceElement?.resolve() as? PsiClass
+ if (psiClass != null && psiClass.isAnnotationType) {
+ node.append(lookupOrBuildClass(psiClass), RefKind.Link)
+ }
parameterList.attributes.forEach {
val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter)
val value = it.value
diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt
index c25f5813..70af73f9 100644
--- a/core/src/main/kotlin/Java/JavadocParser.kt
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -1,14 +1,16 @@
package org.jetbrains.dokka
import com.intellij.psi.*
-import com.intellij.psi.javadoc.PsiDocTag
-import com.intellij.psi.javadoc.PsiDocTagValue
-import com.intellij.psi.javadoc.PsiDocToken
-import com.intellij.psi.javadoc.PsiInlineDocTag
+import com.intellij.psi.impl.source.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.containers.isNullOrEmpty
+import org.jetbrains.kotlin.utils.keysToMap
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
+import java.net.URI
data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) {
companion object {
@@ -20,75 +22,163 @@ interface JavaDocumentationParser {
fun parseDocumentation(element: PsiNamedElement): JavadocParseResult
}
-class JavadocParser(private val refGraph: NodeReferenceGraph,
- private val logger: DokkaLogger) : JavaDocumentationParser {
+class JavadocParser(
+ private val refGraph: NodeReferenceGraph,
+ private val logger: DokkaLogger,
+ private val signatureProvider: ElementSignatureProvider,
+ private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+) : JavaDocumentationParser {
+
+ private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) {
+ append(LazyContentBlock {
+ val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList()
+ listOf(ContentBlock().apply {
+ append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY))
+ symbol(":")
+ text(" ")
+ })
+ })
+ }
+
override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
- val docComment = (element as? PsiDocCommentOwner)?.docComment
- if (docComment == null) return JavadocParseResult.Empty
+ val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return JavadocParseResult.Empty
val result = MutableContent()
var deprecatedContent: Content? = null
- val para = ContentParagraph()
- result.append(para)
- para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() })
+
+ val nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element)
+ val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph }
+ val firstParagraph = ContentParagraph()
+ if (firstParagraphContents.isNotEmpty()) {
+ firstParagraphContents.forEach { firstParagraph.append(it) }
+ result.append(firstParagraph)
+ }
+
+ result.appendAll(nodes.drop(firstParagraphContents.size))
+
+ if (element is PsiMethod) {
+ val tagsByName = element.searchInheritedTags()
+ for ((tagName, tags) in tagsByName) {
+ for ((tag, context) in tags) {
+ val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName())
+ val signature = signatureProvider.signature(element)
+ when (tagName) {
+ "param" -> {
+ section.appendTypeElement(signature) {
+ it.details
+ .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() }
+ ?.detailOrNull(NodeKind.Type)
+ }
+ }
+ "return" -> {
+ section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) }
+ }
+ }
+ section.appendAll(convertJavadocElements(tag.contentElements(), context))
+ }
+ }
+ }
+
docComment.tags.forEach { tag ->
- when(tag.name) {
+ when (tag.name) {
"see" -> result.convertSeeTag(tag)
"deprecated" -> {
- deprecatedContent = Content()
- deprecatedContent!!.convertJavadocElements(tag.contentElements())
+ deprecatedContent = Content().apply {
+ appendAll(convertJavadocElements(tag.contentElements(), element))
+ }
}
+ in tagsToInherit -> {}
else -> {
val subjectName = tag.getSubjectName()
val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName)
- section.convertJavadocElements(tag.contentElements())
+ section.appendAll(convertJavadocElements(tag.contentElements(), element))
}
}
}
return JavadocParseResult(result, deprecatedContent)
}
+ private val tagsToInherit = setOf("param", "return", "throws")
+
+ private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement)
+
+ private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> {
+
+ val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() }
+
+ fun recursiveSearch(methods: Array<PsiMethod>) {
+ for (method in methods) {
+ recursiveSearch(method.findSuperMethods())
+ }
+ for (method in methods) {
+ for (tag in method.docComment?.tags.orEmpty()) {
+ if (tag.name in tagsToInherit) {
+ output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method)
+ }
+ }
+ }
+ }
+
+ recursiveSearch(arrayOf(this))
+ return output.mapValues { it.value.values }
+ }
+
+
private fun PsiDocTag.contentElements(): Iterable<PsiElement> {
val tagValueElements = children
- .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME }
- .dropWhile { it is PsiWhiteSpace }
- .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
+ .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME }
+ .dropWhile { it is PsiWhiteSpace }
+ .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS }
return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements
}
- private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) {
+ private fun convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<ContentNode> {
+ val doc = Jsoup.parse(expandAllForElements(elements, element))
+ return doc.body().childNodes().mapNotNull {
+ convertHtmlNode(it)
+ }
+ }
+
+ private fun ContentBlock.appendAll(nodes: List<ContentNode>) {
+ nodes.forEach { append(it) }
+ }
+
+ private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String {
val htmlBuilder = StringBuilder()
elements.forEach {
if (it is PsiInlineDocTag) {
- htmlBuilder.append(convertInlineDocTag(it))
+ htmlBuilder.append(convertInlineDocTag(it, element))
} else {
htmlBuilder.append(it.text)
}
}
- val doc = Jsoup.parse(htmlBuilder.toString().trim())
- doc.body().childNodes().forEach {
- convertHtmlNode(it)
- }
+ return htmlBuilder.toString().trim()
}
- private fun ContentBlock.convertHtmlNode(node: Node) {
+ private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? {
if (node is TextNode) {
- append(ContentText(node.text()))
+ val text = if (insidePre) node.wholeText else node.text()
+ return ContentText(text)
} else if (node is Element) {
- val childBlock = createBlock(node)
+ val childBlock = createBlock(node, insidePre)
+
node.childNodes().forEach {
- childBlock.convertHtmlNode(it)
+ val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode)
+ if (child != null) {
+ childBlock.append(child)
+ }
}
- append(childBlock)
+ return childBlock
}
+ return null
}
- private fun createBlock(element: Element): ContentBlock = when(element.tagName()) {
+ private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) {
"p" -> ContentParagraph()
"b", "strong" -> ContentStrong()
"i", "em" -> ContentEmphasis()
"s", "del" -> ContentStrikethrough()
- "code" -> ContentCode()
+ "code" -> if (insidePre) ContentBlock() else ContentCode()
"pre" -> ContentBlockCode()
"ul" -> ContentUnorderedList()
"ol" -> ContentOrderedList()
@@ -99,45 +189,73 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
}
private fun createLink(element: Element): ContentBlock {
- val docref = element.attr("docref")
- if (docref != null) {
- return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)})
- }
- val href = element.attr("href")
- if (href != null) {
- return ContentExternalLink(href)
- } else {
- return ContentBlock()
+ return when {
+ element.hasAttr("docref") -> {
+ val docref = element.attr("docref")
+ ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) })
+ }
+ element.hasAttr("href") -> {
+ val href = element.attr("href")
+
+ val uri = try {
+ URI(href)
+ } catch (_: Exception) {
+ null
+ }
+
+ if (uri?.isAbsolute == false) {
+ ContentLocalLink(href)
+ } else {
+ ContentExternalLink(href)
+ }
+ }
+ element.hasAttr("name") -> {
+ ContentBookmark(element.attr("name"))
+ }
+ else -> ContentBlock()
}
}
private fun MutableContent.convertSeeTag(tag: PsiDocTag) {
- val linkElement = tag.linkElement()
- if (linkElement == null) {
- return
- }
+ val linkElement = tag.linkElement() ?: return
val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null)
- val linkSignature = resolveLink(linkElement)
+
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
val text = ContentText(linkElement.text)
- if (linkSignature != null) {
- val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)})
- linkNode.append(text)
- seeSection.append(linkNode)
- } else {
- seeSection.append(text)
+
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ val node = when {
+ externalLink != null -> {
+ val linkNode = ContentExternalLink(externalLink)
+ linkNode.append(text)
+ linkNode
+ }
+ linkSignature != null -> {
+ val linkNode =
+ ContentNodeLazyLink(
+ (tag.valueElement ?: linkElement).text,
+ { -> refGraph.lookupOrWarn(linkSignature, logger) }
+ )
+ linkNode.append(text)
+ linkNode
+ }
+ else -> text
}
+ seeSection.append(node)
}
- private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) {
+ private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) {
"link", "linkplain" -> {
- val valueElement = tag.linkElement()
- val linkSignature = resolveLink(valueElement)
- if (linkSignature != null) {
+ val valueElement = tag.referenceElement()
+ val externalLink = resolveExternalLink(valueElement)
+ val linkSignature by lazy { resolveInternalLink(valueElement) }
+ if (externalLink != null || linkSignature != null) {
val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text
- val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>"
+ val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\""
+ val link = "<a $linkTarget>${labelText.htmlEscape()}</a>"
if (tag.name == "link") "<code>$link</code>" else link
- }
- else if (valueElement != null) {
+ } else if (valueElement != null) {
valueElement.text
} else {
""
@@ -149,16 +267,45 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
val escaped = text.toString().trimStart().htmlEscape()
if (tag.name == "code") "<code>$escaped</code>" else escaped
}
+ "inheritDoc" -> {
+ val result = (element as? PsiMethod)?.let {
+ // @{inheritDoc} is only allowed on functions
+ val parent = tag.parent
+ when (parent) {
+ is PsiDocComment -> element.findSuperDocCommentOrWarn()
+ is PsiDocTag -> element.findSuperDocTagOrWarn(parent)
+ else -> null
+ }
+ }
+ result ?: tag.text
+ }
else -> tag.text
}
+ private fun PsiDocTag.referenceElement(): PsiElement? =
+ linkElement()?.let {
+ if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) {
+ PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java)
+ } else {
+ it
+ }
+ }
+
private fun PsiDocTag.linkElement(): PsiElement? =
- valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
+ valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
+
+ private fun resolveExternalLink(valueElement: PsiElement?): String? {
+ val target = valueElement?.reference?.resolve()
+ if (target != null) {
+ return externalDocumentationLinkResolver.buildExternalDocumentationLink(target)
+ }
+ return null
+ }
- private fun resolveLink(valueElement: PsiElement?): String? {
+ private fun resolveInternalLink(valueElement: PsiElement?): String? {
val target = valueElement?.reference?.resolve()
if (target != null) {
- return getSignature(target)
+ return signatureProvider.signature(target)
}
return null
}
@@ -169,4 +316,88 @@ class JavadocParser(private val refGraph: NodeReferenceGraph,
}
return null
}
+
+ private fun PsiMethod.findSuperDocCommentOrWarn(): String {
+ val method = findFirstSuperMethodWithDocumentation(this)
+ if (method != null) {
+ val descriptionElements = method.docComment?.descriptionElements?.dropWhile {
+ it.text.trim().isEmpty()
+ } ?: return ""
+
+ return expandAllForElements(descriptionElements, method)
+ }
+ logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}")
+ return ""
+ }
+
+
+ private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this)
+
+ if (result != null) {
+ val (method, tag) = result
+
+ val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() }
+
+ val expandedString = expandAllForElements(contentElements, method)
+
+ return expandedString
+ }
+ logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}")
+ return ""
+ }
+
+ private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? {
+ val superMethods = current.findSuperMethods()
+ for (method in superMethods) {
+ val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() }
+ if (!docs.isNullOrEmpty()) {
+ return method
+ }
+ }
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentation(method)
+ if (result != null) {
+ return result
+ }
+ }
+
+ return null
+ }
+
+ private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair<PsiMethod, PsiDocTag>? {
+ val superMethods = current.findSuperMethods()
+ val mappedFilteredTags = superMethods.map {
+ it to it.docComment?.tags?.filter { it.name == elementToExpand.name }
+ }
+
+ for ((method, tags) in mappedFilteredTags) {
+ tags ?: continue
+ for (tag in tags) {
+ val (tagSubject, elementSubject) = when (tag.name) {
+ "throws" -> {
+ // match class names only for throws, ignore possibly fully qualified path
+ // TODO: Always match exactly here
+ tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last()
+ }
+ else -> {
+ tag.getSubjectName() to elementToExpand.getSubjectName()
+ }
+ }
+
+ if (tagSubject == elementSubject) {
+ return method to tag
+ }
+ }
+ }
+
+ for (method in superMethods) {
+ val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method)
+ if (result != null) {
+ return result
+ }
+ }
+ return null
+ }
+
}
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
index ffef399d..d73bef4a 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -1,13 +1,10 @@
package org.jetbrains.dokka
import com.google.inject.Inject
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
class DeclarationLinkResolver
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
@@ -15,7 +12,7 @@ class DeclarationLinkResolver
val logger: DokkaLogger,
val options: DocumentationOptions,
val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver,
- val descriptorSignatureProvider: DescriptorSignatureProvider) {
+ val elementSignatureProvider: ElementSignatureProvider) {
fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? {
@@ -34,14 +31,14 @@ class DeclarationLinkResolver
if (externalHref != null) {
return ContentExternalLink(externalHref)
}
- val signature = descriptorSignatureProvider.signature(symbol)
+ val signature = elementSignatureProvider.signature(symbol)
val referencedAt = fromDescriptor.signatureWithSourceLocation()
return ContentNodeLazyLink(href, { ->
val target = refGraph.lookup(signature)
if (target == null) {
- logger.warn("Can't find node by signature $signature, referenced at $referencedAt")
+ logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt")
}
target
})
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
index 6e44df74..d1f98184 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -19,6 +19,8 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -33,12 +35,15 @@ class DescriptorDocumentationParser
val linkResolver: DeclarationLinkResolver,
val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
- val sampleService: SampleProcessingService)
+ val sampleService: SampleProcessingService,
+ val signatureProvider: KotlinElementSignatureProvider,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+)
{
- fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content =
- parseDocumentationAndDetails(descriptor, inline).first
+ fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content =
+ parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first
- fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
+ fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) {
return parseJavadoc(descriptor)
}
@@ -59,7 +64,10 @@ class DescriptorDocumentationParser
?.resolveToDescriptorIfAny()
?: descriptor
- var kdocText = kdoc.getContent()
+ var kdocText = if (isDefaultNoArgConstructor) {
+ getConstructorTagContent(descriptor) ?: kdoc.getContent()
+ } else kdoc.getContent()
+
// workaround for code fence parsing problem in IJ markdown parser
if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
kdocText += "\n"
@@ -87,6 +95,13 @@ class DescriptorDocumentationParser
return content to { node -> }
}
+ private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? {
+ return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag(
+ KDocKnownTag.CONSTRUCTOR
+ )?.getContent()
+ }
+
+
private fun DeclarationDescriptor.isSuppressWarning() : Boolean {
val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
return if (suppressAnnotation != null) {
@@ -129,7 +144,12 @@ class DescriptorDocumentationParser
fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> {
val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
if (psi is PsiDocCommentOwner) {
- val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement)
+ val parseResult = JavadocParser(
+ refGraph,
+ logger,
+ signatureProvider,
+ externalDocumentationLinkResolver
+ ).parseDocumentation(psi as PsiNamedElement)
return parseResult.content to { node ->
parseResult.deprecatedContent?.let {
val deprecationNode = DocumentationNode("", it, NodeKind.Modifier)
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 7b50fff5..38804e39 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -11,25 +11,29 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
-import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.*
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.supertypes
+import org.jetbrains.kotlin.util.supertypesWithAny
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
@@ -50,10 +54,12 @@ class DocumentationOptions(val outputDir: String,
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 suppressedFiles: Set<File> = emptySet(),
+ val collectInheritedExtensionsFromLibraries: Boolean = false) {
init {
if (perPackageOptions.any { it.prefix == "" })
throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
@@ -66,7 +72,10 @@ class DocumentationOptions(val outputDir: String,
fun effectivePackageOptions(pack: FqName): PackageOptions = effectivePackageOptions(pack.asString())
val defaultLinks = run {
- val links = mutableListOf(ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build())
+ val links = mutableListOf<ExternalDocumentationLink>()
+ if (!noJdkLink)
+ links += ExternalDocumentationLink.Builder("https://docs.oracle.com/javase/$jdkVersion/docs/api/").build()
+
if (!noStdlibLink)
links += ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build()
links
@@ -103,6 +112,10 @@ 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,
@@ -134,8 +147,20 @@ class DocumentationBuilder
refGraph.register(descriptor.signature(), node)
}
- fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
- val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+ fun <T> nodeForDescriptor(
+ descriptor: T,
+ kind: NodeKind,
+ external: Boolean = false
+ ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ val (doc, callback) =
+ if (external) {
+ Content.Empty to { node -> }
+ } else {
+ descriptorDocumentationParser.parseDocumentationAndDetails(
+ descriptor,
+ kind == NodeKind.Parameter
+ )
+ }
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
node.appendSignature(descriptor)
callback(node)
@@ -169,27 +194,20 @@ class DocumentationBuilder
appendTextNode(modifier, NodeKind.Modifier)
}
- fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType) {
+ fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) {
val unwrappedType = superType.unwrap()
if (unwrappedType is AbbreviatedType) {
- appendSupertype(descriptor, unwrappedType.abbreviation)
- } else if (!ignoreSupertype(unwrappedType)) {
+ appendSupertype(descriptor, unwrappedType.abbreviation, backref)
+ } else {
appendType(unwrappedType, NodeKind.Supertype)
val superclass = unwrappedType.constructor.declarationDescriptor
- link(superclass, descriptor, RefKind.Inheritor)
+ if (backref) {
+ link(superclass, descriptor, RefKind.Inheritor)
+ }
link(descriptor, superclass, RefKind.Superclass)
}
}
- private fun ignoreSupertype(superType: KotlinType): Boolean {
- val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
- if (superClass != null) {
- val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
- return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
- }
- return false
- }
-
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
if (projection.isStarProjection) {
appendTextNode("*", NodeKind.Type)
@@ -227,19 +245,39 @@ class DocumentationBuilder
if (prefix != "") {
node.appendTextNode(prefix, NodeKind.Modifier)
}
- if (kotlinType.isMarkedNullable) {
+ if (kotlinType.isNullabilityFlexible()) {
+ node.appendTextNode("!", NodeKind.NullabilityModifier)
+ } else if (kotlinType.isMarkedNullable) {
node.appendTextNode("?", NodeKind.NullabilityModifier)
}
if (classifierDescriptor != null) {
- val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ val externalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
if (externalLink != null) {
- node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ val targetNode =
+ refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+ node.append(targetNode, RefKind.ExternalType)
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
} else {
- link(node, classifierDescriptor,
- if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+ link(
+ node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+ )
+ }
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ node.append(
+ DocumentationNode(
+ classifierDescriptor.fqNameUnsafe.asString(),
+ Content.Empty,
+ NodeKind.QualifiedName
+ ), RefKind.Detail
+ )
}
}
+
append(node, RefKind.Detail)
node.appendAnnotations(kotlinType)
for (typeArgument in kotlinType.arguments) {
@@ -273,6 +311,17 @@ class DocumentationBuilder
}
}
+ fun DocumentationNode.appendExternalLink(externalLink: String) {
+ append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+
+ fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+ val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+ if (target != null) {
+ appendExternalLink(target)
+ }
+ }
+
fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
val kotlinVersion = annotation
.detail(NodeKind.Parameter)
@@ -284,7 +333,11 @@ class DocumentationBuilder
fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
- KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach {
+ KtTokens.MODIFIER_KEYWORDS_ARRAY.filter {
+ it !in knownModifiers
+ }.sortedBy {
+ MODIFIERS_ORDER.indexOf(it)
+ }.forEach {
if (psi.hasModifier(it)) {
appendTextNode(it.value, NodeKind.Modifier)
}
@@ -414,20 +467,49 @@ class DocumentationBuilder
if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue
logger.info(" package $packageName: ${declarations.count()} declarations")
- val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
+ val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode,
declarations, allFqNames)
}
- propagateExtensionFunctionsToSubclasses(fragments)
}
- private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
- val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
- val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
- val classHierarchy = buildClassHierarchy(allClasses)
+ fun propagateExtensionFunctionsToSubclasses(
+ fragments: Collection<PackageFragmentDescriptor>,
+ resolutionFacade: DokkaResolutionFacade
+ ) {
+
+ val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+ // Wide-collect all view descriptors
+ val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+ packages
+ .flatMap { pkg ->
+ moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+ }.map { fqName ->
+ moduleDescriptor.getPackage(fqName)
+ }.takeUnless { it.isEmpty() }
+ }.flatten()
+
+ val allDescriptors =
+ if (options.collectInheritedExtensionsFromLibraries) {
+ allPackageViewDescriptors.map { it.memberScope }
+ } else {
+ fragments.asSequence().map { it.getMemberScope() }
+ }.flatMap {
+ it.getDescriptorsFiltered(
+ DescriptorKindFilter.CALLABLES
+ ).asSequence()
+ }
+
- val allExtensionFunctions = allDescriptors
+ 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 }
@@ -435,15 +517,31 @@ class DocumentationBuilder
for (extensionFunction in allExtensionFunctions) {
if (extensionFunction.dispatchReceiverParameter != null) continue
val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
- ?.filter { fn -> fn.canShadow(extensionFunction) }
+ ?.filter { fn -> fn.canShadow(extensionFunction) }
?: emptyList()
if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
- val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
- val subclasses = classHierarchy[classDescriptor] ?: continue
- subclasses.forEach { subclass ->
+ val subclasses =
+ classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+ if (subclasses.isEmpty()) continue
+ subclasses.values.flatten().forEach { subclass ->
if (subclass.isExtensionApplicable(extensionFunction) &&
- possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+ possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+ val hasExternalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+ extensionFunction
+ ) != null
+ if (hasExternalLink) {
+ val containerDesc =
+ extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+ if (containerDesc != null) {
+ val container = refGraph.lookup(containerDesc.signature())
+ ?: containerDesc.buildExternal()
+ container.append(extensionFunction.buildExternal(), RefKind.Member)
+ }
+ }
+
refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
}
}
@@ -451,12 +549,9 @@ class DocumentationBuilder
}
private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
- val receiverType = extensionFunction.extensionReceiverParameter!!.type
- if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
- val receiverClass = receiverType.constructor.declarationDescriptor
- return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
- }
- return defaultType.isSubtypeOf(receiverType)
+ val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+ val classType = defaultType.toFuzzyType(declaredTypeParameters)
+ return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
}
private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -495,34 +590,60 @@ class DocumentationBuilder
}
fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
- is ClassDescriptor -> build()
+ is ClassifierDescriptor -> build()
is ConstructorDescriptor -> build()
is PropertyDescriptor -> build()
is FunctionDescriptor -> build()
- is TypeParameterDescriptor -> build()
is ValueParameterDescriptor -> build()
is ReceiverParameterDescriptor -> build()
- is TypeAliasDescriptor -> build()
else -> throw IllegalStateException("Descriptor $this is not known")
}
- fun TypeAliasDescriptor.build(): DocumentationNode {
+ fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+ val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+ is FunctionDescriptor -> build(true)
+ is PropertyDescriptor -> build(true)
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+
+ fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
+ is ClassDescriptor -> build(external)
+ is TypeAliasDescriptor -> build(external)
+ is TypeParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode {
val node = nodeForDescriptor(this, NodeKind.TypeAlias)
- node.appendAnnotations(this)
+ if (!external) {
+ node.appendAnnotations(this)
+ }
node.appendModifiers(this)
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
-
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
register(this, node)
return node
}
- fun ClassDescriptor.build(): DocumentationNode {
+ fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
val kind = when {
kind == ClassKind.OBJECT -> NodeKind.Object
kind == ClassKind.INTERFACE -> NodeKind.Interface
@@ -532,21 +653,25 @@ class DocumentationBuilder
isSubclassOfThrowable() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForDescriptor(this, kind)
- typeConstructor.supertypes.forEach {
- node.appendSupertype(this, it)
+ val node = nodeForDescriptor(this, kind, external)
+ register(this, node)
+ supertypesWithAnyPrecise().forEach {
+ node.appendSupertype(this, it, !external)
}
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
}
- for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
- node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ if (!external) {
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
}
- node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- register(this, node)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
return node
}
@@ -613,12 +738,12 @@ class DocumentationBuilder
return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
}
- fun FunctionDescriptor.build(): DocumentationNode {
+ fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
if (ErrorUtils.containsErrorType(this)) {
logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
}
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+ val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -626,8 +751,12 @@ class DocumentationBuilder
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ } else {
+ node.appendExternalLink(this)
+ }
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -648,32 +777,47 @@ class DocumentationBuilder
}
}
- fun PropertyDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+ fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+ val node = nodeForDescriptor(
+ this,
+ if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+ external
+ )
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- if (isVar) {
- node.appendTextNode("var", NodeKind.Modifier)
- }
- getter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ if (!external) {
+ node.appendSourceLink(source)
+ if (isVar) {
+ node.appendTextNode("var", NodeKind.Modifier)
}
- }
- setter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+ if (isConst) {
+ this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) }
+ }
+
+
+ getter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ }
+ }
+ setter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+ }
}
+ node.appendDefaultPlatforms(this)
+ }
+ if (external) {
+ node.appendExternalLink(this)
}
overriddenDescriptors.forEach {
addOverrideLink(it, this)
}
- node.appendDefaultPlatforms(this)
register(this, node)
return node
@@ -775,8 +919,8 @@ class DocumentationBuilder
return node
}
- fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value?.let { value ->
- when (value) {
+ fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value ->
+ val text = when (value) {
is String ->
"\"" + StringUtil.escapeStringCharacters(value) + "\""
is EnumEntrySyntheticClassDescriptor ->
@@ -789,19 +933,40 @@ class DocumentationBuilder
value.toString()
}
}
- else -> value.toString()
- }.let { valueString ->
- DocumentationNode(valueString, Content.Empty, NodeKind.Value)
+ else -> "$value"
}
+ DocumentationNode(text, 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
}
-}
-val visibleToDocumentation = setOf(Visibilities.PROTECTED, Visibilities.PUBLIC)
+}
fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean {
return (options.effectivePackageOptions(fqNameSafe).includeNonPublic
|| this !is MemberDescriptor
- || this.visibility in visibleToDocumentation)
+ || this.visibility.isPublicAPI)
&& !isDocumentationSuppressed(options)
&& (!options.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
}
@@ -833,16 +998,11 @@ class KotlinJavaDocumentationBuilder
val logger: DokkaLogger) : JavaDocumentationBuilder {
override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
val classDescriptors = file.classes.map {
- val javaDescriptorResolver = resolutionFacade.getFrontendService(JavaDescriptorResolver::class.java)
-
- javaDescriptorResolver.resolveClass(JavaClassImpl(it)) ?: run {
- logger.warn("Cannot find descriptor for Java class ${it.qualifiedName}")
- null
- }
+ it.getJavaClassDescriptor(resolutionFacade)
}
if (classDescriptors.any { it != null && it.isDocumented(options) }) {
- val packageNode = module.findOrCreatePackageNode(file.packageName, packageContent, documentationBuilder.refGraph)
+ val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph)
for (descriptor in classDescriptors.filterNotNull()) {
with(documentationBuilder) {
@@ -893,24 +1053,6 @@ fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any {
DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated"
} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated())
-fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
- externalClassNodes: MutableMap<FqName, DocumentationNode>,
- allFqNames: Collection<FqName>): DocumentationNode {
- if (descriptor is CallableMemberDescriptor) {
- val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
- if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) &&
- !ErrorUtils.isError(extensionClassDescriptor)) {
- val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
- return externalClassNodes.getOrPut(fqName, {
- val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass)
- append(newNode, RefKind.Member)
- newNode
- })
- }
- }
- return this
-}
-
fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
val extensionReceiver = extensionReceiverParameter
if (extensionReceiver != null) {
@@ -1004,7 +1146,7 @@ fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
fun DocumentationNode.generateAllTypesNode() {
val allTypes = members(NodeKind.Package)
.flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } }
- .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name }
+ .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() }
val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
for (typeNode in allTypes) {
@@ -1013,3 +1155,10 @@ fun DocumentationNode.generateAllTypesNode() {
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
index e19ecf76..a8129793 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -2,14 +2,16 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
+import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.util.io.*
+import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor
+import org.jetbrains.dokka.Formats.FormatDescriptor
+import org.jetbrains.dokka.Utilities.ServiceLocator
+import org.jetbrains.dokka.Utilities.lookup
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -21,12 +23,15 @@ 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
) {
@@ -129,13 +134,22 @@ class ExternalDocumentationLinkResolver @Inject constructor(
.map { (key, value) -> key to value }
.toMap()
- val resolver = if (format == "javadoc") {
- InboundExternalLinkResolutionService.Javadoc()
- } else {
- val linkExtension = paramsMap["linkExtension"]?.singleOrNull() ?:
- throw RuntimeException("Failed to parse package list from $packageListUrl")
- InboundExternalLinkResolutionService.Dokka(linkExtension)
- }
+
+ val defaultResolverDesc = 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)
@@ -152,11 +166,17 @@ class ExternalDocumentationLinkResolver @Inject constructor(
}
}
+ fun buildExternalDocumentationLink(element: PsiElement): String? {
+ return element.extractDescriptor(libraryResolutionFacade)?.let {
+ buildExternalDocumentationLink(it)
+ }
+ }
+
fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
val packageFqName: FqName =
when (symbol) {
- is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
is PackageFragmentDescriptor -> symbol.fqName
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
else -> return null
}
@@ -170,6 +190,15 @@ class ExternalDocumentationLinkResolver @Inject constructor(
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()
}
}
@@ -177,7 +206,7 @@ class ExternalDocumentationLinkResolver @Inject constructor(
interface InboundExternalLinkResolutionService {
fun getPath(symbol: DeclarationDescriptor): String?
- class Javadoc : InboundExternalLinkResolutionService {
+ class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
override fun getPath(symbol: DeclarationDescriptor): String? {
if (symbol is EnumEntrySyntheticClassDescriptor) {
return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() }
@@ -187,7 +216,7 @@ interface InboundExternalLinkResolutionService {
val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
val containingClassLink = getPath(containingClass)
if (containingClassLink != null) {
- if (symbol is JavaMethodDescriptor) {
+ if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) {
val psi = symbol.sourcePsi() as? PsiMethod
if (psi != null) {
val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
@@ -203,7 +232,9 @@ interface InboundExternalLinkResolutionService {
}
}
- class Dokka(val extension: String) : InboundExternalLinkResolutionService {
+ class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver")
+
override fun getPath(symbol: DeclarationDescriptor): String? {
val leafElement = when (symbol) {
is CallableDescriptor, is TypeAliasDescriptor -> true
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
deleted file mode 100644
index a3be658e..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.getSignature
-import org.jetbrains.dokka.sourcePsi
-import org.jetbrains.kotlin.asJava.toLightElements
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.psi.KtElement
-
-class KotlinAsJavaDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String {
- val sourcePsi = forDesc.sourcePsi()
- val javaLikePsi = if (sourcePsi is KtElement) {
- sourcePsi.toLightElements().firstOrNull()
- } else {
- sourcePsi
- }
-
- return getSignature(javaLikePsi) ?:
- "not implemented for $forDesc with psi: $sourcePsi"
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
index c7ed8292..00a795d0 100644
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -6,9 +6,11 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.PsiNamedElement
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPropertyAccessor
@@ -59,8 +61,9 @@ class KotlinAsJavaDocumentationParser
return JavadocParseResult.Empty
}
}
+ val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass
val descriptor = resolutionFacade.resolveToDescriptor(origin)
- val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter)
+ val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor)
return JavadocParseResult(content, null)
}
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
new file mode 100644
index 00000000..20ea179e
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.asJava.toLightElements
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.psi.KtElement
+
+class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider {
+
+ private fun PsiElement.javaLikePsi() = when {
+ this is KtElement -> toLightElements().firstOrNull()
+ else -> this
+ }
+
+ override fun signature(forPsi: PsiElement): String {
+ return getSignature(forPsi.javaLikePsi()) ?:
+ "not implemented for $forPsi"
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String {
+ val sourcePsi = forDesc.sourcePsi()
+ return getSignature(sourcePsi?.javaLikePsi()) ?:
+ "not implemented for $forDesc with psi: $sourcePsi"
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
deleted file mode 100644
index 7ecd0389..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.signature
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-class KotlinDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
new file mode 100644
index 00000000..bcac0182
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMember
+import com.intellij.psi.PsiPackage
+import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.BindingContext
+import javax.inject.Inject
+
+class KotlinElementSignatureProvider @Inject constructor(
+ val resolutionFacade: DokkaResolutionFacade
+) : ElementSignatureProvider {
+ override fun signature(forPsi: PsiElement): String {
+ return forPsi.extractDescriptor(resolutionFacade)
+ ?.let { signature(it) }
+ ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" }
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
+}
+
+
+fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? {
+ val forPsi = this
+
+ return when (forPsi) {
+ is KtLightElement<*, *> -> return (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade)
+ is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName))
+ is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade)
+ else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi]
+ }
+}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index f33c8c96..5f43c22e 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -5,8 +5,13 @@ import org.jetbrains.dokka.LanguageService.RenderMode
/**
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
-class KotlinLanguageService : LanguageService {
- private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
+class KotlinLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return node.name !in fullOnlyModifiers
+ }
+
+ private val fullOnlyModifiers =
+ setOf("public", "protected", "private", "internal", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -17,11 +22,12 @@ class KotlinLanguageService : LanguageService {
NodeKind.EnumItem,
NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
+ NodeKind.Parameter -> renderParameter(node, renderMode)
NodeKind.TypeParameter -> renderTypeParameter(node, renderMode)
NodeKind.Type,
NodeKind.UpperBound -> renderType(node, renderMode)
- NodeKind.Modifier -> renderModifier(node)
+ NodeKind.Modifier -> renderModifier(this, node, renderMode)
NodeKind.Constructor,
NodeKind.Function,
NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
@@ -32,12 +38,6 @@ class KotlinLanguageService : LanguageService {
}
}
- override fun renderName(node: DocumentationNode): String {
- return when (node.kind) {
- NodeKind.Constructor -> node.owner!!.name
- else -> node.name
- }
- }
override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
if (nodes.size < 2) return null
@@ -46,10 +46,17 @@ class KotlinLanguageService : LanguageService {
return content {
val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first()
if (functionWithTypeParameter.kind == NodeKind.Function) {
- renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
- }
- else {
- renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ renderFunction(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
+ } else {
+ renderProperty(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
}
}
}
@@ -70,26 +77,27 @@ class KotlinLanguageService : LanguageService {
companion object {
private val arrayClasses = setOf(
- "kotlin.Array",
- "kotlin.BooleanArray",
- "kotlin.ByteArray",
- "kotlin.CharArray",
- "kotlin.ShortArray",
- "kotlin.IntArray",
- "kotlin.LongArray",
- "kotlin.FloatArray",
- "kotlin.DoubleArray"
+ "kotlin.Array",
+ "kotlin.BooleanArray",
+ "kotlin.ByteArray",
+ "kotlin.CharArray",
+ "kotlin.ShortArray",
+ "kotlin.IntArray",
+ "kotlin.LongArray",
+ "kotlin.FloatArray",
+ "kotlin.DoubleArray"
)
private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
private val iterableClasses = setOf(
- "kotlin.Collection",
- "kotlin.Sequence",
- "kotlin.Iterable",
- "kotlin.Map",
- "kotlin.String",
- "kotlin.CharSequence") + arrayOrListClasses
+ "kotlin.Collection",
+ "kotlin.Sequence",
+ "kotlin.Iterable",
+ "kotlin.Map",
+ "kotlin.String",
+ "kotlin.CharSequence"
+ ) + arrayOrListClasses
}
private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
@@ -102,53 +110,21 @@ class KotlinLanguageService : LanguageService {
fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
}
- private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper {
override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
to.text("<$typeParameterName>")
}
}
- private fun ContentBlock.renderPackage(node: DocumentationNode) {
- keyword("package")
- text(" ")
- identifier(node.name)
- }
-
- private fun <T> ContentBlock.renderList(nodes: List<T>, separator: String = ", ",
- noWrap: Boolean = false, renderItem: (T) -> Unit) {
- if (nodes.none())
- return
- renderItem(nodes.first())
- nodes.drop(1).forEach {
- if (noWrap) {
- symbol(separator.removeSuffix(" "))
- nbsp()
- } else {
- symbol(separator)
- }
- renderItem(it)
- }
- }
-
- private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode)->Unit) {
- val to = node.links.firstOrNull()
- if (to == null)
- body(node)
- else
- link(to) {
- body(node)
- }
- }
-
private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) {
node.references(RefKind.HiddenAnnotation).map { it.to }
- .find { it.name == "ParameterName" }?.let {
- val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
- identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
- symbol(":")
- nbsp()
- }
+ .find { it.name == "ParameterName" }?.let {
+ val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
+ identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
+ symbol(":")
+ nbsp()
+ }
}
private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
@@ -190,15 +166,27 @@ class KotlinLanguageService : LanguageService {
keyword("dynamic")
return
}
+
+ val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier)
+
if (node.isFunctionalType()) {
- renderFunctionalType(node, renderMode)
+ if (nullabilityModifier != null) {
+ symbol("(")
+ renderFunctionalType(node, renderMode)
+ symbol(")")
+ symbol(nullabilityModifier.name)
+ } else {
+ renderFunctionalType(node, renderMode)
+ }
return
}
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
renderModifiersForNode(node, renderMode, true)
- renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
+ renderLinked(this, node) {
+ identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
+ }
val typeArguments = node.details(NodeKind.Type)
if (typeArguments.isNotEmpty()) {
symbol("<")
@@ -207,22 +195,24 @@ class KotlinLanguageService : LanguageService {
}
symbol(">")
}
- val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull()
- if (nullabilityModifier != null) {
+
+ nullabilityModifier ?.apply {
symbol(nullabilityModifier.name)
}
}
- private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
+ override fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean
+ ) {
when (node.name) {
- "final", "public", "var" -> {}
+ "final", "public", "var" -> {
+ }
else -> {
- keyword(node.name)
- if (nowrap) {
- nbsp()
- }
- else {
- text(" ")
+ if (showModifierInSummary(node) || renderMode == RenderMode.FULL) {
+ super.renderModifier(block, node, renderMode, nowrap)
}
}
}
@@ -238,11 +228,12 @@ class KotlinLanguageService : LanguageService {
nbsp()
symbol(":")
nbsp()
- renderList(constraints, noWrap=true) {
+ renderList(constraints, noWrap = true) {
renderType(it, renderMode)
}
}
}
+
private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
@@ -274,9 +265,12 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
- val parametersWithMultipleConstraints = node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
+ val parametersWithMultipleConstraints =
+ node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
val parametersWithConstraints = parametersWithMultipleConstraints
- .flatMap { parameter -> parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } }
+ .flatMap { parameter ->
+ parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint }
+ }
if (parametersWithMultipleConstraints.isNotEmpty()) {
keyword(" where ")
renderList(parametersWithConstraints) {
@@ -290,7 +284,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
- val supertypes = node.details(NodeKind.Supertype)
+ val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
if (supertypes.any()) {
nbsp()
symbol(":")
@@ -302,20 +296,6 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifiersForNode(node: DocumentationNode,
- renderMode: RenderMode,
- nowrap: Boolean = false) {
- val modifiers = node.details(NodeKind.Modifier)
- for (it in modifiers) {
- if (node.kind == org.jetbrains.dokka.NodeKind.Interface && it.name == "abstract")
- continue
- if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) {
- continue
- }
- renderModifier(it, nowrap)
- }
- }
-
private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
node.annotations.forEach {
renderAnnotation(it)
@@ -365,9 +345,11 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderFunction(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderFunction(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -385,7 +367,7 @@ class KotlinLanguageService : LanguageService {
renderReceiver(node, renderMode, signatureMapper)
- if (node.kind != org.jetbrains.dokka.NodeKind.Constructor)
+ if (node.kind != NodeKind.Constructor)
identifierOrDeprecated(node)
symbol("(")
@@ -401,14 +383,17 @@ class KotlinLanguageService : LanguageService {
symbol(")")
symbol(": ")
renderType(node.detail(NodeKind.Type), renderMode)
- }
- else {
+ } else {
symbol(")")
}
renderExtraTypeParameterConstraints(node, renderMode)
}
- private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) {
+ private fun ContentBlock.renderReceiver(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper?
+ ) {
val receiver = node.details(NodeKind.Receiver).singleOrNull()
if (receiver != null) {
if (signatureMapper != null) {
@@ -428,17 +413,19 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun needReturnType(node: DocumentationNode) = when(node.kind) {
+ private fun needReturnType(node: DocumentationNode) = when (node.kind) {
NodeKind.Constructor -> false
else -> !node.isUnitReturnType()
}
fun DocumentationNode.isUnitReturnType(): Boolean =
- detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
+ detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
- private fun ContentBlock.renderProperty(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderProperty(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -462,7 +449,7 @@ class KotlinLanguageService : LanguageService {
}
fun DocumentationNode.getPropertyKeyword() =
- if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
+ if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
if (node.deprecation != null) {
@@ -475,4 +462,12 @@ class KotlinLanguageService : LanguageService {
}
}
-fun DocumentationNode.qualifiedNameFromType() = (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() ?: name
+fun DocumentationNode.qualifiedNameFromType(): String {
+ return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
+ ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName()
+ ?: name
+}
+
+
+val DocumentationNode.typeDeclarationClass
+ get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)
diff --git a/core/src/main/kotlin/Languages/CommonLanguageService.kt b/core/src/main/kotlin/Languages/CommonLanguageService.kt
new file mode 100644
index 00000000..ddc95d32
--- /dev/null
+++ b/core/src/main/kotlin/Languages/CommonLanguageService.kt
@@ -0,0 +1,84 @@
+package org.jetbrains.dokka
+
+
+abstract class CommonLanguageService : LanguageService {
+
+ protected fun ContentBlock.renderPackage(node: DocumentationNode) {
+ keyword("package")
+ nbsp()
+ identifier(node.name)
+ }
+
+ override fun renderName(node: DocumentationNode): String {
+ return when (node.kind) {
+ NodeKind.Constructor -> node.owner!!.name
+ else -> node.name
+ }
+ }
+
+ open fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: LanguageService.RenderMode,
+ nowrap: Boolean = false
+ ) = with(block) {
+ keyword(node.name)
+ if (nowrap) {
+ nbsp()
+ } else {
+ text(" ")
+ }
+ }
+
+ protected fun renderLinked(
+ block: ContentBlock,
+ node: DocumentationNode,
+ body: ContentBlock.(DocumentationNode) -> Unit
+ ) = with(block) {
+ val to = node.links.firstOrNull()
+ if (to == null)
+ body(node)
+ else
+ link(to) {
+ this.body(node)
+ }
+ }
+
+ protected fun <T> ContentBlock.renderList(
+ nodes: List<T>, separator: String = ", ",
+ noWrap: Boolean = false, renderItem: (T) -> Unit
+ ) {
+ if (nodes.none())
+ return
+ renderItem(nodes.first())
+ nodes.drop(1).forEach {
+ if (noWrap) {
+ symbol(separator.removeSuffix(" "))
+ nbsp()
+ } else {
+ symbol(separator)
+ }
+ renderItem(it)
+ }
+ }
+
+ abstract fun showModifierInSummary(node: DocumentationNode): Boolean
+
+ protected fun ContentBlock.renderModifiersForNode(
+ node: DocumentationNode,
+ renderMode: LanguageService.RenderMode,
+ nowrap: Boolean = false
+ ) {
+ val modifiers = node.details(NodeKind.Modifier)
+ for (it in modifiers) {
+ if (node.kind == NodeKind.Interface && it.name == "abstract")
+ continue
+ if (renderMode == LanguageService.RenderMode.SUMMARY && !showModifierInSummary(it)) {
+ continue
+ }
+ renderModifier(this, it, renderMode, nowrap)
+ }
+ }
+
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
new file mode 100644
index 00000000..992cd090
--- /dev/null
+++ b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
@@ -0,0 +1,197 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.LanguageService.RenderMode
+
+/**
+ * Implements [LanguageService] and provides rendering of symbols in Java language
+ */
+class NewJavaLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return true
+ }
+
+ override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
+ return content {
+ (when (node.kind) {
+ NodeKind.Package -> renderPackage(node)
+ in NodeKind.classLike -> renderClass(node, renderMode)
+
+ NodeKind.Modifier -> renderModifier(this, node, renderMode)
+ NodeKind.TypeParameter -> renderTypeParameter(node)
+ NodeKind.Type,
+ NodeKind.UpperBound -> renderType(node)
+ NodeKind.Parameter -> renderParameter(node)
+ NodeKind.Constructor,
+ NodeKind.Function -> renderFunction(node)
+ NodeKind.Property -> renderProperty(node)
+ else -> "${node.kind}: ${node.name}"
+ })
+ }
+ }
+
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
+
+
+ override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) {
+ when (node.name) {
+ "open", "internal" -> {
+ }
+ else -> super.renderModifier(block, node, renderMode, nowrap)
+ }
+ }
+
+ fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et }
+ ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type)
+
+ else -> null
+ }
+
+ fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) {
+ "kotlin.Array" ->
+ 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0)
+
+ "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray",
+ "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" ->
+ 1
+ else -> 0
+ }
+
+ fun ContentBlock.renderType(node: DocumentationNode) {
+ when (node.name) {
+ "Unit" -> identifier("void")
+ "Int" -> identifier("int")
+ "Long" -> identifier("long")
+ "Double" -> identifier("double")
+ "Float" -> identifier("float")
+ "Char" -> identifier("char")
+ "Boolean" -> identifier("bool")
+ // TODO: render arrays
+ else -> renderLinked(this, node) {
+ identifier(node.name)
+ }
+ }
+ }
+
+ private fun ContentBlock.renderTypeParameter(node: DocumentationNode) {
+ val constraints = node.details(NodeKind.UpperBound)
+ if (constraints.none())
+ identifier(node.name)
+ else {
+ identifier(node.name)
+ text(" ")
+ keyword("extends")
+ text(" ")
+ constraints.forEach { renderType(node) }
+ }
+ }
+
+ private fun ContentBlock.renderParameter(node: DocumentationNode) {
+ renderType(node.detail(NodeKind.Type))
+ text(" ")
+ identifier(node.name)
+ }
+
+ private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) {
+ val typeParameters = node.details(NodeKind.TypeParameter)
+ if (typeParameters.any()) {
+ symbol("<")
+ renderList(typeParameters, noWrap = true) {
+ renderTypeParameter(it)
+ }
+ symbol(">")
+ text(" ")
+ }
+ }
+
+// private fun renderModifiersForNode(node: DocumentationNode): String {
+// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" }
+// if (modifiers.none())
+// return ""
+// return modifiers.joinToString(" ", postfix = " ")
+// }
+
+ private fun ContentBlock.renderClassKind(node: DocumentationNode) {
+ when (node.kind) {
+ NodeKind.Interface -> {
+ keyword("interface")
+ }
+ NodeKind.EnumItem -> {
+ keyword("enum value")
+ }
+ NodeKind.Enum -> {
+ keyword("enum")
+ }
+ NodeKind.Class, NodeKind.Exception, NodeKind.Object -> {
+ keyword("class")
+ }
+ else -> throw IllegalArgumentException("Node $node is not a class-like object")
+ }
+ text(" ")
+ }
+
+ private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode)
+ renderClassKind(node)
+
+ identifier(node.name)
+ renderTypeParametersForNode(node)
+ }
+
+ private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) {
+ renderList(nodes) {
+ renderParameter(it)
+ }
+ }
+
+ private fun ContentBlock.renderFunction(node: DocumentationNode) {
+ when (node.kind) {
+ NodeKind.Constructor -> identifier(node.owner?.name ?: "")
+ NodeKind.Function -> {
+ renderTypeParametersForNode(node)
+ renderType(node.detail(NodeKind.Type))
+ text(" ")
+ identifier(node.name)
+
+ }
+ else -> throw IllegalArgumentException("Node $node is not a function-like object")
+ }
+
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ symbol("(")
+ if (receiver != null)
+ renderParameters(listOf(receiver) + node.details(NodeKind.Parameter))
+ else
+ renderParameters(node.details(NodeKind.Parameter))
+
+ symbol(")")
+ }
+
+ private fun ContentBlock.renderProperty(node: DocumentationNode) {
+
+ when (node.kind) {
+ NodeKind.Property -> {
+ keyword("val")
+ text(" ")
+ }
+ else -> throw IllegalArgumentException("Node $node is not a property")
+ }
+ renderTypeParametersForNode(node)
+ val receiver = node.details(NodeKind.Receiver).singleOrNull()
+ if (receiver != null) {
+ renderType(receiver.detail(NodeKind.Type))
+ symbol(".")
+ }
+
+ identifier(node.name)
+ symbol(":")
+ text(" ")
+ renderType(node.detail(NodeKind.Type))
+
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt
index 1f5bbc83..c142f4a4 100644
--- a/core/src/main/kotlin/Model/Content.kt
+++ b/core/src/main/kotlin/Model/Content.kt
@@ -9,7 +9,7 @@ object ContentEmpty : ContentNode {
}
open class ContentBlock() : ContentNode {
- val children = arrayListOf<ContentNode>()
+ open val children = arrayListOf<ContentNode>()
fun append(node: ContentNode) {
children.add(node)
@@ -27,6 +27,34 @@ open class ContentBlock() : ContentNode {
get() = children.sumBy { it.textLength }
}
+class NodeRenderContent(
+ val node: DocumentationNode,
+ val mode: LanguageService.RenderMode
+): ContentNode {
+ override val textLength: Int
+ get() = 0 //TODO: Clarify?
+}
+
+class LazyContentBlock(private val fillChildren: () -> List<ContentNode>) : ContentBlock() {
+ private var computed = false
+ override val children: ArrayList<ContentNode>
+ get() {
+ if (!computed) {
+ computed = true
+ children.addAll(fillChildren())
+ }
+ return super.children
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return super.hashCode() + 31 * fillChildren.hashCode()
+ }
+}
+
enum class IdentifierKind {
TypeName,
ParameterName,
@@ -87,6 +115,7 @@ class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: Cont
abstract class ContentNodeLink() : ContentBlock() {
abstract val node: DocumentationNode?
+ abstract val text: String?
}
object ContentHardLineBreak : ContentNode {
@@ -100,6 +129,8 @@ class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLi
override fun hashCode(): Int =
children.hashCode() * 31 + node.name.hashCode()
+
+ override val text: String? = null
}
class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() {
@@ -110,6 +141,8 @@ class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> Documentatio
override fun hashCode(): Int =
children.hashCode() * 31 + linkText.hashCode()
+
+ override val text: String? = linkText
}
class ContentExternalLink(val href : String) : ContentBlock() {
@@ -120,6 +153,9 @@ class ContentExternalLink(val href : String) : ContentBlock() {
children.hashCode() * 31 + href.hashCode()
}
+data class ContentBookmark(val name: String): ContentBlock()
+data class ContentLocalLink(val href: String) : ContentBlock()
+
class ContentUnorderedList() : ContentBlock()
class ContentOrderedList() : ContentBlock()
class ContentListItem() : ContentBlock()
diff --git a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt b/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
deleted file mode 100644
index 85584e3c..00000000
--- a/core/src/main/kotlin/Model/DescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.jetbrains.dokka.Model
-
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-interface DescriptorSignatureProvider {
- fun signature(forDesc: DeclarationDescriptor): String
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index da85cabf..ad7801f2 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -48,6 +48,7 @@ enum class NodeKind {
Signature,
ExternalLink,
+ QualifiedName,
Platform,
AllTypes,
@@ -62,7 +63,7 @@ enum class NodeKind {
companion object {
val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias)
- val memberLike = setOf(Function, Property, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
+ val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem)
}
}
@@ -85,6 +86,8 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Member).map { it.to }
val inheritedMembers: List<DocumentationNode>
get() = references(RefKind.InheritedMember).map { it.to }
+ val allInheritedMembers: List<DocumentationNode>
+ get() = recursiveInheritedMembers()
val inheritedCompanionObjectMembers: List<DocumentationNode>
get() = references(RefKind.InheritedCompanionObjectMember).map { it.to }
val extensions: List<DocumentationNode>
@@ -103,6 +106,28 @@ open class DocumentationNode(val name: String,
get() = references(RefKind.Deprecation).singleOrNull()?.to
val platforms: List<String>
get() = references(RefKind.Platform).map { it.to.name }
+ val externalType: DocumentationNode?
+ get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
+
+ val supertypes: List<DocumentationNode>
+ get() = details(NodeKind.Supertype)
+
+ val superclassType: DocumentationNode?
+ get() = when (kind) {
+ NodeKind.Supertype -> {
+ (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType
+ }
+ NodeKind.Interface -> null
+ in NodeKind.classLike -> supertypes.firstOrNull {
+ (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) }
+ }
+ else -> null
+ }
+
+ val superclassTypeSequence: Sequence<DocumentationNode>
+ get() = generateSequence(superclassType) {
+ it.superclassType
+ }
// TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
@@ -123,7 +148,6 @@ open class DocumentationNode(val name: String,
}
(content as MutableContent).body()
}
-
fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind }
fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind }
fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind }
@@ -154,17 +178,21 @@ val DocumentationNode.path: List<DocumentationNode>
return parent.path + this
}
-fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
- val existingNode = members(NodeKind.Package).firstOrNull { it.name == packageName }
- if (existingNode != null) {
- return existingNode
- }
- val newNode = DocumentationNode(packageName,
+fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode {
+ val node = refGraph.lookup(packageName) ?: run {
+ val newNode = DocumentationNode(
+ packageName,
packageContent.getOrElse(packageName) { Content.Empty },
- NodeKind.Package)
- append(newNode, RefKind.Member)
- refGraph.register(packageName, newNode)
- return newNode
+ NodeKind.Package
+ )
+
+ refGraph.register(packageName, newNode)
+ newNode
+ }
+ if (module != null && node !in module.members) {
+ module.append(node, RefKind.Member)
+ }
+ return node
}
fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
@@ -173,7 +201,8 @@ fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) {
RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Member -> child.addReferenceTo(this, RefKind.Owner)
RefKind.Owner -> child.addReferenceTo(this, RefKind.Member)
- else -> { /* Do not add any links back for other types */ }
+ else -> { /* Do not add any links back for other types */
+ }
}
}
@@ -186,8 +215,37 @@ fun DocumentationNode.appendTextNode(text: String,
fun DocumentationNode.qualifiedName(): String {
if (kind == NodeKind.Type) {
return qualifiedNameFromType()
+ } else if (kind == NodeKind.Package) {
+ return name
}
- return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
+ return path.dropWhile { it.kind == NodeKind.Module }.map { it.name }.filter { it.isNotEmpty() }.joinToString(".")
}
fun DocumentationNode.simpleName() = name.substringAfterLast('.')
+
+private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> {
+ val allInheritedMembers = mutableListOf<DocumentationNode>()
+ recursiveInheritedMembers(allInheritedMembers)
+ return allInheritedMembers
+}
+
+private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) {
+ allInheritedMembers.addAll(inheritedMembers)
+ System.out.println(allInheritedMembers.size)
+ inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) ->
+ node.recursiveInheritedMembers(allInheritedMembers)
+ }
+}
+
+private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean {
+ return when(node.kind) {
+ NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class
+ NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception
+ else -> false
+ }
+}
+
+fun DocumentationNode.classNodeNameWithOuterClass(): String {
+ assert(kind in NodeKind.classLike)
+ return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index a968f400..89ec1b3e 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,7 +18,8 @@ enum class RefKind {
HiddenAnnotation,
Deprecation,
TopLevelPage,
- Platform
+ Platform,
+ ExternalType
}
data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
@@ -61,7 +62,7 @@ class NodeReferenceGraph() {
fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? {
val result = nodeMap[signature]
if (result == null) {
- logger.warn("Can't find node by signature $signature")
+ logger.warn("Can't find node by signature `$signature`")
}
return result
}
diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
new file mode 100644
index 00000000..e8fdde6e
--- /dev/null
+++ b/core/src/main/kotlin/Model/ElementSignatureProvider.kt
@@ -0,0 +1,9 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+
+interface ElementSignatureProvider {
+ fun signature(forDesc: DeclarationDescriptor): String
+ fun signature(forPsi: PsiElement): String
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt
index 1f6bdcb9..9804f68b 100644
--- a/core/src/main/kotlin/Model/PackageDocs.kt
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -2,16 +2,26 @@ 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 com.intellij.openapi.util.text.StringUtil
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import java.io.File
@Singleton
class PackageDocs
@Inject constructor(val linkResolver: DeclarationLinkResolver?,
- val logger: DokkaLogger)
+ val logger: DokkaLogger,
+ val environment: KotlinCoreEnvironment,
+ val refGraph: NodeReferenceGraph,
+ val elementSignatureProvider: ElementSignatureProvider)
{
val moduleContent: MutableContent = MutableContent()
private val _packageContent: MutableMap<String, MutableContent> = hashMapOf()
@@ -21,7 +31,7 @@ class PackageDocs
fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) {
val file = File(fileName)
if (file.exists()) {
- val text = file.readText()
+ val text = StringUtil.convertLineSeparators(file.readText())
val tree = parseMarkdown(text)
val linkMap = LinkMap.buildLinkMap(tree.node, text)
var targetContent: MutableContent = moduleContent
@@ -40,6 +50,64 @@ class PackageDocs
}
}
+ private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) {
+ val javadocText = text
+ .replace("*/", "*&#47;")
+ .removeSurrounding("<html>", "</html>", true).trim()
+ .removeSurrounding("<body>", "</body>", true)
+ .lineSequence()
+ .map { "* $it" }
+ .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/")
+ parseJavadoc(javadocText, packageName, file)
+ }
+
+ private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence {
+ if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) {
+ return subSequence(prefix.length, length - suffix.length)
+ }
+ return subSequence(0, length)
+ }
+
+
+ private fun parseJavadoc(text: String, packageName: String, file: File) {
+
+ val psiFileFactory = PsiFileFactory.getInstance(environment.project)
+ val psiFile = psiFileFactory.createFileFromText(
+ file.nameWithoutExtension + ".java",
+ JavaFileType.INSTANCE,
+ "package $packageName; $text\npublic class C {}",
+ LocalTimeCounter.currentTime(),
+ false,
+ true
+ )
+
+ val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!!
+ val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!)
+ findOrCreatePackageContent(packageName).apply {
+ val content = parser.parseDocumentation(psiClass).content
+ children.addAll(content.children)
+ content.sections.forEach {
+ addSection(it.tag, it.subjectName).children.addAll(it.children)
+ }
+ }
+ }
+
+
+ fun parseJava(fileName: String, packageName: String) {
+ val file = File(fileName)
+ if (file.exists()) {
+ val text = file.readText()
+
+ val trimmedText = text.trim()
+
+ if (trimmedText.startsWith("/**")) {
+ parseJavadoc(text, packageName, file)
+ } else if (trimmedText.toLowerCase().startsWith("<html>")) {
+ parseHtmlAsJavadoc(trimmedText, packageName, file)
+ }
+ }
+ }
+
private fun findTargetContent(heading: String): MutableContent {
if (heading.startsWith("Module") || heading.startsWith("module")) {
return moduleContent
@@ -51,7 +119,7 @@ class PackageDocs
}
private fun findOrCreatePackageContent(packageName: String) =
- _packageContent.getOrPut(packageName) { -> MutableContent() }
+ _packageContent.getOrPut(packageName) { MutableContent() }
private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock {
if (linkResolver != null) {
diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt
index 2c75cfda..99ee362e 100644
--- a/core/src/main/kotlin/Model/SourceLinks.kt
+++ b/core/src/main/kotlin/Model/SourceLinks.kt
@@ -10,20 +10,20 @@ import java.io.File
fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) {
val path = psi?.containingFile?.virtualFile?.path ?: return
+ val canonicalPath = File(path).canonicalPath
val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi
- val absPath = File(path).absolutePath
- val linkDef = sourceLinks.firstOrNull { absPath.startsWith(it.path) }
- if (linkDef != null) {
- var url = linkDef.url + path.substring(linkDef.path.length)
- if (linkDef.lineSuffix != null) {
+ val pair = determineSourceLinkDefinition(canonicalPath, sourceLinks)
+ if (pair != null) {
+ val (sourceLinkDefinition, sourceLinkCanonicalPath) = pair
+ var url = determineUrl(canonicalPath, sourceLinkDefinition, sourceLinkCanonicalPath)
+ if (sourceLinkDefinition.lineSuffix != null) {
val line = target?.lineNumber()
if (line != null) {
- url += linkDef.lineSuffix + line.toString()
+ url += sourceLinkDefinition.lineSuffix + line.toString()
}
}
- append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl),
- RefKind.Detail);
+ append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), RefKind.Detail)
}
if (target != null) {
@@ -31,6 +31,28 @@ fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<Sourc
}
}
+private fun determineSourceLinkDefinition(
+ canonicalPath: String,
+ sourceLinks: List<SourceLinkDefinition>
+): Pair<SourceLinkDefinition, String>? {
+ return sourceLinks
+ .asSequence()
+ .map { it to File(it.path).canonicalPath }
+ .firstOrNull { (_, sourceLinkCanonicalPath) ->
+ canonicalPath.startsWith(sourceLinkCanonicalPath)
+ }
+}
+
+private fun determineUrl(
+ canonicalPath: String,
+ sourceLinkDefinition: SourceLinkDefinition,
+ sourceLinkCanonicalPath: String
+): String {
+ val relativePath = canonicalPath.substring(sourceLinkCanonicalPath.length)
+ val relativeUrl = relativePath.replace('\\', '/').removePrefix("/")
+ return "${sourceLinkDefinition.url.removeSuffix("/")}/$relativeUrl"
+}
+
private fun PsiElement.sourcePosition(): String {
val path = containingFile.virtualFile.path
val lineNumber = lineNumber()
diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
index 36704918..732cbc48 100644
--- a/core/src/main/kotlin/Utilities/DokkaModules.kt
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -2,14 +2,11 @@ package org.jetbrains.dokka.Utilities
import com.google.inject.Binder
import com.google.inject.Module
-import com.google.inject.Provider
import com.google.inject.TypeLiteral
import com.google.inject.binder.AnnotatedBindingBuilder
import com.google.inject.name.Names
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Formats.FormatDescriptor
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import java.io.File
import kotlin.reflect.KClass
@@ -27,8 +24,9 @@ class DokkaAnalysisModule(val environment: AnalysisEnvironment,
val coreEnvironment = environment.createCoreEnvironment()
binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment)
- val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment)
+ val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment)
binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade)
+ binder.bind<DokkaResolutionFacade>().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade)
binder.bind<DocumentationOptions>().toInstance(options)
diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/Utilities/Html.kt
index a5a93d9e..de1ce1a5 100644
--- a/core/src/main/kotlin/Utilities/Html.kt
+++ b/core/src/main/kotlin/Utilities/Html.kt
@@ -1,8 +1,12 @@
package org.jetbrains.dokka
+import java.net.URLEncoder
+
/**
* Replaces symbols reserved in HTML with their respective entities.
* Replaces & with &amp;, < with &lt; and > with &gt;
*/
fun String.htmlEscape(): String = replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8") \ No newline at end of file
diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/Utilities/ServiceLocator.kt
index 71bfd21b..83c4c65c 100644
--- a/core/src/main/kotlin/Utilities/ServiceLocator.kt
+++ b/core/src/main/kotlin/Utilities/ServiceLocator.kt
@@ -14,12 +14,21 @@ class ServiceLookupException(message: String) : Exception(message)
object ServiceLocator {
fun <T : Any> lookup(clazz: Class<T>, category: String, implementationName: String): T {
val descriptor = lookupDescriptor(category, implementationName)
+ return lookup(clazz, descriptor)
+ }
+
+ fun <T : Any> lookup(
+ clazz: Class<T>,
+ descriptor: ServiceDescriptor
+ ): T {
val loadedClass = javaClass.classLoader.loadClass(descriptor.className)
val constructor = loadedClass.constructors
- .filter { it.parameterTypes.isEmpty() }
- .firstOrNull() ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
+ .filter { it.parameterTypes.isEmpty() }
+ .firstOrNull()
+ ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor")
- val implementationRawType: Any = if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor)
+ val implementationRawType: Any =
+ if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor)
if (!clazz.isInstance(implementationRawType)) {
throw ServiceLookupException("Class ${descriptor.className} is not a subtype of ${clazz.name}")
@@ -79,6 +88,7 @@ object ServiceLocator {
}
inline fun <reified T : Any> ServiceLocator.lookup(category: String, implementationName: String): T = lookup(T::class.java, category, implementationName)
+inline fun <reified T : Any> ServiceLocator.lookup(desc: ServiceDescriptor): T = lookup(T::class.java, desc)
private val ZipEntry.fileName: String
get() = name.substringAfterLast("/", name)
diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/Utilities/Uri.kt
new file mode 100644
index 00000000..9827c624
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/Uri.kt
@@ -0,0 +1,40 @@
+package org.jetbrains.dokka
+
+import java.net.URI
+
+
+fun URI.relativeTo(uri: URI): URI {
+ // Normalize paths to remove . and .. segments
+ val base = uri.normalize()
+ val child = this.normalize()
+
+ fun StringBuilder.appendRelativePath() {
+ // Split paths into segments
+ var bParts = base.path.split('/').dropLastWhile { it.isEmpty() }
+ val cParts = child.path.split('/').dropLastWhile { it.isEmpty() }
+
+ // Discard trailing segment of base path
+ if (bParts.isNotEmpty() && !base.path.endsWith("/")) {
+ bParts = bParts.dropLast(1)
+ }
+
+ // Compute common prefix
+ val commonPartsSize = bParts.zip(cParts).takeWhile { (basePart, childPart) -> basePart == childPart }.count()
+ bParts.drop(commonPartsSize).joinTo(this, separator = "") { "../" }
+ cParts.drop(commonPartsSize).joinTo(this, separator = "/")
+ }
+
+ return URI.create(buildString {
+ if (base.path != child.path) {
+ appendRelativePath()
+ }
+ child.rawQuery?.let {
+ append("?")
+ append(it)
+ }
+ child.rawFragment?.let {
+ append("#")
+ append(it)
+ }
+ })
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt
index 2a14c6ff..118b134a 100644
--- a/core/src/main/kotlin/javadoc/docbase.kt
+++ b/core/src/main/kotlin/javadoc/docbase.kt
@@ -2,7 +2,7 @@ package org.jetbrains.dokka.javadoc
import com.sun.javadoc.*
import org.jetbrains.dokka.*
-import java.lang.reflect.Modifier
+import java.lang.reflect.Modifier.*
import java.util.*
import kotlin.reflect.KClass
@@ -75,9 +75,7 @@ open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node
node.deprecation?.let {
val content = it.content.asText()
- if (content != null) {
- result.add(TagImpl(this, "deprecated", content))
- }
+ result.add(TagImpl(this, "deprecated", content ?: ""))
}
return result.toTypedArray()
@@ -116,20 +114,27 @@ class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc {
- override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node) // TODO ?????
+ override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node.links.find { it.kind == NodeKind.AnnotationClass } ?: node) // TODO ?????
override fun isSynthesized(): Boolean = false
override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO
}
open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc {
- override fun isPublic(): Boolean = true
+ override fun isPublic(): Boolean = node.hasModifier("public") || node.hasModifier("internal")
override fun isPackagePrivate(): Boolean = false
override fun isStatic(): Boolean = node.hasModifier("static")
- override fun modifierSpecifier(): Int = Modifier.PUBLIC + if (isStatic) Modifier.STATIC else 0
+ override fun modifierSpecifier(): Int = visibilityModifier or (if (isStatic) STATIC else 0)
+ private val visibilityModifier
+ get() = when {
+ isPublic() -> PUBLIC
+ isPrivate() -> PRIVATE
+ isProtected() -> PROTECTED
+ else -> 0
+ }
override fun qualifiedName(): String? = node.qualifiedName()
override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray()
override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim()
- override fun isProtected(): Boolean = false
+ override fun isProtected(): Boolean = node.hasModifier("protected")
override fun isFinal(): Boolean = node.hasModifier("final")
@@ -165,7 +170,7 @@ open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationN
return null
}
- override fun isPrivate(): Boolean = false
+ override fun isPrivate(): Boolean = node.hasModifier("private")
override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true
}
@@ -173,7 +178,7 @@ open class TypeAdapter(override val module: ModuleNodeAdapter, override val node
private val javaLanguageService = JavaLanguageService()
override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType()
- override fun typeName(): String = javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()
+ override fun typeName(): String = (javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()) + dimension()
override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName()
override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("")
@@ -275,7 +280,7 @@ class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNod
}
class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter {
- override fun typeName(): String? = JavaLanguageService().renderType(node.detail(NodeKind.Type))
+ override fun typeName(): String? = type()?.typeName()
override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type))
override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray()
}
@@ -317,7 +322,7 @@ open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: Documentatio
.map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) }
.toTypedArray()
- override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO
+ override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).last().hasModifier("vararg")
override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" }
@@ -406,6 +411,9 @@ open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNod
return classNode.simpleName()
}
+ override fun qualifiedName(): String? {
+ return super.qualifiedName()
+ }
override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray()
override fun constructors(): Array<out ConstructorDoc> = constructors(true)
override fun importedPackages(): Array<out PackageDoc> = emptyArray()
@@ -514,12 +522,13 @@ class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorR
}
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.details(kind)
+ .filter(DocumentationNode::hasNonEmptyContent)
+ .map { ParamTagAdapter(module, this, it.name, true, it.content.children) }
- + node.content.sections
- .filter(sectionFilter)
- .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) })
-
- .toTypedArray() \ No newline at end of file
+ + node.content.sections
+ .filter(sectionFilter)
+ .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) }
+ )
+ .distinctBy { it.parameterName }
+ .toTypedArray() \ No newline at end of file
diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt
index 05d98b71..99c9bfff 100644
--- a/core/src/main/kotlin/javadoc/tags.kt
+++ b/core/src/main/kotlin/javadoc/tags.kt
@@ -71,7 +71,7 @@ class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: Conte
override fun referencedPackage(): PackageDoc? = null
override fun referencedClass(): ClassDoc? = method.containingClass()
override fun referencedClassName(): String = method.containingClass()?.name() ?: ""
- override fun label(): String = "${method.containingClass()?.name()}.${method.name()}"
+ override fun label(): String = content.text ?: "${method.containingClass()?.name()}.${method.name()}"
override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties
new file mode 100644
index 00000000..c484a920
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Dokka
+description=Uses Dokka Default resolver \ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
new file mode 100644
index 00000000..3b61eabe
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.JavaLayoutHtmlInboundLinkResolutionService
+description=Resolver for JavaLayoutHtml \ No newline at end of file
diff --git a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties
new file mode 100644
index 00000000..0d5d7d17
--- /dev/null
+++ b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Javadoc
+description=Uses Javadoc Default resolver \ No newline at end of file
diff --git a/core/src/test/kotlin/NodeSelect.kt b/core/src/test/kotlin/NodeSelect.kt
new file mode 100644
index 00000000..fe0394f9
--- /dev/null
+++ b/core/src/test/kotlin/NodeSelect.kt
@@ -0,0 +1,90 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+
+class SelectBuilder {
+ private val root = ChainFilterNode(SubgraphTraverseFilter(), null)
+ private var activeNode = root
+ private val chainEnds = mutableListOf<SelectFilter>()
+
+ fun withName(name: String) = matching { it.name == name }
+
+ fun withKind(kind: NodeKind) = matching{ it.kind == kind }
+
+ fun matching(block: (DocumentationNode) -> Boolean) {
+ attachFilterAndMakeActive(PredicateFilter(block))
+ }
+
+ fun subgraph() {
+ attachFilterAndMakeActive(SubgraphTraverseFilter())
+ }
+
+ fun subgraphOf(kind: RefKind) {
+ attachFilterAndMakeActive(DirectEdgeFilter(kind))
+ }
+
+ private fun attachFilterAndMakeActive(next: SelectFilter) {
+ activeNode = ChainFilterNode(next, activeNode)
+ }
+
+ private fun endChain() {
+ chainEnds += activeNode
+ }
+
+ fun build(): SelectFilter {
+ endChain()
+ return CombineFilterNode(chainEnds)
+ }
+}
+
+private class ChainFilterNode(val filter: SelectFilter, val previous: SelectFilter?): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return filter.select(previous?.select(roots) ?: roots)
+ }
+}
+
+private class CombineFilterNode(val previous: List<SelectFilter>): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return previous.asSequence().flatMap { it.select(roots) }
+ }
+}
+
+abstract class SelectFilter {
+ abstract fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode>
+}
+
+private class SubgraphTraverseFilter: SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ val visited = mutableSetOf<DocumentationNode>()
+ return roots.flatMap {
+ generateSequence(listOf(it)) { nodes ->
+ nodes.flatMap { it.allReferences() }
+ .map { it.to }
+ .filter { visited.add(it) }
+ .takeUnless { it.isEmpty() }
+ }
+ }.flatten()
+ }
+
+}
+
+private class PredicateFilter(val condition: (DocumentationNode) -> Boolean): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return roots.filter(condition)
+ }
+}
+
+private class DirectEdgeFilter(val kind: RefKind): SelectFilter() {
+ override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
+ return roots.flatMap { it.references(kind).asSequence() }.map { it.to }
+ }
+}
+
+
+fun selectNodes(root: DocumentationNode, block: SelectBuilder.() -> Unit): List<DocumentationNode> {
+ val builder = SelectBuilder()
+ builder.apply(block)
+ return builder.build().select(sequenceOf(root)).toMutableSet().toList()
+} \ No newline at end of file
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index aa3eff48..4f77a2f6 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -8,12 +8,12 @@ import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.dokka.*
import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
+import org.jetbrains.kotlin.cli.common.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.ContentRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.junit.Assert
import org.junit.Assert.fail
@@ -25,22 +25,27 @@ fun verifyModel(vararg roots: ContentRoot,
format: String = "html",
includeNonPublic: Boolean = true,
perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
+ sourceLinks: List<SourceLinkDefinition> = emptyList(),
verifier: (DocumentationModule) -> Unit) {
val documentation = DocumentationModule("test")
val options = DocumentationOptions(
- "",
- format,
- includeNonPublic = includeNonPublic,
- skipEmptyPackages = false,
- includeRootPackage = true,
- sourceLinks = listOf(),
- perPackageOptions = perPackageOptions,
- generateIndexPages = false,
- noStdlibLink = true,
- cacheRoot = "default",
- languageVersion = null,
- apiVersion = null
+ "",
+ format,
+ includeNonPublic = includeNonPublic,
+ skipEmptyPackages = false,
+ includeRootPackage = true,
+ sourceLinks = sourceLinks,
+ perPackageOptions = perPackageOptions,
+ generateIndexPages = false,
+ noStdlibLink = noStdlibLink,
+ noJdkLink = false,
+ cacheRoot = "default",
+ languageVersion = null,
+ apiVersion = null,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
)
appendDocumentation(documentation, *roots,
@@ -110,15 +115,17 @@ fun verifyModel(source: String,
withKotlinRuntime: Boolean = false,
format: String = "html",
includeNonPublic: Boolean = true,
+ sourceLinks: List<SourceLinkDefinition> = emptyList(),
verifier: (DocumentationModule) -> Unit) {
- if (!File(source).exists()) {
- throw IllegalArgumentException("Can't find test data file $source")
+ require (File(source).exists()) {
+ "Cannot find test data file $source"
}
verifyModel(contentRootFromPath(source),
withJdk = withJdk,
withKotlinRuntime = withKotlinRuntime,
format = format,
includeNonPublic = includeNonPublic,
+ sourceLinks = sourceLinks,
verifier = verifier)
}
@@ -161,13 +168,17 @@ fun verifyOutput(roots: Array<ContentRoot>,
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
+ *roots,
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ format = format,
+ includeNonPublic = includeNonPublic,
+ noStdlibLink = noStdlibLink,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
) {
verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
}
@@ -184,21 +195,27 @@ fun verifyModelOutput(it: DocumentationModule,
assertEqualsIgnoringSeparators(expectedFile, output.toString())
}
-fun verifyOutput(path: String,
- outputExtension: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+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,
- outputGenerator
+ arrayOf(contentRootFromPath(path)),
+ outputExtension,
+ withJdk,
+ withKotlinRuntime,
+ format,
+ includeNonPublic,
+ noStdlibLink,
+ collectInheritedExtensionsFromLibraries,
+ outputGenerator
)
}
@@ -257,6 +274,14 @@ fun StringBuilder.appendNode(node: ContentNode): StringBuilder {
is ContentBlock -> {
appendChildren(node)
}
+ is NodeRenderContent -> {
+ append("render(")
+ append(node.node)
+ append(",")
+ append(node.mode)
+ append(")")
+ }
+ is ContentSymbol -> { append(node.text) }
is ContentEmpty -> { /* nothing */ }
else -> throw IllegalStateException("Don't know how to format node $node")
}
diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt
index 54c367fd..807118c5 100644
--- a/core/src/test/kotlin/format/HtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/HtmlFormatTest.kt
@@ -1,9 +1,8 @@
package org.jetbrains.dokka.tests
import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
-import org.jetbrains.kotlin.config.KotlinSourceRoot
-import org.junit.Before
import org.junit.Test
import java.io.File
@@ -100,7 +99,8 @@ class HtmlFormatTest: FileGeneratorTestCase() {
}
@Test fun crossLanguageKotlinExtendsJava() {
- verifyOutput(arrayOf(KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt"),
+ verifyOutput(arrayOf(
+ KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt", false),
JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)),
".html") { model, output ->
buildPagesAndReadInto(
diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
index b971b54d..062cf0b5 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt
@@ -54,6 +54,7 @@ class KotlinWebSiteFormatTest: FileGeneratorTestCase() {
outputFormat = "html",
generateIndexPages = false,
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
index 49fa6d2f..8076fb92 100644
--- a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
+++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
@@ -65,6 +65,7 @@ class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() {
outputFormat = "kotlin-website-html",
generateIndexPages = false,
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt
index 9e4c831d..b078292b 100644
--- a/core/src/test/kotlin/format/MarkdownFormatTest.kt
+++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt
@@ -109,7 +109,12 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
}
@Test fun javaCodeInParam() {
- verifyJavaMarkdownNode("javaCodeInParam")
+ verifyJavaMarkdownNodes("javaCodeInParam") {
+ selectNodes(it) {
+ subgraphOf(RefKind.Member)
+ withKind(NodeKind.Function)
+ }
+ }
}
@Test fun javaSpaceInAuthor() {
@@ -160,6 +165,14 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
verifyMarkdownNode("reifiedTypeParameter", withKotlinRuntime = true)
}
+ @Test fun suspendInlineFunctionOrder() {
+ verifyMarkdownNode("suspendInlineFunction", withKotlinRuntime = true)
+ }
+
+ @Test fun inlineSuspendFunctionOrderChanged() {
+ verifyMarkdownNode("inlineSuspendFunction", withKotlinRuntime = true)
+ }
+
@Test fun annotatedTypeParameter() {
verifyMarkdownNode("annotatedTypeParameter", withKotlinRuntime = true)
}
@@ -313,6 +326,7 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
outputFormat = "html",
generateIndexPages = false,
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
@@ -447,6 +461,7 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
outputFormat = "html",
generateIndexPages = false,
noStdlibLink = true,
+ noJdkLink = true,
languageVersion = null,
apiVersion = null
)
@@ -524,4 +539,8 @@ class MarkdownFormatTest: FileGeneratorTestCase() {
nodesWithName
}
}
+
+ @Test fun nullableTypeParameterFunction() {
+ verifyMarkdownNode("nullableTypeParameterFunction", withKotlinRuntime = true)
+ }
}
diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt
index 704f7b99..3ff5f123 100644
--- a/core/src/test/kotlin/format/PackageDocsTest.kt
+++ b/core/src/test/kotlin/format/PackageDocsTest.kt
@@ -1,19 +1,46 @@
package org.jetbrains.dokka.tests.format
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.util.Disposer
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doAnswer
import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.mock
import org.jetbrains.dokka.*
import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment
+import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
+import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Before
import org.junit.Test
import java.io.File
class PackageDocsTest {
+
+ private lateinit var testDisposable: Disposable
+
+ @Before
+ fun setup() {
+ testDisposable = Disposer.newDisposable()
+ }
+
+ @After
+ fun cleanup() {
+ Disposer.dispose(testDisposable)
+ }
+
+ fun createPackageDocs(linkResolver: DeclarationLinkResolver?): PackageDocs {
+ val environment = KotlinCoreEnvironment.createForTests(testDisposable, CompilerConfiguration.EMPTY, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+ return PackageDocs(linkResolver, DokkaConsoleLogger, environment, mock(), mock())
+ }
+
@Test fun verifyParse() {
- val docs = PackageDocs(null, DokkaConsoleLogger)
+
+ val docs = createPackageDocs(null)
docs.parse("testdata/packagedocs/stdlib.md", emptyList())
val packageContent = docs.packageContent["kotlin"]!!
val block = (packageContent.children.single() as ContentBlock).children.first() as ContentText
@@ -22,13 +49,13 @@ class PackageDocsTest {
@Test fun testReferenceLinksInPackageDocs() {
val mockLinkResolver = mock<DeclarationLinkResolver> {
- val exampleCom = "http://example.com"
+ val exampleCom = "https://example.com"
on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) }
}
val mockPackageDescriptor = mock<PackageFragmentDescriptor> {}
- val docs = PackageDocs(mockLinkResolver, DokkaConsoleLogger)
+ val docs = createPackageDocs(mockLinkResolver)
docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor))
checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks")
diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt
index 45c45aa4..6adbe7a0 100644
--- a/core/src/test/kotlin/javadoc/JavadocTest.kt
+++ b/core/src/test/kotlin/javadoc/JavadocTest.kt
@@ -7,6 +7,7 @@ import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators
import org.jetbrains.dokka.tests.verifyModel
import org.junit.Assert.*
import org.junit.Test
+import java.lang.reflect.Modifier.*
class JavadocTest {
@Test fun testTypes() {
@@ -64,7 +65,7 @@ class JavadocTest {
val member = classDoc.methods().find { it.name() == "main" }!!
val paramType = member.parameters()[0].type()
assertNull(paramType.asParameterizedType())
- assertEquals("String", paramType.typeName())
+ assertEquals("String[]", paramType.typeName())
assertEquals("String", paramType.asClassDoc().name())
}
}
@@ -161,6 +162,115 @@ class JavadocTest {
}
}
+ @Test
+ fun testVararg() {
+ verifyJavadoc("testdata/javadoc/vararg.kt") { doc ->
+ val classDoc = doc.classNamed("VarargKt")!!
+ val methods = classDoc.methods()
+ methods.single { it.name() == "vararg" }.let { method ->
+ assertTrue(method.isVarArgs)
+ assertEquals("int", method.parameters().last().typeName())
+ }
+ methods.single { it.name() == "varargInMiddle" }.let { method ->
+ assertFalse(method.isVarArgs)
+ assertEquals("int[]", method.parameters()[1].typeName())
+ }
+ }
+ }
+
+ @Test
+ fun shouldHaveValidVisibilityModifiers() {
+ verifyJavadoc("testdata/javadoc/visibilityModifiers.kt", withKotlinRuntime = true) { doc ->
+ val classDoc = doc.classNamed("foo.Apple")!!
+ val methods = classDoc.methods()
+
+ val getName = methods[0]
+ val setName = methods[1]
+ val getWeight = methods[2]
+ val setWeight = methods[3]
+ val getRating = methods[4]
+ val setRating = methods[5]
+ val getCode = methods[6]
+ val color = classDoc.fields()[3]
+ val code = classDoc.fields()[4]
+
+ assertTrue(getName.isProtected)
+ assertEquals(PROTECTED, getName.modifierSpecifier())
+ assertTrue(setName.isProtected)
+ assertEquals(PROTECTED, setName.modifierSpecifier())
+
+ assertTrue(getWeight.isPublic)
+ assertEquals(PUBLIC, getWeight.modifierSpecifier())
+ assertTrue(setWeight.isPublic)
+ assertEquals(PUBLIC, setWeight.modifierSpecifier())
+
+ assertTrue(getRating.isPublic)
+ assertEquals(PUBLIC, getRating.modifierSpecifier())
+ assertTrue(setRating.isPublic)
+ assertEquals(PUBLIC, setRating.modifierSpecifier())
+
+ assertTrue(getCode.isPublic)
+ assertEquals(PUBLIC or STATIC, getCode.modifierSpecifier())
+
+ assertEquals(methods.size, 7)
+
+ assertTrue(color.isPrivate)
+ assertEquals(PRIVATE, color.modifierSpecifier())
+
+ assertTrue(code.isPrivate)
+ assertTrue(code.isStatic)
+ assertEquals(PRIVATE or STATIC, code.modifierSpecifier())
+ }
+ }
+
+ @Test
+ fun shouldNotHaveDuplicatedConstructorParameters() {
+ verifyJavadoc("testdata/javadoc/constructorParameters.kt") { doc ->
+ val classDoc = doc.classNamed("bar.Banana")!!
+ val paramTags = classDoc.constructors()[0].paramTags()
+
+ assertEquals(3, paramTags.size)
+ }
+ }
+
+ @Test fun shouldHaveAllFunctionMarkedAsDeprecated() {
+ verifyJavadoc("testdata/javadoc/deprecated.java") { doc ->
+ val classDoc = doc.classNamed("bar.Banana")!!
+
+ classDoc.methods().forEach { method ->
+ assertTrue(method.tags().any { it.kind() == "deprecated" })
+ }
+ }
+ }
+
+ @Test
+ fun testDefaultNoArgConstructor() {
+ verifyJavadoc("testdata/javadoc/defaultNoArgConstructor.kt") { doc ->
+ val classDoc = doc.classNamed("foo.Peach")!!
+ assertTrue(classDoc.constructors()[0].tags()[2].text() == "print peach")
+ }
+ }
+
+ @Test
+ fun testNoArgConstructor() {
+ verifyJavadoc("testdata/javadoc/noArgConstructor.kt") { doc ->
+ val classDoc = doc.classNamed("foo.Plum")!!
+ assertTrue(classDoc.constructors()[0].tags()[2].text() == "print plum")
+ }
+ }
+
+ @Test
+ fun testArgumentReference() {
+ verifyJavadoc("testdata/javadoc/argumentReference.kt") { doc ->
+ val classDoc = doc.classNamed("ArgumentReferenceKt")!!
+ val method = classDoc.methods().first()
+ val tag = method.seeTags().first()
+ assertEquals("argNamedError", tag.referencedMemberName())
+ assertEquals("error", tag.label())
+ }
+ }
+
+
private fun verifyJavadoc(name: String,
withJdk: Boolean = false,
withKotlinRuntime: Boolean = false,
diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt
index 32910682..fd7a16a4 100644
--- a/core/src/test/kotlin/model/FunctionTest.kt
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -167,6 +167,33 @@ Documentation""", content.description.toTestString())
}
}
+ @Test fun suspendFunction() {
+ verifyPackageMember("testdata/functions/suspendFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }
+ assertTrue("suspend" in modifiers)
+ }
+ }
+
+ @Test fun suspendInlineFunctionOrder() {
+ verifyPackageMember("testdata/functions/suspendInlineFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter {
+ it == "suspend" || it == "inline"
+ }
+
+ assertEquals(listOf("suspend", "inline"), modifiers)
+ }
+ }
+
+ @Test fun inlineSuspendFunctionOrderChanged() {
+ verifyPackageMember("testdata/functions/inlineSuspendFunction.kt") { func ->
+ val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter {
+ it == "suspend" || it == "inline"
+ }
+
+ assertEquals(listOf("suspend", "inline"), modifiers)
+ }
+ }
+
@Test fun functionWithAnnotatedParam() {
verifyModel("testdata/functions/functionWithAnnotatedParam.kt") { model ->
with(model.members.single().members.single { it.name == "function" }) {
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
index e6c22ee4..0bec6d01 100644
--- a/core/src/test/kotlin/model/JavaTest.kt
+++ b/core/src/test/kotlin/model/JavaTest.kt
@@ -18,12 +18,12 @@ public class JavaTest {
with(content.sections[0]) {
assertEquals("Parameters", tag)
assertEquals("name", subjectName)
- assertEquals("is String parameter", toTestString())
+ assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString())
}
with(content.sections[1]) {
assertEquals("Parameters", tag)
assertEquals("value", subjectName)
- assertEquals("is int parameter", toTestString())
+ assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString())
}
with(content.sections[2]) {
assertEquals("Author", tag)
@@ -150,7 +150,7 @@ public class JavaTest {
/**
* `@suppress` not supported in Java!
*
- * [Proposed tags](http://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html)
+ * [Proposed tags](https://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html)
* Proposed tag `@exclude` for it, but not supported yet
*/
@Ignore("@suppress not supported in Java!") @Test fun suppressTag() {
@@ -193,7 +193,7 @@ public class JavaTest {
@Test fun enumValues() {
verifyJavaPackageMember("testdata/java/enumValues.java") { cls ->
val superTypes = cls.details(NodeKind.Supertype)
- assertEquals(0, superTypes.size)
+ assertEquals(1, superTypes.size)
assertEquals(1, cls.members(NodeKind.EnumItem).size)
}
}
diff --git a/core/src/test/kotlin/model/KotlinAsJavaTest.kt b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
index d24d8bdd..22818038 100644
--- a/core/src/test/kotlin/model/KotlinAsJavaTest.kt
+++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
@@ -2,6 +2,8 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.DocumentationModule
import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.RefKind
+import org.junit.Assert
import org.junit.Test
import org.junit.Assert.assertEquals
@@ -27,6 +29,24 @@ class KotlinAsJavaTest {
assertEquals("doc", getter.content.summary.toTestString())
}
}
+
+
+ @Test fun constants() {
+ verifyModelAsJava("testdata/java/constants.java") { cls ->
+ selectNodes(cls) {
+ subgraphOf(RefKind.Member)
+ matching { it.name == "constStr" || it.name == "refConst" }
+ }.forEach {
+ assertEquals("In $it", "\"some value\"", it.detailOrNull(NodeKind.Value)?.name)
+ }
+ val nullConstNode = selectNodes(cls) {
+ subgraphOf(RefKind.Member)
+ withName("nullConst")
+ }.single()
+
+ Assert.assertNull(nullConstNode.detailOrNull(NodeKind.Value))
+ }
+ }
}
fun verifyModelAsJava(source: String,
diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt
index 052f0d28..7a8f0d06 100644
--- a/core/src/test/kotlin/model/PackageTest.kt
+++ b/core/src/test/kotlin/model/PackageTest.kt
@@ -3,7 +3,7 @@ package org.jetbrains.dokka.tests
import org.jetbrains.dokka.Content
import org.jetbrains.dokka.NodeKind
import org.jetbrains.dokka.PackageOptionsImpl
-import org.jetbrains.kotlin.config.KotlinSourceRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.junit.Assert.*
import org.junit.Test
@@ -48,8 +48,8 @@ public class PackageTest {
}
@Test fun multipleFiles() {
- verifyModel(KotlinSourceRoot("testdata/packages/dottedNamePackage.kt"),
- KotlinSourceRoot("testdata/packages/simpleNamePackage.kt")) { model ->
+ 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)
@@ -70,8 +70,8 @@ public class PackageTest {
}
@Test fun multipleFilesSamePackage() {
- verifyModel(KotlinSourceRoot("testdata/packages/simpleNamePackage.kt"),
- KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt")) { model ->
+ 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)
@@ -85,7 +85,7 @@ public class PackageTest {
}
@Test fun classAtPackageLevel() {
- verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt")) { model ->
+ verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)) { model ->
assertEquals(1, model.members.count())
with(model.members.elementAt(0)) {
assertEquals(NodeKind.Package, kind)
@@ -99,7 +99,7 @@ public class PackageTest {
}
@Test fun suppressAtPackageLevel() {
- verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt"),
+ 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)) {
diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt
index 0ee0e0f5..296eaa4c 100644
--- a/core/src/test/kotlin/model/PropertyTest.kt
+++ b/core/src/test/kotlin/model/PropertyTest.kt
@@ -69,7 +69,7 @@ class PropertyTest {
with(model.members.single().members.single()) {
assertEquals(1, annotations.count())
with(annotations[0]) {
- assertEquals("Volatile", name)
+ assertEquals("Strictfp", name)
assertEquals(Content.Empty, content)
assertEquals(NodeKind.Annotation, kind)
}
diff --git a/core/src/test/kotlin/model/SourceLinksErrorTest.kt b/core/src/test/kotlin/model/SourceLinksErrorTest.kt
new file mode 100644
index 00000000..a72bd62a
--- /dev/null
+++ b/core/src/test/kotlin/model/SourceLinksErrorTest.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka.tests.model
+
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.tests.verifyModel
+import org.junit.Assert
+import org.junit.Test
+import java.io.File
+
+class SourceLinksErrorTest {
+
+ @Test
+ fun absolutePath_notMatching() {
+ val sourceLink = SourceLinkDefinitionImpl(File("testdata/nonExisting").absolutePath, "http://...", null)
+ verifyNoSourceUrl(sourceLink)
+ }
+
+ @Test
+ fun relativePath_notMatching() {
+ val sourceLink = SourceLinkDefinitionImpl("testdata/nonExisting", "http://...", null)
+ verifyNoSourceUrl(sourceLink)
+ }
+
+ private fun verifyNoSourceUrl(sourceLink: SourceLinkDefinitionImpl) {
+ verifyModel("testdata/sourceLinks/dummy.kt", sourceLinks = listOf(sourceLink)) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals("foo", name)
+ Assert.assertEquals(NodeKind.Function, kind)
+ Assert.assertTrue("should not have source urls", details(NodeKind.SourceUrl).isEmpty())
+ }
+ }
+ }
+}
+
diff --git a/core/src/test/kotlin/model/SourceLinksTest.kt b/core/src/test/kotlin/model/SourceLinksTest.kt
new file mode 100644
index 00000000..0e9b666c
--- /dev/null
+++ b/core/src/test/kotlin/model/SourceLinksTest.kt
@@ -0,0 +1,74 @@
+package org.jetbrains.dokka.tests.model
+
+import org.jetbrains.dokka.NodeKind
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.tests.verifyModel
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.io.File
+
+@RunWith(Parameterized::class)
+class SourceLinksTest(
+ private val srcLink: String,
+ private val url: String,
+ private val lineSuffix: String?,
+ private val expectedUrl: String
+) {
+
+ @Test
+ fun test() {
+ val link = if(srcLink.contains(sourceLinks)){
+ srcLink.substringBeforeLast(sourceLinks) + sourceLinks
+ } else {
+ srcLink.substringBeforeLast(testdata) + testdata
+ }
+ val sourceLink = SourceLinkDefinitionImpl(link, url, lineSuffix)
+
+ verifyModel(filePath, sourceLinks = listOf(sourceLink)) { model ->
+ with(model.members.single().members.single()) {
+ Assert.assertEquals("foo", name)
+ Assert.assertEquals(NodeKind.Function, kind)
+ Assert.assertEquals(expectedUrl, details(NodeKind.SourceUrl).single().name)
+ }
+ }
+ }
+
+ companion object {
+ private const val testdata = "testdata"
+ private const val sourceLinks = "sourceLinks"
+ private const val dummy = "dummy.kt"
+ private const val pathSuffix = "$sourceLinks/$dummy"
+ private const val filePath = "$testdata/$pathSuffix"
+ private const val url = "https://example.com"
+
+ @Parameterized.Parameters(name = "{index}: {0}, {1}, {2} = {3}")
+ @JvmStatic
+ fun data(): Collection<Array<String?>> {
+ val longestPath = File(testdata).absolutePath.removeSuffix("/") + "/../$testdata/"
+ val maxLength = longestPath.length
+ val list = listOf(
+ arrayOf(File(testdata).absolutePath.removeSuffix("/"), "$url/$pathSuffix"),
+ arrayOf(File("$testdata/$sourceLinks").absolutePath.removeSuffix("/") + "/", "$url/$dummy"),
+ arrayOf(longestPath, "$url/$pathSuffix"),
+
+ arrayOf(testdata, "$url/$pathSuffix"),
+ arrayOf("./$testdata", "$url/$pathSuffix"),
+ arrayOf("../core/$testdata", "$url/$pathSuffix"),
+ arrayOf("$testdata/$sourceLinks", "$url/$dummy"),
+ arrayOf("./$testdata/../$testdata/$sourceLinks", "$url/$dummy")
+ )
+
+ return list.map { arrayOf(it[0].padEnd(maxLength, '_'), url, null, it[1]) } +
+ listOf(
+ // check that it also works if url ends with /
+ arrayOf((File(testdata).absolutePath.removeSuffix("/") + "/").padEnd(maxLength, '_'), "$url/", null, "$url/$pathSuffix"),
+ // check if line suffix work
+ arrayOf<String?>("../core/../core/./$testdata/$sourceLinks/".padEnd(maxLength, '_'), "$url/", "#L", "$url/$dummy#L4")
+ )
+ }
+ }
+
+}
+
diff --git a/core/testdata/format/JavaSupertype.html b/core/testdata/format/JavaSupertype.html
index 27b8e5d0..892ef63a 100644
--- a/core/testdata/format/JavaSupertype.html
+++ b/core/testdata/format/JavaSupertype.html
@@ -7,7 +7,7 @@
<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">JavaSupertype</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
<br/>
<h1>Bar</h1>
-<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
+<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a></code>
<h3>Constructors</h3>
<table>
<tbody>
@@ -28,7 +28,7 @@
<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">Foo</span></a><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">Foo</span></a></code></td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span></code></td>
</tr>
</tbody>
</table>
diff --git a/core/testdata/format/enumRef.md b/core/testdata/format/enumRef.md
index 8b2a6650..f5f5a3e0 100644
--- a/core/testdata/format/enumRef.md
+++ b/core/testdata/format/enumRef.md
@@ -4,5 +4,5 @@
`fun f(): Unit`
-[java.math.RoundingMode.UP](http://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html#UP)
+[java.math.RoundingMode.UP](https://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html#UP)
diff --git a/core/testdata/format/externalReferenceLink.kt b/core/testdata/format/externalReferenceLink.kt
index 4ca0ee21..775b2e66 100644
--- a/core/testdata/format/externalReferenceLink.kt
+++ b/core/testdata/format/externalReferenceLink.kt
@@ -3,7 +3,7 @@
*
* Sure, it is [example.com]
*
- * [example.com]: http://example.com
+ * [example.com]: https://example.com
*/
fun a() {
diff --git a/core/testdata/format/externalReferenceLink.md b/core/testdata/format/externalReferenceLink.md
index 38ffde78..3565d9aa 100644
--- a/core/testdata/format/externalReferenceLink.md
+++ b/core/testdata/format/externalReferenceLink.md
@@ -4,7 +4,7 @@
`fun a(): Unit`
-It is link to [example site](http://example.com)
+It is link to [example site](https://example.com)
-Sure, it is [example.com](http://example.com)
+Sure, it is [example.com](https://example.com)
diff --git a/core/testdata/format/inheritedLink.md b/core/testdata/format/inheritedLink.md
index e5af326c..aec07a75 100644
--- a/core/testdata/format/inheritedLink.md
+++ b/core/testdata/format/inheritedLink.md
@@ -13,5 +13,5 @@
Overrides [Foo.sayHello](../../p1/-foo/say-hello.md)
-Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
+Says hello - [LinkedList](https://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
diff --git a/core/testdata/format/inlineSuspendFunction.kt b/core/testdata/format/inlineSuspendFunction.kt
new file mode 100644
index 00000000..f2009eff
--- /dev/null
+++ b/core/testdata/format/inlineSuspendFunction.kt
@@ -0,0 +1,6 @@
+/**
+ * returns 1
+ */
+inline suspend fun foo(): Int {
+ 1
+}
diff --git a/core/testdata/format/inlineSuspendFunction.md b/core/testdata/format/inlineSuspendFunction.md
new file mode 100644
index 00000000..946463f7
--- /dev/null
+++ b/core/testdata/format/inlineSuspendFunction.md
@@ -0,0 +1,8 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`suspend inline fun foo(): Int`
+
+returns 1
+
diff --git a/core/testdata/format/javaCodeInParam.java b/core/testdata/format/javaCodeInParam.java
index 73025fcc..0d1607ba 100644
--- a/core/testdata/format/javaCodeInParam.java
+++ b/core/testdata/format/javaCodeInParam.java
@@ -1,5 +1,7 @@
-/**
- * @param T this is {@code some code} and other text
- */
-class C<T> {
+class C {
+
+ /**
+ * @param par this is {@code some code} and other text
+ */
+ public void withParam(String par) {}
}
diff --git a/core/testdata/format/javaCodeInParam.md b/core/testdata/format/javaCodeInParam.md
index 319c6d87..566b176d 100644
--- a/core/testdata/format/javaCodeInParam.md
+++ b/core/testdata/format/javaCodeInParam.md
@@ -1,14 +1,9 @@
-[test](../index.md) / [C](./index.md)
+[test](../index.md) / [C](index.md) / [withParam](./with-param.md)
-# C
+# withParam
-`protected open class C<T : Any>`
+`open fun withParam(par: String!): Unit`
### Parameters
-`T` - this is `some code` and other text
-
-### Constructors
-
-| [&lt;init&gt;](-init-.md) | `C()` |
-
+`par` - String!: this is `some code` and other text \ No newline at end of file
diff --git a/core/testdata/format/javaCodeLiteralTags.md b/core/testdata/format/javaCodeLiteralTags.md
index b36be04d..c2c58047 100644
--- a/core/testdata/format/javaCodeLiteralTags.md
+++ b/core/testdata/format/javaCodeLiteralTags.md
@@ -12,5 +12,5 @@ A&lt;B&gt;C
### Constructors
-| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
+| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` |
diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java
index 622116b2..9e77402e 100644
--- a/core/testdata/format/javadocHtml.java
+++ b/core/testdata/format/javadocHtml.java
@@ -9,6 +9,18 @@
* <code>Code</code>
* <pre>Block code</pre>
* <ul><li>List Item</li></ul>
+ * <pre>
+ * with( some ) {
+ * multi = lines
+ * sample()
+ * }
+ * </pre>
+ * <pre>
+ * {@code
+ * with (some) { <code> }
+ * }
+ * </pre>
+ *
*/
public class C {
}
diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md
index a3c1baff..77f6c829 100644
--- a/core/testdata/format/javadocHtml.md
+++ b/core/testdata/format/javadocHtml.md
@@ -16,13 +16,23 @@ Block code
* List Item
-### Constructors
-| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
+```
+
+ with( some ) {
+ multi = lines
+ sample()
+ }
+ ```
+
+
```
-Block code<br>```
-<br>
-* List Item
-<br> |
+with (some) { <code> }
+
+ ```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* |
diff --git a/core/testdata/format/jdkLinks.md b/core/testdata/format/jdkLinks.md
index 7498171d..c3a5fbf4 100644
--- a/core/testdata/format/jdkLinks.md
+++ b/core/testdata/format/jdkLinks.md
@@ -2,13 +2,13 @@
# C
-`class C : `[`ClassLoader`](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html)
+`class C : `[`ClassLoader`](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html)
-This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String))
+This is a [ClassLoader](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String))
-You can print something to [java.lang.System.out](http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now!
+You can print something to [java.lang.System.out](https://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now!
### Constructors
-| [&lt;init&gt;](-init-.md) | `C()`<br>This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) |
+| [&lt;init&gt;](-init-.md) | `C()`<br>This is a [ClassLoader](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](https://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) |
diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html
index 596cca73..f0bb475e 100644
--- a/core/testdata/format/markdownInLinks.html
+++ b/core/testdata/format/markdownInLinks.html
@@ -9,6 +9,6 @@
<h1>foo</h1>
<a name="$foo()"></a>
<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
-<p><a href="http://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p>
+<p><a href="https://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p>
</BODY>
</HTML>
diff --git a/core/testdata/format/markdownInLinks.kt b/core/testdata/format/markdownInLinks.kt
index 67b6311f..380727ee 100644
--- a/core/testdata/format/markdownInLinks.kt
+++ b/core/testdata/format/markdownInLinks.kt
@@ -1,4 +1,4 @@
/**
- * [a**b**__d__ kas ](http://www.ibm.com)
+ * [a**b**__d__ kas ](https://www.ibm.com)
*/
fun foo() {}
diff --git a/core/testdata/format/nullableTypeParameterFunction.kt b/core/testdata/format/nullableTypeParameterFunction.kt
new file mode 100644
index 00000000..01805a7b
--- /dev/null
+++ b/core/testdata/format/nullableTypeParameterFunction.kt
@@ -0,0 +1,8 @@
+class Bar<T> {
+ val dataList = mutableListOf<T>()
+
+ open fun checkElement(
+ elem: T,
+ addFunc: ((elem: T) -> Unit)? = { dataList.add(it) }
+ ): Int = 1
+} \ No newline at end of file
diff --git a/core/testdata/format/nullableTypeParameterFunction.md b/core/testdata/format/nullableTypeParameterFunction.md
new file mode 100644
index 00000000..5764007b
--- /dev/null
+++ b/core/testdata/format/nullableTypeParameterFunction.md
@@ -0,0 +1,18 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Properties
+
+| [dataList](data-list.md) | `val dataList: MutableList<`[`T`](index.md#T)`>` |
+
+### Functions
+
+| [checkElement](check-element.md) | `fun checkElement(elem: `[`T`](index.md#T)`, addFunc: ((elem: `[`T`](index.md#T)`) -> Unit)? = { dataList.add(it) }): Int` |
+
diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
index ad632fef..4b5f3a64 100644
--- a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
+++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md
@@ -1,6 +1,6 @@
-[test](../index.md) / [kotlin.SuspendFunction0](./index.md)
+[test](../index.md) / [kotlin.coroutines.SuspendFunction0](./index.md)
-### Extensions for kotlin.SuspendFunction0
+### Extensions for kotlin.coroutines.SuspendFunction0
| [foo](foo.md) | `fun (suspend () -> Unit).foo(): Unit` |
diff --git a/core/testdata/format/suspendInlineFunction.kt b/core/testdata/format/suspendInlineFunction.kt
new file mode 100644
index 00000000..8af0d11a
--- /dev/null
+++ b/core/testdata/format/suspendInlineFunction.kt
@@ -0,0 +1,6 @@
+/**
+ * returns 1
+ */
+suspend inline fun foo(): Int {
+ 1
+}
diff --git a/core/testdata/format/suspendInlineFunction.md b/core/testdata/format/suspendInlineFunction.md
new file mode 100644
index 00000000..946463f7
--- /dev/null
+++ b/core/testdata/format/suspendInlineFunction.md
@@ -0,0 +1,8 @@
+[test](index.md) / [foo](./foo.md)
+
+# foo
+
+`suspend inline fun foo(): Int`
+
+returns 1
+
diff --git a/core/testdata/functions/inlineSuspendFunction.kt b/core/testdata/functions/inlineSuspendFunction.kt
new file mode 100644
index 00000000..54032ccf
--- /dev/null
+++ b/core/testdata/functions/inlineSuspendFunction.kt
@@ -0,0 +1,2 @@
+inline suspend fun f() {
+}
diff --git a/core/testdata/functions/suspendFunction.kt b/core/testdata/functions/suspendFunction.kt
new file mode 100644
index 00000000..49ecca2a
--- /dev/null
+++ b/core/testdata/functions/suspendFunction.kt
@@ -0,0 +1,2 @@
+suspend fun f() {
+}
diff --git a/core/testdata/functions/suspendInlineFunction.kt b/core/testdata/functions/suspendInlineFunction.kt
new file mode 100644
index 00000000..15a9018f
--- /dev/null
+++ b/core/testdata/functions/suspendInlineFunction.kt
@@ -0,0 +1,2 @@
+suspend inline fun f() {
+}
diff --git a/core/testdata/java/constants.java b/core/testdata/java/constants.java
new file mode 100644
index 00000000..26f16639
--- /dev/null
+++ b/core/testdata/java/constants.java
@@ -0,0 +1,5 @@
+public class Constants {
+ public static final String constStr = "some value";
+ public static final Object nullConst = null;
+ public static final String refConst = constStr;
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/argumentReference.kt b/core/testdata/javadoc/argumentReference.kt
new file mode 100644
index 00000000..ac3104e9
--- /dev/null
+++ b/core/testdata/javadoc/argumentReference.kt
@@ -0,0 +1,4 @@
+/**
+ * [error]
+ */
+fun argNamedError(error: String) {} \ No newline at end of file
diff --git a/core/testdata/javadoc/constructorParameters.kt b/core/testdata/javadoc/constructorParameters.kt
new file mode 100644
index 00000000..c29ae912
--- /dev/null
+++ b/core/testdata/javadoc/constructorParameters.kt
@@ -0,0 +1,14 @@
+package bar
+
+/**
+ * Just a fruit
+ *
+ * @param weight in grams
+ * @param ranking quality from 0 to 10, where 10 is best
+ * @param color yellow is default
+ */
+class Banana (
+ private val weight: Double,
+ private val ranking: Int,
+ color: String = "yellow"
+) \ No newline at end of file
diff --git a/core/testdata/javadoc/defaultNoArgConstructor.kt b/core/testdata/javadoc/defaultNoArgConstructor.kt
new file mode 100644
index 00000000..3a6d04a5
--- /dev/null
+++ b/core/testdata/javadoc/defaultNoArgConstructor.kt
@@ -0,0 +1,12 @@
+package foo
+
+/**
+ * Description
+ *
+ * @constructor print peach
+ */
+class Peach {
+ init {
+ println("peach")
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/deprecated.java b/core/testdata/javadoc/deprecated.java
new file mode 100644
index 00000000..5a6cdd77
--- /dev/null
+++ b/core/testdata/javadoc/deprecated.java
@@ -0,0 +1,28 @@
+package bar;
+
+/**
+ * Just a fruit
+ */
+public class Banana {
+ private Double weight;
+
+ /**
+ * Returns weight
+ *
+ * @return weight
+ * @deprecated
+ */
+ public Double getWeight() {
+ return weight;
+ }
+
+ /**
+ * Sets weight
+ *
+ * @param weight in grams
+ * @deprecated with message
+ */
+ public void setWeight(Double weight) {
+ this.weight = weight;
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/noArgConstructor.kt b/core/testdata/javadoc/noArgConstructor.kt
new file mode 100644
index 00000000..25e5548c
--- /dev/null
+++ b/core/testdata/javadoc/noArgConstructor.kt
@@ -0,0 +1,12 @@
+package foo
+
+/**
+ * Description
+ *
+ * @constructor print plum
+ */
+class Plum() {
+ init {
+ println("plum")
+ }
+} \ No newline at end of file
diff --git a/core/testdata/javadoc/vararg.kt b/core/testdata/javadoc/vararg.kt
new file mode 100644
index 00000000..aa6c26d7
--- /dev/null
+++ b/core/testdata/javadoc/vararg.kt
@@ -0,0 +1,3 @@
+fun vararg(a: String, vararg b: Int) {}
+
+fun varargInMiddle(a: String, vararg b: Int, c: Short) {} \ No newline at end of file
diff --git a/core/testdata/javadoc/visibilityModifiers.kt b/core/testdata/javadoc/visibilityModifiers.kt
new file mode 100644
index 00000000..e48e7f62
--- /dev/null
+++ b/core/testdata/javadoc/visibilityModifiers.kt
@@ -0,0 +1,15 @@
+package foo
+
+abstract class Apple {
+ protected var name: String = "foo"
+ internal var weight: Int = 180
+ var rating: Int = 10
+ private var color: String = "red"
+
+ companion object {
+ @JvmStatic
+ val code : Int = 123456
+ }
+
+
+} \ No newline at end of file
diff --git a/core/testdata/markdown/spec.txt b/core/testdata/markdown/spec.txt
index fce87924..916bdd89 100644
--- a/core/testdata/markdown/spec.txt
+++ b/core/testdata/markdown/spec.txt
@@ -23,7 +23,7 @@ HTML but in LaTeX and many other formats.
## Why is a spec needed?
John Gruber's [canonical description of Markdown's
-syntax](http://daringfireball.net/projects/markdown/syntax)
+syntax](https://daringfireball.net/projects/markdown/syntax)
does not specify the syntax unambiguously. Here are some examples of
questions it does not answer:
@@ -34,7 +34,7 @@ questions it does not answer:
not require that. This is hardly a "corner case," and divergences
between implementations on this issue often lead to surprises for
users in real documents. (See [this comment by John
- Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).)
+ Gruber](https://article.gmane.org/gmane.text.markdown.general/1997).)
2. Is a blank line needed before a block quote or header?
Most implementations do not require the blank line. However,
@@ -42,7 +42,7 @@ questions it does not answer:
also to ambiguities in parsing (note that some implementations
put the header inside the blockquote, while others do not).
(John Gruber has also spoken [in favor of requiring the blank
- lines](http://article.gmane.org/gmane.text.markdown.general/2146).)
+ lines](https://article.gmane.org/gmane.text.markdown.general/2146).)
3. Is a blank line needed before an indented code block?
(`Markdown.pl` requires it, but this is not mentioned in the
@@ -75,7 +75,7 @@ questions it does not answer:
```
(There are some relevant comments by John Gruber
- [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
+ [here](https://article.gmane.org/gmane.text.markdown.general/2554).)
5. Can list markers be indented? Can ordered list markers be right-aligned?
@@ -509,7 +509,7 @@ More than six `#` characters is not a header:
A space is required between the `#` characters and the header's
contents. Note that many implementations currently do not require
the space. However, the space was required by the [original ATX
-implementation](http://www.aaronsw.com/2002/atx/atx.py), and it helps
+implementation](https://www.aaronsw.com/2002/atx/atx.py), and it helps
prevent things like the following from being parsed as headers:
.
@@ -3686,9 +3686,9 @@ raw HTML:
.
.
-<http://google.com?find=\*>
+<https://google.com?find=\*>
.
-<p><a href="http://google.com?find=%5C*">http://google.com?find=\*</a></p>
+<p><a href="https://google.com?find=%5C*">https://google.com?find=\*</a></p>
.
.
@@ -3736,7 +3736,7 @@ and simplifies the job of implementations targetting other languages, as these w
UTF8 chars and need not be HTML-entity aware.
[Named entities](#name-entities) <a id="named-entities"></a> consist of `&`
-+ any of the valid HTML5 entity names + `;`. The [following document](http://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json)
++ any of the valid HTML5 entity names + `;`. The [following document](https://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json)
is used as an authoritative source of the valid entity names and their corresponding codepoints.
Conforming implementations that target Markdown don't need to generate entities for all the valid
@@ -3955,9 +3955,9 @@ And this is not parsed as a link:
But this is a link:
.
-<http://foo.bar.`baz>`
+<https://foo.bar.`baz>`
.
-<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
+<p><a href="https://foo.bar.%60baz">https://foo.bar.`baz</a>`</p>
.
And this is an HTML tag:
@@ -3986,7 +3986,7 @@ we just have literal backticks:
## Emphasis and strong emphasis
John Gruber's original [Markdown syntax
-description](http://daringfireball.net/projects/markdown/syntax#em) says:
+description](https://daringfireball.net/projects/markdown/syntax#em) says:
> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML
@@ -4229,15 +4229,15 @@ _a `_`_
.
.
-**a<http://foo.bar?q=**>
+**a<https://foo.bar?q=**>
.
-<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p>
+<p>**a<a href="https://foo.bar?q=**">https://foo.bar?q=**</a></p>
.
.
-__a<http://foo.bar?q=__>
+__a<https://foo.bar?q=__>
.
-<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p>
+<p>__a<a href="https://foo.bar?q=__">https://foo.bar?q=__</a></p>
.
This is not emphasis, because the opening delimiter is
@@ -5455,15 +5455,15 @@ soap.beep`, `soap.beeps`, `tag`, `tel`, `telnet`, `tftp`, `thismessage`,
Here are some valid autolinks:
.
-<http://foo.bar.baz>
+<https://foo.bar.baz>
.
-<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
+<p><a href="https://foo.bar.baz">https://foo.bar.baz</a></p>
.
.
-<http://foo.bar.baz?q=hello&id=22&boolean>
+<https://foo.bar.baz?q=hello&id=22&boolean>
.
-<p><a href="http://foo.bar.baz?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz?q=hello&amp;id=22&amp;boolean</a></p>
+<p><a href="https://foo.bar.baz?q=hello&amp;id=22&amp;boolean">https://foo.bar.baz?q=hello&amp;id=22&amp;boolean</a></p>
.
.
@@ -5483,9 +5483,9 @@ Uppercase is also fine:
Spaces are not allowed in autolinks:
.
-<http://foo.bar/baz bim>
+<https://foo.bar/baz bim>
.
-<p>&lt;http://foo.bar/baz bim&gt;</p>
+<p>&lt;https://foo.bar/baz bim&gt;</p>
.
An [email autolink](#email-autolink) <a id="email-autolink"></a>
@@ -5496,7 +5496,7 @@ and the URL is `mailto:` followed by the email address.
An [email address](#email-address), <a id="email-address"></a>
for these purposes, is anything that matches
the [non-normative regex from the HTML5
-spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29):
+spec](https://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29):
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
@@ -5530,9 +5530,9 @@ These are not autolinks:
.
.
-< http://foo.bar >
+< https://foo.bar >
.
-<p>&lt; http://foo.bar &gt;</p>
+<p>&lt; https://foo.bar &gt;</p>
.
.
@@ -5548,9 +5548,9 @@ These are not autolinks:
.
.
-http://google.com
+https://google.com
.
-<p>http://google.com</p>
+<p>https://google.com</p>
.
.
diff --git a/core/testdata/packagedocs/referenceLinks.kotlin.md b/core/testdata/packagedocs/referenceLinks.kotlin.md
index ac7e4b48..f7b1edad 100644
--- a/core/testdata/packagedocs/referenceLinks.kotlin.md
+++ b/core/testdata/packagedocs/referenceLinks.kotlin.md
@@ -1,7 +1,6 @@
Core functions and types
-See [ref](http://example.com)
-Also, [example](http://example.com)
+See [ref](https://example.com)
+Also, [example](https://example.com)
- \ No newline at end of file
diff --git a/core/testdata/packagedocs/referenceLinks.md b/core/testdata/packagedocs/referenceLinks.md
index 7583ee9d..177dea0c 100644
--- a/core/testdata/packagedocs/referenceLinks.md
+++ b/core/testdata/packagedocs/referenceLinks.md
@@ -14,4 +14,4 @@ See [ref]
Also, [example][ref]
<!-- Refs -->
-[ref]: http://example.com
+[ref]: https://example.com
diff --git a/core/testdata/packagedocs/referenceLinks.module.md b/core/testdata/packagedocs/referenceLinks.module.md
index ddbdbe2f..08372175 100644
--- a/core/testdata/packagedocs/referenceLinks.module.md
+++ b/core/testdata/packagedocs/referenceLinks.module.md
@@ -4,6 +4,6 @@
The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections,
text and files.
-See [ref](http://example.com)
-Also, [example](http://example.com)
+See [ref](https://example.com)
+Also, [example](https://example.com)
diff --git a/core/testdata/properties/annotatedProperty.kt b/core/testdata/properties/annotatedProperty.kt
index 8990af29..3c12691b 100644
--- a/core/testdata/properties/annotatedProperty.kt
+++ b/core/testdata/properties/annotatedProperty.kt
@@ -1 +1 @@
-@Volatile var property = "test" \ No newline at end of file
+@Strictfp var property = "test" \ No newline at end of file
diff --git a/core/testdata/sourceLinks/dummy.kt b/core/testdata/sourceLinks/dummy.kt
new file mode 100644
index 00000000..cbaffe7c
--- /dev/null
+++ b/core/testdata/sourceLinks/dummy.kt
@@ -0,0 +1,6 @@
+/**
+ * Some doc.
+ */
+fun foo(){
+
+}
diff --git a/gradle.properties b/gradle.properties
index 0e98fc46..9436e38b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,8 +1,8 @@
-dokka_version=0.9.17-SNAPSHOT
+dokka_version=0.9.18-SNAPSHOT
dokka_publication_channel=dokka
#Kotlin compiler and plugin
-bundled_kotlin_compiler_version=1.2.40-dev-529
+bundled_kotlin_compiler_version=1.3.20-dev-564
kotlin_version=1.2.21
kotlin_for_gradle_runtime_version=1.1.60
@@ -13,7 +13,4 @@ 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 \ No newline at end of file
+maven_plugin_tools_version=3.5.2
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
index 90e5b5fc..69d19944 100644
--- a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
@@ -39,8 +39,10 @@ interface DokkaConfiguration {
val languageVersion: String?
val apiVersion: String?
val noStdlibLink: Boolean
+ val noJdkLink: Boolean
val cacheRoot: String?
val suppressedFiles: List<String>
+ val collectInheritedExtensionsFromLibraries: Boolean
interface SourceRoot {
val path: String
@@ -82,29 +84,31 @@ interface DokkaConfiguration {
}
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 generateIndexPages: 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 cacheRoot: String?,
- override val suppressedFiles: List<String>,
- override val languageVersion: String?,
- override val apiVersion: String?
+ 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 generateIndexPages: 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
) : DokkaConfiguration
diff --git a/runners/android-gradle-plugin/build.gradle b/runners/android-gradle-plugin/build.gradle
index 72d1be9e..28b0cbb9 100644
--- a/runners/android-gradle-plugin/build.gradle
+++ b/runners/android-gradle-plugin/build.gradle
@@ -76,7 +76,7 @@ artifacts {
}
pluginBundle {
- website = 'http://www.kotlinlang.org/'
+ website = 'https://www.kotlinlang.org/'
vcsUrl = 'https://github.com/kotlin/dokka.git'
description = 'Dokka, the Kotlin documentation tool'
tags = ['dokka', 'kotlin', 'kdoc', 'android']
diff --git a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
index bd2e88c2..b1996da0 100644
--- a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
+++ b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
@@ -10,6 +10,7 @@ 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 {
+ dokkaRuntime = project.configurations.create("dokkaRuntime")
moduleName = project.name
outputDirectory = File(project.buildDir, "dokka").absolutePath
}
diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt
index d1b6bef5..b23328e0 100644
--- a/runners/ant/src/main/kotlin/ant/dokka.kt
+++ b/runners/ant/src/main/kotlin/ant/dokka.kt
@@ -40,6 +40,7 @@ class DokkaAntTask: Task() {
var jdkVersion: Int = 6
var noStdlibLink: Boolean = false
+ var noJdkLink: Boolean = false
var skipDeprecated: Boolean = false
@@ -110,6 +111,10 @@ class DokkaAntTask: Task() {
}
val sourceLinks = antSourceLinks.map {
val path = it.path ?: throw BuildException("'path' attribute of a <sourceLink> element is required")
+ if (path.contains("\\")) {
+ throw BuildException("'dir' attribute of a <sourceLink> - incorrect value, only Unix based path allowed.")
+ }
+
val url = it.url ?: throw BuildException("'url' attribute of a <sourceLink> element is required")
SourceLinkDefinitionImpl(File(path).canonicalFile.absolutePath, url, it.lineSuffix)
}
@@ -131,6 +136,7 @@ class DokkaAntTask: Task() {
perPackageOptions = antPackageOptions,
externalDocumentationLinks = antExternalDocumentationLinks.map { it.build() },
noStdlibLink = noStdlibLink,
+ noJdkLink = noJdkLink,
cacheRoot = cacheRoot,
languageVersion = languageVersion,
apiVersion = apiVersion
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
index fe945ed3..f871f406 100644
--- a/runners/cli/src/main/kotlin/cli/main.kt
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -53,6 +53,9 @@ class DokkaArguments {
@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
@@ -62,6 +65,9 @@ class DokkaArguments {
@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
+
}
@@ -106,18 +112,20 @@ object MainKt {
val classPath = arguments.classpath.split(File.pathSeparatorChar).toList()
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
+ 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,
+ noJdkLink = arguments.noJdkLink
)
val generator = DokkaGenerator(
diff --git a/runners/gradle-integration-tests/build.gradle b/runners/gradle-integration-tests/build.gradle
index a681c82e..297a175a 100644
--- a/runners/gradle-integration-tests/build.gradle
+++ b/runners/gradle-integration-tests/build.gradle
@@ -56,4 +56,5 @@ testClasses.dependsOn createClasspathManifest
test {
systemProperty "android.licenses.overwrite", project.findProperty("android.licenses.overwrite") ?: ""
+ inputs.dir(file('testData'))
} \ No newline at end of file
diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt
new file mode 100644
index 00000000..7b179e92
--- /dev/null
+++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt
@@ -0,0 +1,36 @@
+package org.jetbrains.dokka.gradle
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class TypeSafeConfigurationTest(private val testCase: TestCase) : AbstractDokkaGradleTest() {
+
+ data class TestCase(val gradleVersion: String, val kotlinVersion: String) {
+ override fun toString(): String = "Gradle $gradleVersion and Kotlin $kotlinVersion"
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun testCases() = listOf(
+ TestCase("4.0", "1.1.2"),
+ TestCase("4.5", "1.2.20"),
+ TestCase("4.10.1", "1.2.60")
+ )
+ }
+
+ @Test
+ fun test() {
+
+ testDataFolder.resolve("typeSafeConfiguration").toFile()
+ .copyRecursively(testProjectDir.root)
+
+ configure(
+ testCase.gradleVersion,
+ testCase.kotlinVersion,
+ arguments = arrayOf("help", "-s")
+ ).build()
+ }
+}
diff --git a/runners/gradle-integration-tests/testData/androidApp/build.gradle b/runners/gradle-integration-tests/testData/androidApp/build.gradle
index 59477b52..35356b90 100644
--- a/runners/gradle-integration-tests/testData/androidApp/build.gradle
+++ b/runners/gradle-integration-tests/testData/androidApp/build.gradle
@@ -3,7 +3,7 @@ buildscript {
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-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -15,7 +15,7 @@ allprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://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
index 3827b69e..f66c79e3 100644
--- a/runners/gradle-integration-tests/testData/androidApp/fileTree.txt
+++ b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt
@@ -9,6 +9,7 @@
-init-.html
index.html
on-create-options-menu.html
+ on-create.html
-kotlin-activity/
-init-.html
index.html
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle
index 59477b52..35356b90 100644
--- a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle
@@ -3,7 +3,7 @@ buildscript {
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-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -15,7 +15,7 @@ allprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://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/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle
index 59477b52..35356b90 100644
--- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle
@@ -3,7 +3,7 @@ buildscript {
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-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -15,7 +15,7 @@ allprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://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
index 5e969d8b..77b563b2 100644
--- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt
@@ -10,6 +10,7 @@
-init-.html
index.html
on-create-options-menu.html
+ on-create.html
-kotlin-activity/
-init-.html
index.html
@@ -35,6 +36,7 @@
-init-.html
index.html
on-create-options-menu.html
+ on-create.html
-kotlin-activity/
-init-.html
index.html
diff --git a/runners/gradle-integration-tests/testData/basic/build.gradle b/runners/gradle-integration-tests/testData/basic/build.gradle
index 4a259f50..a3116751 100644
--- a/runners/gradle-integration-tests/testData/basic/build.gradle
+++ b/runners/gradle-integration-tests/testData/basic/build.gradle
@@ -2,7 +2,7 @@ buildscript {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -21,7 +21,7 @@ repositories {
mavenCentral()
jcenter()
maven {
- url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
+ url "https://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/multiProjectSingleOut/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle
index 68d93e30..4f561472 100644
--- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle
+++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle
@@ -7,7 +7,7 @@ subprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -17,7 +17,7 @@ subprojects {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://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/sourcesChange/build.gradle b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle
index bc20e1cf..4627e8ef 100644
--- a/runners/gradle-integration-tests/testData/sourcesChange/build.gradle
+++ b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle
@@ -2,7 +2,7 @@ buildscript {
repositories {
mavenCentral()
jcenter()
- maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+ maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" }
maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
}
dependencies {
@@ -21,7 +21,7 @@ repositories {
mavenCentral()
jcenter()
maven {
- url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
+ url "https://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/typeSafeConfiguration/build.gradle b/runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle
new file mode 100644
index 00000000..327cead8
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle
@@ -0,0 +1,84 @@
+import org.jetbrains.dokka.*
+import org.jetbrains.dokka.gradle.*
+import org.jetbrains.kotlin.gradle.tasks.*
+
+import groovy.transform.CompileStatic
+import java.util.concurrent.Callable
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+ }
+}
+
+plugins {
+ id 'org.jetbrains.dokka'
+}
+
+apply plugin: 'kotlin'
+
+@CompileStatic
+def configureDokkaTypeSafely(DokkaTask dokka) {
+ dokka.with {
+ moduleName = "some String"
+ outputFormat = "some String"
+ outputDirectory = "some String"
+ classpath = Collections.singleton(file("someClassDir"))
+ includes = Collections.emptyList()
+ linkMappings = new ArrayList<LinkMapping>()
+ samples = Collections.emptyList()
+ jdkVersion = 6
+ sourceDirs = Collections.<File>emptyList()
+ sourceRoots = new ArrayList<SourceRoot>()
+ dokkaFatJar = file("some File")
+ includeNonPublic = false
+ skipDeprecated = false
+ skipEmptyPackages = true
+ reportUndocumented = true
+ perPackageOptions = new ArrayList<PackageOptions>()
+ impliedPlatforms = Collections.<String>emptyList()
+ externalDocumentationLinks = new ArrayList<DokkaConfiguration.ExternalDocumentationLink>()
+ noStdlibLink = false
+ cacheRoot = null as String
+ languageVersion = null as String
+ apiVersion = null as String
+ kotlinTasks(new Callable<List<Object>>() {
+ @Override
+ List<Object> call() {
+ return defaultKotlinTasks()
+ }
+ })
+ linkMapping(new Action<LinkMapping>() {
+ @Override
+ void execute(LinkMapping mapping) {
+ mapping.dir = "some String"
+ mapping.url = "some String"
+ }
+ })
+ sourceRoot(new Action<SourceRoot>() {
+ @Override
+ void execute(SourceRoot sourceRoot) {
+ sourceRoot.path = "some String"
+ }
+ })
+ packageOptions(new Action<PackageOptions>() {
+ @Override
+ void execute(PackageOptions packageOptions) {
+ packageOptions.prefix = "some String"
+ }
+ })
+ externalDocumentationLink(new Action<DokkaConfiguration.ExternalDocumentationLink.Builder>() {
+ @Override
+ void execute(DokkaConfiguration.ExternalDocumentationLink.Builder builder) {
+ builder.url = uri("some URI").toURL()
+ }
+ })
+ }
+}
+
+project.tasks.withType(DokkaTask) { dokka ->
+ configureDokkaTypeSafely(dokka)
+}
diff --git a/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle b/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle
new file mode 100644
index 00000000..be82e328
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "type-safe-configuration" \ No newline at end of file
diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle
index 661d432b..8e59a7be 100644
--- a/runners/gradle-plugin/build.gradle
+++ b/runners/gradle-plugin/build.gradle
@@ -74,7 +74,7 @@ artifacts {
}
pluginBundle {
- website = 'http://www.kotlinlang.org/'
+ website = 'https://www.kotlinlang.org/'
vcsUrl = 'https://github.com/kotlin/dokka.git'
description = 'Dokka, the Kotlin documentation tool'
tags = ['dokka', 'kotlin', 'kdoc']
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
index 5f02cd0e..f4adc1c3 100644
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -1,10 +1,12 @@
package org.jetbrains.dokka.gradle
import groovy.lang.Closure
+import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
+import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPluginConvention
@@ -30,6 +32,7 @@ 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 {
+ dokkaRuntime = project.configurations.create("dokkaRuntime")
moduleName = project.name
outputDirectory = File(project.buildDir, "dokka").absolutePath
}
@@ -80,7 +83,7 @@ open class DokkaTask : DefaultTask() {
@Input
var outputFormat: String = "html"
var outputDirectory: String = ""
-
+ var dokkaRuntime: Configuration? = null
@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()
@@ -124,6 +127,9 @@ open class DokkaTask : DefaultTask() {
@Input var noStdlibLink: Boolean = false
+ @Input
+ var noJdkLink: Boolean = false
+
@Optional @Input
var cacheRoot: String? = null
@@ -134,6 +140,9 @@ open class DokkaTask : DefaultTask() {
@Optional @Input
var apiVersion: String? = null
+ @Input
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+
@get:Internal
internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
@@ -141,14 +150,17 @@ open class DokkaTask : DefaultTask() {
private var kotlinTasksConfigurator: () -> List<Any?>? = { defaultKotlinTasks() }
private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks() }
+ fun kotlinTasks(taskSupplier: Callable<List<Any>>) {
+ kotlinTasksConfigurator = { taskSupplier.call() }
+ }
+
fun kotlinTasks(closure: Closure<Any?>) {
kotlinTasksConfigurator = { closure.call() as? List<Any?> }
}
- fun linkMapping(closure: Closure<Unit>) {
+ fun linkMapping(action: Action<LinkMapping>) {
val mapping = LinkMapping()
- closure.delegate = mapping
- closure.call()
+ action.execute(mapping)
if (mapping.path.isEmpty()) {
throw IllegalArgumentException("Link mapping should have dir")
@@ -160,33 +172,55 @@ open class DokkaTask : DefaultTask() {
linkMappings.add(mapping)
}
- fun sourceRoot(closure: Closure<Unit>) {
+ fun linkMapping(closure: Closure<Unit>) {
+ linkMapping(Action { mapping ->
+ closure.delegate = mapping
+ closure.call()
+ })
+ }
+
+ fun sourceRoot(action: Action<SourceRoot>) {
val sourceRoot = SourceRoot()
- closure.delegate = sourceRoot
- closure.call()
+ action.execute(sourceRoot)
sourceRoots.add(sourceRoot)
}
- fun packageOptions(closure: Closure<Unit>) {
+ fun sourceRoot(closure: Closure<Unit>) {
+ sourceRoot(Action { sourceRoot ->
+ closure.delegate = sourceRoot
+ closure.call()
+ })
+ }
+
+ fun packageOptions(action: Action<PackageOptions>) {
val packageOptions = PackageOptions()
- closure.delegate = packageOptions
- closure.call()
+ action.execute(packageOptions)
perPackageOptions.add(packageOptions)
}
- fun externalDocumentationLink(closure: Closure<Unit>) {
+ fun packageOptions(closure: Closure<Unit>) {
+ packageOptions(Action { packageOptions ->
+ closure.delegate = packageOptions
+ closure.call()
+ })
+ }
+
+ fun externalDocumentationLink(action: Action<DokkaConfiguration.ExternalDocumentationLink.Builder>) {
val builder = DokkaConfiguration.ExternalDocumentationLink.Builder()
- closure.delegate = builder
- closure.call()
+ action.execute(builder)
externalDocumentationLinks.add(builder.build())
}
- fun tryResolveFatJar(project: Project): File {
+ fun externalDocumentationLink(closure: Closure<Unit>) {
+ externalDocumentationLink(Action { builder ->
+ closure.delegate = builder
+ closure.call()
+ })
+ }
+
+ fun tryResolveFatJar(project: Project): Set<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()
+ dokkaRuntime!!.resolve()
} catch (e: Exception) {
project.parent?.let { tryResolveFatJar(it) } ?: throw e
}
@@ -194,11 +228,11 @@ open class DokkaTask : DefaultTask() {
fun loadFatJar() {
if (fatJarClassLoader == null) {
- val fatjar = if (dokkaFatJar is File)
- dokkaFatJar as File
+ val jars = if (dokkaFatJar is File)
+ setOf(dokkaFatJar as File)
else
tryResolveFatJar(project)
- fatJarClassLoader = URLClassLoader(arrayOf(fatjar.toURI().toURL()), ClassLoader.getSystemClassLoader().parent)
+ fatJarClassLoader = URLClassLoader(jars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent)
}
}
@@ -263,6 +297,11 @@ open class DokkaTask : DefaultTask() {
@TaskAction
fun generate() {
+ if (dokkaRuntime == null){
+ dokkaRuntime = project.configurations.getByName("dokkaRuntime")
+ }
+
+ dokkaRuntime?.defaultDependencies{ dependencies -> dependencies.add(project.dependencies.create(dokkaFatJar)) }
val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false"
System.setProperty(COLORS_ENABLED_PROPERTY, "false")
try {
@@ -287,29 +326,32 @@ open class DokkaTask : DefaultTask() {
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,
- true,
- linkMappings,
- impliedPlatforms,
- perPackageOptions,
- externalDocumentationLinks,
- noStdlibLink,
+ 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,
+ true,
+ linkMappings,
+ impliedPlatforms,
+ perPackageOptions,
+ externalDocumentationLinks,
+ noStdlibLink,
+ noJdkLink,
cacheRoot,
collectSuppressedFiles(sourceRoots),
languageVersion,
- apiVersion)
+ apiVersion,
+ collectInheritedExtensionsFromLibraries
+ )
bootstrapProxy.configure(
@@ -406,7 +448,9 @@ open class LinkMapping : Serializable, DokkaConfiguration.SourceLinkDefinition {
var dir: String
get() = path
set(value) {
- path = value
+ if (value.contains("\\"))
+ throw java.lang.IllegalArgumentException("Incorrect dir property, only Unix based path allowed.")
+ else path = value
}
override var path: String = ""
diff --git a/runners/maven-plugin/build.gradle b/runners/maven-plugin/build.gradle
index 79a8c22b..2e9d0b1b 100644
--- a/runners/maven-plugin/build.gradle
+++ b/runners/maven-plugin/build.gradle
@@ -19,7 +19,13 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
}
}
+configurations {
+ maven
+}
+
dependencies {
+ maven group: "org.apache.maven", name: 'apache-maven', version: maven_version, classifier: 'bin', ext: 'zip'
+
shadow project(":runners:fatjar")
shadow "org.apache.maven:maven-core:$maven_version"
shadow "org.apache.maven:maven-model:$maven_version"
@@ -31,50 +37,96 @@ dependencies {
shadow "com.github.olivergondza:maven-jdk-tools-wrapper:0.1"
}
+final File mavenHome = new File(buildDir, "maven-bin")
+final File mvn = new File(mavenHome, "apache-maven-$maven_version/bin/mvn")
+
+tasks.clean.doLast {
+ delete mavenHome
+}
+
+task setupMaven(type: Sync) {
+ from configurations.maven.collect{ zipTree(it) }
+ into "$buildDir/maven-bin"
+}
+
+def mavenBuildDir = "$buildDir/maven"
+
+
+sourceSets.main.resources {
+ srcDirs += "$mavenBuildDir/classes/java/main"
+ exclude "**/*.class"
+}
+
task generatePom() {
inputs.file(new File(projectDir, "pom.tpl.xml"))
- outputs.file(new File(buildDir, "pom.xml"))
+ outputs.file(new File(mavenBuildDir, "pom.xml"))
doLast {
final pomTemplate = new File(projectDir, "pom.tpl.xml")
- final pom = new File(buildDir, "pom.xml")
+ final pom = new File(mavenBuildDir, "pom.xml")
+ pom.parentFile.mkdirs()
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 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 syncKotlinClasses(type: Sync, dependsOn: compileKotlin) {
+ from "$buildDir/classes/kotlin"
+ into "$mavenBuildDir/classes/java"
+
+ preserve {
+ include '**/*.class'
}
}
-task pluginDescriptor(type: CrossPlatformExec) {
- workingDir buildDir
- commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:descriptor'
+task syncJavaClasses(type: Sync, dependsOn: compileJava) {
+ from "$buildDir/classes/java"
+ into "$mavenBuildDir/classes/java"
- dependsOn mergeClassOutputs
+ preserve {
+ include '**/*.class'
+ }
}
-task helpMojo(type: CrossPlatformExec) {
- workingDir buildDir
+task helpMojo(type: CrossPlatformExec, dependsOn: setupMaven) {
+ workingDir mavenBuildDir
commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:helpmojo'
- dependsOn mergeClassOutputs
+ dependsOn syncKotlinClasses
}
+
+task pluginDescriptor(type: CrossPlatformExec, dependsOn: setupMaven) {
+ workingDir mavenBuildDir
+ commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:descriptor'
+
+ dependsOn syncJavaClasses
+}
+
+
+//mergeClassOutputs.dependsOn compileKotlin
+//helpMojo.dependsOn mergeClassOutputs
helpMojo.dependsOn generatePom
-sourceSets.main.java.srcDir("$buildDir/generated-sources/plugin")
+sourceSets.main.java.srcDir("$buildDir/maven/generated-sources/plugin")
compileJava.dependsOn helpMojo
+processResources.dependsOn pluginDescriptor
pluginDescriptor.dependsOn generatePom
diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
index 09da90c6..324703a0 100644
--- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
+++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt
@@ -4,6 +4,7 @@ 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.plugin.MojoExecutionException
import org.apache.maven.plugins.annotations.*
import org.apache.maven.project.MavenProject
import org.apache.maven.project.MavenProjectHelper
@@ -104,6 +105,9 @@ abstract class AbstractDokkaMojo : AbstractMojo() {
@Parameter(defaultValue = "false")
var noStdlibLink: Boolean = false
+ @Parameter(defaultValue = "false")
+ var noJdkLink: Boolean = false
+
@Parameter
var cacheRoot: String? = null
@@ -122,6 +126,12 @@ abstract class AbstractDokkaMojo : AbstractMojo() {
return
}
+ sourceLinks.forEach {
+ if (it.dir.contains("\\")) {
+ throw MojoExecutionException("Incorrect dir property, only Unix based path allowed.")
+ }
+ }
+
val gen = DokkaGenerator(
MavenDokkaLogger(log),
classpath,
@@ -139,6 +149,7 @@ abstract class AbstractDokkaMojo : AbstractMojo() {
perPackageOptions = perPackageOptions,
externalDocumentationLinks = externalDocumentationLinks.map { it.build() },
noStdlibLink = noStdlibLink,
+ noJdkLink = noJdkLink,
cacheRoot = cacheRoot,
languageVersion = languageVersion,
apiVersion = apiVersion
@@ -196,7 +207,7 @@ class DokkaJavadocJarMojo : AbstractDokkaMojo() {
/**
* The archive configuration to use.
- * See [Maven Archiver Reference](http://maven.apache.org/shared/maven-archiver/index.html)
+ * See [Maven Archiver Reference](https://maven.apache.org/shared/maven-archiver/index.html)
*/
@Parameter
private val archive = MavenArchiveConfiguration()
diff --git a/settings.gradle b/settings.gradle
index 4dcfd255..ef5f9869 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,5 @@
+rootProject.name = "dokka"
+
include 'core',
'integration',
'runners:fatjar',