diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-11-28 15:58:10 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-11-28 15:58:10 +0000 |
commit | ecf8061396ac2d1bb0f75c37d48caf53c3be1ce4 (patch) | |
tree | 4058cd853f36166d424eee7e69e95ad3125d1189 | |
parent | 7f79c3b49be99f3d45d099a30b78faa6d96e93b2 (diff) | |
parent | 77665b03848c6574ff3e40ec875c347d5eb41a8f (diff) | |
download | dokka-androidx-versionedparcelable-release.tar.gz |
Snap for 11149604 from 77665b03848c6574ff3e40ec875c347d5eb41a8f to androidx-versionedparcelable-releaseandroidx-versionedparcelable-release
Change-Id: Ia11dc58bb589d4d20dd8983fae63265d878b9752
874 files changed, 38447 insertions, 452 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8227df3c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +# mac +.DS_Store + +doc +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +### Java template +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/shelf +.idea/jsLibraryMappings.xml +.idea/runConfigurations +.idea/caches + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +## File-based project format: +*.iws +*.iml + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Gradle template +.gradle +build/ +runners/android-gradle-plugin/out/ +runners/ant/out/ +runners/gradle-integration-tests/out/ +runners/gradle-plugin/out/ +core/out/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +!lib/*.jar + +local.properties +android.local.properties + +!runners/gradle-integration-tests/testData/basic/classDir/**/*.class + +/samples diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 000000000..c8bb43f20 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +dokka
\ No newline at end of file diff --git a/.idea/artifacts/dokka_jar.xml b/.idea/artifacts/dokka_jar.xml new file mode 100644 index 000000000..5998df7be --- /dev/null +++ b/.idea/artifacts/dokka_jar.xml @@ -0,0 +1,9 @@ +<component name="ArtifactManager"> + <artifact type="jar" name="dokka:jar"> + <output-path>$PROJECT_DIR$/out/artifacts/</output-path> + <root id="archive" name="dokka.jar"> + <element id="module-output" name="dokka" /> + <element id="module-output" name="ant" /> + </root> + </artifact> +</component>
\ No newline at end of file diff --git a/.idea/artifacts/dokka_zip.xml b/.idea/artifacts/dokka_zip.xml new file mode 100644 index 000000000..b636e0972 --- /dev/null +++ b/.idea/artifacts/dokka_zip.xml @@ -0,0 +1,27 @@ +<component name="ArtifactManager"> + <artifact name="dokka:zip"> + <output-path>$PROJECT_DIR$/out/artifacts</output-path> + <root id="root"> + <element id="archive" name="dokka.zip"> + <element id="directory" name="lib"> + <element id="artifact" artifact-name="dokka:jar" /> + <element id="library" level="project" name="trove4j" /> + <element id="library" level="project" name="jps-model" /> + <element id="library" level="project" name="asm" /> + <element id="library" level="project" name="protobuf" /> + <element id="library" level="project" name="picocontainer" /> + <element id="library" level="project" name="intellij-core-analysis" /> + <element id="library" level="project" name="markdown" /> + <element id="library" level="project" name="kotlin-for-upsource" /> + <element id="library" level="project" name="jsoup" /> + <element id="library" level="project" name="jdom" /> + <element id="library" level="project" name="ant-1.9.4" /> + <element id="library" level="project" name="cli-parser" /> + <element id="library" level="project" name="guava" /> + <element id="library" level="project" name="com.google.inject:guice:4.0" /> + <element id="artifact" artifact-name="javadoc:jar" /> + </element> + </element> + </root> + </artifact> +</component>
\ No newline at end of file diff --git a/.idea/artifacts/javadoc_jar.xml b/.idea/artifacts/javadoc_jar.xml new file mode 100644 index 000000000..0f0eee5b4 --- /dev/null +++ b/.idea/artifacts/javadoc_jar.xml @@ -0,0 +1,8 @@ +<component name="ArtifactManager"> + <artifact type="jar" name="javadoc:jar"> + <output-path>$PROJECT_DIR$/out/artifacts/javadoc_jar</output-path> + <root id="archive" name="javadoc.jar"> + <element id="module-output" name="javadoc" /> + </root> + </artifact> +</component>
\ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..ba49a93ea --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,24 @@ +<component name="ProjectCodeStyleConfiguration"> + <code_scheme name="Project" version="173"> + <JetCodeStyleSettings> + <option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="false" /> + <option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="false" /> + <option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="false" /> + <option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="false" /> + <option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="false" /> + <option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="false" /> + <option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="1" /> + </JetCodeStyleSettings> + <codeStyleSettings language="kotlin"> + <option name="CALL_PARAMETERS_WRAP" value="5" /> + <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> + <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> + <option name="METHOD_PARAMETERS_WRAP" value="5" /> + <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> + <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> + <option name="EXTENDS_LIST_WRAP" value="1" /> + <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> + <option name="ASSIGNMENT_WRAP" value="1" /> + </codeStyleSettings> + </code_scheme> +</component>
\ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ +<component name="ProjectCodeStyleConfiguration"> + <state> + <option name="USE_PER_PROJECT_SETTINGS" value="true" /> + </state> +</component>
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..4694bee54 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <wildcardResourcePatterns> + <entry name="!?*.java" /> + <entry name="!?*.form" /> + <entry name="!?*.class" /> + <entry name="!?*.groovy" /> + <entry name="!?*.scala" /> + <entry name="!?*.flex" /> + <entry name="!?*.kt" /> + <entry name="!?*.clj" /> + </wildcardResourcePatterns> + <bytecodeTargetLevel> + <module name="android-gradle-plugin_main" target="1.8" /> + <module name="android-gradle-plugin_test" target="1.8" /> + <module name="ant_main" target="1.8" /> + <module name="ant_test" target="1.8" /> + <module name="buildSrc_main" target="1.8" /> + <module name="buildSrc_test" target="1.8" /> + <module name="cli_main" target="1.8" /> + <module name="cli_test" target="1.8" /> + <module name="core_main" target="1.8" /> + <module name="core_test" target="1.8" /> + <module name="dokka.buildSrc.main" target="1.8" /> + <module name="dokka.buildSrc.test" target="1.8" /> + <module name="dokka.core.main" target="1.8" /> + <module name="dokka.core.test" target="1.8" /> + <module name="dokka.integration.main" target="1.8" /> + <module name="dokka.integration.test" target="1.8" /> + <module name="dokka.runners.android-gradle-plugin.main" target="1.8" /> + <module name="dokka.runners.android-gradle-plugin.test" target="1.8" /> + <module name="dokka.runners.ant.main" target="1.8" /> + <module name="dokka.runners.ant.test" target="1.8" /> + <module name="dokka.runners.cli.main" target="1.8" /> + <module name="dokka.runners.cli.test" target="1.8" /> + <module name="dokka.runners.fatjar.main" target="1.8" /> + <module name="dokka.runners.fatjar.test" target="1.8" /> + <module name="dokka.runners.gradle-integration-tests.main" target="1.8" /> + <module name="dokka.runners.gradle-integration-tests.test" target="1.8" /> + <module name="dokka.runners.gradle-plugin.main" target="1.8" /> + <module name="dokka.runners.gradle-plugin.test" target="1.8" /> + <module name="fatjar_main" target="1.8" /> + <module name="fatjar_test" target="1.8" /> + <module name="gradle-integration-tests_main" target="1.8" /> + <module name="gradle-integration-tests_test" target="1.8" /> + <module name="gradle-plugin_main" target="1.8" /> + <module name="gradle-plugin_test" target="1.8" /> + <module name="integration_main" target="1.8" /> + <module name="integration_test" target="1.8" /> + <module name="maven-plugin_main" target="1.8" /> + <module name="maven-plugin_test" target="1.8" /> + </bytecodeTargetLevel> + </component> +</project>
\ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 000000000..f4aa08e3a --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="CopyrightManager"> + <settings default="" /> +</component>
\ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..f75895965 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"> + <file url="PROJECT" charset="UTF-8" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 000000000..06bda8c9c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,7 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="MemberVisibilityCanPrivate" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> + <inspection_tool class="PackageDirectoryMismatch" enabled="false" level="WARNING" enabled_by_default="false" /> + </profile> +</component>
\ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 000000000..d747fa6cc --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="RemoteRepositoriesConfiguration"> + <remote-repository> + <option name="id" value="central" /> + <option name="name" value="Maven Central repository" /> + <option name="url" value="https://repo1.maven.org/maven2" /> + </remote-repository> + <remote-repository> + <option name="id" value="jboss.community" /> + <option name="name" value="JBoss Community repository" /> + <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven5" /> + <option name="name" value="maven5" /> + <option name="url" value="https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/1.3.20-dev-564/maven" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven6" /> + <option name="name" value="maven6" /> + <option name="url" value="https://dl.bintray.com/kotlin/kotlinx.html" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenRepo" /> + <option name="name" value="MavenRepo" /> + <option name="url" value="https://repo.maven.apache.org/maven2/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$USER_HOME$/.m2/repository/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://dl.bintray.com/jetbrains/markdown" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven8" /> + <option name="name" value="maven8" /> + <option name="url" value="https://www.jetbrains.com/intellij-repository/releases" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven3" /> + <option name="name" value="maven3" /> + <option name="url" value="https://dl.bintray.com/kotlin/kotlin-dev" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven4" /> + <option name="name" value="maven4" /> + <option name="url" value="https://jitpack.io" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven2" /> + <option name="name" value="maven2" /> + <option name="url" value="http://dl.bintray.com/kotlin/kotlin-eap" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven7" /> + <option name="name" value="maven7" /> + <option name="url" value="https://www.jetbrains.com/intellij-repository/snapshots" /> + </remote-repository> + <remote-repository> + <option name="id" value="BintrayJCenter" /> + <option name="name" value="BintrayJCenter" /> + <option name="url" value="https://jcenter.bintray.com/" /> + </remote-repository> + </component> +</project>
\ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 000000000..38d23597c --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Kotlin2JsCompilerArguments"> + <option name="sourceMapEmbedSources" /> + <option name="sourceMapPrefix" /> + </component> + <component name="KotlinCommonCompilerArguments"> + <option name="apiVersion" value="1.1" /> + <option name="languageVersion" value="1.2" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..537aa8e47 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ASMPluginConfiguration"> + <asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" /> + <groovy codeStyle="LEGACY" /> + </component> + <component name="EntryPointsManager"> + <list size="5"> + <item index="0" class="java.lang.String" itemvalue="org.apache.maven.plugins.annotations.Mojo" /> + <item index="1" class="java.lang.String" itemvalue="org.gradle.api.tasks.InputFiles" /> + <item index="2" class="java.lang.String" itemvalue="org.gradle.api.tasks.OutputDirectory" /> + <item index="3" class="java.lang.String" itemvalue="org.gradle.api.tasks.TaskAction" /> + <item index="4" class="java.lang.String" itemvalue="org.junit.BeforeClass" /> + </list> + </component> + <component name="FrameworkDetectionExcludesConfiguration"> + <file type="web" url="file://$PROJECT_DIR$/dokka-fatjar" /> + <file type="web" url="file://$PROJECT_DIR$/gradle-plugin" /> + <file type="web" url="file://$PROJECT_DIR$/javadoc" /> + </component> + <component name="MavenProjectsManager"> + <option name="originalFiles"> + <list> + <option value="$PROJECT_DIR$/maven-plugin/pom.xml" /> + </list> + </option> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..772f03fbc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin.iml" group="runners/android-gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_main.iml" group="runners/android-gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/android-gradle-plugin_test.iml" group="runners/android-gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant.iml" group="runners/ant" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant_main.iml" group="runners/ant" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/ant_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/ant_test.iml" group="runners/ant" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc_main.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc_main.iml" group="buildSrc" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/buildSrc_test.iml" filepath="$PROJECT_DIR$/.idea/modules/buildSrc_test.iml" group="buildSrc" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli.iml" group="runners/cli" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli_main.iml" group="runners/cli" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/cli_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/cli_test.iml" group="runners/cli" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core.iml" group="core" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_main.iml" group="core" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_test.iml" group="core" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.main.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/dokka.buildSrc.test.iml" filepath="$PROJECT_DIR$/.idea/modules/dokka.buildSrc.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/core/dokka.core.test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/dokka.core.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.main.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/dokka.integration.test.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/dokka.integration.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/dokka.runners.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/dokka.runners.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/android-gradle-plugin/dokka.runners.android-gradle-plugin.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/ant/dokka.runners.ant.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/cli/dokka.runners.cli.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/dokka.runners.fatjar.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/dokka.runners.gradle-integration-tests.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/dokka.runners.gradle-plugin.test.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar.iml" group="runners/fatjar" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_main.iml" group="runners/fatjar" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/fatjar/fatjar_test.iml" group="runners/fatjar" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests.iml" group="runners/gradle-integration-tests" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_main.iml" group="runners/gradle-integration-tests" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-integration-tests/gradle-integration-tests_test.iml" group="runners/gradle-integration-tests" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin.iml" group="runners/gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_main.iml" group="runners/gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/gradle-plugin/gradle-plugin_test.iml" group="runners/gradle-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration.iml" group="integration" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration_main.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration_main.iml" group="integration" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/integration/integration_test.iml" filepath="$PROJECT_DIR$/.idea/modules/integration/integration_test.iml" group="integration" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin.iml" group="runners/maven-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_main.iml" group="runners/maven-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/maven-plugin/maven-plugin_test.iml" group="runners/maven-plugin" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/org.jetbrains.dokka.dokka.iml" filepath="$PROJECT_DIR$/.idea/modules/org.jetbrains.dokka.dokka.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/modules/runners/runners.iml" filepath="$PROJECT_DIR$/.idea/modules/runners/runners.iml" group="runners" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 000000000..922003b84 --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ +<component name="DependencyValidationManager"> + <state> + <option name="SKIP_IMPORT_STATEMENTS" value="false" /> + </state> +</component>
\ No newline at end of file diff --git a/Android.bp b/Android.bp index dec37b241..dbfdb5030 100644 --- a/Android.bp +++ b/Android.bp @@ -12,9 +12,44 @@ // See the License for the specific language governing permissions and // limitations under the License. -java_import { +package { + default_applicable_licenses: ["external_dokka_license"], +} + +// Added automatically by a large-scale-change +// +// large-scale-change included anything that looked like it might be a license +// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. +// +// Please consider removing redundant or irrelevant files from 'license_text:'. +// See: http://go/android-license-faq +license { + name: "external_dokka_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "LICENSE", + "NOTICE", + ], +} + +java_binary_host { name: "dokka", - host_supported: true, - jars: ["maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar"], - installable: true, + srcs: [ + "core/src/main/**/*.kt", + "runners/cli/**/*.kt", + "integration/**/*.kt", + "buildSrc/**/*.groovy" + ], + static_libs: [ + "dokka-tools-common-m2-deps", + ], + // Pin to Java 8 since dokka doesn't compile with the Java 9 module system + // (it references packages under com.sun.tools.doclets which are not + // exported from the jdk.javadoc module) (see b/140097603): + java_version: "1.8", + use_tools_jar: true, + java_resource_dirs: ["core/src/main/resources"], } diff --git a/METADATA b/METADATA new file mode 100644 index 000000000..dfb86523a --- /dev/null +++ b/METADATA @@ -0,0 +1,16 @@ +name: "dokka" +description: + "Dokka is a documentation engine for Kotlin and Java." + "For version information, please see the README.android file" +third_party { + url { + type: GIT + value: "https://github.com/Kotlin/dokka.git" + } + license_type: NOTICE + last_upgrade_date { + year: 2019 + month: 7 + day: 24 + } +} @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. @@ -1,2 +1,6 @@ -tiem@google.com +asfalcone@google.com aurimas@google.com +jeffrygaston@google.com +lpf@google.com +nickanthony@google.com +tiem@google.com diff --git a/README.android b/README.android new file mode 100644 index 000000000..199cbcf00 --- /dev/null +++ b/README.android @@ -0,0 +1,234 @@ +URL: https://github.com/Kotlin/dokka +License: Apache 2 +Description: "Dokka is a documentation engine for Kotlin and Java." + +Changes in this fork +-------------------- +This fork of Dokka contains changes for generating reference documentation on +developer.android.com. + +This repository was previously hosted at https://github.com/google/dokka, and +moved to AOSP as of commit 4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad. It was +initally forked from https://github.com/Kotlin/dokka after commit +7a066afb0ba47b5b57e98a5d821a0cbe97322670. + +Development took place on the "devsite-with-java-layout-html" on both the +upstream Dokka repository and the fork on GitHub. For a complete list of +changes, see + +https://github.com/google/dokka/compare/7a066afb0ba47b5b57e98a5d821a0cbe97322670...4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad + +which is echoed here: + +commit 4ff3d4153ca344398bffcdaaa28f1a1f6e76f6ad +Author: Gautam Ajjarapu <gautam@ajjarapu.com> +Date: Fri Jun 14 13:54:08 2019 -0700 + + Automate Downloading of Code Samples (#58) + +commit 47cc6dcfb4b39f0ecbb5aba53c34aa7d54a1a01e +Author: Gautam Ajjarapu <ajjarapu@google.com> +Date: Fri Jun 7 16:03:00 2019 -0700 + + API Info Alignment Fix (#56) + + * alignment fix + + * Removed temporary solution of using method parameter inHeader because + changing api-info div solved spacing problem + +commit b2821b7a89ce501b9e8edea26b2defa03dcc6bc8 +Author: mvivo <mvivo@google.com> +Date: Tue May 21 15:53:43 2019 +0200 + + Adds API info to classes + +commit 04c35131eb4df0899430308c203520627deab0b4 +Author: Tiem Song <tiem@google.com> +Date: Fri Apr 26 12:36:59 2019 -0700 + + Update Dokka to version 0.9.17-g002 + +commit d0449587ad7e380846c078ecc3714ce3a326448a +Author: Tiem Song <tiem@google.com> +Date: Fri Apr 26 11:07:17 2019 -0700 + + Process rowspan + + This is a follow up PR to #45 + +commit d8d07776f790f6be848c6ce6a085dbd00c52ea43 +Author: Manuel Vivo <manuelvicnt@gmail.com> +Date: Fri Apr 26 19:23:16 2019 +0200 + + Process HTML code in comments (#45) + + * Handle Special Reference for callouts + + * Process HTML code in comments + + * Adds colspan to td & th + +commit f76cba219c2fcc051f4f19b72df58d14f3c542fc +Author: Manuel Vivo <mvivo@google.com> +Date: Wed Apr 24 15:57:39 2019 +0200 + + Processes @sample in Java documentation + +commit d0bda76a4d07d467594f3fed4359faab74e51608 +Author: mvivo <mvivo@google.com> +Date: Thu Apr 25 15:07:32 2019 +0200 + + Adds devsite-heading HTML tag to table headers + +commit 8fcffad51ef034e38d100693fac755cae0766d73 +Author: Manuel Vivo <manuelvicnt@gmail.com> +Date: Thu Apr 25 07:38:21 2019 +0200 + + Display deprecated in a callout and class index (#41) + + * Display deprecated callout in classes and enums + + * Follow Google style reference guide + + * Make deprecation message generic + + * Add deprecation message to class index + + * Add HtmlFormatTest back + + * Extends wording to more classes + +commit 1f903e7ebe2e29bcf6773615525d991f49707b5d +Author: mvivo <mvivo@google.com> +Date: Mon Apr 22 16:19:44 2019 +0200 + + Adds enum table to class summary + +commit 3caaa4425bd146b07077d6e040776fd1a5ba6933 +Author: Manuel Vivo <mvivo@google.com> +Date: Wed Apr 10 20:42:34 2019 +0200 + + Display XML attributes with a short description + +commit 200d6131c1aea49db8d5a9ed0a120ab46834da37 +Author: mvivo <mvivo@google.com> +Date: Wed Apr 10 15:26:06 2019 +0200 + + Follow Google style reference + +commit e4ecf324642e0b7a359aa163848df6996ec7a57f +Author: Manuel Vivo <mvivo@google.com> +Date: Tue Apr 9 19:34:01 2019 +0200 + + Handle Special Reference for callouts + +commit 6c41c5db202ddda907e79a7a63f7c306c81636a3 +Author: Tiem Song <tiem@google.com> +Date: Thu Apr 11 09:17:20 2019 -0700 + + Update Dokka to version 0.9.17-g001 + + This release also uses the new naming convention, using a -gXXX suffix + instead of a -g<Date> suffix. + +commit 83b513effdb12e74f5560a464c266b1965bf44c2 +Author: mvivo <mvivo@google.com> +Date: Wed Apr 10 16:19:31 2019 +0200 + + Add tests to CodeNode + +commit f9e69f02ae6704ec797b390b178886c4d53bacb2 +Author: mvivo <mvivo@google.com> +Date: Tue Apr 9 16:29:23 2019 +0200 + + Add Java multiline code + +commit 47f3ab52ba16b72ed346db82dd1dd36de23367af +Author: Manuel Vivo <mvivo@google.com> +Date: Mon Apr 8 20:27:04 2019 +0200 + + Remove deprecated version in classes + +commit 9e26ab39a86f18c919257763733ec7a36080065b +Author: Manuel Vivo <mvivo@google.com> +Date: Mon Apr 8 20:20:02 2019 +0200 + + Add description to XML attributes + +commit 7c70db208725d6442b3739a42cc4d7351dde48d9 +Author: Tiem Song <tiem@google.com> +Date: Fri Apr 5 10:04:47 2019 -0700 + + Rename minApiLevel method + + This method was originally created to fetch the API level for minimum API + usage. The logic of the method itself doesn't really pertain to minimum or + deprecation - it's just finding a Doc Tag from the data elements. Thus, + this method should be renamed apiLevel() or something similar, with the + "min" part removed to avoid confusion. + + Bug: 129726096 + +commit b64371e2845b0a05dcf2f50a383bd043fc750384 +Author: Tiem Song <tiem@google.com> +Date: Thu Apr 4 16:19:06 2019 -0700 + + Update package for StringExtensions + + This is to fix issues when developing on a case sensitive file system / + MacOS. This is a continuation of + https://github.com/google/dokka/pull/29. + +commit 7baef1414f61adcd77963581bffcd0f80d820059 +Author: Manuel Vivo <mvivo@google.com> +Date: Wed Apr 3 20:09:15 2019 +0200 + + Fix tests broken by pull 32 + +commit daf718e858242a82b42d3cecd9ec6d23e680f9da +Author: Manuel Vivo <mvivo@google.com> +Date: Wed Apr 3 11:39:44 2019 +0200 + + Strikethrough words are inline + +commit 67b37de06ed23105a4f78957f1a99654459028b9 +Author: Manuel Vivo <manuelvicnt@gmail.com> +Date: Wed Apr 3 19:00:09 2019 +0200 + + List functions in alphabetical order (#32) + + * List functions in alphabetical order + + * Order other members alphabetically + +commit 97871cf42bf724645eb2ed4d1dd16304083e44cd +Author: Manuel Vivo <manuelvicnt@gmail.com> +Date: Tue Apr 2 22:15:22 2019 +0200 + + Show summary on deprecated methods (#31) + +commit b5e480e1c087b5a5307a9176bb2835ebbebee8b0 +Author: Manuel Vivo <manuelvicnt@gmail.com> +Date: Tue Apr 2 22:12:56 2019 +0200 + + Process deprecatedSince annotation (#30) + + * Process deprecatedSince annotation + + * Add info to class + + * Capitalize wording + +commit f4f7e6fa673fe13fff68dd1bf3c005bf9d94875c +Author: mvivo <mvivo@google.com> +Date: Fri Mar 29 12:20:59 2019 +0100 + + Improve Java enums documentation + +commit c1c86b92c15b97e7aec41ed9892aa6965974d66f +Author: Tiem Song <tiem@google.com> +Date: Fri Mar 29 10:45:06 2019 -0700 + + Move StringExtensions file to Utilities directory + diff --git a/README.md b/README.md new file mode 100644 index 000000000..b4b83eeae --- /dev/null +++ b/README.md @@ -0,0 +1,483 @@ +dokka +===== + +**_Note_: This is Google's fork of Dokka, customized for [Android API reference docs](https://developer.android.com/reference/) +on [developer.android.com](https://developer.android.com/) and other Google products.** + +Dokka is a documentation engine for Kotlin, performing the same function as javadoc for Java. +Just like Kotlin itself, Dokka fully supports mixed-language Java/Kotlin projects. It understands +standard Javadoc comments in Java files and [KDoc comments](https://kotlinlang.org/docs/reference/kotlin-doc.html) in Kotlin files, +and can generate documentation in multiple formats including standard Javadoc, HTML and Markdown. + +## Using Dokka + +### Using the Gradle plugin + +```groovy +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" + } +} + +apply plugin: 'org.jetbrains.dokka' +``` + +The plugin adds a task named "dokka" to the project. + +Minimal dokka configuration: + +```groovy +dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" +} +``` + +[Output formats](#output_formats) + +The available configuration options are shown below: + +```groovy +dokka { + moduleName = 'data' + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" + + // These tasks will be used to determine source directories and classpath + kotlinTasks { + defaultKotlinTasks() + [':some:otherCompileKotlin', project("another").compileKotlin] + } + + // List of files with module and package documentation + // http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation + includes = ['packages.md', 'extra.md'] + + // The list of files or directories containing sample code (referenced with @sample tags) + samples = ['samples/basic.kt', 'samples/advanced.kt'] + + jdkVersion = 6 // Used for linking to JDK + + // Use default or set to custom path to cache directory + // to enable package-list caching + // When set to default, caches stored in $USER_HOME/.cache/dokka + cacheRoot = 'default' + + // Use to include or exclude non public members. + includeNonPublic = false + + // Do not output deprecated members. Applies globally, can be overridden by packageOptions + skipDeprecated = false + + // Emit warnings about not documented members. Applies globally, also can be overridden by packageOptions + reportUndocumented = true + + skipEmptyPackages = true // Do not create index pages for empty packages + + impliedPlatforms = ["JVM"] // See platforms section of documentation + + // Manual adding files to classpath + // This property not overrides classpath collected from kotlinTasks but appends to it + classpath = [new File("$buildDir/other.jar")] + + // By default, sourceRoots is taken from kotlinTasks, following roots will be appended to it + // Short form sourceRoots + sourceDirs = files('src/main/kotlin') + + // By default, sourceRoots is taken from kotlinTasks, following roots will be appended to it + // Full form sourceRoot declaration + // Repeat for multiple sourceRoots + sourceRoot { + // Path to source root + path = "src" + // See platforms section of documentation + platforms = ["JVM"] + } + + // Specifies the location of the project source code on the Web. + // If provided, Dokka generates "source" links for each declaration. + // Repeat for multiple mappings + linkMapping { + // Source directory + dir = "src/main/kotlin" + + // URL showing where the source code can be accessed through the web browser + url = "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin" + + // Suffix which is used to append the line number to the URL. Use #L for GitHub + suffix = "#L" + } + + // No default documentation link to kotlin-stdlib + noStdlibLink = false + + // Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka) + // Repeat for multiple links + externalDocumentationLink { + // Root URL of the generated documentation to link with. The trailing slash is required! + url = new URL("https://example.com/docs/") + + // If package-list file located in non-standard location + // packageListUrl = new URL("file:///home/user/localdocs/package-list") + } + + // Allows to customize documentation generation options on a per-package basis + // Repeat for multiple packageOptions + packageOptions { + prefix = "kotlin" // will match kotlin and all sub-packages of it + // All options are optional, default values are below: + skipDeprecated = false + reportUndocumented = true // Emit warnings about not documented members + includeNonPublic = false + } + // Suppress a package + packageOptions { + prefix = "kotlin.internal" // will match kotlin.internal and all sub-packages of it + suppress = true + } +} +``` + +To generate the documentation, use the `dokka` Gradle task: + +```bash +./gradlew dokka +``` + +More dokka tasks can be added to a project like this: + +```groovy +task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) { + outputFormat = 'javadoc' + outputDirectory = "$buildDir/javadoc" +} +``` + +Please see the [Dokka Gradle example project](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/dokka-gradle-example) for an example. + +#### Android + +If you are using Android there is a separate Gradle plugin. Just make sure you apply the plugin after +`com.android.library` and `kotlin-android`. + +```groovy +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}" + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'org.jetbrains.dokka-android' +``` + +### Using the Maven plugin + +The Maven plugin is available in JCenter. You need to add the JCenter repository to the list of plugin repositories if it's not there: + +```xml +<pluginRepositories> + <pluginRepository> + <id>jcenter</id> + <name>JCenter</name> + <url>https://jcenter.bintray.com/</url> + </pluginRepository> +</pluginRepositories> +``` + +Minimal Maven configuration is + +```xml +<plugin> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>dokka-maven-plugin</artifactId> + <version>${dokka.version}</version> + <executions> + <execution> + <phase>pre-site</phase> + <goals> + <goal>dokka</goal> + </goals> + </execution> + </executions> +</plugin> +``` + +By default files will be generated in `target/dokka`. + +The following goals are provided by the plugin: + + * `dokka:dokka` - generate HTML documentation in Dokka format (showing declarations in Kotlin syntax); + * `dokka:javadoc` - generate HTML documentation in JavaDoc format (showing declarations in Java syntax); + * `dokka:javadocJar` - generate a .jar file with JavaDoc format documentation. + +The available configuration options are shown below: + +```xml +<plugin> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>dokka-maven-plugin</artifactId> + <version>${dokka.version}</version> + <executions> + <execution> + <phase>pre-site</phase> + <goals> + <goal>dokka</goal> + </goals> + </execution> + </executions> + <configuration> + + <!-- Set to true to skip dokka task, default: false --> + <skip>false</skip> + + <!-- Default: ${project.artifactId} --> + <moduleName>data</moduleName> + <!-- See list of possible formats below --> + <outputFormat>html</outputFormat> + <!-- Default: ${project.basedir}/target/dokka --> + <outputDir>some/out/dir</outputDir> + + <!-- Use default or set to custom path to cache directory to enable package-list caching. --> + <!-- When set to default, caches stored in $USER_HOME/.cache/dokka --> + <cacheRoot>default</cacheRoot> + + <!-- List of '.md' files with package and module docs --> + <!-- http://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation --> + <includes> + <file>packages.md</file> + <file>extra.md</file> + </includes> + + <!-- List of sample roots --> + <samplesDirs> + <dir>src/test/samples</dir> + </samplesDirs> + + <!-- Used for linking to JDK, default: 6 --> + <jdkVersion>6</jdkVersion> + + <!-- Do not output deprecated members, applies globally, can be overridden by packageOptions --> + <skipDeprecated>false</skipDeprecated> + <!-- Emit warnings about not documented members, applies globally, also can be overridden by packageOptions --> + <reportNotDocumented>true</reportNotDocumented> + <!-- Do not create index pages for empty packages --> + <skipEmptyPackages>true</skipEmptyPackages> + + <!-- See platforms section of documentation --> + <impliedPlatforms> + <platform>JVM</platform> + </impliedPlatforms> + + <!-- Short form list of sourceRoots, by default, set to ${project.compileSourceRoots} --> + <sourceDirectories> + <dir>src/main/kotlin</dir> + </sourceDirectories> + + <!-- Full form list of sourceRoots --> + <sourceRoots> + <root> + <path>src/main/kotlin</path> + <!-- See platforms section of documentation --> + <platforms>JVM</platforms> + </root> + </sourceRoots> + + <!-- Specifies the location of the project source code on the Web. If provided, Dokka generates "source" links + for each declaration. --> + <sourceLinks> + <link> + <!-- Source directory --> + <dir>${project.basedir}/src/main/kotlin</dir> + <!-- URL showing where the source code can be accessed through the web browser --> + <url>http://github.com/me/myrepo</url> + <!--Suffix which is used to append the line number to the URL. Use #L for GitHub --> + <urlSuffix>#L</urlSuffix> + </link> + </sourceLinks> + + <!-- No default documentation link to kotlin-stdlib --> + <noStdlibLink>false</noStdlibLink> + + <!-- Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka) --> + <externalDocumentationLinks> + <link> + <!-- Root URL of the generated documentation to link with. The trailing slash is required! --> + <url>https://example.com/docs/</url> + <!-- If package-list file located in non-standard location --> + <!-- <packageListUrl>file:///home/user/localdocs/package-list</packageListUrl> --> + </link> + </externalDocumentationLinks> + + <!-- Allows to customize documentation generation options on a per-package basis --> + <perPackageOptions> + <packageOptions> + <!-- Will match kotlin and all sub-packages of it --> + <prefix>kotlin</prefix> + + <!-- All options are optional, default values are below: --> + <skipDeprecated>false</skipDeprecated> + <!-- Emit warnings about not documented members --> + <reportUndocumented>true</reportUndocumented> + <includeNonPublic>false</includeNonPublic> + </packageOptions> + </perPackageOptions> + </configuration> +</plugin> +``` + +Please see the [Dokka Maven example project](https://github.com/JetBrains/kotlin-examples/tree/master/maven/dokka-maven-example) for an example. + +[Output formats](#output_formats) + +### Using the Ant task + +The Ant task definition is also contained in the dokka-fatjar.jar referenced above. Here's an example of using it: + +```xml +<project name="Dokka" default="document"> + <typedef resource="dokka-antlib.xml" classpath="dokka-fatjar.jar"/> + + <target name="document"> + <dokka src="src" outputdir="doc" modulename="myproject"/> + </target> +</project> +``` + +The Ant task supports the following attributes: + + * `outputDir` - the output directory where the documentation is generated + * `outputFormat` - the output format (see the list of supported formats above) + * `classpath` - list of directories or .jar files to include in the classpath (used for resolving references) + * `samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) + * `moduleName` - the name of the module being documented (used as the root directory of the generated documentation) + * `include` - names of files containing the documentation for the module and individual packages + * `skipDeprecated` - if set, deprecated elements are not included in the generated documentation + * `jdkVersion` - version for linking to JDK + * `impliedPlatforms` - See [platforms](#platforms) section + * `<sourceRoot path="src" platforms="JVM" />` - analogue of src, but allows to specify [platforms](#platforms) + * `<packageOptions prefix="kotlin" includeNonPublic="false" reportUndocumented="true" skipDeprecated="false"/>` - + Per package options for package `kotlin` and sub-packages of it + * `noStdlibLink` - No default documentation link to kotlin-stdlib + * `<externalDocumentationLink url="https://example.com/docs/" packageListUrl="file:///home/user/localdocs/package-list"/>` - + linking to external documentation, packageListUrl should be used if package-list located not in standard location + * `cacheRoot` - Use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka + + +### Using the Command Line + +To run Dokka from the command line, download the [Dokka jar](https://github.com/Kotlin/dokka/releases/download/0.9.10/dokka-fatjar.jar). +To generate documentation, run the following command: + + java -jar dokka-fatjar.jar <source directories> <arguments> + +Dokka supports the following command line arguments: + + * `-output` - the output directory where the documentation is generated + * `-format` - the [output format](#output-formats): + * `-classpath` - list of directories or .jar files to include in the classpath (used for resolving references) + * `-samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) + * `-module` - the name of the module being documented (used as the root directory of the generated documentation) + * `-include` - names of files containing the documentation for the module and individual packages + * `-nodeprecated` - if set, deprecated elements are not included in the generated documentation + * `-impliedPlatforms` - List of implied platforms (comma-separated) + * `-packageOptions` - List of package options in format `prefix,-deprecated,-privateApi,+warnUndocumented;...` + * `-links` - External documentation links in format `url^packageListUrl^^url2...` + * `-noStdlibLink` - Disable documentation link to stdlib + * `-cacheRoot` - Use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka + + +### Output formats<a name="output_formats"></a> + + * `html` - minimalistic html format used by default + * `javadoc` - Dokka mimic to javadoc + * `html-as-java` - as `html` but using java syntax + * `markdown` - Markdown structured as `html` + * `gfm` - GitHub flavored markdown + * `jekyll` - Jekyll compatible markdown + * `kotlin-website*` - internal format used for documentation on [kotlinlang.org](https://kotlinlang.org) + +### Platforms<a name="platforms"></a> + +Dokka can annotate elements with special `platform` block with platform requirements + +Example of usage can be found on [kotlinlang.org](https://kotlinlang.org/api/latest/jvm/stdlib/) + +Each source root has a list of platforms for which members are suitable. +Also, the list of 'implied' platforms is passed to Dokka. +If a member is not available for all platforms in the implied platforms set, its documentation will show +the list of platforms for which it's available. + +## Dokka Internals + +### Documentation Model + +Dokka uses Kotlin-as-a-service technology to build `code model`, then processes it into `documentation model`. +`Documentation model` is graph of items describing code elements such as classes, packages, functions, etc. + +Each node has semantic attached, e.g. Value:name -> Type:String means that some value `name` is of type `String`. + +Each reference between nodes also has semantic attached, and there are three of them: + +1. Member - reference means that target is member of the source, form tree. +2. Detail - reference means that target describes source in more details, form tree. +3. Link - any link to any other node, free form. + +Member & Detail has reverse Owner reference, while Link's back reference is also Link. + +Nodes that are Details of other nodes cannot have Members. + +### Rendering Docs + +When we have documentation model, we can render docs in various formats, languages and layouts. We have some core services: + +* FormatService -- represents output format +* LocationService -- represents folder and file layout +* SignatureGenerator -- represents target language by generating class/function/package signatures from model + +Basically, given the `documentation` as a model, we do this: + +```kotlin + val signatureGenerator = KotlinSignatureGenerator() + val locationService = FoldersLocationService(arguments.outputDir) + val markdown = JekyllFormatService(locationService, signatureGenerator) + val generator = FileGenerator(signatureGenerator, locationService, markdown) + generator.generate(documentation) +``` + +## Building Dokka + +Dokka is built with Gradle. To build it, use `./gradlew build`. +Alternatively, open the project directory in IntelliJ IDEA and use the IDE to build and run Dokka. + +Here's how to import and configure Dokka in IntelliJ IDEA: + +* Select "Open" from the IDEA welcome screen, or File > Open if a project is + already open +* Select the directory with your clone of Dokka + * Note: IDEA may have an error after the project is initally opened; it is OK + to ignore this as the next step will address this error +* After IDEA opens the project, select File > New > Module from existing sources + and select the `build.gradle` file from the root directory of your Dokka clone +* Use the default options and select "OK" +* After Dokka is loaded into IDEA, open the Gradle tool window (View > Tool + Windows > Gradle) and click on the top left "Refresh all Gradle projects" + button +* Verify the following project settings. In File > Settings > Build, Execution, + Deployment > Build Tools > Gradle > Runner: + * Ensure "Delegate IDE build/run actions to gradle" is checked + * "Gradle Test Runner" should be selected in the "Run tests using" drop-down + menu +* Note: After closing and re-opening the project, IDEA may give an error + message: "Error Loading Project: Cannot load 3 modules". Open up the details + of the error, and click "Remove Selected", as these module `.iml` files are + safe to remove. diff --git a/build-docs.xml b/build-docs.xml new file mode 100644 index 000000000..b46353b01 --- /dev/null +++ b/build-docs.xml @@ -0,0 +1,20 @@ +<project name="Dokka" default="document"> + <!-- Demonstrates the usage of the Dokka Ant task. Assumes Dokka has already been compiled --> + + <typedef resource="dokka-antlib.xml"> + <classpath> + <fileset dir="runners/fatjar/build/libs" includes="dokka-fatjar-*.jar"/> + </classpath> + </typedef> + + <path id="dokka.source.path"> + <pathelement location="core/src/main/kotlin"/> + <fileset dir="runners" includes="*/src/main/kotlin/**" /> + </path> + + <target name="document"> + <dokka srcref="dokka.source.path" outputdir="doc" modulename="dokka"> + <sourcelink path="." url="https://github.com/kotlin/dokka/blob/master" linesuffix="#L"/> + </dokka> + </target> +</project> diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..08951ee9a --- /dev/null +++ b/build.gradle @@ -0,0 +1,124 @@ +allprojects { + group 'org.jetbrains.dokka' + version dokka_version + + def repo = { + artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/[revision]/internal/[module](.[ext])") + artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/IntelliJMarkdownParser_Build/[revision]/([module]_[ext]/)[module](.[ext])") + } + + buildscript { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + maven { url "https://plugins.gradle.org/m2/" } + ivy(repo) + } + dependencies { + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' + classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' + + classpath "com.gradle.publish:plugin-publish-plugin:0.9.10" + } + } + + repositories { + mavenCentral() + mavenLocal() + maven { url 'https://kotlin.bintray.com/kotlin-plugin' } + maven { url 'https://www.jetbrains.com/intellij-repository/releases' } + maven { url "https://dl.bintray.com/jetbrains/markdown" } + maven { url "http://dl.bintray.com/kotlin/kotlin-eap" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + maven { url 'https://jitpack.io' } + maven { url "https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/$bundled_kotlin_compiler_version/maven" } + ivy(repo) + maven { url "https://dl.bintray.com/kotlin/kotlinx.html" } + } +} + + +def bintrayPublication(project, List<String> _publications) { + configure(project, { + apply plugin: 'com.jfrog.bintray' + + bintray { + user = System.getenv('BINTRAY_USER') + key = System.getenv('BINTRAY_KEY') + + pkg { + repo = dokka_publication_channel + name = 'dokka' + userOrg = 'kotlin' + desc = 'Dokka, the Kotlin documentation tool' + vcsUrl = 'https://github.com/kotlin/dokka.git' + licenses = ['Apache-2.0'] + version { + name = dokka_version + } + } + + publications = _publications + } + }) +} + +task wrapper(type: Wrapper) { + gradleVersion = '4.2.1' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" +} + +configurations { + ideaIC + intellijCore +} + +repositories { + maven { url 'https://kotlin.bintray.com/kotlin-plugin' } + maven { url 'https://www.jetbrains.com/intellij-repository/snapshots' } + maven { url 'https://www.jetbrains.com/intellij-repository/releases' } +} + +dependencies { + intellijCore "com.jetbrains.intellij.idea:intellij-core:$idea_version" + ideaIC "com.jetbrains.intellij.idea:ideaIC:$idea_version" +} + +def intellijCoreAnalysis() { + return zipTree(configurations.intellijCore.singleFile).matching ({ + include("intellij-core-analysis.jar") + }) +} + +def ideaRT() { + return zipTree(project.configurations.ideaIC.singleFile).matching ({ + include("lib/idea_rt.jar") + }) +} + +def repoLocation = uri(file("$buildDir/dist-maven")) + +allprojects { + + task publishToDistMaven { + group "publishing" + description "Publishes all Maven publications to Maven repository 'distMaven'." + dependsOn tasks.withType(PublishToMavenRepository).matching { + it.repository == publishing.repositories.distMaven + } + } + + plugins.withType(MavenPublishPlugin) { + publishing { + repositories { + maven { + name 'distMaven' + url repoLocation + } + } + } + + } +}
\ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 000000000..874dabc96 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'groovy' +repositories { + mavenCentral() + jcenter() + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } +} +dependencies { + compile 'com.github.jengelman.gradle.plugins:shadow:2.0.1' +}
\ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy new file mode 100644 index 000000000..aacede449 --- /dev/null +++ b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy @@ -0,0 +1,39 @@ +package org.jetbrains + +import org.gradle.api.Project +import org.gradle.api.artifacts.ModuleVersionIdentifier +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.SelfResolvingDependency +import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyPublicationResolver +import org.gradle.api.publish.maven.MavenPom +import org.gradle.api.publish.maven.MavenPublication + +//https://github.com/johnrengelman/shadow/issues/334 +static void configure(MavenPublication publication, Project project) { + publication.artifact(project.tasks.shadowJar) + + publication.pom { MavenPom pom -> + pom.withXml { xml -> + def dependenciesNode = xml.asNode().appendNode('dependencies') + + project.configurations.shadow.allDependencies.each { + if (it instanceof ProjectDependency) { + final ProjectDependencyPublicationResolver projectDependencyResolver = project.gradle.services.get(ProjectDependencyPublicationResolver) + final ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier, it) + addDependency(dependenciesNode, identifier) + } else if (!(it instanceof SelfResolvingDependency)) { + addDependency(dependenciesNode, it) + } + + } + } + } +} + +private static void addDependency(Node dependenciesNode, dep) { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', dep.group) + dependencyNode.appendNode('artifactId', dep.name) + dependencyNode.appendNode('version', dep.version) + dependencyNode.appendNode('scope', 'compile') +} diff --git a/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy b/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy new file mode 100644 index 000000000..d3973a8a2 --- /dev/null +++ b/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy @@ -0,0 +1,84 @@ +package org.jetbrains + +import org.gradle.api.tasks.AbstractExecTask +import org.gradle.api.tasks.TaskAction +import org.gradle.internal.os.OperatingSystem + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +class CrossPlatformExec extends AbstractExecTask { + private static final def windowsExtensions = ['bat', 'cmd', 'exe']; + private static final def unixExtensions = [null, 'sh']; + + private boolean windows; + + public CrossPlatformExec() { + super(CrossPlatformExec.class); + windows = OperatingSystem.current().windows; + } + + @Override + @TaskAction + protected void exec() { + List<String> commandLine = this.getCommandLine(); + + if (!commandLine.isEmpty()) { + commandLine[0] = findCommand(commandLine[0], windows); + } + + if (windows) { + if (!commandLine.isEmpty() && commandLine[0]) { + commandLine + } + commandLine.add(0, '/c'); + commandLine.add(0, 'cmd'); + } + + this.setCommandLine(commandLine); + + super.exec(); + } + + private static String findCommand(String command, boolean windows) { + command = normalizeCommandPaths(command); + def extensions = windows ? windowsExtensions : unixExtensions; + + return extensions.findResult(command) { extension -> + Path commandFile + if (extension) { + commandFile = Paths.get(command + '.' + extension); + } else { + commandFile = Paths.get(command); + } + + return resolveCommandFromFile(commandFile, windows); + }; + } + + private static String resolveCommandFromFile(Path commandFile, boolean windows) { + if (!Files.isExecutable(commandFile)) { + return null; + } + + return commandFile.toAbsolutePath().normalize(); + } + + private static String normalizeCommandPaths(String command) { + // need to escape backslash so it works with regex + String backslashSeparator = '\\\\'; + + String forwardSlashSeparator = '/'; + + // escape separator if it's a backslash + char backslash = '\\'; + String separator = File.separatorChar == backslash ? backslashSeparator : File.separator + + return command + // first replace all of the backslashes with forward slashes + .replaceAll(backslashSeparator, forwardSlashSeparator) + // then replace all forward slashes with whatever the separator actually is + .replaceAll(forwardSlashSeparator, separator); + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy b/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy new file mode 100644 index 000000000..194f11afb --- /dev/null +++ b/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy @@ -0,0 +1,14 @@ +package org.jetbrains + +import org.gradle.api.Project + +class DependenciesVersionGetter { + static Properties getVersions(Project project, String artifactVersionSelector) { + def dep = project.dependencies.create(group: 'teamcity', name: 'dependencies', version: artifactVersionSelector, ext: 'properties') + def file = project.configurations.detachedConfiguration(dep).resolve().first() + + def prop = new Properties() + prop.load(new FileReader(file)) + return prop + } +} diff --git a/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy b/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy new file mode 100644 index 000000000..e711388f4 --- /dev/null +++ b/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy @@ -0,0 +1,71 @@ +package org.jetbrains + +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import groovy.xml.XmlUtil +import org.gradle.api.file.FileTreeElement +import shadow.org.apache.tools.zip.ZipEntry +import shadow.org.apache.tools.zip.ZipOutputStream + +public class PluginXmlTransformer implements Transformer { + private Map<String, Node> transformedPluginXmlFiles = new HashMap<>(); + + @Override + boolean canTransformResource(FileTreeElement fileTreeElement) { + return fileTreeElement.relativePath.segments.contains("META-INF") && fileTreeElement.name.endsWith(".xml") + } + + @Override + void transform(TransformerContext context) { + def path = context.path + def inputStream = context.is + System.out.println(path) + Node node = new XmlParser().parse(inputStream) + relocateXml(node, context) + transformedPluginXmlFiles.put(path, node) + } + + @Override + boolean hasTransformedResource() { + return !transformedPluginXmlFiles.isEmpty() + } + + @Override + void modifyOutputStream(ZipOutputStream zipOutputStream) { + for (Map.Entry<String, Node> entry : transformedPluginXmlFiles.entrySet()) { + zipOutputStream.putNextEntry(new ZipEntry(entry.key)) + XmlUtil.serialize(entry.value, zipOutputStream) + } + } + + private static void relocateXml(Node node, TransformerContext context) { + Map attributes = node.attributes() + RelocateClassContext relocateClassContext = new RelocateClassContext() + relocateClassContext.stats = context.stats + for (Map.Entry entry : attributes.entrySet()) { + relocateClassContext.setClassName((String) entry.getValue()) + entry.setValue(relocateClassName(relocateClassContext, context)) + } + List<String> localText = node.localText() + if (localText.size() == 1) { + relocateClassContext.setClassName(localText[0]) + node.setValue(relocateClassName(relocateClassContext, context)) + } + node.children().each { + if (it instanceof Node) { + relocateXml((Node) it, context) + } + } + } + + private static String relocateClassName(RelocateClassContext relocateContext, TransformerContext context) { + for (Relocator relocator : context.relocators) { + if (relocator.canRelocateClass(relocateContext)) { + return relocator.relocateClass(relocateContext) + } + } + return relocateContext.className + } +}
\ No newline at end of file diff --git a/busytown.gradle b/busytown.gradle new file mode 100644 index 000000000..1b9a7119f --- /dev/null +++ b/busytown.gradle @@ -0,0 +1,38 @@ +def destDir = (System.getenv("DIST_DIR") == null) ? file("dist") : file(System.getenv("DIST_DIR")) + +def hostTestDir = new File(destDir, "host-test-reports") + +allprojects { project -> + project.tasks.withType(Test) { task -> + def report = task.reports.junitXml + if (report.isEnabled()) { + def zipTask = project.tasks.create("zipResultsOf${project.name}", Zip) { + destinationDir = hostTestDir + archiveName = "${project.name}.zip" + } + task.finalizedBy(zipTask) + task.doFirst { + zipTask.from(report.destination) + } + } + task.ignoreFailures = true + } + if (project.rootProject == project) { + def zipMaven = project.tasks.create("zipMaven", Zip) { + from file("${project.buildDir}/dist-maven") + destinationDir destDir + archiveName = "maven.zip" + } + + def copyRepository = project.tasks.create("copyRepository", Copy) { + from file("${project.buildDir}/dist-maven") + into "${destDir}/repository" + } + + [copyRepository, zipMaven].forEach { + it.dependsOn(":runners:android-gradle-plugin:publishToDistMaven") + it.dependsOn(":runners:gradle-plugin:publishToDistMaven") + it.dependsOn(":runners:fatjar:publishToDistMaven") + } + } +} diff --git a/busytown.sh b/busytown.sh new file mode 100755 index 000000000..ca1ae6199 --- /dev/null +++ b/busytown.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd $(dirname $0) && pwd)" + +"$SCRIPT_DIR"/gradlew -p "$SCRIPT_DIR" -I "$SCRIPT_DIR"/busytown.gradle --no-daemon :core:build :runners:android-gradle-plugin:build :runners:gradle-integration-tests:build zipMaven copyRepository
\ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 000000000..4a8928de1 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,56 @@ +import javax.tools.ToolProvider + +buildscript { + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + languageVersion = "1.2" + apiVersion = languageVersion + jvmTarget = "1.8" + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + + compile "org.jetbrains.kotlin:kotlin-stdlib:$bundled_kotlin_compiler_version" + compile "org.jetbrains.kotlin:kotlin-reflect:$bundled_kotlin_compiler_version" + + compile group: 'com.google.inject', name: 'guice', version: '3.0' + compile "org.jsoup:jsoup:1.8.3" + + compile "org.jetbrains.kotlin:kotlin-compiler:$bundled_kotlin_compiler_version" + compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version" + + compile "org.jetbrains:markdown:0.1.41" + + implementation "com.squareup.okhttp3:okhttp:4.0.0-RC1" + + compile intellijCoreAnalysis() + + compile "org.jetbrains.kotlin:kotlin-plugin-ij193:$kotlin_plugin_version" //TODO: parametrize ij version after 1.3.70 + +// Google version of the library in the libs folder. Fixing 129528660 +// compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8' + + //tools.jar + def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") }) + compileOnly toolsJar + testCompile toolsJar + + compile project(":integration") + + testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_version + testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0" + + testCompile ideaRT() +}
\ No newline at end of file diff --git a/core/libs/kotlinx-html-jvm-0.6.8-google.jar b/core/libs/kotlinx-html-jvm-0.6.8-google.jar Binary files differnew file mode 100644 index 000000000..08d3c2efa --- /dev/null +++ b/core/libs/kotlinx-html-jvm-0.6.8-google.jar diff --git a/core/samples/memberWithModifiers.java b/core/samples/memberWithModifiers.java new file mode 100644 index 000000000..ea645c218 --- /dev/null +++ b/core/samples/memberWithModifiers.java @@ -0,0 +1,12 @@ +public abstract class Test { + /** + * Summary for Function + * @param name is String parameter + * @param value is int parameter + */ + protected final void fn(String name, int value) { + + } + + protected void openFn() {} +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt new file mode 100644 index 000000000..9fea67407 --- /dev/null +++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt @@ -0,0 +1,371 @@ +package org.jetbrains.dokka + +import com.google.common.collect.ImmutableMap +import com.intellij.core.CoreApplicationEnvironment +import com.intellij.core.CoreModuleManager +import com.intellij.mock.MockComponentManager +import com.intellij.openapi.Disposable +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.OrderEnumerationHandler +import com.intellij.openapi.roots.ProjectFileIndex +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.javadoc.JavadocManagerImpl +import com.intellij.psi.javadoc.CustomJavadocTagProvider +import com.intellij.psi.javadoc.JavadocManager +import com.intellij.psi.javadoc.JavadocTagInfo +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analyzer.* +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.config.ContentRoot +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.cli.jvm.config.* +import org.jetbrains.kotlin.cli.jvm.index.JavaRoot +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.container.getService +import org.jetbrains.kotlin.container.tryGetService +import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.context.withModule +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.platform.konan.KonanPlatforms +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters +import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice +import org.jetbrains.kotlin.util.slicedMap.WritableSlice +import java.io.File + +/** + * Kotlin as a service entry point + * + * Configures environment, analyses files and provides facilities to perform code processing without emitting bytecode + * + * $messageCollector: required by compiler infrastructure and will receive all compiler messages + * $body: optional and can be used to configure environment without creating local variable + */ +class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { + val configuration = CompilerConfiguration() + + init { + configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) + } + + fun createCoreEnvironment(): KotlinCoreEnvironment { + System.setProperty("idea.io.use.fallback", "true") + val environment = KotlinCoreEnvironment.createForProduction(this, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) + val projectComponentManager = environment.project as MockComponentManager + + val projectFileIndex = CoreProjectFileIndex(environment.project, + environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS)) + + val moduleManager = object : CoreModuleManager(environment.project, this) { + override fun getModules(): Array<out Module> = arrayOf(projectFileIndex.module) + } + + CoreApplicationEnvironment.registerComponentInstance(projectComponentManager.picoContainer, + ModuleManager::class.java, moduleManager) + + Extensions.registerAreaClass("IDEA_MODULE", null) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), + OrderEnumerationHandler.EP_NAME, OrderEnumerationHandler.Factory::class.java) + + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getArea(environment.project), + JavadocTagInfo.EP_NAME, JavadocTagInfo::class.java) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), + CustomJavadocTagProvider.EP_NAME, CustomJavadocTagProvider::class.java) + + projectComponentManager.registerService(ProjectFileIndex::class.java, + projectFileIndex) + projectComponentManager.registerService(ProjectRootManager::class.java, + CoreProjectRootManager(projectFileIndex)) + projectComponentManager.registerService(JavadocManager::class.java, + JavadocManagerImpl(environment.project)) + projectComponentManager.registerService(CustomJavadocTagProvider::class.java, + CustomJavadocTagProvider { emptyList() }) + return environment + } + + fun createSourceModuleSearchScope(project: Project, sourceFiles: List<KtFile>): GlobalSearchScope { + // TODO: Fix when going to implement dokka for JS + return TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles) + } + + + fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair<DokkaResolutionFacade, DokkaResolutionFacade> { + + val projectContext = ProjectContext(environment.project, "Dokka") + val sourceFiles = environment.getSourceFiles() + + + val library = object : ModuleInfo { + override val name: Name = Name.special("<library>") + override val platform: TargetPlatform + get() = JvmPlatforms.defaultJvmPlatform + override val analyzerServices: PlatformDependentAnalyzerServices = + JvmPlatformAnalyzerServices + override fun dependencies(): List<ModuleInfo> = listOf(this) + } + val module = object : ModuleInfo { + override val name: Name = Name.special("<module>") + override val platform: TargetPlatform + get() = JvmPlatforms.defaultJvmPlatform + override val analyzerServices: PlatformDependentAnalyzerServices = + JvmPlatformAnalyzerServices + override fun dependencies(): List<ModuleInfo> = listOf(this, library) + } + + val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles) + + val builtIns = JvmBuiltIns( + projectContext.storageManager, + JvmBuiltIns.Kind.FROM_CLASS_LOADER + ) + + + val javaRoots = classpath + .mapNotNull { + val rootFile = when { + it.extension == "jar" -> + StandardFileSystems.jar().findFileByPath("${it.absolutePath}${"!/"}") + else -> + StandardFileSystems.local().findFileByPath(it.absolutePath) + } + + rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) } + } + + val resolverForProject = object : AbstractResolverForProject<ModuleInfo>( + "Dokka", + projectContext, + modules = listOf(module, library) + ) { + override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> = + when (module) { + library -> ModuleContent(module, emptyList(), GlobalSearchScope.notScope(sourcesScope)) + module -> ModuleContent(module, emptyList(), sourcesScope) + else -> throw IllegalArgumentException("Unexpected module info") + } + + override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = builtIns + + override fun createResolverForModule( + descriptor: ModuleDescriptor, + moduleInfo: ModuleInfo + ): ResolverForModule = JvmResolverForModuleFactory( + JvmPlatformParameters({ content -> + JvmPackagePartProvider( + configuration.languageVersionSettings, + content.moduleContentScope + ) + .apply { + addRoots(javaRoots, messageCollector) + } + }, { + val file = (it as JavaClassImpl).psi.containingFile.virtualFile + if (file in sourcesScope) + module + else + library + }), + CompilerEnvironment, + KonanPlatforms.defaultKonanPlatform + ).createResolverForModule( + descriptor as ModuleDescriptorImpl, + projectContext.withModule(descriptor), + modulesContent(moduleInfo), + this, + LanguageVersionSettingsImpl.DEFAULT + ) + + override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null + } + + val resolverForLibrary = resolverForProject.resolverForModule(library) // Required before module to initialize library properly + val resolverForModule = resolverForProject.resolverForModule(module) + val libraryModuleDescriptor = resolverForProject.descriptorForModule(library) + val moduleDescriptor = resolverForProject.descriptorForModule(module) + builtIns.initialize(moduleDescriptor, true) + val libraryResolutionFacade = DokkaResolutionFacade(environment.project, libraryModuleDescriptor, resolverForLibrary) + val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule) + val projectComponentManager = environment.project as MockComponentManager + projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created)) + + return created to libraryResolutionFacade + } + + fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) { + val languageVersion = LanguageVersion.fromVersionString(languageVersionString) ?: LanguageVersion.LATEST_STABLE + val apiVersion = apiVersionString?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(languageVersion) + configuration.languageVersionSettings = LanguageVersionSettingsImpl(languageVersion, apiVersion) + } + + /** + * Classpath for this environment. + */ + val classpath: List<File> + get() = configuration.jvmClasspathRoots + + /** + * Adds list of paths to classpath. + * $paths: collection of files to add + */ + fun addClasspath(paths: List<File>) { + configuration.addJvmClasspathRoots(paths) + } + + /** + * Adds path to classpath. + * $path: path to add + */ + fun addClasspath(path: File) { + configuration.addJvmClasspathRoot(path) + } + + /** + * List of source roots for this environment. + */ + val sources: List<String> + get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<KotlinSourceRoot>() + ?.map { it.path } ?: emptyList() + + /** + * Adds list of paths to source roots. + * $list: collection of files to add + */ + fun addSources(list: List<String>) { + list.forEach { + configuration.addKotlinSourceRoot(it) + val file = File(it) + if (file.isDirectory || file.extension == ".java") { + configuration.addJavaSourceRoot(file) + } + } + } + + fun addRoots(list: List<ContentRoot>) { + configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list) + } + + /** + * Disposes the environment and frees all associated resources. + */ + override fun dispose() { + Disposer.dispose(this) + } +} + +fun contentRootFromPath(path: String): ContentRoot { + val file = File(path) + return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false) +} + + +class DokkaResolutionFacade(override val project: Project, + override val moduleDescriptor: ModuleDescriptor, + val resolverForModule: ResolverForModule) : ResolutionFacade { + override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult { + throw UnsupportedOperationException() + } + + override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? { + return resolverForModule.componentProvider.tryGetService(serviceClass) + } + + override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor { + return resolveSession.resolveToDescriptor(declaration) + } + + override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext { + throw UnsupportedOperationException() + } + + val resolveSession: ResolveSession get() = getFrontendService(ResolveSession::class.java) + + override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext { + if (element is KtDeclaration) { + val descriptor = resolveToDescriptor(element) + return object : BindingContext { + override fun <K : Any?, V : Any?> getKeys(p0: WritableSlice<K, V>?): Collection<K> { + throw UnsupportedOperationException() + } + + override fun getType(p0: KtExpression): KotlinType? { + throw UnsupportedOperationException() + } + + override fun <K : Any?, V : Any?> get(slice: ReadOnlySlice<K, V>?, key: K): V? { + if (key != element) { + throw UnsupportedOperationException() + } + return when { + slice == BindingContext.DECLARATION_TO_DESCRIPTOR -> descriptor as V + slice == BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER && (element as KtParameter).hasValOrVar() -> descriptor as V + else -> null + } + } + + override fun getDiagnostics(): Diagnostics { + throw UnsupportedOperationException() + } + + override fun addOwnDataTo(p0: BindingTrace, p1: Boolean) { + throw UnsupportedOperationException() + } + + override fun <K : Any?, V : Any?> getSliceContents(p0: ReadOnlySlice<K, V>): ImmutableMap<K, V> { + throw UnsupportedOperationException() + } + + } + } + throw UnsupportedOperationException() + } + + override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T { + throw UnsupportedOperationException() + } + + override fun <T : Any> getFrontendService(serviceClass: Class<T>): T { + return resolverForModule.componentProvider.getService(serviceClass) + } + + override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T { + return resolverForModule.componentProvider.getService(serviceClass) + } + + override fun <T : Any> getIdeService(serviceClass: Class<T>): T { + throw UnsupportedOperationException() + } + +} diff --git a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt new file mode 100644 index 000000000..d9093760c --- /dev/null +++ b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt @@ -0,0 +1,42 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache + + +class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService { + override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { + return resolutionFacade + } + + override fun getResolutionFacade( + elements: List<KtElement>, + platform: org.jetbrains.kotlin.platform.TargetPlatform + ): ResolutionFacade { + return resolutionFacade + } + + override fun getResolutionFacadeByFile( + file: PsiFile, + platform: org.jetbrains.kotlin.platform.TargetPlatform + ): ResolutionFacade? { + return resolutionFacade + } + + override fun getResolutionFacadeByModuleInfo( + moduleInfo: ModuleInfo, + platform: org.jetbrains.kotlin.platform.TargetPlatform + ): ResolutionFacade? { + return resolutionFacade + } + + override fun getSuppressionCache(): KotlinSuppressCache { + throw UnsupportedOperationException() + } + +} + diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt new file mode 100644 index 000000000..4ece8d300 --- /dev/null +++ b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt @@ -0,0 +1,569 @@ +package org.jetbrains.dokka + +import com.intellij.openapi.Disposable +import com.intellij.openapi.components.BaseComponent +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.projectRoots.SdkAdditionalData +import com.intellij.openapi.projectRoots.SdkModificator +import com.intellij.openapi.projectRoots.SdkTypeId +import com.intellij.openapi.roots.* +import com.intellij.openapi.roots.impl.ProjectOrderEnumerator +import com.intellij.openapi.util.Condition +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.UserDataHolderBase +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VfsUtilCore.getVirtualFileForJar +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileFilter +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.util.messages.MessageBus +import org.jetbrains.jps.model.module.JpsModuleSourceRootType +import org.jetbrains.kotlin.cli.common.config.ContentRoot +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot +import org.picocontainer.PicoContainer +import java.io.File + +/** + * Workaround for the lack of ability to create a ProjectFileIndex implementation using only + * classes from projectModel-{api,impl}. + */ +class CoreProjectFileIndex(private val project: Project, contentRoots: List<ContentRoot>) : ProjectFileIndex, ModuleFileIndex { + override fun iterateContent(p0: ContentIterator, p1: VirtualFileFilter?): Boolean { + throw UnsupportedOperationException() + } + + override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator, p2: VirtualFileFilter?): Boolean { + throw UnsupportedOperationException() + } + + override fun isInLibrary(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + val sourceRoots = contentRoots.filter { it !is JvmClasspathRoot } + val classpathRoots = contentRoots.filterIsInstance<JvmClasspathRoot>() + + val module: Module = object : UserDataHolderBase(), Module { + override fun isDisposed(): Boolean { + throw UnsupportedOperationException() + } + + override fun getOptionValue(p0: String): String? { + throw UnsupportedOperationException() + } + + override fun clearOption(p0: String) { + throw UnsupportedOperationException() + } + + override fun getName(): String = "<Dokka module>" + + override fun getModuleWithLibrariesScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleWithDependentsScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleContentScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun isLoaded(): Boolean { + throw UnsupportedOperationException() + } + + override fun setOption(p0: String, p1: String?) { + throw UnsupportedOperationException() + } + + override fun getModuleWithDependenciesScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleWithDependenciesAndLibrariesScope(p0: Boolean): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getProject(): Project = this@CoreProjectFileIndex.project + + override fun getModuleContentWithDependenciesScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleFilePath(): String { + throw UnsupportedOperationException() + } + + override fun getModuleTestsWithDependentsScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleScope(): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleScope(p0: Boolean): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleRuntimeScope(p0: Boolean): GlobalSearchScope { + throw UnsupportedOperationException() + } + + override fun getModuleFile(): VirtualFile? { + throw UnsupportedOperationException() + } + + override fun <T : Any?> getExtensions(p0: ExtensionPointName<T>): Array<out T> { + throw UnsupportedOperationException() + } + + override fun getComponent(p0: String): BaseComponent? { + throw UnsupportedOperationException() + } + + override fun <T : Any?> getComponent(p0: Class<T>, p1: T): T { + throw UnsupportedOperationException() + } + + override fun <T : Any?> getComponent(interfaceClass: Class<T>): T? { + if (interfaceClass == ModuleRootManager::class.java) { + return moduleRootManager as T + } + throw UnsupportedOperationException() + } + + override fun getDisposed(): Condition<*> { + throw UnsupportedOperationException() + } + + override fun <T : Any?> getComponents(p0: Class<T>): Array<out T> { + throw UnsupportedOperationException() + } + + override fun getPicoContainer(): PicoContainer { + throw UnsupportedOperationException() + } + + override fun hasComponent(p0: Class<*>): Boolean { + throw UnsupportedOperationException() + } + + override fun getMessageBus(): MessageBus { + throw UnsupportedOperationException() + } + + override fun dispose() { + throw UnsupportedOperationException() + } + } + + private val sdk: Sdk = object : Sdk, RootProvider { + override fun getFiles(rootType: OrderRootType): Array<out VirtualFile> = classpathRoots + .mapNotNull { StandardFileSystems.local().findFileByPath(it.file.path) } + .toTypedArray() + + override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener) { + throw UnsupportedOperationException() + } + + override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener, p1: Disposable) { + throw UnsupportedOperationException() + } + + override fun getUrls(p0: OrderRootType): Array<out String> { + throw UnsupportedOperationException() + } + + override fun removeRootSetChangedListener(p0: RootProvider.RootSetChangedListener) { + throw UnsupportedOperationException() + } + + override fun getSdkModificator(): SdkModificator { + throw UnsupportedOperationException() + } + + override fun getName(): String = "<dokka SDK>" + + override fun getRootProvider(): RootProvider = this + + override fun getHomePath(): String? { + throw UnsupportedOperationException() + } + + override fun getVersionString(): String? { + throw UnsupportedOperationException() + } + + override fun getSdkAdditionalData(): SdkAdditionalData? { + throw UnsupportedOperationException() + } + + override fun clone(): Any { + throw UnsupportedOperationException() + } + + override fun getSdkType(): SdkTypeId { + throw UnsupportedOperationException() + } + + override fun getHomeDirectory(): VirtualFile? { + throw UnsupportedOperationException() + } + + override fun <T : Any?> getUserData(p0: Key<T>): T? { + throw UnsupportedOperationException() + } + + override fun <T : Any?> putUserData(p0: Key<T>, p1: T?) { + throw UnsupportedOperationException() + } + } + + private val moduleSourceOrderEntry = object : ModuleSourceOrderEntry { + override fun getFiles(p0: OrderRootType): Array<VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getUrls(p0: OrderRootType): Array<String> { + throw UnsupportedOperationException() + } + + override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R { + throw UnsupportedOperationException() + } + + + override fun getPresentableName(): String { + throw UnsupportedOperationException() + } + + override fun getOwnerModule(): Module = module + + + override fun isValid(): Boolean { + throw UnsupportedOperationException() + } + + override fun compareTo(other: OrderEntry?): Int { + throw UnsupportedOperationException() + } + + override fun getRootModel(): ModuleRootModel = moduleRootManager + + override fun isSynthetic(): Boolean { + throw UnsupportedOperationException() + } + } + + private val sdkOrderEntry = object : JdkOrderEntry { + override fun getFiles(p0: OrderRootType): Array<VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getUrls(p0: OrderRootType): Array<String> { + throw UnsupportedOperationException() + } + + override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R { + throw UnsupportedOperationException() + } + + override fun getJdkName(): String? { + throw UnsupportedOperationException() + } + + override fun getJdk(): Sdk = sdk + + override fun getPresentableName(): String { + throw UnsupportedOperationException() + } + + override fun getOwnerModule(): Module { + throw UnsupportedOperationException() + } + + override fun isValid(): Boolean { + throw UnsupportedOperationException() + } + + override fun getRootFiles(p0: OrderRootType): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getRootUrls(p0: OrderRootType): Array<out String> { + throw UnsupportedOperationException() + } + + override fun compareTo(other: OrderEntry?): Int { + throw UnsupportedOperationException() + } + + override fun isSynthetic(): Boolean { + throw UnsupportedOperationException() + } + + } + + inner class MyModuleRootManager : ModuleRootManager() { + override fun getExternalSource(): ProjectModelExternalSource? { + throw UnsupportedOperationException() + } + + override fun getExcludeRoots(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getContentEntries(): Array<out ContentEntry> { + throw UnsupportedOperationException() + } + + override fun getExcludeRootUrls(): Array<out String> { + throw UnsupportedOperationException() + } + + override fun <R : Any?> processOrder(p0: RootPolicy<R>, p1: R): R { + throw UnsupportedOperationException() + } + + override fun getSourceRoots(p0: Boolean): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getSourceRoots(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getSourceRoots(p0: JpsModuleSourceRootType<*>): MutableList<VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getContentRoots(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun orderEntries(): OrderEnumerator = + ProjectOrderEnumerator(project, null).using(object : RootModelProvider { + override fun getModules(): Array<out Module> = arrayOf(module) + + override fun getRootModel(p0: Module): ModuleRootModel = this@MyModuleRootManager + }) + + override fun <T : Any?> getModuleExtension(p0: Class<T>): T { + throw UnsupportedOperationException() + } + + override fun getDependencyModuleNames(): Array<out String> { + throw UnsupportedOperationException() + } + + override fun getModule(): Module = this@CoreProjectFileIndex.module + + override fun isSdkInherited(): Boolean { + throw UnsupportedOperationException() + } + + override fun getOrderEntries(): Array<out OrderEntry> = arrayOf(moduleSourceOrderEntry, sdkOrderEntry) + + override fun getSourceRootUrls(): Array<out String> { + throw UnsupportedOperationException() + } + + override fun getSourceRootUrls(p0: Boolean): Array<out String> { + throw UnsupportedOperationException() + } + + override fun getSdk(): Sdk? { + throw UnsupportedOperationException() + } + + override fun getContentRootUrls(): Array<out String> { + throw UnsupportedOperationException() + } + + override fun getModuleDependencies(): Array<out Module> { + throw UnsupportedOperationException() + } + + override fun getModuleDependencies(p0: Boolean): Array<out Module> { + throw UnsupportedOperationException() + } + + override fun getModifiableModel(): ModifiableRootModel { + throw UnsupportedOperationException() + } + + override fun isDependsOn(p0: Module): Boolean { + throw UnsupportedOperationException() + } + + override fun getFileIndex(): ModuleFileIndex { + return this@CoreProjectFileIndex + } + + override fun getDependencies(): Array<out Module> { + throw UnsupportedOperationException() + } + + override fun getDependencies(p0: Boolean): Array<out Module> { + throw UnsupportedOperationException() + } + } + + val moduleRootManager = MyModuleRootManager() + + override fun getContentRootForFile(p0: VirtualFile): VirtualFile? { + throw UnsupportedOperationException() + } + + override fun getContentRootForFile(p0: VirtualFile, p1: Boolean): VirtualFile? { + throw UnsupportedOperationException() + } + + override fun getPackageNameByDirectory(p0: VirtualFile): String? { + throw UnsupportedOperationException() + } + + override fun isInLibrarySource(file: VirtualFile): Boolean = false + + override fun getClassRootForFile(file: VirtualFile): VirtualFile? = + classpathRoots.firstOrNull { it.contains(file) }?.let { StandardFileSystems.local().findFileByPath(it.file.path) } + + override fun getOrderEntriesForFile(file: VirtualFile): List<OrderEntry> = + if (classpathRoots.contains(file)) listOf(sdkOrderEntry) else emptyList() + + override fun isInLibraryClasses(file: VirtualFile): Boolean = classpathRoots.contains(file) + + override fun isExcluded(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun getSourceRootForFile(p0: VirtualFile): VirtualFile? { + throw UnsupportedOperationException() + } + + override fun isUnderIgnored(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun isLibraryClassFile(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun getModuleForFile(file: VirtualFile): Module? = + if (sourceRoots.contains(file)) module else null + + private fun List<ContentRoot>.contains(file: VirtualFile): Boolean = any { it.contains(file) } + + override fun getModuleForFile(p0: VirtualFile, p1: Boolean): Module? { + throw UnsupportedOperationException() + } + + override fun isInSource(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun isIgnored(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun isContentSourceFile(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun isInSourceContent(file: VirtualFile): Boolean = sourceRoots.contains(file) + + override fun iterateContent(p0: ContentIterator): Boolean { + throw UnsupportedOperationException() + } + + override fun isInContent(p0: VirtualFile): Boolean { + throw UnsupportedOperationException() + } + + override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator): Boolean { + throw UnsupportedOperationException() + } + + override fun isInTestSourceContent(file: VirtualFile): Boolean = false + + override fun isUnderSourceRootOfType(p0: VirtualFile, p1: MutableSet<out JpsModuleSourceRootType<*>>): Boolean { + throw UnsupportedOperationException() + } + + override fun getOrderEntryForFile(p0: VirtualFile): OrderEntry? { + throw UnsupportedOperationException() + } +} + +class CoreProjectRootManager(val projectFileIndex: CoreProjectFileIndex) : ProjectRootManager() { + override fun orderEntries(): OrderEnumerator { + throw UnsupportedOperationException() + } + + override fun orderEntries(p0: MutableCollection<out Module>): OrderEnumerator { + throw UnsupportedOperationException() + } + + override fun getContentRootsFromAllModules(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun setProjectSdk(p0: Sdk?) { + throw UnsupportedOperationException() + } + + override fun setProjectSdkName(p0: String) { + throw UnsupportedOperationException() + } + + override fun getModuleSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getContentSourceRoots(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getFileIndex(): ProjectFileIndex = projectFileIndex + + override fun getProjectSdkName(): String? { + throw UnsupportedOperationException() + } + + override fun getProjectSdk(): Sdk? { + throw UnsupportedOperationException() + } + + override fun getContentRoots(): Array<out VirtualFile> { + throw UnsupportedOperationException() + } + + override fun getContentRootUrls(): MutableList<String> { + throw UnsupportedOperationException() + } + +} + +fun ContentRoot.contains(file: VirtualFile) = when (this) { + is JvmContentRoot -> { + val path = if (file.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL) + getVirtualFileForJar(file)?.path ?: file.path + else + file.path + File(path).startsWith(this.file.absoluteFile) + } + is KotlinSourceRoot -> File(file.path).startsWith(File(this.path).absoluteFile) + else -> false +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt new file mode 100644 index 000000000..4a4c78e56 --- /dev/null +++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("JavaResolutionUtils") + +package org.jetbrains.dokka + +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.java.structure.impl.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope + +// TODO: Remove that file + +@JvmOverloads +fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + val method = originalElement as? PsiMethod ?: return null + if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null + val resolver = method.getJavaDescriptorResolver(resolutionFacade) + return when { + method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method)) + else -> resolver?.resolveMethod(JavaMethodImpl(method)) + } +} + +@JvmOverloads +fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? { + val psiClass = originalElement as? PsiClass ?: return null + return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass)) +} + +@JvmOverloads +fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? { + val field = originalElement as? PsiField ?: return null + return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field)) +} + +@JvmOverloads +fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + return when (this) { + is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade) + is PsiClass -> getJavaClassDescriptor(resolutionFacade) + is PsiMethod -> getJavaMethodDescriptor(resolutionFacade) + is PsiField -> getJavaFieldDescriptor(resolutionFacade) + else -> null + } +} + +@JvmOverloads +fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { + val callable = unwrapped + return when (callable) { + is PsiMember -> getJavaMemberDescriptor(resolutionFacade) + is KtDeclaration -> { + val descriptor = resolutionFacade.resolveToDescriptor(callable) + if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor + } + else -> null + } +} + +private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? { + return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java) +} + +private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? { + return getContainingScope(method) + ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES) + ?.filterIsInstance<DeclarationDescriptorWithSource>() + ?.findByJavaElement(method) +} + +private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? { + return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor) +} + +private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? { + return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field) +} + +private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? { + val containingClass = resolveClass(member.containingClass) + return if (member.isStatic) + containingClass?.staticScope + else + containingClass?.defaultType?.memberScope +} + +private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElement(javaElement: JavaElement): T? { + return firstOrNull { member -> + val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement + when { + memberJavaElement == javaElement -> + true + memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> -> + memberJavaElement.psi.isEquivalentTo(javaElement.psi) + else -> + false + } + } +} + +fun PsiElement.javaResolutionFacade() = + KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatforms.defaultJvmPlatform)!! diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt new file mode 100644 index 000000000..402018102 --- /dev/null +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -0,0 +1,83 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaConfiguration.PackageOptions +import ru.yole.jkid.deserialization.deserialize +import java.io.File +import java.util.function.BiConsumer + + +fun parsePerPackageOptions(arg: String): List<PackageOptions> { + if (arg.isBlank()) return emptyList() + + return arg.split(";").map { it.split(",") }.map { + val prefix = it.first() + if (prefix == "") + throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead") + val args = it.subList(1, it.size) + val deprecated = args.find { it.endsWith("deprecated") }?.startsWith("+") ?: true + val reportUndocumented = args.find { it.endsWith("warnUndocumented") }?.startsWith("+") ?: true + val privateApi = args.find { it.endsWith("privateApi") }?.startsWith("+") ?: false + val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") ?: false + PackageOptionsImpl(prefix, includeNonPublic = privateApi, reportUndocumented = reportUndocumented, skipDeprecated = !deprecated, suppress = suppress) + } +} + +class DokkaBootstrapImpl : DokkaBootstrap { + + private class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { + override fun info(message: String) { + consumer.accept("info", message) + } + + override fun warn(message: String) { + consumer.accept("warn", message) + } + + override fun error(message: String) { + consumer.accept("error", message) + } + } + + lateinit var generator: DokkaGenerator + + override fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String) + = configure(DokkaProxyLogger(logger), deserialize<DokkaConfigurationImpl>(serializedConfigurationJSON)) + + fun configure(logger: DokkaLogger, configuration: DokkaConfiguration) = with(configuration) { + generator = DokkaGenerator( + logger, + classpath, + sourceRoots, + samples, + includes, + moduleName, + DocumentationOptions( + outputDir = outputDir, + outputFormat = format, + includeNonPublic = includeNonPublic, + includeRootPackage = includeRootPackage, + reportUndocumented = reportUndocumented, + skipEmptyPackages = skipEmptyPackages, + skipDeprecated = skipDeprecated, + jdkVersion = jdkVersion, + generateClassIndexPage = generateClassIndexPage, + generatePackageIndexPage = generatePackageIndexPage, + sourceLinks = sourceLinks, + impliedPlatforms = impliedPlatforms, + perPackageOptions = perPackageOptions, + externalDocumentationLinks = externalDocumentationLinks, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + languageVersion = languageVersion, + apiVersion = apiVersion, + cacheRoot = cacheRoot, + suppressedFiles = suppressedFiles.map { File(it) }.toSet(), + collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries, + outlineRoot = outlineRoot, + dacRoot = dacRoot + ) + ) + } + + override fun generate() = generator.generate() +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt new file mode 100644 index 000000000..d78d4a0c1 --- /dev/null +++ b/core/src/main/kotlin/Formats/AnalysisComponents.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Binder +import org.jetbrains.dokka.* +import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider +import org.jetbrains.dokka.KotlinElementSignatureProvider +import org.jetbrains.dokka.ElementSignatureProvider +import org.jetbrains.dokka.Samples.DefaultSampleProcessingService +import org.jetbrains.dokka.Samples.SampleProcessingService +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.toType +import kotlin.reflect.KClass + + +interface DefaultAnalysisComponentServices { + val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder> + val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder> + val sampleProcessingService: KClass<out SampleProcessingService> + val elementSignatureProvider: KClass<out ElementSignatureProvider> +} + +interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { + override fun configureAnalysis(binder: Binder): Unit = with(binder) { + bind<ElementSignatureProvider>() toType elementSignatureProvider + bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass + bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass + bind<SampleProcessingService>() toType sampleProcessingService + } +} + + +object KotlinAsJava : DefaultAnalysisComponentServices { + override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class + override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class + override val sampleProcessingService = DefaultSampleProcessingService::class + override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class +} + + +object KotlinAsKotlin : DefaultAnalysisComponentServices { + override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class + override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class + override val sampleProcessingService = DefaultSampleProcessingService::class + override val elementSignatureProvider = KotlinElementSignatureProvider::class +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/DacHtmlFormat.kt b/core/src/main/kotlin/Formats/DacHtmlFormat.kt new file mode 100644 index 000000000..e2399435b --- /dev/null +++ b/core/src/main/kotlin/Formats/DacHtmlFormat.kt @@ -0,0 +1,949 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Inject +import com.google.inject.name.Named +import kotlinx.html.* +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Samples.DevsiteSampleProcessingService +import org.jetbrains.dokka.Kotlin.ParameterInfoNode +import org.jetbrains.dokka.Utilities.firstSentence +import java.lang.Math.max +import java.net.URI +import kotlin.reflect.KClass + +/** + * On Devsite, certain headers and footers are needed for generating Devsite metadata. + */ +class DevsiteHtmlTemplateService @Inject constructor( + @Named("outlineRoot") val outlineRoot: String, + @Named("dacRoot") val dacRoot: String +) : JavaLayoutHtmlTemplateService { + override fun composePage(page: JavaLayoutHtmlFormatOutputBuilder.Page, tagConsumer: TagConsumer<Appendable>, headContent: HEAD.() -> Unit, bodyContent: BODY.() -> Unit) { + tagConsumer.html { + attributes["devsite"] = "true" + head { + headContent() + title { + +when (page) { + is JavaLayoutHtmlFormatOutputBuilder.Page.ClassIndex -> "Class Index" + is JavaLayoutHtmlFormatOutputBuilder.Page.ClassPage -> page.node.nameWithOuterClass() + is JavaLayoutHtmlFormatOutputBuilder.Page.PackageIndex -> "Package Index" + is JavaLayoutHtmlFormatOutputBuilder.Page.PackagePage -> page.node.nameWithOuterClass() + } + } + unsafe { +"{% setvar book_path %}${dacRoot}/${outlineRoot}_book.yaml{% endsetvar %}\n{% include \"_shared/_reference-head-tags.html\" %}\n" } + } + body { + bodyContent() + } + } + } +} + +class DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl @javax.inject.Inject constructor( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val templateService: JavaLayoutHtmlTemplateService, + val logger: DokkaLogger +) : JavaLayoutHtmlFormatOutputBuilderFactory { + override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder { + return createOutputBuilder(output, uriProvider.mainUri(node)) + } + + override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder { + return DevsiteLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) + } +} + +class DevsiteLayoutHtmlFormatOutputBuilder( + output: Appendable, + languageService: LanguageService, + uriProvider: JavaLayoutHtmlUriProvider, + templateService: JavaLayoutHtmlTemplateService, + logger: DokkaLogger, + uri: URI +) : JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) { + override fun FlowContent.fullMemberDocs(node: DocumentationNode) { + fullMemberDocs(node, node) + } + + override fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) { + a { + attributes["name"] = uriNode.signatureForAnchor(logger).anchorEncoded() + } + div(classes = "api apilevel-${node.apiLevel.name}") { + attributes["data-version-added"] = node.apiLevel.name + h3(classes = "api-name") { + //id = node.signatureForAnchor(logger).urlEncoded() + +node.prettyName + } + apiAndDeprecatedVersions(node) + pre(classes = "api-signature no-pretty-print") { renderedSignature(node, LanguageService.RenderMode.FULL) } + deprecationWarningToMarkup(node, prefix = true) + nodeContent(node, uriNode) + node.constantValue()?.let { value -> + pre { + +"Value: " + code { +value } + } + } + for ((name, sections) in node.content.sections.groupBy { it.tag }) { + when (name) { + ContentTags.Return -> { + table(classes = "responsive") { + tbody { + tr { + th { + colSpan = "2" + +name + } + } + sections.forEach { + tr { + if (it.children.size > 0) { + td { + val firstChild = it.children.first() + if (firstChild is ContentBlock && + firstChild.children.size == 3 && + firstChild.children[0] is NodeRenderContent && + firstChild.children[1] is ContentSymbol && + firstChild.children[2] is ContentText) { + // it.children is expected to have two items + // First should have 3 children of its own: + // - NodeRenderContent is the return type + // - ContentSymbol - ":" + // - ContentText - " " + // We want to only use NodeRenderContent in a separate <td> and + // <code> to get proper formatting in DAC. + code { + metaMarkup(listOf(firstChild.children[0])) + } + } else { + metaMarkup(listOf(firstChild)) + } + } + td { + if (it.children.size > 1) { + metaMarkup(it.children.subList(1, it.children.size)) + } + } + } + } + } + } + } + } + ContentTags.Parameters -> { + table(classes = "responsive") { + tbody { + tr { + th { + colSpan = "2" + +name + } + } + sections.forEach { section -> + tr { + td { + val parameterInfoNode = section.children.find { it is ParameterInfoNode } as? ParameterInfoNode + // If there is no info found, just display the parameter + // name. + if (parameterInfoNode?.parameterContent == null) { + code { + section.subjectName?.let { +it } + } + } else { + // Add already marked up type information here + metaMarkup( + listOf(parameterInfoNode.parameterContent!!) + ) + } + } + td { + metaMarkup(section.children) + } + } + } + } + } + } + ContentTags.SeeAlso -> { + div { + p { + b { + +name + } + } + ul(classes = "nolist") { + sections.filter {it.tag == "See Also"}.forEach { + it.children.forEach { child -> + if (child is ContentNodeLazyLink || child is ContentExternalLink) { + li { + code { + contentNodeToMarkup(child) // Wrap bare links in listItems. + } // bare links come from the java-to-kotlin parser. + } + } + else if (child is ContentUnorderedList) { + metaMarkup(child.children) // Already wrapped in listItems. + } // this is how we want things to look. No parser currently does this (yet). + else if (child is ContentParagraph) { + li{ + code { + metaMarkup (child.children) // Replace paragraphs with listItems. + } // paragraph-wrapped links come from the kotlin parser + } + } // NOTE: currently the java-to-java parser does not add See Also links! + } + } + } + } + } + ContentTags.Exceptions -> { + table(classes = "responsive") { + tbody { + tr { + th { + colSpan = "2" + +name + } + } + sections.forEach { + tr { + td { + code { + it.subjectName?.let { +it } + } + } + td { + metaMarkup(it.children) + } + } + } + } + } + } + } + } + } + } + + override fun summary(node: DocumentationNode) = node.firstSentenceOfSummary() + + fun TBODY.xmlAttributeRow(attr: DocumentationNode) = tr { + td { + a(href = attr) { + code { + +attr.attributeRef!!.name + } + } + } + td { + +attr.attributeRef!!.firstSentence() + } + } + + protected fun FlowContent.fullAttributeDocs( + attributes: List<DocumentationNode>, + header: String + ) { + if (attributes.none()) return + h2 { + +header + } + attributes.forEach { + fullMemberDocs(it.attributeRef!!, it) + } + } + + override fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) { + fullAttributeDocs(attributes, "XML attributes") + fullMemberDocs(enumValues, "Enum values") + fullMemberDocs(constants, "Constants") + + constructors.forEach { (visibility, group) -> + fullMemberDocs(group, "${visibility.capitalize()} constructors") + } + + functions.forEach { (visibility, group) -> + fullMemberDocs(group, "${visibility.capitalize()} methods") + } + + fullMemberDocs(properties, "Properties") + + fields.forEach { (visibility, group) -> + fullMemberDocs(group, "${visibility.capitalize()} fields") + } + if (!hasMeaningfulCompanion) { + fullMemberDocs(companionFunctions, "Companion functions") + fullMemberDocs(companionProperties, "Companion properties") + } + } + + override fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) { + this@classLikeSummaries.summaryNodeGroup( + nestedClasses, + header = "Nested classes", + summaryId = "nestedclasses", + tableClass = "responsive", + headerAsRow = true + ) { + nestedClassSummaryRow(it) + } + + this@classLikeSummaries.summaryNodeGroup( + attributes, + header="XML attributes", + summaryId="lattrs", + tableClass = "responsive", + headerAsRow = true + ) { + xmlAttributeRow(it) + } + + this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers( + superClasses = inheritedAttributes.entries, + header="Inherited XML attributes", + tableId="inhattrs", + tableClass = "responsive", + row = { inheritedXmlAttributeRow(it)} + ) + + this@classLikeSummaries.summaryNodeGroup( + constants, + header = "Constants", + summaryId = "constants", + tableClass = "responsive", + headerAsRow = true + ) { propertyLikeSummaryRow(it) } + + this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers( + superClasses = inheritedConstants.entries, + header = "Inherited constants", + tableId = "inhconstants", + tableClass = "responsive constants inhtable", + row = { inheritedMemberRow(it) } + ) + + constructors.forEach { (visibility, group) -> + this@classLikeSummaries.summaryNodeGroup( + group, + header = "${visibility.capitalize()} constructors", + summaryId = "${visibility.take(3)}ctors", + tableClass = "responsive", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + } + + this@classLikeSummaries.summaryNodeGroup( + enumValues, + header = "Enum values", + summaryId = "enumvalues", + tableClass = "responsive", + headerAsRow = true + ) { + propertyLikeSummaryRow(it, showSignature = false) + } + + functions.forEach { (visibility, group) -> + this@classLikeSummaries.summaryNodeGroup( + group, + header = "${visibility.capitalize()} methods", + summaryId = "${visibility.take(3)}methods", + tableClass = "responsive", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + } + + this@classLikeSummaries.summaryNodeGroup( + companionFunctions, + header = "Companion functions", + summaryId = "compmethods", + tableClass = "responsive", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + + this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers( + superClasses = inheritedFunctionsByReceiver.entries, + header = "Inherited functions", + tableId = "inhmethods", + tableClass = "responsive", + row = { inheritedMemberRow(it) } + ) + + this@classLikeSummaries.summaryNodeGroup( + extensionFunctions.entries, + header = "Extension functions", + summaryId = "extmethods", + tableClass = "responsive", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } + this@classLikeSummaries.summaryNodeGroup( + inheritedExtensionFunctions.entries, + header = "Inherited extension functions", + summaryId = "inhextmethods", + tableClass = "responsive", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } + + fields.forEach { (visibility, group) -> + this@classLikeSummaries.summaryNodeGroup( + group, + header = "${visibility.capitalize()} fields", + summaryId = "${visibility.take(3)}fields", + tableClass = "responsive", + headerAsRow = true + ) { propertyLikeSummaryRow(it) } + } + + this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers( + superClasses = inheritedFieldsByReceiver.entries, + header = "Inherited fields", + tableId = "inhfields", + tableClass = "responsive properties inhtable", + row = { inheritedMemberRow(it) } + ) + + this@classLikeSummaries.summaryNodeGroup( + properties, + header = "Properties", + summaryId = "properties", + tableClass = "responsive", + headerAsRow = true + ) { propertyLikeSummaryRow(it) } + + + this@classLikeSummaries.summaryNodeGroup( + companionProperties, + "Companion properties", + headerAsRow = true + ) { + propertyLikeSummaryRow(it) + } + + this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers( + superClasses = inheritedPropertiesByReceiver.entries, + header = "Inherited properties", + tableId = "inhfields", + tableClass = "responsive properties inhtable", + row = { inheritedMemberRow(it) } + ) + + this@classLikeSummaries.summaryNodeGroup( + extensionProperties.entries, + "Extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + + this@classLikeSummaries.summaryNodeGroup( + inheritedExtensionProperties.entries, + "Inherited extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + } + + fun <T> FlowContent.summaryNodeGroup( + nodes: Iterable<T>, + header: String, + headerAsRow: Boolean, + summaryId: String, + tableClass: String = "responsive", + row: TBODY.(T) -> Unit + ) { + if (nodes.none()) return + if (!headerAsRow) { + h2 { +header } + } + table(classes = tableClass) { + id = summaryId + tbody { + if (headerAsRow) { + developerHeading(header) + } + nodes.forEach { node -> + row(node) + } + } + } + } + + override fun FlowContent.contentBlockCode(content: ContentBlockCode) { + pre { + attributes["class"] = "prettyprint" + contentNodesToMarkup(content.children) + } + } + + override fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) { + pre { + attributes["class"] = "prettyprint" + contentNodesToMarkup(content.importsBlock.children) + +"\n\n" + contentNodesToMarkup(content.children) + } + } + + override fun generatePackage(page: Page.PackagePage) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +page.node.name } + nodeContent(page.node) + this@composePage.summaryNodeGroup(page.interfaces.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Interfaces", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.classes.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Classes", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.exceptions.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Exceptions", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.typeAliases.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Type-aliases", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.annotations.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Annotations", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.enums.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Enums", headerAsRow = false) { classLikeRow(it) } + + this@composePage.summaryNodeGroup( + page.constants.sortedBy { it.name }, + "Top-level constants summary", + headerAsRow = false + ) { + propertyLikeSummaryRow(it) + } + + this@composePage.summaryNodeGroup( + page.functions.sortedBy { it.name }, + "Top-level functions summary", + headerAsRow = false + ) { + functionLikeSummaryRow(it) + } + + this@composePage.summaryNodeGroup( + page.properties.sortedBy { it.name }, + "Top-level properties summary", + headerAsRow = false + ) { + propertyLikeSummaryRow(it) + } + + summaryNodeGroupForExtensions("Extension functions summary", page.extensionFunctions.entries) + summaryNodeGroupForExtensions("Extension properties summary", page.extensionProperties.entries) + + fullMemberDocs(page.constants.sortedBy { it.name }, "Top-level constants") + fullMemberDocs(page.functions.sortedBy { it.name }, "Top-level functions") + fullMemberDocs(page.properties.sortedBy { it.name }, "Top-level properties") + fullMemberDocs(page.extensionFunctions.values.flatten().sortedBy { it.name }, "Extension functions") + fullMemberDocs(page.extensionProperties.values.flatten().sortedBy { it.name }, "Extension properties") + } + ) + + private fun TBODY.inheritedXmlAttributeRow(inheritedMember: DocumentationNode) { + tr(classes = "api apilevel-${inheritedMember.attributeRef!!.apiLevel.name}") { + attributes["data-version-added"] = "${inheritedMember.apiLevel}" + td { + code { + a(href = inheritedMember) { +inheritedMember.attributeRef!!.name } + } + } + td { + attributes["width"] = "100%" + p { + nodeContent(inheritedMember.attributeRef!!, inheritedMember) + } + } + } + } + + private fun TBODY.inheritedMemberRow(inheritedMember: DocumentationNode) { + tr(classes = "api apilevel-${inheritedMember.apiLevel.name}") { + attributes["data-version-added"] = "${inheritedMember.apiLevel}" + val type = inheritedMember.detailOrNull(NodeKind.Type) + td { + code { + type?.let { + renderedSignature(it, LanguageService.RenderMode.SUMMARY) + } + } + } + td { + attributes["width"] = "100%" + code { + a(href = inheritedMember) { +inheritedMember.name } + if (inheritedMember.kind == NodeKind.Function) { + shortFunctionParametersList(inheritedMember) + } + } + p { + nodeContent(inheritedMember) + } + } + } + } + + private fun FlowContent.expandableSummaryNodeGroupForInheritedMembers( + tableId: String, + header: String, + tableClass: String, + superClasses: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>, + row: TBODY.(inheritedMember: DocumentationNode) -> Unit + ) { + if (superClasses.none()) return + table(classes = tableClass) { + attributes["id"] = tableId + tbody { + developerHeading(header) + superClasses.forEach { (superClass, members) -> + tr(classes = "api apilevel-${superClass.apiLevel.name}") { + td { + attributes["colSpan"] = "2" + div(classes = "expandable jd-inherited-apis") { + span(classes = "expand-control exw-expanded") { + +"From class " + code { + a(href = superClass) { +superClass.name } + } + } + table(classes = "responsive exw-expanded-content") { + tbody { + members.forEach { inheritedMember -> + row(inheritedMember) + } + } + } + } + } + } + } + } + } + } + + private fun FlowContent.summaryNodeGroupForExtensions( + header: String, + receivers: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>> + ) { + if (receivers.none()) return + h2 { +header } + div { + receivers.forEach { + table { + tr { + td { + attributes["colSpan"] = "2" + +"For " + a(href = it.key) { +it.key.name } + } + } + it.value.forEach { node -> + tr { + if (node.kind != NodeKind.Constructor) { + td { + modifiers(node) + renderedSignature(node.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY) + } + } + td { + div { + code { + val receiver = node.detailOrNull(NodeKind.Receiver) + if (receiver != null) { + renderedSignature(receiver.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY) + +"." + } + a(href = node) { +node.name } + shortFunctionParametersList(node) + } + } + + nodeSummary(node) + } + } + } + } + } + } + } + + + override fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +"Package Index" } + table { + tbody { + for (node in page.packages) { + tr { + td { + a(href = uriProvider.linkTo(node, uri)) { +node.name } + } + } + } + } + } + } + ) + + override fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +"Class Index" } + + p { + +"These are all the API classes. See all " + a(href="packages.html") { + +"API packages." + } + } + + div(classes = "jd-letterlist") { + page.classesByFirstLetter.forEach { (letter) -> + +"\n " + a(href = "#letter_$letter") { +letter } + unsafe { + raw(" ") + } + } + +"\n " + } + + page.classesByFirstLetter.forEach { (letter, classes) -> + h2 { + id = "letter_$letter" + +letter + } + table { + tbody { + for (node in classes) { + tr { + td { + a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() } + } + td { + if (!deprecatedIndexSummary(node)) { + nodeSummary(node) + } + } + } + } + } + } + } + } + ) + + override fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) { + table(classes = "jd-inheritance-table") { + var level = superclasses.size + superclasses.forEach { + tr { + var spaceColumns = max(superclasses.size - 1 - level, 0) + while (spaceColumns > 0) { + td(classes = "jd-inheritance-space") { + +" " + } + spaceColumns-- + } + if (it != superclasses.first()) { + td(classes = "jd-inheritance-space") { + +" ↳" + } + } + td(classes = "jd-inheritance-class-cell") { + attributes["colSpan"] = "$level" + qualifiedTypeReference(it) + } + } + level-- + } + } + } + + override fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) { + if (inheritors.isEmpty()) return + + // The number of subclasses in collapsed view before truncating and adding a "and xx others". + // See https://developer.android.com/reference/android/view/View for an example. + val numToShow = 12 + + table(classes = "jd-sumtable jd-sumtable-subclasses") { + tbody { + tr { + td { + div(classes = "expandable") { + span(classes = "expand-control") { + if (direct) + +"Known Direct Subclasses" + else + +"Known Indirect Subclasses" + } + div(classes = "showalways") { + attributes["id"] = if (direct) "subclasses-direct" else "subclasses-indirect" + + inheritors.take(numToShow).forEach { inheritor -> + a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() } + if (inheritor != inheritors.last()) +", " + } + + if (inheritors.size > numToShow) { + +"and ${inheritors.size - numToShow} others." + } + } + div(classes = "exw-expanded-content") { + attributes["id"] = if (direct) "subclasses-direct-summary" else "subclasses-indirect-summary" + table(classes = "jd-sumtable-expando") { + inheritors.forEach { inheritor -> + tr(classes = "api api-level-${inheritor.apiLevel.name}") { + attributes["data-version-added"] = inheritor.apiLevel.name + td(classes = "jd-linkcol") { + a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() } + } + td(classes = "jd-descrcol") { + attributes["width"] = "100%" + nodeSummary(inheritor) + } + } + } + } + } + } + } + } + } + } + } + + + fun DocumentationNode.firstSentenceOfSummary(): ContentNode { + + fun Sequence<ContentNode>.flatten(): Sequence<ContentNode> { + return flatMap { + when (it) { + is ContentParagraph -> it.children.asSequence().flatten() + else -> sequenceOf(it) + } + } + } + + fun ContentNode.firstSentence(): ContentText? = when(this) { + is ContentText -> ContentText(text.firstSentence()) + else -> null + } + + val elements = sequenceOf(summary).flatten() + fun containsDot(it: ContentNode) = (it as? ContentText)?.text?.contains(".") == true + + val paragraph = ContentParagraph() + (elements.takeWhile { !containsDot(it) } + elements.firstOrNull { containsDot(it) }?.firstSentence()).forEach { + if (it != null) { + paragraph.append(it) + } + } + if (paragraph.isEmpty()) { + return ContentEmpty + } + + return paragraph + } + + fun DocumentationNode.firstSentence(): String { + val sb = StringBuilder() + addContentNodeToStringBuilder(content, sb) + return sb.toString().firstSentence() + } + + private fun addContentNodesToStringBuilder(content: List<ContentNode>, sb: StringBuilder): Unit = + content.forEach { addContentNodeToStringBuilder(it, sb) } + + private fun addContentNodeToStringBuilder(content: ContentNode, sb: StringBuilder) { + when (content) { + is ContentText -> sb.appendWith(content.text) + is ContentSymbol -> sb.appendWith(content.text) + is ContentKeyword -> sb.appendWith(content.text) + is ContentIdentifier -> sb.appendWith(content.text) + is ContentEntity -> sb.appendWith(content.text) + + is ContentHeading -> addContentNodesToStringBuilder(content.children, sb) + is ContentStrong -> addContentNodesToStringBuilder(content.children, sb) + is ContentStrikethrough -> addContentNodesToStringBuilder(content.children, sb) + is ContentEmphasis -> addContentNodesToStringBuilder(content.children, sb) + is ContentOrderedList -> addContentNodesToStringBuilder(content.children, sb) + is ContentUnorderedList -> addContentNodesToStringBuilder(content.children, sb) + is ContentListItem -> addContentNodesToStringBuilder(content.children, sb) + is ContentCode -> addContentNodesToStringBuilder(content.children, sb) + is ContentBlockSampleCode -> addContentNodesToStringBuilder(content.children, sb) + is ContentBlockCode -> addContentNodesToStringBuilder(content.children, sb) + is ContentParagraph -> addContentNodesToStringBuilder(content.children, sb) + is ContentNodeLink -> addContentNodesToStringBuilder(content.children, sb) + is ContentBookmark -> addContentNodesToStringBuilder(content.children, sb) + is ContentExternalLink -> addContentNodesToStringBuilder(content.children, sb) + is ContentLocalLink -> addContentNodesToStringBuilder(content.children, sb) + is ContentSection -> { } + is ContentBlock -> addContentNodesToStringBuilder(content.children, sb) + } + } + + private fun StringBuilder.appendWith(text: String, delimiter: String = " ") { + if (this.length == 0) { + append(text) + } else { + append(delimiter) + append(text) + } + } +} + +fun TBODY.developerHeading(header: String) { + tr { + th { + attributes["colSpan"] = "2" + +header + } + } +} + +class DacFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin { + override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class + + override val outlineFactoryClass = DacOutlineFormatter::class + override val languageServiceClass = KotlinLanguageService::class + override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class + override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class + override val sampleProcessingService = DevsiteSampleProcessingService::class +} + + +class DacAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava { + override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class + + override val outlineFactoryClass = DacOutlineFormatter::class + override val languageServiceClass = NewJavaLanguageService::class + override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class + override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class +} diff --git a/core/src/main/kotlin/Formats/DacOutlineService.kt b/core/src/main/kotlin/Formats/DacOutlineService.kt new file mode 100644 index 000000000..e249c39f7 --- /dev/null +++ b/core/src/main/kotlin/Formats/DacOutlineService.kt @@ -0,0 +1,395 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Inject +import org.jetbrains.dokka.* +import java.net.URI +import com.google.inject.name.Named +import org.jetbrains.kotlin.cfg.pseudocode.AllTypes + + +interface DacOutlineFormatService { + fun computeOutlineURI(node: DocumentationNode): URI + fun format(to: Appendable, node: DocumentationNode) +} + +class DacOutlineFormatter @Inject constructor( + uriProvider: JavaLayoutHtmlUriProvider, + languageService: LanguageService, + @Named("dacRoot") dacRoot: String, + @Named("generateClassIndex") generateClassIndex: Boolean, + @Named("generatePackageIndex") generatePackageIndex: Boolean +) : JavaLayoutHtmlFormatOutlineFactoryService { + val tocOutline = TocOutlineService(uriProvider, languageService, dacRoot, generateClassIndex, generatePackageIndex) + val outlines = listOf(tocOutline) + + override fun generateOutlines(outputProvider: (URI) -> Appendable, nodes: Iterable<DocumentationNode>) { + for (node in nodes) { + for (outline in outlines) { + val uri = outline.computeOutlineURI(node) + val output = outputProvider(uri) + outline.format(output, node) + } + } + } +} + +/** + * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each + * index.html file in the doc tree. + */ +class BookOutlineService( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val dacRoot: String, + val generateClassIndex: Boolean, + val generatePackageIndex: Boolean +) : DacOutlineFormatService { + override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_book.yaml") + + override fun format(to: Appendable, node: DocumentationNode) { + appendOutline(to, listOf(node)) + } + + var outlineLevel = 0 + + /** Appends formatted outline to [StringBuilder](to) using specified [location] */ + fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) { + if (outlineLevel == 0) to.appendln("reference:") + for (node in nodes) { + appendOutlineHeader(node, to) + val subPackages = node.members.filter { + it.kind == NodeKind.Package + } + if (subPackages.any()) { + val sortedMembers = subPackages.sortedBy { it.name.toLowerCase() } + appendOutlineLevel(to) { + appendOutline(to, sortedMembers) + } + } + + } + } + + fun appendOutlineHeader(node: DocumentationNode, to: Appendable) { + if (node is DocumentationModule) { + to.appendln("- title: Package Index") + to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}") + to.appendln(" status_text: no-toggle") + } else { + to.appendln("- title: ${languageService.renderName(node)}") + to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(node)}") + to.appendln(" status_text: no-toggle") + } + } + + fun appendOutlineLevel(to: Appendable, body: () -> Unit) { + outlineLevel++ + body() + outlineLevel-- + } +} + +/** + * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each + * index.html file in the doc tree. + */ +class TocOutlineService( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val dacRoot: String, + val generateClassIndex: Boolean, + val generatePackageIndex: Boolean +) : DacOutlineFormatService { + override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_toc.yaml") + + override fun format(to: Appendable, node: DocumentationNode) { + appendOutline(to, listOf(node)) + } + + var outlineLevel = 0 + + /** Appends formatted outline to [StringBuilder](to) using specified [location] */ + fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) { + if (outlineLevel == 0) to.appendln("toc:") + for (node in nodes) { + appendOutlineHeader(node, to) + val subPackages = node.members.filter { + it.kind == NodeKind.Package + } + if (subPackages.any()) { + val sortedMembers = subPackages.sortedBy { it.nameWithOuterClass() } + appendOutlineLevel { + appendOutline(to, sortedMembers) + } + } + } + } + + fun appendOutlineHeader(node: DocumentationNode, to: Appendable) { + if (node is DocumentationModule) { + if (generateClassIndex) { + node.members.filter { it.kind == NodeKind.AllTypes }.firstOrNull()?.let { + to.appendln("- title: Class Index") + to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(it).resolve("classes.html")}") + to.appendln() + } + } + if (generatePackageIndex) { + to.appendln("- title: Package Index") + to.appendln(" path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}") + to.appendln() + } + } else if (node.kind != NodeKind.AllTypes && !(node is DocumentationModule)) { + to.appendln("- title: ${languageService.renderName(node)}") + to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(node)}") + to.appendln() + var addedSectionHeader = false + for (kind in NodeKind.classLike) { + val members = node.getMembersOfKinds(kind) + if (members.isNotEmpty()) { + if (!addedSectionHeader) { + to.appendln(" section:") + addedSectionHeader = true + } + to.appendln(" - title: ${kind.pluralizedName()}") + to.appendln() + to.appendln(" section:") + members.sortedBy { it.nameWithOuterClass().toLowerCase() }.forEach { member -> + to.appendln(" - title: ${languageService.renderNameWithOuterClass(member)}") + to.appendln(" path: $dacRoot${uriProvider.mainUriOrWarn(member)}".trimEnd('#')) + to.appendln() + } + } + } + to.appendln().appendln() + } + } + + fun appendOutlineLevel(body: () -> Unit) { + outlineLevel++ + body() + outlineLevel-- + } +} + +class DacNavOutlineService constructor( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val dacRoot: String +) : DacOutlineFormatService { + override fun computeOutlineURI(node: DocumentationNode): URI = + uriProvider.outlineRootUri(node).resolve("navtree_data.js") + + override fun format(to: Appendable, node: DocumentationNode) { + to.append("var NAVTREE_DATA = ").appendNavTree(node.members).append(";") + } + + private fun Appendable.appendNavTree(nodes: Iterable<DocumentationNode>): Appendable { + append("[ ") + var first = true + for (node in nodes) { + if (!first) append(", ") + first = false + val interfaces = node.getMembersOfKinds(NodeKind.Interface) + val classes = node.getMembersOfKinds(NodeKind.Class) + val objects = node.getMembersOfKinds(NodeKind.Object) + val annotations = node.getMembersOfKinds(NodeKind.AnnotationClass) + val enums = node.getMembersOfKinds(NodeKind.Enum) + val exceptions = node.getMembersOfKinds(NodeKind.Exception) + + append("[ \"${node.name}\", \"$dacRoot${uriProvider.tryGetMainUri(node)}\", [ ") + var needComma = false + if (interfaces.firstOrNull() != null) { + appendNavTreePagesOfKind("Interfaces", interfaces) + needComma = true + } + if (classes.firstOrNull() != null) { + if (needComma) append(", ") + appendNavTreePagesOfKind("Classes", classes) + needComma = true + } + if (objects.firstOrNull() != null) { + if (needComma) append(", ") + appendNavTreePagesOfKind("Objects", objects) + } + if (annotations.firstOrNull() != null) { + if (needComma) append(", ") + appendNavTreePagesOfKind("Annotations", annotations) + needComma = true + } + if (enums.firstOrNull() != null) { + if (needComma) append(", ") + appendNavTreePagesOfKind("Enums", enums) + needComma = true + } + if (exceptions.firstOrNull() != null) { + if (needComma) append(", ") + appendNavTreePagesOfKind("Exceptions", exceptions) + } + append(" ] ]") + } + append(" ]") + return this + } + + private fun Appendable.appendNavTreePagesOfKind(kindTitle: String, + nodesOfKind: Iterable<DocumentationNode>): Appendable { + append("[ \"$kindTitle\", null, [ ") + var started = false + for (node in nodesOfKind) { + if (started) append(", ") + started = true + appendNavTreeChild(node) + } + append(" ], null, null ]") + return this + } + + private fun Appendable.appendNavTreeChild(node: DocumentationNode): Appendable { + append("[ \"${node.nameWithOuterClass()}\", \"${dacRoot}${uriProvider.tryGetMainUri(node)}\"") + append(", null, null, null ]") + return this + } +} + +class DacSearchOutlineService( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val dacRoot: String +) : DacOutlineFormatService { + + override fun computeOutlineURI(node: DocumentationNode): URI = + uriProvider.outlineRootUri(node).resolve("lists.js") + + override fun format(to: Appendable, node: DocumentationNode) { + val pageNodes = node.getAllPageNodes() + var id = 0 + to.append("var KTX_CORE_DATA = [\n") + var first = true + for (pageNode in pageNodes) { + if (pageNode.kind == NodeKind.Module) continue + if (!first) to.append(", \n") + first = false + to.append(" { " + + "id:$id, " + + "label:\"${pageNode.qualifiedName()}\", " + + "link:\"${dacRoot}${uriProvider.tryGetMainUri(pageNode)}\", " + + "type:\"${pageNode.getClassOrPackage()}\", " + + "deprecated:\"false\" }") + id++ + } + to.append("\n];") + } + + private fun DocumentationNode.getClassOrPackage(): String = + if (hasOwnPage()) + "class" + else if (isPackage()) { + "package" + } else { + "" + } + + private fun DocumentationNode.getAllPageNodes(): Iterable<DocumentationNode> { + val allPageNodes = mutableListOf<DocumentationNode>() + recursiveSetAllPageNodes(allPageNodes) + return allPageNodes + } + + private fun DocumentationNode.recursiveSetAllPageNodes( + allPageNodes: MutableList<DocumentationNode>) { + for (child in members) { + if (child.hasOwnPage() || child.isPackage()) { + allPageNodes.add(child) + child.qualifiedName() + child.recursiveSetAllPageNodes(allPageNodes) + } + } + } + +} + +/** + * Return all children of the node who are one of the selected `NodeKind`s. It recursively fetches + * all offspring, not just immediate children. + */ +fun DocumentationNode.getMembersOfKinds(vararg kinds: NodeKind): MutableList<DocumentationNode> { + val membersOfKind = mutableListOf<DocumentationNode>() + recursiveSetMembersOfKinds(kinds, membersOfKind) + return membersOfKind +} + +private fun DocumentationNode.recursiveSetMembersOfKinds(kinds: Array<out NodeKind>, + membersOfKind: MutableList<DocumentationNode>) { + for (member in members) { + if (member.kind in kinds) { + membersOfKind.add(member) + } + member.recursiveSetMembersOfKinds(kinds, membersOfKind) + } +} + +/** + * Returns whether or not this node owns a page. The criteria for whether a node owns a page is + * similar to the way javadoc is structured. Classes, Interfaces, Enums, AnnotationClasses, + * Exceptions, and Objects (Kotlin-specific) meet the criteria. + */ +fun DocumentationNode.hasOwnPage() = + kind == NodeKind.Class || kind == NodeKind.Interface || kind == NodeKind.Enum || + kind == NodeKind.AnnotationClass || kind == NodeKind.Exception || + kind == NodeKind.Object + +/** + * In most cases, this returns the short name of the `Type`. When the Type is an inner Type, it + * prepends the name with the containing Type name(s). + * + * For example, if you have a class named OuterClass and an inner class named InnerClass, this would + * return OuterClass.InnerClass. + * + */ +fun DocumentationNode.nameWithOuterClass(): String { + val nameBuilder = StringBuilder(name) + var parent = owner + if (hasOwnPage()) { + while (parent != null && parent.hasOwnPage()) { + nameBuilder.insert(0, "${parent.name}.") + parent = parent.owner + } + } + return nameBuilder.toString() +} + +/** + * Return whether the node is a package. + */ +fun DocumentationNode.isPackage(): Boolean { + return kind == NodeKind.Package +} + +/** + * Return the 'page owner' of this node. `DocumentationNode.hasOwnPage()` defines the criteria for + * a page owner. If this node is not a page owner, then it iterates up through its ancestors to + * find the first page owner. + */ +fun DocumentationNode.pageOwner(): DocumentationNode { + if (hasOwnPage() || owner == null) { + return this + } else { + var parent: DocumentationNode = owner!! + while (!parent.hasOwnPage() && !parent.isPackage()) { + parent = parent.owner!! + } + return parent + } +} + +fun NodeKind.pluralizedName() = when(this) { + NodeKind.Class -> "Classes" + NodeKind.Interface -> "Interfaces" + NodeKind.AnnotationClass -> "Annotations" + NodeKind.Enum -> "Enums" + NodeKind.Exception -> "Exceptions" + NodeKind.Object -> "Objects" + NodeKind.TypeAlias -> "TypeAliases" + else -> "${name}s" +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/ExtraOutlineServices.kt b/core/src/main/kotlin/Formats/ExtraOutlineServices.kt new file mode 100644 index 000000000..e4eeac01a --- /dev/null +++ b/core/src/main/kotlin/Formats/ExtraOutlineServices.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka + +import java.io.File + +/** + * Outline service that is responsible for generating a single outline format. + * + * TODO: port existing implementations of ExtraOutlineService to OutlineService, and remove this. + */ +interface ExtraOutlineService { + fun getFileName(): String + fun getFile(location: Location): File + fun format(node: DocumentationNode): String +} + +/** + * Holder of all of the extra outline services needed for a StandardFormat, in addition to the main + * [OutlineFormatService]. + */ +abstract class ExtraOutlineServices(vararg val services: ExtraOutlineService) diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt new file mode 100644 index 000000000..b497fb0f5 --- /dev/null +++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt @@ -0,0 +1,42 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Binder +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.lazyBind +import org.jetbrains.dokka.Utilities.toOptional +import org.jetbrains.dokka.Utilities.toType +import kotlin.reflect.KClass + + +interface FormatDescriptorAnalysisComponent { + fun configureAnalysis(binder: Binder) +} + +interface FormatDescriptorOutputComponent { + fun configureOutput(binder: Binder) +} + +interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent + + +abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor { + + override fun configureOutput(binder: Binder): Unit = with(binder) { + bind<Generator>() toType NodeLocationAwareGenerator::class + bind<NodeLocationAwareGenerator>() toType generatorServiceClass + + bind<LanguageService>() toType languageServiceClass + + lazyBind<OutlineFormatService>() toOptional (outlineServiceClass) + lazyBind<FormatService>() toOptional formatServiceClass + lazyBind<PackageListService>() toOptional packageListServiceClass + } + + abstract val formatServiceClass: KClass<out FormatService>? + abstract val outlineServiceClass: KClass<out OutlineFormatService>? + abstract val generatorServiceClass: KClass<out FileGenerator> + abstract val packageListServiceClass: KClass<out PackageListService>? + + open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatService.kt b/core/src/main/kotlin/Formats/FormatService.kt new file mode 100644 index 000000000..63f25008f --- /dev/null +++ b/core/src/main/kotlin/Formats/FormatService.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka + +/** + * Abstract representation of a formatting service used to output documentation in desired format + * + * Bundled Formatters: + * * [HtmlFormatService] – outputs documentation to HTML format + * * [MarkdownFormatService] – outputs documentation in Markdown format + */ +interface FormatService { + /** Returns extension for output files */ + val extension: String + + /** extension which will be used for internal and external linking */ + val linkExtension: String + get() = extension + + fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder + + fun enumerateSupportFiles(callback: (resource: String, targetPath: String) -> Unit) { + } +} + +interface FormattedOutputBuilder { + /** Appends formatted content to [StringBuilder](to) using specified [location] */ + fun appendNodes(nodes: Iterable<DocumentationNode>) +} + +/** Format content to [String] using specified [location] */ +fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply { + createOutputBuilder(this, location).appendNodes(nodes) +}.toString() diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt new file mode 100644 index 000000000..036ec8564 --- /dev/null +++ b/core/src/main/kotlin/Formats/GFMFormatService.kt @@ -0,0 +1,61 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName + +open class GFMOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String> +) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { + override fun appendTable(vararg columns: String, body: () -> Unit) { + to.appendln(columns.joinToString(" | ", "| ", " |")) + to.appendln("|" + "---|".repeat(columns.size)) + body() + } + + override fun appendUnorderedList(body: () -> Unit) { + if (inTableCell) { + wrapInTag("ul", body) + } else { + super.appendUnorderedList(body) + } + } + + override fun appendOrderedList(body: () -> Unit) { + if (inTableCell) { + wrapInTag("ol", body) + } else { + super.appendOrderedList(body) + } + } + + override fun appendListItem(body: () -> Unit) { + if (inTableCell) { + wrapInTag("li", body) + } else { + super.appendListItem(body) + } + } +} + +open class GFMFormatService( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + linkExtension: String, + impliedPlatforms: List<String> +) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { + + @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String> + ) : this(generator, signatureGenerator, "md", impliedPlatforms) + + override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = + GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +} diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt new file mode 100644 index 000000000..0ad946be2 --- /dev/null +++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt @@ -0,0 +1,168 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName +import java.io.File + +open class HtmlOutputBuilder(to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>, + val templateService: HtmlTemplateService) + : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +{ + override fun appendText(text: String) { + to.append(text.htmlEscape()) + } + + override fun appendSymbol(text: String) { + to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") + } + + override fun appendKeyword(text: String) { + to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") + } + + override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { + val id = signature?.let { " id=\"$it\"" }.orEmpty() + to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>") + } + + override fun appendBlockCode(language: String, body: () -> Unit) { + val openTags = if (language.isNotBlank()) + "<pre><code class=\"lang-$language\">" + else + "<pre><code>" + wrap(openTags, "</code></pre>", body) + } + + override fun appendHeader(level: Int, body: () -> Unit) = + wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true) + override fun appendParagraph(body: () -> Unit) = + wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true) + + override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) + + override fun appendLine() { + to.appendln("<br/>") + } + + override fun appendAnchor(anchor: String) { + to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>") + } + + override fun appendTable(vararg columns: String, body: () -> Unit) = + wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true) + override fun appendTableBody(body: () -> Unit) = + wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true) + override fun appendTableRow(body: () -> Unit) = + wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true) + override fun appendTableCell(body: () -> Unit) = + wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true) + + override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) + + override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body) + override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body) + override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) + override fun appendCode(body: () -> Unit) = wrapInTag("code", body) + + override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true) + override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true) + override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true) + + override fun appendBreadcrumbSeparator() { + to.append(" / ") + } + + override fun appendNodes(nodes: Iterable<DocumentationNode>) { + templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location)) + super.appendNodes(nodes) + templateService.appendFooter(to) + } + + override fun appendNonBreakingSpace() { + to.append(" ") + } + + override fun ensureParagraph() { + + } +} + +open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + val templateService: HtmlTemplateService, + @Named(impliedPlatformsName) val impliedPlatforms: List<String>) +: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService { + + override fun enumerateSupportFiles(callback: (String, String) -> Unit) { + callback("/dokka/styles/style.css", "style.css") + } + + override fun createOutputBuilder(to: StringBuilder, location: Location) = + HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) + + override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { + templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location)) + super.appendOutline(location, to, nodes) + templateService.appendFooter(to) + } + + override fun getOutlineFileName(location: Location): File { + return File("${location.path}-outline.html") + } + + override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { + val link = ContentNodeDirectLink(node) + link.append(languageService.render(node, LanguageService.RenderMode.FULL)) + val tempBuilder = StringBuilder() + createOutputBuilder(tempBuilder, location).appendContent(link) + to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>") + } + + override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { + to.appendln("<ul>") + body() + to.appendln("</ul>") + } +} + +fun getPageTitle(nodes: Iterable<DocumentationNode>): String? { + val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } + return breakdownByLocation.keys.singleOrNull() +} + +fun formatPageTitle(node: DocumentationNode): String { + val path = node.path + val moduleName = path.first().name + if (path.size == 1) { + return moduleName + } + + val qName = qualifiedNameForPageTitle(node) + return qName + " - " + moduleName +} + +private fun qualifiedNameForPageTitle(node: DocumentationNode): String { + if (node.kind == NodeKind.Package) { + var packageName = node.qualifiedName() + if (packageName.isEmpty()) { + packageName = "root package" + } + return packageName + } + + val path = node.path + var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike } + if (pathFromToplevelMember.isEmpty()) { + pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function } + } + if (pathFromToplevelMember.isNotEmpty()) { + return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".") + } + return node.qualifiedName() +} diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt new file mode 100644 index 000000000..a65a7b18c --- /dev/null +++ b/core/src/main/kotlin/Formats/HtmlTemplateService.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka + +import java.io.File + +interface HtmlTemplateService { + fun appendHeader(to: StringBuilder, title: String?, basePath: File) + fun appendFooter(to: StringBuilder) + + companion object { + fun default(css: String? = null): HtmlTemplateService { + return object : HtmlTemplateService { + override fun appendFooter(to: StringBuilder) { + if (!to.endsWith('\n')) { + to.append('\n') + } + to.appendln("</BODY>") + to.appendln("</HTML>") + } + override fun appendHeader(to: StringBuilder, title: String?, basePath: File) { + to.appendln("<HTML>") + to.appendln("<HEAD>") + to.appendln("<meta charset=\"UTF-8\">") + if (title != null) { + to.appendln("<title>$title</title>") + } + if (css != null) { + val cssPath = basePath.resolve(css).toUnixString() + to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">") + } + to.appendln("</HEAD>") + to.appendln("<BODY>") + } + } + } + } +} + + diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt new file mode 100644 index 000000000..b94886693 --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt @@ -0,0 +1,141 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Binder +import kotlinx.html.* +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.lazyBind +import org.jetbrains.dokka.Utilities.toOptional +import org.jetbrains.dokka.Utilities.toType +import java.net.URI +import kotlin.reflect.KClass + + +abstract class JavaLayoutHtmlFormatDescriptorBase : FormatDescriptor, DefaultAnalysisComponent { + + override fun configureOutput(binder: Binder): Unit = with(binder) { + bind<Generator>() toType generatorServiceClass + bind<LanguageService>() toType languageServiceClass + bind<JavaLayoutHtmlTemplateService>() toType templateServiceClass + bind<JavaLayoutHtmlUriProvider>() toType generatorServiceClass + lazyBind<JavaLayoutHtmlFormatOutlineFactoryService>() toOptional outlineFactoryClass + bind<PackageListService>() toType packageListServiceClass + bind<JavaLayoutHtmlFormatOutputBuilderFactory>() toType outputBuilderFactoryClass + } + + val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class + abstract val languageServiceClass: KClass<out LanguageService> + abstract val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> + abstract val outlineFactoryClass: KClass<out JavaLayoutHtmlFormatOutlineFactoryService>? + abstract val packageListServiceClass: KClass<out PackageListService> + abstract val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> +} + +class JavaLayoutHtmlFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin { + override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = JavaLayoutHtmlFormatOutputBuilderFactoryImpl::class + override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class + override val languageServiceClass = KotlinLanguageService::class + override val templateServiceClass = JavaLayoutHtmlTemplateService.Default::class + override val outlineFactoryClass = null +} + +class JavaLayoutHtmlAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava { + override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = JavaLayoutHtmlFormatOutputBuilderFactoryImpl::class + override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class + override val languageServiceClass = NewJavaLanguageService::class + override val templateServiceClass = JavaLayoutHtmlTemplateService.Default::class + override val outlineFactoryClass = null +} + +interface JavaLayoutHtmlFormatOutlineFactoryService { + fun generateOutlines(outputProvider: (URI) -> Appendable, nodes: Iterable<DocumentationNode>) +} + + +interface JavaLayoutHtmlUriProvider { + fun tryGetContainerUri(node: DocumentationNode): URI? + fun tryGetMainUri(node: DocumentationNode): URI? + fun tryGetOutlineRootUri(node: DocumentationNode): URI? + fun containerUri(node: DocumentationNode): URI = tryGetContainerUri(node) ?: error("Unsupported ${node.kind}") + fun mainUri(node: DocumentationNode): URI = tryGetMainUri(node) ?: error("Unsupported ${node.kind}") + fun outlineRootUri(node: DocumentationNode): URI = tryGetOutlineRootUri(node) ?: error("Unsupported ${node.kind}") + + + fun linkTo(to: DocumentationNode, from: URI): String { + return mainUri(to).relativeTo(from).toString() + } + + fun linkToFromOutline(to: DocumentationNode, from: URI): String { + return outlineRootUri(to).relativeTo(from).toString() + } + + fun mainUriOrWarn(node: DocumentationNode): URI? = tryGetMainUri(node) ?: (null).also { + AssertionError("Not implemented mainUri for ${node.kind} (${node})").printStackTrace() + } +} + + +interface JavaLayoutHtmlTemplateService { + fun composePage( + page: JavaLayoutHtmlFormatOutputBuilder.Page, + tagConsumer: TagConsumer<Appendable>, + headContent: HEAD.() -> Unit, + bodyContent: BODY.() -> Unit + ) + + class Default : JavaLayoutHtmlTemplateService { + override fun composePage( + page: JavaLayoutHtmlFormatOutputBuilder.Page, + tagConsumer: TagConsumer<Appendable>, + headContent: HEAD.() -> Unit, + bodyContent: BODY.() -> Unit + ) { + tagConsumer.html { + head { + meta(charset = "UTF-8") + headContent() + } + body(block = bodyContent) + } + } + } +} + +val DocumentationNode.companion get() = members(NodeKind.Object).find { it.details(NodeKind.Modifier).any { it.name == "companion" } } + +fun DocumentationNode.signatureForAnchor(logger: DokkaLogger): String { + + fun StringBuilder.appendReceiverIfSo() { + detailOrNull(NodeKind.Receiver)?.let { + append("(") + append(it.detail(NodeKind.Type).qualifiedNameFromType()) + append(").") + } + } + + return when (kind) { + NodeKind.Function, NodeKind.Constructor, NodeKind.CompanionObjectFunction -> buildString { + if (kind == NodeKind.CompanionObjectFunction) { + append("Companion.") + } + appendReceiverIfSo() + append(prettyName) + details(NodeKind.Parameter).joinTo(this, prefix = "(", postfix = ")") { it.detail(NodeKind.Type).qualifiedNameFromType() } + } + NodeKind.Property, NodeKind.CompanionObjectProperty -> buildString { + if (kind == NodeKind.CompanionObjectProperty) { + append("Companion.") + } + appendReceiverIfSo() + append(name) + append(":") + append(detail(NodeKind.Type).qualifiedNameFromType()) + } + NodeKind.TypeParameter, NodeKind.Parameter -> this.detail(NodeKind.Signature).name // Todo Why not signatureForAnchor + NodeKind.Field -> name + NodeKind.EnumItem -> "ENUM_VALUE:$name" + NodeKind.Attribute -> "attr_$name" + else -> "Not implemented signatureForAnchor $this".also { logger.warn(it) } + } +} + diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt new file mode 100644 index 000000000..59d898a2a --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt @@ -0,0 +1,1171 @@ +package org.jetbrains.dokka.Formats + +import com.google.common.base.Throwables +import kotlinx.html.* +import kotlinx.html.Entities.nbsp +import kotlinx.html.stream.appendHTML +import org.jetbrains.dokka.* +import org.jetbrains.dokka.LanguageService.RenderMode.FULL +import org.jetbrains.dokka.LanguageService.RenderMode.SUMMARY +import org.jetbrains.dokka.NodeKind.Companion.classLike +import java.net.URI +import javax.inject.Inject + + +open class JavaLayoutHtmlFormatOutputBuilder( + val output: Appendable, + val languageService: LanguageService, + val uriProvider: JavaLayoutHtmlUriProvider, + val templateService: JavaLayoutHtmlTemplateService, + val logger: DokkaLogger, + val uri: URI +) { + + val htmlConsumer = output.appendHTML() + + + private fun FlowContent.hN( + level: Int, + classes: String? = null, + block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit + ) { + when (level) { + 1 -> h1(classes, block) + 2 -> h2(classes, block) + 3 -> h3(classes, block) + 4 -> h4(classes, block) + 5 -> h5(classes, block) + 6 -> h6(classes, block) + } + } + + protected open fun FlowContent.metaMarkup(content: List<ContentNode>, contextUri: URI = uri) = + contentNodesToMarkup(content, contextUri) + + protected fun FlowContent.nodeContent(node: DocumentationNode, uriNode: DocumentationNode) = + contentNodeToMarkup(node.content, uriProvider.mainUriOrWarn(uriNode) ?: uri) + + protected fun FlowContent.nodeContent(node: DocumentationNode) = + nodeContent(node, node) + + protected fun FlowContent.contentNodesToMarkup(content: List<ContentNode>, contextUri: URI = uri): Unit = + content.forEach { contentNodeToMarkup(it, contextUri) } + + protected fun FlowContent.contentNodeToMarkup(content: ContentNode, contextUri: URI = uri) { + when (content) { + is ContentText -> +content.text + is ContentSymbol -> span("symbol") { +content.text } + is ContentKeyword -> span("keyword") { +content.text } + is ContentIdentifier -> span("identifier") { + content.signature?.let { id = it } + +content.text + } + + is ContentHeading -> hN(level = content.level) { contentNodesToMarkup(content.children, contextUri) } + + is ContentEntity -> +content.text + + is ContentStrong -> strong { contentNodesToMarkup(content.children, contextUri) } + is ContentStrikethrough -> del { contentNodesToMarkup(content.children, contextUri) } + is ContentEmphasis -> em { contentNodesToMarkup(content.children, contextUri) } + + is ContentOrderedList -> ol { contentNodesToMarkup(content.children, contextUri) } + is ContentUnorderedList -> ul { contentNodesToMarkup(content.children, contextUri) } + is ContentListItem -> consumer.li { + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) } + ?: contentNodesToMarkup(content.children, contextUri) + } + + is ContentDescriptionList -> dl { contentNodesToMarkup(content.children, contextUri) } + is ContentDescriptionTerm -> consumer.dt { + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> this@contentNodeToMarkup.contentNodesToMarkup(paragraph.children, contextUri) } + ?: this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) + } + is ContentDescriptionDefinition -> consumer.dd { + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) } + ?: contentNodesToMarkup(content.children, contextUri) + } + + is ContentTable -> table { contentNodesToMarkup(content.children, contextUri) } + is ContentTableBody -> consumer.tbody { this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) } + is ContentTableRow -> consumer.tr { this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) } + is ContentTableHeader -> consumer.th { + content.colspan?.let { + if (it.isNotBlank()) { + attributes["colspan"] = content.colspan + } + } + content.rowspan?.let { + if (it.isNotBlank()) { + attributes["rowspan"] = content.rowspan + } + } + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> this@contentNodeToMarkup.contentNodesToMarkup(paragraph.children, contextUri) } + ?: this@contentNodeToMarkup.contentNodesToMarkup(content.children, contextUri) + } + is ContentTableCell -> consumer.td { + content.colspan?.let { + if (it.isNotBlank()) { + attributes["colspan"] = content.colspan + } + } + content.rowspan?.let { + if (it.isNotBlank()) { + attributes["rowspan"] = content.rowspan + } + } + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> contentNodesToMarkup(paragraph.children, contextUri) } + ?: contentNodesToMarkup(content.children, contextUri) + } + + is ContentSpecialReference -> aside(classes = "note") { + contentNodesToMarkup(content.children, contextUri) + } + + is ContentCode -> contentInlineCode(content) + is ContentBlockSampleCode -> contentBlockSampleCode(content) + is ContentBlockCode -> contentBlockCode(content) + + ContentNonBreakingSpace -> +nbsp + ContentSoftLineBreak, ContentIndentedSoftLineBreak -> { + } + ContentHardLineBreak -> br + + is ContentParagraph -> p(classes = content.label) { contentNodesToMarkup(content.children, contextUri) } + + is NodeRenderContent -> renderedSignature(content.node, mode = content.mode) + is ContentNodeLink -> { + fun FlowContent.body() = contentNodesToMarkup(content.children, contextUri) + + when (content.node?.kind) { + NodeKind.TypeParameter -> body() + else -> a(href = content.node, block = FlowContent::body) + } + } + is ContentBookmark -> a { + id = content.name + contentNodesToMarkup(content.children, contextUri) + } + is ContentExternalLink -> contentExternalLink(content) + is ContentLocalLink -> a(href = contextUri.resolve(content.href).relativeTo(uri).toString()) { + contentNodesToMarkup(content.children, contextUri) + } + is ContentSection -> { + } + is ScriptBlock -> script(content.type, content.src) {} + is ContentBlock -> contentNodesToMarkup(content.children, contextUri) + } + } + + protected open fun FlowContent.contentInlineCode(content: ContentCode) { + code { contentNodesToMarkup(content.children) } + } + + protected open fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) { + pre { + code { + attributes["data-language"] = content.language + contentNodesToMarkup(content.importsBlock.children) + +"\n\n" + contentNodesToMarkup(content.children) + } + } + } + + protected open fun FlowContent.contentBlockCode(content: ContentBlockCode) { + pre { + code { + attributes["data-language"] = content.language + contentNodesToMarkup(content.children) + } + } + } + + protected open fun FlowContent.contentExternalLink(content: ContentExternalLink) { + a(href = content.href) { contentNodesToMarkup(content.children) } + } + + protected open fun <T> FlowContent.summaryNodeGroup( + nodes: Iterable<T>, + header: String, + headerAsRow: Boolean = true, + row: TBODY.(T) -> Unit + ) { + if (nodes.none()) return + if (!headerAsRow) { + h2 { +header } + } + table { + tbody { + if (headerAsRow) { + developerHeading(header) + } + nodes.forEach { node -> + row(node) + } + } + } + } + + + protected open fun summary(node: DocumentationNode) = node.summary + + protected open fun TBODY.classLikeRow(node: DocumentationNode) = tr { + td { a(href = uriProvider.linkTo(node, uri)) { +node.simpleName() } } + td { nodeSummary(node) } + } + + protected fun FlowContent.modifiers(node: DocumentationNode) { + for (modifier in node.details(NodeKind.Modifier)) { + renderedSignature(modifier, SUMMARY) + } + } + + protected fun FlowContent.shortFunctionParametersList(func: DocumentationNode) { + val params = func.details(NodeKind.Parameter) + .map { languageService.render(it, FULL) } + .run { + drop(1).fold(listOfNotNull(firstOrNull())) { acc, node -> + acc + ContentText(", ") + node + } + } + metaMarkup(listOf(ContentText("(")) + params + listOf(ContentText(")"))) + } + + + protected open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr { + if (node.kind != NodeKind.Constructor) { + td { + modifiers(node) + renderedSignature(node.detail(NodeKind.Type), SUMMARY) + } + } + td { + div { + code { + val receiver = node.detailOrNull(NodeKind.Receiver) + if (receiver != null) { + renderedSignature(receiver.detail(NodeKind.Type), SUMMARY) + +"." + } + a(href = node) { +node.prettyName } + shortFunctionParametersList(node) + } + } + + nodeSummary(node) + } + } + + protected open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode, showSignature: Boolean = true) = tr { + if (showSignature) { + td { + modifiers(node) + renderedSignature(node.detail(NodeKind.Type), SUMMARY) + } + } + td { + div { + code { + a(href = node) { +node.name } + } + } + + nodeSummary(node) + } + } + + protected open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr { + td { + modifiers(node) + } + td { + div { + code { + a(href = node) { +node.name } + } + } + + nodeSummary(node) + } + } + + protected fun HtmlBlockTag.nodeSummary(node: DocumentationNode, uriNode: DocumentationNode) { + contentNodeToMarkup(summary(node), uriProvider.mainUriOrWarn(uriNode) ?: uri) + } + + protected fun HtmlBlockTag.nodeSummary(node: DocumentationNode) { + nodeSummary(node, node) + } + + protected open fun TBODY.inheritRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = tr { + td { + val (from, nodes) = entry + +"From class " + a(href = from.owner!!) { +from.qualifiedName() } + table { + tbody { + for (node in nodes) { + summaryRow(node) + } + } + } + } + } + + protected open fun TBODY.groupedRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + groupHeader: HtmlBlockTag.(DocumentationNode) -> Unit, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = tr { + td { + val (from, nodes) = entry + groupHeader(from) + table { + tbody { + for (node in nodes) { + summaryRow(node) + } + } + } + } + } + + protected open fun TBODY.extensionRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = groupedRow(entry, { from -> + +"From " + a(href = from) { +from.qualifiedName() } + }, summaryRow) + + + protected open fun TBODY.extensionByReceiverRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = groupedRow(entry, { from -> + +"For " + a(href = from) { +from.name } + }, summaryRow) + + protected open fun FlowOrInteractiveOrPhrasingContent.a(href: DocumentationNode?, classes: String? = null, block: HtmlBlockInlineTag.() -> Unit) { + if (href == null) { + return a(href = "#", classes = classes, block = block) + } + + val hrefText = try { + href.name.takeIf { href.kind == NodeKind.ExternalLink } + ?: href.links.firstOrNull { it.kind == NodeKind.ExternalLink }?.name + ?: "#".takeIf { href.kind == NodeKind.ExternalClass } // When external class unresolved + ?: uriProvider.linkTo(href, uri) + } catch (e: Exception) { + val owners = generateSequence(href) { it.owner }.toList().reversed() + logger.warn("Exception while resolving link to ${owners.joinToString(separator = " ")}\n" + + Throwables.getStackTraceAsString(e)) + "#" + } + + a(href = hrefText, classes = classes, block = block) + } + + protected open fun FlowContent.renderedSignature( + node: DocumentationNode, + mode: LanguageService.RenderMode = SUMMARY + ) { + contentNodeToMarkup(languageService.render(node, mode), uri) + } + + protected open fun generatePackage(page: Page.PackagePage) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +page.node.name } + nodeContent(page.node) + this@composePage.summaryNodeGroup(page.interfaces, "Interfaces", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) } + this@composePage.summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) } + + this@composePage.summaryNodeGroup( + page.constants, + "Top-level constants summary", + headerAsRow = false + ) { + propertyLikeSummaryRow(it) + } + + this@composePage.summaryNodeGroup( + page.functions, + "Top-level functions summary", + headerAsRow = false + ) { + functionLikeSummaryRow(it) + } + + this@composePage.summaryNodeGroup( + page.properties, + "Top-level properties summary", + headerAsRow = false + ) { + propertyLikeSummaryRow(it) + } + + this@composePage.summaryNodeGroup( + page.extensionFunctions.entries, + "Extension functions summary", + headerAsRow = false + ) { + extensionByReceiverRow(it) { + functionLikeSummaryRow(it) + } + } + + this@composePage.summaryNodeGroup( + page.extensionProperties.entries, + "Extension properties summary", + headerAsRow = false + ) { + extensionByReceiverRow(it) { + functionLikeSummaryRow(it) + } + } + + fullMemberDocs(page.constants, "Top-level constants") + fullMemberDocs(page.functions, "Top-level functions") + fullMemberDocs(page.properties, "Top-level properties") + fullMemberDocs(page.extensionFunctions.values.flatten(), "Extension functions") + fullMemberDocs(page.extensionProperties.values.flatten(), "Extension properties") + } + ) + + protected fun FlowContent.qualifiedTypeReference(node: DocumentationNode) { + if (node.kind in classLike) { + a(href = node) { +node.qualifiedName() } + return + } + + val targetLink = node.links.firstOrNull() + + if (targetLink?.kind == NodeKind.TypeParameter) { + +node.name + return + } + + a(href = targetLink) { + +node.qualifiedNameFromType() + } + val typeParameters = node.details(NodeKind.Type) + if (typeParameters.isNotEmpty()) { + +"<" + typeParameters.forEach { + if (it != typeParameters.first()) { + +", " + } + qualifiedTypeReference(it) + } + +">" + } + } + + protected open fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) { + table { + superclasses.forEach { + tr { + if (it != superclasses.first()) { + td { + +" ↳" + } + } + td { + qualifiedTypeReference(it) + } + } + } + } + } + + protected open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) { + if (inheritors.isEmpty()) return + div { + table { + thead { + tr { + td { + if (direct) + +"Known Direct Subclasses" + else + +"Known Indirect Subclasses" + } + } + } + tbody { + inheritors.forEach { inheritor -> + tr { + td { + a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() } + } + td { + nodeSummary(inheritor) + } + } + } + } + } + } + } + + protected open fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) { + this@classLikeSummaries.summaryNodeGroup( + nestedClasses, + "Nested classes", + headerAsRow = true + ) { + nestedClassSummaryRow(it) + } + + this@classLikeSummaries.summaryNodeGroup(enumValues, "Enum values") { + propertyLikeSummaryRow(it) + } + + this@classLikeSummaries.summaryNodeGroup(constants, "Constants") { propertyLikeSummaryRow(it) } + + constructors.forEach { (visibility, group) -> + this@classLikeSummaries.summaryNodeGroup( + group, + "${visibility.capitalize()} constructors", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + } + + functions.forEach { (visibility, group) -> + this@classLikeSummaries.summaryNodeGroup( + group, + "${visibility.capitalize()} functions", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + } + + this@classLikeSummaries.summaryNodeGroup( + companionFunctions, + "Companion functions", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + this@classLikeSummaries.summaryNodeGroup( + inheritedFunctionsByReceiver.entries, + "Inherited functions", + headerAsRow = true + ) { + inheritRow(it) { + functionLikeSummaryRow(it) + } + } + this@classLikeSummaries.summaryNodeGroup( + extensionFunctions.entries, + "Extension functions", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } + this@classLikeSummaries.summaryNodeGroup( + inheritedExtensionFunctions.entries, + "Inherited extension functions", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } + + + this@classLikeSummaries.summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) } + this@classLikeSummaries.summaryNodeGroup( + companionProperties, + "Companion properties", + headerAsRow = true + ) { + propertyLikeSummaryRow(it) + } + + this@classLikeSummaries.summaryNodeGroup( + inheritedPropertiesByReceiver.entries, + "Inherited properties", + headerAsRow = true + ) { + inheritRow(it) { + propertyLikeSummaryRow(it) + } + } + this@classLikeSummaries.summaryNodeGroup( + extensionProperties.entries, + "Extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + this@classLikeSummaries.summaryNodeGroup( + inheritedExtensionProperties.entries, + "Inherited extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + } + + protected open fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) { + fullMemberDocs(enumValues, "Enum values") + fullMemberDocs(constants, "Constants") + + constructors.forEach { (visibility, group) -> + fullMemberDocs(group, "${visibility.capitalize()} constructors") + } + + functions.forEach { (visibility, group) -> + fullMemberDocs(group, "${visibility.capitalize()} methods") + } + + fullMemberDocs(properties, "Properties") + if (!hasMeaningfulCompanion) { + fullMemberDocs(companionFunctions, "Companion functions") + fullMemberDocs(companionProperties, "Companion properties") + } + } + + protected open fun generateClassLike(page: Page.ClassPage) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + val node = page.node + with(page) { + + div { + id = "api-info-block" + apiAndDeprecatedVersions(node) + } + + if (node.artifactId.name.isNotEmpty()) { + div(classes = "api-level") { br { +"belongs to Maven artifact ${node.artifactId}" } } + } + h1 { +node.name } + pre { renderedSignature(node, FULL) } + classHierarchy(page.superclasses) + + subclasses(page.directInheritors, true) + subclasses(page.indirectInheritors, false) + + deprecatedClassCallOut(node) + nodeContent(node) + + h2 { +"Summary" } + classLikeSummaries(page) + classLikeFullMemberDocs(page) + } + } + ) + + protected open fun FlowContent.classIndexSummary(node: DocumentationNode) { + nodeContent(node) + } + + protected open fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +"Class Index" } + + + ul { + page.classesByFirstLetter.forEach { (letter) -> + li { a(href = "#letter_$letter") { +letter } } + } + } + + page.classesByFirstLetter.forEach { (letter, classes) -> + h2 { + id = "letter_$letter" + +letter + } + table { + tbody { + for (node in classes) { + tr { + td { + a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() } + } + td { + if (!deprecatedIndexSummary(node)) { + classIndexSummary(node) + } + } + } + } + } + } + } + } + ) + + protected open fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +"Package Index" } + table { + tbody { + for (node in page.packages) { + tr { + td { + a(href = uriProvider.linkTo(node, uri)) { +node.name } + } + td { + nodeContent(node) + } + } + } + } + } + } + ) + + fun generatePage(page: Page) { + when (page) { + is Page.PackageIndex -> generatePackageIndex(page) + is Page.ClassIndex -> generateClassIndex(page) + is Page.ClassPage -> generateClassLike(page) + is Page.PackagePage -> generatePackage(page) + } + } + + protected fun FlowContent.fullMemberDocs( + nodes: List<DocumentationNode>, + header: String + ) { + if (nodes.none()) return + h2 { + +header + } + for (node in nodes) { + fullMemberDocs(node) + } + } + + protected open fun FlowContent.seeAlsoSection(links: List<List<ContentNode>>) { + p { b { +"See Also" } } + ul { + links.forEach { linkParts -> + li { code { metaMarkup(linkParts) } } + } + } + } + + protected open fun FlowContent.regularSection(name: String, entries: List<ContentSection>) { + table { + thead { + tr { + th { + colSpan = "2" + +name + } + } + } + tbody { + entries.forEach { + tr { + if (it.subjectName != null) { + td { +it.subjectName } + } + td { + metaMarkup(it.children) + } + } + } + } + } + } + + protected open fun FlowContent.deprecationWarningToMarkup( + node: DocumentationNode, + prefix: Boolean = false, + emphasis: Boolean = true + ): Boolean { + val deprecated = formatDeprecationOrNull(node, prefix, emphasis) + deprecated?.let { + contentNodeToMarkup(deprecated, uriProvider.mainUri(node)) + return true + } + return false + } + + protected open fun FlowContent.deprecatedClassCallOut(node: DocumentationNode) { + val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty() + if (deprecatedLevelExists) { + hr { } + aside(classes = "caution") { + strong { +node.deprecatedLevelMessage() } + deprecationWarningToMarkup(node, emphasis = false) + } + } + } + + protected open fun FlowContent.deprecatedIndexSummary(node: DocumentationNode): Boolean { + val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty() + if (deprecatedLevelExists) { + val em = ContentEmphasis() + em.append(ContentText(node.deprecatedLevelMessage())) + em.append(ContentText(" ")) + for (child in node.deprecation?.content?.children ?: emptyList<ContentNode>()) { + em.append(child) + } + contentNodeToMarkup(em, uriProvider.mainUri(node)) + return true + } + return false + } + + protected open fun FlowContent.apiAndDeprecatedVersions(node: DocumentationNode) { + val apiLevelExists = node.apiLevel.name.isNotEmpty() + val sdkExtSinceExists = node.sdkExtSince.name.isNotEmpty() + val deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty() + if (apiLevelExists || sdkExtSinceExists || deprecatedLevelExists) { + div(classes = "api-level") { + if (apiLevelExists) { + +"Added in " + a(href = "https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels") { + +"API level ${node.apiLevel.name}" + } + } + if (sdkExtSinceExists) { + if (apiLevelExists) { + br + +"Also in " + } else { + +"Added in " + } + a(href = "https://developer.android.com/sdkExtensions") { + +"${node.sdkExtSince.name}" + } + } + if (deprecatedLevelExists) { + if (apiLevelExists || sdkExtSinceExists) { + br + } + +"Deprecated in " + a(href = "https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels") { + +"API level ${node.deprecatedLevel.name}" + } + } + } + } + } + + protected open fun formatDeprecationOrNull( + node: DocumentationNode, + prefix: Boolean = false, + emphasis: Boolean = true): ContentNode? { + val deprecated = node.deprecation + deprecated?.let { + return ContentParagraph("caution").apply { + if (prefix) { + append(ContentStrong().apply { text( + if (deprecated.content.children.size == 0) "Deprecated." + else "Deprecated: " + ) }) + } + val em = if (emphasis) ContentEmphasis() else ContentBlock() + for (child in deprecated.content.children) { + em.append(child) + } + append(em) + } + } + return null + } + + protected open fun FlowContent.section(name: String, sectionParts: List<ContentSection>) { + when (name) { + ContentTags.SeeAlso -> seeAlsoSection(sectionParts.map { it.children.flatMap { (it as? ContentParagraph)?.children ?: listOf(it) } }) + else -> regularSection(name, sectionParts) + } + } + + protected open fun FlowContent.sections(content: Content) { + val sectionsByTag = content.sections.groupByTo(mutableMapOf()) { it.tag } + + val seeAlso = sectionsByTag.remove(ContentTags.SeeAlso) + + for ((name, entries) in sectionsByTag) { + section(name, entries) + } + + seeAlso?.let { section(ContentTags.SeeAlso, it) } + } + + protected open fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) { + div { + id = node.signatureForAnchor(logger) + h3 { +node.name } + pre { renderedSignature(node, FULL) } + deprecationWarningToMarkup(node, prefix = true) + nodeContent(node) + node.constantValue()?.let { value -> + pre { + +"Value: " + code { +value } + } + } + + sections(node.content) + } + } + + protected open fun FlowContent.fullMemberDocs(node: DocumentationNode) { + fullMemberDocs(node, node) + } + + sealed class Page { + class PackageIndex(packages: List<DocumentationNode>) : Page() { + init { + assert(packages.all { it.kind == NodeKind.Package }) + } + + val packages = packages.sortedBy { it.name } + } + + class ClassIndex(allTypesNode: DocumentationNode) : Page() { + init { + assert(allTypesNode.kind == NodeKind.AllTypes) + } + + // Wide-collect all nested classes + val classes: List<DocumentationNode> = + generateSequence(listOf(allTypesNode)) { nodes -> + nodes + .flatMap { it.members.filter { it.kind in NodeKind.classLike } } + .takeUnless { it.isEmpty() } + }.drop(1) + .flatten() + .sortedBy { it.classNodeNameWithOuterClass().toLowerCase() } + .toList() + + + // Group all classes by it's first letter and sort + val classesByFirstLetter = + classes + .groupBy { + it.classNodeNameWithOuterClass().first().toString() + } + .entries + .sortedBy { (letter) -> + val x = letter.toLowerCase() + x + } + } + + class ClassPage(val node: DocumentationNode) : Page() { + + init { + assert(node.kind in NodeKind.classLike) + } + + val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed() + + val enumValues = node.members(NodeKind.EnumItem).sortedBy { it.name } + + val directInheritors: List<DocumentationNode> + val indirectInheritors: List<DocumentationNode> + + init { + // Wide-collect all inheritors + val inheritors = generateSequence(node.inheritors) { inheritors -> + inheritors + .flatMap { it.inheritors } + .takeUnless { it.isEmpty() } + } + directInheritors = inheritors.first().sortedBy { it.classNodeNameWithOuterClass() } + indirectInheritors = inheritors.drop(1).flatten().toList().sortedBy { it.classNodeNameWithOuterClass() } + } + + val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" } + val hasMeaningfulCompanion = !isCompanion && node.companion != null + + private fun DocumentationNode.thisTypeExtension() = + detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node } + + val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction + val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty + + private fun DocumentationNode.isFunction() = kind == functionKind + private fun DocumentationNode.isProperty() = kind == propertyKind + + + val nestedClasses = node.members.filter { it.kind in NodeKind.classLike } - enumValues + + val attributes = node.attributes + + val inheritedAttributes = + node.superclassTypeSequence + .toList() + .sortedBy { it.name } + .flatMap { it.typeDeclarationClass?.attributes.orEmpty() } + .distinctBy { it.attributeRef!!.name } + .groupBy { it.owner!! } + + val allInheritedMembers = node.allInheritedMembers + val constants = node.members.filter { it.constantValue() != null } + val inheritedConstants = allInheritedMembers.filter { it.constantValue() != null }.groupBy { it.owner!! } + + + fun compareVisibilities(a: String, b: String): Int { + return visibilityNames.indexOf(a) - visibilityNames.indexOf(b) + } + + fun Collection<DocumentationNode>.groupByVisibility() = + groupBy { it.visibility() }.toSortedMap(Comparator { a, b -> compareVisibilities(a, b) }) + + + val constructors = node.members(NodeKind.Constructor).groupByVisibility() + val functions = node.members(functionKind).groupByVisibility() + val fields = (node.members(NodeKind.Field) - constants).groupByVisibility() + val properties = node.members(propertyKind) - constants + val inheritedFunctionsByReceiver = allInheritedMembers.filter { it.kind == functionKind }.groupBy { it.owner!! } + val inheritedPropertiesByReceiver = + allInheritedMembers.filter { + it.kind == propertyKind && it.constantValue() == null + }.groupBy { it.owner!! } + + val inheritedFieldsByReceiver = + allInheritedMembers.filter { + it.kind == NodeKind.Field && it.constantValue() != null + }.groupBy { it.owner!! } + + val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions + + val extensionFunctions: Map<DocumentationNode, List<DocumentationNode>> + val extensionProperties: Map<DocumentationNode, List<DocumentationNode>> + val inheritedExtensionFunctions: Map<DocumentationNode, List<DocumentationNode>> + val inheritedExtensionProperties: Map<DocumentationNode, List<DocumentationNode>> + + init { + val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() } + extensionFunctions = extensions.filter { it.isFunction() }.sortedBy { it.name }.groupBy { it.owner!! } + extensionProperties = extensions.filter { it.isProperty() }.sortedBy { it.name }.groupBy { it.owner!! } + inheritedExtensionFunctions = + inheritedExtensions.filter { it.isFunction() }.sortedBy { it.name }.groupBy { it.owner!! } + inheritedExtensionProperties = + inheritedExtensions.filter { it.isProperty() }.sortedBy { it.name }.groupBy { it.owner!! } + } + + val companionFunctions = node.members(NodeKind.CompanionObjectFunction).takeUnless { isCompanion }.orEmpty() + val companionProperties = + node.members(NodeKind.CompanionObjectProperty).takeUnless { isCompanion }.orEmpty() - constants + + + } + + class PackagePage(val node: DocumentationNode) : Page() { + + init { + assert(node.kind == NodeKind.Package) + } + + val interfaces = node.members(NodeKind.Interface) + + node.members(NodeKind.Class).flatMap { it.members(NodeKind.Interface) } + val classes = node.members(NodeKind.Class) + val exceptions = node.members(NodeKind.Exception) + val typeAliases = node.members(NodeKind.TypeAlias) + val annotations = node.members(NodeKind.AnnotationClass) + val enums = node.members(NodeKind.Enum) + + val constants = node.members(NodeKind.Property).filter { it.constantValue() != null } + + + private fun DocumentationNode.getClassExtensionReceiver() = + detailOrNull(NodeKind.Receiver)?.detailOrNull(NodeKind.Type)?.takeIf { + it.links.any { it.kind == NodeKind.ExternalLink || it.kind in NodeKind.classLike } + } + + private fun List<DocumentationNode>.groupedExtensions() = + filter { it.getClassExtensionReceiver() != null } + .groupBy { + val receiverType = it.getClassExtensionReceiver()!! + receiverType.links.filter { it.kind != NodeKind.ExternalLink}.firstOrNull() ?: + receiverType.links(NodeKind.ExternalLink).first() + } + + private fun List<DocumentationNode>.externalExtensions(kind: NodeKind) = + associateBy({ it }, { it.members(kind) }) + .filterNot { (_, values) -> values.isEmpty() } + + val extensionFunctions = + node.members(NodeKind.ExternalClass).externalExtensions(NodeKind.Function) + + node.members(NodeKind.Function).groupedExtensions() + + val extensionProperties = + node.members(NodeKind.ExternalClass).externalExtensions(NodeKind.Property) + + node.members(NodeKind.Property).groupedExtensions() + + val functions = node.members(NodeKind.Function) - extensionFunctions.values.flatten() + val properties = node.members(NodeKind.Property) - constants - extensionProperties.values.flatten() + + } + } +} + +class JavaLayoutHtmlFormatOutputBuilderFactoryImpl @Inject constructor( + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val templateService: JavaLayoutHtmlTemplateService, + val logger: DokkaLogger +) : JavaLayoutHtmlFormatOutputBuilderFactory { + override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder { + return createOutputBuilder(output, uriProvider.mainUri(node)) + } + + override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder { + return JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) + } +} + +fun DocumentationNode.constantValue(): String? = + detailOrNull(NodeKind.Value)?.name.takeIf { + kind == NodeKind.Field || kind == NodeKind.Property || kind == NodeKind.CompanionObjectProperty + } + + +private val visibilityNames = setOf("public", "protected", "internal", "package-local", "private") + +fun DocumentationNode.visibility(): String = + details(NodeKind.Modifier).firstOrNull { it.name in visibilityNames }?.name ?: "" diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt new file mode 100644 index 000000000..9928a8e9e --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt @@ -0,0 +1,165 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatOutputBuilder.Page +import org.jetbrains.dokka.NodeKind.Companion.classLike +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult +import java.io.BufferedWriter +import java.io.File +import java.net.URI + +class JavaLayoutHtmlFormatGenerator @Inject constructor( + @Named("outputDir") val root: File, + val packageListService: PackageListService, + val outputBuilderFactoryService: JavaLayoutHtmlFormatOutputBuilderFactory, + private val options: DocumentationOptions, + val logger: DokkaLogger, + @Named("outlineRoot") val outlineRoot: String +) : Generator, JavaLayoutHtmlUriProvider { + + @set:Inject(optional = true) + var outlineFactoryService: JavaLayoutHtmlFormatOutlineFactoryService? = null + + fun createOutputBuilderForNode(node: DocumentationNode, output: Appendable) = outputBuilderFactoryService.createOutputBuilder(output, node) + + fun DocumentationNode.getOwnerOrReport() = owner ?: run { + error("Owner not found for $this") + } + + override fun tryGetContainerUri(node: DocumentationNode): URI? { + return when (node.kind) { + NodeKind.Module -> URI("/").resolve(node.name + "/") + NodeKind.Package -> tryGetContainerUri(node.getOwnerOrReport())?.resolve(node.name.replace('.', '/') + '/') + NodeKind.GroupNode -> tryGetContainerUri(node.getOwnerOrReport()) + in NodeKind.classLike -> tryGetContainerUri(node.getOwnerOrReport())?.resolve("${node.classNodeNameWithOuterClass()}.html") + else -> null + } + } + + override fun tryGetMainUri(node: DocumentationNode): URI? { + return when (node.kind) { + NodeKind.Package -> tryGetContainerUri(node)?.resolve("package-summary.html") + in NodeKind.classLike -> tryGetContainerUri(node)?.resolve("#") + in NodeKind.memberLike -> { + val owner = if (node.owner?.kind != NodeKind.ExternalClass) node.owner else node.owner?.owner + if (owner!!.kind in classLike && + (node.kind == NodeKind.CompanionObjectProperty || node.kind == NodeKind.CompanionObjectFunction) && + owner.companion != null + ) { + val signature = node.detail(NodeKind.Signature) + val originalFunction = owner.companion!!.members.first { it.detailOrNull(NodeKind.Signature)?.name == signature.name } + tryGetMainUri(owner.companion!!)?.resolveInPage(originalFunction) + } else { + tryGetMainUri(owner)?.resolveInPage(node) + } + } + NodeKind.TypeParameter, NodeKind.Parameter -> node.path.asReversed().drop(1).firstNotNullResult(this::tryGetMainUri)?.resolveInPage(node) + NodeKind.AllTypes -> outlineRootUri(node).resolve ("classes.html") + else -> null + } + } + + override fun tryGetOutlineRootUri(node: DocumentationNode): URI? { + return when(node.kind) { + NodeKind.AllTypes -> tryGetContainerUri(node.getOwnerOrReport()) + else -> tryGetContainerUri(node) + }?.resolve(outlineRoot) + } + + fun URI.resolveInPage(node: DocumentationNode): URI = resolve("#${node.signatureForAnchor(logger).anchorEncoded()}") + + fun buildClass(node: DocumentationNode, parentDir: File) { + val fileForClass = parentDir.resolve(node.classNodeNameWithOuterClass() + ".html") + fileForClass.bufferedWriter().use { + createOutputBuilderForNode(node, it).generatePage(Page.ClassPage(node)) + } + for (memberClass in node.members.filter { it.kind in NodeKind.classLike }) { + buildClass(memberClass, parentDir) + } + } + + fun buildPackage(node: DocumentationNode, parentDir: File) { + assert(node.kind == NodeKind.Package) + var members = node.members + val directoryForPackage = parentDir.resolve(node.name.replace('.', File.separatorChar)) + directoryForPackage.mkdirsOrFail() + + directoryForPackage.resolve("package-summary.html").bufferedWriter().use { + createOutputBuilderForNode(node, it).generatePage(Page.PackagePage(node)) + } + + members.filter { it.kind == NodeKind.GroupNode }.forEach { + members += it.members + } + members.filter { it.kind in NodeKind.classLike }.forEach { + buildClass(it, directoryForPackage) + } + } + + fun buildClassIndex(node: DocumentationNode, parentDir: File) { + val file = parentDir.resolve("classes.html") + file.bufferedWriter().use { + createOutputBuilderForNode(node, it).generatePage(Page.ClassIndex(node)) + } + } + + fun buildPackageIndex(module: DocumentationNode, nodes: List<DocumentationNode>, parentDir: File) { + val file = parentDir.resolve("packages.html") + file.bufferedWriter().use { + val uri = outlineRootUri(module).resolve("packages.html") + outputBuilderFactoryService.createOutputBuilder(it, uri) + .generatePage(Page.PackageIndex(nodes)) + } + } + + override fun buildPages(nodes: Iterable<DocumentationNode>) { + val module = nodes.single() + + val moduleRoot = root.resolve(module.name) + val packages = module.members.filter { it.kind == NodeKind.Package } + packages.forEach { buildPackage(it, moduleRoot) } + val outlineRootFile = moduleRoot.resolve(outlineRoot) + if (options.generateClassIndexPage) { + buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, outlineRootFile) + } + + if (options.generatePackageIndexPage) { + buildPackageIndex(module, packages, outlineRootFile) + } + } + + override fun buildOutlines(nodes: Iterable<DocumentationNode>) { + val uriToWriter = mutableMapOf<URI, BufferedWriter>() + + fun provideOutput(uri: URI): BufferedWriter { + val normalized = uri.normalize() + uriToWriter[normalized]?.let { return it } + val file = root.resolve(normalized.path.removePrefix("/")) + file.parentFile.mkdirsOrFail() + val writer = file.bufferedWriter() + uriToWriter[normalized] = writer + return writer + } + + outlineFactoryService?.generateOutlines(::provideOutput, nodes) + + uriToWriter.values.forEach { it.close() } + } + + override fun buildSupportFiles() {} + + override fun buildPackageList(nodes: Iterable<DocumentationNode>) { + nodes.filter { it.kind == NodeKind.Module }.forEach { module -> + val moduleRoot = root.resolve(module.name) + val packageListFile = moduleRoot.resolve("package-list") + packageListFile.writeText(packageListService.formatPackageList(module as DocumentationModule)) + } + } +} + +interface JavaLayoutHtmlFormatOutputBuilderFactory { + fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder + fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder +} diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt new file mode 100644 index 000000000..ce05fe897 --- /dev/null +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt @@ -0,0 +1,154 @@ +package org.jetbrains.dokka.Formats + +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiParameter +import org.jetbrains.dokka.* +import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject +import org.jetbrains.kotlin.types.KotlinType + +class JavaLayoutHtmlPackageListService: PackageListService { + + private fun StringBuilder.appendParam(name: String, value: String) { + append(DOKKA_PARAM_PREFIX) + append(name) + append(":") + appendln(value) + } + + override fun formatPackageList(module: DocumentationModule): String { + val packages = module.members(NodeKind.Package).map { it.name } + + return buildString { + appendParam("format", "java-layout-html") + appendParam("mode", "kotlin") + for (p in packages) { + appendln(p) + } + } + } + +} + +class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>, + private val resolutionFacade: DokkaResolutionFacade) : InboundExternalLinkResolutionService { + + constructor(asJava: Boolean, resolutionFacade: DokkaResolutionFacade) : + this(mapOf("mode" to listOf(if (asJava) "java" else "kotlin")), resolutionFacade) + + + private val isJavaMode = paramMap["mode"]!!.single() == "java" + + private fun getContainerPath(symbol: DeclarationDescriptor): String? { + return when (symbol) { + is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/" + is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html" + else -> null + } + } + + private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor = + generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first() + + private fun ClassifierDescriptor.nameWithOuter(): String = + generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor } + .toList().asReversed().joinToString(".") { it.name.asString() } + + private fun getJavaPagePath(symbol: DeclarationDescriptor): String? { + + val sourcePsi = symbol.sourcePsi() ?: return null + val source = (if (sourcePsi is KtDeclaration) { + sourcePsi.toLightElements().firstOrNull() + } else { + sourcePsi + }) as? PsiMember ?: return null + val desc = source.getJavaMemberDescriptor(resolutionFacade) ?: return null + return getPagePath(desc) + } + + private fun getPagePath(symbol: DeclarationDescriptor): String? { + return when (symbol) { + is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html" + is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded() + is ClassifierDescriptor -> getContainerPath(symbol) + "#" + is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded() + else -> null + } + } + + private fun DeclarationDescriptor.signatureForAnchor(): String? { + + fun ReceiverParameterDescriptor.extractReceiverName(): String { + var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! + if (receiverClass.isCompanionObject()) { + receiverClass = receiverClass.containingDeclaration!! + } else if (receiverClass is TypeParameterDescriptor) { + val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor + if (upperBoundClass != null) { + receiverClass = upperBoundClass + } + } + + return receiverClass.name.asString() + } + + fun KotlinType.qualifiedNameForSignature(): String { + val desc = constructor.declarationDescriptor + return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>" + } + + fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) { + if (desc.containingDeclaration.isCompanionObject()) { + append("Companion.") + } + desc.extensionReceiverParameter?.let { + append("(") + append(it.extractReceiverName()) + append(").") + } + } + + return when (this) { + is EnumEntrySyntheticClassDescriptor -> buildString { + append("ENUM_VALUE:") + append(name.asString()) + } + is JavaMethodDescriptor -> buildString { + append(name.asString()) + valueParameters.joinTo(this, prefix = "(", postfix = ")") { + val param = it.sourcePsi() as PsiParameter + param.type.canonicalText + } + } + is JavaPropertyDescriptor -> buildString { + append(name.asString()) + } + is FunctionDescriptor -> buildString { + appendReceiverAndCompanion(this@signatureForAnchor) + append(name.asString()) + valueParameters.joinTo(this, prefix = "(", postfix = ")") { + it.type.qualifiedNameForSignature() + } + } + is PropertyDescriptor -> buildString { + appendReceiverAndCompanion(this@signatureForAnchor) + append(name.asString()) + append(":") + + append(returnType?.qualifiedNameForSignature()) + } + else -> null + } + } + + private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.anchorEncoded() + + override fun getPath(symbol: DeclarationDescriptor) = if (isJavaMode) getJavaPagePath(symbol) else getPagePath(symbol) +} diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt new file mode 100644 index 000000000..a948dfa93 --- /dev/null +++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt @@ -0,0 +1,44 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName + +open class JekyllOutputBuilder(to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>) + : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { + override fun appendNodes(nodes: Iterable<DocumentationNode>) { + to.appendln("---") + appendFrontMatter(nodes, to) + to.appendln("---") + to.appendln("") + super.appendNodes(nodes) + } + + protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) { + to.appendln("title: ${getPageTitle(nodes)}") + } +} + + +open class JekyllFormatService( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + linkExtension: String, + impliedPlatforms: List<String> +) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { + + @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String> + ) : this(generator, signatureGenerator, "html", impliedPlatforms) + + override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = + JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) + +} diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt new file mode 100644 index 000000000..a98002d49 --- /dev/null +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -0,0 +1,224 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty + + +open class KotlinWebsiteOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String> +) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { + private var needHardLineBreaks = false + private var insideDiv = 0 + + override fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) { + super.appendFrontMatter(nodes, to) + to.appendln("layout: api") + } + + override fun appendBreadcrumbs(path: Iterable<FormatLink>) { + if (path.count() > 1) { + to.append("<div class='api-docs-breadcrumbs'>") + super.appendBreadcrumbs(path) + to.append("</div>") + } + } + + override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body) + + override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) + + protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) { + to.append("<div class=\"$cssClass\"$otherAttributes") + if (markdown) to.append(" markdown=\"1\"") + to.append(">") + if (!markdown) insideDiv++ + block() + if (!markdown) insideDiv-- + to.append("</div>\n") + } + + override fun appendAsSignature(node: ContentNode, block: () -> Unit) { + val contentLength = node.textLength + if (contentLength == 0) return + div(to, "signature") { + needHardLineBreaks = contentLength >= 62 + try { + block() + } finally { + needHardLineBreaks = false + } + } + } + + override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { + div(to, "overload-group", calculateDataAttributes(platforms), true) { + ensureParagraph() + block() + ensureParagraph() + } + } + + override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) + + override fun appendHeader(level: Int, body: () -> Unit) { + if (insideDiv > 0) { + wrapInTag("p", body, newlineAfterClose = true) + } else { + super.appendHeader(level, body) + } + } + + override fun appendLine() { + if (insideDiv > 0) { + to.appendln("<br/>") + } else { + super.appendLine() + } + } + + override fun appendTable(vararg columns: String, body: () -> Unit) { + to.appendln("<table class=\"api-docs-table\">") + body() + to.appendln("</table>") + } + + override fun appendTableBody(body: () -> Unit) { + to.appendln("<tbody>") + body() + to.appendln("</tbody>") + } + + override fun appendTableRow(body: () -> Unit) { + to.appendln("<tr>") + body() + to.appendln("</tr>") + } + + override fun appendTableCell(body: () -> Unit) { + to.appendln("<td markdown=\"1\">") + body() + to.appendln("\n</td>") + } + + override fun appendBlockCode(language: String, body: () -> Unit) { + if (language.isNotEmpty()) { + super.appendBlockCode(language, body) + } else { + wrap("<pre markdown=\"1\">", "</pre>", body) + } + } + + override fun appendSymbol(text: String) { + to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") + } + + override fun appendKeyword(text: String) { + to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") + } + + override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { + val id = signature?.let { " id=\"$it\"" }.orEmpty() + to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>") + } + + override fun appendSoftLineBreak() { + if (needHardLineBreaks) + to.append("<br/>") + + } + + override fun appendIndentedSoftLineBreak() { + if (needHardLineBreaks) { + to.append("<br/> ") + } + } + + private fun identifierClassName(kind: IdentifierKind) = when (kind) { + IdentifierKind.ParameterName -> "parameterName" + IdentifierKind.SummarizedTypeName -> "summarizedTypeName" + else -> "identifier" + } + + fun calculateDataAttributes(platforms: Set<String>): String { + fun String.isKotlinVersion() = this.startsWith("Kotlin") + fun String.isJREVersion() = this.startsWith("JRE") + val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) + val jreVersion = platforms.singleOrNull(String::isJREVersion) + val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } + + val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" + val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" + val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" + return "$platformsAttr$kotlinVersionAttr$jreVersionAttr" + } + + override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { + if (platforms.isNotEmpty()) + wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) + else + appendTableRow(block) + } + + override fun appendPlatforms(platforms: Set<String>) { + + } +} + +class KotlinWebsiteFormatService @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>, + logger: DokkaLogger +) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { + init { + logger.warn("Format kotlin-website deprecated and will be removed in next release") + } + + override fun createOutputBuilder(to: StringBuilder, location: Location) = + KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +} + + +class KotlinWebsiteRunnableSamplesOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String> +) : KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { + + override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { + div(to, "sample", markdown = true) { + appendBlockCode(language) { + imports() + wrap("\n\nfun main(args: Array<String>) {", "}") { + wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) + } + } + } + } +} + +class KotlinWebsiteRunnableSamplesFormatService @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>, + logger: DokkaLogger +) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { + + init { + logger.warn("Format kotlin-website-samples deprecated and will be removed in next release") + } + + override fun createOutputBuilder(to: StringBuilder, location: Location) = + KotlinWebsiteRunnableSamplesOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +} + diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt new file mode 100644 index 000000000..6ced75b55 --- /dev/null +++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt @@ -0,0 +1,186 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty +import java.io.File + + +object EmptyHtmlTemplateService : HtmlTemplateService { + override fun appendFooter(to: StringBuilder) {} + + override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {} +} + + +open class KotlinWebsiteHtmlOutputBuilder( + to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>, + templateService: HtmlTemplateService +) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) { + private var needHardLineBreaks = false + private var insideDiv = 0 + + override fun appendLine() {} + + override fun appendBreadcrumbs(path: Iterable<FormatLink>) { + if (path.count() > 1) { + to.append("<div class='api-docs-breadcrumbs'>") + super.appendBreadcrumbs(path) + to.append("</div>") + } + } + + override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body) + + protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) { + to.append("<div class=\"$cssClass\"$otherAttributes") + to.append(">") + insideDiv++ + block() + insideDiv-- + to.append("</div>\n") + } + + override fun appendAsSignature(node: ContentNode, block: () -> Unit) { + val contentLength = node.textLength + if (contentLength == 0) return + div(to, "signature") { + needHardLineBreaks = contentLength >= 62 + try { + block() + } finally { + needHardLineBreaks = false + } + } + } + + override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { + div(to, "overload-group", calculateDataAttributes(platforms)) { + block() + } + } + + override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) + + override fun appendTable(vararg columns: String, body: () -> Unit) { + to.appendln("<table class=\"api-docs-table\">") + body() + to.appendln("</table>") + } + + override fun appendTableBody(body: () -> Unit) { + to.appendln("<tbody>") + body() + to.appendln("</tbody>") + } + + override fun appendTableRow(body: () -> Unit) { + to.appendln("<tr>") + body() + to.appendln("</tr>") + } + + override fun appendTableCell(body: () -> Unit) { + to.appendln("<td>") + body() + to.appendln("\n</td>") + } + + override fun appendSymbol(text: String) { + to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") + } + + override fun appendKeyword(text: String) { + to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") + } + + override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { + val id = signature?.let { " id=\"$it\"" }.orEmpty() + to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>") + } + + override fun appendSoftLineBreak() { + if (needHardLineBreaks) + to.append("<br/>") + } + + override fun appendIndentedSoftLineBreak() { + if (needHardLineBreaks) { + to.append("<br/> ") + } + } + + private fun identifierClassName(kind: IdentifierKind) = when (kind) { + IdentifierKind.ParameterName -> "parameterName" + IdentifierKind.SummarizedTypeName -> "summarizedTypeName" + else -> "identifier" + } + + fun calculateDataAttributes(platforms: Set<String>): String { + fun String.isKotlinVersion() = this.startsWith("Kotlin") + fun String.isJREVersion() = this.startsWith("JRE") + val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) + val jreVersion = platforms.singleOrNull(String::isJREVersion) + val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } + + val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" + val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" + val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" + return "$platformsAttr$kotlinVersionAttr$jreVersionAttr" + } + + override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { + if (platforms.isNotEmpty()) + wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) + else + appendTableRow(block) + } + + override fun appendPlatforms(platforms: Set<String>) {} + + override fun appendBreadcrumbSeparator() { + to.append(" / ") + } + + override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { + div(to, "sample") { + appendBlockCode(language) { + imports() + wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") { + wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) + } + } + } + } + + override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) + + + override fun appendSectionWithTag(section: ContentSection) { + appendParagraph { + appendStrong { appendText(section.tag) } + appendText(" ") + appendContent(section) + } + } +} + +class KotlinWebsiteHtmlFormatService @Inject constructor( + generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>, + templateService: HtmlTemplateService +) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) { + + override fun enumerateSupportFiles(callback: (String, String) -> Unit) {} + + override fun createOutputBuilder(to: StringBuilder, location: Location) = + KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) +} + diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt new file mode 100644 index 000000000..4265394f2 --- /dev/null +++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt @@ -0,0 +1,239 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName +import java.util.* + +enum class ListKind { + Ordered, + Unordered +} + +private class ListState(val kind: ListKind, var size: Int = 1) { + fun getTagAndIncrement() = when (kind) { + ListKind.Ordered -> "${size++}. " + else -> "* " + } +} + +private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() + +open class MarkdownOutputBuilder(to: StringBuilder, + location: Location, + generator: NodeLocationAwareGenerator, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>) + : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +{ + private val listStack = ArrayDeque<ListState>() + protected var inTableCell = false + protected var inCodeBlock = false + private var lastTableCellStart = -1 + private var maxBackticksInCodeBlock = 0 + + private fun appendNewline() { + while (to.endsWith(' ')) { + to.setLength(to.length - 1) + } + to.appendln() + } + + private fun ensureNewline() { + if (inTableCell && listStack.isEmpty()) { + if (to.length != lastTableCellStart && !to.endsWith("<br>")) { + to.append("<br>") + } + } + else { + if (!endsWithNewline()) { + appendNewline() + } + } + } + + private fun endsWithNewline(): Boolean { + var index = to.length - 1 + while (index > 0) { + val c = to[index] + if (c != ' ') { + return c == '\n' + } + index-- + } + return false + } + + override fun ensureParagraph() { + if (!to.endsWith(TWO_LINE_BREAKS)) { + if (!to.endsWith('\n')) { + appendNewline() + } + appendNewline() + } + } + override fun appendBreadcrumbSeparator() { + to.append(" / ") + } + + private val backTickFindingRegex = """(`+)""".toRegex() + + override fun appendText(text: String) { + if (inCodeBlock) { + to.append(text) + val backTicks = backTickFindingRegex.findAll(text) + val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0 + maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun) + } + else { + if (text == "\n" && inTableCell) { + to.append(" ") + } else { + to.append(text.htmlEscape()) + } + } + } + + override fun appendCode(body: () -> Unit) { + inCodeBlock = true + val codeBlockStart = to.length + maxBackticksInCodeBlock = 0 + + wrapIfNotEmpty("`", "`", body, checkEndsWith = true) + + if (maxBackticksInCodeBlock > 0) { + val extraBackticks = "`".repeat(maxBackticksInCodeBlock) + to.insert(codeBlockStart, extraBackticks) + to.append(extraBackticks) + } + + inCodeBlock = false + } + + override fun appendUnorderedList(body: () -> Unit) { + listStack.push(ListState(ListKind.Unordered)) + body() + listStack.pop() + ensureNewline() + } + + override fun appendOrderedList(body: () -> Unit) { + listStack.push(ListState(ListKind.Ordered)) + body() + listStack.pop() + ensureNewline() + } + + override fun appendListItem(body: () -> Unit) { + ensureNewline() + to.append(listStack.peek()?.getTagAndIncrement()) + body() + ensureNewline() + } + + override fun appendStrong(body: () -> Unit) = wrap("**", "**", body) + override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body) + override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body) + + override fun appendLink(href: String, body: () -> Unit) { + if (inCodeBlock) { + wrap("`[`", "`]($href)`", body) + } + else { + wrap("[", "]($href)", body) + } + } + + override fun appendLine() { + if (inTableCell) { + to.append("<br>") + } + else { + appendNewline() + } + } + + override fun appendAnchor(anchor: String) { + // no anchors in Markdown + } + + override fun appendParagraph(body: () -> Unit) { + if (inTableCell) { + ensureNewline() + body() + } else if (listStack.isNotEmpty()) { + body() + ensureNewline() + } else { + ensureParagraph() + body() + ensureParagraph() + } + } + + override fun appendHeader(level: Int, body: () -> Unit) { + ensureParagraph() + to.append("${"#".repeat(level)} ") + body() + ensureParagraph() + } + + override fun appendBlockCode(language: String, body: () -> Unit) { + inCodeBlock = true + ensureParagraph() + to.appendln(if (language.isEmpty()) "```" else "``` $language") + body() + ensureNewline() + to.appendln("```") + appendLine() + inCodeBlock = false + } + + override fun appendTable(vararg columns: String, body: () -> Unit) { + ensureParagraph() + body() + ensureParagraph() + } + + override fun appendTableBody(body: () -> Unit) { + body() + } + + override fun appendTableRow(body: () -> Unit) { + to.append("|") + body() + appendNewline() + } + + override fun appendTableCell(body: () -> Unit) { + to.append(" ") + inTableCell = true + lastTableCellStart = to.length + body() + inTableCell = false + to.append(" |") + } + + override fun appendNonBreakingSpace() { + if (inCodeBlock) { + to.append(" ") + } + else { + to.append(" ") + } + } +} + +open class MarkdownFormatService(generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + linkExtension: String, + val impliedPlatforms: List<String>) +: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) { + @Inject constructor(generator: NodeLocationAwareGenerator, + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms) + + override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = + MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) +} diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt new file mode 100644 index 000000000..958e93aff --- /dev/null +++ b/core/src/main/kotlin/Formats/OutlineService.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka + +import java.io.File + +/** + * Service for building the outline of the package contents. + */ +interface OutlineFormatService { + fun getOutlineFileName(location: Location): File + + fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) + fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) + + /** Appends formatted outline to [StringBuilder](to) using specified [location] */ + fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { + for (node in nodes) { + appendOutlineHeader(location, node, to) + if (node.members.any()) { + val sortedMembers = node.members.sortedBy { it.name.toLowerCase() } + appendOutlineLevel(to) { + appendOutline(location, to, sortedMembers) + } + } + } + } + + fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String = + StringBuilder().apply { appendOutline(location, this, nodes) }.toString() +} diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt new file mode 100644 index 000000000..7b68098e8 --- /dev/null +++ b/core/src/main/kotlin/Formats/PackageListService.kt @@ -0,0 +1,63 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject + + +interface PackageListService { + fun formatPackageList(module: DocumentationModule): String +} + +class DefaultPackageListService @Inject constructor( + val generator: NodeLocationAwareGenerator, + val formatService: FormatService +) : PackageListService { + + override fun formatPackageList(module: DocumentationModule): String { + val packages = mutableSetOf<String>() + val nonStandardLocations = mutableMapOf<String, String>() + + fun visit(node: DocumentationNode, relocated: Boolean = false) { + val nodeKind = node.kind + + when (nodeKind) { + NodeKind.Package -> { + packages.add(node.qualifiedName()) + node.members.forEach { visit(it) } + } + NodeKind.Signature -> { + if (relocated) + nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!) + } + NodeKind.ExternalClass -> { + node.members.forEach { visit(it, relocated = true) } + } + NodeKind.GroupNode -> { + //only children of top-level GN records interesting for us, since link to top-level ones should point to GN + node.members.forEach { it.members.forEach { visit(it, relocated = true) } } + //record signature of GN as signature of type alias and class merged to GN, so link to it should point to GN + node.detailOrNull(NodeKind.Signature)?.let { visit(it, relocated = true) } + } + else -> { + if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) { + node.details(NodeKind.Signature).forEach { visit(it, relocated) } + node.members.forEach { visit(it, relocated) } + } + } + } + } + + module.members.forEach { visit(it) } + + return buildString { + appendln("\$dokka.linkExtension:${formatService.linkExtension}") + + nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" } + .sorted().joinTo(this, separator = "\n", postfix = "\n") + + packages.sorted().joinTo(this, separator = "\n", postfix = "\n") + } + + } + +} + diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt new file mode 100644 index 000000000..dd67ac972 --- /dev/null +++ b/core/src/main/kotlin/Formats/StandardFormats.kt @@ -0,0 +1,66 @@ +package org.jetbrains.dokka.Formats + +import com.google.inject.Binder +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService +import org.jetbrains.dokka.Utilities.bind +import kotlin.reflect.KClass + +abstract class KotlinFormatDescriptorBase + : FileGeneratorBasedFormatDescriptor(), + DefaultAnalysisComponent, + DefaultAnalysisComponentServices by KotlinAsKotlin { + override val generatorServiceClass = FileGenerator::class + override val outlineServiceClass: KClass<out OutlineFormatService>? = null + override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class +} + +abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent { + override val formatServiceClass = HtmlFormatService::class + override val outlineServiceClass = HtmlFormatService::class + override val generatorServiceClass = FileGenerator::class + override val packageListServiceClass = DefaultPackageListService::class + + override fun configureOutput(binder: Binder): Unit = with(binder) { + super.configureOutput(binder) + bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") } + } +} + +class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin + +class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava + +class KotlinWebsiteFormatDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = KotlinWebsiteFormatService::class + override val outlineServiceClass = YamlOutlineService::class +} + +class KotlinWebsiteFormatRunnableSamplesDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = KotlinWebsiteRunnableSamplesFormatService::class + override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class + override val outlineServiceClass = YamlOutlineService::class +} + +class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = KotlinWebsiteHtmlFormatService::class + override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class + override val outlineServiceClass = YamlOutlineService::class + + override fun configureOutput(binder: Binder) = with(binder) { + super.configureOutput(binder) + bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService) + } +} + +class JekyllFormatDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = JekyllFormatService::class +} + +class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = MarkdownFormatService::class +} + +class GFMFormatDescriptor : KotlinFormatDescriptorBase() { + override val formatServiceClass = GFMFormatService::class +} diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt new file mode 100644 index 000000000..a2c9078f3 --- /dev/null +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -0,0 +1,691 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.LanguageService.RenderMode +import java.util.* + +data class FormatLink(val text: String, val href: String) + +abstract class StructuredOutputBuilder(val to: StringBuilder, + val location: Location, + val generator: NodeLocationAwareGenerator, + val languageService: LanguageService, + val extension: String, + val impliedPlatforms: List<String>) : FormattedOutputBuilder { + + protected fun DocumentationNode.location() = generator.location(this) + + protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { + to.append(prefix) + body() + to.append(suffix) + } + + protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) { + val startLength = to.length + to.append(prefix) + body() + if (checkEndsWith && to.endsWith(suffix)) { + to.setLength(to.length - suffix.length) + } else if (to.length > startLength + prefix.length) { + to.append(suffix) + } else { + to.setLength(startLength) + } + } + + protected fun wrapInTag(tag: String, + body: () -> Unit, + newlineBeforeOpen: Boolean = false, + newlineAfterOpen: Boolean = false, + newlineAfterClose: Boolean = false) { + if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln() + to.append("<$tag>") + if (newlineAfterOpen) to.appendln() + body() + to.append("</$tag>") + if (newlineAfterClose) to.appendln() + } + + protected abstract fun ensureParagraph() + + open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body) + abstract fun appendBlockCode(language: String, body: () -> Unit) + abstract fun appendHeader(level: Int = 1, body: () -> Unit) + abstract fun appendParagraph(body: () -> Unit) + + open fun appendSoftParagraph(body: () -> Unit) { + ensureParagraph() + body() + } + + abstract fun appendLine() + abstract fun appendAnchor(anchor: String) + + abstract fun appendTable(vararg columns: String, body: () -> Unit) + abstract fun appendTableBody(body: () -> Unit) + abstract fun appendTableRow(body: () -> Unit) + abstract fun appendTableCell(body: () -> Unit) + + abstract fun appendText(text: String) + + open fun appendSinceKotlin(version: String) { + appendParagraph { + appendText("Available since Kotlin: ") + appendCode { appendText(version) } + } + } + + open fun appendSectionWithTag(section: ContentSection) { + appendParagraph { + appendStrong { appendText(section.tag) } + appendLine() + appendContent(section) + } + } + + open fun appendSymbol(text: String) { + appendText(text) + } + + open fun appendKeyword(text: String) { + appendText(text) + } + + open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { + appendText(text) + } + + fun appendEntity(text: String) { + to.append(text) + } + + abstract fun appendLink(href: String, body: () -> Unit) + + open fun appendLink(link: FormatLink) { + appendLink(link.href) { appendText(link.text) } + } + + abstract fun appendStrong(body: () -> Unit) + abstract fun appendStrikethrough(body: () -> Unit) + abstract fun appendEmphasis(body: () -> Unit) + abstract fun appendCode(body: () -> Unit) + abstract fun appendUnorderedList(body: () -> Unit) + abstract fun appendOrderedList(body: () -> Unit) + abstract fun appendListItem(body: () -> Unit) + + abstract fun appendBreadcrumbSeparator() + abstract fun appendNonBreakingSpace() + open fun appendSoftLineBreak() { + } + + open fun appendIndentedSoftLineBreak() { + } + + fun appendContent(content: List<ContentNode>) { + for (contentNode in content) { + appendContent(contentNode) + } + } + + open fun appendContent(content: ContentNode) { + when (content) { + is ContentText -> appendText(content.text) + is ContentSymbol -> appendSymbol(content.text) + is ContentKeyword -> appendKeyword(content.text) + is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature) + is ContentNonBreakingSpace -> appendNonBreakingSpace() + is ContentSoftLineBreak -> appendSoftLineBreak() + is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak() + is ContentEntity -> appendEntity(content.text) + is ContentStrong -> appendStrong { appendContent(content.children) } + is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) } + is ContentCode -> appendCode { appendContent(content.children) } + is ContentEmphasis -> appendEmphasis { appendContent(content.children) } + is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) } + is ContentOrderedList -> appendOrderedList { appendContent(content.children) } + is ContentListItem -> appendListItem { + val child = content.children.singleOrNull() + if (child is ContentParagraph) { + appendContent(child.children) + } else { + appendContent(content.children) + } + } + + is ContentNodeLink -> { + val node = content.node + val linkTo = if (node != null) locationHref(location, node) else "#" + appendLinkIfNotThisPage(linkTo, content) + } + is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content) + + is ContentParagraph -> { + if (!content.isEmpty()) { + appendParagraph { appendContent(content.children) } + } + } + + is ContentSpecialReference -> wrapInTag(tag = "aside class=\"note\"", body = { + if (!content.isEmpty()) { + appendContent(content.children) + } + }) + + is ContentBlockSampleCode, is ContentBlockCode -> { + content as ContentBlockCode + fun ContentBlockCode.appendBlockCodeContent() { + children + .dropWhile { it is ContentText && it.text.isBlank() } + .forEach { appendContent(it) } + } + when (content) { + is ContentBlockSampleCode -> + appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent, { content.appendBlockCodeContent() }) + is ContentBlockCode -> + appendBlockCode(content.language, { content.appendBlockCodeContent() }) + } + } + is ContentHeading -> appendHeader(content.level) { appendContent(content.children) } + is ContentBlock -> appendContent(content.children) + } + } + + private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) { + if (href == ".") { + appendContent(content.children) + } else { + appendLink(href) { appendContent(content.children) } + } + } + + open fun link(from: DocumentationNode, + to: DocumentationNode, + name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink = link(from, to, extension, name) + + open fun link(from: DocumentationNode, + to: DocumentationNode, + extension: String, + name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink { + if (to.owner?.kind == NodeKind.GroupNode) + return link(from, to.owner!!, extension, name) + + if (from.owner?.kind == NodeKind.GroupNode) + return link(from.owner!!, to, extension, name) + + return FormatLink(name(to), from.location().relativePathTo(to.location())) + } + + fun locationHref(from: Location, to: DocumentationNode): String { + val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to + if (topLevelPage != null) { + val signature = to.detailOrNull(NodeKind.Signature) + return from.relativePathTo(topLevelPage.location(), signature?.name ?: to.name) + } + return from.relativePathTo(to.location()) + } + + private fun DocumentationNode.isModuleOrPackage(): Boolean = + kind == NodeKind.Module || kind == NodeKind.Package + + protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) { + block() + } + + protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { + block() + } + + protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { + appendTableRow(block) + } + + protected open fun appendPlatforms(platforms: Set<String>) { + if (platforms.isNotEmpty()) { + appendLine() + appendText(platforms.joinToString(prefix = "(", postfix = ")")) + } + } + + protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) { + for ((index, item) in path.withIndex()) { + if (index > 0) { + appendBreadcrumbSeparator() + } + appendLink(item) + } + } + + fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> = + sections.filter { it.subjectName != null }.groupBy { it.tag } + + private fun ContentNode.appendSignature() { + if (this is ContentBlock && this.isEmpty()) { + return + } + + val signatureAsCode = ContentCode() + signatureAsCode.append(this) + appendContent(signatureAsCode) + } + + open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { + open fun build() { + val breakdownByLocation = nodes.groupBy { node -> + node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct() + } + + for ((path, nodes) in breakdownByLocation) { + if (!noHeader && path.isNotEmpty()) { + appendBreadcrumbs(path) + appendLine() + appendLine() + } + appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) + } + } + + private fun appendLocation(nodes: Iterable<DocumentationNode>) { + val singleNode = nodes.singleOrNull() + if (singleNode != null && singleNode.isModuleOrPackage()) { + if (singleNode.kind == NodeKind.Package) { + val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name + appendHeader(2) { appendText("Package $packageName") } + } + singleNode.appendPlatforms() + appendContent(singleNode.content) + } else { + val breakdownByName = nodes.groupBy { node -> node.name } + for ((name, items) in breakdownByName) { + if (!noHeader) + appendHeader { appendText(name) } + appendDocumentation(items, singleNode != null) + } + } + } + + private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) { + val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content } + + if (breakdownBySummary.size == 1) { + formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode) + } else { + for ((_, items) in breakdownBySummary) { + + appendAsOverloadGroup(to, platformsOfItems(items)) { + formatOverloadGroup(items) + } + + } + } + } + + private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) { + for ((index, item) in items.withIndex()) { + if (index > 0) appendLine() + val rendered = languageService.render(item) + item.detailOrNull(NodeKind.Signature)?.let { + if (item.kind !in NodeKind.classLike || !isSingleNode) + appendAnchor(it.name) + } + appendAsSignature(rendered) { + appendCode { appendContent(rendered) } + item.appendSourceLink() + } + item.appendOverrides() + item.appendDeprecation() + item.appendPlatforms() + } + // All items have exactly the same documentation, so we can use any item to render it + val item = items.first() + item.details(NodeKind.OverloadGroupNote).forEach { + appendContent(it.content) + } + + appendContent(item.content.summary) + item.appendDescription() + } + + private fun DocumentationNode.appendSourceLink() { + val sourceUrl = details(NodeKind.SourceUrl).firstOrNull() + if (sourceUrl != null) { + to.append(" ") + appendLink(sourceUrl.name) { to.append("(source)") } + } + } + + private fun DocumentationNode.appendOverrides() { + overrides.forEach { + appendParagraph { + to.append("Overrides ") + val location = location().relativePathTo(it.location()) + + appendLink(FormatLink(it.owner!!.name + "." + it.name, location)) + } + } + } + + private fun DocumentationNode.appendDeprecation() { + if (deprecation != null) { + val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull() + val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull() + appendLine() + if (deprecationValue != null) { + appendStrong { to.append("Deprecated:") } + appendText(" " + deprecationValue.name.removeSurrounding("\"")) + appendLine() + appendLine() + } else if (deprecation?.content != Content.Empty) { + appendStrong { to.append("Deprecated:") } + to.append(" ") + appendContent(deprecation!!.content) + } else { + appendStrong { to.append("Deprecated") } + appendLine() + appendLine() + } + } + } + + private fun DocumentationNode.appendPlatforms() { + val platforms = if (isModuleOrPackage()) + platformsToShow.toSet() + platformsOfItems(members) + else + platformsToShow + + if (platforms.isEmpty()) return + + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + platforms.joinToString()) + } + } + + protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { + val platforms = items.asSequence().map { + when (it.kind) { + NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members) + else -> it.platformsToShow.toSet() + } + } + + fun String.isKotlinVersion() = this.startsWith("Kotlin") + + // Calculating common platforms for items + return platforms.reduce { result, platformsOfItem -> + val otherKotlinVersion = result.find { it.isKotlinVersion() } + val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } + + // When no Kotlin version specified, it means that version is 1.0 + if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { + val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct() + + val minVersion = allKotlinVersions.min()!! + val resultVersion = when { + allKotlinVersions.size == 1 -> allKotlinVersions.single() + minVersion.endsWith("+") -> minVersion + else -> minVersion + "+" + } + + result.intersect(otherPlatforms) + resultVersion + } else { + result.intersect(platformsOfItem) + } + } + } + + val DocumentationNode.platformsToShow: List<String> + get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it } + + private fun DocumentationNode.appendDescription() { + if (content.description != ContentEmpty) { + appendContent(content.description) + } + content.getSectionsWithSubjects().forEach { + appendSectionWithSubject(it.key, it.value) + } + + for (section in content.sections.filter { it.subjectName == null }) { + appendSectionWithTag(section) + } + } + + fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) { + appendHeader(3) { appendText(title) } + subjectSections.forEach { + val subjectName = it.subjectName + if (subjectName != null) { + appendSoftParagraph { + appendAnchor(subjectName) + appendCode { to.append(subjectName) } + to.append(" - ") + appendContent(it) + } + } + } + } + } + + inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { + + override fun build() { + val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } + + appendBreadcrumbs(breakdownByLocation) + appendLine() + appendLine() + appendHeader { appendText(node.name) } + + fun DocumentationNode.priority(): Int = when (kind) { + NodeKind.TypeAlias -> 1 + NodeKind.Class -> 2 + else -> 3 + } + + for (member in node.members.sortedBy(DocumentationNode::priority)) { + + appendAsOverloadGroup(to, platformsOfItems(listOf(member))) { + formatSubNodeOfGroup(member) + } + + } + } + + fun formatSubNodeOfGroup(member: DocumentationNode) { + SingleNodePageBuilder(member, true).build() + } + } + + inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) + : PageBuilder(listOf(node), noHeader) { + + override fun build() { + super.build() + + if (node.kind == NodeKind.ExternalClass) { + appendSection("Extensions for ${node.name}", node.members) + return + } + + fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { + return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) } + } + + fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { + return membersOrGroupMembers { it.kind == kind } + } + + appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) + appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) + appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) + appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) + appendSection("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias)) + appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) + appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) + appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true) + appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true) + appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) + appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true) + appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) + appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) + appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) + appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) + appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) + appendSection("Other members", node.members.filter { + it.kind !in setOf( + NodeKind.Class, + NodeKind.Interface, + NodeKind.Enum, + NodeKind.Object, + NodeKind.AnnotationClass, + NodeKind.Exception, + NodeKind.TypeAlias, + NodeKind.Constructor, + NodeKind.Property, + NodeKind.Package, + NodeKind.Function, + NodeKind.CompanionObjectProperty, + NodeKind.CompanionObjectFunction, + NodeKind.ExternalClass, + NodeKind.EnumItem, + NodeKind.AllTypes, + NodeKind.GroupNode + ) + }) + + val allExtensions = node.extensions + appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property }) + appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function }) + appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty }) + appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction }) + appendSection("Inheritors", + node.inheritors.filter { it.kind != NodeKind.EnumItem }) + + if (node.kind == NodeKind.Module) { + appendHeader(3) { to.append("Index") } + node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes -> + appendLink(link(node, allTypes, { "All Types" })) + } + } + } + + private fun appendSection(caption: String, members: List<DocumentationNode>, + sortMembers: Boolean = true, + omitSamePlatforms: Boolean = false, + platformsBasedOnMembers: Boolean = false) { + if (members.isEmpty()) return + + appendHeader(3) { appendText(caption) } + + val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members + val membersMap = children.groupBy { link(node, it) } + + + + appendTable("Name", "Summary") { + appendTableBody { + for ((memberLocation, members) in membersMap) { + val elementPlatforms = platformsOfItems(members, omitSamePlatforms) + val platforms = if (platformsBasedOnMembers) + members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms + else + elementPlatforms + appendIndexRow(platforms) { + appendTableCell { + appendParagraph { + appendLink(memberLocation) + if (members.singleOrNull()?.kind != NodeKind.ExternalClass) { + appendPlatforms(platforms) + } + } + } + appendTableCell { + val breakdownBySummary = members.groupBy { it.summary } + for ((summary, items) in breakdownBySummary) { + appendSummarySignatures(items) + appendContent(summary) + } + } + } + } + } + } + } + + private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> { + val platforms = platformsOfItems(items) + if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { + return platforms + } + return emptySet() + } + + private fun appendSummarySignatures(items: List<DocumentationNode>) { + val summarySignature = languageService.summarizeSignatures(items) + if (summarySignature != null) { + appendAsSignature(summarySignature) { + summarySignature.appendSignature() + } + return + } + val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) } + renderedSignatures.subList(0, renderedSignatures.size - 1).forEach { + appendAsSignature(it) { + it.appendSignature() + } + appendLine() + } + appendAsSignature(renderedSignatures.last()) { + renderedSignatures.last().appendSignature() + } + } + } + + inner class AllTypesNodeBuilder(val node: DocumentationNode) + : PageBuilder(listOf(node)) { + + override fun build() { + appendContent(node.owner!!.summary) + appendHeader(3) { to.append("All Types") } + + appendTable("Name", "Summary") { + appendTableBody { + for (type in node.members) { + appendTableRow { + appendTableCell { + appendLink(link(node, type) { + if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName() + }) + if (type.kind == NodeKind.ExternalClass) { + val packageName = type.owner?.name + if (packageName != null) { + appendText(" (extensions in package $packageName)") + } + } + } + appendTableCell { + appendContent(type.summary) + } + } + } + } + } + } + } + + override fun appendNodes(nodes: Iterable<DocumentationNode>) { + val singleNode = nodes.singleOrNull() + when (singleNode?.kind) { + NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() + NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() + null -> PageBuilder(nodes).build() + else -> SingleNodePageBuilder(singleNode).build() + } + } +} + +abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator, + val languageService: LanguageService, + override val extension: String, + override final val linkExtension: String = extension) : FormatService { + +} diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt new file mode 100644 index 000000000..c36f98eb0 --- /dev/null +++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt @@ -0,0 +1,26 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import java.io.File + +class YamlOutlineService @Inject constructor( + val generator: NodeLocationAwareGenerator, + val languageService: LanguageService +) : OutlineFormatService { + override fun getOutlineFileName(location: Location): File = File("${location.path}.yml") + + var outlineLevel = 0 + override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { + val indent = " ".repeat(outlineLevel) + to.appendln("$indent- title: ${languageService.renderName(node)}") + to.appendln("$indent url: ${generator.location(node).path}") + } + + override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { + val indent = " ".repeat(outlineLevel) + to.appendln("$indent content:") + outlineLevel++ + body() + outlineLevel-- + } +} diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt new file mode 100644 index 000000000..6f063b587 --- /dev/null +++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt @@ -0,0 +1,207 @@ +package org.jetbrains.dokka + +import com.google.inject.Guice +import com.google.inject.Injector +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiManager +import org.jetbrains.dokka.DokkaConfiguration.SourceRoot +import org.jetbrains.dokka.Utilities.DokkaAnalysisModule +import org.jetbrains.dokka.Utilities.DokkaOutputModule +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer +import org.jetbrains.kotlin.resolve.TopDownAnalysisMode +import java.io.File +import kotlin.system.measureTimeMillis + +class DokkaGenerator(val logger: DokkaLogger, + val classpath: List<String>, + val sources: List<SourceRoot>, + val samples: List<String>, + val includes: List<String>, + val moduleName: String, + val options: DocumentationOptions) { + + private val documentationModule = DocumentationModule(moduleName) + + fun generate() { + val sourcesGroupedByPlatform = sources.groupBy { it.platforms.firstOrNull() } + for ((platform, roots) in sourcesGroupedByPlatform) { + appendSourceModule(platform, roots) + } + documentationModule.prepareForGeneration(options) + + val timeBuild = measureTimeMillis { + logger.info("Generating pages... ") + val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger)) + outputInjector.getInstance(Generator::class.java).buildAll(documentationModule) + } + logger.info("done in ${timeBuild / 1000} secs") + } + + private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) { + val sourcePaths = sourceRoots.map { it.path } + val environment = createAnalysisEnvironment(sourcePaths) + + logger.info("Module: $moduleName") + logger.info("Output: ${File(options.outputDir)}") + logger.info("Sources: ${sourcePaths.joinToString()}") + logger.info("Classpath: ${environment.classpath.joinToString()}") + + logger.info("Analysing sources and libraries... ") + val startAnalyse = System.currentTimeMillis() + + val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty() + val defaultPlatformsProvider = object : DefaultPlatformsProvider { + override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> { + val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath + ?.let { File(it).absolutePath } + val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } + return sourceRoot?.platforms ?: defaultPlatformAsList + } + } + + val injector = Guice.createInjector( + DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger)) + + buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes) + + val timeAnalyse = System.currentTimeMillis() - startAnalyse + logger.info("done in ${timeAnalyse / 1000} secs") + + Disposer.dispose(environment) + } + + fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment { + val environment = AnalysisEnvironment(DokkaMessageCollector(logger)) + + environment.apply { + //addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) + // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath()) + for (element in this@DokkaGenerator.classpath) { + addClasspath(File(element)) + } + + addSources(sourcePaths) + addSources(this@DokkaGenerator.samples) + + loadLanguageVersionSettings(options.languageVersion, options.apiVersion) + } + + return environment + } + + fun isNotSample(file: PsiFile): Boolean { + val sourceFile = File(file.virtualFile!!.path) + return samples.none { sample -> + val canonicalSample = File(sample).canonicalPath + val canonicalSource = sourceFile.canonicalPath + canonicalSource.startsWith(canonicalSample) + } + } +} + +class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector { + override fun clear() { + seenErrors = false + } + + private var seenErrors = false + + override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { + if (severity == CompilerMessageSeverity.ERROR) { + seenErrors = true + } + logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) + } + + override fun hasErrors() = seenErrors +} + +fun buildDocumentationModule(injector: Injector, + documentationModule: DocumentationModule, + filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, + includes: List<String> = listOf()) { + + val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) + val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) + + val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java) + val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java) + analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) + + val fragments = fragmentFiles + .map { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) } + .filterNotNull() + .distinct() + + val packageDocs = injector.getInstance(PackageDocs::class.java) + for (include in includes) { + packageDocs.parse(include, fragments) + } + if (documentationModule.content.isEmpty()) { + documentationModule.updateContent { + for (node in packageDocs.moduleContent.children) { + append(node) + } + } + } + + parseJavaPackageDocs(packageDocs, coreEnvironment) + + with(injector.getInstance(DocumentationBuilder::class.java)) { + documentationModule.appendFragments(fragments, packageDocs.packageContent, + injector.getInstance(PackageDocumentationBuilder::class.java)) + + propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade) + } + + val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter) + with(injector.getInstance(JavaDocumentationBuilder::class.java)) { + javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } + } +} + +fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) { + val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<JavaSourceRoot>() + ?.map { it.file } + ?: listOf() + contentRoots.forEach { root -> + root.walkTopDown().filter { it.name == "overview.html" }.forEach { + packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", ".")) + } + } +} + + +fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> { + val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<JavaSourceRoot>() + ?.map { it.file } + ?: listOf() + + val result = arrayListOf<PsiJavaFile>() + val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") + sourceRoots.forEach { sourceRoot -> + sourceRoot.absoluteFile.walkTopDown().forEach { + val vFile = localFileSystem.findFileByPath(it.path) + if (vFile != null) { + val psiFile = PsiManager.getInstance(project).findFile(vFile) + if (psiFile is PsiJavaFile) { + result.add(psiFile) + } + } + } + } + return result +} diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt new file mode 100644 index 000000000..2d202db84 --- /dev/null +++ b/core/src/main/kotlin/Generation/FileGenerator.kt @@ -0,0 +1,89 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStreamWriter + +class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator { + + @set:Inject(optional = true) var outlineService: OutlineFormatService? = null + @set:Inject(optional = true) lateinit var formatService: FormatService + @set:Inject(optional = true) lateinit var options: DocumentationOptions + @set:Inject(optional = true) var packageListService: PackageListService? = null + + override fun location(node: DocumentationNode): FileLocation { + return FileLocation(fileForNode(node, formatService.linkExtension)) + } + + private fun fileForNode(node: DocumentationNode, extension: String = ""): File { + return File(root, relativePathToNode(node)).appendExtension(extension) + } + + fun locationWithoutExtension(node: DocumentationNode): FileLocation { + return FileLocation(fileForNode(node)) + } + + override fun buildPages(nodes: Iterable<DocumentationNode>) { + + for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) { + + file.parentFile?.mkdirsOrFail() + try { + FileOutputStream(file).use { + OutputStreamWriter(it, Charsets.UTF_8).use { + it.write(formatService.format(location(items.first()), items)) + } + } + } catch (e: Throwable) { + println(e) + } + buildPages(items.flatMap { it.members }) + } + } + + override fun buildOutlines(nodes: Iterable<DocumentationNode>) { + val outlineService = this.outlineService ?: return + for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) { + val file = outlineService.getOutlineFileName(location) + file.parentFile?.mkdirsOrFail() + FileOutputStream(file).use { + OutputStreamWriter(it, Charsets.UTF_8).use { + it.write(outlineService.formatOutline(location, items)) + } + } + } + } + + override fun buildSupportFiles() { + formatService.enumerateSupportFiles { resource, targetPath -> + FileOutputStream(File(root, relativePathToNode(listOf(targetPath), false))).use { + javaClass.getResourceAsStream(resource).copyTo(it) + } + } + } + + override fun buildPackageList(nodes: Iterable<DocumentationNode>) { + if (packageListService == null) return + + for (module in nodes) { + + val moduleRoot = location(module).file.parentFile + val packageListFile = File(moduleRoot, "package-list") + + packageListFile.writeText("\$dokka.format:${options.outputFormat}\n" + + packageListService!!.formatPackageList(module as DocumentationModule)) + } + + } + +} + +fun File.mkdirsOrFail() { + if (!mkdirs() && !exists()) { + throw IOException("Failed to create directory $this") + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt new file mode 100644 index 000000000..23286e299 --- /dev/null +++ b/core/src/main/kotlin/Generation/Generator.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka + +import java.io.File + +interface Generator { + fun buildPages(nodes: Iterable<DocumentationNode>) + fun buildOutlines(nodes: Iterable<DocumentationNode>) + fun buildSupportFiles() + fun buildPackageList(nodes: Iterable<DocumentationNode>) +} + +fun Generator.buildAll(nodes: Iterable<DocumentationNode>) { + buildPages(nodes) + buildOutlines(nodes) + buildSupportFiles() + buildPackageList(nodes) +} + +fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)) + +fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node)) + +fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node)) + + +interface NodeLocationAwareGenerator: Generator { + fun location(node: DocumentationNode): Location + val root: File +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt new file mode 100644 index 000000000..c8f93c519 --- /dev/null +++ b/core/src/main/kotlin/Generation/configurationImpl.kt @@ -0,0 +1,67 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition +import org.jetbrains.dokka.DokkaConfiguration.SourceRoot +import java.io.File + + +data class SourceLinkDefinitionImpl(override val path: String, + override val url: String, + override val lineSuffix: String?) : SourceLinkDefinition { + companion object { + fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition { + val (path, urlAndLine) = srcLink.split('=') + return SourceLinkDefinitionImpl(File(path).absolutePath, + urlAndLine.substringBefore("#"), + urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it }) + } + } +} + +class SourceRootImpl(path: String, override val platforms: List<String> = emptyList()) : SourceRoot { + override val path: String = File(path).absolutePath + + companion object { + fun parseSourceRoot(sourceRoot: String): SourceRoot { + val components = sourceRoot.split("::", limit = 2) + return SourceRootImpl(components.last(), if (components.size == 1) listOf() else components[0].split(',')) + } + } +} + +data class PackageOptionsImpl(override val prefix: String, + override val includeNonPublic: Boolean = false, + override val reportUndocumented: Boolean = true, + override val skipDeprecated: Boolean = false, + override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions + +data class DokkaConfigurationImpl( + override val moduleName: String, + override val classpath: List<String>, + override val sourceRoots: List<SourceRootImpl>, + override val samples: List<String>, + override val includes: List<String>, + override val outputDir: String, + override val format: String, + override val includeNonPublic: Boolean, + override val includeRootPackage: Boolean, + override val reportUndocumented: Boolean, + override val skipEmptyPackages: Boolean, + override val skipDeprecated: Boolean, + override val jdkVersion: Int, + override val generateClassIndexPage: Boolean, + override val generatePackageIndexPage: Boolean, + override val sourceLinks: List<SourceLinkDefinitionImpl>, + override val impliedPlatforms: List<String>, + override val perPackageOptions: List<PackageOptionsImpl>, + override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>, + override val noStdlibLink: Boolean, + override val noJdkLink: Boolean, + override val cacheRoot: String?, + override val suppressedFiles: List<String>, + override val languageVersion: String?, + override val apiVersion: String?, + override val collectInheritedExtensionsFromLibraries: Boolean, + override val outlineRoot: String, + override val dacRoot: String +) : DokkaConfiguration
\ No newline at end of file diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt new file mode 100644 index 000000000..94bb0455d --- /dev/null +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -0,0 +1,392 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.* +import com.intellij.psi.impl.JavaConstantExpressionEvaluator +import com.intellij.psi.impl.source.PsiClassReferenceType +import com.intellij.psi.util.InheritanceUtil +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtModifierListOwner +import java.io.File + +fun getSignature(element: PsiElement?) = when(element) { + is PsiPackage -> element.qualifiedName + is PsiClass -> element.qualifiedName + is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name + is PsiMethod -> + element.containingClass?.qualifiedName + "$" + element.name + "(" + + element.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")" + else -> null +} + +private fun PsiType.typeSignature(): String = when(this) { + is PsiArrayType -> "Array((${componentType.typeSignature()}))" + is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize() + is PsiClassType -> resolve()?.qualifiedName ?: className + else -> mapTypeName(this) +} + +private fun mapTypeName(psiType: PsiType): String = when (psiType) { + is PsiPrimitiveType -> psiType.canonicalText + is PsiClassType -> psiType.resolve()?.name ?: psiType.className + is PsiEllipsisType -> mapTypeName(psiType.componentType) + is PsiArrayType -> "kotlin.Array" + else -> psiType.canonicalText +} + +interface JavaDocumentationBuilder { + fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) +} + +class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { + private val options: DocumentationOptions + private val refGraph: NodeReferenceGraph + private val docParser: JavaDocumentationParser + private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + + @Inject constructor( + options: DocumentationOptions, + refGraph: NodeReferenceGraph, + logger: DokkaLogger, + signatureProvider: ElementSignatureProvider, + externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + ) { + this.options = options + this.refGraph = refGraph + this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) + this.externalDocumentationLinkResolver = externalDocumentationLinkResolver + } + + constructor( + options: DocumentationOptions, + refGraph: NodeReferenceGraph, + docParser: JavaDocumentationParser, + externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + ) { + this.options = options + this.refGraph = refGraph + this.docParser = docParser + this.externalDocumentationLinkResolver = externalDocumentationLinkResolver + } + + override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { + if (skipFile(file) || file.classes.all { skipElement(it) }) { + return + } + val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) + appendClasses(packageNode, file.classes) + } + + fun appendClasses(packageNode: DocumentationNode, classes: Array<PsiClass>) { + packageNode.appendChildren(classes) { build() } + } + + fun register(element: PsiElement, node: DocumentationNode) { + val signature = getSignature(element) + if (signature != null) { + refGraph.register(signature, node) + } + } + + fun link(node: DocumentationNode, element: PsiElement?) { + val qualifiedName = getSignature(element) + if (qualifiedName != null) { + refGraph.link(node, qualifiedName, RefKind.Link) + } + } + + fun link(element: PsiElement?, node: DocumentationNode, kind: RefKind) { + val qualifiedName = getSignature(element) + if (qualifiedName != null) { + refGraph.link(qualifiedName, node, kind) + } + } + + fun nodeForElement(element: PsiNamedElement, + kind: NodeKind, + name: String = element.name ?: "<anonymous>"): DocumentationNode { + val (docComment, deprecatedContent, attrs, apiLevel, sdkExtSince, deprecatedLevel, artifactId, attribute) = docParser.parseDocumentation(element) + val node = DocumentationNode(name, docComment, kind) + if (element is PsiModifierListOwner) { + node.appendModifiers(element) + val modifierList = element.modifierList + if (modifierList != null) { + modifierList.annotations.filter { !ignoreAnnotation(it) }.forEach { + val annotation = it.build() + if (it.qualifiedName == "java.lang.Deprecated" || it.qualifiedName == "kotlin.Deprecated") { + node.append(annotation, RefKind.Deprecation) + annotation.convertDeprecationDetailsToChildren() + } else { + node.append(annotation, RefKind.Annotation) + } + } + } + } + if (deprecatedContent != null) { + val deprecationNode = DocumentationNode("", deprecatedContent, NodeKind.Modifier) + node.append(deprecationNode, RefKind.Deprecation) + } + if (element is PsiDocCommentOwner && element.isDeprecated && node.deprecation == null) { + val deprecationNode = DocumentationNode("", Content.of(ContentText("Deprecated")), NodeKind.Modifier) + node.append(deprecationNode, RefKind.Deprecation) + } + apiLevel?.let { + node.append(it, RefKind.Detail) + } + sdkExtSince?.let { + node.append(it, RefKind.Detail) + } + deprecatedLevel?.let { + node.append(it, RefKind.Detail) + } + artifactId?.let { + node.append(it, RefKind.Detail) + } + attrs.forEach { + refGraph.link(node, it, RefKind.Detail) + refGraph.link(it, node, RefKind.Owner) + } + attribute?.let { + val attrName = node.qualifiedName() + refGraph.register("Attr:$attrName", attribute) + } + return node + } + + fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) { + "java.lang.SuppressWarnings" -> true + else -> false + } + + fun <T : Any> DocumentationNode.appendChildren(elements: Array<T>, + kind: RefKind = RefKind.Member, + buildFn: T.() -> DocumentationNode) { + elements.forEach { + if (!skipElement(it)) { + append(it.buildFn(), kind) + } + } + } + + private fun skipFile(javaFile: PsiJavaFile): Boolean = options.effectivePackageOptions(javaFile.packageName).suppress + + private fun skipElement(element: Any) = + skipElementByVisibility(element) || + hasSuppressDocTag(element) || + hasHideAnnotation(element) || + skipElementBySuppressedFiles(element) + + private fun skipElementByVisibility(element: Any): Boolean = + element is PsiModifierListOwner && + element !is PsiParameter && + !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && + (element.hasModifierProperty(PsiModifier.PRIVATE) || + element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || + element.isInternal()) + + private fun skipElementBySuppressedFiles(element: Any): Boolean = + element is PsiElement && element.containingFile.virtualFile != null && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles + + private fun PsiElement.isInternal(): Boolean { + val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false + return (ktElement as? KtModifierListOwner)?.hasModifier(KtTokens.INTERNAL_KEYWORD) ?: false + } + + fun <T : Any> DocumentationNode.appendMembers(elements: Array<T>, buildFn: T.() -> DocumentationNode) = + appendChildren(elements, RefKind.Member, buildFn) + + fun <T : Any> DocumentationNode.appendDetails(elements: Array<T>, buildFn: T.() -> DocumentationNode) = + appendChildren(elements, RefKind.Detail, buildFn) + + fun PsiClass.build(): DocumentationNode { + val kind = when { + isInterface -> NodeKind.Interface + isEnum -> NodeKind.Enum + isAnnotationType -> NodeKind.AnnotationClass + isException() -> NodeKind.Exception + else -> NodeKind.Class + } + val node = nodeForElement(this, kind) + superTypes.filter { !ignoreSupertype(it) }.forEach { superType -> + node.appendType(superType, NodeKind.Supertype) + val superClass = superType.resolve() + if (superClass != null) { + link(superClass, node, RefKind.Inheritor) + } + } + + var methodsAndConstructors = methods + + if (constructors.isEmpty()) { + // Having no constructor represents a class that only has an implicit/default constructor + // so we create one synthetically for documentation + val factory = JavaPsiFacade.getElementFactory(this.project) + methodsAndConstructors += factory.createMethodFromText("public $name() {}", this) + } + node.appendDetails(typeParameters) { build() } + node.appendMembers(methodsAndConstructors) { build() } + node.appendMembers(fields) { build() } + node.appendMembers(innerClasses) { build() } + register(this, node) + return node + } + + fun PsiClass.isException() = InheritanceUtil.isInheritor(this, "java.lang.Throwable") + + fun ignoreSupertype(psiType: PsiClassType): Boolean = false +// psiType.isClass("java.lang.Enum") || psiType.isClass("java.lang.Object") + + fun PsiClassType.isClass(qName: String): Boolean { + val shortName = qName.substringAfterLast('.') + if (className == shortName) { + val psiClass = resolve() + return psiClass?.qualifiedName == qName + } + return false + } + + fun PsiField.build(): DocumentationNode { + val node = nodeForElement(this, nodeKind()) + node.appendType(type) + + node.appendConstantValueIfAny(this) + register(this, node) + return node + } + + private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) { + val modifierList = field.modifierList ?: return + val initializer = field.initializer ?: return + if (modifierList.hasExplicitModifier(PsiModifier.FINAL) && + modifierList.hasExplicitModifier(PsiModifier.STATIC)) { + val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false) ?: return + val text = when(value) { + is String -> "\"${StringUtil.escapeStringCharacters(value)}\"" + else -> value.toString() + } + append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail) + } + } + + private fun PsiField.nodeKind(): NodeKind = when { + this is PsiEnumConstant -> NodeKind.EnumItem + else -> NodeKind.Field + } + + fun PsiMethod.build(): DocumentationNode { + val node = nodeForElement(this, nodeKind(), name) + + if (!isConstructor) { + node.appendType(returnType) + } + node.appendDetails(parameterList.parameters) { build() } + node.appendDetails(typeParameters) { build() } + register(this, node) + return node + } + + private fun PsiMethod.nodeKind(): NodeKind = when { + isConstructor -> NodeKind.Constructor + else -> NodeKind.Function + } + + fun PsiParameter.build(): DocumentationNode { + val node = nodeForElement(this, NodeKind.Parameter) + node.appendType(type) + if (type is PsiEllipsisType) { + node.appendTextNode("vararg", NodeKind.Modifier, RefKind.Detail) + } + return node + } + + fun PsiTypeParameter.build(): DocumentationNode { + val node = nodeForElement(this, NodeKind.TypeParameter) + extendsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } + implementsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } + return node + } + + fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) { + val modifierList = element.modifierList ?: return + + PsiModifier.MODIFIERS.forEach { + if (modifierList.hasExplicitModifier(it)) { + appendTextNode(it, NodeKind.Modifier) + } + } + } + + fun DocumentationNode.appendType(psiType: PsiType?, kind: NodeKind = NodeKind.Type) { + if (psiType == null) { + return + } + + val node = psiType.build(kind) + append(node, RefKind.Detail) + + // Attempt to create an external link if the psiType is one + if (psiType is PsiClassReferenceType) { + val target = psiType.reference.resolve() + if (target != null) { + val externalLink = externalDocumentationLinkResolver.buildExternalDocumentationLink(target) + if (externalLink != null) { + node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) + } + } + } + } + + fun PsiType.build(kind: NodeKind = NodeKind.Type): DocumentationNode { + val name = mapTypeName(this) + val node = DocumentationNode(name, Content.Empty, kind) + if (this is PsiClassType) { + node.appendDetails(parameters) { build(NodeKind.Type) } + link(node, resolve()) + } + if (this is PsiArrayType && this !is PsiEllipsisType) { + node.append(componentType.build(NodeKind.Type), RefKind.Detail) + } + return node + } + + fun PsiAnnotation.build(): DocumentationNode { + val node = DocumentationNode(nameReferenceElement?.text ?: "<?>", Content.Empty, NodeKind.Annotation) + parameterList.attributes.forEach { + val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter) + val value = it.value + if (value != null) { + val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text + val valueNode = DocumentationNode(valueText, Content.Empty, NodeKind.Value) + parameter.append(valueNode, RefKind.Detail) + } + node.append(parameter, RefKind.Detail) + } + return node + } +} + +fun hasSuppressDocTag(element: Any?): Boolean { + val declaration = (element as? KtLightDeclaration<*, *>)?.kotlinOrigin as? KtDeclaration ?: return false + return PsiTreeUtil.findChildrenOfType(declaration.docComment, KDocTag::class.java).any { it.knownTag == KDocKnownTag.SUPPRESS } +} + +/** + * Determines if the @hide annotation is present in a Javadoc comment. + * + * @param element a doc element to analyze for the presence of @hide + * + * @return true if @hide is present, otherwise false + * + * Note: this does not process @hide annotations in KDoc. For KDoc, use the @suppress tag instead, which is processed + * by [hasSuppressDocTag]. + */ +fun hasHideAnnotation(element: Any?): Boolean { + return element is PsiDocCommentOwner && element.docComment?.run { findTagByName("hide") != null } ?: false +} diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt new file mode 100644 index 000000000..0c73e7661 --- /dev/null +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -0,0 +1,709 @@ +package org.jetbrains.dokka + +import com.intellij.psi.* +import com.intellij.psi.impl.source.javadoc.PsiDocTagValueImpl +import com.intellij.psi.impl.source.tree.JavaDocElementType +import com.intellij.psi.javadoc.* +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.IncorrectOperationException +import org.jetbrains.dokka.Model.CodeNode +import org.jetbrains.kotlin.utils.join +import org.jetbrains.kotlin.utils.keysToMap +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode +import java.io.File +import java.net.URI +import java.util.regex.Pattern + +private val NAME_TEXT = Pattern.compile("(\\S+)(.*)", Pattern.DOTALL) +private val TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL) + +data class JavadocParseResult( + val content: Content, + val deprecatedContent: Content?, + val attributeRefs: List<String>, + val apiLevel: DocumentationNode? = null, + val sdkExtSince: DocumentationNode? = null, + val deprecatedLevel: DocumentationNode? = null, + val artifactId: DocumentationNode? = null, + val attribute: DocumentationNode? = null +) { + companion object { + val Empty = JavadocParseResult(Content.Empty, + null, + emptyList(), + null, + null, + null, + null + ) + } +} + +interface JavaDocumentationParser { + fun parseDocumentation(element: PsiNamedElement): JavadocParseResult +} + +class JavadocParser( + private val refGraph: NodeReferenceGraph, + private val logger: DokkaLogger, + private val signatureProvider: ElementSignatureProvider, + private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver +) : JavaDocumentationParser { + + private fun ContentSection.appendTypeElement( + signature: String, + selector: (DocumentationNode) -> DocumentationNode? + ) { + append(LazyContentBlock { + val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) + if (node != null) { + it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) + it.symbol(":") + it.text(" ") + } + }) + } + + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { + val docComment = (element as? PsiDocCommentOwner)?.docComment + if (docComment == null) return JavadocParseResult.Empty + val result = MutableContent() + var deprecatedContent: Content? = null + val firstParagraph = ContentParagraph() + firstParagraph.convertJavadocElements( + docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, + element + ) + val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } + firstParagraph.children.removeAll(paragraphs) + if (!firstParagraph.isEmpty()) { + result.append(firstParagraph) + } + paragraphs.forEach { + result.append(it) + } + + if (element is PsiMethod) { + val tagsByName = element.searchInheritedTags() + for ((tagName, tags) in tagsByName) { + for ((tag, context) in tags) { + val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) + val signature = signatureProvider.signature(element) + when (tagName) { + "param" -> { + section.appendTypeElement(signature) { + it.details + .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() } + ?.detailOrNull(NodeKind.Type) + } + } + "return" -> { + section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } + } + } + section.convertJavadocElements(tag.contentElements(), context) + } + } + } + + val attrRefSignatures = mutableListOf<String>() + var since: DocumentationNode? = null + var sdkextsince: DocumentationNode? = null + var deprecated: DocumentationNode? = null + var artifactId: DocumentationNode? = null + var attrName: String? = null + var attrDesc: Content? = null + var attr: DocumentationNode? = null + docComment.tags.forEach { tag -> + when (tag.name.toLowerCase()) { + "see" -> result.convertSeeTag(tag) + "deprecated" -> { + deprecatedContent = Content().apply { + convertJavadocElements(tag.contentElements(), element) + } + } + "attr" -> { + when (tag.valueElement?.text) { + "ref" -> + tag.getAttrRef(element)?.let { + attrRefSignatures.add(it) + } + "name" -> attrName = tag.getAttrName() + "description" -> attrDesc = tag.getAttrDesc(element) + } + } + "since", "apisince" -> { + since = DocumentationNode(tag.getApiLevel() ?: "", Content.Empty, NodeKind.ApiLevel) + } + "sdkextsince" -> { + sdkextsince = DocumentationNode(tag.getSdkExtSince() ?: "", Content.Empty, NodeKind.SdkExtSince) + } + "deprecatedsince" -> { + deprecated = DocumentationNode(tag.getApiLevel() ?: "", Content.Empty, NodeKind.DeprecatedLevel) + } + "artifactid" -> { + artifactId = DocumentationNode(tag.artifactId() ?: "", Content.Empty, NodeKind.ArtifactId) + } + in tagsToInherit -> { + } + else -> { + val subjectName = tag.getSubjectName() + val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) + section.convertJavadocElements(tag.contentElements(), element) + } + } + } + attrName?.let { name -> + attr = DocumentationNode(name, attrDesc ?: Content.Empty, NodeKind.AttributeRef) + } + return JavadocParseResult(result, deprecatedContent, attrRefSignatures, since, sdkextsince, deprecated, artifactId, attr) + } + + private val tagsToInherit = setOf("param", "return", "throws") + + private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement) + + fun PsiDocTag.artifactId(): String? { + var artifactName: String? = null + if (dataElements.isNotEmpty()) { + artifactName = join(dataElements.map { it.text }, "") + } + return artifactName + } + + fun PsiDocTag.getApiLevel(): String? { + if (dataElements.isNotEmpty()) { + val data = dataElements + if (data[0] is PsiDocTagValueImpl) { + val docTagValue = data[0] + if (docTagValue.firstChild != null) { + val apiLevel = docTagValue.firstChild + return apiLevel.text + } + } + } + return null + } + + fun PsiDocTag.getSdkExtSince(): String? { + if (dataElements.isNotEmpty()) { + return join(dataElements.map { it.text }, " ") + } + return null + } + + private fun PsiDocTag.getAttrRef(element: PsiNamedElement): String? { + if (dataElements.size > 1) { + val elementText = dataElements[1].text + try { + val linkComment = JavaPsiFacade.getInstance(project).elementFactory + .createDocCommentFromText("/** {@link $elementText} */", element) + val linkElement = PsiTreeUtil.getChildOfType(linkComment, PsiInlineDocTag::class.java)?.linkElement() + val signature = resolveInternalLink(linkElement) + val attrSignature = "AttrMain:$signature" + return attrSignature + } catch (e: IncorrectOperationException) { + return null + } + } else return null + } + + private fun PsiDocTag.getAttrName(): String? { + if (dataElements.size > 1) { + val nameMatcher = NAME_TEXT.matcher(dataElements[1].text) + if (nameMatcher.matches()) { + return nameMatcher.group(1) + } else { + return null + } + } else return null + } + + private fun PsiDocTag.getAttrDesc(element: PsiNamedElement): Content? { + return Content().apply { + convertJavadocElementsToAttrDesc(contentElements(), element) + } + } + + private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> { + + val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() } + + fun recursiveSearch(methods: Array<PsiMethod>) { + for (method in methods) { + recursiveSearch(method.findSuperMethods()) + } + for (method in methods) { + for (tag in method.docComment?.tags.orEmpty()) { + if (tag.name in tagsToInherit) { + output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method) + } + } + } + } + + recursiveSearch(arrayOf(this)) + return output.mapValues { it.value.values } + } + + + private fun PsiDocTag.contentElements(): Iterable<PsiElement> { + val tagValueElements = children + .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } + .dropWhile { it is PsiWhiteSpace } + .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } + return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements + } + + private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement) { + val doc = Jsoup.parse(expandAllForElements(elements, element)) + doc.body().childNodes().forEach { + convertHtmlNode(it)?.let { append(it) } + } + doc.head().childNodes().forEach { + convertHtmlNode(it)?.let { append(it) } + } + } + + private fun ContentBlock.convertJavadocElementsToAttrDesc(elements: Iterable<PsiElement>, element: PsiNamedElement) { + val doc = Jsoup.parse(expandAllForElements(elements, element)) + doc.body().childNodes().forEach { + convertHtmlNode(it)?.let { + var content = it + if (content is ContentText) { + var description = content.text + val matcher = TEXT.matcher(content.text) + if (matcher.matches()) { + val command = matcher.group(1) + if (command == "description") { + description = matcher.group(2) + content = ContentText(description) + } + } + } + append(content) + } + } + } + + private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String { + val htmlBuilder = StringBuilder() + elements.forEach { + if (it is PsiInlineDocTag) { + htmlBuilder.append(convertInlineDocTag(it, element)) + } else { + htmlBuilder.append(it.text) + } + } + return htmlBuilder.toString().trim() + } + + private fun convertHtmlNode(node: Node, isBlockCode: Boolean = false): ContentNode? { + if (isBlockCode) { + return if (node is TextNode) { // Fixes b/129762453 + val codeNode = CodeNode(node.wholeText, "") + ContentText(codeNode.text().removePrefix("#")) + } else { // Fixes b/129857975 + ContentText(node.toString()) + } + } + if (node is TextNode) { + return ContentText(node.text().removePrefix("#")) + } else if (node is Element) { + val childBlock = createBlock(node) + node.childNodes().forEach { + val child = convertHtmlNode(it, isBlockCode = childBlock is ContentBlockCode) + if (child != null) { + childBlock.append(child) + } + } + return (childBlock) + } + return null + } + + private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { + "p" -> ContentParagraph() + "b", "strong" -> ContentStrong() + "i", "em" -> ContentEmphasis() + "s", "del" -> ContentStrikethrough() + "code" -> ContentCode() + "pre" -> ContentBlockCode() + "ul" -> ContentUnorderedList() + "ol" -> ContentOrderedList() + "li" -> ContentListItem() + "a" -> createLink(element) + "br" -> ContentBlock().apply { hardLineBreak() } + + "dl" -> ContentDescriptionList() + "dt" -> ContentDescriptionTerm() + "dd" -> ContentDescriptionDefinition() + + "table" -> ContentTable() + "tbody" -> ContentTableBody() + "tr" -> ContentTableRow() + "th" -> { + val colspan = element.attr("colspan") + val rowspan = element.attr("rowspan") + ContentTableHeader(colspan, rowspan) + } + "td" -> { + val colspan = element.attr("colspan") + val rowspan = element.attr("rowspan") + ContentTableCell(colspan, rowspan) + } + + "h1" -> ContentHeading(1) + "h2" -> ContentHeading(2) + "h3" -> ContentHeading(3) + "h4" -> ContentHeading(4) + "h5" -> ContentHeading(5) + "h6" -> ContentHeading(6) + + "div" -> { + val divClass = element.attr("class") + if (divClass == "special reference" || divClass == "note") ContentSpecialReference() + else ContentParagraph() + } + + "script" -> { + + // If the `type` attr is an empty string, we want to use null instead so that the resulting generated + // Javascript does not contain a `type` attr. + // + // Example: + // type == "" => <script type="" src="..."> + // type == null => <script src="..."> + val type = if (element.attr("type").isNotEmpty()) { + element.attr("type") + } else { + null + } + ScriptBlock(type, element.attr("src")) + } + + else -> ContentBlock() + } + + private fun createLink(element: Element): ContentBlock { + return when { + element.hasAttr("docref") -> { + val docref = element.attr("docref") + ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) + } + element.hasAttr("href") -> { + val href = element.attr("href") + + val uri = try { + URI(href) + } catch (_: Exception) { + null + } + + if (uri?.isAbsolute == false) { + ContentLocalLink(href) + } else { + ContentExternalLink(href) + } + } + element.hasAttr("name") -> { + ContentBookmark(element.attr("name")) + } + else -> ContentBlock() + } + } + + private fun MutableContent.convertSeeTag(tag: PsiDocTag) { + val linkElement = tag.linkElement() ?: return + val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) + + val valueElement = tag.referenceElement() + val externalLink = resolveExternalLink(valueElement) + val text = ContentText(linkElement.text) + + val linkSignature by lazy { resolveInternalLink(valueElement) } + val node = when { + externalLink != null -> { + val linkNode = ContentExternalLink(externalLink) + linkNode.append(text) + linkNode + } + linkSignature != null -> { + @Suppress("USELESS_CAST") + val signature: String = linkSignature as String + val linkNode = + ContentNodeLazyLink( + (tag.valueElement ?: linkElement).text + ) { refGraph.lookupOrWarn(signature, logger) } + linkNode.append(text) + linkNode + } + else -> text + } + seeSection.append(node) + } + + private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { + "link", "linkplain" -> { + val valueElement = tag.referenceElement() + val externalLink = resolveExternalLink(valueElement) + val linkSignature by lazy { resolveInternalLink(valueElement) } + if (externalLink != null || linkSignature != null) { + + // sometimes `dataElements` contains multiple `PsiDocToken` elements and some have whitespace in them + // this is best effort to find the first non-empty one before falling back to using the symbol name. + val labelText = tag.dataElements.firstOrNull { + it is PsiDocToken && it.text?.trim()?.isNotEmpty() ?: false + }?.text ?: valueElement!!.text + + val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\"" + val link = "<a $linkTarget>$labelText</a>" + if (tag.name == "link") "<code>$link</code>" else link + } else if (valueElement != null) { + valueElement.text + } else { + "" + } + } + "code", "literal" -> { + val text = StringBuilder() + tag.dataElements.forEach { text.append(it.text) } + val escaped = text.toString().trimStart().htmlEscape() + if (tag.name == "code") "<code>$escaped</code>" else escaped + } + "inheritDoc" -> { + val result = (element as? PsiMethod)?.let { + // @{inheritDoc} is only allowed on functions + val parent = tag.parent + when (parent) { + is PsiDocComment -> element.findSuperDocCommentOrWarn() + is PsiDocTag -> element.findSuperDocTagOrWarn(parent) + else -> null + } + } + result ?: tag.text + } + "docRoot" -> { + // TODO: fix that + "https://developer.android.com/" + } + "sample" -> { + tag.text?.let { tagText -> + val (absolutePath, delimiter) = getSampleAnnotationInformation(tagText) + val code = retrieveCodeInFile(absolutePath, delimiter) + return if (code != null && code.isNotEmpty()) { + "<pre is-upgraded>$code</pre>" + } else { + "" + } + } + } + + // Loads MathJax script from local source, which then updates MathJax HTML code + "usesMathJax" -> { + "<script src=\"/_static/js/managed/mathjax/MathJax.js?config=TeX-AMS_SVG\"></script>" + } + + else -> tag.text + } + + private fun PsiDocTag.referenceElement(): PsiElement? = + linkElement()?.let { + if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { + PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) + } else { + it + } + } + + private fun PsiDocTag.linkElement(): PsiElement? = + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } + + private fun resolveExternalLink(valueElement: PsiElement?): String? { + val target = valueElement?.reference?.resolve() + if (target != null) { + return externalDocumentationLinkResolver.buildExternalDocumentationLink(target) + } + return null + } + + private fun resolveInternalLink(valueElement: PsiElement?): String? { + val target = valueElement?.reference?.resolve() + if (target != null) { + return signatureProvider.signature(target) + } + return null + } + + fun PsiDocTag.getSubjectName(): String? { + if (name == "param" || name == "throws" || name == "exception") { + return valueElement?.text + } + return null + } + + private fun PsiMethod.findSuperDocCommentOrWarn(): String { + val method = findFirstSuperMethodWithDocumentation(this) + if (method != null) { + val descriptionElements = method.docComment?.descriptionElements?.dropWhile { + it.text.trim().isEmpty() + } ?: return "" + + return expandAllForElements(descriptionElements, method) + } + logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") + return "" + } + + + private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String { + val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this) + + if (result != null) { + val (method, tag) = result + + val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() } + + val expandedString = expandAllForElements(contentElements, method) + + return expandedString + } + logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") + return "" + } + + private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? { + val superMethods = current.findSuperMethods() + for (method in superMethods) { + val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() } + if (docs?.isNotEmpty() == true) { + return method + } + } + for (method in superMethods) { + val result = findFirstSuperMethodWithDocumentation(method) + if (result != null) { + return result + } + } + + return null + } + + private fun findFirstSuperMethodWithDocumentationforTag( + elementToExpand: PsiDocTag, + current: PsiMethod + ): Pair<PsiMethod, PsiDocTag>? { + val superMethods = current.findSuperMethods() + val mappedFilteredTags = superMethods.map { + it to it.docComment?.tags?.filter { it.name == elementToExpand.name } + } + + for ((method, tags) in mappedFilteredTags) { + tags ?: continue + for (tag in tags) { + val (tagSubject, elementSubject) = when (tag.name) { + "throws" -> { + // match class names only for throws, ignore possibly fully qualified path + // TODO: Always match exactly here + tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last() + } + else -> { + tag.getSubjectName() to elementToExpand.getSubjectName() + } + } + + if (tagSubject == elementSubject) { + return method to tag + } + } + } + + for (method in superMethods) { + val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method) + if (result != null) { + return result + } + } + return null + } + + /** + * Returns information inside @sample + * + * Component1 is the absolute path to the file + * Component2 is the delimiter if exists in the file + */ + private fun getSampleAnnotationInformation(tagText: String): Pair<String, String> { + val pathContent = tagText + .trim { it == '{' || it == '}' } + .removePrefix("@sample ") + + val formattedPath = pathContent.substringBefore(" ").trim() + val potentialDelimiter = pathContent.substringAfterLast(" ").trim() + + val delimiter = if (potentialDelimiter == formattedPath) "" else potentialDelimiter + val path = "samples/$formattedPath" + + return Pair(path, delimiter) + } + + /** + * Retrieves the code inside a file. + * + * If betweenTag is not empty, it retrieves the code between + * BEGIN_INCLUDE($betweenTag) and END_INCLUDE($betweenTag) comments. + * + * Also, the method will trim every line with the number of spaces in the first line + */ + private fun retrieveCodeInFile(path: String, betweenTag: String = "") = StringBuilder().apply { + try { + if (betweenTag.isEmpty()) { + appendContent(path) + } else { + appendContentBetweenIncludes(path, betweenTag) + } + } catch (e: java.lang.Exception) { + logger.error("No file found when processing Java @sample. Path to sample: $path\n") + } + } + + private fun StringBuilder.appendContent(path: String) { + val spaces = InitialSpaceIndent() + File(path).forEachLine { + appendWithoutInitialIndent(it, spaces) + } + } + + private fun StringBuilder.appendContentBetweenIncludes(path: String, includeTag: String) { + var shouldAppend = false + val beginning = "BEGIN_INCLUDE($includeTag)" + val end = "END_INCLUDE($includeTag)" + val spaces = InitialSpaceIndent() + File(path).forEachLine { + if (shouldAppend) { + if (it.contains(end)) { + shouldAppend = false + } else { + appendWithoutInitialIndent(it, spaces) + } + } else { + if (it.contains(beginning)) shouldAppend = true + } + } + } + + private fun StringBuilder.appendWithoutInitialIndent(it: String, spaces: InitialSpaceIndent) { + if (spaces.value == -1) { + spaces.value = (it.length - it.trimStart().length).coerceAtLeast(0) + appendln(it) + } else { + appendln(if (it.isBlank()) it else it.substring(spaces.value, it.length)) + } + } + + private data class InitialSpaceIndent(var value: Int = -1) +} diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt new file mode 100644 index 000000000..c60625a4a --- /dev/null +++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt @@ -0,0 +1,188 @@ +package org.jetbrains.dokka + +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.intellij.markdown.html.entities.EntityConverter +import org.intellij.markdown.parser.LinkMap +import java.util.* + +class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) { + fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel) + fun resolve(href: String): ContentBlock = contentFactory(href) +} + +fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent { + val result = MutableContent() + if (inline) { + buildInlineContentTo(tree, result, linkResolver) + } else { + buildContentTo(tree, result, linkResolver) + } + return result +} + +fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { +// println(tree.toTestString()) + val nodeStack = ArrayDeque<ContentBlock>() + nodeStack.push(target) + + tree.visit { node, processChildren -> + val parent = nodeStack.peek() + + fun appendNodeWithChildren(content: ContentBlock) { + nodeStack.push(content) + processChildren() + parent.append(nodeStack.pop()) + } + + when (node.type) { + MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1)) + MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2)) + MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3)) + MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4)) + MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5)) + MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6)) + MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList()) + MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList()) + MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem()) + MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis()) + MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong()) + MarkdownElementTypes.CODE_SPAN -> { + val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text + if (startDelimiter != null) { + val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) + val codeSpan = ContentCode().apply { append(ContentText(text)) } + parent.append(codeSpan) + } + } + MarkdownElementTypes.CODE_BLOCK, + MarkdownElementTypes.CODE_FENCE -> { + val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" + appendNodeWithChildren(ContentBlockCode(language)) + } + MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph()) + + MarkdownElementTypes.INLINE_LINK -> { + val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) + val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) + if (linkTextNode != null) { + if (destination != null) { + val link = ContentExternalLink(destination.text) + renderLinkTextTo(linkTextNode, link, linkResolver) + parent.append(link) + } else { + val link = ContentExternalLink(linkTextNode.getLabelText()) + renderLinkTextTo(linkTextNode, link, linkResolver) + parent.append(link) + } + } + } + MarkdownElementTypes.SHORT_REFERENCE_LINK, + MarkdownElementTypes.FULL_REFERENCE_LINK -> { + val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) + if (labelElement != null) { + val linkInfo = linkResolver.getLinkInfo(labelElement.text) + val labelText = labelElement.getLabelText() + val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText) + val linkText = node.child(MarkdownElementTypes.LINK_TEXT) + if (linkText != null) { + renderLinkTextTo(linkText, link, linkResolver) + } else { + link.append(ContentText(labelText)) + } + parent.append(link) + } + } + MarkdownTokenTypes.WHITE_SPACE -> { + // Don't append first space if start of header (it is added during formatting later) + // v + // #### Some Heading + if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { + parent.append(ContentText(node.text)) + } + } + MarkdownTokenTypes.EOL -> { + if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || + // Keep extra blank lines when processing lists (affects Markdown formatting) + (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { + parent.append(ContentText(node.text)) + } + } + + MarkdownTokenTypes.CODE_LINE -> { + val content = ContentText(node.text) + if (parent is ContentBlockCode) { + parent.append(content) + } else { + parent.append(ContentBlockCode().apply { append(content) }) + } + } + + MarkdownTokenTypes.TEXT -> { + fun createEntityOrText(text: String): ContentNode { + if (text == "&" || text == """ || text == "<" || text == ">") { + return ContentEntity(text) + } + if (text == "&") { + return ContentEntity("&") + } + val decodedText = EntityConverter.replaceEntities(text, true, true) + if (decodedText != text) { + return ContentEntity(text) + } + return ContentText(text) + } + + parent.append(createEntityOrText(node.text)) + } + + MarkdownTokenTypes.EMPH -> { + val parentNodeType = node.parent?.type + if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { + parent.append(ContentText(node.text)) + } + } + + MarkdownTokenTypes.COLON, + MarkdownTokenTypes.SINGLE_QUOTE, + MarkdownTokenTypes.DOUBLE_QUOTE, + MarkdownTokenTypes.LT, + MarkdownTokenTypes.GT, + MarkdownTokenTypes.LPAREN, + MarkdownTokenTypes.RPAREN, + MarkdownTokenTypes.LBRACKET, + MarkdownTokenTypes.RBRACKET, + MarkdownTokenTypes.EXCLAMATION_MARK, + MarkdownTokenTypes.BACKTICK, + MarkdownTokenTypes.CODE_FENCE_CONTENT -> { + parent.append(ContentText(node.text)) + } + + MarkdownElementTypes.LINK_DEFINITION -> { + } + + else -> { + processChildren() + } + } + } +} + +private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text } + +private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode +private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList + +fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { + val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) + inlineContent.forEach { + buildContentTo(it, target, linkResolver) + } +} + +fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { + val linkTextNodes = tree.children.drop(1).dropLast(1) + linkTextNodes.forEach { + buildContentTo(it, target, linkResolver) + } +} diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt new file mode 100644 index 000000000..d73bef4a5 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt @@ -0,0 +1,72 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink + +class DeclarationLinkResolver + @Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val refGraph: NodeReferenceGraph, + val logger: DokkaLogger, + val options: DocumentationOptions, + val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, + val elementSignatureProvider: ElementSignatureProvider) { + + + fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { + val symbol = try { + val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, + resolutionFacade, fromDescriptor, null, href.split('.').toList()) + findTargetSymbol(symbols) + } catch(e: Exception) { + null + } + + // don't include unresolved links in generated doc + // assume that if an href doesn't contain '/', it's not an attempt to reference an external file + if (symbol != null) { + val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol) + if (externalHref != null) { + return ContentExternalLink(externalHref) + } + val signature = elementSignatureProvider.signature(symbol) + val referencedAt = fromDescriptor.signatureWithSourceLocation() + + return ContentNodeLazyLink(href, { -> + val target = refGraph.lookup(signature) + + if (target == null) { + logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt") + } + target + }) + } + if ("/" in href) { + return ContentExternalLink(href) + } + return null + } + + fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) = + tryResolveContentLink(fromDescriptor, href) ?: run { + logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") + ContentExternalLink("#") + } + + fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { + if (symbols.isEmpty()) { + return null + } + val symbol = symbols.first() + if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + return symbol.overriddenDescriptors.firstOrNull() + } + if (symbol is TypeAliasDescriptor && !symbol.isDocumented(options)) { + return symbol.classDescriptor + } + return symbol + } + +} diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt new file mode 100644 index 000000000..098a17f97 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt @@ -0,0 +1,339 @@ +package org.jetbrains.dokka.Kotlin + +import com.google.inject.Inject +import com.intellij.psi.PsiDocCommentOwner +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.util.PsiTreeUtil +import org.intellij.markdown.parser.LinkMap +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Samples.SampleProcessingService +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.kdoc.findKDoc +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.api.KDoc +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.annotations.argumentValue +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import java.util.regex.Pattern + +private val REF_COMMAND = "ref" +private val NAME_COMMAND = "name" +private val DESCRIPTION_COMMAND = "description" +private val TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL) +private val NAME_TEXT = Pattern.compile("(\\S+)(.*)", Pattern.DOTALL) + +class DescriptorDocumentationParser @Inject constructor( + val options: DocumentationOptions, + val logger: DokkaLogger, + val linkResolver: DeclarationLinkResolver, + val resolutionFacade: DokkaResolutionFacade, + val refGraph: NodeReferenceGraph, + val sampleService: SampleProcessingService, + val signatureProvider: KotlinElementSignatureProvider, + val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver +) { + fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content = + parseDocumentationAndDetails(descriptor, inline).first + + fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> { + if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor || + descriptor is EnumEntrySyntheticClassDescriptor) { + return parseJavadoc(descriptor) + } + + val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor) + if (kdoc == null) { + if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() && + descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor && + descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) { + logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}") + } + return Content.Empty to { node -> } + } + + val contextDescriptor = + (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration) + ?.takeIf { it != descriptor.original.sourcePsi() } + ?.resolveToDescriptorIfAny() + ?: descriptor + + // This will build the initial node for all content above the tags, however we also sometimes have @Sample + // tags between content, so we handle that case below + var kdocText = kdoc.getContent() + // workaround for code fence parsing problem in IJ markdown parser + if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) { + kdocText += "\n" + } + val tree = parseMarkdown(kdocText) + val linkMap = LinkMap.buildLinkMap(tree.node, kdocText) + val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }), inline) + if (kdoc is KDocSection) { + val tags = kdoc.getTags() + tags.forEach { + when (it.knownTag) { + KDocKnownTag.SAMPLE -> { + content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it)) + // If the sample tag has text below it, it will be considered as the child of the tag, so add it + val tagSubContent = it.getContent() + if (tagSubContent.isNotBlank()) { + val markdownNode = parseMarkdown(tagSubContent) + buildInlineContentTo(markdownNode, content, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) })) + } + } + KDocKnownTag.SEE -> + content.addTagToSeeAlso(contextDescriptor, it) + KDocKnownTag.PARAM -> { + val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) + section.append(ParameterInfoNode { + val signature = signatureProvider.signature(descriptor) + refGraph.lookupOrWarn(signature, logger)?.details?.find { node -> + node.kind == NodeKind.Parameter && node.name == it.getSubjectName() + } + }) + val sectionContent = it.getContent() + val markdownNode = parseMarkdown(sectionContent) + buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) })) + } + else -> { + val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) + val sectionContent = it.getContent() + val markdownNode = parseMarkdown(sectionContent) + buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) })) + } + } + } + } + return content to { node -> + if (kdoc is KDocSection) { + val tags = kdoc.getTags() + node.addExtraTags(tags, descriptor) + } + } + } + + /** + * Adds @attr tag. There are 3 types of syntax for this: + * *@attr ref <android.>R.styleable.<attribute_name> + * *@attr name <attribute_name> + * *@attr description <attribute_description> + * This also adds the @since and @apiSince tags. + */ + private fun DocumentationNode.addExtraTags(tags: Array<KDocTag>, descriptor: DeclarationDescriptor) { + tags.forEach { + val name = it.name + if (name?.toLowerCase() == "attr") { + it.getAttr(descriptor)?.let { append(it, RefKind.Detail) } + } else if (name?.toLowerCase() == "since" || name?.toLowerCase() == "apisince") { + val apiLevel = DocumentationNode(it.getContent(), Content.Empty, NodeKind.ApiLevel) + append(apiLevel, RefKind.Detail) + } else if (name?.toLowerCase() == "sdkextsince") { + val sdkExtSince = DocumentationNode(it.getContent(), Content.Empty, NodeKind.SdkExtSince) + append(sdkExtSince, RefKind.Detail) + } else if (name?.toLowerCase() == "deprecatedsince") { + val deprecatedLevel = DocumentationNode(it.getContent(), Content.Empty, NodeKind.DeprecatedLevel) + append(deprecatedLevel, RefKind.Detail) + } else if (name?.toLowerCase() == "artifactid") { + val artifactId = DocumentationNode(it.getContent(), Content.Empty, NodeKind.ArtifactId) + append(artifactId, RefKind.Detail) + } + } + } + + private fun DeclarationDescriptor.isSuppressWarning(): Boolean { + val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!)) + return if (suppressAnnotation != null) { + @Suppress("UNCHECKED_CAST") + (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" } + } else containingDeclaration?.isSuppressWarning() ?: false + } + + /** + * Special case for generating stdlib documentation (the Any class to which the override chain will resolve + * is not the same one as the Any class included in the source scope). + */ + fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? { + if (descriptor !is CallableMemberDescriptor) { + return null + } + val name = descriptor.name.asString() + if (name == "equals" || name == "hashCode" || name == "toString") { + var deepestDescriptor: CallableMemberDescriptor = descriptor + while (!deepestDescriptor.overriddenDescriptors.isEmpty()) { + deepestDescriptor = deepestDescriptor.overriddenDescriptors.first() + } + if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") { + val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors( + FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE) + anyClassDescriptors.forEach { + val anyMethod = (it as ClassDescriptor).getMemberScope(listOf()) + .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS, { it == descriptor.name }) + .single() + val kdoc = anyMethod.findKDoc() + if (kdoc != null) { + return kdoc + } + } + } + } + return null + } + + fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> { + val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi + if (psi is PsiDocCommentOwner) { + val parseResult = JavadocParser( + refGraph, + logger, + signatureProvider, + externalDocumentationLinkResolver + ).parseDocumentation(psi as PsiNamedElement) + return parseResult.content to { node -> + parseResult.deprecatedContent?.let { + val deprecationNode = DocumentationNode("", it, NodeKind.Modifier) + node.append(deprecationNode, RefKind.Deprecation) + } + if (node.kind in NodeKind.classLike) { + parseResult.attributeRefs.forEach { + val signature = node.detailOrNull(NodeKind.Signature) + val signatureName = signature?.name + val classAttrSignature = "${signatureName}:$it" + refGraph.register(classAttrSignature, DocumentationNode(node.name, Content.Empty, NodeKind.Attribute)) + refGraph.link(node, classAttrSignature, RefKind.Detail) + refGraph.link(classAttrSignature, node, RefKind.Owner) + refGraph.link(classAttrSignature, it, RefKind.AttributeRef) + } + } else if (node.kind in NodeKind.memberLike) { + parseResult.attributeRefs.forEach { + refGraph.link(node, it, RefKind.HiddenLink) + } + } + parseResult.apiLevel?.let { + node.append(it, RefKind.Detail) + } + parseResult.sdkExtSince?.let { + node.append(it, RefKind.Detail) + } + parseResult.deprecatedLevel?.let { + node.append(it, RefKind.Detail) + } + parseResult.artifactId?.let { + node.append(it, RefKind.Detail) + } + parseResult.attribute?.let { + val signature = node.detailOrNull(NodeKind.Signature) + val signatureName = signature?.name + val attrSignature = "AttrMain:$signatureName" + refGraph.register(attrSignature, it) + refGraph.link(attrSignature, node, RefKind.AttributeSource) + } + } + } + return Content.Empty to { _ -> } + } + + fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) + ?: arrayOf() + + private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) { + addTagToSection(seeTag, descriptor, "See Also") + } + + private fun MutableContent.addTagToSection(seeTag: KDocTag, descriptor: DeclarationDescriptor, sectionName: String) { + val subjectName = seeTag.getSubjectName() + if (subjectName != null) { + val section = findSectionByTag(sectionName) ?: addSection(sectionName, null) + val link = linkResolver.resolveContentLink(descriptor, subjectName) + link.append(ContentText(subjectName)) + val para = ContentParagraph() + para.append(link) + section.append(para) + } + } + + private fun KDocTag.getAttr(descriptor: DeclarationDescriptor): DocumentationNode? { + var attribute: DocumentationNode? = null + val matcher = TEXT.matcher(getContent()) + if (matcher.matches()) { + val command = matcher.group(1) + val more = matcher.group(2) + attribute = when (command) { + REF_COMMAND -> { + val attrRef = more.trim() + val qualified = attrRef.split('.', '#') + val targetDescriptor = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, resolutionFacade, descriptor, this, qualified) + DocumentationNode(attrRef, Content.Empty, NodeKind.Attribute).also { + if (targetDescriptor.isNotEmpty()) { + refGraph.link(it, targetDescriptor.first().signature(), RefKind.Detail) + } + } + } + NAME_COMMAND -> { + val nameMatcher = NAME_TEXT.matcher(more) + if (nameMatcher.matches()) { + val attrName = nameMatcher.group(1) + DocumentationNode(attrName, Content.Empty, NodeKind.Attribute) + } else { + null + } + } + DESCRIPTION_COMMAND -> { + val attrDescription = more + DocumentationNode(attrDescription, Content.Empty, NodeKind.Attribute) + } + else -> null + } + } + return attribute + } + +} + +/** + * Lazily executed wrapper node holding a [NodeKind.Parameter] node that will be used to add type + * and default value information to + * [org.jetbrains.dokka.Formats.DevsiteLayoutHtmlFormatOutputBuilder]. + * + * We make this a [ContentBlock] instead of a [ContentNode] so we won't fallback to calling + * [toString] on this and trying to add it to documentation somewhere - returning an empty list + * should make this a no-op. + * + * @property wrappedNode lazily executable lambda that will return the matching documentation node + * for this parameter (if it exists) + */ +class ParameterInfoNode(private val wrappedNode: () -> DocumentationNode?) : ContentBlock() { + private var computed = false + + val parameterContent: NodeRenderContent? + get() = lazyNode + + private var lazyNode: NodeRenderContent? = null + get() { + if (!computed) { + computed = true + + val node = wrappedNode() + if (node != null) { + field = NodeRenderContent(node, LanguageService.RenderMode.SUMMARY) + } + } + return field + } + + override val children = arrayListOf<ContentNode>() +} diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt new file mode 100644 index 000000000..b9fe8483e --- /dev/null +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -0,0 +1,1177 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiField +import com.intellij.psi.PsiJavaFile +import org.jetbrains.dokka.DokkaConfiguration.* +import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.kotlin.idea.kdoc.findKDoc +import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType +import org.jetbrains.kotlin.idea.util.makeNotNullable +import org.jetbrains.kotlin.idea.util.toFuzzyType +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.supertypes +import org.jetbrains.kotlin.util.supertypesWithAny +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import com.google.inject.name.Named as GuiceNamed + +class DocumentationOptions(val outputDir: String, + val outputFormat: String, + includeNonPublic: Boolean = false, + val includeRootPackage: Boolean = false, + reportUndocumented: Boolean = true, + val skipEmptyPackages: Boolean = true, + skipDeprecated: Boolean = false, + jdkVersion: Int = 6, + val generateClassIndexPage: Boolean = true, + val generatePackageIndexPage: Boolean = true, + val sourceLinks: List<SourceLinkDefinition> = emptyList(), + val impliedPlatforms: List<String> = emptyList(), + // Sorted by pattern length + perPackageOptions: List<PackageOptions> = emptyList(), + externalDocumentationLinks: List<ExternalDocumentationLink> = emptyList(), + noStdlibLink: Boolean, + noJdkLink: Boolean = false, + val languageVersion: String?, + val apiVersion: String?, + cacheRoot: String? = null, + val suppressedFiles: Set<File> = emptySet(), + val collectInheritedExtensionsFromLibraries: Boolean = false, + val outlineRoot: String = "", + val dacRoot: String = "") { + init { + if (perPackageOptions.any { it.prefix == "" }) + throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead") + } + + val perPackageOptions = perPackageOptions.sortedByDescending { it.prefix.length } + val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated) + + fun effectivePackageOptions(pack: String): PackageOptions = perPackageOptions.firstOrNull { pack == it.prefix || pack.startsWith(it.prefix + ".") } ?: rootPackageOptions + fun effectivePackageOptions(pack: FqName): PackageOptions = effectivePackageOptions(pack.asString()) + + val defaultLinks = run { + val links = mutableListOf<ExternalDocumentationLink>() + //links += ExternalDocumentationLink.Builder("https://developer.android.com/reference/").build() + if (!noJdkLink) + links += ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build() + + if (!noStdlibLink) + links += ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build() + links + } + + val externalDocumentationLinks = defaultLinks + externalDocumentationLinks + + val cacheRoot: Path? = when { + cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka") + cacheRoot != null -> Paths.get(cacheRoot) + else -> null + } +} + +private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor, + extensionReceiverDescriptor: DeclarationDescriptor, + allFqNames: Collection<FqName>): Boolean { + val extensionFunctionPackage = DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java) + val extensionReceiverPackage = DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java) + return extensionFunctionPackage != null && extensionReceiverPackage != null && + extensionFunctionPackage.fqName != extensionReceiverPackage.fqName && + extensionReceiverPackage.fqName !in allFqNames +} + +interface PackageDocumentationBuilder { + fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + allFqNames: Collection<FqName>) +} + +interface DefaultPlatformsProvider { + fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> +} + +val ignoredSupertypes = setOf( + "kotlin.Annotation", "kotlin.Enum", "kotlin.Any" +) + +class DocumentationBuilder +@Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val descriptorDocumentationParser: DescriptorDocumentationParser, + val options: DocumentationOptions, + val refGraph: NodeReferenceGraph, + val platformNodeRegistry: PlatformNodeRegistry, + val logger: DokkaLogger, + val linkResolver: DeclarationLinkResolver, + val defaultPlatformsProvider: DefaultPlatformsProvider) { + val boringBuiltinClasses = setOf( + "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", + "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") + val knownModifiers = setOf( + KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD, + KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD, + KtTokens.OVERRIDE_KEYWORD) + + fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) { + refGraph.link(node, descriptor.signature(), kind) + } + + fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: RefKind) { + if (fromDescriptor != null && toDescriptor != null) { + refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind) + } + } + + fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) { + refGraph.register(descriptor.signature(), node) + } + + fun <T> nodeForDescriptor( + descriptor: T, + kind: NodeKind, + external: Boolean = false + ): DocumentationNode where T : DeclarationDescriptor, T : Named { + val (doc, callback) = + if (external) { + Content.Empty to { node -> } + } else { + descriptorDocumentationParser.parseDocumentationAndDetails( + descriptor, + kind == NodeKind.Parameter + ) + } + val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor) + node.appendSignature(descriptor) + callback(node) + return node + } + + private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode { + if (descriptor is MemberDescriptor) { + appendVisibility(descriptor) + if (descriptor !is ConstructorDescriptor) { + appendModality(descriptor) + } + } + return this + } + + fun DocumentationNode.appendModality(descriptor: MemberDescriptor) { + var modality = descriptor.modality + if (modality == Modality.OPEN) { + val containingClass = descriptor.containingDeclaration as? ClassDescriptor + if (containingClass?.modality == Modality.FINAL) { + modality = Modality.FINAL + } + } + val modifier = modality.name.toLowerCase() + appendTextNode(modifier, NodeKind.Modifier) + } + + fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) { + val modifier = descriptor.visibility.normalize().displayName + appendTextNode(modifier, NodeKind.Modifier) + } + + fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) { + val unwrappedType = superType.unwrap() + if (unwrappedType is AbbreviatedType) { + appendSupertype(descriptor, unwrappedType.abbreviation, backref) + } else { + appendType(unwrappedType, NodeKind.Supertype) + val superclass = unwrappedType.constructor.declarationDescriptor + if (backref) { + link(superclass, descriptor, RefKind.Inheritor) + } + link(descriptor, superclass, RefKind.Superclass) + } + } + + fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) { + if (projection.isStarProjection) { + appendTextNode("*", NodeKind.Type) + } else { + appendType(projection.type, kind, projection.projectionKind.label) + } + } + + fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") { + if (kotlinType == null) + return + (kotlinType.unwrap() as? AbbreviatedType)?.let { + return appendType(it.abbreviation) + } + + if (kotlinType.isDynamic()) { + append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail) + return + } + + val classifierDescriptor = kotlinType.constructor.declarationDescriptor + val name = when (classifierDescriptor) { + is ClassDescriptor -> { + if (classifierDescriptor.isCompanionObject) { + classifierDescriptor.containingDeclaration.name.asString() + + "." + classifierDescriptor.name.asString() + } else { + classifierDescriptor.name.asString() + } + } + is Named -> classifierDescriptor.name.asString() + else -> "<anonymous>" + } + val node = DocumentationNode(name, Content.Empty, kind) + if (prefix != "") { + node.appendTextNode(prefix, NodeKind.Modifier) + } + if (kotlinType.isNullabilityFlexible()) { + node.appendTextNode("!", NodeKind.NullabilityModifier) + } else if (kotlinType.isMarkedNullable) { + node.appendTextNode("?", NodeKind.NullabilityModifier) + } + if (classifierDescriptor != null) { + val externalLink = + linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor) + if (externalLink != null) { + if (classifierDescriptor !is TypeParameterDescriptor) { + val targetNode = + refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true) + node.append(targetNode, RefKind.ExternalType) + node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) + } + } + link( + node, classifierDescriptor, + if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link + ) + if (classifierDescriptor !is TypeParameterDescriptor) { + node.append( + DocumentationNode( + classifierDescriptor.fqNameUnsafe.asString(), + Content.Empty, + NodeKind.QualifiedName + ), RefKind.Detail + ) + } + } + + + append(node, RefKind.Detail) + node.appendAnnotations(kotlinType) + for (typeArgument in kotlinType.arguments) { + node.appendProjection(typeArgument) + } + } + + fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean = + DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses + + fun DocumentationNode.appendAnnotations(annotated: Annotated) { + annotated.annotations.forEach { + it.build()?.let { annotationNode -> + if (annotationNode.isSinceKotlin()) { + appendSinceKotlin(annotationNode) + } + else { + val refKind = when { + it.isDocumented() -> + when { + annotationNode.isDeprecation() -> RefKind.Deprecation + else -> RefKind.Annotation + } + it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation + else -> return@forEach + } + append(annotationNode, refKind) + if (refKind == RefKind.Deprecation) annotationNode.convertDeprecationDetailsToChildren() + } + } + } + } + + fun DocumentationNode.appendExternalLink(externalLink: String) { + append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) + } + + fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) { + val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor) + if (target != null) { + appendExternalLink(target) + } + } + + fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) { + val kotlinVersion = annotation + .detail(NodeKind.Parameter) + .detail(NodeKind.Value) + .name.removeSurrounding("\"") + + append(platformNodeRegistry["Kotlin " + kotlinVersion], RefKind.Platform) + } + + fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { + val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return + KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach { + if (psi.hasModifier(it)) { + appendTextNode(it.value, NodeKind.Modifier) + } + } + } + + fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + + fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" + + fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation + + fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { + appendSourceLink(sourceElement.getPsi(), options.sourceLinks) + } + + fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) { + appendTextNode(descriptor.signature(), NodeKind.Signature, RefKind.Detail) + } + + fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? { + if (!descriptor.isGenerated() && descriptor.isDocumented(options)) { + val node = descriptor.build() + append(node, kind) + return node + } + return null + } + + fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?: + DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply { + appendTextNode(signature, NodeKind.Signature, RefKind.Detail) + }) + .also { groupNode -> + nodes.forEach { node -> + if (node != groupNode) { + node.owner?.let { owner -> + node.dropReferences { it.to == owner && it.kind == RefKind.Owner } + owner.dropReferences { it.to == node && it.kind == RefKind.Member } + owner.append(groupNode, RefKind.Member) + } + groupNode.append(node, RefKind.Member) + } + } + } + + + fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { + if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return + + val existingNode = refGraph.lookup(descriptor.signature()) + if (existingNode != null) { + if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor + || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) { + val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build())) + register(descriptor, node) + return + } + + existingNode.updatePlatforms(descriptor) + + if (descriptor is ClassDescriptor) { + val membersToDocument = descriptor.collectMembersToDocument() + for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) { + if (memberDescriptor is ClassDescriptor) { + existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes + } + else { + val existingMemberNode = refGraph.lookup(memberDescriptor.signature()) + if (existingMemberNode != null) { + existingMemberNode.updatePlatforms(memberDescriptor) + } + else { + existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier) + } + } + } + } + } + else { + appendChild(descriptor, RefKind.Member) + } + } + + private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + + fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor, + inheritedLinkKind: RefKind = RefKind.InheritedMember, + extraModifier: String?) { + if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() + if (baseDescriptor != null) { + link(this, baseDescriptor, inheritedLinkKind) + } + } else { + val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original + val child = appendChild(descriptorToUse, RefKind.Member) + if (extraModifier != null) { + child?.appendTextNode("static", NodeKind.Modifier) + } + } + } + + fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { + descriptors.forEach { descriptor -> + val node = appendChild(descriptor, kind) + node?.addReferenceTo(this, RefKind.TopLevelPage) + } + } + + fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>, + packageContent: Map<String, Content>, + packageDocumentationBuilder: PackageDocumentationBuilder) { + val allFqNames = fragments.map { it.fqName }.distinct() + + for (packageName in allFqNames) { + if (packageName.isRoot && !options.includeRootPackage) continue + val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } + + if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue + logger.info(" package $packageName: ${declarations.count()} declarations") + val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph) + packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode, + declarations, allFqNames) + } + + } + + fun propagateExtensionFunctionsToSubclasses( + fragments: Collection<PackageFragmentDescriptor>, + resolutionFacade: DokkaResolutionFacade + ) { + + val moduleDescriptor = resolutionFacade.moduleDescriptor + + // Wide-collect all view descriptors + val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages -> + packages + .flatMap { pkg -> + moduleDescriptor.getSubPackagesOf(pkg.fqName) { true } + }.map { fqName -> + moduleDescriptor.getPackage(fqName) + }.takeUnless { it.isEmpty() } + }.flatten() + + val allDescriptors = + if (options.collectInheritedExtensionsFromLibraries) { + allPackageViewDescriptors.map { it.memberScope } + } else { + fragments.asSequence().map { it.getMemberScope() } + }.flatMap { + it.getDescriptorsFiltered( + DescriptorKindFilter.CALLABLES + ).asSequence() + } + + + val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() } + val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>() + + val classHierarchy = buildClassHierarchy(documentingClasses) + + val allExtensionFunctions = + allDescriptors + .filterIsInstance<CallableMemberDescriptor>() + .filter { it.extensionReceiverParameter != null } + val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name } + + for (extensionFunction in allExtensionFunctions) { + if (extensionFunction.dispatchReceiverParameter != null) continue + val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name] + ?.filter { fn -> fn.canShadow(extensionFunction) } + ?: emptyList() + + if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue + val subclasses = + classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) } + if (subclasses.isEmpty()) continue + subclasses.values.flatten().forEach { subclass -> + if (subclass.isExtensionApplicable(extensionFunction) && + possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) { + + val hasExternalLink = + linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink( + extensionFunction + ) != null + if (hasExternalLink) { + val containerDesc = + extensionFunction.containingDeclaration as? PackageFragmentDescriptor + if (containerDesc != null) { + val container = refGraph.lookup(containerDesc.signature()) + ?: containerDesc.buildExternal() + container.append(extensionFunction.buildExternal(), RefKind.Member) + } + } + + refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension) + } + } + } + } + + private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean { + val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable() + val classType = defaultType.toFuzzyType(declaredTypeParameters) + return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null + } + + private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> { + val result = hashMapOf<ClassDescriptor, MutableList<ClassDescriptor>>() + classes.forEach { cls -> + TypeUtils.getAllSupertypes(cls.defaultType).forEach { supertype -> + val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor + if (classDescriptor != null) { + val subtypesList = result.getOrPut(classDescriptor) { arrayListOf() } + subtypesList.add(cls) + } + } + } + return result + } + + private fun CallableMemberDescriptor.canShadow(other: CallableMemberDescriptor): Boolean { + if (this == other) return false + if (this is PropertyDescriptor && other is PropertyDescriptor) { + return true + } + if (this is FunctionDescriptor && other is FunctionDescriptor) { + val parameters1 = valueParameters + val parameters2 = other.valueParameters + if (parameters1.size != parameters2.size) { + return false + } + for ((p1, p2) in parameters1 zip parameters2) { + if (p1.type != p2.type) { + return false + } + } + return true + } + return false + } + + fun DeclarationDescriptor.build(): DocumentationNode = when (this) { + is ClassifierDescriptor -> build() + is ConstructorDescriptor -> build() + is PropertyDescriptor -> build() + is FunctionDescriptor -> build() + is ValueParameterDescriptor -> build() + is ReceiverParameterDescriptor -> build() + else -> throw IllegalStateException("Descriptor $this is not known") + } + + fun PackageFragmentDescriptor.buildExternal(): DocumentationNode { + val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package) + + val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this) + if (externalLink != null) { + node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) + } + register(this, node) + return node + } + + fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) { + is FunctionDescriptor -> build(true) + is PropertyDescriptor -> build(true) + else -> throw IllegalStateException("Descriptor $this is not known") + } + + + fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) { + is ClassDescriptor -> build(external) + is TypeAliasDescriptor -> build(external) + is TypeParameterDescriptor -> build() + else -> throw IllegalStateException("Descriptor $this is not known") + } + + fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode { + val node = nodeForDescriptor(this, NodeKind.TypeAlias) + + if (!external) { + node.appendAnnotations(this) + } + node.appendModifiers(this) + node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) + + node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) + + if (!external) { + node.appendSourceLink(source) + node.appendDefaultPlatforms(this) + } + register(this, node) + return node + } + + fun ClassDescriptor.build(external: Boolean = false): DocumentationNode { + val kind = when { + kind == ClassKind.OBJECT -> NodeKind.Object + kind == ClassKind.INTERFACE -> NodeKind.Interface + kind == ClassKind.ENUM_CLASS -> NodeKind.Enum + kind == ClassKind.ANNOTATION_CLASS -> NodeKind.AnnotationClass + kind == ClassKind.ENUM_ENTRY -> NodeKind.EnumItem + isSubclassOfThrowable() -> NodeKind.Exception + else -> NodeKind.Class + } + val node = nodeForDescriptor(this, kind, external) + register(this, node) + supertypesWithAnyPrecise().forEach { + node.appendSupertype(this, it, !external) + } + if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { + node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) + } + if (!external) { + for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) { + node.appendClassMember(descriptor, inheritedLinkKind, extraModifier) + } + node.appendAnnotations(this) + } + node.appendModifiers(this) + if (!external) { + node.appendSourceLink(source) + node.appendDefaultPlatforms(this) + } + return node + } + + data class ClassMember(val descriptor: DeclarationDescriptor, + val inheritedLinkKind: RefKind = RefKind.InheritedMember, + val extraModifier: String? = null) + + fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> { + val result = arrayListOf<ClassMember>() + if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) { + val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS) + constructors.filter { it.valueParameters.size > 0 } + else + constructors + constructorsToDocument.mapTo(result) { ClassMember(it) } + } + + defaultType.memberScope.getContributedDescriptors() + .filter { it != companionObjectDescriptor } + .mapTo(result) { ClassMember(it) } + + staticScope.getContributedDescriptors() + .mapTo(result) { ClassMember(it, extraModifier = "static") } + + val companionObjectDescriptor = companionObjectDescriptor + if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(options)) { + val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors() + val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() } + descriptorsToDocument.mapTo(result) { + ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember) + } + + if (companionObjectDescriptor.getAllSuperclassesWithoutAny().isNotEmpty() + || companionObjectDescriptor.getSuperInterfaces().isNotEmpty()) { + result += ClassMember(companionObjectDescriptor) + } + } + return result + } + + fun CallableDescriptor.isInheritedFromAny(): Boolean { + return findTopMostOverriddenDescriptors().any { + DescriptorUtils.getFqNameSafe(it.containingDeclaration).asString() == "kotlin.Any" + } + } + + fun ClassDescriptor.isSubclassOfThrowable(): Boolean = + defaultType.supertypes().any { it.constructor.declarationDescriptor == builtIns.throwable } + + fun ConstructorDescriptor.build(): DocumentationNode { + val node = nodeForDescriptor(this, NodeKind.Constructor) + node.appendInPageChildren(valueParameters, RefKind.Detail) + node.appendDefaultPlatforms(this) + register(this, node) + return node + } + + private fun CallableMemberDescriptor.inCompanionObject(): Boolean { + val containingDeclaration = containingDeclaration + if ((containingDeclaration as? ClassDescriptor)?.isCompanionObject ?: false) { + return true + } + val receiver = extensionReceiverParameter + return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false + } + + fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode { + if (ErrorUtils.containsErrorTypeInParameters(this) || ErrorUtils.containsErrorType(this.returnType)) { + logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}") + } + + val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external) + + node.appendInPageChildren(typeParameters, RefKind.Detail) + extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) } + node.appendInPageChildren(valueParameters, RefKind.Detail) + node.appendType(returnType) + node.appendAnnotations(this) + node.appendModifiers(this) + if (!external) { + node.appendSourceLink(source) + node.appendDefaultPlatforms(this) + } else { + node.appendExternalLink(this) + } + + overriddenDescriptors.forEach { + addOverrideLink(it, this) + } + + register(this, node) + return node + } + + fun addOverrideLink(baseClassFunction: CallableMemberDescriptor, overridingFunction: CallableMemberDescriptor) { + val source = baseClassFunction.original.source.getPsi() + if (source != null) { + link(overridingFunction, baseClassFunction, RefKind.Override) + } else { + baseClassFunction.overriddenDescriptors.forEach { + addOverrideLink(it, overridingFunction) + } + } + } + + fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode { + val node = nodeForDescriptor( + this, + if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property, + external + ) + node.appendInPageChildren(typeParameters, RefKind.Detail) + extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) } + node.appendType(returnType) + node.appendAnnotations(this) + node.appendModifiers(this) + if (!external) { + node.appendSourceLink(source) + if (isVar) { + node.appendTextNode("var", NodeKind.Modifier) + } + + if (isConst) { + val psi = sourcePsi() + val valueText = when (psi) { + is KtVariableDeclaration -> psi.initializer?.text + is PsiField -> psi.initializer?.text + else -> null + } + valueText?.let { node.appendTextNode(it, NodeKind.Value) } + } + + + getter?.let { + if (!it.isDefault) { + node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter") + } + } + setter?.let { + if (!it.isDefault) { + node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter") + } + } + node.appendDefaultPlatforms(this) + } + if (external) { + node.appendExternalLink(this) + } + + overriddenDescriptors.forEach { + addOverrideLink(it, this) + } + + register(this, node) + return node + } + + fun DocumentationNode.addAccessorDocumentation(documentation: Content, prefix: String) { + if (documentation == Content.Empty) return + updateContent { + if (!documentation.children.isEmpty()) { + val section = addSection(prefix, null) + documentation.children.forEach { section.append(it) } + } + documentation.sections.forEach { + val section = addSection("$prefix ${it.tag}", it.subjectName) + it.children.forEach { section.append(it) } + } + } + } + + fun ValueParameterDescriptor.build(): DocumentationNode { + val node = nodeForDescriptor(this, NodeKind.Parameter) + node.appendType(varargElementType ?: type) + if (declaresDefaultValue()) { + val psi = source.getPsi() as? KtParameter + if (psi != null) { + val defaultValueText = psi.defaultValue?.text + if (defaultValueText != null) { + node.appendTextNode(defaultValueText, NodeKind.Value) + } + } + } + node.appendAnnotations(this) + node.appendModifiers(this) + if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) { + node.appendTextNode("vararg", NodeKind.Modifier) + } + register(this, node) + return node + } + + fun TypeParameterDescriptor.build(): DocumentationNode { + val doc = descriptorDocumentationParser.parseDocumentation(this) + val name = name.asString() + val prefix = variance.label + + val node = DocumentationNode(name, doc, NodeKind.TypeParameter) + if (prefix != "") { + node.appendTextNode(prefix, NodeKind.Modifier) + } + if (isReified) { + node.appendTextNode("reified", NodeKind.Modifier) + } + + for (constraint in upperBounds) { + if (KotlinBuiltIns.isDefaultBound(constraint)) { + continue + } + node.appendType(constraint, NodeKind.UpperBound) + } + register(this, node) + return node + } + + fun ReceiverParameterDescriptor.build(): DocumentationNode { + var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! + if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) { + receiverClass = receiverClass.containingDeclaration!! + } else if (receiverClass is TypeParameterDescriptor) { + val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor + if (upperBoundClass != null) { + receiverClass = upperBoundClass + } + } + + if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) { + link(receiverClass, containingDeclaration, RefKind.Extension) + } + + val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver) + node.appendType(type) + register(this, node) + return node + } + + fun AnnotationDescriptor.build(isWithinReplaceWith: Boolean = false): DocumentationNode? { + val annotationClass = type.constructor.declarationDescriptor + if (annotationClass == null || ErrorUtils.isError(annotationClass)) { + return null + } + val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, NodeKind.Annotation) + allValueArguments.forEach foreach@{ (name, value) -> + if (name.toString() == "imports" && value.toString() == "[]") return@foreach + var valueNode: DocumentationNode? = null + if (value.toString() == "@kotlin.ReplaceWith") { + valueNode = (value.value as AnnotationDescriptor).build(true) + } + else valueNode = value.toDocumentationNode(isWithinReplaceWith) + if (valueNode != null) { + val paramNode = DocumentationNode(name.asString(), Content.Empty, NodeKind.Parameter) + paramNode.append(valueNode, RefKind.Detail) + node.append(paramNode, RefKind.Detail) + } + } + return node + } + + fun ConstantValue<*>.toDocumentationNode(isWithinReplaceWith: Boolean = false): DocumentationNode? = value?.let { value -> + when (value) { + is String -> + (if (isWithinReplaceWith) "Replace with: " else "") + "\"" + StringUtil.escapeStringCharacters(value) + "\"" + is EnumEntrySyntheticClassDescriptor -> + value.containingDeclaration.name.asString() + "." + value.name.asString() + is Pair<*, *> -> { + val (classId, name) = value + if (classId is ClassId && name is Name) { + classId.shortClassName.asString() + "." + name.asString() + } else { + value.toString() + } + } + else -> value.toString() + }.let { valueString -> + DocumentationNode(valueString, Content.Empty, NodeKind.Value) + } + } + + + fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor, + externalClassNodes: MutableMap<FqName, DocumentationNode>, + allFqNames: Collection<FqName>): DocumentationNode { + if (descriptor is CallableMemberDescriptor) { + val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() + if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) && + !ErrorUtils.isError(extensionClassDescriptor)) { + val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) + return externalClassNodes.getOrPut(fqName, { + val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass) + val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor) + if (externalLink != null) { + newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) + } + append(newNode, RefKind.Member) + newNode + }) + } + } + return this + } + +} + +fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean { + return (options.effectivePackageOptions(fqNameSafe).includeNonPublic + || this !is MemberDescriptor + || this.visibility.isPublicAPI) + && !isDocumentationSuppressed(options) + && (!options.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated()) +} + +private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION + +class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { + override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + allFqNames: Collection<FqName>) { + val externalClassNodes = hashMapOf<FqName, DocumentationNode>() + declarations.forEach { descriptor -> + with(documentationBuilder) { + if (descriptor.isDocumented(options)) { + val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames) + parent.appendOrUpdateMember(descriptor) + } + } + } + } +} + +class KotlinJavaDocumentationBuilder +@Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val documentationBuilder: DocumentationBuilder, + val options: DocumentationOptions, + val logger: DokkaLogger) : JavaDocumentationBuilder { + override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { + val classDescriptors = file.classes.map { + it.getJavaClassDescriptor(resolutionFacade) + } + + if (classDescriptors.any { it != null && it.isDocumented(options) }) { + val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph) + + for (descriptor in classDescriptors.filterNotNull()) { + with(documentationBuilder) { + packageNode.appendChild(descriptor, RefKind.Member) + } + } + } + } +} + +private val hiddenAnnotations = setOf( + KotlinBuiltIns.FQ_NAMES.parameterName.asString() +) + +private fun AnnotationDescriptor.isHiddenInDocumentation() = + type.constructor.declarationDescriptor?.fqNameSafe?.asString() in hiddenAnnotations + +private fun AnnotationDescriptor.isDocumented(): Boolean { + if (source.getPsi() != null && mustBeDocumented()) return true + val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() + return annotationClassName == KotlinBuiltIns.FQ_NAMES.extensionFunctionType.asString() +} + +fun AnnotationDescriptor.mustBeDocumented(): Boolean { + val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false + return annotationClass.isDocumentedAnnotation() +} + +fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOptions): Boolean { + + if (options.effectivePackageOptions(fqNameSafe).suppress) return true + + val path = this.findPsi()?.containingFile?.virtualFile?.path + if (path != null) { + if (File(path).absoluteFile in options.suppressedFiles) return true + } + + val doc = findKDoc() + if (doc is KDocSection && doc.findTagByName("suppress") != null) return true + + return hasSuppressDocTag(sourcePsi()) || hasHideAnnotation(sourcePsi()) +} + +fun DeclarationDescriptor.sourcePsi() = + ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi + +fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { + DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" +} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) + +fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { + val extensionReceiver = extensionReceiverParameter + if (extensionReceiver != null) { + val type = extensionReceiver.type + val receiverClass = type.constructor.declarationDescriptor as? ClassDescriptor + if (receiverClass?.isCompanionObject ?: false) { + return receiverClass?.containingDeclaration as? ClassifierDescriptor + } + return receiverClass + } + return null +} + +fun DeclarationDescriptor.signature(): String { + if (this != original) return original.signature() + return when (this) { + is ClassDescriptor, + is PackageFragmentDescriptor, + is PackageViewDescriptor, + is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString() + + is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature() + is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() + is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name + is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name + is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name + else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this") + } +} + +fun PropertyDescriptor.receiverSignature(): String { + val receiver = extensionReceiverParameter + if (receiver != null) { + return "#" + receiver.type.signature() + } + return "" +} + +fun CallableMemberDescriptor.parameterSignature(): String { + val params = valueParameters.map { it.type }.toMutableList() + val extensionReceiver = extensionReceiverParameter + if (extensionReceiver != null) { + params.add(0, extensionReceiver.type) + } + return params.joinToString(prefix = "(", postfix = ")") { it.signature() } +} + +fun KotlinType.signature(): String { + val visited = hashSetOf<KotlinType>() + + fun KotlinType.signatureRecursive(): String { + if (this in visited) { + return "" + } + visited.add(this) + + val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>" + val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() + if (arguments.isEmpty()) { + return typeName + } + return typeName + arguments.joinToString(prefix = "((", postfix = "))") { it.type.signatureRecursive() } + } + + return signatureRecursive() +} + +fun DeclarationDescriptor.signatureWithSourceLocation(): String { + val signature = signature() + val sourceLocation = sourceLocation() + return if (sourceLocation != null) "$signature ($sourceLocation)" else signature +} + +fun DeclarationDescriptor.sourceLocation(): String? { + val psi = sourcePsi() + if (psi != null) { + val fileName = psi.containingFile.name + val lineNumber = psi.lineNumber() + return if (lineNumber != null) "$fileName:$lineNumber" else fileName + } + return null +} + +fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) { + if (options.generateClassIndexPage) { + generateAllTypesNode() + } + nodeRefGraph.resolveReferences() +} + +fun DocumentationNode.generateAllTypesNode() { + val allTypes = members(NodeKind.Package) + .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } } + .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() } + + val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) + for (typeNode in allTypes) { + allTypesNode.addReferenceTo(typeNode, RefKind.Member) + } + + append(allTypesNode, RefKind.Member) +} + +fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> { + if (KotlinBuiltIns.isAny(this)) { + return emptyList() + } + return typeConstructor.supertypesWithAny() +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt new file mode 100644 index 000000000..d09bc1c9f --- /dev/null +++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt @@ -0,0 +1,258 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.Singleton +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.util.io.* +import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor +import org.jetbrains.dokka.Formats.FormatDescriptor +import org.jetbrains.dokka.Utilities.ServiceLocator +import org.jetbrains.dokka.Utilities.lookup +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.kotlin.load.java.descriptors.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.parents +import java.io.ByteArrayOutputStream +import java.io.PrintWriter +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import java.nio.file.Path +import java.security.MessageDigest +import javax.inject.Named +import kotlin.reflect.full.findAnnotation + +fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) } + +@Singleton +class ExternalDocumentationLinkResolver @Inject constructor( + val options: DocumentationOptions, + @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade, + val logger: DokkaLogger +) { + + val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>() + val formats = mutableMapOf<String, InboundExternalLinkResolutionService>() + + class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) { + override fun toString(): String = rootUrl.toString() + } + + val cacheDir: Path? = options.cacheRoot?.resolve("packageListCache")?.apply { toFile().mkdirs() } + + val cachedProtocols = setOf("http", "https", "ftp") + + fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { + val connection = this.openConnection() + connection.connectTimeout = timeout + connection.readTimeout = timeout + + when (connection) { + is HttpURLConnection -> { + return when (connection.responseCode) { + in 200..299 -> { + connection + } + HttpURLConnection.HTTP_MOVED_PERM, + HttpURLConnection.HTTP_MOVED_TEMP, + HttpURLConnection.HTTP_SEE_OTHER -> { + if (redirectsAllowed > 0) { + val newUrl = connection.getHeaderField("Location") + URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) + } else { + throw RuntimeException("Too many redirects") + } + } + else -> { + throw RuntimeException("Unhandled http code: ${connection.responseCode}") + } + } + } + else -> return connection + } + } + + fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) { + + val packageListUrl = link.packageListUrl + val needsCache = packageListUrl.protocol in cachedProtocols + + val packageListStream = if (cacheDir != null && needsCache) { + val packageListLink = packageListUrl.toExternalForm() + + val digest = MessageDigest.getInstance("SHA-256") + val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString() + val cacheEntry = cacheDir.resolve(hash).toFile() + + if (cacheEntry.exists()) { + try { + val connection = packageListUrl.doOpenConnectionToReadContent() + val originModifiedDate = connection.date + val cacheDate = cacheEntry.lastModified() + if (originModifiedDate > cacheDate || originModifiedDate == 0L) { + if (originModifiedDate == 0L) + logger.warn("No date header for $packageListUrl, downloading anyway") + else + logger.info("Renewing package-list from $packageListUrl") + connection.getInputStream().copyTo(cacheEntry.outputStream()) + } + } catch (e: Exception) { + logger.error("Failed to update package-list cache for $link") + val baos = ByteArrayOutputStream() + PrintWriter(baos).use { + e.printStackTrace(it) + } + baos.flush() + logger.error(baos.toString()) + } + } else { + logger.info("Downloading package-list from $packageListUrl") + packageListUrl.openStream().copyTo(cacheEntry.outputStream()) + } + cacheEntry.inputStream() + } else { + packageListUrl.doOpenConnectionToReadContent().getInputStream() + } + + val (params, packages) = + packageListStream + .bufferedReader() + .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } } + + val paramsMap = params.asSequence() + .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) } + .groupBy({ (key, _) -> key }, { (_, value) -> value }) + + val format = paramsMap["format"]?.singleOrNull() ?: "javadoc" + + val locations = paramsMap["location"].orEmpty() + .map { it.split("\u001f", limit = 2) } + .map { (key, value) -> key to value } + .toMap() + + + val defaultResolverDesc = services["dokka-default"]!! + val resolverDesc = services[format] + ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver } + ?: defaultResolverDesc.also { + logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default") + } + + + val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin + + val constructors = resolverClass.constructors + + val constructor = constructors.singleOrNull() + ?: constructors.first { it.findAnnotation<Inject>() != null } + val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService + + val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations) + + packages.map { FqName(it) }.forEach { packageFqNameToLocation[it] = rootInfo } + } + + init { + options.externalDocumentationLinks.forEach { + try { + loadPackageList(it) + } catch (e: Exception) { + throw RuntimeException("Exception while loading package-list from $it", e) + } + } + } + + fun buildExternalDocumentationLink(element: PsiElement): String? { + return element.extractDescriptor(libraryResolutionFacade)?.let { + buildExternalDocumentationLink(it) + } + } + + fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? { + val packageFqName: FqName = + when (symbol) { + is PackageFragmentDescriptor -> symbol.fqName + is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null + else -> return null + } + + val externalLocation = packageFqNameToLocation[packageFqName] ?: return null + + val path = externalLocation.locations[symbol.signature()] ?: + externalLocation.resolver.getPath(symbol) ?: return null + + return URL(externalLocation.rootUrl, path).toExternalForm() + } + + companion object { + const val DOKKA_PARAM_PREFIX = "\$dokka." + val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name } + private val formatsWithDefaultResolver = + ServiceLocator + .allServices("format") + .filter { + val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor + desc?.generatorServiceClass == FileGenerator::class + }.map { it.name } + .toSet() + } +} + + +interface InboundExternalLinkResolutionService { + fun getPath(symbol: DeclarationDescriptor): String? + + class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { + override fun getPath(symbol: DeclarationDescriptor): String? { + if (symbol is EnumEntrySyntheticClassDescriptor) { + return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() } + } else if (symbol is ClassDescriptor) { + return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html" + } else if (symbol is JavaCallableMemberDescriptor) { + val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null + val containingClassLink = getPath(containingClass) + if (containingClassLink != null) { + if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) { + val psi = symbol.sourcePsi() as? PsiMethod + if (psi != null) { + val params = psi.parameterList.parameters.joinToString { it.type.canonicalText } + return containingClassLink + "#" + symbol.name + "(" + params + ")" + } + } else if (symbol is JavaPropertyDescriptor) { + return "$containingClassLink#${symbol.name}" + } + } + } + // TODO Kotlin javadoc + return null + } + } + + class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { + val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver") + + override fun getPath(symbol: DeclarationDescriptor): String? { + val leafElement = when (symbol) { + is CallableDescriptor, is TypeAliasDescriptor -> true + else -> false + } + val path = getPathWithoutExtension(symbol) + if (leafElement) return "$path.$extension" + else return "$path/index.$extension" + } + + private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String { + return when { + symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString()) + symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString()) + else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString()) + } + } + + } +} + diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt new file mode 100644 index 000000000..c5fb15385 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt @@ -0,0 +1,68 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtPropertyAccessor + +class KotlinAsJavaDocumentationBuilder + @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder +{ + override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, + packageName: FqName, + packageNode: DocumentationNode, + declarations: List<DeclarationDescriptor>, + allFqNames: Collection<FqName>) { + val project = documentationBuilder.resolutionFacade.project + val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString()) + if (psiPackage == null) { + documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}") + return + } + + val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.options, + documentationBuilder.refGraph, + kotlinAsJavaDocumentationParser, + documentationBuilder.linkResolver.externalDocumentationLinkResolver + ) + + psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach { + javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it)) + } + } + + fun PsiClass.isVisibleInDocumentation(): Boolean { + val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true + + return origin.hasModifier(KtTokens.INTERNAL_KEYWORD) != true && + origin.hasModifier(KtTokens.PRIVATE_KEYWORD) != true + } +} + +class KotlinAsJavaDocumentationParser + @Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser +{ + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { + val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty + val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty + if (origin is KtParameter) { + // LazyDeclarationResolver does not support setter parameters + val grandFather = origin.parent?.parent + if (grandFather is KtPropertyAccessor) { + return JavadocParseResult.Empty + } + } + val descriptor = resolutionFacade.resolveToDescriptor(origin) + val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter) + return JavadocParseResult(content, null, emptyList(), null, null) + } +} diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt new file mode 100644 index 000000000..20ea179ee --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtElement + +class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider { + + private fun PsiElement.javaLikePsi() = when { + this is KtElement -> toLightElements().firstOrNull() + else -> this + } + + override fun signature(forPsi: PsiElement): String { + return getSignature(forPsi.javaLikePsi()) ?: + "not implemented for $forPsi" + } + + override fun signature(forDesc: DeclarationDescriptor): String { + val sourcePsi = forDesc.sourcePsi() + return getSignature(sourcePsi?.javaLikePsi()) ?: + "not implemented for $forDesc with psi: $sourcePsi" + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt new file mode 100644 index 000000000..bcac01829 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt @@ -0,0 +1,34 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiPackage +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.BindingContext +import javax.inject.Inject + +class KotlinElementSignatureProvider @Inject constructor( + val resolutionFacade: DokkaResolutionFacade +) : ElementSignatureProvider { + override fun signature(forPsi: PsiElement): String { + return forPsi.extractDescriptor(resolutionFacade) + ?.let { signature(it) } + ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" } + } + + override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() +} + + +fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? { + val forPsi = this + + return when (forPsi) { + is KtLightElement<*, *> -> return (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade) + is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName)) + is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) + else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] + } +} diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt new file mode 100644 index 000000000..8a33ff8b7 --- /dev/null +++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt @@ -0,0 +1,463 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.classNodeNameWithOuterClass +import org.jetbrains.dokka.LanguageService.RenderMode + +/** + * Implements [LanguageService] and provides rendering of symbols in Kotlin language + */ +class KotlinLanguageService : CommonLanguageService() { + override fun showModifierInSummary(node: DocumentationNode): Boolean { + return node.name !in fullOnlyModifiers + } + + private val fullOnlyModifiers = + setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified") + + override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { + return content { + when (node.kind) { + NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node) + in NodeKind.classLike -> renderClass(node, renderMode) + + NodeKind.EnumItem -> renderClass(node, renderMode) + NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name) + + NodeKind.Parameter -> renderParameter(node, renderMode) + NodeKind.TypeParameter -> renderTypeParameter(node, renderMode) + NodeKind.Type, + NodeKind.UpperBound -> renderType(node, renderMode) + + NodeKind.Modifier -> renderModifier(this, node, renderMode) + NodeKind.Constructor, + NodeKind.Function, + NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode) + NodeKind.Property, + NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode) + else -> identifier(node.name) + } + } + } + + + override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? { + if (nodes.size < 2) return null + val receiverKind = nodes.getReceiverKind() ?: return null + val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null + return content { + val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first() + if (functionWithTypeParameter.kind == NodeKind.Function) { + renderFunction( + functionWithTypeParameter, + RenderMode.SUMMARY, + SummarizingMapper(receiverKind, typeParameter.name) + ) + } else { + renderProperty( + functionWithTypeParameter, + RenderMode.SUMMARY, + SummarizingMapper(receiverKind, typeParameter.name) + ) + } + } + } + + private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? { + val qNames = map { it.getReceiverQName() }.filterNotNull() + if (qNames.size != size) + return null + + return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } } + } + + private fun DocumentationNode.getReceiverQName(): String? { + if (kind != NodeKind.Function && kind != NodeKind.Property) return null + val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null + return receiver.detail(NodeKind.Type).qualifiedNameFromType() + } + + companion object { + private val arrayClasses = setOf( + "kotlin.Array", + "kotlin.BooleanArray", + "kotlin.ByteArray", + "kotlin.CharArray", + "kotlin.ShortArray", + "kotlin.IntArray", + "kotlin.LongArray", + "kotlin.FloatArray", + "kotlin.DoubleArray" + ) + + private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses + + private val iterableClasses = setOf( + "kotlin.Collection", + "kotlin.Sequence", + "kotlin.Iterable", + "kotlin.Map", + "kotlin.String", + "kotlin.CharSequence" + ) + arrayOrListClasses + } + + private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) { + ARRAY("any_array", arrayClasses), + ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses), + ITERABLE("any_iterable", iterableClasses), + } + + interface SignatureMapper { + fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) + } + + private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper { + override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) { + to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName)) + to.text("<$typeParameterName>") + } + } + + private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) { + node.references(RefKind.HiddenAnnotation).map { it.to } + .find { it.name == "ParameterName" }?.let { + val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value) + identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName) + symbol(":") + nbsp() + } + } + + private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) { + var typeArguments = node.details(NodeKind.Type) + + if (node.name.startsWith("Suspend")) { + keyword("suspend ") + } + + // lambda + val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } + if (isExtension) { + renderType(typeArguments.first(), renderMode) + symbol(".") + typeArguments = typeArguments.drop(1) + } + symbol("(") + renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { + renderFunctionalTypeParameterName(it, renderMode) + renderType(it, renderMode) + } + symbol(")") + nbsp() + symbol("->") + nbsp() + renderType(typeArguments.last(), renderMode) + + } + + private fun DocumentationNode.isFunctionalType(): Boolean { + val typeArguments = details(NodeKind.Type) + val functionalTypeName = "Function${typeArguments.count() - 1}" + val suspendFunctionalTypeName = "Suspend$functionalTypeName" + return name == functionalTypeName || name == suspendFunctionalTypeName + } + + private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + if (node.name == "dynamic") { + keyword("dynamic") + return + } + if (node.isFunctionalType()) { + renderFunctionalType(node, renderMode) + return + } + if (renderMode == RenderMode.FULL) { + renderAnnotationsForNode(node) + } + renderModifiersForNode(node, renderMode, true) + renderLinked(this, node) { + identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName) + } + val typeArguments = node.details(NodeKind.Type) + if (typeArguments.isNotEmpty()) { + symbol("<") + renderList(typeArguments, noWrap = true) { + renderType(it, renderMode) + } + symbol(">") + } + val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull() + if (nullabilityModifier != null) { + symbol(nullabilityModifier.name) + } + } + + override fun renderModifier( + block: ContentBlock, + node: DocumentationNode, + renderMode: RenderMode, + nowrap: Boolean + ) { + when (node.name) { + "final", "public", "var" -> { + } + else -> { + if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) { + super.renderModifier(block, node, renderMode, nowrap) + } + } + } + } + + private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) { + renderModifiersForNode(node, renderMode, true) + + identifier(node.name) + + val constraints = node.details(NodeKind.UpperBound) + if (constraints.size == 1) { + nbsp() + symbol(":") + nbsp() + renderList(constraints, noWrap = true) { + renderType(it, renderMode) + } + } + } + + private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) { + if (renderMode == RenderMode.FULL) { + renderAnnotationsForNode(node) + } + renderModifiersForNode(node, renderMode) + identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name) + symbol(":") + nbsp() + val parameterType = node.detail(NodeKind.Type) + renderType(parameterType, renderMode) + val valueNode = node.details(NodeKind.Value).firstOrNull() + if (valueNode != null) { + nbsp() + symbol("=") + nbsp() + text(valueNode.name) + } + } + + private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) { + val typeParameters = node.details(NodeKind.TypeParameter) + if (typeParameters.any()) { + symbol("<") + renderList(typeParameters) { + renderTypeParameter(it, renderMode) + } + symbol(">") + } + } + + private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) { + val parametersWithMultipleConstraints = + node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 } + val parametersWithConstraints = parametersWithMultipleConstraints + .flatMap { parameter -> + parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } + } + if (parametersWithMultipleConstraints.isNotEmpty()) { + keyword(" where ") + renderList(parametersWithConstraints) { + identifier(it.first.name) + nbsp() + symbol(":") + nbsp() + renderType(it.second, renderMode) + } + } + } + + private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) { + val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes } + if (supertypes.any()) { + nbsp() + symbol(":") + nbsp() + renderList(supertypes) { + indentedSoftLineBreak() + renderType(it, renderMode) + } + } + } + + private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) { + node.annotations.forEach { + renderAnnotation(it) + } + } + + private fun ContentBlock.renderAnnotation(node: DocumentationNode) { + identifier("@" + node.name, IdentifierKind.AnnotationName) + val parameters = node.details(NodeKind.Parameter) + if (!parameters.isEmpty()) { + symbol("(") + renderList(parameters) { + text(it.detail(NodeKind.Value).name) + } + symbol(")") + } + text(" ") + } + + private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { + if (renderMode == RenderMode.FULL) { + renderAnnotationsForNode(node) + } + renderModifiersForNode(node, renderMode) + when (node.kind) { + NodeKind.Class, + NodeKind.AnnotationClass, + NodeKind.Exception, + NodeKind.Enum -> keyword("class ") + NodeKind.Interface -> keyword("interface ") + NodeKind.EnumItem -> keyword("enum val ") + NodeKind.Object -> keyword("object ") + NodeKind.TypeAlias -> keyword("typealias ") + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + + identifierOrDeprecated(node) + renderTypeParametersForNode(node, renderMode) + renderSupertypesForNode(node, renderMode) + renderExtraTypeParameterConstraints(node, renderMode) + + if (node.kind == NodeKind.TypeAlias) { + nbsp() + symbol("=") + nbsp() + renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode) + } + } + + private fun ContentBlock.renderFunction( + node: DocumentationNode, + renderMode: RenderMode, + signatureMapper: SignatureMapper? = null + ) { + if (renderMode == RenderMode.FULL) { + renderAnnotationsForNode(node) + } + renderModifiersForNode(node, renderMode) + when (node.kind) { + NodeKind.Constructor -> identifier(node.owner!!.name) + NodeKind.Function, + NodeKind.CompanionObjectFunction -> keyword("fun ") + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + renderTypeParametersForNode(node, renderMode) + if (node.details(NodeKind.TypeParameter).any()) { + text(" ") + } + + renderReceiver(node, renderMode, signatureMapper) + + if (node.kind != NodeKind.Constructor) + identifierOrDeprecated(node) + + symbol("(") + val parameters = node.details(NodeKind.Parameter) + renderHardWrappingList(parameters) { + renderParameter(it, renderMode) + } + if (needReturnType(node)) { + if (parameters.size > 1) { + hardLineBreak() + } + symbol(")") + symbol(": ") + renderType(node.detail(NodeKind.Type), renderMode) + } else { + symbol(")") + } + renderExtraTypeParameterConstraints(node, renderMode) + } + + private fun ContentBlock.renderReceiver( + node: DocumentationNode, + renderMode: RenderMode, + signatureMapper: SignatureMapper? + ) { + val receiver = node.details(NodeKind.Receiver).singleOrNull() + if (receiver != null) { + if (signatureMapper != null) { + signatureMapper.renderReceiver(receiver, this) + } else { + val type = receiver.detail(NodeKind.Type) + + if (type.isFunctionalType()) { + symbol("(") + renderFunctionalType(type, renderMode) + symbol(")") + } else { + renderType(type, renderMode) + } + } + symbol(".") + } + } + + private fun needReturnType(node: DocumentationNode) = when (node.kind) { + NodeKind.Constructor -> false + else -> !node.isUnitReturnType() + } + + fun DocumentationNode.isUnitReturnType(): Boolean = + detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit" + + private fun ContentBlock.renderProperty( + node: DocumentationNode, + renderMode: RenderMode, + signatureMapper: SignatureMapper? = null + ) { + if (renderMode == RenderMode.FULL) { + renderAnnotationsForNode(node) + } + renderModifiersForNode(node, renderMode) + when (node.kind) { + NodeKind.Property, + NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ") + else -> throw IllegalArgumentException("Node $node is not a property") + } + renderTypeParametersForNode(node, renderMode) + if (node.details(NodeKind.TypeParameter).any()) { + text(" ") + } + + renderReceiver(node, renderMode, signatureMapper) + + identifierOrDeprecated(node) + symbol(": ") + renderType(node.detail(NodeKind.Type), renderMode) + renderExtraTypeParameterConstraints(node, renderMode) + } + + fun DocumentationNode.getPropertyKeyword() = + if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val" + + fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) { + if (node.deprecation != null) { + val strike = ContentStrikethrough() + strike.identifier(node.name) + append(strike) + } else { + identifier(node.name) + } + } +} + +fun DocumentationNode.qualifiedNameFromType(): String { + return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name + ?: (links.firstOrNull { it.kind != NodeKind.ExternalLink } ?: hiddenLinks.firstOrNull())?.qualifiedName() + ?: name +} + + +val DocumentationNode.typeDeclarationClass + get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType) diff --git a/core/src/main/kotlin/Languages/CommonLanguageService.kt b/core/src/main/kotlin/Languages/CommonLanguageService.kt new file mode 100644 index 000000000..c6e3cd37f --- /dev/null +++ b/core/src/main/kotlin/Languages/CommonLanguageService.kt @@ -0,0 +1,118 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.Formats.nameWithOuterClass + + +abstract class CommonLanguageService : LanguageService { + + protected fun ContentBlock.renderPackage(node: DocumentationNode) { + keyword("package") + nbsp() + identifier(node.name) + } + + override fun renderName(node: DocumentationNode): String { + return when (node.kind) { + NodeKind.Constructor -> node.owner!!.name + else -> node.name + } + } + + override fun renderNameWithOuterClass(node: DocumentationNode): String { + return when (node.kind) { + NodeKind.Constructor -> node.owner!!.nameWithOuterClass() + else -> node.nameWithOuterClass() + } + } + + open fun renderModifier( + block: ContentBlock, + node: DocumentationNode, + renderMode: LanguageService.RenderMode, + nowrap: Boolean = false + ) = with(block) { + keyword(node.name) + if (nowrap) { + nbsp() + } else { + text(" ") + } + } + + protected fun renderLinked( + block: ContentBlock, + node: DocumentationNode, + body: ContentBlock.(DocumentationNode) -> Unit + ) = with(block) { + val to = node.links.firstOrNull() + if (to == null) + body(node) + else + link(to) { + this.body(node) + } + } + + protected fun <T> ContentBlock.renderHardWrappingList( + nodes: List<T>, separator: String = ", ", + renderItem: (T) -> Unit + ) { + if (nodes.none()) + return + + if (nodes.count() > 1) { + hardLineBreak() + repeat(4) { + nbsp() + } + } + + renderItem(nodes.first()) + nodes.drop(1).forEach { + symbol(separator) + hardLineBreak() + repeat(4) { + nbsp() + } + renderItem(it) + } + } + + protected fun <T> ContentBlock.renderList( + nodes: List<T>, separator: String = ", ", + noWrap: Boolean = false, renderItem: (T) -> Unit + ) { + if (nodes.none()) + return + renderItem(nodes.first()) + nodes.drop(1).forEach { + if (noWrap) { + symbol(separator.removeSuffix(" ")) + nbsp() + } else { + symbol(separator) + } + renderItem(it) + } + } + + abstract fun showModifierInSummary(node: DocumentationNode): Boolean + + protected fun ContentBlock.renderModifiersForNode( + node: DocumentationNode, + renderMode: LanguageService.RenderMode, + nowrap: Boolean = false + ) { + val modifiers = node.details(NodeKind.Modifier) + for (it in modifiers) { + if (node.kind == NodeKind.Interface && it.name == "abstract") + continue + if (renderMode == LanguageService.RenderMode.SUMMARY && !showModifierInSummary(it)) { + continue + } + renderModifier(this, it, renderMode, nowrap) + } + } + + +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/JavaLanguageService.kt b/core/src/main/kotlin/Languages/JavaLanguageService.kt new file mode 100644 index 000000000..4b3979b54 --- /dev/null +++ b/core/src/main/kotlin/Languages/JavaLanguageService.kt @@ -0,0 +1,179 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.Formats.nameWithOuterClass +import org.jetbrains.dokka.LanguageService.RenderMode + +/** + * Implements [LanguageService] and provides rendering of symbols in Java language + */ +class JavaLanguageService : LanguageService { + override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { + return ContentText(when (node.kind) { + NodeKind.Package -> renderPackage(node) + in NodeKind.classLike -> renderClass(node) + + NodeKind.TypeParameter -> renderTypeParameter(node) + NodeKind.Type, + NodeKind.UpperBound -> renderType(node) + + NodeKind.Constructor, + NodeKind.Function -> renderFunction(node) + NodeKind.Property -> renderProperty(node) + else -> "${node.kind}: ${node.name}" + }) + } + + override fun renderName(node: DocumentationNode): String { + return when (node.kind) { + NodeKind.Constructor -> node.owner!!.name + else -> node.name + } + } + + override fun renderNameWithOuterClass(node: DocumentationNode): String { + return when (node.kind) { + NodeKind.Constructor -> node.owner!!.nameWithOuterClass() + else -> node.nameWithOuterClass() + } + } + + override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null + + private fun renderPackage(node: DocumentationNode): String { + return "package ${node.name}" + } + + private fun renderModifier(node: DocumentationNode): String { + return when (node.name) { + "open" -> "" + "internal" -> "" + else -> node.name + } + } + + fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { + "kotlin.Array" -> + node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?: + DocumentationNode("Object", node.content, NodeKind.ExternalClass) + + "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", + "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> + DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) + + else -> null + } + + fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { + "kotlin.Array" -> + 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) + + "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", + "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> + 1 + else -> 0 + } + + fun renderType(node: DocumentationNode): String { + return when (node.name) { + "Unit" -> "void" + "Int" -> "int" + "Long" -> "long" + "Double" -> "double" + "Float" -> "float" + "Char" -> "char" + "Boolean" -> "bool" + // TODO: render arrays + else -> node.name + } + } + + private fun renderTypeParameter(node: DocumentationNode): String { + val constraints = node.details(NodeKind.UpperBound) + return if (constraints.none()) + node.name + else { + node.name + " extends " + constraints.map { renderType(node) }.joinToString() + } + } + + private fun renderParameter(node: DocumentationNode): String { + return "${renderType(node.detail(NodeKind.Type))} ${node.name}" + } + + private fun renderTypeParametersForNode(node: DocumentationNode): String { + return StringBuilder().apply { + val typeParameters = node.details(NodeKind.TypeParameter) + if (typeParameters.any()) { + append("<") + append(typeParameters.map { renderTypeParameter(it) }.joinToString()) + append("> ") + } + }.toString() + } + + private fun renderModifiersForNode(node: DocumentationNode): String { + val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } + if (modifiers.none()) + return "" + return modifiers.joinToString(" ", postfix = " ") + } + + private fun renderClass(node: DocumentationNode): String { + return StringBuilder().apply { + when (node.kind) { + NodeKind.Class -> append("class ") + NodeKind.Interface -> append("interface ") + NodeKind.Enum -> append("enum ") + NodeKind.EnumItem -> append("enum value ") + NodeKind.Object -> append("class ") + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + + append(node.name) + append(renderTypeParametersForNode(node)) + }.toString() + } + + private fun renderFunction(node: DocumentationNode): String { + return StringBuilder().apply { + when (node.kind) { + NodeKind.Constructor -> append(node.owner?.name) + NodeKind.Function -> { + append(renderTypeParametersForNode(node)) + append(renderType(node.detail(NodeKind.Type))) + append(" ") + append(node.name) + } + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + + val receiver = node.details(NodeKind.Receiver).singleOrNull() + append("(") + if (receiver != null) + (listOf(receiver) + node.details(NodeKind.Parameter)).map { renderParameter(it) }.joinTo(this) + else + node.details(NodeKind.Parameter).map { renderParameter(it) }.joinTo(this) + + append(")") + }.toString() + } + + private fun renderProperty(node: DocumentationNode): String { + return StringBuilder().apply { + when (node.kind) { + NodeKind.Property -> append("val ") + else -> throw IllegalArgumentException("Node $node is not a property") + } + append(renderTypeParametersForNode(node)) + val receiver = node.details(NodeKind.Receiver).singleOrNull() + if (receiver != null) { + append(renderType(receiver.detail(NodeKind.Type))) + append(".") + } + + append(node.name) + append(": ") + append(renderType(node.detail(NodeKind.Type))) + }.toString() + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/LanguageService.kt b/core/src/main/kotlin/Languages/LanguageService.kt new file mode 100644 index 000000000..e43081993 --- /dev/null +++ b/core/src/main/kotlin/Languages/LanguageService.kt @@ -0,0 +1,43 @@ +package org.jetbrains.dokka + +/** + * Provides facility for rendering [DocumentationNode] as a language-dependent declaration + */ +interface LanguageService { + enum class RenderMode { + /** Brief signature (used in a list of all members of the class). */ + SUMMARY, + /** Full signature (used in the page describing the member itself */ + FULL + } + + /** + * Renders a [node] as a class, function, property or other signature in a target language. + * @param node A [DocumentationNode] to render + * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService] + */ + fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode + + /** + * Tries to summarize the signatures of the specified documentation nodes in a compact representation. + * Returns the representation if successful, or null if the signatures could not be summarized. + */ + fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? + + /** + * Renders [node] as a named representation in the target language + * + * For example: + * ${code org.jetbrains.dokka.example} + * + * $node: A [DocumentationNode] to render + * $returns: [String] which is a string representation of the node's name + */ + fun renderName(node: DocumentationNode): String + + fun renderNameWithOuterClass(node: DocumentationNode): String +} + +fun example(service: LanguageService, node: DocumentationNode) { + println("Node name: ${service.renderName(node)}") +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt new file mode 100644 index 000000000..c4b5fa090 --- /dev/null +++ b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt @@ -0,0 +1,250 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.classNodeNameWithOuterClass +import org.jetbrains.dokka.LanguageService.RenderMode + +/** + * Implements [LanguageService] and provides rendering of symbols in Java language + */ +class NewJavaLanguageService : CommonLanguageService() { + override fun showModifierInSummary(node: DocumentationNode): Boolean { + return node.name !in fullOnlyModifiers + } + + private val fullOnlyModifiers = setOf("public", "protected", "private") + + override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { + return content { + (when (node.kind) { + NodeKind.Package -> renderPackage(node) + in NodeKind.classLike -> renderClass(node, renderMode) + + NodeKind.Modifier -> renderModifier(this, node, renderMode) + NodeKind.TypeParameter -> renderTypeParameter(node) + NodeKind.Type, + NodeKind.UpperBound -> renderType(node) + NodeKind.Parameter -> renderParameter(node) + NodeKind.Constructor, + NodeKind.Function -> renderFunction(node, renderMode) + NodeKind.Property -> renderProperty(node) + NodeKind.Field -> renderField(node, renderMode) + NodeKind.EnumItem -> renderClass(node, renderMode) + else -> "${node.kind}: ${node.name}" + }) + } + } + + override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null + + + override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) { + when (node.name) { + "open", "internal" -> { + } + else -> { + if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) { + super.renderModifier(block, node, renderMode, nowrap) + } + } + } + } + + fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { + "kotlin.Array" -> + node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } + ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass) + + "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", + "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> + DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) + + else -> null + } + + fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { + "kotlin.Array" -> + 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) + + "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", + "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> + 1 + else -> 0 + } + + fun ContentBlock.renderType(node: DocumentationNode) { + when (node.name) { + "Unit" -> identifier("void") + "Int" -> identifier("int") + "Long" -> identifier("long") + "Double" -> identifier("double") + "Float" -> identifier("float") + "Char" -> identifier("char") + "Boolean" -> identifier("bool") + // TODO: render arrays + else -> { + renderLinked(this, node) { + identifier( + it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, + IdentifierKind.TypeName + ) + } + renderTypeArgumentsForType(node) + } + } + } + + private fun ContentBlock.renderTypeParameter(node: DocumentationNode) { + val constraints = node.details(NodeKind.UpperBound) + if (constraints.none()) + identifier(node.name) + else { + identifier(node.name) + text(" ") + keyword("extends") + text(" ") + constraints.forEach { renderType(node) } + } + } + + private fun ContentBlock.renderParameter(node: DocumentationNode) { + renderType(node.detail(NodeKind.Type)) + text(" ") + identifier(node.name) + } + + private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) { + val typeParameters = node.details(NodeKind.TypeParameter) + if (typeParameters.any()) { + symbol("<") + renderList(typeParameters, noWrap = true) { + renderTypeParameter(it) + } + symbol(">") + text(" ") + } + } + + private fun ContentBlock.renderTypeArgumentsForType(node: DocumentationNode) { + val typeArguments = node.details(NodeKind.Type) + if (typeArguments.any()) { + symbol("<") + renderList(typeArguments, noWrap = true) { + renderType(it) + } + symbol(">") + } + } + +// private fun renderModifiersForNode(node: DocumentationNode): String { +// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } +// if (modifiers.none()) +// return "" +// return modifiers.joinToString(" ", postfix = " ") +// } + + private fun ContentBlock.renderClassKind(node: DocumentationNode) { + when (node.kind) { + NodeKind.Interface -> { + keyword("interface") + } + NodeKind.EnumItem -> { + keyword("enum value") + } + NodeKind.Enum -> { + keyword("enum") + } + NodeKind.Class, NodeKind.Exception, NodeKind.Object -> { + keyword("class") + } + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + text(" ") + } + + private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { + renderModifiersForNode(node, renderMode) + renderClassKind(node) + + identifier(node.name) + renderTypeParametersForNode(node) + val superClassType = node.superclassType + val interfaces = node.supertypes - superClassType + if (superClassType != null) { + text(" ") + keyword("extends") + text(" ") + renderType(superClassType) + } + if (interfaces.isNotEmpty()) { + text(" ") + keyword("implements") + text(" ") + renderList(interfaces.filterNotNull()) { + renderType(it) + } + } + } + + private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) { + renderList(nodes) { + renderParameter(it) + } + } + + private fun ContentBlock.renderFunction( + node: DocumentationNode, + renderMode: RenderMode + ) { + renderModifiersForNode(node, renderMode) + when (node.kind) { + NodeKind.Constructor -> identifier(node.owner?.name ?: "") + NodeKind.Function -> { + renderTypeParametersForNode(node) + renderType(node.detail(NodeKind.Type)) + text(" ") + identifier(node.name) + + } + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + + val receiver = node.details(NodeKind.Receiver).singleOrNull() + symbol("(") + if (receiver != null) + renderParameters(listOf(receiver) + node.details(NodeKind.Parameter)) + else + renderParameters(node.details(NodeKind.Parameter)) + + symbol(")") + } + + private fun ContentBlock.renderProperty(node: DocumentationNode) { + + when (node.kind) { + NodeKind.Property -> { + keyword("val") + text(" ") + } + else -> throw IllegalArgumentException("Node $node is not a property") + } + renderTypeParametersForNode(node) + val receiver = node.details(NodeKind.Receiver).singleOrNull() + if (receiver != null) { + renderType(receiver.detail(NodeKind.Type)) + symbol(".") + } + + identifier(node.name) + symbol(":") + text(" ") + renderType(node.detail(NodeKind.Type)) + + } + + private fun ContentBlock.renderField(node: DocumentationNode, renderMode: RenderMode) { + renderModifiersForNode(node, renderMode) + renderType(node.detail(NodeKind.Type)) + text(" ") + identifier(node.name) + } +} diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt new file mode 100644 index 000000000..4cb0ac39c --- /dev/null +++ b/core/src/main/kotlin/Locations/Location.kt @@ -0,0 +1,61 @@ +package org.jetbrains.dokka + +import java.io.File + +interface Location { + val path: String get + fun relativePathTo(other: Location, anchor: String? = null): String +} + +/** + * Represents locations in the documentation in the form of [path](File). + * + * $file: [File] for this location + * $path: [String] representing path of this location + */ +data class FileLocation(val file: File) : Location { + override val path: String + get() = file.path + + override fun relativePathTo(other: Location, anchor: String?): String { + if (other !is FileLocation) { + throw IllegalArgumentException("$other is not a FileLocation") + } + if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) { + return "./${file.name}" + } + val ownerFolder = file.parentFile!! + val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/') + return if (anchor == null) relativePath else relativePath + "#" + anchor + } +} + +fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String { + val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() } + return if (!hasMembers) { + // leaf node, use file in owner's folder + parts.joinToString("/") + } else { + parts.joinToString("/") + (if (parts.none()) "" else "/") + "index" + } +} + + +fun relativePathToNode(node: DocumentationNode) = relativePathToNode(node.path.map { it.name }, node.members.any()) + +fun identifierToFilename(path: String): String { + val escaped = path.replace('<', '-').replace('>', '-') + val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } + return if (lowercase == "index") "--index--" else lowercase +} + +fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String { + return location(owner).relativePathTo(location(node), null) +} + +fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File { + val file = File(from.path).parentFile + return root.relativeTo(file) +} + +fun File.toUnixString() = toString().replace(File.separatorChar, '/') diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt new file mode 100644 index 000000000..2c8f7a739 --- /dev/null +++ b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt @@ -0,0 +1,53 @@ +package org.jetbrains.dokka + +import org.intellij.markdown.IElementType +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.ast.ASTNode +import org.intellij.markdown.ast.LeafASTNode +import org.intellij.markdown.ast.getTextInNode +import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor +import org.intellij.markdown.parser.MarkdownParser + +class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { + val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) } + val type: IElementType get() = node.type + val text: String get() = node.getTextInNode(markdown).toString() + fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } + + val previous get() = parent?.children?.getOrNull(parent.children.indexOf(this) - 1) + + override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString() +} + +fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) { + action(this) { + for (child in children) { + child.visit(action) + } + } +} + +fun MarkdownNode.toTestString(): String { + val sb = StringBuilder() + var level = 0 + visit { node, visitChildren -> + sb.append(" ".repeat(level * 2)) + node.presentTo(sb) + sb.appendln() + level++ + visitChildren() + level-- + } + return sb.toString() +} + +private fun MarkdownNode.presentTo(sb: StringBuilder) { + sb.append(type.toString()) + sb.append(":" + text.replace("\n", "\u23CE")) +} + +fun parseMarkdown(markdown: String): MarkdownNode { + if (markdown.isEmpty()) + return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown) + return MarkdownNode(MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown), null, markdown) +} diff --git a/core/src/main/kotlin/Model/CodeNode.kt b/core/src/main/kotlin/Model/CodeNode.kt new file mode 100644 index 000000000..17b506276 --- /dev/null +++ b/core/src/main/kotlin/Model/CodeNode.kt @@ -0,0 +1,42 @@ +package org.jetbrains.dokka.Model + +import org.jsoup.helper.StringUtil.isWhitespace +import org.jsoup.nodes.TextNode + +class CodeNode(text: String, baseUri: String): TextNode(text, baseUri) { + + override fun text(): String { + return normaliseInitialWhitespace(wholeText.removePrefix("<code>") + .removeSuffix("</code>")) + } + + private fun normaliseInitialWhitespace(text: String): String { + val sb = StringBuilder(text.length) + removeInitialWhitespace(sb, text) + return sb.toString() + } + + /** + * Remove initial whitespace. + * @param accum builder to append to + * @param string string to remove the initial whitespace + */ + private fun removeInitialWhitespace(accum: StringBuilder, string: String) { + var reachedNonWhite = false + + val len = string.length + var c: Int + var i = 0 + while (i < len) { + c = string.codePointAt(i) + if (isWhitespace(c) && !reachedNonWhite) { + i += Character.charCount(c) + continue + } else { + accum.appendCodePoint(c) + reachedNonWhite = true + } + i += Character.charCount(c) + } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt new file mode 100644 index 000000000..a4c78fabf --- /dev/null +++ b/core/src/main/kotlin/Model/Content.kt @@ -0,0 +1,296 @@ +package org.jetbrains.dokka + +interface ContentNode { + val textLength: Int +} + +object ContentEmpty : ContentNode { + override val textLength: Int get() = 0 +} + +open class ContentBlock() : ContentNode { + open val children = arrayListOf<ContentNode>() + + fun append(node: ContentNode) { + children.add(node) + } + + fun isEmpty() = children.isEmpty() + + override fun equals(other: Any?): Boolean = + other is ContentBlock && javaClass == other.javaClass && children == other.children + + override fun hashCode(): Int = + children.hashCode() + + override val textLength: Int + get() = children.sumBy { it.textLength } +} + +class NodeRenderContent( + val node: DocumentationNode, + val mode: LanguageService.RenderMode +): ContentNode { + override val textLength: Int + get() = 0 //TODO: Clarify? +} + +class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() { + private var computed = false + override val children: ArrayList<ContentNode> + get() { + if (!computed) { + computed = true + fillChildren(this) + } + return super.children + } + + override fun equals(other: Any?): Boolean { + return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other) + } + + override fun hashCode(): Int { + return super.hashCode() + 31 * fillChildren.hashCode() + } +} + +enum class IdentifierKind { + TypeName, + ParameterName, + AnnotationName, + SummarizedTypeName, + Other +} + +data class ContentText(val text: String) : ContentNode { + override val textLength: Int + get() = text.length +} + +data class ContentKeyword(val text: String) : ContentNode { + override val textLength: Int + get() = text.length +} + +data class ContentIdentifier(val text: String, + val kind: IdentifierKind = IdentifierKind.Other, + val signature: String? = null) : ContentNode { + override val textLength: Int + get() = text.length +} + +data class ContentSymbol(val text: String) : ContentNode { + override val textLength: Int + get() = text.length +} + +data class ContentEntity(val text: String) : ContentNode { + override val textLength: Int + get() = text.length +} + +object ContentNonBreakingSpace: ContentNode { + override val textLength: Int + get() = 1 +} + +object ContentSoftLineBreak: ContentNode { + override val textLength: Int + get() = 0 +} + +object ContentIndentedSoftLineBreak: ContentNode { + override val textLength: Int + get() = 0 +} +class ScriptBlock(val type: String?, val src: String) : ContentBlock() + +class ContentParagraph(val label: String? = null) : ContentBlock() +class ContentEmphasis() : ContentBlock() +class ContentStrong() : ContentBlock() +class ContentStrikethrough() : ContentBlock() +class ContentCode() : ContentBlock() + +class ContentDescriptionList() : ContentBlock() +class ContentDescriptionTerm() : ContentBlock() +class ContentDescriptionDefinition() : ContentBlock() + +class ContentTable() : ContentBlock() +class ContentTableBody() : ContentBlock() +class ContentTableRow() : ContentBlock() +class ContentTableHeader(val colspan: String? = null, val rowspan: String? = null) : ContentBlock() +class ContentTableCell(val colspan: String? = null, val rowspan: String? = null) : ContentBlock() + +class ContentSpecialReference() : ContentBlock() + +open class ContentBlockCode(val language: String = "") : ContentBlock() +class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language) + +abstract class ContentNodeLink() : ContentBlock() { + abstract val node: DocumentationNode? +} + +object ContentHardLineBreak : ContentNode { + override val textLength: Int + get() = 0 +} + +class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() { + override fun equals(other: Any?): Boolean = + super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name + + override fun hashCode(): Int = + children.hashCode() * 31 + node.name.hashCode() +} + +class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() { + override val node: DocumentationNode? get() = lazyNode() + + override fun equals(other: Any?): Boolean = + super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText + + override fun hashCode(): Int = + children.hashCode() * 31 + linkText.hashCode() +} + +class ContentExternalLink(val href : String) : ContentBlock() { + override fun equals(other: Any?): Boolean = + super.equals(other) && other is ContentExternalLink && href == other.href + + override fun hashCode(): Int = + children.hashCode() * 31 + href.hashCode() +} + +data class ContentBookmark(val name: String): ContentBlock() +data class ContentLocalLink(val href: String) : ContentBlock() + +class ContentUnorderedList() : ContentBlock() +class ContentOrderedList() : ContentBlock() +class ContentListItem() : ContentBlock() + +class ContentHeading(val level: Int) : ContentBlock() + +class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() { + override fun equals(other: Any?): Boolean = + super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName + + override fun hashCode(): Int = + children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0) +} + +object ContentTags { + val Description = "Description" + val SeeAlso = "See Also" + val Return = "Return" + val Exceptions = "Exceptions" + val Parameters = "Parameters" +} + +fun content(body: ContentBlock.() -> Unit): ContentBlock { + val block = ContentBlock() + block.body() + return block +} + +fun ContentBlock.text(value: String) = append(ContentText(value)) +fun ContentBlock.keyword(value: String) = append(ContentKeyword(value)) +fun ContentBlock.symbol(value: String) = append(ContentSymbol(value)) + +fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other, signature: String? = null) { + append(ContentIdentifier(value, kind, signature)) +} + +fun ContentBlock.nbsp() = append(ContentNonBreakingSpace) +fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak) +fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak) +fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak) + +fun ContentBlock.strong(body: ContentBlock.() -> Unit) { + val strong = ContentStrong() + strong.body() + append(strong) +} + +fun ContentBlock.code(body: ContentBlock.() -> Unit) { + val code = ContentCode() + code.body() + append(code) +} + +fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) { + val block = if (to.kind == NodeKind.ExternalLink) + ContentExternalLink(to.name) + else + ContentNodeDirectLink(to) + + block.body() + append(block) +} + +open class Content(): ContentBlock() { + open val sections: List<ContentSection> get() = emptyList() + open val summary: ContentNode get() = ContentEmpty + open val description: ContentNode get() = ContentEmpty + + fun findSectionByTag(tag: String): ContentSection? = + sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) } + + companion object { + val Empty = Content() + + fun of(vararg child: ContentNode): Content { + val result = MutableContent() + child.forEach { result.append(it) } + return result + } + } +} + +open class MutableContent() : Content() { + private val sectionList = arrayListOf<ContentSection>() + override val sections: List<ContentSection> + get() = sectionList + + fun addSection(tag: String?, subjectName: String?): ContentSection { + val section = ContentSection(tag ?: "", subjectName) + sectionList.add(section) + return section + } + + override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty + + override val description: ContentNode by lazy { + val descriptionNodes = children.drop(1) + if (descriptionNodes.isEmpty()) { + ContentEmpty + } else { + val result = ContentSection(ContentTags.Description, null) + result.children.addAll(descriptionNodes) + result + } + } + + override fun equals(other: Any?): Boolean { + if (other !is Content) + return false + return sections == other.sections && children == other.children + } + + override fun hashCode(): Int { + return sections.map { it.hashCode() }.sum() + } + + override fun toString(): String { + if (sections.isEmpty()) + return "<empty>" + return (listOf(summary, description) + sections).joinToString() + } +} + +fun javadocSectionDisplayName(sectionName: String?): String? = + when(sectionName) { + "param" -> "Parameters" + "throws", "exception" -> ContentTags.Exceptions + else -> sectionName?.capitalize() + } diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt new file mode 100644 index 000000000..7ac6d8f4a --- /dev/null +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -0,0 +1,311 @@ +package org.jetbrains.dokka + +import java.util.* + +enum class NodeKind { + Unknown, + + Package, + Class, + Interface, + Enum, + AnnotationClass, + Exception, + EnumItem, + Object, + TypeAlias, + + Constructor, + Function, + Property, + Field, + + CompanionObjectProperty, + CompanionObjectFunction, + + Parameter, + Receiver, + TypeParameter, + Type, + Supertype, + UpperBound, + LowerBound, + + TypeAliasUnderlyingType, + + Modifier, + NullabilityModifier, + + Module, + + ExternalClass, + Annotation, + + Value, + + SourceUrl, + SourcePosition, + Signature, + + ExternalLink, + QualifiedName, + Platform, + + AllTypes, + + /** + * A note which is rendered once on a page documenting a group of overloaded functions. + * Needs to be generated equally on all overloads. + */ + OverloadGroupNote, + + Attribute, + + AttributeRef, + + ApiLevel, + SdkExtSince, + + DeprecatedLevel, + + ArtifactId, + + GroupNode; + + companion object { + val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) + val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem, Attribute) + } +} + +open class DocumentationNode(val name: String, + content: Content, + val kind: NodeKind) { + + private val references = LinkedHashSet<DocumentationReference>() + + var content: Content = content + private set + + val summary: ContentNode get() = content.summary + + val owner: DocumentationNode? + get() = references(RefKind.Owner).singleOrNull()?.to + val details: List<DocumentationNode> + get() = references(RefKind.Detail).map { it.to } + val members: List<DocumentationNode> + get() = references(RefKind.Member).map { it.to }.sortedBy { it.name } + val inheritedMembers: List<DocumentationNode> + get() = references(RefKind.InheritedMember).map { it.to } + val allInheritedMembers: List<DocumentationNode> + get() = recursiveInheritedMembers().sortedBy { it.name } + val inheritedCompanionObjectMembers: List<DocumentationNode> + get() = references(RefKind.InheritedCompanionObjectMember).map { it.to } + val extensions: List<DocumentationNode> + get() = references(RefKind.Extension).map { it.to } + val inheritors: List<DocumentationNode> + get() = references(RefKind.Inheritor).map { it.to } + val overrides: List<DocumentationNode> + get() = references(RefKind.Override).map { it.to } + val links: List<DocumentationNode> + get() = references(RefKind.Link).map { it.to } + val hiddenLinks: List<DocumentationNode> + get() = references(RefKind.HiddenLink).map { it.to } + val annotations: List<DocumentationNode> + get() = references(RefKind.Annotation).map { it.to } + val deprecation: DocumentationNode? + get() = references(RefKind.Deprecation).map { it.to }.firstOrNull() + val platforms: List<String> + get() = references(RefKind.Platform).map { it.to.name } + val externalType: DocumentationNode? + get() = references(RefKind.ExternalType).map { it.to }.firstOrNull() + val apiLevel: DocumentationNode + get() = detailOrNull(NodeKind.ApiLevel) ?: DocumentationNode("", Content.Empty, NodeKind.ApiLevel) + val sdkExtSince: DocumentationNode + get() = detailOrNull(NodeKind.SdkExtSince) ?: DocumentationNode("", Content.Empty, NodeKind.SdkExtSince) + val deprecatedLevel: DocumentationNode + get() = detailOrNull(NodeKind.DeprecatedLevel) ?: DocumentationNode("", Content.Empty, NodeKind.DeprecatedLevel) + val artifactId: DocumentationNode + get() = detailOrNull(NodeKind.ArtifactId) ?: DocumentationNode("", Content.Empty, NodeKind.ArtifactId) + val attributes: List<DocumentationNode> + get() = details(NodeKind.Attribute).sortedBy { it.attributeRef!!.name } + val attributeRef: DocumentationNode? + get() = references(RefKind.AttributeRef).map { it.to }.firstOrNull() + val relatedAttributes: List<DocumentationNode> + get() = hiddenLinks(NodeKind.Attribute) + val supertypes: List<DocumentationNode> + get() = details(NodeKind.Supertype) + val signatureName = detailOrNull(NodeKind.Signature)?.name + + val prettyName : String + get() = when(kind) { + NodeKind.Constructor -> owner!!.name + else -> name + } + + val superclassType: DocumentationNode? + get() = when (kind) { + NodeKind.Supertype -> { + (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType + } + NodeKind.Interface -> null + in NodeKind.classLike -> supertypes.firstOrNull { + (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) } + } + else -> null + } + + val superclassTypeSequence: Sequence<DocumentationNode> + get() = generateSequence(superclassType) { + it.superclassType + } + + // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice + fun addReferenceTo(to: DocumentationNode, kind: RefKind) { + references.add(DocumentationReference(this, to, kind)) + } + + fun dropReferences(predicate: (DocumentationReference) -> Boolean) { + references.removeAll(predicate) + } + + fun addAllReferencesFrom(other: DocumentationNode) { + references.addAll(other.references) + } + + fun updateContent(body: MutableContent.() -> Unit) { + if (content !is MutableContent) { + content = MutableContent() + } + (content as MutableContent).body() + } + fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind } + fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind } + fun hiddenLinks(kind: NodeKind): List<DocumentationNode> = hiddenLinks.filter { it.kind == kind } + fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind } + fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind } + fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind } + + fun detail(kind: NodeKind): DocumentationNode = details.filter { it.kind == kind }.single() + fun detailOrNull(kind: NodeKind): DocumentationNode? = details.filter { it.kind == kind }.singleOrNull() + fun member(kind: NodeKind): DocumentationNode = members.filter { it.kind == kind }.single() + fun link(kind: NodeKind): DocumentationNode = links.filter { it.kind == kind }.single() + + fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind } + fun allReferences(): Set<DocumentationReference> = references + + override fun toString(): String { + return "$kind:$name" + } +} + +class DocumentationModule(name: String, content: Content = Content.Empty) + : DocumentationNode(name, content, NodeKind.Module) { + val nodeRefGraph = NodeReferenceGraph() +} + +val DocumentationNode.path: List<DocumentationNode> + get() { + val parent = owner ?: return listOf(this) + return parent.path + this + } + +fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode { + val existingNode = refGraph.lookup(packageName) + if (existingNode != null) { + return existingNode + } + val newNode = DocumentationNode(packageName, + packageContent.getOrElse(packageName) { Content.Empty }, + NodeKind.Package) + + refGraph.register(packageName, newNode) + module?.append(newNode, RefKind.Member) + return newNode +} + +fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) { + addReferenceTo(child, kind) + when (kind) { + RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner) + RefKind.Member -> child.addReferenceTo(this, RefKind.Owner) + RefKind.Owner -> child.addReferenceTo(this, RefKind.Member) + else -> { /* Do not add any links back for other types */ + } + } +} + +fun DocumentationNode.appendTextNode(text: String, + kind: NodeKind, + refKind: RefKind = RefKind.Detail) { + append(DocumentationNode(text, Content.Empty, kind), refKind) +} + +fun DocumentationNode.qualifiedName(): String { + if (kind == NodeKind.Type) { + return qualifiedNameFromType() + } else if (kind == NodeKind.Package) { + return name + } + return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".") +} + +fun DocumentationNode.simpleName() = name.substringAfterLast('.') + +private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> { + val allInheritedMembers = mutableListOf<DocumentationNode>() + recursiveInheritedMembers(allInheritedMembers) + return allInheritedMembers +} + +private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) { + allInheritedMembers.addAll(inheritedMembers) + inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) -> + node.recursiveInheritedMembers(allInheritedMembers) + } +} + +private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean { + return when(node.kind) { + NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class + NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception + else -> false + } +} + +fun DocumentationNode.classNodeNameWithOuterClass(): String { + assert(kind in NodeKind.classLike) + return path.dropWhile { it.kind !in NodeKind.classLike }.joinToString(separator = ".") { it.name } +} + +fun DocumentationNode.deprecatedLevelMessage(): String { + val kindName = when(kind) { + NodeKind.Enum -> "enum" + NodeKind.Interface -> "interface" + NodeKind.AnnotationClass -> "annotation" + NodeKind.Exception -> "exception" + else -> "class" + } + return "This $kindName was deprecated in API level ${deprecatedLevel.name}." +} + +fun DocumentationNode.convertDeprecationDetailsToChildren() { + val toProcess = details.toMutableList() + while (!toProcess.isEmpty()) { + var child = toProcess.removeAt(0) + if (child.details.isEmpty() && child.name != "") { + updateContent { text(child.name.cleanForAppending()) } + } + else toProcess.addAll(0, child.details) + } +} + + /* + * Removes extraneous quotation marks and adds a ". " to make appending children readable. + */ +fun String.cleanForAppending(): String { + var result = this + if (this.first() == this.last() && this.first() == '"') result = result.substring(1, result.length - 1) + if (result[result.length - 2] != '.' && result.last() != ' ') result += ". " + return result +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt new file mode 100644 index 000000000..cc582a0fd --- /dev/null +++ b/core/src/main/kotlin/Model/DocumentationReference.kt @@ -0,0 +1,86 @@ +package org.jetbrains.dokka + +import com.google.inject.Singleton + +enum class RefKind { + Owner, + Member, + InheritedMember, + InheritedCompanionObjectMember, + Detail, + Link, + HiddenLink, + Extension, + Inheritor, + Superclass, + Override, + Annotation, + HiddenAnnotation, + Deprecation, + TopLevelPage, + Platform, + ExternalType, + AttributeRef, + AttributeSource +} + +data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) { +} + +class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?, + val lazyNodeTo: () -> DocumentationNode?, + val kind: RefKind) { + fun resolve() { + val fromNode = lazyNodeFrom() + val toNode = lazyNodeTo() + if (fromNode != null && toNode != null) { + fromNode.addReferenceTo(toNode, kind) + } + } +} + +class NodeReferenceGraph() { + private val nodeMap = hashMapOf<String, DocumentationNode>() + val references = arrayListOf<PendingDocumentationReference>() + + fun register(signature: String, node: DocumentationNode) { + nodeMap.put(signature, node) + } + + fun link(fromNode: DocumentationNode, toSignature: String, kind: RefKind) { + references.add(PendingDocumentationReference({ -> fromNode}, { -> nodeMap[toSignature]}, kind)) + } + + fun link(fromSignature: String, toNode: DocumentationNode, kind: RefKind) { + references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> toNode}, kind)) + } + + fun link(fromSignature: String, toSignature: String, kind: RefKind) { + references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind)) + } + + fun lookup(signature: String) = nodeMap[signature] + + fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? { + val result = nodeMap[signature] + if (result == null) { + logger.warn("Can't find node by signature `$signature`") + } + return result + } + + fun resolveReferences() { + references.forEach { it.resolve() } + } +} + +@Singleton +class PlatformNodeRegistry { + private val platformNodes = hashMapOf<String, DocumentationNode>() + + operator fun get(platform: String): DocumentationNode { + return platformNodes.getOrPut(platform) { + DocumentationNode(platform, Content.Empty, NodeKind.Platform) + } + } +} diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt new file mode 100644 index 000000000..e8fdde6e3 --- /dev/null +++ b/core/src/main/kotlin/Model/ElementSignatureProvider.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor + +interface ElementSignatureProvider { + fun signature(forDesc: DeclarationDescriptor): String + fun signature(forPsi: PsiElement): String +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt new file mode 100644 index 000000000..5b6289146 --- /dev/null +++ b/core/src/main/kotlin/Model/PackageDocs.kt @@ -0,0 +1,135 @@ +package org.jetbrains.dokka + +import com.google.inject.Inject +import com.google.inject.Singleton +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiFileFactory +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.LocalTimeCounter +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.intellij.markdown.parser.LinkMap +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import java.io.File + +@Singleton +class PackageDocs + @Inject constructor(val linkResolver: DeclarationLinkResolver?, + val logger: DokkaLogger, + val environment: KotlinCoreEnvironment, + val refGraph: NodeReferenceGraph, + val elementSignatureProvider: ElementSignatureProvider) +{ + val moduleContent: MutableContent = MutableContent() + private val _packageContent: MutableMap<String, MutableContent> = hashMapOf() + val packageContent: Map<String, Content> + get() = _packageContent + + fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) { + val file = File(fileName) + if (file.exists()) { + val text = file.readText() + val tree = parseMarkdown(text) + val linkMap = LinkMap.buildLinkMap(tree.node, text) + var targetContent: MutableContent = moduleContent + tree.children.forEach { + if (it.type == MarkdownElementTypes.ATX_1) { + val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text + if (headingText != null) { + targetContent = findTargetContent(headingText.trimStart()) + } + } else { + buildContentTo(it, targetContent, LinkResolver(linkMap, { resolveContentLink(fileName, it, linkResolveContext) })) + } + } + } else { + logger.warn("Include file $file was not found.") + } + } + + private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) { + val javadocText = text + .replace("*/", "*/") + .removeSurrounding("<html>", "</html>", true).trim() + .removeSurrounding("<body>", "</body>", true) + .lineSequence() + .map { "* $it" } + .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/") + parseJavadoc(javadocText, packageName, file) + } + + private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence { + if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) { + return subSequence(prefix.length, length - suffix.length) + } + return subSequence(0, length) + } + + + private fun parseJavadoc(text: String, packageName: String, file: File) { + + val psiFileFactory = PsiFileFactory.getInstance(environment.project) + val psiFile = psiFileFactory.createFileFromText( + file.nameWithoutExtension + ".java", + JavaFileType.INSTANCE, + "package $packageName; $text\npublic class C {}", + LocalTimeCounter.currentTime(), + false, + true + ) + + val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!! + val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!) + findOrCreatePackageContent(packageName).apply { + val content = parser.parseDocumentation(psiClass).content + children.addAll(content.children) + content.sections.forEach { + addSection(it.tag, it.subjectName).children.addAll(it.children) + } + } + } + + + fun parseJava(fileName: String, packageName: String) { + val file = File(fileName) + if (file.exists()) { + val text = file.readText() + + val trimmedText = text.trim() + + if (trimmedText.startsWith("/**")) { + parseJavadoc(text, packageName, file) + } else if (trimmedText.toLowerCase().startsWith("<html>")) { + parseHtmlAsJavadoc(trimmedText, packageName, file) + } + } + } + + private fun findTargetContent(heading: String): MutableContent { + if (heading.startsWith("Module") || heading.startsWith("module")) { + return moduleContent + } + if (heading.startsWith("Package") || heading.startsWith("package")) { + return findOrCreatePackageContent(heading.substring("package".length).trim()) + } + return findOrCreatePackageContent(heading) + } + + private fun findOrCreatePackageContent(packageName: String) = + _packageContent.getOrPut(packageName) { -> MutableContent() } + + private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock { + if (linkResolver != null) { + linkResolveContext + .asSequence() + .map { p -> linkResolver.tryResolveContentLink(p, href) } + .filterNotNull() + .firstOrNull() + ?.let { return it } + } + logger.warn("Unresolved link to `$href` in include ($fileName)") + return ContentExternalLink("#") + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt new file mode 100644 index 000000000..2c75cfdac --- /dev/null +++ b/core/src/main/kotlin/Model/SourceLinks.kt @@ -0,0 +1,56 @@ +package org.jetbrains.dokka + +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import java.io.File + + +fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) { + val path = psi?.containingFile?.virtualFile?.path ?: return + + val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi + val absPath = File(path).absolutePath + val linkDef = sourceLinks.firstOrNull { absPath.startsWith(it.path) } + if (linkDef != null) { + var url = linkDef.url + path.substring(linkDef.path.length) + if (linkDef.lineSuffix != null) { + val line = target?.lineNumber() + if (line != null) { + url += linkDef.lineSuffix + line.toString() + } + } + append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), + RefKind.Detail); + } + + if (target != null) { + append(DocumentationNode(target.sourcePosition(), Content.Empty, NodeKind.SourcePosition), RefKind.Detail) + } +} + +private fun PsiElement.sourcePosition(): String { + val path = containingFile.virtualFile.path + val lineNumber = lineNumber() + val columnNumber = columnNumber() + + return when { + lineNumber == null -> path + columnNumber == null -> "$path:$lineNumber" + else -> "$path:$lineNumber:$columnNumber" + } +} + +fun PsiElement.lineNumber(): Int? { + val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) + // IJ uses 0-based line-numbers; external source browsers use 1-based + return doc?.getLineNumber(textRange.startOffset)?.plus(1) +} + +fun PsiElement.columnNumber(): Int? { + val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) ?: return null + val lineNumber = doc.getLineNumber(textRange.startOffset) + return startOffset - doc.getLineStartOffset(lineNumber) +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt new file mode 100644 index 000000000..116a5c02f --- /dev/null +++ b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.Samples + +import com.google.inject.Inject +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtDeclarationWithBody +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.ResolutionScope + + +open class DefaultSampleProcessingService +@Inject constructor(val options: DocumentationOptions, + val logger: DokkaLogger, + val resolutionFacade: DokkaResolutionFacade) + : SampleProcessingService { + + override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode { + if (functionName == null) { + logger.warn("Missing function name in @sample in ${descriptor.signature()}") + return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) } + } + val bindingContext = BindingContext.EMPTY + val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull() + if (symbol == null) { + logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}") + return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) } + } + val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol) + if (psiElement == null) { + logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}") + return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) } + } + + val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() + val lines = text.split("\n") + val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 + val finalText = lines.map { it.drop(indent) }.joinToString("\n") + + return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) } + } + + protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) { + is KtDeclarationWithBody -> { + val bodyExpression = psiElement.bodyExpression + when (bodyExpression) { + is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") + else -> bodyExpression!!.text + } + } + else -> psiElement.text + } + + protected open fun processImports(psiElement: PsiElement): ContentBlockCode { + val psiFile = psiElement.containingFile + if (psiFile is KtFile) { + return ContentBlockCode("kotlin").apply { + append(ContentText(psiFile.importList?.text ?: "")) + } + } else { + return ContentBlockCode("") + } + } + + private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? { + var currentScope = scope + val parts = functionName.split('.') + + var symbol: DeclarationDescriptor? = null + + for (part in parts) { + // short name + val symbolName = Name.identifier(part) + val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL, { it == symbolName }) + .filter { it.name == symbolName } + .firstOrNull() + + if (partSymbol == null) { + symbol = null + break + } + @Suppress("IfThenToElvis") + currentScope = if (partSymbol is ClassDescriptor) + partSymbol.defaultType.memberScope + else if (partSymbol is PackageViewDescriptor) + partSymbol.memberScope + else + getKDocLinkResolutionScope(resolutionFacade, partSymbol) + symbol = partSymbol + } + + return symbol + } +} + diff --git a/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt new file mode 100644 index 000000000..33d6cfeba --- /dev/null +++ b/core/src/main/kotlin/Samples/DevsiteSampleProcessingService.kt @@ -0,0 +1,52 @@ +package org.jetbrains.dokka.Samples + +import com.google.inject.Inject +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.addIfNotNull + +open class DevsiteSampleProcessingService +@Inject constructor( + options: DocumentationOptions, + logger: DokkaLogger, + resolutionFacade: DokkaResolutionFacade +) : DefaultSampleProcessingService(options, logger, resolutionFacade) { + + override fun processImports(psiElement: PsiElement): ContentBlockCode { + // List of expression calls inside this sample, so we can trim the imports to only show relevant expressions + val sampleExpressionCalls = mutableSetOf<String>() + val psiFile = psiElement.containingFile + (psiElement as KtDeclarationWithBody).bodyExpression!!.accept(object : KtTreeVisitorVoid() { + override fun visitCallExpression(expression: KtCallExpression) { + sampleExpressionCalls.addIfNotNull(expression.calleeExpression?.text) + super.visitCallExpression(expression) + } + }) + val androidxPackage = Name.identifier("androidx") + if (psiFile is KtFile) { + val filteredImports = psiFile.importList?.imports?.filter { element -> + val fqImportName = element.importPath?.fqName ?: return@filter false + + val shortName = fqImportName.shortName().identifier + // Hide all non-androidx imports + if (!fqImportName.startsWith(androidxPackage)) return@filter false + + sampleExpressionCalls.any { call -> + call == shortName + } + } + + return ContentBlockCode("kotlin").apply { + filteredImports?.forEach { import -> + if (import != filteredImports.first()) { + append(ContentText("\n")) + } + append(ContentText(import.text)) + } + } + } + return super.processImports(psiElement) + } +} diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt new file mode 100644 index 000000000..b0988c352 --- /dev/null +++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt @@ -0,0 +1,137 @@ +package org.jetbrains.dokka.Samples + +import com.google.inject.Inject +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren +import org.jetbrains.kotlin.psi.psiUtil.prevLeaf +import org.jetbrains.kotlin.resolve.ImportPath + +open class KotlinWebsiteSampleProcessingService +@Inject constructor(options: DocumentationOptions, + logger: DokkaLogger, + resolutionFacade: DokkaResolutionFacade) + : DefaultSampleProcessingService(options, logger, resolutionFacade) { + + private class SampleBuilder : KtTreeVisitorVoid() { + val builder = StringBuilder() + val text: String + get() = builder.toString() + + fun KtValueArgument.extractStringArgumentValue() = + (getArgumentExpression() as KtStringTemplateExpression) + .entries.joinToString("") { it.text } + + + fun convertAssertPrints(expression: KtCallExpression) { + val (argument, commentArgument) = expression.valueArguments + builder.apply { + append("println(") + append(argument.text) + append(") // ") + append(commentArgument.extractStringArgumentValue()) + } + } + + fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { + val (argument) = expression.valueArguments + builder.apply { + expression.valueArguments.getOrNull(1)?.let { + append("// ${it.extractStringArgumentValue()}") + val ws = expression.prevLeaf { it is PsiWhiteSpace } + append(ws?.text ?: "\n") + } + append("println(\"") + append(argument.text) + append(" is \${") + append(argument.text) + append("}\") // $expectedResult") + } + } + + fun convertAssertFails(expression: KtCallExpression) { + val (message, funcArgument) = expression.valueArguments + builder.apply { + val argument = if (funcArgument.getArgumentExpression() is KtLambdaExpression) + PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: "" + else + funcArgument.text + append(argument.lines().joinToString(separator = "\n") { "// $it" }) + append(" // ") + append(message.extractStringArgumentValue()) + append(" will fail") + } + } + + fun convertAssertFailsWith(expression: KtCallExpression) { + val (funcArgument) = expression.valueArguments + val (exceptionType) = expression.typeArguments + builder.apply { + val argument = if (funcArgument.firstChild is KtLambdaExpression) + PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: "" + else + funcArgument.text + append(argument.lines().joinToString(separator = "\n") { "// $it" }) + append(" // will fail with ") + append(exceptionType.text) + } + } + + override fun visitCallExpression(expression: KtCallExpression) { + when (expression.calleeExpression?.text) { + "assertPrints" -> convertAssertPrints(expression) + "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) + "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) + "assertFails" -> convertAssertFails(expression) + "assertFailsWith" -> convertAssertFailsWith(expression) + else -> super.visitCallExpression(expression) + } + } + + override fun visitElement(element: PsiElement) { + if (element is LeafPsiElement) + builder.append(element.text) + super.visitElement(element) + } + } + + private fun PsiElement.buildSampleText(): String { + val sampleBuilder = SampleBuilder() + this.accept(sampleBuilder) + return sampleBuilder.text + } + + val importsToIgnore = arrayOf("samples.*").map { ImportPath.fromString(it) } + + override fun processImports(psiElement: PsiElement): ContentBlockCode { + val psiFile = psiElement.containingFile + if (psiFile is KtFile) { + return ContentBlockCode("kotlin").apply { + append(ContentText("\n")) + psiFile.importList?.let { + it.allChildren.filter { + it !is KtImportDirective || it.importPath !in importsToIgnore + }.forEach { append(ContentText(it.text)) } + } + } + } + return super.processImports(psiElement) + } + + override fun processSampleBody(psiElement: PsiElement) = when (psiElement) { + is KtDeclarationWithBody -> { + val bodyExpression = psiElement.bodyExpression + val bodyExpressionText = bodyExpression!!.buildSampleText() + when (bodyExpression) { + is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") + else -> bodyExpressionText + } + } + else -> psiElement.buildSampleText() + } +} + diff --git a/core/src/main/kotlin/Samples/SampleProcessingService.kt b/core/src/main/kotlin/Samples/SampleProcessingService.kt new file mode 100644 index 000000000..86c917cf5 --- /dev/null +++ b/core/src/main/kotlin/Samples/SampleProcessingService.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.Samples + +import org.jetbrains.dokka.ContentNode +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag + +interface SampleProcessingService { + fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/DokkaLogging.kt b/core/src/main/kotlin/Utilities/DokkaLogging.kt new file mode 100644 index 000000000..1ef528378 --- /dev/null +++ b/core/src/main/kotlin/Utilities/DokkaLogging.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka + +interface DokkaLogger { + fun info(message: String) + fun warn(message: String) + fun error(message: String) +} + +object DokkaConsoleLogger : DokkaLogger { + var warningCount: Int = 0 + + override fun info(message: String) = println(message) + override fun warn(message: String) { + println("WARN: $message") + warningCount++ + } + + override fun error(message: String) = println("ERROR: $message") + + fun report() { + if (warningCount > 0) { + println("generation completed with $warningCount warnings") + } else { + println("generation completed successfully") + } + } +} diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt new file mode 100644 index 000000000..7c8e5c347 --- /dev/null +++ b/core/src/main/kotlin/Utilities/DokkaModules.kt @@ -0,0 +1,81 @@ +package org.jetbrains.dokka.Utilities + +import com.google.inject.Binder +import com.google.inject.Module +import com.google.inject.TypeLiteral +import com.google.inject.binder.AnnotatedBindingBuilder +import com.google.inject.name.Names +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Formats.FormatDescriptor +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import java.io.File +import kotlin.reflect.KClass + +const val impliedPlatformsName = "impliedPlatforms" + +class DokkaAnalysisModule(val environment: AnalysisEnvironment, + val options: DocumentationOptions, + val defaultPlatformsProvider: DefaultPlatformsProvider, + val nodeReferenceGraph: NodeReferenceGraph, + val logger: DokkaLogger) : Module { + override fun configure(binder: Binder) { + binder.bind<DokkaLogger>().toInstance(logger) + + val coreEnvironment = environment.createCoreEnvironment() + binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) + + val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment) + binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) + binder.bind<DokkaResolutionFacade>().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade) + + binder.bind<DocumentationOptions>().toInstance(options) + + binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider) + + binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph) + + val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat) + descriptor.configureAnalysis(binder) + } +} + +object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>() + +class DokkaOutputModule(val options: DocumentationOptions, + val logger: DokkaLogger) : Module { + override fun configure(binder: Binder) { + binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir)) + + binder.bind<DocumentationOptions>().toInstance(options) + binder.bind<DokkaLogger>().toInstance(logger) + binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms) + binder.bind<String>().annotatedWith(Names.named("outlineRoot")).toInstance(options.outlineRoot) + binder.bind<String>().annotatedWith(Names.named("dacRoot")).toInstance(options.dacRoot) + binder.bind<Boolean>().annotatedWith(Names.named("generateClassIndex")).toInstance(options.generateClassIndexPage) + binder.bind<Boolean>().annotatedWith(Names.named("generatePackageIndex")).toInstance(options.generatePackageIndexPage) + val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat) + + descriptor.configureOutput(binder) + } +} + +private inline fun <reified T: Any> Binder.registerCategory(category: String) { + ServiceLocator.allServices(category).forEach { + @Suppress("UNCHECKED_CAST") + bind(T::class.java).annotatedWith(Names.named(it.name)).to(T::class.java.classLoader.loadClass(it.className) as Class<T>) + } +} + +private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotated(name: String) { + bind(Base::class.java).annotatedWith(Names.named(name)).to(T::class.java) +} + + +inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java) + +inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) } + +inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) = + kClass?.let { value toType it } + +inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java) diff --git a/core/src/main/kotlin/Utilities/DownloadSamples.kt b/core/src/main/kotlin/Utilities/DownloadSamples.kt new file mode 100644 index 000000000..3c28d6cc7 --- /dev/null +++ b/core/src/main/kotlin/Utilities/DownloadSamples.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.dokka.Utilities + +import okhttp3.OkHttpClient +import okhttp3.Request +import java.io.File +import java.io.FileOutputStream + +object DownloadSamples { + + /** HTTP Client to make requests **/ + val client = OkHttpClient() + + /** + * Function that downloads samples based on the directory structure described in hashmap + */ + fun downloadSamples(): Boolean { + + //loop through each directory of AOSP code in SamplesPathsToURLs.kt + filepathsToUrls.forEach { (filepath, url) -> + + //build request using each URL + val request = Request.Builder() + .url(url) + .build() + + val response = client.newCall(request).execute() + + if (response.isSuccessful) { + + //save .tar.gz file to filepath designated by map + val currentFile = File(filepath) + currentFile.mkdirs() + + val fos = FileOutputStream("$filepath.tar.gz") + fos.write(response.body?.bytes()) + fos.close() + + //Unzip, Untar, and delete compressed file after + extractFiles(filepath) + + } else { + println("Error Downloading Samples: $response") + return false + } + } + + println("Successfully completed download of samples.") + return true + + } + + /** + * Execute bash commands to extract file, then delete archive file + */ + private fun extractFiles(pathToFile: String) { + + ProcessBuilder() + .command("tar","-zxf", "$pathToFile.tar.gz", "-C", pathToFile) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start() + .waitFor() + + ProcessBuilder() + .command("rm", "$pathToFile.tar.gz") + .redirectError(ProcessBuilder.Redirect.INHERIT) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .start() + .waitFor() + } + +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/Utilities/Html.kt new file mode 100644 index 000000000..d9463c595 --- /dev/null +++ b/core/src/main/kotlin/Utilities/Html.kt @@ -0,0 +1,18 @@ +package org.jetbrains.dokka + +import java.net.URI + + +/** + * Replaces symbols reserved in HTML with their respective entities. + * Replaces & with &, < with < and > with > + */ +fun String.htmlEscape(): String = replace("&", "&").replace("<", "<").replace(">", ">") + +// A URI consists of several parts (as described in https://docs.oracle.com/javase/7/docs/api/java/net/URI.html ): +// [scheme:][//authority][path][?query][#fragment] +// +// The anchorEnchoded() function encodes the given string to make it a legal value for <fragment> +fun String.anchorEncoded(): String { + return URI(null, null, this).getRawFragment() +} diff --git a/core/src/main/kotlin/Utilities/Path.kt b/core/src/main/kotlin/Utilities/Path.kt new file mode 100644 index 000000000..058384993 --- /dev/null +++ b/core/src/main/kotlin/Utilities/Path.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka + +import java.io.File + +fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(path + "." + extension) diff --git a/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt b/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt new file mode 100644 index 000000000..173389d5e --- /dev/null +++ b/core/src/main/kotlin/Utilities/SamplesPathsToURLs.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.dokka.Utilities + +//HashMap of all filepaths to the URLs that should be downloaded to that filepath +val filepathsToUrls: HashMap<String, String> = hashMapOf( + "./samples/development/samples/ApiDemos" to "https://android.googlesource.com/platform/development/+archive/refs/heads/master/samples/ApiDemos.tar.gz", + "./samples/development/samples/NotePad" to "https://android.googlesource.com/platform/development/+archive/refs/heads/master/samples/NotePad.tar.gz", + "./samples/external/icu/android_icu4j/src/samples/java/android/icu/samples/text" to "https://android.googlesource.com/platform/external/icu/+archive/refs/heads/master/android_icu4j/src/samples/java/android/icu/samples/text.tar.gz", + "./samples/frameworks/base/core/java/android/content" to "https://android.googlesource.com/platform/frameworks/base/+archive/refs/heads/master/core/java/android/content.tar.gz", + "./samples/frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost" to "https://android.googlesource.com/platform/frameworks/base/+archive/refs/heads/master/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost.tar.gz" + ) diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/Utilities/ServiceLocator.kt new file mode 100644 index 000000000..83c4c65c1 --- /dev/null +++ b/core/src/main/kotlin/Utilities/ServiceLocator.kt @@ -0,0 +1,100 @@ +package org.jetbrains.dokka.Utilities + +import java.io.File +import java.net.URISyntaxException +import java.net.URL +import java.util.* +import java.util.jar.JarFile +import java.util.zip.ZipEntry + +data class ServiceDescriptor(val name: String, val category: String, val description: String?, val className: String) + +class ServiceLookupException(message: String) : Exception(message) + +object ServiceLocator { + fun <T : Any> lookup(clazz: Class<T>, category: String, implementationName: String): T { + val descriptor = lookupDescriptor(category, implementationName) + return lookup(clazz, descriptor) + } + + fun <T : Any> lookup( + clazz: Class<T>, + descriptor: ServiceDescriptor + ): T { + val loadedClass = javaClass.classLoader.loadClass(descriptor.className) + val constructor = loadedClass.constructors + .filter { it.parameterTypes.isEmpty() } + .firstOrNull() + ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor") + + val implementationRawType: Any = + if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor) + + if (!clazz.isInstance(implementationRawType)) { + throw ServiceLookupException("Class ${descriptor.className} is not a subtype of ${clazz.name}") + } + + @Suppress("UNCHECKED_CAST") + return implementationRawType as T + } + + private fun lookupDescriptor(category: String, implementationName: String): ServiceDescriptor { + val properties = javaClass.classLoader.getResourceAsStream("dokka/$category/$implementationName.properties")?.use { stream -> + Properties().let { properties -> + properties.load(stream) + properties + } + } ?: throw ServiceLookupException("No implementation with name $implementationName found in category $category") + + val className = properties["class"]?.toString() ?: throw ServiceLookupException("Implementation $implementationName has no class configured") + + return ServiceDescriptor(implementationName, category, properties["description"]?.toString(), className) + } + + fun URL.toFile(): File { + assert(protocol == "file") + + return try { + File(toURI()) + } catch (e: URISyntaxException) { //Try to handle broken URLs, with unescaped spaces + File(path) + } + } + + fun allServices(category: String): List<ServiceDescriptor> { + val entries = this.javaClass.classLoader.getResources("dokka/$category")?.toList() ?: emptyList() + + return entries.flatMap { + when (it.protocol) { + "file" -> it.toFile().listFiles()?.filter { it.extension == "properties" }?.map { lookupDescriptor(category, it.nameWithoutExtension) } ?: emptyList() + "jar" -> { + val file = JarFile(URL(it.file.substringBefore("!")).toFile()) + try { + val jarPath = it.file.substringAfterLast("!").removePrefix("/") + file.entries() + .asSequence() + .filter { entry -> !entry.isDirectory && entry.path == jarPath && entry.extension == "properties" } + .map { entry -> + lookupDescriptor(category, entry.fileName.substringBeforeLast(".")) + }.toList() + } finally { + file.close() + } + } + else -> emptyList<ServiceDescriptor>() + } + } + } +} + +inline fun <reified T : Any> ServiceLocator.lookup(category: String, implementationName: String): T = lookup(T::class.java, category, implementationName) +inline fun <reified T : Any> ServiceLocator.lookup(desc: ServiceDescriptor): T = lookup(T::class.java, desc) + +private val ZipEntry.fileName: String + get() = name.substringAfterLast("/", name) + +private val ZipEntry.path: String + get() = name.substringBeforeLast("/", "").removePrefix("/") + +private val ZipEntry.extension: String? + get() = fileName.let { fn -> if ("." in fn) fn.substringAfterLast(".") else null } diff --git a/core/src/main/kotlin/Utilities/StringExtensions.kt b/core/src/main/kotlin/Utilities/StringExtensions.kt new file mode 100644 index 000000000..98f8c8036 --- /dev/null +++ b/core/src/main/kotlin/Utilities/StringExtensions.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.dokka.Utilities + +/** + * Finds the first sentence of a string, accounting for periods that may occur in parenthesis. + */ +fun String.firstSentence(): String { + + // First, search for location of first period and first parenthesis. + val firstPeriodIndex = this.indexOf('.') + val openParenIndex = this.indexOf('(') + + // If there is no opening parenthesis found or if it occurs after the occurrence of the first period, just return + // the first sentence, or the entire string if no period is found. + if (openParenIndex == -1 || openParenIndex > firstPeriodIndex) { + return if (firstPeriodIndex != -1) { + this.substring(0, firstPeriodIndex + 1) + } else { + this + } + } + + // At this point we know that the opening parenthesis occurs before the first period, so we look for the matching + // closing parenthesis. + val closeParenIndex = this.indexOf(')', openParenIndex) + + // If a matching closing parenthesis is found, take that substring and recursively process the rest of the string. + // This is to accommodate periods inside of parenthesis. If a matching closing parenthesis is not found, return the + // original string. + return if (closeParenIndex != -1) { + this.substring(0, closeParenIndex) + this.substring(closeParenIndex, this.length).firstSentence() + } else { + this + } + +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/Utilities/Uri.kt new file mode 100644 index 000000000..9827c624c --- /dev/null +++ b/core/src/main/kotlin/Utilities/Uri.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka + +import java.net.URI + + +fun URI.relativeTo(uri: URI): URI { + // Normalize paths to remove . and .. segments + val base = uri.normalize() + val child = this.normalize() + + fun StringBuilder.appendRelativePath() { + // Split paths into segments + var bParts = base.path.split('/').dropLastWhile { it.isEmpty() } + val cParts = child.path.split('/').dropLastWhile { it.isEmpty() } + + // Discard trailing segment of base path + if (bParts.isNotEmpty() && !base.path.endsWith("/")) { + bParts = bParts.dropLast(1) + } + + // Compute common prefix + val commonPartsSize = bParts.zip(cParts).takeWhile { (basePart, childPart) -> basePart == childPart }.count() + bParts.drop(commonPartsSize).joinTo(this, separator = "") { "../" } + cParts.drop(commonPartsSize).joinTo(this, separator = "/") + } + + return URI.create(buildString { + if (base.path != child.path) { + appendRelativePath() + } + child.rawQuery?.let { + append("?") + append(it) + } + child.rawFragment?.let { + append("#") + append(it) + } + }) +}
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt new file mode 100644 index 000000000..12f571bee --- /dev/null +++ b/core/src/main/kotlin/javadoc/docbase.kt @@ -0,0 +1,525 @@ +package org.jetbrains.dokka.javadoc + +import com.sun.javadoc.* +import org.jetbrains.dokka.* +import java.lang.reflect.Modifier +import java.util.* +import kotlin.reflect.KClass + +private interface HasModule { + val module: ModuleNodeAdapter +} + +private interface HasDocumentationNode { + val node: DocumentationNode +} + +open class DocumentationNodeBareAdapter(override val node: DocumentationNode) : Doc, HasDocumentationNode { + private var rawCommentText_: String? = null + + override fun name(): String = node.name + override fun position(): SourcePosition? = SourcePositionAdapter(node) + + override fun inlineTags(): Array<out Tag>? = emptyArray() + override fun firstSentenceTags(): Array<out Tag>? = emptyArray() + override fun tags(): Array<out Tag> = emptyArray() + override fun tags(tagname: String?): Array<out Tag>? = tags().filter { it.kind() == tagname || it.kind() == "@$tagname" }.toTypedArray() + override fun seeTags(): Array<out SeeTag>? = tags().filterIsInstance<SeeTag>().toTypedArray() + override fun commentText(): String = "" + + override fun setRawCommentText(rawDocumentation: String?) { + rawCommentText_ = rawDocumentation ?: "" + } + + override fun getRawCommentText(): String = rawCommentText_ ?: "" + + override fun isError(): Boolean = false + override fun isException(): Boolean = node.kind == NodeKind.Exception + override fun isEnumConstant(): Boolean = node.kind == NodeKind.EnumItem + override fun isEnum(): Boolean = node.kind == NodeKind.Enum + override fun isMethod(): Boolean = node.kind == NodeKind.Function + override fun isInterface(): Boolean = node.kind == NodeKind.Interface + override fun isField(): Boolean = node.kind == NodeKind.Field + override fun isClass(): Boolean = node.kind == NodeKind.Class + override fun isAnnotationType(): Boolean = node.kind == NodeKind.AnnotationClass + override fun isConstructor(): Boolean = node.kind == NodeKind.Constructor + override fun isOrdinaryClass(): Boolean = node.kind == NodeKind.Class + override fun isAnnotationTypeElement(): Boolean = node.kind == NodeKind.Annotation + + override fun compareTo(other: Any?): Int = when (other) { + !is DocumentationNodeAdapter -> 1 + else -> node.name.compareTo(other.node.name) + } + + override fun equals(other: Any?): Boolean = node.qualifiedName() == (other as? DocumentationNodeAdapter)?.node?.qualifiedName() + override fun hashCode(): Int = node.name.hashCode() + + override fun isIncluded(): Boolean = node.kind != NodeKind.ExternalClass +} + + +// TODO think of source position instead of null +// TODO tags +open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeBareAdapter(node), HasModule { + override fun inlineTags(): Array<out Tag> = buildInlineTags(module, this, node.content).toTypedArray() + override fun firstSentenceTags(): Array<out Tag> = buildInlineTags(module, this, node.summary).toTypedArray() + + override fun tags(): Array<out Tag> { + val result = ArrayList<Tag>(buildInlineTags(module, this, node.content)) + node.content.sections.flatMapTo(result) { + when (it.tag) { + ContentTags.SeeAlso -> buildInlineTags(module, this, it) + else -> emptyList<Tag>() + } + } + + node.deprecation?.let { + val content = it.content.asText() + result.add(TagImpl(this, "deprecated", content ?: "")) + } + + return result.toTypedArray() + } +} + +// should be extension property but can't because of KT-8745 +private fun <T> nodeAnnotations(self: T): List<AnnotationDescAdapter> where T : HasModule, T : HasDocumentationNode + = self.node.annotations.map { AnnotationDescAdapter(self.module, it) } + +private fun DocumentationNode.hasAnnotation(klass: KClass<*>) = klass.qualifiedName in annotations.map { it.qualifiedName() } +private fun DocumentationNode.hasModifier(name: String) = details(NodeKind.Modifier).any { it.name == name } + + +class PackageAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), PackageDoc { + private val allClasses = listOf(node).collectAllTypesRecursively() + + override fun findClass(className: String?): ClassDoc? = + allClasses.get(className)?.let { ClassDocumentationNodeAdapter(module, it) } + + override fun annotationTypes(): Array<out AnnotationTypeDoc> = emptyArray() + override fun annotations(): Array<out AnnotationDesc> = node.members(NodeKind.AnnotationClass).map { AnnotationDescAdapter(module, it) }.toTypedArray() + override fun exceptions(): Array<out ClassDoc> = node.members(NodeKind.Exception).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun ordinaryClasses(): Array<out ClassDoc> = node.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun interfaces(): Array<out ClassDoc> = node.members(NodeKind.Interface).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun errors(): Array<out ClassDoc> = emptyArray() + override fun enums(): Array<out ClassDoc> = node.members(NodeKind.Enum).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun allClasses(filter: Boolean): Array<out ClassDoc> = allClasses.values.map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun allClasses(): Array<out ClassDoc> = allClasses(true) + + override fun isIncluded(): Boolean = node.name in module.allPackages +} + +class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ClassDocumentationNodeAdapter(module, node), AnnotationTypeDoc { + override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO +} + +class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc { + override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node) // TODO ????? + override fun isSynthesized(): Boolean = false + override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO +} + +open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc { + override fun isPublic(): Boolean = true + override fun isPackagePrivate(): Boolean = false + override fun isStatic(): Boolean = node.hasModifier("static") + override fun modifierSpecifier(): Int = Modifier.PUBLIC + if (isStatic) Modifier.STATIC else 0 + override fun qualifiedName(): String? = node.qualifiedName() + override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray() + override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim() + override fun isProtected(): Boolean = false + + override fun isFinal(): Boolean = node.hasModifier("final") + + override fun containingPackage(): PackageDoc? { + if (node.kind == NodeKind.Type) { + return null + } + + var owner: DocumentationNode? = node + while (owner != null) { + if (owner.kind == NodeKind.Package) { + return PackageAdapter(module, owner) + } + owner = owner.owner + } + + return null + } + + override fun containingClass(): ClassDoc? { + if (node.kind == NodeKind.Type) { + return null + } + + var owner = node.owner + while (owner != null) { + if (owner.kind in NodeKind.classLike) { + return ClassDocumentationNodeAdapter(module, owner) + } + owner = owner.owner + } + + return null + } + + override fun isPrivate(): Boolean = false + override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true +} + +open class TypeAdapter(override val module: ModuleNodeAdapter, override val node: DocumentationNode) : Type, HasDocumentationNode, HasModule { + private val javaLanguageService = JavaLanguageService() + + override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType() + override fun typeName(): String = javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName() + override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName() + + override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("") + override fun isPrimitive(): Boolean = simpleTypeName() in setOf("int", "long", "short", "byte", "char", "double", "float", "boolean", "void") + + override fun asClassDoc(): ClassDoc? = if (isPrimitive) null else + elementType?.asClassDoc() ?: + when (node.kind) { + in NodeKind.classLike, + NodeKind.ExternalClass, + NodeKind.Exception -> module.classNamed(qualifiedTypeName()) ?: ClassDocumentationNodeAdapter(module, node) + + else -> when { + node.links.firstOrNull { it.kind != NodeKind.ExternalLink } != null -> { + TypeAdapter(module, node.links.firstOrNull { it.kind != NodeKind.ExternalLink }!!).asClassDoc() + } + else -> ClassDocumentationNodeAdapter(module, node) // TODO ? + } + } + + override fun asTypeVariable(): TypeVariable? = if (node.kind == NodeKind.TypeParameter) TypeVariableAdapter(module, node) else null + override fun asParameterizedType(): ParameterizedType? = + if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null) + ParameterizedTypeAdapter(module, node) + else + null + + override fun asAnnotationTypeDoc(): AnnotationTypeDoc? = if (node.kind == NodeKind.AnnotationClass) AnnotationTypeDocAdapter(module, node) else null + override fun asAnnotatedType(): AnnotatedType? = if (node.annotations.isNotEmpty()) AnnotatedTypeAdapter(module, node) else null + override fun getElementType(): Type? = javaLanguageService.getArrayElementType(node)?.let { et -> TypeAdapter(module, et) } + override fun asWildcardType(): WildcardType? = null + + override fun toString(): String = qualifiedTypeName() + dimension() + override fun hashCode(): Int = node.name.hashCode() + override fun equals(other: Any?): Boolean = other is TypeAdapter && toString() == other.toString() +} + +class NotAnnotatedTypeAdapter(typeAdapter: AnnotatedTypeAdapter) : Type by typeAdapter { + override fun asAnnotatedType() = null +} + +class AnnotatedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), AnnotatedType { + override fun underlyingType(): Type? = NotAnnotatedTypeAdapter(this) + override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() +} + +class WildcardTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), WildcardType { + override fun extendsBounds(): Array<out Type> = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() + override fun superBounds(): Array<out Type> = node.details(NodeKind.LowerBound).map { TypeAdapter(module, it) }.toTypedArray() +} + +class TypeVariableAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), TypeVariable { + override fun owner(): ProgramElementDoc = node.owner!!.let<DocumentationNode, ProgramElementDoc> { owner -> + when (owner.kind) { + NodeKind.Function, + NodeKind.Constructor -> ExecutableMemberAdapter(module, owner) + + NodeKind.Class, + NodeKind.Interface, + NodeKind.Enum -> ClassDocumentationNodeAdapter(module, owner) + + else -> ProgramElementAdapter(module, node.owner!!) + } + } + + override fun bounds(): Array<out Type>? = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() + override fun annotations(): Array<out AnnotationDesc>? = node.members(NodeKind.Annotation).map { AnnotationDescAdapter(module, it) }.toTypedArray() + + override fun qualifiedTypeName(): String = node.name + override fun simpleTypeName(): String = node.name + override fun typeName(): String = node.name + + override fun hashCode(): Int = node.name.hashCode() + override fun equals(other: Any?): Boolean = other is Type && other.typeName() == typeName() && other.asTypeVariable()?.owner() == owner() + + override fun asTypeVariable(): TypeVariableAdapter = this +} + +class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), ParameterizedType { + override fun typeArguments(): Array<out Type> = node.details(NodeKind.Type).map { TypeVariableAdapter(module, it) }.toTypedArray() + override fun superclassType(): Type? = + node.lookupSuperClasses(module) + .firstOrNull { it.kind == NodeKind.Class || it.kind == NodeKind.ExternalClass } + ?.let { ClassDocumentationNodeAdapter(module, it) } + + override fun interfaceTypes(): Array<out Type> = + node.lookupSuperClasses(module) + .filter { it.kind == NodeKind.Interface } + .map { ClassDocumentationNodeAdapter(module, it) } + .toTypedArray() + + override fun containingType(): Type? = when (node.owner?.kind) { + NodeKind.Package -> null + NodeKind.Class, + NodeKind.Interface, + NodeKind.Object, + NodeKind.Enum -> ClassDocumentationNodeAdapter(module, node.owner!!) + + else -> null + } +} + +class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter { + override fun typeName(): String? = JavaLanguageService().renderType(node.detail(NodeKind.Type)) + override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type)) + override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() +} + +class ReceiverParameterAdapter(module: ModuleNodeAdapter, val receiverType: DocumentationNode, val parent: ExecutableMemberAdapter) : DocumentationNodeAdapter(module, receiverType), Parameter { + override fun typeName(): String? = receiverType.name + override fun type(): Type? = TypeAdapter(module, receiverType) + override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() + override fun name(): String = tryName("receiver") + + private tailrec fun tryName(name: String): String = when (name) { + in parent.parameters().drop(1).map { it.name() } -> tryName("$$name") + else -> name + } +} + +fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode(fqName.substringAfterLast(".", fqName), Content.Empty, kind).let { node -> + val pkg = fqName.substringBeforeLast(".", "") + if (pkg.isNotEmpty()) { + node.append(DocumentationNode(pkg, Content.Empty, NodeKind.Package), RefKind.Owner) + } + + node +} + +private fun DocumentationNode.hasNonEmptyContent() = + this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty() + + +open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), ExecutableMemberDoc { + + override fun isSynthetic(): Boolean = false + override fun isNative(): Boolean = node.annotations.any { it.name == "native" } + + override fun thrownExceptions(): Array<out ClassDoc> = emptyArray() // TODO + override fun throwsTags(): Array<out ThrowsTag> = + node.content.sections + .filter { it.tag == ContentTags.Exceptions && it.subjectName != null } + .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) } + .toTypedArray() + + override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).any { false } // TODO + + override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" } + + override fun paramTags(): Array<out ParamTag> = + collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } }) + + override fun thrownExceptionTypes(): Array<out Type> = emptyArray() + override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) } + override fun flatSignature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") + override fun signature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") // TODO it should be FQ types + + override fun parameters(): Array<out Parameter> = + ((receiverNode()?.let { receiver -> listOf<Parameter>(ReceiverParameterAdapter(module, receiver, this)) } ?: emptyList()) + + node.details(NodeKind.Parameter).map { ParameterAdapter(module, it) } + ).toTypedArray() + + override fun typeParameters(): Array<out TypeVariable> = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() + + override fun typeParamTags(): Array<out ParamTag> = + collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) + + private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers -> + when { + receivers.isNotEmpty() -> receivers.single().detail(NodeKind.Type) + else -> null + } + } +} + +class ConstructorAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), ConstructorDoc { + override fun name(): String = node.owner?.name ?: throw IllegalStateException("No owner for $node") + + override fun containingClass(): ClassDoc? { + return super.containingClass() + } +} + +class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), MethodDoc { + override fun overrides(meth: MethodDoc?): Boolean = false // TODO + + override fun overriddenType(): Type? = node.overrides.firstOrNull()?.owner?.let { owner -> TypeAdapter(module, owner) } + + override fun overriddenMethod(): MethodDoc? = node.overrides.map { MethodAdapter(module, it) }.firstOrNull() + override fun overriddenClass(): ClassDoc? = overriddenMethod()?.containingClass() + + override fun isAbstract(): Boolean = false // TODO + + override fun isDefault(): Boolean = false + + override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) + + override fun tags(tagname: String?) = super.tags(tagname) + + override fun tags(): Array<out Tag> { + val tags = super.tags().toMutableList() + node.content.findSectionByTag(ContentTags.Return)?.let { + tags += ReturnTagAdapter(module, this, it.children) + } + + return tags.toTypedArray() + } +} + +class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), FieldDoc { + override fun isSynthetic(): Boolean = false + + override fun constantValueExpression(): String? = node.detailOrNull(NodeKind.Value)?.let { it.name } + override fun constantValue(): Any? = constantValueExpression() + + override fun type(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) + override fun isTransient(): Boolean = node.hasAnnotation(Transient::class) + override fun serialFieldTags(): Array<out SerialFieldTag> = emptyArray() + + override fun isVolatile(): Boolean = node.hasAnnotation(Volatile::class) +} +open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNode: DocumentationNode) + : ProgramElementAdapter(module, classNode), + Type by TypeAdapter(module, classNode), + ClassDoc { + + override fun name(): String { + val parent = classNode.owner + if (parent?.kind in NodeKind.classLike) { + return parent!!.name + "." + classNode.name + } + return classNode.simpleName() + } + + override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray() + override fun constructors(): Array<out ConstructorDoc> = constructors(true) + override fun importedPackages(): Array<out PackageDoc> = emptyArray() + override fun importedClasses(): Array<out ClassDoc>? = emptyArray() + override fun typeParameters(): Array<out TypeVariable> = classNode.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() + override fun asTypeVariable(): TypeVariable? = if (classNode.kind == NodeKind.Class) TypeVariableAdapter(module, classNode) else null + override fun isExternalizable(): Boolean = interfaces().any { it.qualifiedName() == "java.io.Externalizable" } + override fun definesSerializableFields(): Boolean = false + override fun methods(filter: Boolean): Array<out MethodDoc> = classNode.members(NodeKind.Function).map { MethodAdapter(module, it) }.toTypedArray() // TODO include get/set methods + override fun methods(): Array<out MethodDoc> = methods(true) + override fun enumConstants(): Array<out FieldDoc>? = classNode.members(NodeKind.EnumItem).map { FieldAdapter(module, it) }.toTypedArray() + override fun isAbstract(): Boolean = classNode.details(NodeKind.Modifier).any { it.name == "abstract" } + override fun interfaceTypes(): Array<out Type> = classNode.lookupSuperClasses(module) + .filter { it.kind == NodeKind.Interface } + .map { ClassDocumentationNodeAdapter(module, it) } + .toTypedArray() + + override fun interfaces(): Array<out ClassDoc> = classNode.lookupSuperClasses(module) + .filter { it.kind == NodeKind.Interface } + .map { ClassDocumentationNodeAdapter(module, it) } + .toTypedArray() + + override fun typeParamTags(): Array<out ParamTag> = + collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) + + override fun fields(): Array<out FieldDoc> = fields(true) + override fun fields(filter: Boolean): Array<out FieldDoc> = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray() + + override fun findClass(className: String?): ClassDoc? = null // TODO !!! + override fun serializableFields(): Array<out FieldDoc> = emptyArray() + override fun superclassType(): Type? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } + override fun serializationMethods(): Array<out MethodDoc> = emptyArray() // TODO + override fun superclass(): ClassDoc? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } + override fun isSerializable(): Boolean = false // TODO + override fun subclassOf(cd: ClassDoc?): Boolean { + if (cd == null) { + return false + } + + val expectedFQName = cd.qualifiedName() + val types = arrayListOf(classNode) + val visitedTypes = HashSet<String>() + + while (types.isNotEmpty()) { + val type = types.removeAt(types.lastIndex) + val fqName = type.qualifiedName() + + if (expectedFQName == fqName) { + return true + } + + visitedTypes.add(fqName) + types.addAll(type.details(NodeKind.Supertype).filter { it.qualifiedName() !in visitedTypes }) + } + + return false + } + + override fun innerClasses(): Array<out ClassDoc> = classNode.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() + override fun innerClasses(filter: Boolean): Array<out ClassDoc> = innerClasses() +} + +fun DocumentationNode.lookupSuperClasses(module: ModuleNodeAdapter) = + details(NodeKind.Supertype) + .map { it.links.firstOrNull() } + .map { module.allTypes[it?.qualifiedName()] } + .filterNotNull() + +fun List<DocumentationNode>.collectAllTypesRecursively(): Map<String, DocumentationNode> { + val result = hashMapOf<String, DocumentationNode>() + + fun DocumentationNode.collectTypesRecursively() { + val classLikeMembers = NodeKind.classLike.flatMap { members(it) } + classLikeMembers.forEach { + result.put(it.qualifiedName(), it) + it.collectTypesRecursively() + } + } + + forEach { it.collectTypesRecursively() } + return result +} + +class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorReporter, val outputPath: String) : DocumentationNodeBareAdapter(module), DocErrorReporter by reporter, RootDoc { + val allPackages = module.members(NodeKind.Package).associateBy { it.name } + val allTypes = module.members(NodeKind.Package).collectAllTypesRecursively() + + override fun packageNamed(name: String?): PackageDoc? = allPackages[name]?.let { PackageAdapter(this, it) } + + override fun classes(): Array<out ClassDoc> = + allTypes.values.map { ClassDocumentationNodeAdapter(this, it) }.toTypedArray() + + override fun options(): Array<out Array<String>> = arrayOf( + arrayOf("-d", outputPath), + arrayOf("-docencoding", "UTF-8"), + arrayOf("-charset", "UTF-8"), + arrayOf("-keywords") + ) + + override fun specifiedPackages(): Array<out PackageDoc>? = module.members(NodeKind.Package).map { PackageAdapter(this, it) }.toTypedArray() + + override fun classNamed(qualifiedName: String?): ClassDoc? = + allTypes[qualifiedName]?.let { ClassDocumentationNodeAdapter(this, it) } + + override fun specifiedClasses(): Array<out ClassDoc> = classes() +} + +private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) = + (node.details(kind) + .filter(DocumentationNode::hasNonEmptyContent) + .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } + + + node.content.sections + .filter(sectionFilter) + .map { ParamTagAdapter(module, this, it.subjectName ?: "?", true, it.children) }) + + .toTypedArray()
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/dokka-adapters.kt b/core/src/main/kotlin/javadoc/dokka-adapters.kt new file mode 100644 index 000000000..483fb3cdc --- /dev/null +++ b/core/src/main/kotlin/javadoc/dokka-adapters.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.javadoc + +import com.google.inject.Binder +import com.google.inject.Inject +import com.sun.tools.doclets.formats.html.HtmlDoclet +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Formats.* +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.Utilities.toType + +class JavadocGenerator @Inject constructor(val options: DocumentationOptions, val logger: DokkaLogger) : Generator { + + override fun buildPages(nodes: Iterable<DocumentationNode>) { + val module = nodes.single() as DocumentationModule + + HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), options.outputDir)) + } + + override fun buildOutlines(nodes: Iterable<DocumentationNode>) { + // no outline could be generated separately + } + + override fun buildSupportFiles() { + } + + override fun buildPackageList(nodes: Iterable<DocumentationNode>) { + // handled by javadoc itself + } +} + +class JavadocFormatDescriptor : + FormatDescriptor, + DefaultAnalysisComponent, + DefaultAnalysisComponentServices by KotlinAsJava { + + override fun configureOutput(binder: Binder): Unit = with(binder) { + bind<Generator>() toType JavadocGenerator::class + } +} diff --git a/core/src/main/kotlin/javadoc/reporter.kt b/core/src/main/kotlin/javadoc/reporter.kt new file mode 100644 index 000000000..fc38368c9 --- /dev/null +++ b/core/src/main/kotlin/javadoc/reporter.kt @@ -0,0 +1,34 @@ +package org.jetbrains.dokka.javadoc + +import com.sun.javadoc.DocErrorReporter +import com.sun.javadoc.SourcePosition +import org.jetbrains.dokka.DokkaLogger + +class StandardReporter(val logger: DokkaLogger) : DocErrorReporter { + override fun printWarning(msg: String?) { + logger.warn(msg.toString()) + } + + override fun printWarning(pos: SourcePosition?, msg: String?) { + logger.warn(format(pos, msg)) + } + + override fun printError(msg: String?) { + logger.error(msg.toString()) + } + + override fun printError(pos: SourcePosition?, msg: String?) { + logger.error(format(pos, msg)) + } + + override fun printNotice(msg: String?) { + logger.info(msg.toString()) + } + + override fun printNotice(pos: SourcePosition?, msg: String?) { + logger.info(format(pos, msg)) + } + + private fun format(pos: SourcePosition?, msg: String?) = + if (pos == null) msg.toString() else "${pos.file()}:${pos.line()}:${pos.column()}: $msg" +}
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/source-position.kt b/core/src/main/kotlin/javadoc/source-position.kt new file mode 100644 index 000000000..6125f9689 --- /dev/null +++ b/core/src/main/kotlin/javadoc/source-position.kt @@ -0,0 +1,19 @@ +package org.jetbrains.dokka.javadoc + +import com.sun.javadoc.SourcePosition +import org.jetbrains.dokka.DocumentationNode +import org.jetbrains.dokka.NodeKind +import java.io.File + +class SourcePositionAdapter(val docNode: DocumentationNode) : SourcePosition { + + private val sourcePositionParts: List<String> by lazy { + docNode.details(NodeKind.SourcePosition).firstOrNull()?.name?.split(":") ?: emptyList() + } + + override fun file(): File? = if (sourcePositionParts.isEmpty()) null else File(sourcePositionParts[0]) + + override fun line(): Int = sourcePositionParts.getOrNull(1)?.toInt() ?: -1 + + override fun column(): Int = sourcePositionParts.getOrNull(2)?.toInt() ?: -1 +} diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt new file mode 100644 index 000000000..95c6e87fc --- /dev/null +++ b/core/src/main/kotlin/javadoc/tags.kt @@ -0,0 +1,240 @@ +package org.jetbrains.dokka.javadoc + +import com.sun.javadoc.* +import org.jetbrains.dokka.* +import java.util.* + +class TagImpl(val holder: Doc, val name: String, val text: String): Tag { + override fun text(): String? = text + + override fun holder(): Doc = holder + override fun firstSentenceTags(): Array<out Tag>? = arrayOf() + override fun inlineTags(): Array<out Tag>? = arrayOf() + + override fun name(): String = name + override fun kind(): String = name + + override fun position(): SourcePosition = holder.position() +} + +class TextTag(val holder: Doc, val content: ContentText) : Tag { + val plainText: String + get() = content.text + + override fun name(): String = "Text" + override fun kind(): String = name() + override fun text(): String? = plainText + override fun inlineTags(): Array<out Tag> = arrayOf(this) + override fun holder(): Doc = holder + override fun firstSentenceTags(): Array<out Tag> = arrayOf(this) + override fun position(): SourcePosition = holder.position() +} + +abstract class SeeTagAdapter(val holder: Doc, val content: ContentNodeLink) : SeeTag { + override fun position(): SourcePosition? = holder.position() + override fun name(): String = "@see" + override fun kind(): String = "@see" + override fun holder(): Doc = holder + + override fun text(): String? = content.node?.name ?: "(?)" +} + +class SeeExternalLinkTagAdapter(val holder: Doc, val link: ContentExternalLink) : SeeTag { + override fun position(): SourcePosition = holder.position() + override fun text(): String = label() + override fun inlineTags(): Array<out Tag> = emptyArray() // TODO + + override fun label(): String { + val label = link.asText() ?: link.href + return "<a href=\"${link.href}\">$label</a>" + } + + override fun referencedPackage(): PackageDoc? = null + override fun referencedClass(): ClassDoc? = null + override fun referencedMemberName(): String? = null + override fun referencedClassName(): String? = null + override fun referencedMember(): MemberDoc? = null + override fun holder(): Doc = holder + override fun firstSentenceTags(): Array<out Tag> = inlineTags() + override fun name(): String = "@link" + override fun kind(): String = "@see" +} + +fun ContentBlock.asText(): String? { + val contentText = children.singleOrNull() as? ContentText + return contentText?.text +} + +class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { + override fun referencedMember(): MemberDoc = method + override fun referencedMemberName(): String = method.name() + override fun referencedPackage(): PackageDoc? = null + override fun referencedClass(): ClassDoc? = method.containingClass() + override fun referencedClassName(): String = method.containingClass()?.name() ?: "" + override fun label(): String = "${method.containingClass()?.name()}.${method.name()}" + + override fun inlineTags(): Array<out Tag> = emptyArray() // TODO + override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO +} + +class SeeClassTagAdapter(holder: Doc, val clazz: ClassDocumentationNodeAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { + override fun referencedMember(): MemberDoc? = null + override fun referencedMemberName(): String? = null + override fun referencedPackage(): PackageDoc? = null + override fun referencedClass(): ClassDoc = clazz + override fun referencedClassName(): String = clazz.name() + override fun label(): String = "${clazz.classNode.kind.name.toLowerCase()} ${clazz.name()}" + + override fun inlineTags(): Array<out Tag> = emptyArray() // TODO + override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO +} + +class ParamTagAdapter(val module: ModuleNodeAdapter, + val holder: Doc, + val parameterName: String, + val typeParameter: Boolean, + val content: List<ContentNode>) : ParamTag { + + constructor(module: ModuleNodeAdapter, holder: Doc, parameterName: String, isTypeParameter: Boolean, content: ContentNode) + : this(module, holder, parameterName, isTypeParameter, listOf(content)) { + } + + override fun name(): String = "@param" + override fun kind(): String = name() + override fun holder(): Doc = holder + override fun position(): SourcePosition? = holder.position() + + override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug + override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray() + override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text()))) + + override fun isTypeParameter(): Boolean = typeParameter + override fun parameterComment(): String = content.toString() // TODO + override fun parameterName(): String = parameterName +} + + +class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag { + override fun name(): String = "@throws" + override fun kind(): String = name() + override fun holder(): Doc = holder + override fun position(): SourcePosition? = holder.position() + + override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}" + override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray() + override fun firstSentenceTags(): Array<out Tag> = emptyArray() + + override fun exceptionComment(): String = content.toString() + override fun exceptionType(): Type = type + override fun exception(): ClassDoc = type + override fun exceptionName(): String = type.qualifiedTypeName() +} + +class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag { + override fun name(): String = "@return" + override fun kind() = name() + override fun holder() = holder + override fun position(): SourcePosition? = holder.position() + + override fun text(): String = "@return $content" // Seems has no effect, so used for debug + override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray() + override fun firstSentenceTags(): Array<Tag> = inlineTags() +} + +fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } } + +fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) } + +private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) { + nodes.forEach { + buildInlineTags(module, holder, it, result) + } +} + + +private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, node: ContentNode, result: MutableList<Tag>) { + fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentBlock, result: MutableList<Tag>) { + if (node.children.isNotEmpty()) { + val open = TextTag(holder, ContentText(prefix)) + val close = TextTag(holder, ContentText(postfix)) + + result.add(open) + buildInlineTags(module, holder, node.children, result) + + if (result.last() === open) { + result.removeAt(result.lastIndex) + } else { + result.add(close) + } + } + } + + fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentNode, result: MutableList<Tag>) { + if (node !is ContentEmpty) { + val open = TextTag(holder, ContentText(prefix)) + val close = TextTag(holder, ContentText(postfix)) + + result.add(open) + buildInlineTags(module, holder, node, result) + if (result.last() === open) { + result.removeAt(result.lastIndex) + } else { + result.add(close) + } + } + } + + when (node) { + is ContentText -> result.add(TextTag(holder, node)) + is ContentNodeLink -> { + val target = node.node + when (target?.kind) { + NodeKind.Function -> result.add(SeeMethodTagAdapter(holder, MethodAdapter(module, node.node!!), node)) + + in NodeKind.classLike -> result.add(SeeClassTagAdapter(holder, ClassDocumentationNodeAdapter(module, node.node!!), node)) + + else -> buildInlineTags(module, holder, node.children, result) + } + } + is ContentExternalLink -> result.add(SeeExternalLinkTagAdapter(holder, node)) + is ContentSpecialReference -> surroundWith(module, holder, "<aside class=\"note\">", "</aside>", node, result) + is ContentCode -> surroundWith(module, holder, "<code>", "</code>", node, result) + is ContentBlockCode -> surroundWith(module, holder, "<code><pre>", "</pre></code>", node, result) + is ContentEmpty -> {} + is ContentEmphasis -> surroundWith(module, holder, "<em>", "</em>", node, result) + is ContentHeading -> surroundWith(module, holder, "<h${node.level}>", "</h${node.level}>", node, result) + is ContentEntity -> result.add(TextTag(holder, ContentText(node.text))) // TODO ?? + is ContentIdentifier -> result.add(TextTag(holder, ContentText(node.text))) // TODO + is ContentKeyword -> result.add(TextTag(holder, ContentText(node.text))) // TODO + is ContentListItem -> surroundWith(module, holder, "<li>", "</li>", node, result) + is ContentOrderedList -> surroundWith(module, holder, "<ol>", "</ol>", node, result) + is ContentUnorderedList -> surroundWith(module, holder, "<ul>", "</ul>", node, result) + is ContentParagraph -> surroundWith(module, holder, "<p>", "</p>", node, result) + + is ContentDescriptionList -> surroundWith(module, holder, "<dl>", "</dl>", node, result) + is ContentDescriptionTerm -> surroundWith(module, holder, "<dt>", "</dt>", node, result) + is ContentDescriptionDefinition -> surroundWith(module, holder, "<dd>", "</dd>", node, result) + + is ContentTable -> surroundWith(module, holder, "<table>", "</table>", node, result) + is ContentTableBody -> surroundWith(module, holder, "<tbody>", "</tbody>", node, result) + is ContentTableRow -> surroundWith(module, holder, "<tr>", "</tr>", node, result) + is ContentTableHeader -> surroundWith(module, holder, "<th>", "</th>", node, result) + is ContentTableCell -> surroundWith(module, holder, "<td>", "</td>", node, result) + + is ContentSection -> surroundWith(module, holder, "<p>", "</p>", node, result) // TODO how section should be represented? + is ContentNonBreakingSpace -> result.add(TextTag(holder, ContentText(" "))) + is ContentStrikethrough -> surroundWith(module, holder, "<strike>", "</strike>", node, result) + is ContentStrong -> surroundWith(module, holder, "<strong>", "</strong>", node, result) + is ContentSymbol -> result.add(TextTag(holder, ContentText(node.text))) // TODO? + is Content -> { + surroundWith(module, holder, "<p>", "</p>", node.summary, result) + surroundWith(module, holder, "<p>", "</p>", node.description, result) + } + is ContentBlock -> { + surroundWith(module, holder, "", "", node, result) + } + is ContentHardLineBreak -> result.add(TextTag(holder, ContentText("<br/>"))) + + else -> result.add(TextTag(holder, ContentText("$node"))) + } +}
\ No newline at end of file diff --git a/core/src/main/resources/META-INF/MANIFEST.MF b/core/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..87807e1e3 --- /dev/null +++ b/core/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Class-Path: kotlin-plugin.jar +Main-Class: org.jetbrains.dokka.MainKt + diff --git a/core/src/main/resources/dokka/format/dac-as-java.properties b/core/src/main/resources/dokka/format/dac-as-java.properties new file mode 100644 index 000000000..29e05b3fd --- /dev/null +++ b/core/src/main/resources/dokka/format/dac-as-java.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.DacAsJavaFormatDescriptor +description=Generates developer.android.com website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/dac.properties b/core/src/main/resources/dokka/format/dac.properties new file mode 100644 index 000000000..52b19097f --- /dev/null +++ b/core/src/main/resources/dokka/format/dac.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.DacFormatDescriptor +description=Generates developer.android.com website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/gfm.properties b/core/src/main/resources/dokka/format/gfm.properties new file mode 100644 index 000000000..5e8f7aa8c --- /dev/null +++ b/core/src/main/resources/dokka/format/gfm.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.GFMFormatDescriptor +description=Produces documentation in GitHub-flavored markdown format diff --git a/core/src/main/resources/dokka/format/html-as-java.properties b/core/src/main/resources/dokka/format/html-as-java.properties new file mode 100644 index 000000000..f598f3771 --- /dev/null +++ b/core/src/main/resources/dokka/format/html-as-java.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.HtmlAsJavaFormatDescriptor +description=Produces output in HTML format using Java syntax
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/html.properties b/core/src/main/resources/dokka/format/html.properties new file mode 100644 index 000000000..7881dfae8 --- /dev/null +++ b/core/src/main/resources/dokka/format/html.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.HtmlFormatDescriptor +description=Produces output in HTML format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/java-layout-html-as-java.properties b/core/src/main/resources/dokka/format/java-layout-html-as-java.properties new file mode 100644 index 000000000..7d178ba4a --- /dev/null +++ b/core/src/main/resources/dokka/format/java-layout-html-as-java.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.JavaLayoutHtmlAsJavaFormatDescriptor +description=Produces Java Style Docs with Javadoc like layout
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/java-layout-html.properties b/core/src/main/resources/dokka/format/java-layout-html.properties new file mode 100644 index 000000000..fbb2bbedc --- /dev/null +++ b/core/src/main/resources/dokka/format/java-layout-html.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor +description=Produces Kotlin Style Docs with Javadoc like layout
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/javadoc.properties b/core/src/main/resources/dokka/format/javadoc.properties new file mode 100644 index 000000000..a0d8a945d --- /dev/null +++ b/core/src/main/resources/dokka/format/javadoc.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor +description=Produces Javadoc, with Kotlin declarations as Java view
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/jekyll.properties b/core/src/main/resources/dokka/format/jekyll.properties new file mode 100644 index 000000000..b11401a4b --- /dev/null +++ b/core/src/main/resources/dokka/format/jekyll.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.JekyllFormatDescriptor +description=Produces documentation in Jekyll format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/kotlin-website-html.properties b/core/src/main/resources/dokka/format/kotlin-website-html.properties new file mode 100644 index 000000000..f4c320b9f --- /dev/null +++ b/core/src/main/resources/dokka/format/kotlin-website-html.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.KotlinWebsiteHtmlFormatDescriptor +description=Generates Kotlin website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/kotlin-website-samples.properties b/core/src/main/resources/dokka/format/kotlin-website-samples.properties new file mode 100644 index 000000000..bda616a41 --- /dev/null +++ b/core/src/main/resources/dokka/format/kotlin-website-samples.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.KotlinWebsiteFormatRunnableSamplesDescriptor +description=Generates Kotlin website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/kotlin-website.properties b/core/src/main/resources/dokka/format/kotlin-website.properties new file mode 100644 index 000000000..c13e76754 --- /dev/null +++ b/core/src/main/resources/dokka/format/kotlin-website.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.KotlinWebsiteFormatDescriptor +description=Generates Kotlin website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/markdown.properties b/core/src/main/resources/dokka/format/markdown.properties new file mode 100644 index 000000000..6217a6df1 --- /dev/null +++ b/core/src/main/resources/dokka/format/markdown.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.MarkdownFormatDescriptor +description=Produces documentation in markdown format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties new file mode 100644 index 000000000..c484a920d --- /dev/null +++ b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Dokka +description=Uses Dokka Default resolver
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties new file mode 100644 index 000000000..3b61eabe7 --- /dev/null +++ b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.Formats.JavaLayoutHtmlInboundLinkResolutionService +description=Resolver for JavaLayoutHtml
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties new file mode 100644 index 000000000..0d5d7d17c --- /dev/null +++ b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties @@ -0,0 +1,2 @@ +class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Javadoc +description=Uses Javadoc Default resolver
\ No newline at end of file diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css new file mode 100644 index 000000000..914be69d6 --- /dev/null +++ b/core/src/main/resources/dokka/styles/style.css @@ -0,0 +1,283 @@ +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700); + +body, table { + padding:50px; + font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; + color:#555; + font-weight:300; + margin-left: auto; + margin-right: auto; + max-width: 1440px; +} + +.keyword { + color:black; + font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + font-size:12px; +} + +.symbol { + font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + font-size:12px; +} + +.identifier { + color: darkblue; + font-size:12px; + font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; +} + +h1, h2, h3, h4, h5, h6 { + color:#222; + margin:0 0 20px; +} + +p, ul, ol, table, pre, dl { + margin:0 0 20px; +} + +h1, h2, h3 { + line-height:1.1; +} + +h1 { + font-size:28px; +} + +h2 { + color:#393939; +} + +h3, h4, h5, h6 { + color:#494949; +} + +a { + color:#258aaf; + font-weight:400; + text-decoration:none; +} + +a:hover { + color: inherit; + text-decoration:underline; +} + +a small { + font-size:11px; + color:#555; + margin-top:-0.6em; + display:block; +} + +.wrapper { + width:860px; + margin:0 auto; +} + +blockquote { + border-left:1px solid #e5e5e5; + margin:0; + padding:0 0 0 20px; + font-style:italic; +} + +code, pre { + font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color:#333; + font-size:12px; +} + +pre { + display: block; +/* + padding:8px 8px; + background: #f8f8f8; + border-radius:5px; + border:1px solid #e5e5e5; +*/ + overflow-x: auto; +} + +table { + width:100%; + border-collapse:collapse; +} + +th, td { + text-align:left; + vertical-align: top; + padding:5px 10px; +} + +dt { + color:#444; + font-weight:700; +} + +th { + color:#444; +} + +img { + max-width:100%; +} + +header { + width:270px; + float:left; + position:fixed; +} + +header ul { + list-style:none; + height:40px; + + padding:0; + + background: #eee; + background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%); + + border-radius:5px; + border:1px solid #d2d2d2; + box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0; + width:270px; +} + +header li { + width:89px; + float:left; + border-right:1px solid #d2d2d2; + height:40px; +} + +header ul a { + line-height:1; + font-size:11px; + color:#999; + display:block; + text-align:center; + padding-top:6px; + height:40px; +} + +strong { + color:#222; + font-weight:700; +} + +header ul li + li { + width:88px; + border-left:1px solid #fff; +} + +header ul li + li + li { + border-right:none; + width:89px; +} + +header ul a strong { + font-size:14px; + display:block; + color:#222; +} + +section { + width:500px; + float:right; + padding-bottom:50px; +} + +small { + font-size:11px; +} + +hr { + border:0; + background:#e5e5e5; + height:1px; + margin:0 0 20px; +} + +footer { + width:270px; + float:left; + position:fixed; + bottom:50px; +} + +@media print, screen and (max-width: 960px) { + + div.wrapper { + width:auto; + margin:0; + } + + header, section, footer { + float:none; + position:static; + width:auto; + } + + header { + padding-right:320px; + } + + section { + border:1px solid #e5e5e5; + border-width:1px 0; + padding:20px 0; + margin:0 0 20px; + } + + header a small { + display:inline; + } + + header ul { + position:absolute; + right:50px; + top:52px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap:break-word; + } + + header { + padding:0; + } + + header ul, header p.view { + position:static; + } + + pre, code { + word-wrap:normal; + } +} + +@media print, screen and (max-width: 480px) { + body { + padding:15px; + } + + header ul { + display:none; + } +} + +@media print { + body { + padding:0.4in; + font-size:12pt; + color:#444; + } +} diff --git a/core/src/test/kotlin/Model/CodeNodeTest.kt b/core/src/test/kotlin/Model/CodeNodeTest.kt new file mode 100644 index 000000000..ae3e67183 --- /dev/null +++ b/core/src/test/kotlin/Model/CodeNodeTest.kt @@ -0,0 +1,14 @@ +package Model + +import org.jetbrains.dokka.Model.CodeNode +import org.junit.Test +import kotlin.test.assertEquals + +class CodeNodeTest { + + @Test fun text_normalisesInitialWhitespace() { + val expected = "Expected\ntext in this\ttest" + val sut = CodeNode("\n \t \r $expected", "") + assertEquals(expected, sut.text()) + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt new file mode 100644 index 000000000..ef2923cce --- /dev/null +++ b/core/src/test/kotlin/TestAPI.kt @@ -0,0 +1,302 @@ +package org.jetbrains.dokka.tests + +import com.google.inject.Guice +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.rt.execution.junit.FileComparisonFailure +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Utilities.DokkaAnalysisModule +import org.jetbrains.kotlin.cli.common.config.ContentRoot +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.junit.Assert +import org.junit.Assert.fail +import java.io.File + +fun verifyModel(vararg roots: ContentRoot, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + format: String = "html", + includeNonPublic: Boolean = true, + perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(), + noStdlibLink: Boolean = true, + collectInheritedExtensionsFromLibraries: Boolean = false, + verifier: (DocumentationModule) -> Unit) { + val documentation = DocumentationModule("test") + + val options = DocumentationOptions( + "", + format, + includeNonPublic = includeNonPublic, + skipEmptyPackages = false, + includeRootPackage = true, + sourceLinks = listOf(), + perPackageOptions = perPackageOptions, + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = noStdlibLink, + noJdkLink = false, + cacheRoot = "default", + languageVersion = null, + apiVersion = null, + collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries + ) + + appendDocumentation(documentation, *roots, + withJdk = withJdk, + withKotlinRuntime = withKotlinRuntime, + options = options) + documentation.prepareForGeneration(options) + + verifier(documentation) +} + +fun appendDocumentation(documentation: DocumentationModule, + vararg roots: ContentRoot, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + options: DocumentationOptions, + defaultPlatforms: List<String> = emptyList()) { + val messageCollector = object : MessageCollector { + override fun clear() { + + } + + override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { + when (severity) { + CompilerMessageSeverity.STRONG_WARNING, + CompilerMessageSeverity.WARNING, + CompilerMessageSeverity.LOGGING, + CompilerMessageSeverity.OUTPUT, + CompilerMessageSeverity.INFO, + CompilerMessageSeverity.ERROR -> { + println("$severity: $message at $location") + } + CompilerMessageSeverity.EXCEPTION -> { + fail("$severity: $message at $location") + } + } + } + + override fun hasErrors() = false + } + + val environment = AnalysisEnvironment(messageCollector) + environment.apply { + if (withJdk || withKotlinRuntime) { + val stringRoot = PathManager.getResourceRoot(String::class.java, "/java/lang/String.class") + addClasspath(File(stringRoot)) + } + if (withKotlinRuntime) { + val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") + addClasspath(File(kotlinStrictfpRoot)) + } + addRoots(roots.toList()) + + loadLanguageVersionSettings(options.languageVersion, options.apiVersion) + } + val defaultPlatformsProvider = object : DefaultPlatformsProvider { + override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms + } + val injector = Guice.createInjector( + DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger)) + buildDocumentationModule(injector, documentation) + Disposer.dispose(environment) +} + +fun verifyModel(source: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + format: String = "html", + includeNonPublic: Boolean = true, + verifier: (DocumentationModule) -> Unit) { + if (!File(source).exists()) { + throw IllegalArgumentException("Can't find test data file $source") + } + verifyModel(contentRootFromPath(source), + withJdk = withJdk, + withKotlinRuntime = withKotlinRuntime, + format = format, + includeNonPublic = includeNonPublic, + verifier = verifier) +} + +fun verifyPackageMember(source: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + verifier: (DocumentationNode) -> Unit) { + verifyModel(source, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime) { model -> + val pkg = model.members.single() + verifier(pkg.members.single()) + } +} + +fun verifyJavaModel(source: String, + withKotlinRuntime: Boolean = false, + format: String = "html", + verifier: (DocumentationModule) -> Unit) { + val tempDir = FileUtil.createTempDirectory("dokka", "") + try { + val sourceFile = File(source) + FileUtil.copy(sourceFile, File(tempDir, sourceFile.name)) + verifyModel(JavaSourceRoot(tempDir, null), format = format, withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier) + } + finally { + FileUtil.delete(tempDir) + } +} + +fun verifyJavaPackageMember(source: String, + withKotlinRuntime: Boolean = false, + verifier: (DocumentationNode) -> Unit) { + verifyJavaModel(source, withKotlinRuntime) { model -> + val pkg = model.members.single() + verifier(pkg.members.single()) + } +} + +fun verifyOutput(roots: Array<ContentRoot>, + outputExtension: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + format: String = "html", + includeNonPublic: Boolean = true, + noStdlibLink: Boolean = true, + collectInheritedExtensionsFromLibraries: Boolean = false, + outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { + verifyModel( + *roots, + withJdk = withJdk, + withKotlinRuntime = withKotlinRuntime, + format = format, + includeNonPublic = includeNonPublic, + noStdlibLink = noStdlibLink, + collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries + ) { + verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator) + } +} + +fun verifyModelOutput(it: DocumentationModule, + outputExtension: String, + sourcePath: String, + outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { + val output = StringBuilder() + outputGenerator(it, output) + val ext = outputExtension.removePrefix(".") + val expectedFile = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)) + assertEqualsIgnoringSeparators(expectedFile, output.toString()) +} + +fun verifyOutput( + path: String, + outputExtension: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + format: String = "html", + includeNonPublic: Boolean = true, + noStdlibLink: Boolean = true, + collectInheritedExtensionsFromLibraries: Boolean = false, + outputGenerator: (DocumentationModule, StringBuilder) -> Unit +) { + verifyOutput( + arrayOf(contentRootFromPath(path)), + outputExtension, + withJdk, + withKotlinRuntime, + format, + includeNonPublic, + noStdlibLink, + collectInheritedExtensionsFromLibraries, + outputGenerator + ) +} + +fun verifyJavaOutput(path: String, + outputExtension: String, + withKotlinRuntime: Boolean = false, + format: String = "html", + outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { + verifyJavaModel(path, withKotlinRuntime, format) { model -> + verifyModelOutput(model, outputExtension, path, outputGenerator) + } +} + +fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) { + if (!expectedFile.exists()) expectedFile.createNewFile() + val expectedText = expectedFile.readText().replace("\r\n", "\n") + val actualText = output.replace("\r\n", "\n") + + if(expectedText != actualText) + throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath) +} + +fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) { + Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n")) +} + +fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder { + for (child in node.children) { + val childText = child.toTestString() + append(childText) + } + return this +} + +fun StringBuilder.appendNode(node: ContentNode): StringBuilder { + when (node) { + is ContentText -> { + append(node.text) + } + is ContentEmphasis -> append("*").appendChildren(node).append("*") + is ContentBlockCode -> { + if (node.language.isNotBlank()) + appendln("[code lang=${node.language}]") + else + appendln("[code]") + appendChildren(node) + appendln() + appendln("[/code]") + } + is ContentNodeLink -> { + append("[") + appendChildren(node) + append(" -> ") + append(node.node.toString()) + append("]") + } + is ContentBlock -> { + appendChildren(node) + } + is NodeRenderContent -> { + append("render(") + append(node.node) + append(",") + append(node.mode) + append(")") + } + is ContentSymbol -> { append(node.text) } + is ContentEmpty -> { /* nothing */ } + else -> throw IllegalStateException("Don't know how to format node $node") + } + return this +} + +fun ContentNode.toTestString(): String { + val node = this + return StringBuilder().apply { + appendNode(node) + }.toString() +} + +val ContentRoot.path: String + get() = when(this) { + is KotlinSourceRoot -> path + is JavaSourceRoot -> file.path + else -> throw UnsupportedOperationException() + } diff --git a/core/src/test/kotlin/format/DacFormatTest.kt b/core/src/test/kotlin/format/DacFormatTest.kt new file mode 100644 index 000000000..5d8babc3d --- /dev/null +++ b/core/src/test/kotlin/format/DacFormatTest.kt @@ -0,0 +1,58 @@ +package org.jetbrains.dokka.tests.format + +import org.jetbrains.dokka.Formats.DacAsJavaFormatDescriptor +import org.jetbrains.dokka.Formats.DacFormatDescriptor +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase +import org.junit.Test + +class DacFormatTest: DacFormatTestCase() { + val dacFormatDescriptor = DacFormatDescriptor() + val dacAsJavaFormatDescriptor = DacAsJavaFormatDescriptor() + val dacFormat = "dac" + val dacAsJavaFormat = "dac-as-java" + + private fun verifyBothFormats(directory: String) { + verifyDirectory(directory, dacFormatDescriptor, dacFormat) + verifyDirectory(directory, dacAsJavaFormatDescriptor, dacAsJavaFormat) + } + + @Test fun javaSeeTag() { + verifyBothFormats("javaSeeTag") + } + + @Test fun javaConstructor() { + verifyBothFormats("javaConstructor") + } + + @Test + fun javaSeeTagAsJava() { + verifyBothFormats("javaSeeTag") + } + + @Test + fun javaConstructorAsJava() { + verifyBothFormats("javaConstructor") + } + + @Test + fun javaDefaultConstructor() { + verifyBothFormats("javaDefaultConstructor") + } + + @Test + fun javaInheritedMethods() { + verifyBothFormats("inheritedMethods") + } + + @Test fun javaMethodVisibilities() { + verifyBothFormats("javaMethodVisibilities") + } + + @Test fun javaClassLinks() { + verifyBothFormats("javaClassLinks") + } + + @Test fun deprecation() { + verifyBothFormats("deprecation") + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/DacFormatTestCase.kt b/core/src/test/kotlin/format/DacFormatTestCase.kt new file mode 100644 index 000000000..922b58097 --- /dev/null +++ b/core/src/test/kotlin/format/DacFormatTestCase.kt @@ -0,0 +1,90 @@ +package org.jetbrains.dokka.tests.format + +import com.google.inject.Guice +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.name.Names +import org.jetbrains.dokka.DocumentationOptions +import org.jetbrains.dokka.DokkaLogger +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator +import org.jetbrains.dokka.Generator +import org.jetbrains.dokka.Utilities.bind +import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators +import org.jetbrains.dokka.tests.verifyModel +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import java.net.URI + +abstract class DacFormatTestCase { + @get:Rule + var folder = TemporaryFolder() + + protected fun verifyDirectory(directory: String, formatDescriptor: JavaLayoutHtmlFormatDescriptorBase, dokkaFormat: String) { + val injector: Injector by lazy { + val options = + DocumentationOptions( + folder.toString(), + dokkaFormat, + apiVersion = null, + languageVersion = null, + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = false, + noJdkLink = false, + collectInheritedExtensionsFromLibraries = true + ) + + Guice.createInjector(Module { binder -> + + binder.bind<Boolean>().annotatedWith(Names.named("generateClassIndex")).toInstance(false) + binder.bind<Boolean>().annotatedWith(Names.named("generatePackageIndex")).toInstance(false) + + binder.bind<String>().annotatedWith(Names.named("dacRoot")).toInstance("") + binder.bind<String>().annotatedWith(Names.named("outlineRoot")).toInstance("") + binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.root) + + binder.bind<DocumentationOptions>().toProvider { options } + binder.bind<DokkaLogger>().toInstance(object : DokkaLogger { + override fun info(message: String) { + println(message) + } + + override fun warn(message: String) { + println("WARN: $message") + } + + override fun error(message: String) { + println("ERROR: $message") + } + }) + + formatDescriptor.configureOutput(binder) + }) + } + + + val directoryFile = File("testdata/format/dac/$directory") + verifyModel( + JavaSourceRoot(directoryFile, null), KotlinSourceRoot(directoryFile.path, false), + format = dokkaFormat + ) { documentationModule -> + val nodes = documentationModule.members.single().members + with(injector.getInstance(Generator::class.java)) { + this as JavaLayoutHtmlFormatGenerator + buildPages(listOf(documentationModule)) + val byLocations = nodes.groupBy { mainUri(it) } + val tmpFolder = folder.root.toURI().resolve("${documentationModule.name}/") + byLocations.forEach { (loc, node) -> + val output = StringBuilder() + output.append(tmpFolder.resolve(URI("/").relativize(loc)).toURL().readText()) + val expectedFile = File(File(directoryFile, dokkaFormat), "${node.first().name}.html") + assertEqualsIgnoringSeparators(expectedFile, output.toString()) + } + } + } + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt new file mode 100644 index 000000000..ef9e815d2 --- /dev/null +++ b/core/src/test/kotlin/format/FileGeneratorTestCase.kt @@ -0,0 +1,35 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.junit.Before +import org.junit.Rule +import org.junit.rules.TemporaryFolder + + +abstract class FileGeneratorTestCase { + abstract val formatService: FormatService + + @get:Rule + var folder = TemporaryFolder() + + val fileGenerator = FileGenerator(folder.apply { create() }.root) + + @Before + fun bindGenerator() { + fileGenerator.formatService = formatService + } + + fun buildPagesAndReadInto(nodes: List<DocumentationNode>, sb: StringBuilder) = with(fileGenerator) { + buildPages(nodes) + val byLocations = nodes.groupBy { location(it) } + byLocations.forEach { (loc, _) -> + if (byLocations.size > 1) { + if (sb.isNotBlank() && !sb.endsWith('\n')) { + sb.appendln() + } + sb.appendln("<!-- File: ${loc.file.relativeTo(root).toUnixString()} -->") + } + sb.append(loc.file.readText()) + } + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt new file mode 100644 index 000000000..b90ab2bf2 --- /dev/null +++ b/core/src/test/kotlin/format/GFMFormatTest.kt @@ -0,0 +1,28 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.GFMFormatService +import org.jetbrains.dokka.KotlinLanguageService +import org.junit.Test + +class GFMFormatTest : FileGeneratorTestCase() { + override val formatService = GFMFormatService(fileGenerator, KotlinLanguageService(), listOf()) + + @Test + fun sample() { + verifyGFMNodeByName("sample", "Foo") + } + + @Test + fun listInTableCell() { + verifyGFMNodeByName("listInTableCell", "Foo") + } + + private fun verifyGFMNodeByName(fileName: String, name: String) { + verifyOutput("testdata/format/gfm/$fileName.kt", ".md") { model, output -> + buildPagesAndReadInto( + model.members.single().members.filter { it.name == name }, + output + ) + } + } +} diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt new file mode 100644 index 000000000..01e9b3c5f --- /dev/null +++ b/core/src/test/kotlin/format/HtmlFormatTest.kt @@ -0,0 +1,182 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.junit.Test +import java.io.File + +// TODO: add tests back +class HtmlFormatTest: FileGeneratorTestCase() { + override val formatService = HtmlFormatService(fileGenerator, KotlinLanguageService(), HtmlTemplateService.default(), listOf()) + + @Test fun classWithCompanionObject() { + verifyHtmlNode("classWithCompanionObject") + } + + @Test fun htmlEscaping() { + verifyHtmlNode("htmlEscaping") + } + + @Test fun overloads() { + verifyHtmlNodes("overloads") { model -> model.members } + } + + @Test fun overloadsWithDescription() { + verifyHtmlNode("overloadsWithDescription") + } + + @Test fun overloadsWithDifferentDescriptions() { + verifyHtmlNode("overloadsWithDifferentDescriptions") + } + + @Test fun deprecated() { + verifyOutput("testdata/format/deprecated.kt", ".package.html") { model, output -> + buildPagesAndReadInto(model.members, output) + } + verifyOutput("testdata/format/deprecated.kt", ".class.html") { model, output -> + buildPagesAndReadInto(model.members.single().members, output) + } + } + + @Test fun brokenLink() { + verifyHtmlNode("brokenLink") + } + + @Test fun codeSpan() { + verifyHtmlNode("codeSpan") + } + + @Test fun parenthesis() { + verifyHtmlNode("parenthesis") + } + + @Test fun bracket() { + verifyHtmlNode("bracket") + } + + @Test fun see() { + verifyHtmlNode("see") + } + + @Test fun tripleBackticks() { + verifyHtmlNode("tripleBackticks") + } + + @Test fun typeLink() { + verifyHtmlNodes("typeLink") { model -> model.members.single().members.filter { it.name == "Bar" } } + } + + @Test fun parameterAnchor() { + verifyHtmlNode("parameterAnchor") + } + + @Test fun javaSupertypeLink() { + verifyJavaHtmlNodes("JavaSupertype") { model -> + model.members.single().members.single { it.name == "JavaSupertype" }.members.filter { it.name == "Bar" } + } + } + + @Test fun codeBlock() { + verifyHtmlNode("codeBlock") + } + + @Test fun javaLinkTag() { + verifyJavaHtmlNode("javaLinkTag") + } + + @Test fun javaLinkTagWithLabel() { + verifyJavaHtmlNode("javaLinkTagWithLabel") + } + + @Test fun javaSeeTag() { + verifyJavaHtmlNode("javaSeeTag") + } + + @Test fun javaDeprecated() { + verifyJavaHtmlNodes("javaDeprecated") { model -> + model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" } + } + } + + @Test fun crossLanguageKotlinExtendsJava() { + verifyOutput(arrayOf( + KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt", false), + JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null)), + ".html") { model, output -> + buildPagesAndReadInto( + model.members.single().members.filter { it.name == "Bar" }, + output + ) + } + } + + @Test fun orderedList() { + verifyHtmlNodes("orderedList") { model -> model.members.single().members.filter { it.name == "Bar" } } + } + + @Test fun linkWithLabel() { + verifyHtmlNodes("linkWithLabel") { model -> model.members.single().members.filter { it.name == "Bar" } } + } + + @Test fun entity() { + verifyHtmlNodes("entity") { model -> model.members.single().members.filter { it.name == "Bar" } } + } + + @Test fun uninterpretedEmphasisCharacters() { + verifyHtmlNode("uninterpretedEmphasisCharacters") + } + + @Test fun markdownInLinks() { + verifyHtmlNode("markdownInLinks") + } + + @Test fun returnWithLink() { + verifyHtmlNode("returnWithLink") + } + + @Test fun linkWithStarProjection() { + verifyHtmlNode("linkWithStarProjection", withKotlinRuntime = true) + } + + @Test fun functionalTypeWithNamedParameters() { + verifyHtmlNode("functionalTypeWithNamedParameters") + } + + @Test fun sinceKotlin() { + verifyHtmlNode("sinceKotlin") + } + + @Test fun blankLineInsideCodeBlock() { + verifyHtmlNode("blankLineInsideCodeBlock") + } + + @Test fun indentedCodeBlock() { + verifyHtmlNode("indentedCodeBlock") + } + + private fun verifyHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) { + verifyHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members } + } + + private fun verifyHtmlNodes(fileName: String, + withKotlinRuntime: Boolean = false, + nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { + verifyOutput("testdata/format/$fileName.kt", ".html", withKotlinRuntime = withKotlinRuntime) { model, output -> + buildPagesAndReadInto(nodeFilter(model), output) + } + } + + private fun verifyJavaHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) { + verifyJavaHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members } + } + + private fun verifyJavaHtmlNodes(fileName: String, + withKotlinRuntime: Boolean = false, + nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { + verifyJavaOutput("testdata/format/$fileName.java", ".html", withKotlinRuntime = withKotlinRuntime) { model, output -> + buildPagesAndReadInto(nodeFilter(model), output) + } + } +} + diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt new file mode 100644 index 000000000..59746b10f --- /dev/null +++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt @@ -0,0 +1,114 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor +import org.junit.Test +import java.io.File +import java.net.URL + +class JavaLayoutHtmlFormatTest : JavaLayoutHtmlFormatTestCase() { + override val formatDescriptor = JavaLayoutHtmlFormatDescriptor() + +// @Test +// fun simple() { +// verifyNode("simple.kt") +// } +// +//// @Test +//// fun topLevel() { +//// verifyPackageNode("topLevel.kt") +//// } +// +// @Test +// fun codeBlocks() { +// verifyNode("codeBlocks.kt") { model -> +// listOf(model.members.single().members.single { it.name == "foo" }) +// } +// } +// +// @Test +// fun const() { +// verifyPackageNode("const.kt", noStdlibLink = true) +// verifyNode("const.kt", noStdlibLink = true) { model -> +// model.members.single().members.filter { it.kind in NodeKind.classLike } +// } +// } +// +// @Test +// fun externalClassExtension() { +// verifyPackageNode("externalClassExtension.kt") +// } +// +// @Test +// fun unresolvedExternalClass() { +// verifyNode("unresolvedExternalClass.kt", noStdlibLink = true) { model -> +// listOf(model.members.single().members.single { it.name == "MyException" }) +// } +// } +// +// @Test +// fun genericExtension() { +// verifyNode("genericExtension.kt", noStdlibLink = true) { model -> +// model.members.single().members(NodeKind.Class) +// } +// } +// +// +// @Test +// fun sections() { +// verifyNode("sections.kt", noStdlibLink = true) { model -> +// model.members.single().members.filter { it.name == "sectionsTest" } +// } +// } +// +// @Test +// fun constJava() { +// verifyNode("ConstJava.java", noStdlibLink = true) +// } +// +// @Test +// fun inboundLinksInKotlinMode() { +// val root = "./testdata/format/java-layout-html" +// +// val options = DocumentationOptions( +// "", +// "java-layout-html", +// sourceLinks = listOf(), +// generateClassIndexPage = false, +// generatePackageIndexPage = false, +// noStdlibLink = true, +// apiVersion = null, +// languageVersion = null, +// perPackageOptions = listOf(PackageOptionsImpl("foo", suppress = true)), +// externalDocumentationLinks = +// listOf( +// DokkaConfiguration.ExternalDocumentationLink.Builder( +// URL("file:///"), +// File(root, "inboundLinksTestPackageList").toURI().toURL() +// ).build() +// ) +// ) +// +// +// val sourcePath = "$root/inboundLinksInKotlinMode.kt" +// val documentation = DocumentationModule("test") +// +// appendDocumentation( +// documentation, +// contentRootFromPath(sourcePath), +// contentRootFromPath("$root/inboundLinksInKotlinMode.Dep.kt"), +// withJdk = false, +// withKotlinRuntime = false, +// options = options +// ) +// documentation.prepareForGeneration(options) +// +// verifyModelOutput(documentation, ".html", sourcePath) { model, output -> +// buildPagesAndReadInto( +// model, +// model.members.single { it.name == "bar" }.members, +// output +// ) +// } +// } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt new file mode 100644 index 000000000..620f10dda --- /dev/null +++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt @@ -0,0 +1,117 @@ +package org.jetbrains.dokka.tests + +import com.google.inject.Guice +import com.google.inject.Injector +import com.google.inject.Module +import com.google.inject.name.Names +import org.jetbrains.dokka.DocumentationNode +import org.jetbrains.dokka.DocumentationOptions +import org.jetbrains.dokka.DokkaLogger +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase +import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator +import org.jetbrains.dokka.Generator +import org.jetbrains.dokka.Utilities.bind +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import java.net.URI + +abstract class JavaLayoutHtmlFormatTestCase { + + abstract val formatDescriptor: JavaLayoutHtmlFormatDescriptorBase + + @get:Rule + var folder = TemporaryFolder() + + var options = + DocumentationOptions( + "", + "java-layout-html", + apiVersion = null, + languageVersion = null, + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = false, + noJdkLink = false, + collectInheritedExtensionsFromLibraries = true + ) + + val injector: Injector by lazy { + Guice.createInjector(Module { binder -> + binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.apply { create() }.root) + + binder.bind<DocumentationOptions>().toProvider { options } + binder.bind<DokkaLogger>().toInstance(object : DokkaLogger { + override fun info(message: String) { + println(message) + } + + override fun warn(message: String) { + println("WARN: $message") + } + + override fun error(message: String) { + println("ERROR: $message") + } + + }) + + formatDescriptor.configureOutput(binder) + }) + } + + + protected fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) = + with(injector.getInstance(Generator::class.java)) { + this as JavaLayoutHtmlFormatGenerator + buildPages(listOf(model)) + val byLocations = nodes.groupBy { mainUri(it) } + byLocations.forEach { (loc, _) -> + sb.appendln("<!-- File: $loc -->") + sb.append(folder.root.toURI().resolve(URI("/").relativize(loc)).toURL().readText()) + } + } + + + protected fun verifyNode( + fileName: String, + noStdlibLink: Boolean = false, + fileExtension: String = ".html", + select: (model: DocumentationNode) -> List<DocumentationNode> + ) { + verifyOutput( + "testdata/format/java-layout-html/$fileName", + fileExtension, + format = "java-layout-html", + withKotlinRuntime = true, + noStdlibLink = noStdlibLink, + collectInheritedExtensionsFromLibraries = true + ) { model, output -> + buildPagesAndReadInto( + model, + select(model), + output + ) + } + } + + protected fun verifyNode(fileName: String, noStdlibLink: Boolean = false) { + verifyNode(fileName, noStdlibLink) { model -> listOf(model.members.single().members.single()) } + } + + protected fun verifyPackageNode(fileName: String, noStdlibLink: Boolean = false) { + verifyOutput( + "testdata/format/java-layout-html/$fileName", + ".package-summary.html", + format = "java-layout-html", + withKotlinRuntime = true, + noStdlibLink = noStdlibLink + ) { model, output -> + buildPagesAndReadInto( + model, + listOf(model.members.single()), + output + ) + } + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt new file mode 100644 index 000000000..01ac58da4 --- /dev/null +++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt @@ -0,0 +1,74 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.junit.Before +import org.junit.Ignore +import org.junit.Test + +@Ignore +class KotlinWebSiteFormatTest: FileGeneratorTestCase() { + override val formatService = KotlinWebsiteFormatService(fileGenerator, KotlinLanguageService(), listOf(), DokkaConsoleLogger) + + @Test fun sample() { + verifyKWSNodeByName("sample", "foo") + } + + @Test fun returnTag() { + verifyKWSNodeByName("returnTag", "indexOf") + } + + @Test fun overloadGroup() { + verifyKWSNodeByName("overloadGroup", "magic") + } + + @Test fun dataTags() { + val module = buildMultiplePlatforms("dataTags") + verifyMultiplatformPackage(module, "dataTags") + } + + @Test fun dataTagsInGroupNode() { + val path = "dataTagsInGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto( + listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), + output + ) + } + verifyMultiplatformPackage(module, path) + } + + private fun verifyKWSNodeByName(fileName: String, name: String) { + verifyOutput("testdata/format/website/$fileName.kt", ".md", format = "kotlin-website") { model, output -> + buildPagesAndReadInto( + model.members.single().members.filter { it.name == name }, + output + ) + } + } + + private fun buildMultiplePlatforms(path: String): DocumentationModule { + val module = DocumentationModule("test") + val options = DocumentationOptions( + outputDir = "", + outputFormat = "html", + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = true, + noJdkLink = true, + languageVersion = null, + apiVersion = null + ) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options) + return module + } + + private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".package.md", "testdata/format/website/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto(model.members, output) + } + } + +} diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt new file mode 100644 index 000000000..63d7d5766 --- /dev/null +++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt @@ -0,0 +1,85 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.junit.Before +import org.junit.Test + +class KotlinWebSiteHtmlFormatTest: FileGeneratorTestCase() { + override val formatService = KotlinWebsiteHtmlFormatService(fileGenerator, KotlinLanguageService(), listOf(), EmptyHtmlTemplateService) + + @Test fun dropImport() { + verifyKWSNodeByName("dropImport", "foo") + } + + @Test fun sample() { + verifyKWSNodeByName("sample", "foo") + } + + @Test fun sampleWithAsserts() { + verifyKWSNodeByName("sampleWithAsserts", "a") + } + + @Test fun newLinesInSamples() { + verifyKWSNodeByName("newLinesInSamples", "foo") + } + + @Test fun newLinesInImportList() { + verifyKWSNodeByName("newLinesInImportList", "foo") + } + + @Test fun returnTag() { + verifyKWSNodeByName("returnTag", "indexOf") + } + + @Test fun overloadGroup() { + verifyKWSNodeByName("overloadGroup", "magic") + } + + @Test fun dataTags() { + val module = buildMultiplePlatforms("dataTags") + verifyMultiplatformPackage(module, "dataTags") + } + + @Test fun dataTagsInGroupNode() { + val path = "dataTagsInGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto( + listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), + output + ) + } + verifyMultiplatformPackage(module, path) + } + + private fun verifyKWSNodeByName(fileName: String, name: String) { + verifyOutput("testdata/format/website-html/$fileName.kt", ".html", format = "kotlin-website-html") { model, output -> + buildPagesAndReadInto(model.members.single().members.filter { it.name == name }, output) + } + } + + private fun buildMultiplePlatforms(path: String): DocumentationModule { + val module = DocumentationModule("test") + val options = DocumentationOptions( + outputDir = "", + outputFormat = "kotlin-website-html", + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = true, + noJdkLink = true, + languageVersion = null, + apiVersion = null + ) + appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website-html/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options) + return module + } + + private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".package.html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto(model.members, output) + } + } + +} diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt new file mode 100644 index 000000000..453b1de85 --- /dev/null +++ b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.DokkaConsoleLogger +import org.jetbrains.dokka.KotlinLanguageService +import org.jetbrains.dokka.KotlinWebsiteRunnableSamplesFormatService +import org.junit.Ignore +import org.junit.Test + +@Ignore +class KotlinWebSiteRunnableSamplesFormatTest { +// private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf(), DokkaConsoleLogger) +// +// +// @Test fun dropImport() { +// verifyKWSNodeByName("dropImport", "foo") +// } +// +// @Test fun sample() { +// verifyKWSNodeByName("sample", "foo") +// } +// +// @Test fun sampleWithAsserts() { +// verifyKWSNodeByName("sampleWithAsserts", "a") +// } +// +// @Test fun newLinesInSamples() { +// verifyKWSNodeByName("newLinesInSamples", "foo") +// } +// +// @Test fun newLinesInImportList() { +// verifyKWSNodeByName("newLinesInImportList", "foo") +// } +// +// private fun verifyKWSNodeByName(fileName: String, name: String) { +// verifyOutput("testdata/format/website-samples/$fileName.kt", ".md", format = "kotlin-website-samples") { model, output -> +// kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name }) +// } +// } +} diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt new file mode 100644 index 000000000..08d467995 --- /dev/null +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -0,0 +1,547 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.* +import org.junit.Before +import org.junit.Ignore +import org.junit.Test + +class MarkdownFormatTest: FileGeneratorTestCase() { + override val formatService = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) + + @Test fun emptyDescription() { + verifyMarkdownNode("emptyDescription") + } + + @Test fun classWithCompanionObject() { + verifyMarkdownNode("classWithCompanionObject") + } + + @Test fun annotations() { + verifyMarkdownNode("annotations") + } + + @Test fun annotationClass() { + verifyMarkdownNode("annotationClass", withKotlinRuntime = true) + verifyMarkdownPackage("annotationClass", withKotlinRuntime = true) + } + + @Test fun exceptionClass() { + verifyMarkdownNode("exceptionClass", withKotlinRuntime = true) + verifyMarkdownPackage("exceptionClass", withKotlinRuntime = true) + } + + @Test fun annotationParams() { + verifyMarkdownNode("annotationParams", withKotlinRuntime = true) + } + + @Test fun extensions() { + verifyOutput("testdata/format/extensions.kt", ".package.md") { model, output -> + buildPagesAndReadInto(model.members, output) + } + verifyOutput("testdata/format/extensions.kt", ".class.md") { model, output -> + buildPagesAndReadInto(model.members.single().members, output) + } + } + + @Test fun enumClass() { + verifyOutput("testdata/format/enumClass.kt", ".md") { model, output -> + buildPagesAndReadInto(model.members.single().members, output) + } + verifyOutput("testdata/format/enumClass.kt", ".value.md") { model, output -> + val enumClassNode = model.members.single().members[0] + buildPagesAndReadInto( + enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" }, + output + ) + } + } + + @Test fun varargsFunction() { + verifyMarkdownNode("varargsFunction") + } + + @Test fun overridingFunction() { + verifyMarkdownNodes("overridingFunction") { model-> + val classMembers = model.members.single().members.first { it.name == "D" }.members + classMembers.filter { it.name == "f" } + } + } + + @Test fun propertyVar() { + verifyMarkdownNode("propertyVar") + } + + @Test fun functionWithDefaultParameter() { + verifyMarkdownNode("functionWithDefaultParameter") + } + + @Test fun accessor() { + verifyMarkdownNodes("accessor") { model -> + model.members.single().members.first { it.name == "C" }.members.filter { it.name == "x" } + } + } + + @Test fun paramTag() { + verifyMarkdownNode("paramTag") + } + + @Test fun throwsTag() { + verifyMarkdownNode("throwsTag") + } + + @Test fun typeParameterBounds() { + verifyMarkdownNode("typeParameterBounds") + } + + @Test fun typeParameterVariance() { + verifyMarkdownNode("typeParameterVariance") + } + + @Test fun typeProjectionVariance() { + verifyMarkdownNode("typeProjectionVariance") + } + + @Test + fun javadocCodeMultiline() { + verifyJavaMarkdownNode("javadocCodeMultiline") + } + + // TODO: FIXME + @Ignore + @Test + fun javadocHtml() { + verifyJavaMarkdownNode("javadocHtml") + } + + // TODO: FIXME + @Ignore + @Test + fun javaCodeLiteralTags() { + verifyJavaMarkdownNode("javaCodeLiteralTags") + } + + @Test + fun javaSample() { + verifyJavaMarkdownNode("javaSample") + } + + // TODO: FIXME + @Ignore + @Test + fun javaCodeInParam() { + verifyJavaMarkdownNode("javaCodeInParam") + } + + @Test fun javaSpaceInAuthor() { + verifyJavaMarkdownNode("javaSpaceInAuthor") + } + + @Test fun nullability() { + verifyMarkdownNode("nullability") + } + + @Test fun operatorOverloading() { + verifyMarkdownNodes("operatorOverloading") { model-> + model.members.single().members.single { it.name == "C" }.members.filter { it.name == "plus" } + } + } + + @Test fun javadocOrderedList() { + verifyJavaMarkdownNodes("javadocOrderedList") { model -> + model.members.single().members.filter { it.name == "Bar" } + } + } + + @Test fun codeBlockNoHtmlEscape() { + verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic") + } + + @Test fun companionObjectExtension() { + verifyMarkdownNodeByName("companionObjectExtension", "Foo") + } + + @Test fun starProjection() { + verifyMarkdownNode("starProjection") + } + + @Test fun extensionFunctionParameter() { + verifyMarkdownNode("extensionFunctionParameter") + } + + @Test fun summarizeSignatures() { + verifyMarkdownNodes("summarizeSignatures") { model -> model.members } + } + + @Test fun summarizeSignaturesProperty() { + verifyMarkdownNodes("summarizeSignaturesProperty") { model -> model.members } + } + + @Test fun reifiedTypeParameter() { + verifyMarkdownNode("reifiedTypeParameter", withKotlinRuntime = true) + } + + @Test fun annotatedTypeParameter() { + verifyMarkdownNode("annotatedTypeParameter", withKotlinRuntime = true) + } + + @Test fun inheritedMembers() { + verifyMarkdownNodeByName("inheritedMembers", "Bar") + } + + @Test fun inheritedExtensions() { + verifyMarkdownNodeByName("inheritedExtensions", "Bar") + } + + @Test fun genericInheritedExtensions() { + verifyMarkdownNodeByName("genericInheritedExtensions", "Bar") + } + + @Test fun arrayAverage() { + verifyMarkdownNodeByName("arrayAverage", "XArray") + } + + @Test fun multipleTypeParameterConstraints() { + verifyMarkdownNode("multipleTypeParameterConstraints", withKotlinRuntime = true) + } + + @Test fun inheritedCompanionObjectProperties() { + verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C") + } + + @Test fun shadowedExtensionFunctions() { + verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar") + } + + @Test fun inapplicableExtensionFunctions() { + verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar") + } + + @Test fun receiverParameterTypeBound() { + verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo") + } + + @Test fun extensionWithDocumentedReceiver() { + verifyMarkdownNodes("extensionWithDocumentedReceiver") { model -> + model.members.single().members.single().members.filter { it.name == "fn" } + } + } + + @Test fun codeBlock() { + verifyMarkdownNode("codeBlock") + } + + @Test fun exclInCodeBlock() { + verifyMarkdownNodeByName("exclInCodeBlock", "foo") + } + + @Test fun backtickInCodeBlock() { + verifyMarkdownNodeByName("backtickInCodeBlock", "foo") + } + + @Test fun qualifiedNameLink() { + verifyMarkdownNodeByName("qualifiedNameLink", "foo", withKotlinRuntime = true) + } + + @Test fun functionalTypeWithNamedParameters() { + verifyMarkdownNode("functionalTypeWithNamedParameters") + } + + @Test fun typeAliases() { + verifyMarkdownNode("typeAliases") + verifyMarkdownPackage("typeAliases") + } + + @Test fun sampleByFQName() { + verifyMarkdownNode("sampleByFQName") + } + + @Test fun sampleByShortName() { + verifyMarkdownNode("sampleByShortName") + } + + + @Test fun suspendParam() { + verifyMarkdownNode("suspendParam") + verifyMarkdownPackage("suspendParam") + } + + @Test fun sinceKotlin() { + verifyMarkdownNode("sinceKotlin") + verifyMarkdownPackage("sinceKotlin") + } + + @Test fun sinceKotlinWide() { + verifyMarkdownPackage("sinceKotlinWide") + } + + @Test fun dynamicType() { + verifyMarkdownNode("dynamicType") + } + + @Test fun dynamicExtension() { + verifyMarkdownNodes("dynamicExtension") { model -> model.members.single().members.filter { it.name == "Foo" } } + } + + @Test fun memberExtension() { + verifyMarkdownNodes("memberExtension") { model -> model.members.single().members.filter { it.name == "Foo" } } + } + + @Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() { + verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver") + } + + @Test fun multiplePlatforms() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple") + } + + @Test fun multiplePlatformsMerge() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge") + } + + @Test fun multiplePlatformsMergeMembers() { + val module = buildMultiplePlatforms("multiplatform/mergeMembers") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output -> + buildPagesAndReadInto(model.members.single().members, output) + } + } + + @Test fun multiplePlatformsOmitRedundant() { + val module = buildMultiplePlatforms("multiplatform/omitRedundant") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output -> + buildPagesAndReadInto(model.members.single().members, output) + } + } + + @Test fun multiplePlatformsImplied() { + val module = buildMultiplePlatforms("multiplatform/implied") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output -> + val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf("JVM", "JS")) + fileGenerator.formatService = service + buildPagesAndReadInto(model.members.single().members, output) + } + } + + @Test fun packagePlatformsWithExtExtensions() { + val path = "multiplatform/packagePlatformsWithExtExtensions" + val module = DocumentationModule("test") + val options = DocumentationOptions( + outputDir = "", + outputFormat = "html", + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = true, + noJdkLink = true, + languageVersion = null, + apiVersion = null + ) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), withKotlinRuntime = true, options = options) + verifyMultiplatformIndex(module, path) + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsPackagePlatformFromMembers() { + val path = "multiplatform/packagePlatformsFromMembers" + val module = buildMultiplePlatforms(path) + verifyMultiplatformIndex(module, path) + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsGroupNode() { + val path = "multiplatform/groupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto( + listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), + output + ) + } + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() { + val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto( + listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Class)?.member(NodeKind.Function)), + output + ) + } + } + + @Test fun linksInEmphasis() { + verifyMarkdownNode("linksInEmphasis") + } + + @Test fun linksInStrong() { + verifyMarkdownNode("linksInStrong") + } + + @Test fun linksInHeaders() { + verifyMarkdownNode("linksInHeaders") + } + + @Test fun tokensInEmphasis() { + verifyMarkdownNode("tokensInEmphasis") + } + + @Test fun tokensInStrong() { + verifyMarkdownNode("tokensInStrong") + } + + @Test fun tokensInHeaders() { + verifyMarkdownNode("tokensInHeaders") + } + + @Test fun unorderedLists() { + verifyMarkdownNode("unorderedLists") + } + + @Test fun nestedLists() { + verifyMarkdownNode("nestedLists") + } + + @Test fun referenceLink() { + verifyMarkdownNode("referenceLink") + } + + @Test fun externalReferenceLink() { + verifyMarkdownNode("externalReferenceLink") + } + + @Test fun newlineInTableCell() { + verifyMarkdownPackage("newlineInTableCell") + } + + @Test fun indentedCodeBlock() { + verifyMarkdownNode("indentedCodeBlock") + } + + @Test fun receiverReference() { + verifyMarkdownNode("receiverReference") + } + + @Test fun extensionScope() { + verifyMarkdownNodeByName("extensionScope", "test") + } + + @Test fun typeParameterReference() { + verifyMarkdownNode("typeParameterReference") + } + + @Test fun notPublishedTypeAliasAutoExpansion() { + verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", includeNonPublic = false) + } + + @Test fun companionImplements() { + verifyMarkdownNodeByName("companionImplements", "Foo") + } + + @Test fun enumRef() { + verifyMarkdownNode("enumRef") + } + + @Test fun inheritedLink() { + val filePath = "testdata/format/inheritedLink" + verifyOutput( + arrayOf( + contentRootFromPath("$filePath.kt"), + contentRootFromPath("$filePath.1.kt") + ), + ".md", + withJdk = true, + withKotlinRuntime = true, + includeNonPublic = false + ) { model, output -> + buildPagesAndReadInto(model.members.single { it.name == "p2" }.members.single().members, output) + } + } + + + private fun buildMultiplePlatforms(path: String): DocumentationModule { + val module = DocumentationModule("test") + val options = DocumentationOptions( + outputDir = "", + outputFormat = "html", + generateClassIndexPage = false, + generatePackageIndexPage = false, + noStdlibLink = true, + noJdkLink = true, + languageVersion = null, + apiVersion = null + ) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options) + return module + } + + private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output -> + buildPagesAndReadInto(model.members, output) + } + } + + private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") { + model, output -> + val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) + fileGenerator.formatService = service + buildPagesAndReadInto(listOf(model), output) + } + } + + @Test fun blankLineInsideCodeBlock() { + verifyMarkdownNode("blankLineInsideCodeBlock") + } + + private fun verifyMarkdownPackage(fileName: String, withKotlinRuntime: Boolean = false) { + verifyOutput("testdata/format/$fileName.kt", ".package.md", withKotlinRuntime = withKotlinRuntime) { model, output -> + buildPagesAndReadInto(model.members, output) + } + } + + private fun verifyMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) { + verifyMarkdownNodes(fileName, withKotlinRuntime) { model -> model.members.single().members } + } + + private fun verifyMarkdownNodes( + fileName: String, + withKotlinRuntime: Boolean = false, + includeNonPublic: Boolean = true, + nodeFilter: (DocumentationModule) -> List<DocumentationNode> + ) { + verifyOutput( + "testdata/format/$fileName.kt", + ".md", + withKotlinRuntime = withKotlinRuntime, + includeNonPublic = includeNonPublic + ) { model, output -> + buildPagesAndReadInto(nodeFilter(model), output) + } + } + + private fun verifyJavaMarkdownNode(fileName: String, withKotlinRuntime: Boolean = false) { + verifyJavaMarkdownNodes(fileName, withKotlinRuntime) { model -> model.members.single().members } + } + + private fun verifyJavaMarkdownNodes(fileName: String, withKotlinRuntime: Boolean = false, nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { + verifyJavaOutput("testdata/format/$fileName.java", ".md", withKotlinRuntime = withKotlinRuntime) { model, output -> + buildPagesAndReadInto(nodeFilter(model), output) + } + } + + private fun verifyMarkdownNodeByName( + fileName: String, + name: String, + withKotlinRuntime: Boolean = false, + includeNonPublic: Boolean = true + ) { + verifyMarkdownNodes(fileName, withKotlinRuntime, includeNonPublic) { model-> + val nodesWithName = model.members.single().members.filter { it.name == name } + if (nodesWithName.isEmpty()) { + throw IllegalArgumentException("Found no nodes named $name") + } + nodesWithName + } + } +} diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt new file mode 100644 index 000000000..b7fff1e2e --- /dev/null +++ b/core/src/test/kotlin/format/PackageDocsTest.kt @@ -0,0 +1,92 @@ +package org.jetbrains.dokka.tests.format + +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Disposer +import com.nhaarman.mockito_kotlin.any +import com.nhaarman.mockito_kotlin.doAnswer +import com.nhaarman.mockito_kotlin.eq +import com.nhaarman.mockito_kotlin.mock +import org.jetbrains.dokka.* +import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import java.io.File + +class PackageDocsTest { + + private lateinit var testDisposable: Disposable + + @Before + fun setup() { + testDisposable = Disposer.newDisposable() + } + + @After + fun cleanup() { + Disposer.dispose(testDisposable) + } + + fun createPackageDocs(linkResolver: DeclarationLinkResolver?): PackageDocs { + val environment = KotlinCoreEnvironment.createForTests(testDisposable, CompilerConfiguration.EMPTY, EnvironmentConfigFiles.JVM_CONFIG_FILES) + return PackageDocs(linkResolver, DokkaConsoleLogger, environment, mock(), mock()) + } + + @Test fun verifyParse() { + + val docs = createPackageDocs(null) + docs.parse("testdata/packagedocs/stdlib.md", emptyList()) + val packageContent = docs.packageContent["kotlin"]!! + val block = (packageContent.children.single() as ContentBlock).children.first() as ContentText + assertEquals("Core functions and types", block.text) + } + + @Test fun testReferenceLinksInPackageDocs() { + val mockLinkResolver = mock<DeclarationLinkResolver> { + val exampleCom = "http://example.com" + on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) } + } + + val mockPackageDescriptor = mock<PackageFragmentDescriptor> {} + + val docs = createPackageDocs(mockLinkResolver) + docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor)) + + checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks") + } + + fun checkMarkdownOutput(docs: PackageDocs, expectedFilePrefix: String) { + + val generator = FileGenerator(File("")) + + val out = StringBuilder() + val outputBuilder = MarkdownOutputBuilder( + out, + FileLocation(generator.root), + generator, + KotlinLanguageService(), + ".md", + emptyList() + ) + fun checkOutput(content: Content, filePostfix: String) { + outputBuilder.appendContent(content) + val expectedFile = File(expectedFilePrefix + filePostfix) + assertEqualsIgnoringSeparators(expectedFile, out.toString()) + out.setLength(0) + } + + checkOutput(docs.moduleContent, ".module.md") + + docs.packageContent.forEach { + (name, content) -> + checkOutput(content, ".$name.md") + } + + } +} diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt new file mode 100644 index 000000000..d61088180 --- /dev/null +++ b/core/src/test/kotlin/issues/IssuesTest.kt @@ -0,0 +1,28 @@ +package issues + +import org.jetbrains.dokka.DocumentationNode +import org.jetbrains.dokka.NodeKind +import org.jetbrains.dokka.tests.toTestString +import org.jetbrains.dokka.tests.verifyModel +import org.junit.Test +import kotlin.test.assertEquals + + +class IssuesTest { + + @Test + fun errorClasses() { + verifyModel("testdata/issues/errorClasses.kt", withJdk = true, withKotlinRuntime = true) { model -> + val cls = model.members.single().members.single() + + fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name + assertEquals("Test", cls.members[1].returnType()) + assertEquals("List", cls.members[2].returnType()) + assertEquals("Test", cls.members[3].returnType()) + assertEquals("Test", cls.members[4].returnType()) + assertEquals("String", cls.members[5].returnType()) + assertEquals("String", cls.members[6].returnType()) + assertEquals("String", cls.members[7].returnType()) + } + } +} diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt new file mode 100644 index 000000000..a42d63933 --- /dev/null +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -0,0 +1,185 @@ +package org.jetbrains.dokka.javadoc + +import com.sun.javadoc.Tag +import com.sun.javadoc.Type +import org.jetbrains.dokka.DokkaConsoleLogger +import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators +import org.jetbrains.dokka.tests.verifyModel +import org.junit.Assert.* +import org.junit.Test + +class JavadocTest { + @Test fun testTypes() { + verifyJavadoc("testdata/javadoc/types.kt", withJdk = true) { doc -> + val classDoc = doc.classNamed("foo.TypesKt")!! + val method = classDoc.methods().find { it.name() == "foo" }!! + + val type = method.returnType() + assertFalse(type.asClassDoc().isIncluded) + assertEquals("String", type.qualifiedTypeName()) + assertEquals("String", type.asClassDoc().qualifiedName()) + + val params = method.parameters() + assertTrue(params[0].type().isPrimitive) + assertFalse(params[1].type().asClassDoc().isIncluded) + } + } + + @Test fun testObject() { + verifyJavadoc("testdata/javadoc/obj.kt") { doc -> + val classDoc = doc.classNamed("foo.O") + assertNotNull(classDoc) + + val companionDoc = doc.classNamed("foo.O.Companion") + assertNotNull(companionDoc) + + val pkgDoc = doc.packageNamed("foo")!! + assertEquals(2, pkgDoc.allClasses().size) + } + } + + @Test fun testException() { + verifyJavadoc("testdata/javadoc/exception.kt", withKotlinRuntime = true) { doc -> + val classDoc = doc.classNamed("foo.MyException")!! + val member = classDoc.methods().find { it.name() == "foo" } + assertEquals(classDoc, member!!.containingClass()) + } + } + + @Test fun testByteArray() { + verifyJavadoc("testdata/javadoc/bytearr.kt", withKotlinRuntime = true) { doc -> + val classDoc = doc.classNamed("foo.ByteArray")!! + assertNotNull(classDoc.asClassDoc()) + + val member = classDoc.methods().find { it.name() == "foo" }!! + assertEquals("[]", member.returnType().dimension()) + } + } + + @Test fun testStringArray() { + verifyJavadoc("testdata/javadoc/stringarr.kt", withKotlinRuntime = true) { doc -> + val classDoc = doc.classNamed("foo.Foo")!! + assertNotNull(classDoc.asClassDoc()) + + val member = classDoc.methods().find { it.name() == "main" }!! + val paramType = member.parameters()[0].type() + assertNull(paramType.asParameterizedType()) + assertEquals("String", paramType.typeName()) + assertEquals("String", paramType.asClassDoc().name()) + } + } + + @Test fun testJvmName() { + verifyJavadoc("testdata/javadoc/jvmname.kt", withKotlinRuntime = true) { doc -> + val classDoc = doc.classNamed("foo.Apple")!! + assertNotNull(classDoc.asClassDoc()) + + val member = classDoc.methods().find { it.name() == "_tree" } + assertNotNull(member) + } + } + + @Test fun testLinkWithParam() { + verifyJavadoc("testdata/javadoc/paramlink.kt", withKotlinRuntime = true) { doc -> + val classDoc = doc.classNamed("demo.Apple")!! + assertNotNull(classDoc.asClassDoc()) + val tags = classDoc.inlineTags().filterIsInstance<SeeTagAdapter>() + assertEquals(2, tags.size) + val linkTag = tags[1] as SeeMethodTagAdapter + assertEquals("cutIntoPieces", linkTag.method.name()) + } + } + + @Test fun testInternalVisibility() { + verifyJavadoc("testdata/javadoc/internal.kt", withKotlinRuntime = true, includeNonPublic = false) { doc -> + val classDoc = doc.classNamed("foo.Person")!! + val constructors = classDoc.constructors() + assertEquals(1, constructors.size) + assertEquals(1, constructors.single().parameters().size) + } + } + + @Test fun testSuppress() { + verifyJavadoc("testdata/javadoc/suppress.kt", withKotlinRuntime = true) { doc -> + assertNull(doc.classNamed("Some")) + assertNull(doc.classNamed("SomeAgain")) + assertNull(doc.classNamed("Interface")) + val classSame = doc.classNamed("Same")!! + assertTrue(classSame.fields().isEmpty()) + assertTrue(classSame.methods().isEmpty()) + } + } + + @Test fun testTypeAliases() { + verifyJavadoc("testdata/javadoc/typealiases.kt", withKotlinRuntime = true) { doc -> + assertNull(doc.classNamed("B")) + assertNull(doc.classNamed("D")) + + assertEquals("A", doc.classNamed("C")!!.superclass().name()) + val methodParamType = doc.classNamed("TypealiasesKt")!!.methods() + .find { it.name() == "some" }!!.parameters().first() + .type() + assertEquals("Function1", methodParamType.qualifiedTypeName()) + assertEquals("? super A, C", methodParamType.asParameterizedType().typeArguments() + .map(Type::qualifiedTypeName).joinToString()) + } + } + + @Test fun testKDocKeywordsOnMethod() { + verifyJavadoc("testdata/javadoc/kdocKeywordsOnMethod.kt", withKotlinRuntime = true) { doc -> + val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0] + assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text()) + assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text()) + assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text()) + } + } + + @Test + fun testBlankLineInsideCodeBlock() { + verifyJavadoc("testdata/javadoc/blankLineInsideCodeBlock.kt", withKotlinRuntime = true) { doc -> + val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0] + val text = method.inlineTags().joinToString(separator = "", transform = Tag::text) + assertEqualsIgnoringSeparators(""" + <p><code><pre> + This is a test + of Dokka's code blocks. + Here is a blank line. + + The previous line was blank. + </pre></code></p> + """.trimIndent(), text) + } + } + + @Test + fun testCompanionMethodReference() { + verifyJavadoc("testdata/javadoc/companionMethodReference.kt") { doc -> + val classDoc = doc.classNamed("foo.TestClass")!! + val tag = classDoc.inlineTags().filterIsInstance<SeeMethodTagAdapter>().first() + assertEquals("TestClass.Companion", tag.referencedClassName()) + assertEquals("test", tag.referencedMemberName()) + } + } + + @Test fun shouldHaveAllFunctionMarkedAsDeprecated() { + verifyJavadoc("testdata/javadoc/deprecated.java") { doc -> + val classDoc = doc.classNamed("bar.Banana")!! + + classDoc.methods().forEach { method -> + assertTrue(method.tags().any { it.kind() == "deprecated" }) + } + } + } + + private fun verifyJavadoc(name: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + includeNonPublic: Boolean = true, + callback: (ModuleNodeAdapter) -> Unit) { + + verifyModel(name, format = "javadoc", withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, includeNonPublic = includeNonPublic) { model -> + val doc = ModuleNodeAdapter(model, StandardReporter(DokkaConsoleLogger), "") + callback(doc) + } + } +} diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt new file mode 100644 index 000000000..b0ec68ff9 --- /dev/null +++ b/core/src/test/kotlin/markdown/ParserTest.kt @@ -0,0 +1,154 @@ +package org.jetbrains.dokka.tests + +import org.junit.Test +import org.jetbrains.dokka.toTestString +import org.jetbrains.dokka.parseMarkdown +import org.junit.Ignore + +@Ignore public class ParserTest { + fun runTestFor(text : String) { + println("MD: ---") + println(text) + val markdownTree = parseMarkdown(text) + println("AST: ---") + println(markdownTree.toTestString()) + println() + } + + @Test fun text() { + runTestFor("text") + } + + @Test fun textWithSpaces() { + runTestFor("text and string") + } + + @Test fun textWithColon() { + runTestFor("text and string: cool!") + } + + @Test fun link() { + runTestFor("text [links]") + } + + @Test fun linkWithHref() { + runTestFor("text [links](http://google.com)") + } + + @Test fun multiline() { + runTestFor( + """ +text +and +string +""") + } + + @Test fun para() { + runTestFor( + """ +paragraph number +one + +paragraph +number two +""") + } + + @Test fun bulletList() { + runTestFor( + """* list item 1 +* list item 2 +""") + } + + @Test fun bulletListWithLines() { + runTestFor( + """ +* list item 1 + continue 1 +* list item 2 + continue 2 + """) + } + + @Test fun bulletListStrong() { + runTestFor( + """ +* list *item* 1 + continue 1 +* list *item* 2 + continue 2 + """) + } + + @Test fun emph() { + runTestFor("*text*") + } + + @Test fun underscoresNoEmph() { + runTestFor("text_with_underscores") + } + + @Test fun emphUnderscores() { + runTestFor("_text_") + } + + @Test fun singleStar() { + runTestFor("Embedded*Star") + } + + @Test fun directive() { + runTestFor("A text \${code with.another.value} with directive") + } + + @Test fun emphAndEmptySection() { + runTestFor("*text*\n\$sec:\n") + } + + @Test fun emphAndSection() { + runTestFor("*text*\n\$sec: some text\n") + } + + @Test fun emphAndBracedSection() { + runTestFor("Text *bold* text \n\${sec}: some text") + } + + @Test fun section() { + runTestFor( + "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example") + } + + @Test fun anonymousSection() { + runTestFor("Summary\n\nDescription\n") + } + + @Test fun specialSection() { + runTestFor( + "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example") + } + + @Test fun emptySection() { + runTestFor( + "Plain text \n\$summary:") + } + + val b = "$" + @Test fun pair() { + runTestFor( + """Represents a generic pair of two values. + +There is no meaning attached to values in this class, it can be used for any purpose. +Pair exhibits value semantics, i.e. two pairs are equal if both components are equal. + +An example of decomposing it into values: +${b}{code test.tuples.PairTest.pairMultiAssignment} + +${b}constructor: Creates new instance of [Pair] +${b}first: First value +${b}second: Second value"""" + ) + } + +} + diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt new file mode 100644 index 000000000..6bc45db10 --- /dev/null +++ b/core/src/test/kotlin/model/ClassTest.kt @@ -0,0 +1,293 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.jetbrains.dokka.RefKind +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class ClassTest { + @Test fun emptyClass() { + verifyModel("testdata/classes/emptyClass.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(NodeKind.Class, kind) + assertEquals("Klass", name) + assertEquals(Content.Empty, content) + assertEquals("<init>", members.single().name) + assertTrue(links.none()) + } + } + } + + @Test fun emptyObject() { + verifyModel("testdata/classes/emptyObject.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(NodeKind.Object, kind) + assertEquals("Obj", name) + assertEquals(Content.Empty, content) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun classWithConstructor() { + verifyModel("testdata/classes/classWithConstructor.kt") { model -> + with (model.members.single().members.single()) { + assertEquals(NodeKind.Class, kind) + assertEquals("Klass", name) + assertEquals(Content.Empty, content) + assertTrue(links.none()) + + assertEquals(1, members.count()) + with(members.elementAt(0)) { + assertEquals("<init>", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Constructor, kind) + assertEquals(3, details.count()) + assertEquals("public", details.elementAt(0).name) + with(details.elementAt(2)) { + assertEquals("name", name) + assertEquals(NodeKind.Parameter, kind) + assertEquals(Content.Empty, content) + assertEquals("String", detail(NodeKind.Type).name) + assertTrue(links.none()) + assertTrue(members.none()) + } + assertTrue(links.none()) + assertTrue(members.none()) + } + } + } + } + + @Test fun classWithFunction() { + verifyModel("testdata/classes/classWithFunction.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(NodeKind.Class, kind) + assertEquals("Klass", name) + assertEquals(Content.Empty, content) + assertTrue(links.none()) + + assertEquals(2, members.count()) + with(members.elementAt(0)) { + assertEquals("<init>", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Constructor, kind) + assertEquals(2, details.count()) + assertEquals("public", details.elementAt(0).name) + assertTrue(links.none()) + assertTrue(members.none()) + } + with(members.elementAt(1)) { + assertEquals("fn", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Function, kind) + assertEquals("Unit", detail(NodeKind.Type).name) + assertTrue(links.none()) + assertTrue(members.none()) + } + } + } + } + + @Test fun classWithProperty() { + verifyModel("testdata/classes/classWithProperty.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(NodeKind.Class, kind) + assertEquals("Klass", name) + assertEquals(Content.Empty, content) + assertTrue(links.none()) + + assertEquals(2, members.count()) + with(members.elementAt(0)) { + assertEquals("<init>", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Constructor, kind) + assertEquals(2, details.count()) + assertEquals("public", details.elementAt(0).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + with(members.elementAt(1)) { + assertEquals("name", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Property, kind) + assertEquals("String", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + } + + @Test fun classWithCompanionObject() { + verifyModel("testdata/classes/classWithCompanionObject.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(NodeKind.Class, kind) + assertEquals("Klass", name) + assertEquals(Content.Empty, content) + assertTrue(links.none()) + + assertEquals(3, members.count()) + with(members.elementAt(0)) { + assertEquals("<init>", name) + assertEquals(Content.Empty, content) + } + with(members.elementAt(1)) { + assertEquals("foo", name) + assertEquals(NodeKind.CompanionObjectFunction, kind) + assertTrue(members.none()) + assertTrue(links.none()) + } + with(members.elementAt(2)) { + assertEquals("x", name) + assertEquals(NodeKind.CompanionObjectProperty, kind) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + } + + @Test fun annotatedClass() { + verifyPackageMember("testdata/classes/annotatedClass.kt", withKotlinRuntime = true) { cls -> + assertEquals(1, cls.annotations.count()) + with(cls.annotations[0]) { + assertEquals("Strictfp", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + } + } + } + + @Test fun dataClass() { + verifyPackageMember("testdata/classes/dataClass.kt") { cls -> + val modifiers = cls.details(NodeKind.Modifier).map { it.name } + assertTrue("data" in modifiers) + } + } + + @Test fun sealedClass() { + verifyPackageMember("testdata/classes/sealedClass.kt") { cls -> + val modifiers = cls.details(NodeKind.Modifier).map { it.name } + assertEquals(1, modifiers.count { it == "sealed" }) + } + } + + @Test fun annotatedClassWithAnnotationParameters() { + verifyModel("testdata/classes/annotatedClassWithAnnotationParameters.kt") { model -> + with(model.members.single().members.single()) { + with(deprecation!!) { + assertEquals("Deprecated", name) + // assertEquals(Content.Empty, content) // this is now an empty MutableContent instead + assertEquals(NodeKind.Annotation, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Value, kind) + assertEquals("\"should no longer be used\"", name) + } + } + } + } + } + } + + @Test fun javaAnnotationClass() { + verifyModel("testdata/classes/javaAnnotationClass.kt", withJdk = true) { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("Retention", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + with(details[0]) { + assertEquals(NodeKind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Value, kind) + assertEquals("RetentionPolicy.SOURCE", name) + } + } + } + } + } + } + + @Test fun notOpenClass() { + verifyModel("testdata/classes/notOpenClass.kt") { model -> + with(model.members.single().members.first { it.name == "D"}.members.first { it.name == "f" }) { + val modifiers = details(NodeKind.Modifier) + assertEquals(2, modifiers.size) + assertEquals("final", modifiers[1].name) + + val overrideReferences = references(RefKind.Override) + assertEquals(1, overrideReferences.size) + } + } + } + + @Test fun indirectOverride() { + verifyModel("testdata/classes/indirectOverride.kt") { model -> + with(model.members.single().members.first { it.name == "E"}.members.first { it.name == "foo" }) { + val modifiers = details(NodeKind.Modifier) + assertEquals(2, modifiers.size) + assertEquals("final", modifiers[1].name) + + val overrideReferences = references(RefKind.Override) + assertEquals(1, overrideReferences.size) + } + } + } + + @Test fun innerClass() { + verifyPackageMember("testdata/classes/innerClass.kt") { cls -> + val innerClass = cls.members.single { it.name == "D" } + val modifiers = innerClass.details(NodeKind.Modifier) + assertEquals(3, modifiers.size) + assertEquals("inner", modifiers[2].name) + } + } + + @Test fun companionObjectExtension() { + verifyModel("testdata/classes/companionObjectExtension.kt") { model -> + val pkg = model.members.single() + val cls = pkg.members.single { it.name == "Foo" } + val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty } + assertEquals(1, extensions.size) + } + } + + @Test fun secondaryConstructor() { + verifyPackageMember("testdata/classes/secondaryConstructor.kt") { cls -> + val constructors = cls.members(NodeKind.Constructor) + assertEquals(2, constructors.size) + with (constructors.first { it.details(NodeKind.Parameter).size == 1}) { + assertEquals("<init>", name) + assertEquals("This is a secondary constructor.", summary.toTestString()) + } + } + } + + @Test fun sinceKotlin() { + verifyModel("testdata/classes/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } + + @Test fun privateCompanionObject() { + verifyModel("testdata/classes/privateCompanionObject.kt", includeNonPublic = false) { model -> + with(model.members.single().members.single()) { + assertEquals(0, members(NodeKind.CompanionObjectFunction).size) + assertEquals(0, members(NodeKind.CompanionObjectProperty).size) + } + } + } + +} diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt new file mode 100644 index 000000000..7869837c1 --- /dev/null +++ b/core/src/test/kotlin/model/CommentTest.kt @@ -0,0 +1,186 @@ +package org.jetbrains.dokka.tests + +import org.junit.Test +import org.junit.Assert.* +import org.jetbrains.dokka.* + +public class CommentTest { + + @Test fun codeBlockComment() { + verifyModel("testdata/comments/codeBlockComment.kt") { model -> + with(model.members.single().members.first()) { + assertEqualsIgnoringSeparators("""[code lang=brainfuck] + | + |++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + | + |[/code] + |""".trimMargin(), + content.toTestString()) + } + with(model.members.single().members.last()) { + assertEqualsIgnoringSeparators("""[code] + | + |a + b - c + | + |[/code] + |""".trimMargin(), + content.toTestString()) + } + } + } + + @Test fun emptyDoc() { + verifyModel("testdata/comments/emptyDoc.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(Content.Empty, content) + } + } + } + + @Test fun emptyDocButComment() { + verifyModel("testdata/comments/emptyDocButComment.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(Content.Empty, content) + } + } + } + + @Test fun multilineDoc() { + verifyModel("testdata/comments/multilineDoc.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("doc1", content.summary.toTestString()) + assertEquals("doc2\ndoc3", content.description.toTestString()) + } + } + } + + @Test fun multilineDocWithComment() { + verifyModel("testdata/comments/multilineDocWithComment.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("doc1", content.summary.toTestString()) + assertEquals("doc2\ndoc3", content.description.toTestString()) + } + } + } + + @Test fun oneLineDoc() { + verifyModel("testdata/comments/oneLineDoc.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("doc", content.summary.toTestString()) + } + } + } + + @Test fun oneLineDocWithComment() { + verifyModel("testdata/comments/oneLineDocWithComment.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("doc", content.summary.toTestString()) + } + } + } + + @Test fun oneLineDocWithEmptyLine() { + verifyModel("testdata/comments/oneLineDocWithEmptyLine.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("doc", content.summary.toTestString()) + } + } + } + + @Test fun emptySection() { + verifyModel("testdata/comments/emptySection.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Summary", content.summary.toTestString()) + assertEquals(1, content.sections.count()) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) + assertEquals("", toTestString()) + } + } + } + } + + @Test fun quotes() { + verifyModel("testdata/comments/quotes.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("it's \"useful\"", content.summary.toTestString()) + } + } + } + + @Test fun section1() { + verifyModel("testdata/comments/section1.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Summary", content.summary.toTestString()) + assertEquals(1, content.sections.count()) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) + assertEquals("section one", toTestString()) + } + } + } + } + + @Test fun section2() { + verifyModel("testdata/comments/section2.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Summary", content.summary.toTestString()) + assertEquals(2, content.sections.count()) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) + assertEquals("section one", toTestString()) + } + with (content.findSectionByTag("two")!!) { + assertEquals("Two", tag) + assertEquals("section two", toTestString()) + } + } + } + } + + @Test fun multilineSection() { + verifyModel("testdata/comments/multilineSection.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Summary", content.summary.toTestString()) + assertEquals(1, content.sections.count()) + with (content.findSectionByTag("one")!!) { + assertEquals("One", tag) + assertEquals("""line one +line two""", toTestString()) + } + } + } + } + + @Test fun directive() { + verifyModel("testdata/comments/directive.kt") { model -> + with(model.members.single().members[3]) { + assertEquals("Summary", content.summary.toTestString()) + with (content.description) { + assertEqualsIgnoringSeparators(""" + |[code lang=kotlin] + |if (true) { + | println(property) + |} + |[/code] + |[code lang=kotlin] + |if (true) { + | println(property) + |} + |[/code] + |[code lang=kotlin] + |if (true) { + | println(property) + |} + |[/code] + |[code lang=kotlin] + |if (true) { + | println(property) + |} + |[/code] + |""".trimMargin(), toTestString()) + } + } + } + } +} diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt new file mode 100644 index 000000000..c94d7e990 --- /dev/null +++ b/core/src/test/kotlin/model/FunctionTest.kt @@ -0,0 +1,251 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import kotlin.test.assertNotNull + +class FunctionTest { + @Test fun function() { + verifyModel("testdata/functions/function.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("fn", name) + assertEquals(NodeKind.Function, kind) + assertEquals("Function fn", content.summary.toTestString()) + assertEquals("Unit", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun functionWithReceiver() { + verifyModel("testdata/functions/functionWithReceiver.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("kotlin.String", name) + assertEquals(NodeKind.ExternalClass, kind) + assertEquals(2, members.count()) + with(members[0]) { + assertEquals("fn", name) + assertEquals(NodeKind.Function, kind) + assertEquals("Function with receiver", content.summary.toTestString()) + assertEquals("public", details.elementAt(0).name) + assertEquals("final", details.elementAt(1).name) + with(details.elementAt(3)) { + assertEquals("<this>", name) + assertEquals(NodeKind.Receiver, kind) + assertEquals(Content.Empty, content) + assertEquals("String", details.single().name) + assertTrue(members.none()) + assertTrue(links.none()) + } + assertEquals("Unit", details.elementAt(4).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + with(members[1]) { + assertEquals("fn", name) + assertEquals(NodeKind.Function, kind) + } + } + } + } + + @Test fun genericFunction() { + verifyModel("testdata/functions/genericFunction.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("generic", name) + assertEquals(NodeKind.Function, kind) + assertEquals("generic function", content.summary.toTestString()) + + assertEquals("private", details.elementAt(0).name) + assertEquals("final", details.elementAt(1).name) + with(details.elementAt(3)) { + assertEquals("T", name) + assertEquals(NodeKind.TypeParameter, kind) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + assertEquals("Unit", details.elementAt(4).name) + + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + @Test fun genericFunctionWithConstraints() { + verifyModel("testdata/functions/genericFunctionWithConstraints.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("generic", name) + assertEquals(NodeKind.Function, kind) + assertEquals("generic function", content.summary.toTestString()) + + val functionDetails = details + assertEquals("public", functionDetails.elementAt(0).name) + assertEquals("final", functionDetails.elementAt(1).name) + with(functionDetails.elementAt(3)) { + assertEquals("T", name) + assertEquals(NodeKind.TypeParameter, kind) + assertEquals(Content.Empty, content) + with(details.single()) { + assertEquals("R", name) + assertEquals(NodeKind.UpperBound, kind) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.singleOrNull() == functionDetails.elementAt(4)) + } + assertTrue(members.none()) + assertTrue(links.none()) + } + with(functionDetails.elementAt(4)) { + assertEquals("R", name) + assertEquals(NodeKind.TypeParameter, kind) + assertEquals(Content.Empty, content) + assertTrue(members.none()) + assertTrue(links.none()) + } + assertEquals("Unit", functionDetails.elementAt(5).name) + + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun functionWithParams() { + verifyModel("testdata/functions/functionWithParams.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("function", name) + assertEquals(NodeKind.Function, kind) + assertEquals("Multiline", content.summary.toTestString()) + assertEquals("""Function +Documentation""", content.description.toTestString()) + + assertEquals("public", details.elementAt(0).name) + assertEquals("final", details.elementAt(1).name) + with(details.elementAt(3)) { + assertEquals("x", name) + assertEquals(NodeKind.Parameter, kind) + assertEquals("parameter", content.summary.toTestString()) + assertEquals("Int", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + assertEquals("Unit", details.elementAt(4).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun annotatedFunction() { + verifyPackageMember("testdata/functions/annotatedFunction.kt", withKotlinRuntime = true) { func -> + assertEquals(1, func.annotations.count()) + with(func.annotations[0]) { + assertEquals("Strictfp", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + } + } + } + + @Test fun functionWithNotDocumentedAnnotation() { + verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt") { func -> + assertEquals(0, func.annotations.count()) + } + } + + @Test fun inlineFunction() { + verifyPackageMember("testdata/functions/inlineFunction.kt") { func -> + val modifiers = func.details(NodeKind.Modifier).map { it.name } + assertTrue("inline" in modifiers) + } + } + + @Test fun functionWithAnnotatedParam() { + verifyModel("testdata/functions/functionWithAnnotatedParam.kt") { model -> + with(model.members.single().members.single { it.name == "function" }) { + with(details(NodeKind.Parameter).first()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("Fancy", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + } + } + } + } + } + + @Test fun functionWithNoinlineParam() { + verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt") { func -> + with(func.details(NodeKind.Parameter).first()) { + val modifiers = details(NodeKind.Modifier).map { it.name } + assertTrue("noinline" in modifiers) + } + } + } + + @Test fun annotatedFunctionWithAnnotationParameters() { + verifyModel("testdata/functions/annotatedFunctionWithAnnotationParameters.kt") { model -> + with(model.members.single().members.single { it.name == "f" }) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("Fancy", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Value, kind) + assertEquals("1", name) + } + } + } + } + } + } + + @Test fun functionWithDefaultParameter() { + verifyModel("testdata/functions/functionWithDefaultParameter.kt") { model -> + with(model.members.single().members.single()) { + with(details.elementAt(3)) { + val value = details(NodeKind.Value) + assertEquals(1, value.count()) + with(value[0]) { + assertEquals("\"\"", name) + } + } + } + } + } + + @Test fun sinceKotlin() { + verifyModel("testdata/functions/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } + + // Test for b/159470920, to ensure that we correctly parse annotated function types without resolving 'ERROR CLASS' + // types. Note that the actual annotation is not included in the type information, this is tracked in b/145517104. + @Test fun functionWithAnnotatedLambdaParam() { + verifyModel("testdata/functions/functionWithAnnotatedLambdaParam.kt") { model -> + with(model.members.single().members.single { it.name == "function" }) { + with(details(NodeKind.Parameter).first()) { + with(details(NodeKind.Type).first()) { + assertEquals("Function0", name) + } + } + } + } + } +} diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt new file mode 100644 index 000000000..c00d8dc37 --- /dev/null +++ b/core/src/test/kotlin/model/JavaTest.kt @@ -0,0 +1,219 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.NodeKind +import org.jetbrains.dokka.RefKind +import org.junit.Assert.* +import org.junit.Ignore +import org.junit.Test + +public class JavaTest { + @Test fun function() { + verifyJavaPackageMember("testdata/java/member.java") { cls -> + assertEquals("Test", cls.name) + assertEquals(NodeKind.Class, cls.kind) + with(cls.members(NodeKind.Function).single()) { + assertEquals("fn", name) + assertEquals("Summary for Function", content.summary.toTestString().trimEnd()) + assertEquals(3, content.sections.size) + with(content.sections[0]) { + assertEquals("Parameters", tag) + assertEquals("name", subjectName) + assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString()) + } + with(content.sections[1]) { + assertEquals("Parameters", tag) + assertEquals("value", subjectName) + assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString()) + } + with(content.sections[2]) { + assertEquals("Author", tag) + assertEquals("yole", toTestString()) + } + assertEquals("Unit", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + with(details.first { it.name == "name" }) { + assertEquals(NodeKind.Parameter, kind) + assertEquals("String", detail(NodeKind.Type).name) + } + with(details.first { it.name == "value" }) { + assertEquals(NodeKind.Parameter, kind) + assertEquals("Int", detail(NodeKind.Type).name) + } + } + } + } + + @Test fun memberWithModifiers() { + verifyJavaPackageMember("testdata/java/memberWithModifiers.java") { cls -> + val modifiers = cls.details(NodeKind.Modifier).map { it.name } + assertTrue("abstract" in modifiers) + with(cls.members.single { it.name == "fn" }) { + assertEquals("protected", details[0].name) + } + with(cls.members.single { it.name == "openFn" }) { + assertEquals("open", details[1].name) + } + } + } + + @Test fun superClass() { + verifyJavaPackageMember("testdata/java/superClass.java") { cls -> + val superTypes = cls.details(NodeKind.Supertype) + assertEquals(2, superTypes.size) + assertEquals("Exception", superTypes[0].name) + assertEquals("Cloneable", superTypes[1].name) + } + } + + @Test fun arrayType() { + verifyJavaPackageMember("testdata/java/arrayType.java") { cls -> + with(cls.members(NodeKind.Function).single()) { + val type = detail(NodeKind.Type) + assertEquals("Array", type.name) + assertEquals("String", type.detail(NodeKind.Type).name) + with(details(NodeKind.Parameter).single()) { + val parameterType = detail(NodeKind.Type) + assertEquals("IntArray", parameterType.name) + } + } + } + } + + @Test fun typeParameter() { + verifyJavaPackageMember("testdata/java/typeParameter.java") { cls -> + val typeParameters = cls.details(NodeKind.TypeParameter) + with(typeParameters.single()) { + assertEquals("T", name) + with(detail(NodeKind.UpperBound)) { + assertEquals("Comparable", name) + assertEquals("T", detail(NodeKind.Type).name) + } + } + with(cls.members(NodeKind.Function).single()) { + val methodTypeParameters = details(NodeKind.TypeParameter) + with(methodTypeParameters.single()) { + assertEquals("E", name) + } + } + } + } + + @Test fun constructors() { + verifyJavaPackageMember("testdata/java/constructors.java") { cls -> + val constructors = cls.members(NodeKind.Constructor) + assertEquals(2, constructors.size) + with(constructors[0]) { + assertEquals("<init>", name) + } + } + } + + @Test fun innerClass() { + verifyJavaPackageMember("testdata/java/InnerClass.java") { cls -> + val innerClass = cls.members(NodeKind.Class).single() + assertEquals("D", innerClass.name) + } + } + + @Test fun varargs() { + verifyJavaPackageMember("testdata/java/varargs.java") { cls -> + val fn = cls.members(NodeKind.Function).single() + val param = fn.detail(NodeKind.Parameter) + assertEquals("vararg", param.details(NodeKind.Modifier).first().name) + val psiType = param.detail(NodeKind.Type) + assertEquals("String", psiType.name) + assertTrue(psiType.details(NodeKind.Type).isEmpty()) + } + } + + @Test fun fields() { + verifyJavaPackageMember("testdata/java/field.java") { cls -> + val i = cls.members(NodeKind.Property).single { it.name == "i" } + assertEquals("Int", i.detail(NodeKind.Type).name) + assertTrue("var" in i.details(NodeKind.Modifier).map { it.name }) + + val s = cls.members(NodeKind.Property).single { it.name == "s" } + assertEquals("String", s.detail(NodeKind.Type).name) + assertFalse("var" in s.details(NodeKind.Modifier).map { it.name }) + assertTrue("static" in s.details(NodeKind.Modifier).map { it.name }) + } + } + + @Test fun staticMethod() { + verifyJavaPackageMember("testdata/java/staticMethod.java") { cls -> + val m = cls.members(NodeKind.Function).single { it.name == "foo" } + assertTrue("static" in m.details(NodeKind.Modifier).map { it.name }) + } + } + + /** + * `@suppress` not supported in Java! + * + * [Proposed tags](http://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html) + * Proposed tag `@exclude` for it, but not supported yet + */ + @Ignore("@suppress not supported in Java!") @Test fun suppressTag() { + verifyJavaPackageMember("testdata/java/suppressTag.java") { cls -> + assertEquals(1, cls.members(NodeKind.Function).size) + } + } + + @Test fun hideAnnotation() { + verifyJavaPackageMember("testdata/java/hideAnnotation.java") { cls -> + assertEquals(1, cls.members(NodeKind.Function).size) + assertEquals(1, cls.members(NodeKind.Property).size) + + // The test file contains two classes, one of which is hidden. + // The test for @hide annotation on classes is via verifyJavaPackageMember(), + // which will throw an IllegalArgumentException if it detects more than one class. + } + } + + @Test fun annotatedAnnotation() { + verifyJavaPackageMember("testdata/java/annotatedAnnotation.java") { cls -> + assertEquals(1, cls.annotations.size) + with(cls.annotations[0]) { + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(NodeKind.Value, kind) + assertEquals("[AnnotationTarget.FIELD, AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER]", name) + } + } + } + } + } + + @Test fun deprecation() { + verifyJavaPackageMember("testdata/java/deprecation.java") { cls -> + val fn = cls.members(NodeKind.Function).single() + assertEquals("This should no longer be used", fn.deprecation!!.content.toTestString()) + } + } + + @Test fun javaLangObject() { + verifyJavaPackageMember("testdata/java/javaLangObject.java") { cls -> + val fn = cls.members(NodeKind.Function).single() + assertEquals("Any", fn.detail(NodeKind.Type).name) + } + } + + @Test fun enumValues() { + verifyJavaPackageMember("testdata/java/enumValues.java") { cls -> + val superTypes = cls.details(NodeKind.Supertype) + assertEquals(1, superTypes.size) + assertEquals(1, cls.members(NodeKind.EnumItem).size) + } + } + + @Test fun inheritorLinks() { + verifyJavaPackageMember("testdata/java/InheritorLinks.java") { cls -> + val fooClass = cls.members.single { it.name == "Foo" } + val inheritors = fooClass.references(RefKind.Inheritor) + assertEquals(1, inheritors.size) + } + } +} diff --git a/core/src/test/kotlin/model/KotlinAsJavaTest.kt b/core/src/test/kotlin/model/KotlinAsJavaTest.kt new file mode 100644 index 000000000..4a054a880 --- /dev/null +++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.DocumentationModule +import org.jetbrains.dokka.NodeKind +import org.junit.Test +import org.junit.Assert.assertEquals + +class KotlinAsJavaTest { + @Test fun function() { + verifyModelAsJava("testdata/functions/function.kt") { model -> + val pkg = model.members.single() + + val facadeClass = pkg.members.single { it.name == "FunctionKt" } + assertEquals(NodeKind.Class, facadeClass.kind) + + val fn = facadeClass.members.single { it.kind == NodeKind.Function} + assertEquals("fn", fn.name) + } + } + + @Test fun propertyWithComment() { + verifyModelAsJava("testdata/comments/oneLineDoc.kt") { model -> + val facadeClass = model.members.single().members.single { it.name == "OneLineDocKt" } + val getter = facadeClass.members.single { it.name == "getProperty" } + assertEquals(NodeKind.Function, getter.kind) + assertEquals("doc", getter.content.summary.toTestString()) + } + } +} + +fun verifyModelAsJava(source: String, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + verifier: (DocumentationModule) -> Unit) { + verifyModel(source, + withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, + format = "html-as-java", + verifier = verifier) +} diff --git a/core/src/test/kotlin/model/LinkTest.kt b/core/src/test/kotlin/model/LinkTest.kt new file mode 100644 index 000000000..6b72525fd --- /dev/null +++ b/core/src/test/kotlin/model/LinkTest.kt @@ -0,0 +1,75 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.ContentBlock +import org.jetbrains.dokka.ContentNodeLazyLink +import org.jetbrains.dokka.NodeKind +import org.junit.Assert.assertEquals +import org.junit.Test + +class LinkTest { + @Test fun linkToSelf() { + verifyModel("testdata/links/linkToSelf.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(NodeKind.Class, kind) + assertEquals("This is link to [Foo -> Class:Foo]", content.summary.toTestString()) + } + } + } + + @Test fun linkToMember() { + verifyModel("testdata/links/linkToMember.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(NodeKind.Class, kind) + assertEquals("This is link to [member -> Function:member]", content.summary.toTestString()) + } + } + } + + @Test fun linkToConstantWithUnderscores() { + verifyModel("testdata/links/linkToConstantWithUnderscores.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(NodeKind.Class, kind) + assertEquals("This is link to [MY_CONSTANT_VALUE -> CompanionObjectProperty:MY_CONSTANT_VALUE]", content.summary.toTestString()) + } + } + } + + @Test fun linkToQualifiedMember() { + verifyModel("testdata/links/linkToQualifiedMember.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(NodeKind.Class, kind) + assertEquals("This is link to [Foo.member -> Function:member]", content.summary.toTestString()) + } + } + } + + @Test fun linkToParam() { + verifyModel("testdata/links/linkToParam.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("Foo", name) + assertEquals(NodeKind.Function, kind) + assertEquals("This is link to [param -> Parameter:param]", content.summary.toTestString()) + } + } + } + + @Test fun linkToPackage() { + verifyModel("testdata/links/linkToPackage.kt") { model -> + val packageNode = model.members.single() + with(packageNode) { + assertEquals(this.name, "test.magic") + } + with(packageNode.members.single()) { + assertEquals("Magic", name) + assertEquals(NodeKind.Class, kind) + assertEquals("Basic implementations of [Magic -> Class:Magic] are located in [test.magic -> Package:test.magic] package", content.summary.toTestString()) + assertEquals(packageNode, ((this.content.summary as ContentBlock).children.filterIsInstance<ContentNodeLazyLink>().last()).lazyNode.invoke()) + } + } + } + +}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt new file mode 100644 index 000000000..3936fb4f3 --- /dev/null +++ b/core/src/test/kotlin/model/PackageTest.kt @@ -0,0 +1,116 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot +import org.junit.Assert.* +import org.junit.Test + +public class PackageTest { + @Test fun rootPackage() { + verifyModel("testdata/packages/rootPackage.kt") { model -> + with(model.members.single()) { + assertEquals(NodeKind.Package, kind) + assertEquals("", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun simpleNamePackage() { + verifyModel("testdata/packages/simpleNamePackage.kt") { model -> + with(model.members.single()) { + assertEquals(NodeKind.Package, kind) + assertEquals("simple", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun dottedNamePackage() { + verifyModel("testdata/packages/dottedNamePackage.kt") { model -> + with(model.members.single()) { + assertEquals(NodeKind.Package, kind) + assertEquals("dot.name", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun multipleFiles() { + verifyModel(KotlinSourceRoot("testdata/packages/dottedNamePackage.kt", false), + KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false) + ) { model -> + assertEquals(2, model.members.count()) + with(model.members.single { it.name == "simple" }) { + assertEquals(NodeKind.Package, kind) + assertEquals("simple", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + with(model.members.single { it.name == "dot.name" }) { + assertEquals(NodeKind.Package, kind) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun multipleFilesSamePackage() { + verifyModel(KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false), + KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt", false)) { model -> + assertEquals(1, model.members.count()) + with(model.members.elementAt(0)) { + assertEquals(NodeKind.Package, kind) + assertEquals("simple", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun classAtPackageLevel() { + verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)) { model -> + assertEquals(1, model.members.count()) + with(model.members.elementAt(0)) { + assertEquals(NodeKind.Package, kind) + assertEquals("simple.name", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertEquals(1, members.size) + assertTrue(links.none()) + } + } + } + + @Test fun suppressAtPackageLevel() { + verifyModel(KotlinSourceRoot("testdata/packages/classInPackage.kt", false), + perPackageOptions = listOf(PackageOptionsImpl(prefix = "simple.name", suppress = true))) { model -> + assertEquals(1, model.members.count()) + with(model.members.elementAt(0)) { + assertEquals(NodeKind.Package, kind) + assertEquals("simple.name", name) + assertEquals(Content.Empty, content) + assertTrue(details.none()) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } +} diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt new file mode 100644 index 000000000..41c3a4c8f --- /dev/null +++ b/core/src/test/kotlin/model/PropertyTest.kt @@ -0,0 +1,112 @@ +package org.jetbrains.dokka.tests + +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.jetbrains.dokka.RefKind +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Ignore +import org.junit.Test + +class PropertyTest { + @Test fun valueProperty() { + verifyModel("testdata/properties/valueProperty.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("property", name) + assertEquals(NodeKind.Property, kind) + assertEquals(Content.Empty, content) + assertEquals("String", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun variableProperty() { + verifyModel("testdata/properties/variableProperty.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("property", name) + assertEquals(NodeKind.Property, kind) + assertEquals(Content.Empty, content) + assertEquals("String", detail(NodeKind.Type).name) + assertTrue(members.none()) + assertTrue(links.none()) + } + } + } + + @Test fun valuePropertyWithGetter() { + verifyModel("testdata/properties/valuePropertyWithGetter.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("property", name) + assertEquals(NodeKind.Property, kind) + assertEquals(Content.Empty, content) + assertEquals("String", detail(NodeKind.Type).name) + assertTrue(links.none()) + assertTrue(members.none()) + } + } + } + + @Test fun variablePropertyWithAccessors() { + verifyModel("testdata/properties/variablePropertyWithAccessors.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("property", name) + assertEquals(NodeKind.Property, kind) + assertEquals(Content.Empty, content) + assertEquals("String", detail(NodeKind.Type).name) + val modifiers = details(NodeKind.Modifier).map { it.name } + assertTrue("final" in modifiers) + assertTrue("public" in modifiers) + assertTrue("var" in modifiers) + assertTrue(links.none()) + assertTrue(members.none()) + } + } + } + + @Test fun annotatedProperty() { + verifyModel("testdata/properties/annotatedProperty.kt", withKotlinRuntime = true) { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("Strictfp", name) + assertEquals(Content.Empty, content) + assertEquals(NodeKind.Annotation, kind) + } + } + } + } + + @Test fun propertyWithReceiver() { + verifyModel("testdata/properties/propertyWithReceiver.kt") { model -> + with(model.members.single().members.single()) { + assertEquals("kotlin.String", name) + assertEquals(NodeKind.ExternalClass, kind) + with(members.single()) { + assertEquals("foobar", name) + assertEquals(NodeKind.Property, kind) + } + } + } + } + + @Test fun propertyOverride() { + verifyModel("testdata/properties/propertyOverride.kt") { model -> + with(model.members.single().members.single { it.name == "Bar" }.members.single { it.name == "xyzzy"}) { + assertEquals("xyzzy", name) + val override = references(RefKind.Override).single().to + assertEquals("xyzzy", override.name) + assertEquals("Foo", override.owner!!.name) + } + } + } + + @Test fun sinceKotlin() { + verifyModel("testdata/properties/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } +} diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt new file mode 100644 index 000000000..c653ac83a --- /dev/null +++ b/core/src/test/kotlin/model/TypeAliasTest.kt @@ -0,0 +1,132 @@ +package org.jetbrains.dokka.tests + +import junit.framework.TestCase.assertEquals +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.junit.Test + +class TypeAliasTest { + @Test + fun testSimple() { + verifyModel("testdata/typealias/simple.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("B", name) + assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) + } + } + } + + @Test + fun testInheritanceFromTypeAlias() { + verifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("Same", name) + assertEquals("Some", detail(NodeKind.TypeAliasUnderlyingType).name) + assertEquals("My", inheritors.single().name) + } + with(pkg.members(NodeKind.Class).find { it.name == "My" }!!) { + assertEquals("Same", detail(NodeKind.Supertype).name) + } + } + } + + @Test + fun testChain() { + verifyModel("testdata/typealias/chain.kt") { + val pkg = it.members.single() + with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { + assertEquals(Content.Empty, content) + assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) + } + with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { + assertEquals(Content.Empty, content) + assertEquals("B", detail(NodeKind.TypeAliasUnderlyingType).name) + } + } + } + + @Test + fun testDocumented() { + verifyModel("testdata/typealias/documented.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals("Just typealias", content.summary.toTestString()) + } + } + } + + @Test + fun testDeprecated() { + verifyModel("testdata/typealias/deprecated.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("Deprecated", deprecation!!.name) + assertEquals("\"Not mainstream now\"", deprecation!!.detail(NodeKind.Parameter).detail(NodeKind.Value).name) + } + } + } + + @Test + fun testGeneric() { + verifyModel("testdata/typealias/generic.kt") { + val pkg = it.members.single() + with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { + assertEquals("Any", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) + } + + with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { + assertEquals("T", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) + assertEquals("T", detail(NodeKind.TypeParameter).name) + } + } + } + + @Test + fun testFunctional() { + verifyModel("testdata/typealias/functional.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name) + val typeParams = detail(NodeKind.TypeAliasUnderlyingType).details(NodeKind.Type) + assertEquals("A", typeParams.first().name) + assertEquals("B", typeParams.last().name) + } + + with(pkg.member(NodeKind.Function)) { + assertEquals("Spell", detail(NodeKind.Parameter).detail(NodeKind.Type).name) + } + } + } + + @Test + fun testAsTypeBoundWithVariance() { + verifyModel("testdata/typealias/asTypeBoundWithVariance.kt") { + val pkg = it.members.single() + with(pkg.members(NodeKind.Class).find { it.name == "C" }!!) { + val tParam = detail(NodeKind.TypeParameter) + assertEquals("out", tParam.detail(NodeKind.Modifier).name) + assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) + } + + with(pkg.members(NodeKind.Class).find { it.name == "D" }!!) { + val tParam = detail(NodeKind.TypeParameter) + assertEquals("in", tParam.detail(NodeKind.Modifier).name) + assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) + } + } + } + + @Test + fun sinceKotlin() { + verifyModel("testdata/typealias/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/utilities/StringExtensionsTest.kt b/core/src/test/kotlin/utilities/StringExtensionsTest.kt new file mode 100644 index 000000000..80c18df6d --- /dev/null +++ b/core/src/test/kotlin/utilities/StringExtensionsTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.dokka.tests.utilities + +import org.jetbrains.dokka.Utilities.firstSentence +import org.junit.Assert.assertEquals +import org.junit.Test + +class StringExtensionsTest { + + @Test + fun firstSentence_emptyString() { + assertEquals("", "".firstSentence()) + } + + @Test + fun incompleteSentence() { + assertEquals("Hello there", "Hello there".firstSentence()) + } + + @Test + fun incompleteSentence_withParenthesis() { + assertEquals("Hello there (hi)", "Hello there (hi)".firstSentence()) + assertEquals("Hello there (hi.)", "Hello there (hi.)".firstSentence()) + } + + @Test + fun incompleteSentence_apiLevel() { + assertEquals("API level 8 (Android 2.2, Froyo)", "API level 8 (Android 2.2, Froyo)".firstSentence()) + } + + @Test + fun unmatchedClosingParen() { + assertEquals( + "A notation either declares, by name, the format of an unparsed entity (see \n", + "A notation either declares, by name, the format of an unparsed entity (see \n".firstSentence() + ) + } + + @Test + fun unmatchedClosingParen_withFullFirstSentence() { + assertEquals( + "This interface represents a notation declared in the DTD.", + ("This interface represents a notation declared in the DTD. A notation either declares, by name, " + + "the format of an unparsed entity (see \n").firstSentence() + ) + } + + @Test + fun firstSentence_singleSentence() { + assertEquals("Hello there.", "Hello there.".firstSentence()) + } + + @Test + fun firstSentence_multipleSentences() { + assertEquals("Hello there.", "Hello there. How are you?".firstSentence()) + } + + @Test + fun firstSentence_singleSentence_withParenthesis() { + assertEquals("API level 28 (Android Pie).", "API level 28 (Android Pie).".firstSentence()) + } + + @Test + fun firstSentence_multipleSentences_withParenthesis() { + assertEquals( + "API level 28 (Android Pie).", + "API level 28 (Android Pie). API level 27 (Android Oreo)".firstSentence() + ) + } + + @Test + fun firstSentence_singleSentence_withPeriodInParenthesis() { + assertEquals("API level 28 (Android 9.0 Pie).", "API level 28 (Android 9.0 Pie).".firstSentence()) + } + + @Test + fun firstSentence_multipleSentences_withPeriodInParenthesis() { + assertEquals( + "API level 28 (Android 9.0 Pie).", + "API level 28 (Android 9.0 Pie). API level 27 (Android 8.0 Oreo).".firstSentence() + ) + } + + @Test + fun parenthesisWithperiod_notFirstSentence() { + assertEquals("Foo bar.", "Foo bar. Baz (Wow)".firstSentence()) + assertEquals("Foo bar.", "Foo bar. Baz (Wow).".firstSentence()) + } + + @Test + fun periodInsideParenthesis() { + assertEquals( + "A ViewGroup is a special view that can contain other views (called children.) " + + "The view group is the base class for layouts and views containers.", + ("A ViewGroup is a special view that can contain other views (called children.) " + + "The view group is the base class for layouts and views containers. " + + "This class also defines the android.view.ViewGroup.LayoutParams class " + + "which serves as the base class for layouts parameters.").firstSentence() + ) + assertEquals("Foo (Foo.) bar.", "Foo (Foo.) bar. Baz.".firstSentence()) + assertEquals("Foo (Foo.) bar (bar.) baz.", "Foo (Foo.) bar (bar.) baz. Wow".firstSentence()) + assertEquals("Foo (Foo.) bar (bar.) baz (baz.) Wow", "Foo (Foo.) bar (bar.) baz (baz.) Wow".firstSentence()) + } +}
\ No newline at end of file diff --git a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 000000000..ca6ee9cea --- /dev/null +++ b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline
\ No newline at end of file diff --git a/core/testdata/classes/annotatedClass.kt b/core/testdata/classes/annotatedClass.kt new file mode 100644 index 000000000..1b58f56c1 --- /dev/null +++ b/core/testdata/classes/annotatedClass.kt @@ -0,0 +1 @@ +@Strictfp class Foo() {} diff --git a/core/testdata/classes/annotatedClassWithAnnotationParameters.kt b/core/testdata/classes/annotatedClassWithAnnotationParameters.kt new file mode 100644 index 000000000..930d6a627 --- /dev/null +++ b/core/testdata/classes/annotatedClassWithAnnotationParameters.kt @@ -0,0 +1 @@ +@Deprecated("should no longer be used") class Foo() {} diff --git a/core/testdata/classes/classWithCompanionObject.kt b/core/testdata/classes/classWithCompanionObject.kt new file mode 100644 index 000000000..fdbd915d6 --- /dev/null +++ b/core/testdata/classes/classWithCompanionObject.kt @@ -0,0 +1,7 @@ +class Klass() { + companion object { + val x = 1 + + fun foo() {} + } +} diff --git a/core/testdata/classes/classWithConstructor.kt b/core/testdata/classes/classWithConstructor.kt new file mode 100644 index 000000000..0751d570e --- /dev/null +++ b/core/testdata/classes/classWithConstructor.kt @@ -0,0 +1 @@ +class Klass(name: String)
\ No newline at end of file diff --git a/core/testdata/classes/classWithFunction.kt b/core/testdata/classes/classWithFunction.kt new file mode 100644 index 000000000..a981cfb6a --- /dev/null +++ b/core/testdata/classes/classWithFunction.kt @@ -0,0 +1,4 @@ +class Klass { + fun fn() { + } +} diff --git a/core/testdata/classes/classWithProperty.kt b/core/testdata/classes/classWithProperty.kt new file mode 100644 index 000000000..2a849572e --- /dev/null +++ b/core/testdata/classes/classWithProperty.kt @@ -0,0 +1,3 @@ +class Klass { + val name: String = "" +}
\ No newline at end of file diff --git a/core/testdata/classes/companionObjectExtension.kt b/core/testdata/classes/companionObjectExtension.kt new file mode 100644 index 000000000..4b471376b --- /dev/null +++ b/core/testdata/classes/companionObjectExtension.kt @@ -0,0 +1,10 @@ +class Foo { + companion object Default { + } +} + + +/** + * The def + */ +val Foo.Default.x: Int get() = 1 diff --git a/core/testdata/classes/dataClass.kt b/core/testdata/classes/dataClass.kt new file mode 100644 index 000000000..62c6f0ece --- /dev/null +++ b/core/testdata/classes/dataClass.kt @@ -0,0 +1 @@ +data class Foo() {} diff --git a/core/testdata/classes/emptyClass.kt b/core/testdata/classes/emptyClass.kt new file mode 100644 index 000000000..abd20cc8d --- /dev/null +++ b/core/testdata/classes/emptyClass.kt @@ -0,0 +1,3 @@ +class Klass { + +}
\ No newline at end of file diff --git a/core/testdata/classes/emptyObject.kt b/core/testdata/classes/emptyObject.kt new file mode 100644 index 000000000..4138bf313 --- /dev/null +++ b/core/testdata/classes/emptyObject.kt @@ -0,0 +1,3 @@ +object Obj { + +}
\ No newline at end of file diff --git a/core/testdata/classes/genericClass.kt b/core/testdata/classes/genericClass.kt new file mode 100644 index 000000000..db20ff7e5 --- /dev/null +++ b/core/testdata/classes/genericClass.kt @@ -0,0 +1,3 @@ +class Klass<T> { + +}
\ No newline at end of file diff --git a/core/testdata/classes/indirectOverride.kt b/core/testdata/classes/indirectOverride.kt new file mode 100644 index 000000000..8d091b801 --- /dev/null +++ b/core/testdata/classes/indirectOverride.kt @@ -0,0 +1,9 @@ +abstract class C() { + abstract fun foo() +} + +abstract class D(): C() + +class E(): D() { + override fun foo() {} +} diff --git a/core/testdata/classes/innerClass.kt b/core/testdata/classes/innerClass.kt new file mode 100644 index 000000000..3c6e497df --- /dev/null +++ b/core/testdata/classes/innerClass.kt @@ -0,0 +1,5 @@ +class C { + inner class D { + + } +}
\ No newline at end of file diff --git a/core/testdata/classes/javaAnnotationClass.kt b/core/testdata/classes/javaAnnotationClass.kt new file mode 100644 index 000000000..95600147f --- /dev/null +++ b/core/testdata/classes/javaAnnotationClass.kt @@ -0,0 +1,5 @@ +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy + +@Retention(RetentionPolicy.SOURCE) +public annotation class throws() diff --git a/core/testdata/classes/notOpenClass.kt b/core/testdata/classes/notOpenClass.kt new file mode 100644 index 000000000..edee2c1af --- /dev/null +++ b/core/testdata/classes/notOpenClass.kt @@ -0,0 +1,7 @@ +open class C() { + open fun f() {} +} + +class D() : C() { + override fun f() {} +} diff --git a/core/testdata/classes/privateCompanionObject.kt b/core/testdata/classes/privateCompanionObject.kt new file mode 100644 index 000000000..df43b5f92 --- /dev/null +++ b/core/testdata/classes/privateCompanionObject.kt @@ -0,0 +1,11 @@ +package p + +class Clz { + private companion object { + fun fuun() { + + } + + val aaaa = 0 + } +}
\ No newline at end of file diff --git a/core/testdata/classes/sealedClass.kt b/core/testdata/classes/sealedClass.kt new file mode 100644 index 000000000..933503936 --- /dev/null +++ b/core/testdata/classes/sealedClass.kt @@ -0,0 +1 @@ +sealed class Foo() {} diff --git a/core/testdata/classes/secondaryConstructor.kt b/core/testdata/classes/secondaryConstructor.kt new file mode 100644 index 000000000..e5cb2557c --- /dev/null +++ b/core/testdata/classes/secondaryConstructor.kt @@ -0,0 +1,5 @@ +class C() { + /** This is a secondary constructor. */ + constructor(s: String): this() { + } +} diff --git a/core/testdata/classes/sinceKotlin.kt b/core/testdata/classes/sinceKotlin.kt new file mode 100644 index 000000000..1025cf0d3 --- /dev/null +++ b/core/testdata/classes/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Useful + */ +@SinceKotlin("1.1") +class `Since1.1`
\ No newline at end of file diff --git a/core/testdata/comments/codeBlockComment.kt b/core/testdata/comments/codeBlockComment.kt new file mode 100644 index 000000000..aa5f5ffcd --- /dev/null +++ b/core/testdata/comments/codeBlockComment.kt @@ -0,0 +1,14 @@ +/** + * ``` brainfuck + * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + * ``` + */ +val prop1 = "" + + +/** + * ``` + * a + b - c + * ``` + */ +val prop2 = ""
\ No newline at end of file diff --git a/core/testdata/comments/directive.kt b/core/testdata/comments/directive.kt new file mode 100644 index 000000000..b27f5a482 --- /dev/null +++ b/core/testdata/comments/directive.kt @@ -0,0 +1,35 @@ +/** + * Summary + * + * @sample example1 + * @sample example2 + * @sample X.example3 + * @sample X.Y.example4 + */ +val property = "test" + +fun example1(node: String) = if (true) { + println(property) +} + +fun example2(node: String) { + if (true) { + println(property) + } +} + +class X { + fun example3(node: String) { + if (true) { + println(property) + } + } + + class Y { + fun example4(node: String) { + if (true) { + println(property) + } + } + } +} diff --git a/core/testdata/comments/emptyDoc.kt b/core/testdata/comments/emptyDoc.kt new file mode 100644 index 000000000..b87cce575 --- /dev/null +++ b/core/testdata/comments/emptyDoc.kt @@ -0,0 +1 @@ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/emptyDocButComment.kt b/core/testdata/comments/emptyDocButComment.kt new file mode 100644 index 000000000..ceb247531 --- /dev/null +++ b/core/testdata/comments/emptyDocButComment.kt @@ -0,0 +1,2 @@ +/* comment */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/emptySection.kt b/core/testdata/comments/emptySection.kt new file mode 100644 index 000000000..47d6b1a58 --- /dev/null +++ b/core/testdata/comments/emptySection.kt @@ -0,0 +1,6 @@ + +/** + * Summary + * @one + */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/multilineDoc.kt b/core/testdata/comments/multilineDoc.kt new file mode 100644 index 000000000..31cfa3a7e --- /dev/null +++ b/core/testdata/comments/multilineDoc.kt @@ -0,0 +1,7 @@ +/** + * doc1 + * + * doc2 + * doc3 + */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/multilineDocWithComment.kt b/core/testdata/comments/multilineDocWithComment.kt new file mode 100644 index 000000000..88d226426 --- /dev/null +++ b/core/testdata/comments/multilineDocWithComment.kt @@ -0,0 +1,8 @@ +/** + * doc1 + * + * doc2 + * doc3 + */ +// comment +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/multilineSection.kt b/core/testdata/comments/multilineSection.kt new file mode 100644 index 000000000..6ef4df2cd --- /dev/null +++ b/core/testdata/comments/multilineSection.kt @@ -0,0 +1,7 @@ +/** + * Summary + * @one + * line one + * line two + */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/oneLineDoc.kt b/core/testdata/comments/oneLineDoc.kt new file mode 100644 index 000000000..92a40c670 --- /dev/null +++ b/core/testdata/comments/oneLineDoc.kt @@ -0,0 +1,2 @@ +/** doc */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/oneLineDocWithComment.kt b/core/testdata/comments/oneLineDocWithComment.kt new file mode 100644 index 000000000..c84679333 --- /dev/null +++ b/core/testdata/comments/oneLineDocWithComment.kt @@ -0,0 +1,3 @@ +/** doc */ +// comment +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/oneLineDocWithEmptyLine.kt b/core/testdata/comments/oneLineDocWithEmptyLine.kt new file mode 100644 index 000000000..e364c4168 --- /dev/null +++ b/core/testdata/comments/oneLineDocWithEmptyLine.kt @@ -0,0 +1,3 @@ +/** doc */ + +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/quotes.kt b/core/testdata/comments/quotes.kt new file mode 100644 index 000000000..47ae6892a --- /dev/null +++ b/core/testdata/comments/quotes.kt @@ -0,0 +1,2 @@ +/** it's "useful" */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/section1.kt b/core/testdata/comments/section1.kt new file mode 100644 index 000000000..7c763b4c0 --- /dev/null +++ b/core/testdata/comments/section1.kt @@ -0,0 +1,5 @@ +/** + * Summary + * @one section one + */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/comments/section2.kt b/core/testdata/comments/section2.kt new file mode 100644 index 000000000..e280793e8 --- /dev/null +++ b/core/testdata/comments/section2.kt @@ -0,0 +1,6 @@ +/** + * Summary + * @one section one + * @two section two + */ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/format/JavaSupertype.html b/core/testdata/format/JavaSupertype.html new file mode 100644 index 000000000..892ef63ab --- /dev/null +++ b/core/testdata/format/JavaSupertype.html @@ -0,0 +1,36 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>JavaSupertype.Bar - test</title> +</HEAD> +<BODY> +<a href="../../index.html">test</a> / <a href="../index.html">JavaSupertype</a> / <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> <span class="symbol">:</span> <a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="return-foo.html">returnFoo</a></p> +</td> +<td> +<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span> <a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/JavaSupertype.java b/core/testdata/format/JavaSupertype.java new file mode 100644 index 000000000..2045573c2 --- /dev/null +++ b/core/testdata/format/JavaSupertype.java @@ -0,0 +1,8 @@ +public class JavaSupertype { + public static class Foo { + } + + public static class Bar extends Foo { + public Foo returnFoo(Foo foo) { return foo; } + } +} diff --git a/core/testdata/format/accessor.kt b/core/testdata/format/accessor.kt new file mode 100644 index 000000000..5a4d1742a --- /dev/null +++ b/core/testdata/format/accessor.kt @@ -0,0 +1,5 @@ +class C() { + var x: String + /** The getter returns an empty string. */ get() = "" + /** The setter does nothing. */ set(value) { } +} diff --git a/core/testdata/format/accessor.md b/core/testdata/format/accessor.md new file mode 100644 index 000000000..190e8538d --- /dev/null +++ b/core/testdata/format/accessor.md @@ -0,0 +1,14 @@ +[test](../index.md) / [C](index.md) / [x](./x.md) + +# x + +`var x: String` + +**Getter** + +The getter returns an empty string. + +**Setter** + +The setter does nothing. + diff --git a/core/testdata/format/annotatedTypeParameter.kt b/core/testdata/format/annotatedTypeParameter.kt new file mode 100644 index 000000000..cc3bfc1a8 --- /dev/null +++ b/core/testdata/format/annotatedTypeParameter.kt @@ -0,0 +1,2 @@ +public fun <E> containsAll(elements: Collection<@UnsafeVariance E>): @UnsafeVariance E { +} diff --git a/core/testdata/format/annotatedTypeParameter.md b/core/testdata/format/annotatedTypeParameter.md new file mode 100644 index 000000000..aa622eac0 --- /dev/null +++ b/core/testdata/format/annotatedTypeParameter.md @@ -0,0 +1,5 @@ +[test](index.md) / [containsAll](./contains-all.md) + +# containsAll + +`fun <E> containsAll(elements: Collection<@UnsafeVariance `[`E`](contains-all.md#E)`>): @UnsafeVariance `[`E`](contains-all.md#E)
\ No newline at end of file diff --git a/core/testdata/format/annotationClass.kt b/core/testdata/format/annotationClass.kt new file mode 100644 index 000000000..89d494fb7 --- /dev/null +++ b/core/testdata/format/annotationClass.kt @@ -0,0 +1 @@ +annotation class fancy diff --git a/core/testdata/format/annotationClass.md b/core/testdata/format/annotationClass.md new file mode 100644 index 000000000..55fda40c7 --- /dev/null +++ b/core/testdata/format/annotationClass.md @@ -0,0 +1,10 @@ +[test](../index.md) / [fancy](./index.md) + +# fancy + +`annotation class fancy` + +### Constructors + +| [<init>](-init-.md) | `fancy()` | + diff --git a/core/testdata/format/annotationClass.package.md b/core/testdata/format/annotationClass.package.md new file mode 100644 index 000000000..c8aff7a3d --- /dev/null +++ b/core/testdata/format/annotationClass.package.md @@ -0,0 +1,8 @@ +[test](./index.md) + +## Package <root> + +### Annotations + +| [fancy](fancy/index.md) | `annotation class fancy` | + diff --git a/core/testdata/format/annotationParams.kt b/core/testdata/format/annotationParams.kt new file mode 100644 index 000000000..f259a7407 --- /dev/null +++ b/core/testdata/format/annotationParams.kt @@ -0,0 +1 @@ +@JvmName("FFF") fun f() {} diff --git a/core/testdata/format/annotationParams.md b/core/testdata/format/annotationParams.md new file mode 100644 index 000000000..cfa3b8226 --- /dev/null +++ b/core/testdata/format/annotationParams.md @@ -0,0 +1,5 @@ +[test](index.md) / [f](./f.md) + +# f + +`@JvmName("FFF") fun f(): Unit`
\ No newline at end of file diff --git a/core/testdata/format/annotations.kt b/core/testdata/format/annotations.kt new file mode 100644 index 000000000..57f76249b --- /dev/null +++ b/core/testdata/format/annotations.kt @@ -0,0 +1,6 @@ +data class Foo { + inline fun bar(noinline notInlined: () -> Unit) { + } + + inline val x: Int +} diff --git a/core/testdata/format/annotations.md b/core/testdata/format/annotations.md new file mode 100644 index 000000000..2e1604d0e --- /dev/null +++ b/core/testdata/format/annotations.md @@ -0,0 +1,18 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`data class Foo` + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + +### Properties + +| [x](x.md) | `val x: Int` | + +### Functions + +| [bar](bar.md) | `fun bar(notInlined: () -> Unit): Unit` | + diff --git a/core/testdata/format/arrayAverage.kt b/core/testdata/format/arrayAverage.kt new file mode 100644 index 000000000..1f9e12db0 --- /dev/null +++ b/core/testdata/format/arrayAverage.kt @@ -0,0 +1,8 @@ +class XArray<T> + +fun XArray<out Byte>.average(): Double = 0.0 +fun XArray<out Double>.average(): Double = 0.0 +fun XArray<out Float>.average(): Double = 0.0 +fun XArray<out Int>.average(): Double = 0.0 +fun XArray<out Long>.average(): Double = 0.0 +fun XArray<out Short>.average(): Double = 0.0 diff --git a/core/testdata/format/arrayAverage.md b/core/testdata/format/arrayAverage.md new file mode 100644 index 000000000..2c6927d68 --- /dev/null +++ b/core/testdata/format/arrayAverage.md @@ -0,0 +1,14 @@ +[test](../index.md) / [XArray](./index.md) + +# XArray + +`class XArray<T>` + +### Constructors + +| [<init>](-init-.md) | `XArray()` | + +### Extension Functions + +| [average](../average.md) | `fun `[`XArray`](./index.md)`<out Byte>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Double>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Float>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Int>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Long>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Short>.average(): Double` | + diff --git a/core/testdata/format/backtickInCodeBlock.kt b/core/testdata/format/backtickInCodeBlock.kt new file mode 100644 index 000000000..b457efbd0 --- /dev/null +++ b/core/testdata/format/backtickInCodeBlock.kt @@ -0,0 +1,9 @@ +/** + * bt : `` ` `` + * + * bt+ : ``prefix ` postfix`` + * + * backslash: `\` + */ +fun foo() { +} diff --git a/core/testdata/format/backtickInCodeBlock.md b/core/testdata/format/backtickInCodeBlock.md new file mode 100644 index 000000000..830539ace --- /dev/null +++ b/core/testdata/format/backtickInCodeBlock.md @@ -0,0 +1,12 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): Unit` + +bt : `` ` `` + +bt+ : ``prefix ` postfix`` + +backslash: `\` + diff --git a/core/testdata/format/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html new file mode 100644 index 000000000..f0351d724 --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.html @@ -0,0 +1,18 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>u - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./u.html">u</a><br/> +<br/> +<h1>u</h1> +<a name="$u()"></a> +<code><span class="keyword">fun </span><span class="identifier">u</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><pre><code>This is a test + of Dokka's code blocks. +Here is a blank line. + +The previous line was blank. +</code></pre> +</BODY> +</HTML> diff --git a/core/testdata/format/blankLineInsideCodeBlock.kt b/core/testdata/format/blankLineInsideCodeBlock.kt new file mode 100644 index 000000000..9430f4d58 --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.kt @@ -0,0 +1,12 @@ +/** + * ``` + * This is a test + * of Dokka's code blocks. + * Here is a blank line. + * + * The previous line was blank. + * ``` + */ +fun u() { + +}
\ No newline at end of file diff --git a/core/testdata/format/blankLineInsideCodeBlock.md b/core/testdata/format/blankLineInsideCodeBlock.md new file mode 100644 index 000000000..956f8954a --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.md @@ -0,0 +1,14 @@ +[test](index.md) / [u](./u.md) + +# u + +`fun u(): Unit` + +``` +This is a test + of Dokka's code blocks. +Here is a blank line. + +The previous line was blank. +``` + diff --git a/core/testdata/format/bracket.html b/core/testdata/format/bracket.html new file mode 100644 index 000000000..01aaaf041 --- /dev/null +++ b/core/testdata/format/bracket.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>bar[]</p> +</BODY> +</HTML> diff --git a/core/testdata/format/bracket.kt b/core/testdata/format/bracket.kt new file mode 100644 index 000000000..d41b0073a --- /dev/null +++ b/core/testdata/format/bracket.kt @@ -0,0 +1,4 @@ +/** + * bar[] + */ +fun foo() {} diff --git a/core/testdata/format/brokenLink.html b/core/testdata/format/brokenLink.html new file mode 100644 index 000000000..c598a73e9 --- /dev/null +++ b/core/testdata/format/brokenLink.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f()"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>This references <a href="#">noSuchIdentifier</a>.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/brokenLink.kt b/core/testdata/format/brokenLink.kt new file mode 100644 index 000000000..268a986e4 --- /dev/null +++ b/core/testdata/format/brokenLink.kt @@ -0,0 +1,4 @@ +/** + * This references [noSuchIdentifier]. + */ +fun f() { } diff --git a/core/testdata/format/classWithCompanionObject.html b/core/testdata/format/classWithCompanionObject.html new file mode 100644 index 000000000..88feea5e3 --- /dev/null +++ b/core/testdata/format/classWithCompanionObject.html @@ -0,0 +1,48 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Klass - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Klass</a><br/> +<br/> +<h1>Klass</h1> +<code><span class="keyword">class </span><span class="identifier">Klass</span></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Klass</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +<h3>Companion Object Properties</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="x.html">x</a></p> +</td> +<td> +<code><span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></code></td> +</tr> +</tbody> +</table> +<h3>Companion Object Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="foo.html">foo</a></p> +</td> +<td> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/classWithCompanionObject.kt b/core/testdata/format/classWithCompanionObject.kt new file mode 100644 index 000000000..fdbd915d6 --- /dev/null +++ b/core/testdata/format/classWithCompanionObject.kt @@ -0,0 +1,7 @@ +class Klass() { + companion object { + val x = 1 + + fun foo() {} + } +} diff --git a/core/testdata/format/classWithCompanionObject.md b/core/testdata/format/classWithCompanionObject.md new file mode 100644 index 000000000..40f605be7 --- /dev/null +++ b/core/testdata/format/classWithCompanionObject.md @@ -0,0 +1,18 @@ +[test](../index.md) / [Klass](./index.md) + +# Klass + +`class Klass` + +### Constructors + +| [<init>](-init-.md) | `Klass()` | + +### Companion Object Properties + +| [x](x.md) | `val x: Int` | + +### Companion Object Functions + +| [foo](foo.md) | `fun foo(): Unit` | + diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html new file mode 100644 index 000000000..5ef09d637 --- /dev/null +++ b/core/testdata/format/codeBlock.html @@ -0,0 +1,62 @@ +<!-- File: test/-it-does-some-obfuscated-thing/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>ItDoesSomeObfuscatedThing - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">ItDoesSomeObfuscatedThing</a><br/> +<br/> +<h1>ItDoesSomeObfuscatedThing</h1> +<code><span class="keyword">class </span><span class="identifier">ItDoesSomeObfuscatedThing</span></code> +<p>Check output of</p> +<pre><code class="lang-brainfuck">++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. +</code></pre> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">ItDoesSomeObfuscatedThing</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Check output of</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> +<!-- File: test/-throws/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Throws - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Throws</a><br/> +<br/> +<h1>Throws</h1> +<code><span class="keyword">class </span><span class="identifier">Throws</span></code> +<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p> +<p>Example:</p> +<pre><code>Throws(IOException::class) +fun readFile(name: String): String {...} +</code></pre> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Throws</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/codeBlock.kt b/core/testdata/format/codeBlock.kt new file mode 100644 index 000000000..633bf414b --- /dev/null +++ b/core/testdata/format/codeBlock.kt @@ -0,0 +1,22 @@ +import kotlin.reflect.KClass + +/** + * This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. + * + * Example: + * + * ``` + * Throws(IOException::class) + * fun readFile(name: String): String {...} + * ``` + */ +class Throws + + +/** + * Check output of + * ``` brainfuck + * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + * ``` + */ +class ItDoesSomeObfuscatedThing
\ No newline at end of file diff --git a/core/testdata/format/codeBlock.md b/core/testdata/format/codeBlock.md new file mode 100644 index 000000000..d697e437d --- /dev/null +++ b/core/testdata/format/codeBlock.md @@ -0,0 +1,37 @@ +<!-- File: test/-it-does-some-obfuscated-thing/index.md --> +[test](../index.md) / [ItDoesSomeObfuscatedThing](./index.md) + +# ItDoesSomeObfuscatedThing + +`class ItDoesSomeObfuscatedThing` + +Check output of + +``` brainfuck +++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. +``` + +### Constructors + +| [<init>](-init-.md) | `ItDoesSomeObfuscatedThing()`<br>Check output of | + +<!-- File: test/-throws/index.md --> +[test](../index.md) / [Throws](./index.md) + +# Throws + +`class Throws` + +This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. + +Example: + +``` +Throws(IOException::class) +fun readFile(name: String): String {...} +``` + +### Constructors + +| [<init>](-init-.md) | `Throws()`<br>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. | + diff --git a/core/testdata/format/codeBlockNoHtmlEscape.kt b/core/testdata/format/codeBlockNoHtmlEscape.kt new file mode 100644 index 000000000..5f48b39cb --- /dev/null +++ b/core/testdata/format/codeBlockNoHtmlEscape.kt @@ -0,0 +1,15 @@ +/** + * Try to make this check pass + * ``` + * if(1 > 2) + * ``` + * Or just piece of html + * ``` + * <p>1 = 3</p> + * ``` + */ +fun hackTheArithmetic(){ + valueOf(1) { + set(3) + } +}
\ No newline at end of file diff --git a/core/testdata/format/codeBlockNoHtmlEscape.md b/core/testdata/format/codeBlockNoHtmlEscape.md new file mode 100644 index 000000000..548dac4f0 --- /dev/null +++ b/core/testdata/format/codeBlockNoHtmlEscape.md @@ -0,0 +1,18 @@ +[test](index.md) / [hackTheArithmetic](./hack-the-arithmetic.md) + +# hackTheArithmetic + +`fun hackTheArithmetic(): Unit` + +Try to make this check pass + +``` +if(1 > 2) +``` + +Or just piece of html + +``` +<p>1 = 3</p> +``` + diff --git a/core/testdata/format/codeSpan.html b/core/testdata/format/codeSpan.html new file mode 100644 index 000000000..bfe93f364 --- /dev/null +++ b/core/testdata/format/codeSpan.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>This is a <code>code span</code>.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/codeSpan.kt b/core/testdata/format/codeSpan.kt new file mode 100644 index 000000000..645f454a7 --- /dev/null +++ b/core/testdata/format/codeSpan.kt @@ -0,0 +1,4 @@ +/** + * This is a `code span`. + */ +fun foo() {}
\ No newline at end of file diff --git a/core/testdata/format/companionImplements.kt b/core/testdata/format/companionImplements.kt new file mode 100644 index 000000000..154ef9b1c --- /dev/null +++ b/core/testdata/format/companionImplements.kt @@ -0,0 +1,9 @@ + +interface Bar + +/** + * Correct ref [Foo.Companion] + */ +class Foo { + companion object : Bar +}
\ No newline at end of file diff --git a/core/testdata/format/companionImplements.md b/core/testdata/format/companionImplements.md new file mode 100644 index 000000000..aac7b3e60 --- /dev/null +++ b/core/testdata/format/companionImplements.md @@ -0,0 +1,16 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +Correct ref [Foo.Companion](-companion.md) + +### Types + +| [Companion](-companion.md) | `companion object Companion : `[`Bar`](../-bar.md) | + +### Constructors + +| [<init>](-init-.md) | `Foo()`<br>Correct ref [Foo.Companion](-companion.md) | + diff --git a/core/testdata/format/companionObjectExtension.kt b/core/testdata/format/companionObjectExtension.kt new file mode 100644 index 000000000..f452de2c8 --- /dev/null +++ b/core/testdata/format/companionObjectExtension.kt @@ -0,0 +1,10 @@ +class Foo { + companion object Default { + } +} + + +/** + * The default object property. + */ +val Foo.Default.x: Int get() = 1 diff --git a/core/testdata/format/companionObjectExtension.md b/core/testdata/format/companionObjectExtension.md new file mode 100644 index 000000000..c0e268e6f --- /dev/null +++ b/core/testdata/format/companionObjectExtension.md @@ -0,0 +1,14 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + +### Companion Object Extension Properties + +| [x](../x.md) | `val Foo.Default.x: Int`<br>The default object property. | + diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html new file mode 100644 index 000000000..8842969e5 --- /dev/null +++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html @@ -0,0 +1,39 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Bar - test</title> +</HEAD> +<BODY> +<a href="../../index.html">test</a> / <a href="../index.html">test</a> / <a href="./index.html">Bar</a><br/> +<br/> +<h1>Bar</h1> +<code><span class="keyword">class </span><span class="identifier">Bar</span> <span class="symbol">:</span> <a href="../-foo/index.html"><span class="identifier">Foo</span></a></code> +<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p> +</td> +</tr> +</tbody> +</table> +<h3>Inherited Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="../-foo/xyzzy.html">xyzzy</a></p> +</td> +<td> +<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">xyzzy</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt new file mode 100644 index 000000000..102782f95 --- /dev/null +++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt @@ -0,0 +1,6 @@ +package test + +/** + * See [xyzzy] + */ +class Bar(): Foo() diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java b/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java new file mode 100644 index 000000000..7c1430308 --- /dev/null +++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/test/Foo.java @@ -0,0 +1,6 @@ +package test; + +public class Foo { + public void xyzzy() { + } +} diff --git a/core/testdata/format/dac/deprecation/DeprecatedBar.kt b/core/testdata/format/dac/deprecation/DeprecatedBar.kt new file mode 100644 index 000000000..39ac1c43d --- /dev/null +++ b/core/testdata/format/dac/deprecation/DeprecatedBar.kt @@ -0,0 +1,17 @@ +class Bar { + fun replacementBarMethod(): Bar { return Bar() } + + fun badBarMethod(): DeprecatedBar { return DeprecatedBar() } + + /** + * This method has been deprecated in favor of replacementBarMethod(). + */ + @Deprecated("Obsolete method", ReplaceWith("replacementBarMethod()")) + fun goodBarMethod(): DeprecatedBar { return DeprecatedBar() } +} + +/** + * This class has been deprecated in favor of Bar. + */ +@Deprecated("Obsolete class", ReplaceWith("Bar")) +class DeprecatedBar
\ No newline at end of file diff --git a/core/testdata/format/dac/deprecation/DeprecatedFoo.java b/core/testdata/format/dac/deprecation/DeprecatedFoo.java new file mode 100644 index 000000000..3c9c360ee --- /dev/null +++ b/core/testdata/format/dac/deprecation/DeprecatedFoo.java @@ -0,0 +1,21 @@ +public class Foo { + public Foo() { } + + public static Foo replacementFooMethod() { return Foo() } + + public static DeprecatedFoo badFooMethod() { return new DeprecatedFoo() } + + /** + * @deprecated Use {@link #replacementFooMethod()} instead. + */ + @Deprecated + public static DeprecatedFoo goodFooMethod() { return new DeprecatedFoo() } +} + +/** + * @deprecated Use {@link #Foo} instead. + */ +@Deprecated +public class DeprecatedFoo { + public DeprecatedFoo() { } +} diff --git a/core/testdata/format/dac/deprecation/dac-as-java/Bar.html b/core/testdata/format/dac/deprecation/dac-as-java/Bar.html new file mode 100644 index 000000000..9f9346ee7 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac-as-java/Bar.html @@ -0,0 +1,83 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td> + <td> + <div><code><a href="#badBarMethod()">badBarMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td> + <td> + <div><code><a href="#goodBarMethod()">goodBarMethod</a>()</code></div> + <p>This method has been deprecated in favor of replacementBarMethod().</p> + </td> + </tr> + <tr> + <td><span class="keyword">final</span> <a href="#"><span class="identifier">Bar</span></a></td> + <td> + <div><code><a href="#replacementBarMethod()">replacementBarMethod</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="badBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">badBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a> <span class="identifier">badBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> +<a name="goodBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">goodBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a> <span class="identifier">goodBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p class="caution"><strong>Deprecated: </strong><em>Obsolete method. ReplaceWith("replacementBarMethod()"). </em></p> + <p>This method has been deprecated in favor of replacementBarMethod().</p> + </div> +<a name="replacementBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">replacementBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">final</span> <a href="#"><span class="identifier">Bar</span></a> <span class="identifier">replacementBarMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html new file mode 100644 index 000000000..d6e955413 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedBar.html @@ -0,0 +1,39 @@ +<html devsite="true"> + <head> + <title>DeprecatedBar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>DeprecatedBar</h1> + <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">DeprecatedBar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedBar</a></td> + </tr> + </table> + <p>This class has been deprecated in favor of Bar.</p> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#DeprecatedBar()">DeprecatedBar</a>()</code></div> + <p>This class has been deprecated in favor of Bar.</p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="DeprecatedBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">DeprecatedBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">DeprecatedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>This class has been deprecated in favor of Bar.</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html new file mode 100644 index 000000000..360b24ef3 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac-as-java/DeprecatedFoo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>DeprecatedFoo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>DeprecatedFoo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">DeprecatedFoo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedFoo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#DeprecatedFoo()">DeprecatedFoo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="DeprecatedFoo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">DeprecatedFoo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">DeprecatedFoo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac-as-java/Foo.html b/core/testdata/format/dac/deprecation/dac-as-java/Foo.html new file mode 100644 index 000000000..b468e4daa --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac-as-java/Foo.html @@ -0,0 +1,82 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a></td> + <td> + <div><code><a href="#badFooMethod()">badFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a></td> + <td> + <div><code><a href="#goodFooMethod()">goodFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a></td> + <td> + <div><code><a href="#replacementFooMethod()">replacementFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="badFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">badFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a> <span class="identifier">badFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> +<a name="goodFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">goodFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a> <span class="identifier">goodFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p class="caution"><strong>Deprecated.</strong><em></em></p> + </div> +<a name="replacementFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">replacementFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a> <span class="identifier">replacementFooMethod</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac/Bar.html b/core/testdata/format/dac/deprecation/dac/Bar.html new file mode 100644 index 000000000..7ae5c5c46 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac/Bar.html @@ -0,0 +1,83 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">class </span><span class="identifier">Bar</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td> + <td> + <div><code><a href="#badBarMethod()">badBarMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></td> + <td> + <div><code><a href="#goodBarMethod()">goodBarMethod</a>()</code></div> + <p>This method has been deprecated in favor of replacementBarMethod().</p> + </td> + </tr> + <tr> + <td><a href="#"><span class="identifier">Bar</span></a></td> + <td> + <div><code><a href="#replacementBarMethod()">replacementBarMethod</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="badBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">badBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><span class="identifier">badBarMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></pre> + </div> +<a name="goodBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">goodBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><del><span class="identifier">goodBarMethod</span></del><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedBar.html#"><span class="identifier">DeprecatedBar</span></a></pre> + <p class="caution"><strong>Deprecated: </strong><em>Obsolete method. Replace with: "replacementBarMethod()". </em></p> + <p>This method has been deprecated in favor of replacementBarMethod().</p> + </div> +<a name="replacementBarMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">replacementBarMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">fun </span><span class="identifier">replacementBarMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="#"><span class="identifier">Bar</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html b/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html new file mode 100644 index 000000000..11a6a5667 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac/DeprecatedBar.html @@ -0,0 +1,39 @@ +<html devsite="true"> + <head> + <title>DeprecatedBar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>DeprecatedBar</h1> + <pre><span class="keyword">class </span><del><span class="identifier">DeprecatedBar</span></del></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedBar</a></td> + </tr> + </table> + <p>This class has been deprecated in favor of Bar.</p> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#DeprecatedBar()">DeprecatedBar</a>()</code></div> + <p>This class has been deprecated in favor of Bar.</p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="DeprecatedBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">DeprecatedBar</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">DeprecatedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>This class has been deprecated in favor of Bar.</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html b/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html new file mode 100644 index 000000000..4dcf39c19 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac/DeprecatedFoo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>DeprecatedFoo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>DeprecatedFoo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><del><span class="identifier">DeprecatedFoo</span></del></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">DeprecatedFoo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#DeprecatedFoo()">DeprecatedFoo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="DeprecatedFoo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">DeprecatedFoo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">DeprecatedFoo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/deprecation/dac/Foo.html b/core/testdata/format/dac/deprecation/dac/Foo.html new file mode 100644 index 000000000..9fe6ad2d2 --- /dev/null +++ b/core/testdata/format/dac/deprecation/dac/Foo.html @@ -0,0 +1,82 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></td> + <td> + <div><code><a href="#badFooMethod()">badFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></td> + <td> + <div><code><a href="#goodFooMethod()">goodFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="keyword">static</span> <a href="#"><span class="identifier">Foo</span></a><span class="symbol">!</span></td> + <td> + <div><code><a href="#replacementFooMethod()">replacementFooMethod</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="badFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">badFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><span class="identifier">badFooMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></pre> + </div> +<a name="goodFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">goodFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><del><span class="identifier">goodFooMethod</span></del><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="DeprecatedFoo.html#"><span class="identifier">DeprecatedFoo</span></a><span class="symbol">!</span></pre> + <p class="caution"><strong>Deprecated: </strong><em>Use <code><a href="#replacementFooMethod()">replacementFooMethod()</a></code> instead.</em></p> + </div> +<a name="replacementFooMethod()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">replacementFooMethod</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">static</span> <span class="keyword">fun </span><span class="identifier">replacementFooMethod</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="#"><span class="identifier">Foo</span></a><span class="symbol">!</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/inheritedMethods/Child.java b/core/testdata/format/dac/inheritedMethods/Child.java new file mode 100644 index 000000000..902f64947 --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/Child.java @@ -0,0 +1,8 @@ +public class Child extends Parent { + public void Bar() { } + /** + * Do an override subclass baz + */ + @override + public void baz() { } +}
\ No newline at end of file diff --git a/core/testdata/format/dac/inheritedMethods/Parent.java b/core/testdata/format/dac/inheritedMethods/Parent.java new file mode 100644 index 000000000..59d9ab0f3 --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/Parent.java @@ -0,0 +1,10 @@ +public class Parent { + /** + * Do a superclass foo + */ + public void foo() { } + /** + * Do a superclass baz + */ + public void baz() { } +}
\ No newline at end of file diff --git a/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html b/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html new file mode 100644 index 000000000..67a58125a --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/dac-as-java/Child.html @@ -0,0 +1,74 @@ +<html devsite="true"> + <head> + <title>Child</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Child</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Child</span> <span class="keyword">extends</span> <a href="Parent.html#"><span class="identifier">Parent</span></a></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="2"><a href="Parent.html#">Parent</a></td> + </tr> + <tr> + <td class="jd-inheritance-space"> ↳</td> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Child</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Child()">Child</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#baz()">baz</a>()</code></div> + <p>Do an override subclass baz</p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Child()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Child</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Child</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> +<a name="baz()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">baz</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>Do an override subclass baz</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html b/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html new file mode 100644 index 000000000..977e693d8 --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/dac-as-java/Parent.html @@ -0,0 +1,92 @@ +<html devsite="true"> + <head> + <title>Parent</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Parent</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Parent</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Parent</a></td> + </tr> + </table> + <table class="jd-sumtable jd-sumtable-subclasses"> + <tbody> + <tr> + <td> + <div class="expandable"><span class="expand-control">Known Direct Subclasses</span> + <div class="showalways" id="subclasses-direct"><a href="Child.html#">Child</a></div> + <div class="exw-expanded-content" id="subclasses-direct-summary"> + <table class="jd-sumtable-expando"> + <tr class="api api-level-" data-version-added=""> + <td class="jd-linkcol"><a href="Child.html#">Child</a></td> + <td class="jd-descrcol" width="100%"> + <p></p> + </td> + </tr> + </table> + </div> + </div> + </td> + </tr> + </tbody> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Parent()">Parent</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#baz()">baz</a>()</code></div> + <p>Do a superclass baz</p> + </td> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#foo()">foo</a>()</code></div> + <p>Do a superclass foo</p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Parent()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Parent</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Parent</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="baz()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">baz</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>Do a superclass baz</p> + </div> +<a name="foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>Do a superclass foo</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/inheritedMethods/dac/Child.html b/core/testdata/format/dac/inheritedMethods/dac/Child.html new file mode 100644 index 000000000..c645a89e7 --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/dac/Child.html @@ -0,0 +1,99 @@ +<html devsite="true"> + <head> + <title>Child</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Child</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Child</span> <span class="symbol">:</span> <a href="Parent.html#"><span class="identifier">Parent</span></a></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="2"><a href="Parent.html#">Parent</a></td> + </tr> + <tr> + <td class="jd-inheritance-space"> ↳</td> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Child</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Child()">Child</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#baz()">baz</a>()</code></div> + <p>Do an override subclass baz</p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="inhmethods"> + <tbody> + <tr> + <th colSpan="2">Inherited functions</th> + </tr> + <tr class="api apilevel-"> + <td colSpan="2"> + <div class="expandable jd-inherited-apis"><span class="expand-control exw-expanded">From class <code><a href="Parent.html#">Parent</a></code></span> + <table class="responsive exw-expanded-content"> + <tbody> + <tr class="api apilevel-" data-version-added="ApiLevel:"> + <td><code><span class="identifier">Unit</span></code></td> + <td width="100%"><code><a href="Parent.html#foo()">foo</a>()</code> + <p> + <p>Do a superclass foo</p> + </p> + </td> + </tr> + </tbody> + </table> + </div> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Child()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Child</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Child</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> +<a name="baz()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">baz</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + <p>Do an override subclass baz</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/inheritedMethods/dac/Parent.html b/core/testdata/format/dac/inheritedMethods/dac/Parent.html new file mode 100644 index 000000000..72b0b72a1 --- /dev/null +++ b/core/testdata/format/dac/inheritedMethods/dac/Parent.html @@ -0,0 +1,92 @@ +<html devsite="true"> + <head> + <title>Parent</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Parent</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Parent</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Parent</a></td> + </tr> + </table> + <table class="jd-sumtable jd-sumtable-subclasses"> + <tbody> + <tr> + <td> + <div class="expandable"><span class="expand-control">Known Direct Subclasses</span> + <div class="showalways" id="subclasses-direct"><a href="Child.html#">Child</a></div> + <div class="exw-expanded-content" id="subclasses-direct-summary"> + <table class="jd-sumtable-expando"> + <tr class="api api-level-" data-version-added=""> + <td class="jd-linkcol"><a href="Child.html#">Child</a></td> + <td class="jd-descrcol" width="100%"> + <p></p> + </td> + </tr> + </table> + </div> + </div> + </td> + </tr> + </tbody> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Parent()">Parent</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#baz()">baz</a>()</code></div> + <p>Do a superclass baz</p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#foo()">foo</a>()</code></div> + <p>Do a superclass foo</p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Parent()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Parent</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Parent</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="baz()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">baz</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">baz</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + <p>Do a superclass baz</p> + </div> +<a name="foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + <p>Do a superclass foo</p> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaClassLinks/Bar.java b/core/testdata/format/dac/javaClassLinks/Bar.java new file mode 100644 index 000000000..88192654b --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/Bar.java @@ -0,0 +1,28 @@ +import java.util.ArrayList; +import java.util.HashSet; + +public class Bar { + /** + * @return The best of bars. + */ + public Bar() { } + + /** + * @param input The best foo you can find + * @return Another spectacular Foo for you to use. + */ + public Foo getMyFoo(Foo input) { return null; } + + /** + * @param input The best set you can afford. + * @return + */ + public ArrayList<String> getMyList(HashSet input) { return null; } + + /** + * @return {@code true} if the fun mode available, {@code false} otherwise. + */ + public boolean isFunAvailable() { throw new RuntimeException("Stub!"); } + + +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaClassLinks/Foo.java b/core/testdata/format/dac/javaClassLinks/Foo.java new file mode 100644 index 000000000..0a2b05268 --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/Foo.java @@ -0,0 +1,2 @@ +public class Foo { +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html b/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html new file mode 100644 index 000000000..64d0c4cd8 --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/dac-as-java/Bar.html @@ -0,0 +1,147 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><a href="Foo.html#"><span class="identifier">Foo</span></a></td> + <td> + <div><code><a href="#getMyFoo(Foo)">getMyFoo</a>(<a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">input</span>)</code></div> + <p></p> + </td> + </tr> + <tr> + <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">></span></td> + <td> + <div><code><a href="#getMyList(HashSet)">getMyList</a>(<a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a> <span class="identifier">input</span>)</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="identifier">boolean</span></td> + <td> + <div><code><a href="#isFunAvailable()">isFunAvailable</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td></td> + <td>The best of bars.</td> + </tr> + </tbody> + </table> + </div> + <h2>Public methods</h2> +<a name="getMyFoo(Foo)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">getMyFoo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">getMyFoo</span><span class="symbol">(</span><a href="Foo.html#"><span class="identifier">Foo</span></a> <span class="identifier">input</span><span class="symbol">)</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Parameters</th> + </tr> + <tr> + <td><code>input</code></td> + <td><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">:</span> The best foo you can find</td> + </tr> + </tbody> + </table> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><a href="Foo.html#"><span class="identifier">Foo</span></a></code></td> + <td>Another spectacular Foo for you to use.</td> + </tr> + </tbody> + </table> + </div> +<a name="getMyList(HashSet)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">getMyList</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">></span> <span class="identifier">getMyList</span><span class="symbol">(</span><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a> <span class="identifier">input</span><span class="symbol">)</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Parameters</th> + </tr> + <tr> + <td><code>input</code></td> + <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol">:</span> The best set you can afford.</td> + </tr> + </tbody> + </table> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">></span></code></td> + <td></td> + </tr> + </tbody> + </table> + </div> +<a name="isFunAvailable()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">isFunAvailable</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">boolean</span> <span class="identifier">isFunAvailable</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><span class="identifier">boolean</span></code></td> + <td><code>true</code> if the fun mode available, <code>false</code> otherwise.</td> + </tr> + </tbody> + </table> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html b/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html new file mode 100644 index 000000000..bddb7ff8a --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/dac-as-java/Foo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaClassLinks/dac/Bar.html b/core/testdata/format/dac/javaClassLinks/dac/Bar.html new file mode 100644 index 000000000..c8961e07f --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/dac/Bar.html @@ -0,0 +1,147 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></td> + <td> + <div><code><a href="#getMyFoo(Foo)">getMyFoo</a>(<span class="identifier" id="Bar$getMyFoo(Foo)/input">input</span><span class="symbol">:</span> <a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span>)</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span></td> + <td> + <div><code><a href="#getMyList(java.util.HashSet)">getMyList</a>(<span class="identifier" id="Bar$getMyList(java.util.HashSet((kotlin.Any)))/input">input</span><span class="symbol">:</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol"><</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span>)</code></div> + <p></p> + </td> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Boolean</span></td> + <td> + <div><code><a href="#isFunAvailable()">isFunAvailable</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td></td> + <td>The best of bars.</td> + </tr> + </tbody> + </table> + </div> + <h2>Public methods</h2> +<a name="getMyFoo(Foo)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">getMyFoo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">getMyFoo</span><span class="symbol">(</span><span class="identifier" id="Bar$getMyFoo(Foo)/input">input</span><span class="symbol">:</span> <a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Parameters</th> + </tr> + <tr> + <td><code>input</code></td> + <td><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span><span class="symbol">:</span> The best foo you can find</td> + </tr> + </tbody> + </table> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><a href="Foo.html#"><span class="identifier">Foo</span></a><span class="symbol">!</span></code></td> + <td>Another spectacular Foo for you to use.</td> + </tr> + </tbody> + </table> + </div> +<a name="getMyList(java.util.HashSet)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">getMyList</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">getMyList</span><span class="symbol">(</span><span class="identifier" id="Bar$getMyList(java.util.HashSet((kotlin.Any)))/input">input</span><span class="symbol">:</span> <a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol"><</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Parameters</th> + </tr> + <tr> + <td><code>input</code></td> + <td><a href="http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html"><span class="identifier">HashSet</span></a><span class="symbol"><</span><span class="identifier">Any</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span><span class="symbol">:</span> The best set you can afford.</td> + </tr> + </tbody> + </table> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><a href="http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html"><span class="identifier">ArrayList</span></a><span class="symbol"><</span><span class="identifier">String</span><span class="symbol">!</span><span class="symbol">></span><span class="symbol">!</span></code></td> + <td></td> + </tr> + </tbody> + </table> + </div> +<a name="isFunAvailable()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">isFunAvailable</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">isFunAvailable</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Boolean</span></pre> + <table class="responsive"> + <tbody> + <tr> + <th colspan="2">Return</th> + </tr> + <tr> + <td><code><span class="identifier">Boolean</span></code></td> + <td><code>true</code> if the fun mode available, <code>false</code> otherwise.</td> + </tr> + </tbody> + </table> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaClassLinks/dac/Foo.html b/core/testdata/format/dac/javaClassLinks/dac/Foo.html new file mode 100644 index 000000000..b1ed934c3 --- /dev/null +++ b/core/testdata/format/dac/javaClassLinks/dac/Foo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaConstructor/Foo.java b/core/testdata/format/dac/javaConstructor/Foo.java new file mode 100644 index 000000000..4d70be4fb --- /dev/null +++ b/core/testdata/format/dac/javaConstructor/Foo.java @@ -0,0 +1,4 @@ +public class Foo { + public Foo() {} + public Foo(String value) {} +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html b/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html new file mode 100644 index 000000000..01cb1bb19 --- /dev/null +++ b/core/testdata/format/dac/javaConstructor/dac-as-java/Foo.html @@ -0,0 +1,48 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td> + <div><code><a href="#Foo(String)">Foo</a>(<a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html"><span class="identifier">String</span></a> <span class="identifier">value</span>)</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> +<a name="Foo(String)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html"><span class="identifier">String</span></a> <span class="identifier">value</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaConstructor/dac/Foo.html b/core/testdata/format/dac/javaConstructor/dac/Foo.html new file mode 100644 index 000000000..948d024b8 --- /dev/null +++ b/core/testdata/format/dac/javaConstructor/dac/Foo.html @@ -0,0 +1,48 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + <tr> + <td> + <div><code><a href="#Foo(kotlin.String)">Foo</a>(<span class="identifier" id="Foo$<init>(kotlin.String)/value">value</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">!</span>)</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> +<a name="Foo(kotlin.String)"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="identifier" id="Foo$<init>(kotlin.String)/value">value</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">!</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaDefaultConstructor/Foo.java b/core/testdata/format/dac/javaDefaultConstructor/Foo.java new file mode 100644 index 000000000..0a2b05268 --- /dev/null +++ b/core/testdata/format/dac/javaDefaultConstructor/Foo.java @@ -0,0 +1,2 @@ +public class Foo { +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html b/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html new file mode 100644 index 000000000..bddb7ff8a --- /dev/null +++ b/core/testdata/format/dac/javaDefaultConstructor/dac-as-java/Foo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html b/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html new file mode 100644 index 000000000..b1ed934c3 --- /dev/null +++ b/core/testdata/format/dac/javaDefaultConstructor/dac/Foo.html @@ -0,0 +1,37 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaMethodVisibilities/Foo.java b/core/testdata/format/dac/javaMethodVisibilities/Foo.java new file mode 100644 index 000000000..936632df9 --- /dev/null +++ b/core/testdata/format/dac/javaMethodVisibilities/Foo.java @@ -0,0 +1,5 @@ +public class Foo { + public void publicBar() {} + protected void protectedBar() {} + private void privateBar() {} +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html b/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html new file mode 100644 index 000000000..978b1071d --- /dev/null +++ b/core/testdata/format/dac/javaMethodVisibilities/dac-as-java/Foo.html @@ -0,0 +1,97 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#publicBar()">publicBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="promethods"> + <tbody> + <tr> + <th colSpan="2">Protected methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#protectedBar()">protectedBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="primethods"> + <tbody> + <tr> + <th colSpan="2">Private methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#privateBar()">privateBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="publicBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">publicBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">publicBar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Protected methods</h2> +<a name="protectedBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">protectedBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">protected</span> <span class="identifier">void</span> <span class="identifier">protectedBar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Private methods</h2> +<a name="privateBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">privateBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">private</span> <span class="identifier">void</span> <span class="identifier">privateBar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html b/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html new file mode 100644 index 000000000..b4dddf185 --- /dev/null +++ b/core/testdata/format/dac/javaMethodVisibilities/dac/Foo.html @@ -0,0 +1,97 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#publicBar()">publicBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="promethods"> + <tbody> + <tr> + <th colSpan="2">Protected methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#protectedBar()">protectedBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="primethods"> + <tbody> + <tr> + <th colSpan="2">Private methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#privateBar()">privateBar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="publicBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">publicBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">publicBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> + <h2>Protected methods</h2> +<a name="protectedBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">protectedBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">protectedBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> + <h2>Private methods</h2> +<a name="privateBar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">privateBar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">private</span> <span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">privateBar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaSeeTag/Bar.kt b/core/testdata/format/dac/javaSeeTag/Bar.kt new file mode 100644 index 000000000..21170d363 --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/Bar.kt @@ -0,0 +1,5 @@ +/** + * @see Foo + * @see java.lang.String + */ +class Bar
\ No newline at end of file diff --git a/core/testdata/format/dac/javaSeeTag/Foo.java b/core/testdata/format/dac/javaSeeTag/Foo.java new file mode 100644 index 000000000..b784ae225 --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/Foo.java @@ -0,0 +1,7 @@ +/** + * @see #bar + * @see java.lang.String + */ +public class Foo { + public void bar() {} +}
\ No newline at end of file diff --git a/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html b/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html new file mode 100644 index 000000000..7eb3cdf85 --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/dac-as-java/Bar.html @@ -0,0 +1,44 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="identifier">Bar</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <div> + <p><b>See Also</b></p> + <ul class="nolist"> + <li><code><a href="Foo.html#">Foo</a></code></li> + <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li> + </ul> + </div> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html b/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html new file mode 100644 index 000000000..4529fd04d --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/dac-as-java/Foo.html @@ -0,0 +1,57 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">public</span> <span class="keyword">class</span> <span class="identifier">Foo</span> <span class="keyword">implements</span> <span class="identifier">Object</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="identifier">void</span></td> + <td> + <div><code><a href="#bar()">bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Public methods</h2> +<a name="bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">public</span> <span class="identifier">void</span> <span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaSeeTag/dac/Bar.html b/core/testdata/format/dac/javaSeeTag/dac/Bar.html new file mode 100644 index 000000000..c21dfb27b --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/dac/Bar.html @@ -0,0 +1,44 @@ +<html devsite="true"> + <head> + <title>Bar</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Bar</h1> + <pre><span class="keyword">class </span><span class="identifier">Bar</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Bar</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Bar()">Bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Bar</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <div> + <p><b>See Also</b></p> + <ul class="nolist"> + <li><code><a href="Foo.html#">Foo</a></code></li> + <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li> + </ul> + </div> + </div> + </body> +</html> diff --git a/core/testdata/format/dac/javaSeeTag/dac/Foo.html b/core/testdata/format/dac/javaSeeTag/dac/Foo.html new file mode 100644 index 000000000..7640805ff --- /dev/null +++ b/core/testdata/format/dac/javaSeeTag/dac/Foo.html @@ -0,0 +1,64 @@ +<html devsite="true"> + <head> + <title>Foo</title> +{% setvar book_path %}/_book.yaml{% endsetvar %} +{% include "_shared/_reference-head-tags.html" %} + </head> + <body> + <div id="api-info-block"></div> + <h1>Foo</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table class="jd-inheritance-table"> + <tr> + <td class="jd-inheritance-class-cell" colSpan="1"><a href="#">Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table class="responsive" id="pubctors"> + <tbody> + <tr> + <th colSpan="2">Public constructors</th> + </tr> + <tr> + <td> + <div><code><a href="#Foo()">Foo</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <table class="responsive" id="pubmethods"> + <tbody> + <tr> + <th colSpan="2">Public methods</th> + </tr> + <tr> + <td><span class="keyword">open</span> <span class="identifier">Unit</span></td> + <td> + <div><code><a href="#bar()">bar</a>()</code></div> + <p></p> + </td> + </tr> + </tbody> + </table> + <h2>Public constructors</h2> +<a name="Foo()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">Foo</h3> + <pre class="api-signature no-pretty-print"><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <div> + <p><b>See Also</b></p> + <ul class="nolist"> + <li><code><a href="#bar()">#bar</a></code></li> + <li><code><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html">java.lang.String</a></code></li> + </ul> + </div> + </div> + <h2>Public methods</h2> +<a name="bar()"></a> + <div class="api apilevel-" data-version-added=""> + <h3 class="api-name">bar</h3> + <pre class="api-signature no-pretty-print"><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/deprecated.class.html b/core/testdata/format/deprecated.class.html new file mode 100644 index 000000000..540060d1b --- /dev/null +++ b/core/testdata/format/deprecated.class.html @@ -0,0 +1,59 @@ +<!-- File: test/-c/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>C - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">C</a><br/> +<br/> +<h1>C</h1> +<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code><br/> +<strong>Deprecated:</strong> This class sucks<br/> +<br/> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> +<!-- File: test/f.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f()"></a> +<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/> +<strong>Deprecated:</strong> This function sucks<br/> +<br/> +</BODY> +</HTML> +<!-- File: test/p.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>p - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./p.html">p</a><br/> +<br/> +<h1>p</h1> +<a name="$p"></a> +<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code><br/> +<strong>Deprecated:</strong> This property sucks<br/> +<br/> +</BODY> +</HTML> diff --git a/core/testdata/format/deprecated.kt b/core/testdata/format/deprecated.kt new file mode 100644 index 000000000..4fc568c23 --- /dev/null +++ b/core/testdata/format/deprecated.kt @@ -0,0 +1,5 @@ +@Deprecated("This class sucks") class C() { } + +@Deprecated("This function sucks") fun f() { } + +@Deprecated("This property sucks") val p: Int get() = 0 diff --git a/core/testdata/format/deprecated.package.html b/core/testdata/format/deprecated.package.html new file mode 100644 index 000000000..3506de61e --- /dev/null +++ b/core/testdata/format/deprecated.package.html @@ -0,0 +1,47 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>root package - test</title> +</HEAD> +<BODY> +<a href="./index.html">test</a><br/> +<br/> +<h2>Package <root></h2> +<h3>Types</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-c/index.html">C</a></p> +</td> +<td> +<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td> +</tr> +</tbody> +</table> +<h3>Properties</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="p.html">p</a></p> +</td> +<td> +<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="f.html">f</a></p> +</td> +<td> +<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/dynamicExtension.kt b/core/testdata/format/dynamicExtension.kt new file mode 100644 index 000000000..5c83bf229 --- /dev/null +++ b/core/testdata/format/dynamicExtension.kt @@ -0,0 +1,3 @@ +class Foo + +fun dynamic.bar() {} diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md new file mode 100644 index 000000000..382cf9733 --- /dev/null +++ b/core/testdata/format/dynamicExtension.md @@ -0,0 +1,10 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + diff --git a/core/testdata/format/dynamicType.kt b/core/testdata/format/dynamicType.kt new file mode 100644 index 000000000..9d557ac0a --- /dev/null +++ b/core/testdata/format/dynamicType.kt @@ -0,0 +1,2 @@ +fun foo(): dynamic = "" + diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md new file mode 100644 index 000000000..07a1d103c --- /dev/null +++ b/core/testdata/format/dynamicType.md @@ -0,0 +1,5 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): dynamic`
\ No newline at end of file diff --git a/core/testdata/format/emptyDescription.kt b/core/testdata/format/emptyDescription.kt new file mode 100644 index 000000000..3ed81dfa5 --- /dev/null +++ b/core/testdata/format/emptyDescription.kt @@ -0,0 +1,5 @@ +/** + * Function fn + */ +fun fn() { +}
\ No newline at end of file diff --git a/core/testdata/format/emptyDescription.md b/core/testdata/format/emptyDescription.md new file mode 100644 index 000000000..5d56d968e --- /dev/null +++ b/core/testdata/format/emptyDescription.md @@ -0,0 +1,8 @@ +[test](index.md) / [fn](./fn.md) + +# fn + +`fun fn(): Unit` + +Function fn + diff --git a/core/testdata/format/entity.html b/core/testdata/format/entity.html new file mode 100644 index 000000000..639f2903a --- /dev/null +++ b/core/testdata/format/entity.html @@ -0,0 +1,27 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Bar - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Bar</a><br/> +<br/> +<h1>Bar</h1> +<code><span class="keyword">class </span><span class="identifier">Bar</span></code> +<p>Copyright © JetBrains 2015 "</p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Copyright © JetBrains 2015 "</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/entity.kt b/core/testdata/format/entity.kt new file mode 100644 index 000000000..163d2ee6d --- /dev/null +++ b/core/testdata/format/entity.kt @@ -0,0 +1,4 @@ +/** + * Copyright © JetBrains 2015 " + */ +class Bar {} diff --git a/core/testdata/format/enumClass.kt b/core/testdata/format/enumClass.kt new file mode 100644 index 000000000..3c2a49fcf --- /dev/null +++ b/core/testdata/format/enumClass.kt @@ -0,0 +1,4 @@ +public enum class InlineOption { + ONLY_LOCAL_RETURN, + LOCAL_CONTINUE_AND_BREAK +} diff --git a/core/testdata/format/enumClass.md b/core/testdata/format/enumClass.md new file mode 100644 index 000000000..76c83b9db --- /dev/null +++ b/core/testdata/format/enumClass.md @@ -0,0 +1,11 @@ +[test](../index.md) / [InlineOption](./index.md) + +# InlineOption + +`enum class InlineOption` + +### Enum Values + +| [LOCAL_CONTINUE_AND_BREAK](-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k.md) | `enum val LOCAL_CONTINUE_AND_BREAK : `[`InlineOption`](./index.md) | +| [ONLY_LOCAL_RETURN](-o-n-l-y_-l-o-c-a-l_-r-e-t-u-r-n.md) | `enum val ONLY_LOCAL_RETURN : `[`InlineOption`](./index.md) | + diff --git a/core/testdata/format/enumClass.value.md b/core/testdata/format/enumClass.value.md new file mode 100644 index 000000000..590577dae --- /dev/null +++ b/core/testdata/format/enumClass.value.md @@ -0,0 +1,5 @@ +[test](../index.md) / [InlineOption](index.md) / [LOCAL_CONTINUE_AND_BREAK](./-l-o-c-a-l_-c-o-n-t-i-n-u-e_-a-n-d_-b-r-e-a-k.md) + +# LOCAL_CONTINUE_AND_BREAK + +`enum val LOCAL_CONTINUE_AND_BREAK : `[`InlineOption`](index.md)
\ No newline at end of file diff --git a/core/testdata/format/enumRef.kt b/core/testdata/format/enumRef.kt new file mode 100644 index 000000000..5c0b2de9e --- /dev/null +++ b/core/testdata/format/enumRef.kt @@ -0,0 +1,4 @@ +/** + * [java.math.RoundingMode.UP] + */ +fun f() {}
\ No newline at end of file diff --git a/core/testdata/format/enumRef.md b/core/testdata/format/enumRef.md new file mode 100644 index 000000000..8b2a6650e --- /dev/null +++ b/core/testdata/format/enumRef.md @@ -0,0 +1,8 @@ +[test](index.md) / [f](./f.md) + +# f + +`fun f(): Unit` + +[java.math.RoundingMode.UP](http://docs.oracle.com/javase/6/docs/api/java/math/RoundingMode.html#UP) + diff --git a/core/testdata/format/exceptionClass.kt b/core/testdata/format/exceptionClass.kt new file mode 100644 index 000000000..d005bd898 --- /dev/null +++ b/core/testdata/format/exceptionClass.kt @@ -0,0 +1 @@ +class MyException : Exception diff --git a/core/testdata/format/exceptionClass.md b/core/testdata/format/exceptionClass.md new file mode 100644 index 000000000..1e928bb6a --- /dev/null +++ b/core/testdata/format/exceptionClass.md @@ -0,0 +1,10 @@ +[test](../index.md) / [MyException](./index.md) + +# MyException + +`class MyException : Exception` + +### Constructors + +| [<init>](-init-.md) | `MyException()` | + diff --git a/core/testdata/format/exceptionClass.package.md b/core/testdata/format/exceptionClass.package.md new file mode 100644 index 000000000..8716df0a5 --- /dev/null +++ b/core/testdata/format/exceptionClass.package.md @@ -0,0 +1,8 @@ +[test](./index.md) + +## Package <root> + +### Exceptions + +| [MyException](-my-exception/index.md) | `class MyException : Exception` | + diff --git a/core/testdata/format/exclInCodeBlock.kt b/core/testdata/format/exclInCodeBlock.kt new file mode 100644 index 000000000..62b234ddf --- /dev/null +++ b/core/testdata/format/exclInCodeBlock.kt @@ -0,0 +1,5 @@ +/** + * The magic word is `!` + */ +fun foo() { +} diff --git a/core/testdata/format/exclInCodeBlock.md b/core/testdata/format/exclInCodeBlock.md new file mode 100644 index 000000000..d665c4153 --- /dev/null +++ b/core/testdata/format/exclInCodeBlock.md @@ -0,0 +1,8 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): Unit` + +The magic word is `!` + diff --git a/core/testdata/format/extensionFunctionParameter.kt b/core/testdata/format/extensionFunctionParameter.kt new file mode 100644 index 000000000..bfb344b9a --- /dev/null +++ b/core/testdata/format/extensionFunctionParameter.kt @@ -0,0 +1 @@ +public inline fun <T> T.apply(f: T.() -> Unit): T { f(); return this } diff --git a/core/testdata/format/extensionFunctionParameter.md b/core/testdata/format/extensionFunctionParameter.md new file mode 100644 index 000000000..e1e858245 --- /dev/null +++ b/core/testdata/format/extensionFunctionParameter.md @@ -0,0 +1,5 @@ +[test](index.md) / [apply](./apply.md) + +# apply + +`inline fun <T> `[`T`](apply.md#T)`.apply(f: `[`T`](apply.md#T)`.() -> Unit): `[`T`](apply.md#T)
\ No newline at end of file diff --git a/core/testdata/format/extensionScope.kt b/core/testdata/format/extensionScope.kt new file mode 100644 index 000000000..9f3130b85 --- /dev/null +++ b/core/testdata/format/extensionScope.kt @@ -0,0 +1,14 @@ +/** + * Test class with Type-parameter + */ +class Foo<T> + +/** + * Some extension on Foo + */ +fun <T> Foo<T>.ext() {} + +/** + * Correct link: [Foo.ext] + */ +fun test() {}
\ No newline at end of file diff --git a/core/testdata/format/extensionScope.md b/core/testdata/format/extensionScope.md new file mode 100644 index 000000000..ea765bd59 --- /dev/null +++ b/core/testdata/format/extensionScope.md @@ -0,0 +1,8 @@ +[test](index.md) / [test](./test.md) + +# test + +`fun test(): Unit` + +Correct link: [Foo.ext](ext.md) + diff --git a/core/testdata/format/extensionWithDocumentedReceiver.kt b/core/testdata/format/extensionWithDocumentedReceiver.kt new file mode 100644 index 000000000..37fc09d9d --- /dev/null +++ b/core/testdata/format/extensionWithDocumentedReceiver.kt @@ -0,0 +1,6 @@ +/** + * Function with receiver + * @receiver must be a non-empty string + */ +fun String.fn() { +} diff --git a/core/testdata/format/extensionWithDocumentedReceiver.md b/core/testdata/format/extensionWithDocumentedReceiver.md new file mode 100644 index 000000000..0679ac8f7 --- /dev/null +++ b/core/testdata/format/extensionWithDocumentedReceiver.md @@ -0,0 +1,11 @@ +[test](../index.md) / [kotlin.String](index.md) / [fn](./fn.md) + +# fn + +`fun String.fn(): Unit` + +Function with receiver + +**Receiver** +must be a non-empty string + diff --git a/core/testdata/format/extensions.class.md b/core/testdata/format/extensions.class.md new file mode 100644 index 000000000..b8fa200a1 --- /dev/null +++ b/core/testdata/format/extensions.class.md @@ -0,0 +1,7 @@ +[test](../../index.md) / [foo](../index.md) / [kotlin.String](./index.md) + +### Extensions for kotlin.String + +| [fn](fn.md) | `fun String.fn(): Unit`<br>`fun String.fn(x: Int): Unit`<br>Function with receiver | +| [foobar](foobar.md) | `val String.foobar: Int`<br>Property with receiver. | + diff --git a/core/testdata/format/extensions.kt b/core/testdata/format/extensions.kt new file mode 100644 index 000000000..6f2eff9d2 --- /dev/null +++ b/core/testdata/format/extensions.kt @@ -0,0 +1,19 @@ +package foo + +/** + * Function with receiver + */ +fun String.fn() { +} + +/** + * Function with receiver + */ +fun String.fn(x: Int) { +} + +/** + * Property with receiver. + */ +val String.foobar: Int + get() = size() * 2 diff --git a/core/testdata/format/extensions.package.md b/core/testdata/format/extensions.package.md new file mode 100644 index 000000000..ad895116b --- /dev/null +++ b/core/testdata/format/extensions.package.md @@ -0,0 +1,8 @@ +[test](../index.md) / [foo](./index.md) + +## Package foo + +### Extensions for External Classes + +| [kotlin.String](kotlin.-string/index.md) | | + diff --git a/core/testdata/format/externalReferenceLink.kt b/core/testdata/format/externalReferenceLink.kt new file mode 100644 index 000000000..4ca0ee216 --- /dev/null +++ b/core/testdata/format/externalReferenceLink.kt @@ -0,0 +1,10 @@ +/** + * It is link to [example site][example.com] + * + * Sure, it is [example.com] + * + * [example.com]: http://example.com + */ +fun a() { + +}
\ No newline at end of file diff --git a/core/testdata/format/externalReferenceLink.md b/core/testdata/format/externalReferenceLink.md new file mode 100644 index 000000000..38ffde785 --- /dev/null +++ b/core/testdata/format/externalReferenceLink.md @@ -0,0 +1,10 @@ +[test](index.md) / [a](./a.md) + +# a + +`fun a(): Unit` + +It is link to [example site](http://example.com) + +Sure, it is [example.com](http://example.com) + diff --git a/core/testdata/format/functionWithDefaultParameter.kt b/core/testdata/format/functionWithDefaultParameter.kt new file mode 100644 index 000000000..3a3a102fb --- /dev/null +++ b/core/testdata/format/functionWithDefaultParameter.kt @@ -0,0 +1 @@ +fun f(x: String = "") {} diff --git a/core/testdata/format/functionWithDefaultParameter.md b/core/testdata/format/functionWithDefaultParameter.md new file mode 100644 index 000000000..05f7fbe63 --- /dev/null +++ b/core/testdata/format/functionWithDefaultParameter.md @@ -0,0 +1,5 @@ +[test](index.md) / [f](./f.md) + +# f + +`fun f(x: String = ""): Unit`
\ No newline at end of file diff --git a/core/testdata/format/functionalTypeWithNamedParameters.html b/core/testdata/format/functionalTypeWithNamedParameters.html new file mode 100644 index 000000000..82a9cc03d --- /dev/null +++ b/core/testdata/format/functionalTypeWithNamedParameters.html @@ -0,0 +1,103 @@ +<!-- File: test/-a/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>A - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">A</a><br/> +<br/> +<h1>A</h1> +<code><span class="keyword">class </span><span class="identifier">A</span></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">A</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> +<!-- File: test/-b/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>B - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">B</a><br/> +<br/> +<h1>B</h1> +<code><span class="keyword">class </span><span class="identifier">B</span></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">B</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> +<!-- File: test/-c/index.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>C - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">C</a><br/> +<br/> +<h1>C</h1> +<code><span class="keyword">class </span><span class="identifier">C</span></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> +<!-- File: test/accept-function-type-with-named-arguments.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>acceptFunctionTypeWithNamedArguments - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./accept-function-type-with-named-arguments.html">acceptFunctionTypeWithNamedArguments</a><br/> +<br/> +<h1>acceptFunctionTypeWithNamedArguments</h1> +<a name="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))"></a> +<code><span class="keyword">fun </span><span class="identifier">acceptFunctionTypeWithNamedArguments</span><span class="symbol">(</span><span class="identifier" id="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))/f">f</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">bb</span><span class="symbol">:</span> <a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">,</span> <span class="identifier">aa</span><span class="symbol">:</span> <a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">)</span> <span class="symbol">-></span> <a href="-c/index.html"><span class="identifier">C</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +</BODY> +</HTML> +<!-- File: test/f.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f"></a> +<code><span class="keyword">val </span><span class="identifier">f</span><span class="symbol">: </span><span class="symbol">(</span><span class="identifier">a</span><span class="symbol">:</span> <a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">,</span> <span class="identifier">b</span><span class="symbol">:</span> <a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">)</span> <span class="symbol">-></span> <a href="-c/index.html"><span class="identifier">C</span></a></code> +</BODY> +</HTML> diff --git a/core/testdata/format/functionalTypeWithNamedParameters.kt b/core/testdata/format/functionalTypeWithNamedParameters.kt new file mode 100644 index 000000000..3dada27aa --- /dev/null +++ b/core/testdata/format/functionalTypeWithNamedParameters.kt @@ -0,0 +1,9 @@ +class A +class B +class C + +val f: (a: A, b: B) -> C = { a, b -> C() } + +fun acceptFunctionTypeWithNamedArguments(f: (bb: B, aa: A) -> C) { + +}
\ No newline at end of file diff --git a/core/testdata/format/functionalTypeWithNamedParameters.md b/core/testdata/format/functionalTypeWithNamedParameters.md new file mode 100644 index 000000000..2255c7ae6 --- /dev/null +++ b/core/testdata/format/functionalTypeWithNamedParameters.md @@ -0,0 +1,45 @@ +<!-- File: test/-a/index.md --> +[test](../index.md) / [A](./index.md) + +# A + +`class A` + +### Constructors + +| [<init>](-init-.md) | `A()` | + +<!-- File: test/-b/index.md --> +[test](../index.md) / [B](./index.md) + +# B + +`class B` + +### Constructors + +| [<init>](-init-.md) | `B()` | + +<!-- File: test/-c/index.md --> +[test](../index.md) / [C](./index.md) + +# C + +`class C` + +### Constructors + +| [<init>](-init-.md) | `C()` | + +<!-- File: test/accept-function-type-with-named-arguments.md --> +[test](index.md) / [acceptFunctionTypeWithNamedArguments](./accept-function-type-with-named-arguments.md) + +# acceptFunctionTypeWithNamedArguments + +`fun acceptFunctionTypeWithNamedArguments(f: (bb: `[`B`](-b/index.md)`, aa: `[`A`](-a/index.md)`) -> `[`C`](-c/index.md)`): Unit` +<!-- File: test/f.md --> +[test](index.md) / [f](./f.md) + +# f + +`val f: (a: `[`A`](-a/index.md)`, b: `[`B`](-b/index.md)`) -> `[`C`](-c/index.md)
\ No newline at end of file diff --git a/core/testdata/format/genericInheritedExtensions.kt b/core/testdata/format/genericInheritedExtensions.kt new file mode 100644 index 000000000..4c07e1e57 --- /dev/null +++ b/core/testdata/format/genericInheritedExtensions.kt @@ -0,0 +1,11 @@ +open class Foo<T> + +class Bar<T> : Foo<T>() + +fun <T> Foo<T>.first() { + +} + +fun <T> Bar<T>.second() { + +} diff --git a/core/testdata/format/genericInheritedExtensions.md b/core/testdata/format/genericInheritedExtensions.md new file mode 100644 index 000000000..8d0e316f8 --- /dev/null +++ b/core/testdata/format/genericInheritedExtensions.md @@ -0,0 +1,15 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar<T> : `[`Foo`](../-foo/index.md)`<`[`T`](index.md#T)`>` + +### Constructors + +| [<init>](-init-.md) | `Bar()` | + +### Extension Functions + +| [first](../first.md) | `fun <T> `[`Foo`](../-foo/index.md)`<`[`T`](../first.md#T)`>.first(): Unit` | +| [second](../second.md) | `fun <T> `[`Bar`](./index.md)`<`[`T`](../second.md#T)`>.second(): Unit` | + diff --git a/core/testdata/format/gfm/listInTableCell.kt b/core/testdata/format/gfm/listInTableCell.kt new file mode 100644 index 000000000..2f4fdf54e --- /dev/null +++ b/core/testdata/format/gfm/listInTableCell.kt @@ -0,0 +1,8 @@ +class Foo { + /** + * 1. Foo + * 1. Bar + */ + fun foo() { + } +} diff --git a/core/testdata/format/gfm/listInTableCell.md b/core/testdata/format/gfm/listInTableCell.md new file mode 100644 index 000000000..359ad9161 --- /dev/null +++ b/core/testdata/format/gfm/listInTableCell.md @@ -0,0 +1,17 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +### Constructors + +| Name | Summary | +|---|---| +| [<init>](-init-.md) | `Foo()` | + +### Functions + +| Name | Summary | +|---|---| +| [foo](foo.md) | `fun foo(): Unit`<ol><li>Foo</li><li>Bar</li></ol> | diff --git a/core/testdata/format/gfm/sample.kt b/core/testdata/format/gfm/sample.kt new file mode 100644 index 000000000..3300d2c8c --- /dev/null +++ b/core/testdata/format/gfm/sample.kt @@ -0,0 +1,18 @@ +/** + * The class Foo. + */ +class Foo { + /** + * The method bar. + */ + fun bar() { + + } + + /** + * The method baz. + */ + fun baz() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/gfm/sample.md b/core/testdata/format/gfm/sample.md new file mode 100644 index 000000000..2b082296b --- /dev/null +++ b/core/testdata/format/gfm/sample.md @@ -0,0 +1,20 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +The class Foo. + +### Constructors + +| Name | Summary | +|---|---| +| [<init>](-init-.md) | `Foo()`<br>The class Foo. | + +### Functions + +| Name | Summary | +|---|---| +| [bar](bar.md) | `fun bar(): Unit`<br>The method bar. | +| [baz](baz.md) | `fun baz(): Unit`<br>The method baz. | diff --git a/core/testdata/format/htmlEscaping.html b/core/testdata/format/htmlEscaping.html new file mode 100644 index 000000000..bd64454d6 --- /dev/null +++ b/core/testdata/format/htmlEscaping.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>x - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./x.html">x</a><br/> +<br/> +<h1>x</h1> +<a name="$x()"></a> +<code><span class="keyword">fun </span><span class="symbol"><</span><span class="identifier">T</span><span class="symbol">></span> <span class="identifier">x</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="x.html#T"><span class="identifier">T</span></a><span class="symbol">?</span></code> +<p>Special characters: < is "less than", > is "greater than", & is "ampersand"</p> +</BODY> +</HTML> diff --git a/core/testdata/format/htmlEscaping.kt b/core/testdata/format/htmlEscaping.kt new file mode 100644 index 000000000..8778d8a55 --- /dev/null +++ b/core/testdata/format/htmlEscaping.kt @@ -0,0 +1,4 @@ +/** + * Special characters: < is "less than", > is "greater than", & is "ampersand" + */ +fun x<T>(): T? = null diff --git a/core/testdata/format/inapplicableExtensionFunctions.kt b/core/testdata/format/inapplicableExtensionFunctions.kt new file mode 100644 index 000000000..d2c65b465 --- /dev/null +++ b/core/testdata/format/inapplicableExtensionFunctions.kt @@ -0,0 +1,11 @@ +open class Foo<T> { +} + +class Bar : Foo<Char>() { +} + +fun Foo<Int>.shazam() { +} + +fun Bar.xyzzy() { +} diff --git a/core/testdata/format/inapplicableExtensionFunctions.md b/core/testdata/format/inapplicableExtensionFunctions.md new file mode 100644 index 000000000..08fc2739f --- /dev/null +++ b/core/testdata/format/inapplicableExtensionFunctions.md @@ -0,0 +1,14 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar : `[`Foo`](../-foo/index.md)`<Char>` + +### Constructors + +| [<init>](-init-.md) | `Bar()` | + +### Extension Functions + +| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` | + diff --git a/core/testdata/format/indentedCodeBlock.html b/core/testdata/format/indentedCodeBlock.html new file mode 100644 index 000000000..86c129fb4 --- /dev/null +++ b/core/testdata/format/indentedCodeBlock.html @@ -0,0 +1,17 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Create a new Foo value as follows:</p> +<pre><code> val foo = Foo.create { + type { "ABC" } + }</code></pre> +</BODY> +</HTML> diff --git a/core/testdata/format/indentedCodeBlock.kt b/core/testdata/format/indentedCodeBlock.kt new file mode 100644 index 000000000..19c5365b6 --- /dev/null +++ b/core/testdata/format/indentedCodeBlock.kt @@ -0,0 +1,10 @@ +/** + * Create a new Foo value as follows: + * + * val foo = Foo.create { + * type { "ABC" } + * } + */ +fun foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/indentedCodeBlock.md b/core/testdata/format/indentedCodeBlock.md new file mode 100644 index 000000000..77b0630a0 --- /dev/null +++ b/core/testdata/format/indentedCodeBlock.md @@ -0,0 +1,14 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): Unit` + +Create a new Foo value as follows: + +``` + val foo = Foo.create { + type { "ABC" } + } +``` + diff --git a/core/testdata/format/inheritedCompanionObjectProperties.kt b/core/testdata/format/inheritedCompanionObjectProperties.kt new file mode 100644 index 000000000..74a3749cb --- /dev/null +++ b/core/testdata/format/inheritedCompanionObjectProperties.kt @@ -0,0 +1,18 @@ +open class A { + fun foo() { + } +} + +open class B { + fun bar() { + } +} + +class C : A { + fun xyzzy() { + } + + companion object : B () { + fun shazam() + } +} diff --git a/core/testdata/format/inheritedCompanionObjectProperties.md b/core/testdata/format/inheritedCompanionObjectProperties.md new file mode 100644 index 000000000..ab8f0aa5b --- /dev/null +++ b/core/testdata/format/inheritedCompanionObjectProperties.md @@ -0,0 +1,30 @@ +[test](../index.md) / [C](./index.md) + +# C + +`class C : `[`A`](../-a/index.md) + +### Types + +| [Companion](-companion/index.md) | `companion object Companion : `[`B`](../-b/index.md) | + +### Constructors + +| [<init>](-init-.md) | `C()` | + +### Functions + +| [xyzzy](xyzzy.md) | `fun xyzzy(): Unit` | + +### Inherited Functions + +| [foo](../-a/foo.md) | `fun foo(): Unit` | + +### Companion Object Functions + +| [shazam](shazam.md) | `fun shazam(): Unit` | + +### Inherited Companion Object Functions + +| [bar](../-b/bar.md) | `fun bar(): Unit` | + diff --git a/core/testdata/format/inheritedExtensions.kt b/core/testdata/format/inheritedExtensions.kt new file mode 100644 index 000000000..e38fe00d3 --- /dev/null +++ b/core/testdata/format/inheritedExtensions.kt @@ -0,0 +1,11 @@ +open class Foo + +class Bar : Foo() + +fun Foo.first() { + +} + +fun Bar.second() { + +} diff --git a/core/testdata/format/inheritedExtensions.md b/core/testdata/format/inheritedExtensions.md new file mode 100644 index 000000000..97a73666b --- /dev/null +++ b/core/testdata/format/inheritedExtensions.md @@ -0,0 +1,15 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar : `[`Foo`](../-foo/index.md) + +### Constructors + +| [<init>](-init-.md) | `Bar()` | + +### Extension Functions + +| [first](../first.md) | `fun `[`Foo`](../-foo/index.md)`.first(): Unit` | +| [second](../second.md) | `fun `[`Bar`](./index.md)`.second(): Unit` | + diff --git a/core/testdata/format/inheritedLink.1.kt b/core/testdata/format/inheritedLink.1.kt new file mode 100644 index 000000000..29cc12b48 --- /dev/null +++ b/core/testdata/format/inheritedLink.1.kt @@ -0,0 +1,10 @@ +package p1 + +import java.util.LinkedList + +interface Foo { + + /** Says hello - [LinkedList]. */ + fun sayHello() : String + +}
\ No newline at end of file diff --git a/core/testdata/format/inheritedLink.kt b/core/testdata/format/inheritedLink.kt new file mode 100644 index 000000000..86b8f4e2a --- /dev/null +++ b/core/testdata/format/inheritedLink.kt @@ -0,0 +1,11 @@ +package p2 + +import p1.Foo + +class FooBar : Foo { + + override fun sayHello(): String { + return "Hello!" + } + +}
\ No newline at end of file diff --git a/core/testdata/format/inheritedLink.md b/core/testdata/format/inheritedLink.md new file mode 100644 index 000000000..e5af326c2 --- /dev/null +++ b/core/testdata/format/inheritedLink.md @@ -0,0 +1,17 @@ +<!-- File: test/p2/-foo-bar/-init-.md --> +[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [<init>](./-init-.md) + +# <init> + +`FooBar()` +<!-- File: test/p2/-foo-bar/say-hello.md --> +[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [sayHello](./say-hello.md) + +# sayHello + +`fun sayHello(): String` + +Overrides [Foo.sayHello](../../p1/-foo/say-hello.md) + +Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html). + diff --git a/core/testdata/format/inheritedMembers.kt b/core/testdata/format/inheritedMembers.kt new file mode 100644 index 000000000..2d0c4ca12 --- /dev/null +++ b/core/testdata/format/inheritedMembers.kt @@ -0,0 +1,12 @@ +open class Foo { + fun first() { + } + + val firstProp: Int = 0 +} + +class Bar : Foo() { + fun second() + + val secondProp: Int = 1 +} diff --git a/core/testdata/format/inheritedMembers.md b/core/testdata/format/inheritedMembers.md new file mode 100644 index 000000000..334df360d --- /dev/null +++ b/core/testdata/format/inheritedMembers.md @@ -0,0 +1,26 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar : `[`Foo`](../-foo/index.md) + +### Constructors + +| [<init>](-init-.md) | `Bar()` | + +### Properties + +| [secondProp](second-prop.md) | `val secondProp: Int` | + +### Inherited Properties + +| [firstProp](../-foo/first-prop.md) | `val firstProp: Int` | + +### Functions + +| [second](second.md) | `fun second(): Unit` | + +### Inherited Functions + +| [first](../-foo/first.md) | `fun first(): Unit` | + diff --git a/core/testdata/format/java-layout-html/ConstJava.html b/core/testdata/format/java-layout-html/ConstJava.html new file mode 100644 index 000000000..26c9aa2dc --- /dev/null +++ b/core/testdata/format/java-layout-html/ConstJava.html @@ -0,0 +1,93 @@ +<!-- File: /test/p/ConstJava.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>ConstJava</h1> + <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">ConstJava</span></pre> + <table> + <tr> + <td><a href="#">p.ConstJava</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constants</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><span class="keyword">static</span> <span class="identifier">String</span></td> + <td> + <div><code><a href="#myStringConst%3Akotlin.String">myStringConst</a></code></div> + </td> + </tr> + <tr> + <td><span class="keyword">static</span> <span class="identifier">Int</span></td> + <td> + <div><code><a href="#myIntConst%3Akotlin.Int">myIntConst</a></code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Properties</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><span class="keyword">static</span> <a href="#"><span class="identifier">ConstJava</span></a></td> + <td> + <div><code><a href="#myConstObjConst%3Ap.ConstJava">myConstObjConst</a></code></div> + </td> + </tr> + </tbody> + </table> + <h2>Constants</h2> + <div id="myStringConst:kotlin.String"> + <h3>myStringConst</h3> + <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myStringConst</span><span class="symbol">: </span><span class="identifier">String</span></pre> + <pre>Value: <code>""</code></pre> + </div> + <div id="myIntConst:kotlin.Int"> + <h3>myIntConst</h3> + <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myIntConst</span><span class="symbol">: </span><span class="identifier">Int</span></pre> + <pre>Value: <code>0</code></pre> + </div> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">ConstJava</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Properties</h2> + <div id="myConstObjConst:p.ConstJava"> + <h3>myConstObjConst</h3> + <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myConstObjConst</span><span class="symbol">: </span><a href="#"><span class="identifier">ConstJava</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/ConstJava.java b/core/testdata/format/java-layout-html/ConstJava.java new file mode 100644 index 000000000..eb5bb2bf0 --- /dev/null +++ b/core/testdata/format/java-layout-html/ConstJava.java @@ -0,0 +1,10 @@ +package p; + + +public class ConstJava { + + public static final String myStringConst = ""; + public static final int myIntConst = 0; + + public static final ConstJava myConstObjConst = new ConstJava(); // Not a constant, as it have not primitive type +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/codeBlocks.html b/core/testdata/format/java-layout-html/codeBlocks.html new file mode 100644 index 000000000..1d6bc6cb1 --- /dev/null +++ b/core/testdata/format/java-layout-html/codeBlocks.html @@ -0,0 +1,49 @@ +<!-- File: /test/p/package-summary.html#foo%28%29 --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>p</h1> + <h2>Top-level functions summary</h2> + <table> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></td> + <td> + <div><code><a href="#foo%28%29">foo</a>()</code></div> + <p>See that <code>inline code</code> here</p> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td> + <td> + <div><code><a href="#sample%28%29">sample</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level functions</h2> + <div id="foo()"> + <h3>foo</h3> + <pre><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></pre> + <p>See that <code>inline code</code> here</p> + <p>Some full code-block</p> + <pre><code data-language="Kotlin"> + println(foo()) // Prints 42 + println(foo() - 10) // Prints 32 +</code></pre> + <p>Some indented code-block + fun ref() = foo() + val a = 2</p> + <pre><code data-language="kotlin"> + +println(foo()) // Answer unlimate question of all +println(foo() * 2) // 84!</code></pre> + </div> + <div id="sample()"> + <h3>sample</h3> + <pre><span class="keyword">fun </span><span class="identifier">sample</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/codeBlocks.kt b/core/testdata/format/java-layout-html/codeBlocks.kt new file mode 100644 index 000000000..de0c79441 --- /dev/null +++ b/core/testdata/format/java-layout-html/codeBlocks.kt @@ -0,0 +1,26 @@ +package p + +/** + * See that `inline code` here + * + * Some full code-block + * ```Kotlin + * println(foo()) // Prints 42 + * println(foo() - 10) // Prints 32 + * ``` + * + * Some indented code-block + * fun ref() = foo() + * val a = 2 + * + * @sample p.sample + */ +fun foo(): Int { + return 42 +} + + +fun sample() { + println(foo()) // Answer unlimate question of all + println(foo() * 2) // 84! +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/const.html b/core/testdata/format/java-layout-html/const.html new file mode 100644 index 000000000..89cb572e7 --- /dev/null +++ b/core/testdata/format/java-layout-html/const.html @@ -0,0 +1,99 @@ +<!-- File: /test/p/G.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>G</h1> + <pre><span class="keyword">object </span><span class="identifier">G</span></pre> + <table> + <tr> + <td><a href="#">p.G</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constants</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><span class="keyword">const</span> <span class="identifier">Int</span></td> + <td> + <div><code><a href="#y%3Akotlin.Int">y</a></code></div> + </td> + </tr> + </tbody> + </table> + <h2>Constants</h2> + <div id="y:kotlin.Int"> + <h3>y</h3> + <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">y</span><span class="symbol">: </span><span class="identifier">Int</span></pre> + <pre>Value: <code>0</code></pre> + </div> + </body> +</html> +<!-- File: /test/p/D.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>D</h1> + <pre><span class="keyword">class </span><span class="identifier">D</span></pre> + <table> + <tr> + <td><a href="#">p.D</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constants</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><span class="keyword">const</span> <span class="identifier">Int</span></td> + <td> + <div><code><a href="#Companion.z%3Akotlin.Int">z</a></code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Constants</h2> + <div id="Companion.z:kotlin.Int"> + <h3>z</h3> + <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">z</span><span class="symbol">: </span><span class="identifier">Int</span></pre> + <pre>Value: <code>0</code></pre> + </div> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">D</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/const.kt b/core/testdata/format/java-layout-html/const.kt new file mode 100644 index 000000000..a6a0884f2 --- /dev/null +++ b/core/testdata/format/java-layout-html/const.kt @@ -0,0 +1,14 @@ +package p + +const val x = 0 + + +object G { + const val y = 0 +} + +class D { + companion object { + const val z = 0 + } +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/const.package-summary.html b/core/testdata/format/java-layout-html/const.package-summary.html new file mode 100644 index 000000000..50f06531b --- /dev/null +++ b/core/testdata/format/java-layout-html/const.package-summary.html @@ -0,0 +1,35 @@ +<!-- File: /test/p/package-summary.html --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>p</h1> + <h2>Classes</h2> + <table> + <tbody> + <tr> + <td><a href="D.html#">D</a></td> + <td></td> + </tr> + </tbody> + </table> + <h2>Top-level constants summary</h2> + <table> + <tbody> + <tr> + <td><span class="keyword">const</span> <span class="identifier">Int</span></td> + <td> + <div><code><a href="#x%3Akotlin.Int">x</a></code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level constants</h2> + <div id="x:kotlin.Int"> + <h3>x</h3> + <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></pre> + <pre>Value: <code>0</code></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/externalClassExtension.kt b/core/testdata/format/java-layout-html/externalClassExtension.kt new file mode 100644 index 000000000..04415cc23 --- /dev/null +++ b/core/testdata/format/java-layout-html/externalClassExtension.kt @@ -0,0 +1,5 @@ +package p + +fun String.ext() { + println(this) +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html new file mode 100644 index 000000000..39db6c8d3 --- /dev/null +++ b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html @@ -0,0 +1,25 @@ +<!-- File: /test/p/package-summary.html --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>p</h1> + <h2>Top-level functions summary</h2> + <table> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td> + <td> + <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>.<a href="#%28kotlin.String%29.ext%28%29">ext</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level functions</h2> + <div id="(kotlin.String).ext()"> + <h3>ext</h3> + <pre><span class="keyword">fun </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/genericExtension.html b/core/testdata/format/java-layout-html/genericExtension.html new file mode 100644 index 000000000..0cbd034c4 --- /dev/null +++ b/core/testdata/format/java-layout-html/genericExtension.html @@ -0,0 +1,99 @@ +<!-- File: /test/p/Some.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>Some</h1> + <pre><span class="keyword">class </span><span class="identifier">Some</span></pre> + <table> + <tr> + <td><a href="#">p.Some</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Inherited extension functions</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td>From <a href="package-summary.html">p</a> + <table> + <tbody> + <tr> + <td><span class="identifier">String</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.extFun.T%29.extFun%28%29">extFun</a>()</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">String</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29">nullableExtFun</a>()</code></div> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Inherited extension properties</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td>From <a href="package-summary.html">p</a> + <table> + <tbody> + <tr> + <td><span class="identifier">String</span></td> + <td> + <div><code><a href="package-summary.html#%28p.extVal.T%29.extVal%3Akotlin.String">extVal</a></code></div> + </td> + </tr> + <tr> + <td><span class="identifier">String</span></td> + <td> + <div><code><a href="package-summary.html#%28p.nullableExtVal.T%29.nullableExtVal%3Akotlin.String">nullableExtVal</a></code></div> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/genericExtension.kt b/core/testdata/format/java-layout-html/genericExtension.kt new file mode 100644 index 000000000..0a9d74e64 --- /dev/null +++ b/core/testdata/format/java-layout-html/genericExtension.kt @@ -0,0 +1,10 @@ +package p + +class Some + + +fun <T : Some> T.extFun() = "" +val <T : Some> T.extVal get() = "" + +fun <T : Some?> T.nullableExtFun() = "" +val <T : Some?> T.nullableExtVal get() = ""
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt new file mode 100644 index 000000000..610ebb221 --- /dev/null +++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.Dep.kt @@ -0,0 +1,23 @@ +package foo + + +fun foobar() { + +} + + +val v = 22 + +class G { + + fun oo() = "" + + val og = 11 + + companion object { + + fun dg() = "22" + + val dv = 12 + } +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html new file mode 100644 index 000000000..7025fc771 --- /dev/null +++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html @@ -0,0 +1,80 @@ +<!-- File: /test/bar/X.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>X</h1> + <pre><span class="keyword">class </span><span class="identifier">X</span></pre> + <table> + <tr> + <td><a href="#">bar.X</a></td> + </tr> + </table> + <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a> +See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a> +See <a href="file:/foo/G.html#">foo.G</a> +See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a> +See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a> +See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a> +See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a> +See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a> +See <a href="file:/foo/G.html#">foo.G</a> +See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a> +See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a> +See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a> +See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Functions</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><span class="identifier">String</span></td> + <td> + <div><code><a href="file:/foo/G.html#"><span class="identifier">G</span></a>.<a href="#%28foo.G%29.ext%28%29">ext</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">X</span><span class="symbol">(</span><span class="symbol">)</span></pre> + <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a> +See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a> +See <a href="file:/foo/G.html#">foo.G</a> +See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a> +See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a> +See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a> +See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p> + </div> + <h2>Functions</h2> + <div id="(foo.G).ext()"> + <h3>ext</h3> + <pre><span class="keyword">fun </span><a href="file:/foo/G.html#"><span class="identifier">G</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt new file mode 100644 index 000000000..6420d7876 --- /dev/null +++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt @@ -0,0 +1,16 @@ +package bar + +/** + * See [foo.foobar] + * See [foo.v] + * See [foo.G] + * See [foo.G.oo] + * See [foo.G.og] + * See [foo.G.Companion.dg] + * See [foo.G.Companion.dv] + */ +class X { + + fun (foo.G).ext() = this.oo() +} + diff --git a/core/testdata/format/java-layout-html/inboundLinksTestPackageList b/core/testdata/format/java-layout-html/inboundLinksTestPackageList new file mode 100644 index 000000000..64d25c312 --- /dev/null +++ b/core/testdata/format/java-layout-html/inboundLinksTestPackageList @@ -0,0 +1,3 @@ +$dokka.format:java-layout-html +$dokka.mode:kotlin +foo diff --git a/core/testdata/format/java-layout-html/sections.html b/core/testdata/format/java-layout-html/sections.html new file mode 100644 index 000000000..c4c0469e4 --- /dev/null +++ b/core/testdata/format/java-layout-html/sections.html @@ -0,0 +1,64 @@ +<!-- File: /test/foo/package-summary.html#sectionsTest%28%29 --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>foo</h1> + <h2>Top-level functions summary</h2> + <table> + <tbody> + <tr> + <td><span class="identifier">Unit</span></td> + <td> + <div><code><a href="#sectionsTest%28%29">sectionsTest</a>()</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">Unit</span></td> + <td> + <div><code><a href="#seeMeAlso%28%29">seeMeAlso</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level functions</h2> + <div id="sectionsTest()"> + <h3>sectionsTest</h3> + <pre><span class="keyword">fun </span><span class="identifier">sectionsTest</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + <table> + <thead> + <tr> + <th colspan="2">Exceptions</th> + </tr> + </thead> + <tbody> + <tr> + <td>ArrayOutOfBoundsException</td> + <td>sometimes</td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <th colspan="2">Return</th> + </tr> + </thead> + <tbody> + <tr> + <td>It's simple void</td> + </tr> + </tbody> + </table> + <p><b>See Also</b></p> + <ul> + <li><code><a href="#seeMeAlso%28%29">foo.seeMeAlso</a></code></li> + </ul> + </div> + <div id="seeMeAlso()"> + <h3>seeMeAlso</h3> + <pre><span class="keyword">fun </span><span class="identifier">seeMeAlso</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/sections.kt b/core/testdata/format/java-layout-html/sections.kt new file mode 100644 index 000000000..51358d2f0 --- /dev/null +++ b/core/testdata/format/java-layout-html/sections.kt @@ -0,0 +1,15 @@ +package foo + +/** + * @throws ArrayOutOfBoundsException sometimes + * @return It's simple void + * @see foo.seeMeAlso + */ +fun sectionsTest(): Unit { + println("!") +} + + +fun seeMeAlso() { + +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/simple.html b/core/testdata/format/java-layout-html/simple.html new file mode 100644 index 000000000..0b82c493a --- /dev/null +++ b/core/testdata/format/java-layout-html/simple.html @@ -0,0 +1,177 @@ +<!-- File: /test/p/Foo.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>Foo</h1> + <pre><span class="keyword">class </span><span class="identifier">Foo</span></pre> + <table> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">kotlin.Any</a></td> + </tr> + <tr> + <td> ↳</td> + <td><a href="#">p.Foo</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Functions</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td> + <td> + <div><code><a href="#s%28%29">s</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Inherited extension functions</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html">kotlin</a> + <table> + <tbody> + <tr> + <td><span class="identifier">T</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html">also</a>(<span class="identifier" id="kotlin$also(kotlin.also.T, kotlin.Function1((kotlin.also.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">T</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html">apply</a>(<span class="identifier" id="kotlin$apply(kotlin.apply.T, kotlin.Function1((kotlin.apply.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span> <span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">R</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html">let</a>(<span class="identifier" id="kotlin$let(kotlin.let.T, kotlin.Function1((kotlin.let.T, kotlin.let.R)))/block">block</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <span class="identifier">R</span>)</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">R</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html">run</a>(<span class="identifier" id="kotlin$run(kotlin.run.T, kotlin.Function1((kotlin.run.T, kotlin.run.R)))/block">block</span><span class="symbol">:</span> <span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-></span> <span class="identifier">R</span>)</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">T</span><span class="symbol">?</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html">takeIf</a>(<span class="identifier" id="kotlin$takeIf(kotlin.takeIf.T, kotlin.Function1((kotlin.takeIf.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div> + </td> + </tr> + <tr> + <td><span class="identifier">T</span><span class="symbol">?</span></td> + <td> + <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html">takeUnless</a>(<span class="identifier" id="kotlin$takeUnless(kotlin.takeUnless.T, kotlin.Function1((kotlin.takeUnless.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html"><span class="identifier">Pair</span></a><span class="symbol"><</span><span class="identifier">A</span><span class="symbol">,</span> <span class="identifier">B</span><span class="symbol">></span></td> + <td> + <div><code><span class="identifier">A</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html">to</a>(<span class="identifier" id="kotlin$to(kotlin.to.A, kotlin.to.B)/that">that</span><span class="symbol">:</span> <span class="identifier">B</span>)</code></div> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html"><span class="identifier">Any</span></a><span class="symbol">?</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to-string.html">toString</a>()</code></div> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Properties</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="#g%3Akotlin.String">g</a></code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Inherited extension properties</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html">kotlin.jvm</a> + <table> + <tbody> + <tr> + <td><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html"><span class="identifier">Class</span></a><span class="symbol"><</span><span class="identifier">T</span><span class="symbol">></span></td> + <td> + <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html">javaClass</a></code></div> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + <h2>Functions</h2> + <div id="s()"> + <h3>s</h3> + <pre><span class="keyword">fun </span><span class="identifier">s</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre> + </div> + <h2>Properties</h2> + <div id="g:kotlin.String"> + <h3>g</h3> + <pre><span class="keyword">val </span><span class="identifier">g</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/simple.kt b/core/testdata/format/java-layout-html/simple.kt new file mode 100644 index 000000000..89789c452 --- /dev/null +++ b/core/testdata/format/java-layout-html/simple.kt @@ -0,0 +1,8 @@ +package p + + +class Foo { + fun s(): Unit {} + + val g = "" +}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/topLevel.kt b/core/testdata/format/java-layout-html/topLevel.kt new file mode 100644 index 000000000..85b1a4372 --- /dev/null +++ b/core/testdata/format/java-layout-html/topLevel.kt @@ -0,0 +1,15 @@ +package p + +class Some + +fun topLevelFun() {} + +val topLevelVal = "" + +const val topLevelConst = "" + +val topLevelGetVal get() = "" + +val Some.topLevelExtVal get() = "" + +fun Some.topLevelExtFun() {}
\ No newline at end of file diff --git a/core/testdata/format/java-layout-html/topLevel.package-summary.html b/core/testdata/format/java-layout-html/topLevel.package-summary.html new file mode 100644 index 000000000..03665c6cf --- /dev/null +++ b/core/testdata/format/java-layout-html/topLevel.package-summary.html @@ -0,0 +1,97 @@ +<!-- File: /test/p/package-summary.html --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>p</h1> + <h2>Classes</h2> + <table> + <tbody> + <tr> + <td><a href="Some.html#">Some</a></td> + <td></td> + </tr> + </tbody> + </table> + <h2>Top-level constants summary</h2> + <table> + <tbody> + <tr> + <td><span class="keyword">const</span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="#topLevelConst%3Akotlin.String">topLevelConst</a></code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level functions summary</h2> + <table> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td> + <td> + <div><code><a href="#topLevelFun%28%29">topLevelFun</a>()</code></div> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td> + <td> + <div><code><a href="Some.html#"><span class="identifier">Some</span></a>.<a href="#%28p.Some%29.topLevelExtFun%28%29">topLevelExtFun</a>()</code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level properties summary</h2> + <table> + <tbody> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="#topLevelVal%3Akotlin.String">topLevelVal</a></code></div> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="#topLevelGetVal%3Akotlin.String">topLevelGetVal</a></code></div> + </td> + </tr> + <tr> + <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td> + <td> + <div><code><a href="#%28p.Some%29.topLevelExtVal%3Akotlin.String">topLevelExtVal</a></code></div> + </td> + </tr> + </tbody> + </table> + <h2>Top-level constants</h2> + <div id="topLevelConst:kotlin.String"> + <h3>topLevelConst</h3> + <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">topLevelConst</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre> + <pre>Value: <code>""</code></pre> + </div> + <h2>Top-level functions</h2> + <div id="topLevelFun()"> + <h3>topLevelFun</h3> + <pre><span class="keyword">fun </span><span class="identifier">topLevelFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre> + </div> + <div id="(p.Some).topLevelExtFun()"> + <h3>topLevelExtFun</h3> + <pre><span class="keyword">fun </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre> + </div> + <h2>Top-level properties</h2> + <div id="topLevelVal:kotlin.String"> + <h3>topLevelVal</h3> + <pre><span class="keyword">val </span><span class="identifier">topLevelVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre> + </div> + <div id="topLevelGetVal:kotlin.String"> + <h3>topLevelGetVal</h3> + <pre><span class="keyword">val </span><span class="identifier">topLevelGetVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre> + </div> + <div id="(p.Some).topLevelExtVal:kotlin.String"> + <h3>topLevelExtVal</h3> + <pre><span class="keyword">val </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.html b/core/testdata/format/java-layout-html/unresolvedExternalClass.html new file mode 100644 index 000000000..eca242c9c --- /dev/null +++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.html @@ -0,0 +1,63 @@ +<!-- File: /test/p/MyException.html# --> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <h1>MyException</h1> + <pre><span class="keyword">class </span><span class="identifier">MyException</span> <span class="symbol">:</span> <span class="identifier">Exception</span></pre> + <table> + <tr> + <td><a href="#">p.MyException</a></td> + </tr> + </table> + <h2>Summary</h2> + <table> + <thead> + <tr> + <td> + <h3>Constructors</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div> + </td> + </tr> + </tbody> + </table> + <table> + <thead> + <tr> + <td> + <h3>Inherited extension functions</h3> + </td> + </tr> + </thead> + <tbody> + <tr> + <td>From <a href="#">p.java.lang.Exception</a> + <table> + <tbody> + <tr> + <td><span class="identifier">Unit</span></td> + <td> + <div><code><span class="identifier">Exception</span>.<a href="package-summary.html#%28kotlin.Exception%29.ext%28%29">ext</a>()</code></div> + <p>Some docs...</p> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <h2>Constructors</h2> + <div id="<init>()"> + <h3><init></h3> + <pre><span class="identifier">MyException</span><span class="symbol">(</span><span class="symbol">)</span></pre> + </div> + </body> +</html> diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.kt b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt new file mode 100644 index 000000000..0831dacb0 --- /dev/null +++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt @@ -0,0 +1,13 @@ +package p + +// noStdlibLink set to true for that test + +/** + * Some docs... + */ +fun Exception.ext() { + +} + + +class MyException: Exception()
\ No newline at end of file diff --git a/core/testdata/format/javaCodeInParam.java b/core/testdata/format/javaCodeInParam.java new file mode 100644 index 000000000..73025fcc9 --- /dev/null +++ b/core/testdata/format/javaCodeInParam.java @@ -0,0 +1,5 @@ +/** + * @param T this is {@code some code} and other text + */ +class C<T> { +} diff --git a/core/testdata/format/javaCodeInParam.md b/core/testdata/format/javaCodeInParam.md new file mode 100644 index 000000000..319c6d87e --- /dev/null +++ b/core/testdata/format/javaCodeInParam.md @@ -0,0 +1,14 @@ +[test](../index.md) / [C](./index.md) + +# C + +`protected open class C<T : Any>` + +### Parameters + +`T` - this is `some code` and other text + +### Constructors + +| [<init>](-init-.md) | `C()` | + diff --git a/core/testdata/format/javaCodeLiteralTags.java b/core/testdata/format/javaCodeLiteralTags.java new file mode 100644 index 000000000..e71ddaa70 --- /dev/null +++ b/core/testdata/format/javaCodeLiteralTags.java @@ -0,0 +1,6 @@ +/** + * <p>{@code A<B>C}</p> + * <p>{@literal A<B>C}</p> + */ +class C { +} diff --git a/core/testdata/format/javaCodeLiteralTags.md b/core/testdata/format/javaCodeLiteralTags.md new file mode 100644 index 000000000..b36be04d8 --- /dev/null +++ b/core/testdata/format/javaCodeLiteralTags.md @@ -0,0 +1,16 @@ +[test](../index.md) / [C](./index.md) + +# C + +`protected open class C` + +`A<B>C` + + + +A<B>C + +### Constructors + +| [<init>](-init-.md) | `C()`<br>`A<B>C` <br>A<B>C | + diff --git a/core/testdata/format/javaDeprecated.html b/core/testdata/format/javaDeprecated.html new file mode 100644 index 000000000..6d45fc9ff --- /dev/null +++ b/core/testdata/format/javaDeprecated.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Foo.foo - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="index.html">Foo</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="Foo$foo()"></a> +<code><span class="keyword">open</span> <span class="keyword">fun </span><s><span class="identifier">foo</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/> +<strong>Deprecated:</strong> use <code><a href="bar.html">bar</a></code> instead +</BODY> +</HTML> diff --git a/core/testdata/format/javaDeprecated.java b/core/testdata/format/javaDeprecated.java new file mode 100644 index 000000000..9694a4441 --- /dev/null +++ b/core/testdata/format/javaDeprecated.java @@ -0,0 +1,5 @@ +class Foo { + /** @deprecated use {@link #bar} instead */ + public void foo() {} + public void bar() {} +} diff --git a/core/testdata/format/javaLinkTag.html b/core/testdata/format/javaLinkTag.html new file mode 100644 index 000000000..9a0baaa31 --- /dev/null +++ b/core/testdata/format/javaLinkTag.html @@ -0,0 +1,39 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Foo - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Foo</a><br/> +<br/> +<h1>Foo</h1> +<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code> +<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p> +</td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="bar.html">bar</a></p> +</td> +<td> +<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/javaLinkTag.java b/core/testdata/format/javaLinkTag.java new file mode 100644 index 000000000..84f761c6f --- /dev/null +++ b/core/testdata/format/javaLinkTag.java @@ -0,0 +1,6 @@ +/** + * Call {@link #bar()} to do the job. + */ +class Foo { + public void bar() +} diff --git a/core/testdata/format/javaLinkTagWithLabel.html b/core/testdata/format/javaLinkTagWithLabel.html new file mode 100644 index 000000000..51917f7ab --- /dev/null +++ b/core/testdata/format/javaLinkTagWithLabel.html @@ -0,0 +1,39 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Foo - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Foo</a><br/> +<br/> +<h1>Foo</h1> +<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code> +<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p> +</td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="bar.html">bar</a></p> +</td> +<td> +<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/javaLinkTagWithLabel.java b/core/testdata/format/javaLinkTagWithLabel.java new file mode 100644 index 000000000..1db5ad703 --- /dev/null +++ b/core/testdata/format/javaLinkTagWithLabel.java @@ -0,0 +1,6 @@ +/** + * Call {@link #bar() this wonderful method} to do the job. + */ +class Foo { + public void bar() +} diff --git a/core/testdata/format/javaSample.java b/core/testdata/format/javaSample.java new file mode 100644 index 000000000..b9fb32a74 --- /dev/null +++ b/core/testdata/format/javaSample.java @@ -0,0 +1,5 @@ +/** + * {@sample memberWithModifiers.java } + */ +class C { +} diff --git a/core/testdata/format/javaSample.md b/core/testdata/format/javaSample.md new file mode 100644 index 000000000..fa1757770 --- /dev/null +++ b/core/testdata/format/javaSample.md @@ -0,0 +1,41 @@ +[test](../index.md) / [C](./index.md) + +# C + +`protected open class C` + +``` +public abstract class Test { + /** + * Summary for Function + * @param name is String parameter + * @param value is int parameter + */ + protected final void fn(String name, int value) { + + } + + protected void openFn() {} +} +``` + +### Constructors + +| [<init>](-init-.md) | `C()`<br> + +``` +public abstract class Test { + /** + * Summary for Function + * @param name is String parameter + * @param value is int parameter + */ + protected final void fn(String name, int value) { + + } + + protected void openFn() {} +} +<br>``` +<br> | + diff --git a/core/testdata/format/javaSeeTag.html b/core/testdata/format/javaSeeTag.html new file mode 100644 index 000000000..f8866dc2c --- /dev/null +++ b/core/testdata/format/javaSeeTag.html @@ -0,0 +1,38 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Foo - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Foo</a><br/> +<br/> +<h1>Foo</h1> +<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code> +<p><strong>See Also</strong><br/> +<a href="bar.html">#bar</a></p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="bar.html">bar</a></p> +</td> +<td> +<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/javaSeeTag.java b/core/testdata/format/javaSeeTag.java new file mode 100644 index 000000000..94a24606d --- /dev/null +++ b/core/testdata/format/javaSeeTag.java @@ -0,0 +1,6 @@ +/** + * @see #bar + */ +public class Foo { + public void bar() {} +}
\ No newline at end of file diff --git a/core/testdata/format/javaSpaceInAuthor.java b/core/testdata/format/javaSpaceInAuthor.java new file mode 100644 index 000000000..f980ae07c --- /dev/null +++ b/core/testdata/format/javaSpaceInAuthor.java @@ -0,0 +1,5 @@ +/** + * @author Dmitry Jemerov + */ +class C { +}
\ No newline at end of file diff --git a/core/testdata/format/javaSpaceInAuthor.md b/core/testdata/format/javaSpaceInAuthor.md new file mode 100644 index 000000000..1d2251d0f --- /dev/null +++ b/core/testdata/format/javaSpaceInAuthor.md @@ -0,0 +1,13 @@ +[test](../index.md) / [C](./index.md) + +# C + +`protected open class C` + +**Author** +Dmitry Jemerov + +### Constructors + +| [<init>](-init-.md) | `C()` | + diff --git a/core/testdata/format/javadocCodeMultiline.java b/core/testdata/format/javadocCodeMultiline.java new file mode 100644 index 000000000..a8b8de699 --- /dev/null +++ b/core/testdata/format/javadocCodeMultiline.java @@ -0,0 +1,10 @@ +/** + * <pre> + * <Button + * android:id="@+id/button_id" + * android:layout_height="wrap_content" + * android:layout_width="wrap_content" + * android:text="@string/self_destruct" /></pre> + */ +public class C { +} diff --git a/core/testdata/format/javadocCodeMultiline.md b/core/testdata/format/javadocCodeMultiline.md new file mode 100644 index 000000000..35dd1ac91 --- /dev/null +++ b/core/testdata/format/javadocCodeMultiline.md @@ -0,0 +1,26 @@ +[test](../index.md) / [C](./index.md) + +# C + +`open class C` + +``` +<Button + android:id="@+id/button_id" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/self_destruct" /> +``` + +### Constructors + +| [<init>](-init-.md) | `C()`<br> + +``` +<Button + android:id="@+id/button_id" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:text="@string/self_destruct" /><br>``` +<br> | + diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java new file mode 100644 index 000000000..622116b28 --- /dev/null +++ b/core/testdata/format/javadocHtml.java @@ -0,0 +1,14 @@ +/** + * <b>Bold</b> + * <strong>Strong</strong> + * <i>Italic</i> + * <em>Emphasized</em> + * <p>Paragraph</p> + * <s>Strikethrough</s> + * <del>Deleted</del> + * <code>Code</code> + * <pre>Block code</pre> + * <ul><li>List Item</li></ul> + */ +public class C { +} diff --git a/core/testdata/format/javadocHtml.md b/core/testdata/format/javadocHtml.md new file mode 100644 index 000000000..a3c1baff7 --- /dev/null +++ b/core/testdata/format/javadocHtml.md @@ -0,0 +1,28 @@ +[test](../index.md) / [C](./index.md) + +# C + +`open class C` + +**Bold** **Strong** *Italic* *Emphasized* + +Paragraph + + ~~Strikethrough~~ ~~Deleted~~ `Code` + +``` +Block code +``` + + * List Item + +### Constructors + +| [<init>](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code` + +``` +Block code<br>``` +<br> +* List Item +<br> | + diff --git a/core/testdata/format/javadocOrderedList.java b/core/testdata/format/javadocOrderedList.java new file mode 100644 index 000000000..c32d9032a --- /dev/null +++ b/core/testdata/format/javadocOrderedList.java @@ -0,0 +1,8 @@ +/** + * <ol> + * <li>Rinse</li> + * <li>Repeat</li> + * </ol> + */ +public class Bar { +} diff --git a/core/testdata/format/javadocOrderedList.md b/core/testdata/format/javadocOrderedList.md new file mode 100644 index 000000000..88d5f5b66 --- /dev/null +++ b/core/testdata/format/javadocOrderedList.md @@ -0,0 +1,17 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`open class Bar` + + 1. Rinse + 2. Repeat + + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br> +1. Rinse + 2. Repeat + <br> | + diff --git a/core/testdata/format/jdkLinks.kt b/core/testdata/format/jdkLinks.kt new file mode 100644 index 000000000..660fb4ce4 --- /dev/null +++ b/core/testdata/format/jdkLinks.kt @@ -0,0 +1,7 @@ +/** + * This is a [ClassLoader] and I can get its [ClassLoader.getResource] + * + * You can print something to [java.lang.System.out] now! + */ +class C : ClassLoader { +} diff --git a/core/testdata/format/jdkLinks.md b/core/testdata/format/jdkLinks.md new file mode 100644 index 000000000..7498171d7 --- /dev/null +++ b/core/testdata/format/jdkLinks.md @@ -0,0 +1,14 @@ +[test](../index.md) / [C](./index.md) + +# C + +`class C : `[`ClassLoader`](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) + +This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) + +You can print something to [java.lang.System.out](http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now! + +### Constructors + +| [<init>](-init-.md) | `C()`<br>This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) | + diff --git a/core/testdata/format/linkWithLabel.html b/core/testdata/format/linkWithLabel.html new file mode 100644 index 000000000..59bc6ddf6 --- /dev/null +++ b/core/testdata/format/linkWithLabel.html @@ -0,0 +1,39 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Bar - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Bar</a><br/> +<br/> +<h1>Bar</h1> +<code><span class="keyword">class </span><span class="identifier">Bar</span></code> +<p>Use <a href="foo.html">this method</a> for best results.</p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Use <a href="foo.html">this method</a> for best results.</p> +</td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="foo.html">foo</a></p> +</td> +<td> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/linkWithLabel.kt b/core/testdata/format/linkWithLabel.kt new file mode 100644 index 000000000..4a85c505e --- /dev/null +++ b/core/testdata/format/linkWithLabel.kt @@ -0,0 +1,6 @@ +/** + * Use [this method][Bar.foo] for best results. + */ +class Bar { + fun foo() {} +} diff --git a/core/testdata/format/linkWithStarProjection.html b/core/testdata/format/linkWithStarProjection.html new file mode 100644 index 000000000..e1b6e098a --- /dev/null +++ b/core/testdata/format/linkWithStarProjection.html @@ -0,0 +1,24 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>KClassLoader - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">KClassLoader</a><br/> +<br/> +<h1>KClassLoader</h1> +<code><span class="keyword">object </span><span class="identifier">KClassLoader</span></code> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="foo.html">foo</a></p> +</td> +<td> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="KClassLoader$foo(kotlin.Enum(()))/c">c</span><span class="symbol">:</span> <span class="identifier">Enum</span><span class="symbol"><</span><span class="identifier">*</span><span class="symbol">></span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/linkWithStarProjection.kt b/core/testdata/format/linkWithStarProjection.kt new file mode 100644 index 000000000..6da6c5958 --- /dev/null +++ b/core/testdata/format/linkWithStarProjection.kt @@ -0,0 +1,3 @@ +object KClassLoader { + fun foo(c: Enum<*>) { } +} diff --git a/core/testdata/format/linksInEmphasis.kt b/core/testdata/format/linksInEmphasis.kt new file mode 100644 index 000000000..3e2017d25 --- /dev/null +++ b/core/testdata/format/linksInEmphasis.kt @@ -0,0 +1,13 @@ +/** + * An emphasised class. + * + * _This class [Bar] is awesome._ + * + * _Even more awesomer is the function [Bar.foo]_ + * + * _[Bar.hello] is also OK_ + */ +class Bar { + fun foo() {} + fun hello() {} +} diff --git a/core/testdata/format/linksInEmphasis.md b/core/testdata/format/linksInEmphasis.md new file mode 100644 index 000000000..d0ae70c87 --- /dev/null +++ b/core/testdata/format/linksInEmphasis.md @@ -0,0 +1,23 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +An emphasised class. + +*This class [Bar](./index.md) is awesome.* + +*Even more awesomer is the function [Bar.foo](foo.md)* + +*[Bar.hello](hello.md) is also OK* + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>An emphasised class. | + +### Functions + +| [foo](foo.md) | `fun foo(): Unit` | +| [hello](hello.md) | `fun hello(): Unit` | + diff --git a/core/testdata/format/linksInHeaders.kt b/core/testdata/format/linksInHeaders.kt new file mode 100644 index 000000000..18efd34b2 --- /dev/null +++ b/core/testdata/format/linksInHeaders.kt @@ -0,0 +1,24 @@ +/** + * Some class with really useless documentation. + * + * # Beer o'clock - time to go to the [Bar] + * + * ## But __is [it](isitbeeroclock.com)__ really? + * + * ### [Bar.hello] to the [Bar.world]! + * + * #### _Kotlin is amazing, [Bar.none]_ + * + * ##### We need to go [Bar.deeper] + * + * ###### End of the [Bar.line] - we need to go back! + */ +class Bar { + fun foo() {} + fun hello() {} + fun world() {} + fun kotlin() {} + fun none() {} + fun deeper() {} + fun line() {} +} diff --git a/core/testdata/format/linksInHeaders.md b/core/testdata/format/linksInHeaders.md new file mode 100644 index 000000000..1dc7d18b4 --- /dev/null +++ b/core/testdata/format/linksInHeaders.md @@ -0,0 +1,34 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +Some class with really useless documentation. + +# Beer o'clock - time to go to the [Bar](./index.md) + +## But **is [it](isitbeeroclock.com)** really? + +### [Bar.hello](hello.md) to the [Bar.world](world.md)! + +#### *Kotlin is amazing, [Bar.none](none.md)* + +##### We need to go [Bar.deeper](deeper.md) + +###### End of the [Bar.line](line.md) - we need to go back! + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>Some class with really useless documentation. | + +### Functions + +| [deeper](deeper.md) | `fun deeper(): Unit` | +| [foo](foo.md) | `fun foo(): Unit` | +| [hello](hello.md) | `fun hello(): Unit` | +| [kotlin](kotlin.md) | `fun kotlin(): Unit` | +| [line](line.md) | `fun line(): Unit` | +| [none](none.md) | `fun none(): Unit` | +| [world](world.md) | `fun world(): Unit` | + diff --git a/core/testdata/format/linksInStrong.kt b/core/testdata/format/linksInStrong.kt new file mode 100644 index 000000000..b9e295ecb --- /dev/null +++ b/core/testdata/format/linksInStrong.kt @@ -0,0 +1,13 @@ +/** + * A strong class. + * + * __This class [Bar] is awesome.__ + * + * __Even more awesomer is the function [Bar.foo]__ + * + * __[Bar.hello] is also OK__ + */ +class Bar { + fun foo() {} + fun hello() {} +} diff --git a/core/testdata/format/linksInStrong.md b/core/testdata/format/linksInStrong.md new file mode 100644 index 000000000..5b44112d6 --- /dev/null +++ b/core/testdata/format/linksInStrong.md @@ -0,0 +1,23 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +A strong class. + +**This class [Bar](./index.md) is awesome.** + +**Even more awesomer is the function [Bar.foo](foo.md)** + +**[Bar.hello](hello.md) is also OK** + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>A strong class. | + +### Functions + +| [foo](foo.md) | `fun foo(): Unit` | +| [hello](hello.md) | `fun hello(): Unit` | + diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html new file mode 100644 index 000000000..596cca73e --- /dev/null +++ b/core/testdata/format/markdownInLinks.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p><a href="http://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p> +</BODY> +</HTML> diff --git a/core/testdata/format/markdownInLinks.kt b/core/testdata/format/markdownInLinks.kt new file mode 100644 index 000000000..67b6311f0 --- /dev/null +++ b/core/testdata/format/markdownInLinks.kt @@ -0,0 +1,4 @@ +/** + * [a**b**__d__ kas ](http://www.ibm.com) + */ +fun foo() {} diff --git a/core/testdata/format/memberExtension.kt b/core/testdata/format/memberExtension.kt new file mode 100644 index 000000000..955794d1e --- /dev/null +++ b/core/testdata/format/memberExtension.kt @@ -0,0 +1,8 @@ +open class X + +class Foo : X + +class Bar { + fun X.y() = "" + fun Foo.x() = "" +} diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md new file mode 100644 index 000000000..0ec1fda3e --- /dev/null +++ b/core/testdata/format/memberExtension.md @@ -0,0 +1,10 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo : `[`X`](../-x/index.md) + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt new file mode 100644 index 000000000..d7fbf924a --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt @@ -0,0 +1,7 @@ +package pack + +class Some { + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt new file mode 100644 index 000000000..57f36742e --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md new file mode 100644 index 000000000..37e943ad0 --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md @@ -0,0 +1,8 @@ +[test](../../../index.md) / [pack](../../index.md) / [Some](../index.md) / [magic](./magic.md) + +# magic + +`fun magic(): Unit` + +**Platform and version requirements:** JS + diff --git a/core/testdata/format/multiplatform/groupNode/js.kt b/core/testdata/format/multiplatform/groupNode/js.kt new file mode 100644 index 000000000..045f3f0d6 --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/js.kt @@ -0,0 +1,8 @@ +package pack + +class Some { + + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/groupNode/jvm.kt b/core/testdata/format/multiplatform/groupNode/jvm.kt new file mode 100644 index 000000000..57f36742e --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.md b/core/testdata/format/multiplatform/groupNode/multiplatform.md new file mode 100644 index 000000000..74d464c9b --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md @@ -0,0 +1,20 @@ +[test](../../index.md) / [pack](../index.md) / [Some](./index.md) + +# Some + +`typealias Some = SomeCoolJvmClass` + +**Platform and version requirements:** JVM + +`class Some` + +**Platform and version requirements:** JS + +### Constructors + +| [<init>](-some/-init-.md) | `Some()` | + +### Functions + +| [magic](-some/magic.md) | `fun magic(): Unit` | + diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md new file mode 100644 index 000000000..5708795e6 --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md @@ -0,0 +1,13 @@ +[test](../index.md) / [pack](./index.md) + +## Package pack + +### Types + +| [Some](-some/index.md)<br>(JS) | `class Some` | +| [SomeCoolJvmClass](-some-cool-jvm-class/index.md)<br>(JVM) | `class SomeCoolJvmClass` | + +### Type Aliases + +| [Some](-some/index.md)<br>(JVM) | `typealias Some = SomeCoolJvmClass` | + diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md new file mode 100644 index 000000000..fca2aff4f --- /dev/null +++ b/core/testdata/format/multiplatform/implied/foo.md @@ -0,0 +1,24 @@ +[test](../../index.md) / [foo](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +This is a foo. + +### Constructors + +| [<init>](-init-.md) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJs](prop-js.md)<br>(JS) | `val propJs: String` | +| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` | +| [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` | + +### Functions + +| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` | +| [js](js.md)<br>(JS) | `fun js(): Unit` | +| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatform/implied/js.kt b/core/testdata/format/multiplatform/implied/js.kt new file mode 100644 index 000000000..dd2de5bc1 --- /dev/null +++ b/core/testdata/format/multiplatform/implied/js.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun js() { + } + + val propJvmAndJs = 0 + + val propJs = "abc" +} diff --git a/core/testdata/format/multiplatform/implied/jvm.kt b/core/testdata/format/multiplatform/implied/jvm.kt new file mode 100644 index 000000000..8d73ce25f --- /dev/null +++ b/core/testdata/format/multiplatform/implied/jvm.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun jvm() { + } + + val propJvmAndJs = 0 + + val propJvm = "abc" +} diff --git a/core/testdata/format/multiplatform/merge/js.kt b/core/testdata/format/multiplatform/merge/js.kt new file mode 100644 index 000000000..bbf1dd7ce --- /dev/null +++ b/core/testdata/format/multiplatform/merge/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a foo. + */ +class Foo { +} diff --git a/core/testdata/format/multiplatform/merge/jvm.kt b/core/testdata/format/multiplatform/merge/jvm.kt new file mode 100644 index 000000000..cb77273f2 --- /dev/null +++ b/core/testdata/format/multiplatform/merge/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md new file mode 100644 index 000000000..ea78b5a36 --- /dev/null +++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](../index.md) / [foo](./index.md) + +## Package foo + +**Platform and version requirements:** JVM, JS + +### Types + +| [Foo](-foo/index.md)<br>(JVM, JS) | `class Foo`<br>This is a foo. | + diff --git a/core/testdata/format/multiplatform/mergeMembers/foo.md b/core/testdata/format/multiplatform/mergeMembers/foo.md new file mode 100644 index 000000000..7f41b7d13 --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/foo.md @@ -0,0 +1,26 @@ +[test](../../index.md) / [foo](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +**Platform and version requirements:** JVM, JS + +This is a foo. + +### Constructors + +| [<init>](-init-.md) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJs](prop-js.md)<br>(JS) | `val propJs: String` | +| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` | +| [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` | + +### Functions + +| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` | +| [js](js.md)<br>(JS) | `fun js(): Unit` | +| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatform/mergeMembers/js.kt b/core/testdata/format/multiplatform/mergeMembers/js.kt new file mode 100644 index 000000000..dd2de5bc1 --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/js.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun js() { + } + + val propJvmAndJs = 0 + + val propJs = "abc" +} diff --git a/core/testdata/format/multiplatform/mergeMembers/jvm.kt b/core/testdata/format/multiplatform/mergeMembers/jvm.kt new file mode 100644 index 000000000..8d73ce25f --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/jvm.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun jvm() { + } + + val propJvmAndJs = 0 + + val propJvm = "abc" +} diff --git a/core/testdata/format/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md new file mode 100644 index 000000000..a20b14cf8 --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/foo.md @@ -0,0 +1,22 @@ +[test](../../index.md) / [foo](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo` + +**Platform and version requirements:** JVM + +This is a foo. + +### Constructors + +| [<init>](-init-.md) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJvm](prop-jvm.md) | `val propJvm: String` | + +### Functions + +| [jvm](jvm.md) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatform/omitRedundant/js.kt b/core/testdata/format/multiplatform/omitRedundant/js.kt new file mode 100644 index 000000000..d1b1429cb --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/js.kt @@ -0,0 +1,2 @@ +package foo + diff --git a/core/testdata/format/multiplatform/omitRedundant/jvm.kt b/core/testdata/format/multiplatform/omitRedundant/jvm.kt new file mode 100644 index 000000000..35e3c08d5 --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/jvm.kt @@ -0,0 +1,11 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun jvm() { + } + + val propJvm = "abc" +} diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt new file mode 100644 index 000000000..86d092897 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt @@ -0,0 +1,3 @@ +package foo.bar + +fun buz() {} diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt new file mode 100644 index 000000000..86d092897 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt @@ -0,0 +1,3 @@ +package foo.bar + +fun buz() {} diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md new file mode 100644 index 000000000..6f45342b0 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md @@ -0,0 +1,10 @@ +[test](./index.md) + +**Platform and version requirements:** JVM, JS + +### Packages + +| [foo.bar](foo.bar/index.md)<br>(JVM, JS) | | + +### Index + diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md new file mode 100644 index 000000000..4ddfe2e3d --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](../index.md) / [foo.bar](./index.md) + +## Package foo.bar + +**Platform and version requirements:** JVM, JS + +### Functions + +| [buz](buz.md)<br>(JVM, JS) | `fun buz(): Unit` | + diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt new file mode 100644 index 000000000..27ab1b32e --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt @@ -0,0 +1,5 @@ +package some + +fun String.buz(): Unit { + +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md new file mode 100644 index 000000000..f4186b6ec --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md @@ -0,0 +1,10 @@ +[test](./index.md) + +**Platform and version requirements:** JVM + +### Packages + +| [some](some/index.md)<br>(JVM) | | + +### Index + diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md new file mode 100644 index 000000000..ff480b5a9 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](../index.md) / [some](./index.md) + +## Package some + +**Platform and version requirements:** JVM + +### Extensions for External Classes + +| [kotlin.String](kotlin.-string/index.md) | | + diff --git a/core/testdata/format/multiplatform/simple/js.kt b/core/testdata/format/multiplatform/simple/js.kt new file mode 100644 index 000000000..e6d66ffdc --- /dev/null +++ b/core/testdata/format/multiplatform/simple/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a bar. + */ +class Bar { +} diff --git a/core/testdata/format/multiplatform/simple/jvm.kt b/core/testdata/format/multiplatform/simple/jvm.kt new file mode 100644 index 000000000..cb77273f2 --- /dev/null +++ b/core/testdata/format/multiplatform/simple/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatform/simple/multiplatform.package.md b/core/testdata/format/multiplatform/simple/multiplatform.package.md new file mode 100644 index 000000000..fad7e90d8 --- /dev/null +++ b/core/testdata/format/multiplatform/simple/multiplatform.package.md @@ -0,0 +1,9 @@ +[test](../index.md) / [foo](./index.md) + +## Package foo + +### Types + +| [Bar](-bar/index.md)<br>(JS) | `class Bar`<br>This is a bar. | +| [Foo](-foo/index.md)<br>(JVM) | `class Foo`<br>This is a foo. | + diff --git a/core/testdata/format/multipleTypeParameterConstraints.kt b/core/testdata/format/multipleTypeParameterConstraints.kt new file mode 100644 index 000000000..9d085c3cf --- /dev/null +++ b/core/testdata/format/multipleTypeParameterConstraints.kt @@ -0,0 +1,11 @@ +interface A { + +} + +interface B { + +} + + +fun f<T> where T : A, T : B { +} diff --git a/core/testdata/format/multipleTypeParameterConstraints.md b/core/testdata/format/multipleTypeParameterConstraints.md new file mode 100644 index 000000000..78586acae --- /dev/null +++ b/core/testdata/format/multipleTypeParameterConstraints.md @@ -0,0 +1,18 @@ +<!-- File: test/-a.md --> +[test](index.md) / [A](./-a.md) + +# A + +`interface A` +<!-- File: test/-b.md --> +[test](index.md) / [B](./-b.md) + +# B + +`interface B` +<!-- File: test/f.md --> +[test](index.md) / [f](./f.md) + +# f + +`fun <T> f(): Unit where T : `[`A`](-a.md)`, T : `[`B`](-b.md)
\ No newline at end of file diff --git a/core/testdata/format/nestedLists.kt b/core/testdata/format/nestedLists.kt new file mode 100644 index 000000000..83217f8a5 --- /dev/null +++ b/core/testdata/format/nestedLists.kt @@ -0,0 +1,31 @@ +/** + * Usage instructions: + * + * - __Rinse__ + * 1. Alter any rinse options _(optional)_ + * - Recommended to [Bar.useSoap] + * - Optionally apply [Bar.elbowGrease] for best results + * 2. [Bar.rinse] to begin rinse + * 1. Thus you should call [Bar.rinse] + * 2. *Then* call [Bar.repeat] + * - Don't forget to use: + * - Soap + * - Elbow Grease + * 3. Finally, adjust soap usage [Bar.useSoap] as needed + * 3. Repeat with [Bar.repeat] + * + * - __Repeat__ + * - Will use previously used rinse options + * - [Bar.rinse] must have been called once before + * - Can be repeated any number of times + * - Options include: + * - [Bar.useSoap] + * - [Bar.useElbowGrease] + */ +class Bar { + fun rinse() = Unit + fun repeat() = Unit + + var useSoap = false + var useElbowGrease = false +} diff --git a/core/testdata/format/nestedLists.md b/core/testdata/format/nestedLists.md new file mode 100644 index 000000000..fb25bc32e --- /dev/null +++ b/core/testdata/format/nestedLists.md @@ -0,0 +1,43 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +Usage instructions: + +* **Rinse** + 1. Alter any rinse options *(optional)* + * Recommended to [Bar.useSoap](use-soap.md) + * Optionally apply [Bar.elbowGrease](#) for best results + 2. [Bar.rinse](rinse.md) to begin rinse + 1. Thus you should call [Bar.rinse](rinse.md) + 2. *Then* call [Bar.repeat](repeat.md) + * Don't forget to use: + * Soap + * Elbow Grease + 3. Finally, adjust soap usage [Bar.useSoap](use-soap.md) as needed + 3. Repeat with [Bar.repeat](repeat.md) + +* **Repeat** + * Will use previously used rinse options + * [Bar.rinse](rinse.md) must have been called once before + * Can be repeated any number of times + * Options include: + * [Bar.useSoap](use-soap.md) + * [Bar.useElbowGrease](use-elbow-grease.md) + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>Usage instructions: | + +### Properties + +| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` | +| [useSoap](use-soap.md) | `var useSoap: Boolean` | + +### Functions + +| [repeat](repeat.md) | `fun repeat(): Unit` | +| [rinse](rinse.md) | `fun rinse(): Unit` | + diff --git a/core/testdata/format/newlineInTableCell.kt b/core/testdata/format/newlineInTableCell.kt new file mode 100644 index 000000000..3e0616f05 --- /dev/null +++ b/core/testdata/format/newlineInTableCell.kt @@ -0,0 +1,6 @@ +/** + * There is `long long int` story + * full of + * new lines + */ +class A
\ No newline at end of file diff --git a/core/testdata/format/newlineInTableCell.package.md b/core/testdata/format/newlineInTableCell.package.md new file mode 100644 index 000000000..53716db3a --- /dev/null +++ b/core/testdata/format/newlineInTableCell.package.md @@ -0,0 +1,8 @@ +[test](./index.md) + +## Package <root> + +### Types + +| [A](-a/index.md) | `class A`<br>There is `long long int` story full of new lines | + diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt b/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt new file mode 100644 index 000000000..1f29e1107 --- /dev/null +++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt @@ -0,0 +1,13 @@ + +class A +class B + + +internal typealias TA = A +private typealias TB = B + +/** + * Correct ref [TA] + * Correct ref [TB] + */ +fun foo() {}
\ No newline at end of file diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.md b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md new file mode 100644 index 000000000..ca95093c6 --- /dev/null +++ b/core/testdata/format/notPublishedTypeAliasAutoExpansion.md @@ -0,0 +1,9 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): Unit` + +Correct ref [TA](-a/index.md) +Correct ref [TB](-b/index.md) + diff --git a/core/testdata/format/nullability.kt b/core/testdata/format/nullability.kt new file mode 100644 index 000000000..d1d4545bf --- /dev/null +++ b/core/testdata/format/nullability.kt @@ -0,0 +1,5 @@ +class C<T> { + fun foo(): Comparable<T>? { + return null + } +} diff --git a/core/testdata/format/nullability.md b/core/testdata/format/nullability.md new file mode 100644 index 000000000..7b81c2554 --- /dev/null +++ b/core/testdata/format/nullability.md @@ -0,0 +1,14 @@ +[test](../index.md) / [C](./index.md) + +# C + +`class C<T>` + +### Constructors + +| [<init>](-init-.md) | `C()` | + +### Functions + +| [foo](foo.md) | `fun foo(): Comparable<`[`T`](index.md#T)`>?` | + diff --git a/core/testdata/format/operatorOverloading.kt b/core/testdata/format/operatorOverloading.kt new file mode 100644 index 000000000..6fe78e45f --- /dev/null +++ b/core/testdata/format/operatorOverloading.kt @@ -0,0 +1,3 @@ +class C { + fun plus(other: C): C +} diff --git a/core/testdata/format/operatorOverloading.md b/core/testdata/format/operatorOverloading.md new file mode 100644 index 000000000..0a4c87b64 --- /dev/null +++ b/core/testdata/format/operatorOverloading.md @@ -0,0 +1,5 @@ +[test](../index.md) / [C](index.md) / [plus](./plus.md) + +# plus + +`fun plus(other: `[`C`](index.md)`): `[`C`](index.md)
\ No newline at end of file diff --git a/core/testdata/format/orderedList.html b/core/testdata/format/orderedList.html new file mode 100644 index 000000000..6f735bfdb --- /dev/null +++ b/core/testdata/format/orderedList.html @@ -0,0 +1,30 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Bar - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Bar</a><br/> +<br/> +<h1>Bar</h1> +<code><span class="keyword">class </span><span class="identifier">Bar</span></code> +<p>Usage instructions:</p> +<ol><li>Rinse</li> +<li>Repeat</li> +</ol> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Usage instructions:</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/orderedList.kt b/core/testdata/format/orderedList.kt new file mode 100644 index 000000000..03681c7a3 --- /dev/null +++ b/core/testdata/format/orderedList.kt @@ -0,0 +1,8 @@ +/** + * Usage instructions: + * + * 1. Rinse + * 1. Repeat + */ +class Bar { +} diff --git a/core/testdata/format/overloads.html b/core/testdata/format/overloads.html new file mode 100644 index 000000000..feda82e4e --- /dev/null +++ b/core/testdata/format/overloads.html @@ -0,0 +1,26 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>root package - test</title> +</HEAD> +<BODY> +<a href="./index.html">test</a><br/> +<br/> +<h2>Package <root></h2> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="f.html">f</a></p> +</td> +<td> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Performs an action on x.</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/overloads.kt b/core/testdata/format/overloads.kt new file mode 100644 index 000000000..dcd2d097f --- /dev/null +++ b/core/testdata/format/overloads.kt @@ -0,0 +1,5 @@ +/** Performs an action on x. */ +fun f(x: Int) { } + +/** Performs an action on x. */ +fun f(x: String) { } diff --git a/core/testdata/format/overloadsWithDescription.html b/core/testdata/format/overloadsWithDescription.html new file mode 100644 index 000000000..16b03f7e4 --- /dev/null +++ b/core/testdata/format/overloadsWithDescription.html @@ -0,0 +1,20 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f(kotlin.Int)"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/> +<a name="$f(kotlin.String)"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Performs an action on <a href="f.html#$f(kotlin.Int)/x">x</a>.</p> +<p>This is a long description.</p> +<h3>Parameters</h3> +<p><a name="x"></a> +<code>x</code> - the value to perform the action on.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/overloadsWithDescription.kt b/core/testdata/format/overloadsWithDescription.kt new file mode 100644 index 000000000..740e642f9 --- /dev/null +++ b/core/testdata/format/overloadsWithDescription.kt @@ -0,0 +1,15 @@ +/** + * Performs an action on [x]. + * + * This is a long description. + * @param x the value to perform the action on. + */ +fun f(x: Int) { } + +/** + * Performs an action on [x]. + * + * This is a long description. + * @param x the value to perform the action on. + */ +fun f(x: String) { } diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.html b/core/testdata/format/overloadsWithDifferentDescriptions.html new file mode 100644 index 000000000..4c4f7f748 --- /dev/null +++ b/core/testdata/format/overloadsWithDifferentDescriptions.html @@ -0,0 +1,25 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f(kotlin.Int)"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Performs an action on x.</p> +<p>This is a long description.</p> +<h3>Parameters</h3> +<p><a name="x"></a> +<code>x</code> - the int value to perform the action on.</p> +<a name="$f(kotlin.String)"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Performs an action on x.</p> +<p>This is a long description.</p> +<h3>Parameters</h3> +<p><a name="x"></a> +<code>x</code> - the string value to perform the action on.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.kt b/core/testdata/format/overloadsWithDifferentDescriptions.kt new file mode 100644 index 000000000..ad3169b0c --- /dev/null +++ b/core/testdata/format/overloadsWithDifferentDescriptions.kt @@ -0,0 +1,15 @@ +/** + * Performs an action on x. + * + * This is a long description. + * @param x the int value to perform the action on. + */ +fun f(x: Int) { } + +/** + * Performs an action on x. + * + * This is a long description. + * @param x the string value to perform the action on. + */ +fun f(x: String) { } diff --git a/core/testdata/format/overridingFunction.kt b/core/testdata/format/overridingFunction.kt new file mode 100644 index 000000000..d7329489a --- /dev/null +++ b/core/testdata/format/overridingFunction.kt @@ -0,0 +1,7 @@ +open class C() { + open fun f() {} +} + +class D(): C() { + override fun f() {} +} diff --git a/core/testdata/format/overridingFunction.md b/core/testdata/format/overridingFunction.md new file mode 100644 index 000000000..d0ec82fa0 --- /dev/null +++ b/core/testdata/format/overridingFunction.md @@ -0,0 +1,8 @@ +[test](../index.md) / [D](index.md) / [f](./f.md) + +# f + +`fun f(): Unit` + +Overrides [C.f](../-c/f.md) + diff --git a/core/testdata/format/paramTag.kt b/core/testdata/format/paramTag.kt new file mode 100644 index 000000000..47e471f53 --- /dev/null +++ b/core/testdata/format/paramTag.kt @@ -0,0 +1,6 @@ +/** + * @param x A string + * @param y A number with a really long description that spans multiple lines and goes + * on and on and is very interesting to read + */ +fun f(x: String, y: Int) {} diff --git a/core/testdata/format/paramTag.md b/core/testdata/format/paramTag.md new file mode 100644 index 000000000..f39848887 --- /dev/null +++ b/core/testdata/format/paramTag.md @@ -0,0 +1,12 @@ +[test](index.md) / [f](./f.md) + +# f + +`fun f( x: String, y: Int): Unit` + +### Parameters + +`x` - A string + +`y` - A number with a really long description that spans multiple lines and goes + on and on and is very interesting to read
\ No newline at end of file diff --git a/core/testdata/format/parameterAnchor.html b/core/testdata/format/parameterAnchor.html new file mode 100644 index 000000000..a4ae0997e --- /dev/null +++ b/core/testdata/format/parameterAnchor.html @@ -0,0 +1,17 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>processFiles - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./process-files.html">processFiles</a><br/> +<br/> +<h1>processFiles</h1> +<a name="$processFiles(kotlin.Function0((processFiles.T)))"></a> +<code><span class="keyword">fun </span><span class="symbol"><</span><span class="identifier">T</span><span class="symbol">></span> <span class="identifier">processFiles</span><span class="symbol">(</span><span class="identifier" id="$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</span><span class="symbol">:</span> <span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">List</span><span class="symbol"><</span><a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">></span></code> +<p>Runs <a href="process-files.html#$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</a> for each file and collects its results into single list</p> +<h3>Parameters</h3> +<p><a name="processor"></a> +<code>processor</code> - function to receive context for symbol resolution and file for processing</p> +</BODY> +</HTML> diff --git a/core/testdata/format/parameterAnchor.kt b/core/testdata/format/parameterAnchor.kt new file mode 100644 index 000000000..ae36ee4c9 --- /dev/null +++ b/core/testdata/format/parameterAnchor.kt @@ -0,0 +1,6 @@ +/** + * Runs [processor] for each file and collects its results into single list + * @param processor function to receive context for symbol resolution and file for processing + */ +public fun processFiles<T>(processor: () -> T): List<T> { +} diff --git a/core/testdata/format/parenthesis.html b/core/testdata/format/parenthesis.html new file mode 100644 index 000000000..c63154c12 --- /dev/null +++ b/core/testdata/format/parenthesis.html @@ -0,0 +1,14 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>foo (bar)</p> +</BODY> +</HTML> diff --git a/core/testdata/format/parenthesis.kt b/core/testdata/format/parenthesis.kt new file mode 100644 index 000000000..b906f64a9 --- /dev/null +++ b/core/testdata/format/parenthesis.kt @@ -0,0 +1,4 @@ +/** + * foo (bar) + */ +fun foo() {} diff --git a/core/testdata/format/propertyVar.kt b/core/testdata/format/propertyVar.kt new file mode 100644 index 000000000..88be1a7ae --- /dev/null +++ b/core/testdata/format/propertyVar.kt @@ -0,0 +1 @@ +var x = 1
\ No newline at end of file diff --git a/core/testdata/format/propertyVar.md b/core/testdata/format/propertyVar.md new file mode 100644 index 000000000..887d25a5b --- /dev/null +++ b/core/testdata/format/propertyVar.md @@ -0,0 +1,5 @@ +[test](index.md) / [x](./x.md) + +# x + +`var x: Int`
\ No newline at end of file diff --git a/core/testdata/format/qualifiedNameLink.kt b/core/testdata/format/qualifiedNameLink.kt new file mode 100644 index 000000000..be82a9905 --- /dev/null +++ b/core/testdata/format/qualifiedNameLink.kt @@ -0,0 +1,6 @@ +/** + * See [kotlin.apply] for the docs + */ +fun foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/qualifiedNameLink.md b/core/testdata/format/qualifiedNameLink.md new file mode 100644 index 000000000..92fa8f7ac --- /dev/null +++ b/core/testdata/format/qualifiedNameLink.md @@ -0,0 +1,8 @@ +[test](index.md) / [foo](./foo.md) + +# foo + +`fun foo(): Unit` + +See [kotlin.apply](#) for the docs + diff --git a/core/testdata/format/receiverParameterTypeBound.kt b/core/testdata/format/receiverParameterTypeBound.kt new file mode 100644 index 000000000..2b5f6f176 --- /dev/null +++ b/core/testdata/format/receiverParameterTypeBound.kt @@ -0,0 +1,5 @@ +open class Foo { +} + +fun <T : Foo> T.xyzzy() { +} diff --git a/core/testdata/format/receiverParameterTypeBound.md b/core/testdata/format/receiverParameterTypeBound.md new file mode 100644 index 000000000..978dc0f81 --- /dev/null +++ b/core/testdata/format/receiverParameterTypeBound.md @@ -0,0 +1,14 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`open class Foo` + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + +### Extension Functions + +| [xyzzy](../xyzzy.md) | `fun <T : `[`Foo`](./index.md)`> `[`T`](../xyzzy.md#T)`.xyzzy(): Unit` | + diff --git a/core/testdata/format/receiverReference.kt b/core/testdata/format/receiverReference.kt new file mode 100644 index 000000000..3e6e2056d --- /dev/null +++ b/core/testdata/format/receiverReference.kt @@ -0,0 +1,6 @@ +/** + * Prints [this] + */ +fun String.some() { + println(this) +}
\ No newline at end of file diff --git a/core/testdata/format/receiverReference.md b/core/testdata/format/receiverReference.md new file mode 100644 index 000000000..bdcce3228 --- /dev/null +++ b/core/testdata/format/receiverReference.md @@ -0,0 +1,6 @@ +[test](../index.md) / [kotlin.String](./index.md) + +### Extensions for kotlin.String + +| [some](some.md) | `fun String.some(): Unit`<br>Prints [this](some/-this-.md) | + diff --git a/core/testdata/format/referenceLink.kt b/core/testdata/format/referenceLink.kt new file mode 100644 index 000000000..c6550f044 --- /dev/null +++ b/core/testdata/format/referenceLink.kt @@ -0,0 +1,16 @@ +package example + +/** + * It is link to [example other func][example] + * + * Sure, it is [example] + * + * [example]: example.someOtherFunc + */ +fun a() { + +} + +fun someOtherFunc() { + +}
\ No newline at end of file diff --git a/core/testdata/format/referenceLink.md b/core/testdata/format/referenceLink.md new file mode 100644 index 000000000..ee910cbf1 --- /dev/null +++ b/core/testdata/format/referenceLink.md @@ -0,0 +1,17 @@ +<!-- File: test/example/a.md --> +[test](../index.md) / [example](index.md) / [a](./a.md) + +# a + +`fun a(): Unit` + +It is link to [example other func](some-other-func.md) + +Sure, it is [example](some-other-func.md) + +<!-- File: test/example/some-other-func.md --> +[test](../index.md) / [example](index.md) / [someOtherFunc](./some-other-func.md) + +# someOtherFunc + +`fun someOtherFunc(): Unit`
\ No newline at end of file diff --git a/core/testdata/format/reifiedTypeParameter.kt b/core/testdata/format/reifiedTypeParameter.kt new file mode 100644 index 000000000..00fa1dc92 --- /dev/null +++ b/core/testdata/format/reifiedTypeParameter.kt @@ -0,0 +1,3 @@ +inline fun f<reified T>() { + +} diff --git a/core/testdata/format/reifiedTypeParameter.md b/core/testdata/format/reifiedTypeParameter.md new file mode 100644 index 000000000..40dbed7b8 --- /dev/null +++ b/core/testdata/format/reifiedTypeParameter.md @@ -0,0 +1,5 @@ +[test](index.md) / [f](./f.md) + +# f + +`inline fun <reified T> f(): Unit`
\ No newline at end of file diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt new file mode 100644 index 000000000..84f78dfb1 --- /dev/null +++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt @@ -0,0 +1,3 @@ +fun (suspend () -> Unit).foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md new file mode 100644 index 000000000..4b5f3a64e --- /dev/null +++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md @@ -0,0 +1,6 @@ +[test](../index.md) / [kotlin.coroutines.SuspendFunction0](./index.md) + +### Extensions for kotlin.coroutines.SuspendFunction0 + +| [foo](foo.md) | `fun (suspend () -> Unit).foo(): Unit` | + diff --git a/core/testdata/format/returnWithLink.html b/core/testdata/format/returnWithLink.html new file mode 100644 index 000000000..fe1d031b0 --- /dev/null +++ b/core/testdata/format/returnWithLink.html @@ -0,0 +1,15 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo(kotlin.String)"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="$foo(kotlin.String)/s1">s1</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code> +<p><strong>Return</strong><br/> +Returns <a href="foo.html#$foo(kotlin.String)/s1">s1</a> and does nothing else.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/returnWithLink.kt b/core/testdata/format/returnWithLink.kt new file mode 100644 index 000000000..63d1770c3 --- /dev/null +++ b/core/testdata/format/returnWithLink.kt @@ -0,0 +1,4 @@ +/** + * @return Returns [s1] and does nothing else. + */ +fun foo(s1: String) = s1
\ No newline at end of file diff --git a/core/testdata/format/sampleByFQName.kt b/core/testdata/format/sampleByFQName.kt new file mode 100644 index 000000000..2c0af092d --- /dev/null +++ b/core/testdata/format/sampleByFQName.kt @@ -0,0 +1,12 @@ +package test + +fun sample() { + println("sample") +} + +/** + * @sample test.sample + */ +fun use() { + +}
\ No newline at end of file diff --git a/core/testdata/format/sampleByFQName.md b/core/testdata/format/sampleByFQName.md new file mode 100644 index 000000000..7093179d1 --- /dev/null +++ b/core/testdata/format/sampleByFQName.md @@ -0,0 +1,17 @@ +<!-- File: test/test/sample.md --> +[test](../index.md) / [test](index.md) / [sample](./sample.md) + +# sample + +`fun sample(): Unit` +<!-- File: test/test/use.md --> +[test](../index.md) / [test](index.md) / [use](./use.md) + +# use + +`fun use(): Unit` + +``` kotlin +println("sample") +``` + diff --git a/core/testdata/format/sampleByShortName.kt b/core/testdata/format/sampleByShortName.kt new file mode 100644 index 000000000..2e03310fb --- /dev/null +++ b/core/testdata/format/sampleByShortName.kt @@ -0,0 +1,12 @@ +package test + +fun sample() { + println("sample") +} + +/** + * @sample sample + */ +fun use() { + +}
\ No newline at end of file diff --git a/core/testdata/format/sampleByShortName.md b/core/testdata/format/sampleByShortName.md new file mode 100644 index 000000000..7093179d1 --- /dev/null +++ b/core/testdata/format/sampleByShortName.md @@ -0,0 +1,17 @@ +<!-- File: test/test/sample.md --> +[test](../index.md) / [test](index.md) / [sample](./sample.md) + +# sample + +`fun sample(): Unit` +<!-- File: test/test/use.md --> +[test](../index.md) / [test](index.md) / [use](./use.md) + +# use + +`fun use(): Unit` + +``` kotlin +println("sample") +``` + diff --git a/core/testdata/format/see.html b/core/testdata/format/see.html new file mode 100644 index 000000000..7483250b7 --- /dev/null +++ b/core/testdata/format/see.html @@ -0,0 +1,46 @@ +<!-- File: test/bar.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>bar - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./bar.html">bar</a><br/> +<br/> +<h1>bar</h1> +<a name="$bar()"></a> +<code><span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +</BODY> +</HTML> +<!-- File: test/foo.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +</BODY> +</HTML> +<!-- File: test/quux.html --> +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>quux - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./quux.html">quux</a><br/> +<br/> +<h1>quux</h1> +<a name="$quux()"></a> +<code><span class="keyword">fun </span><span class="identifier">quux</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p><strong>See Also</strong><br/> +<p><a href="foo.html">foo</a></p> +<p><a href="bar.html">bar</a></p> +</p> +</BODY> +</HTML> diff --git a/core/testdata/format/see.kt b/core/testdata/format/see.kt new file mode 100644 index 000000000..a0b153b02 --- /dev/null +++ b/core/testdata/format/see.kt @@ -0,0 +1,12 @@ +/** + * @see foo + * @see bar + */ +fun quux() { +} + +fun foo() { +} + +fun bar() { +}
\ No newline at end of file diff --git a/core/testdata/format/shadowedExtensionFunctions.kt b/core/testdata/format/shadowedExtensionFunctions.kt new file mode 100644 index 000000000..64df1ecbd --- /dev/null +++ b/core/testdata/format/shadowedExtensionFunctions.kt @@ -0,0 +1,18 @@ +open class Foo { +} + +class Bar : Foo() { +} + +fun Foo.xyzzy() { +} + +fun Foo.shazam() { + +} + +fun Bar.xyzzy() { +} + +fun Bar.shazam(i: Int) { +} diff --git a/core/testdata/format/shadowedExtensionFunctions.md b/core/testdata/format/shadowedExtensionFunctions.md new file mode 100644 index 000000000..f900ecb2b --- /dev/null +++ b/core/testdata/format/shadowedExtensionFunctions.md @@ -0,0 +1,15 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar : `[`Foo`](../-foo/index.md) + +### Constructors + +| [<init>](-init-.md) | `Bar()` | + +### Extension Functions + +| [shazam](../shazam.md) | `fun `[`Bar`](./index.md)`.shazam(i: Int): Unit`<br>`fun `[`Foo`](../-foo/index.md)`.shazam(): Unit` | +| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` | + diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html new file mode 100644 index 000000000..32988de2a --- /dev/null +++ b/core/testdata/format/sinceKotlin.html @@ -0,0 +1,28 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Since1.1 - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Since1.1</a><br/> +<br/> +<h1>Since1.1</h1> +<code><span class="keyword">class </span><span class="identifier">Since1.1</span></code> +<p><strong>Platform and version requirements:</strong> Kotlin 1.1</p> +<p>Useful</p> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code> +<p>Useful</p> +</td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/sinceKotlin.kt b/core/testdata/format/sinceKotlin.kt new file mode 100644 index 000000000..1025cf0d3 --- /dev/null +++ b/core/testdata/format/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Useful + */ +@SinceKotlin("1.1") +class `Since1.1`
\ No newline at end of file diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md new file mode 100644 index 000000000..df96db0af --- /dev/null +++ b/core/testdata/format/sinceKotlin.md @@ -0,0 +1,14 @@ +[test](../index.md) / [Since1.1](./index.md) + +# Since1.1 + +`class Since1.1` + +**Platform and version requirements:** Kotlin 1.1 + +Useful + +### Constructors + +| [<init>](-init-.md) | `Since1.1()`<br>Useful | + diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md new file mode 100644 index 000000000..eabf88d59 --- /dev/null +++ b/core/testdata/format/sinceKotlin.package.md @@ -0,0 +1,10 @@ +[test](./index.md) + +## Package <root> + +**Platform and version requirements:** Kotlin 1.1 + +### Types + +| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful | + diff --git a/core/testdata/format/sinceKotlinWide.kt b/core/testdata/format/sinceKotlinWide.kt new file mode 100644 index 000000000..fa1eb7de8 --- /dev/null +++ b/core/testdata/format/sinceKotlinWide.kt @@ -0,0 +1,11 @@ +/** + * Useful + */ +@SinceKotlin("1.1") +class `Since1.1` + +/** + * Useful also + */ +@SinceKotlin("1.2") +class `Since1.2`
\ No newline at end of file diff --git a/core/testdata/format/sinceKotlinWide.package.md b/core/testdata/format/sinceKotlinWide.package.md new file mode 100644 index 000000000..58a5045ee --- /dev/null +++ b/core/testdata/format/sinceKotlinWide.package.md @@ -0,0 +1,11 @@ +[test](./index.md) + +## Package <root> + +**Platform and version requirements:** Kotlin 1.1+ + +### Types + +| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful | +| [Since1.2](-since1.2/index.md)<br>(Kotlin 1.2) | `class Since1.2`<br>Useful also | + diff --git a/core/testdata/format/starProjection.kt b/core/testdata/format/starProjection.kt new file mode 100644 index 000000000..48d53e47d --- /dev/null +++ b/core/testdata/format/starProjection.kt @@ -0,0 +1,3 @@ +public fun Iterable<*>.containsFoo(element: Any?): Boolean { + return false +} diff --git a/core/testdata/format/starProjection.md b/core/testdata/format/starProjection.md new file mode 100644 index 000000000..5a53e5b9b --- /dev/null +++ b/core/testdata/format/starProjection.md @@ -0,0 +1,6 @@ +[test](../index.md) / [kotlin.collections.Iterable](./index.md) + +### Extensions for kotlin.collections.Iterable + +| [containsFoo](contains-foo.md) | `fun Iterable<*>.containsFoo(element: Any?): Boolean` | + diff --git a/core/testdata/format/summarizeSignatures.kt b/core/testdata/format/summarizeSignatures.kt new file mode 100644 index 000000000..1d875a501 --- /dev/null +++ b/core/testdata/format/summarizeSignatures.kt @@ -0,0 +1,20 @@ +package kotlin + +class Array<T> +class IntArray +class CharArray + +/** + * Returns true if foo. + */ +fun IntArray.foo(predicate: (Int) -> Boolean): Boolean = false + +/** + * Returns true if foo. + */ +fun CharArray.foo(predicate: (Char) -> Boolean): Boolean = false + +/** + * Returns true if foo. + */ +fun <T> Array<T>.foo(predicate: (T) -> Boolean): Boolean = false diff --git a/core/testdata/format/summarizeSignatures.md b/core/testdata/format/summarizeSignatures.md new file mode 100644 index 000000000..4f494166a --- /dev/null +++ b/core/testdata/format/summarizeSignatures.md @@ -0,0 +1,14 @@ +[test](../index.md) / [kotlin](./index.md) + +## Package kotlin + +### Types + +| [Array](-array/index.md) | `class Array<T>` | +| [CharArray](-char-array/index.md) | `class CharArray` | +| [IntArray](-int-array/index.md) | `class IntArray` | + +### Functions + +| [foo](foo.md) | `fun <T> any_array<T>.foo(predicate: (`[`T`](foo.md#T)`) -> Boolean): Boolean`<br>Returns true if foo. | + diff --git a/core/testdata/format/summarizeSignaturesProperty.kt b/core/testdata/format/summarizeSignaturesProperty.kt new file mode 100644 index 000000000..fbbdd3288 --- /dev/null +++ b/core/testdata/format/summarizeSignaturesProperty.kt @@ -0,0 +1,20 @@ +package kotlin + +class Array<T> +class IntArray +class CharArray + +/** + * Returns true if foo. + */ +val IntArray.foo: Int = 0 + +/** + * Returns true if foo. + */ +val CharArray.foo: Int = 0 + +/** + * Returns true if foo. + */ +val <T> Array<T>.foo: Int = 0 diff --git a/core/testdata/format/summarizeSignaturesProperty.md b/core/testdata/format/summarizeSignaturesProperty.md new file mode 100644 index 000000000..507ad6a5a --- /dev/null +++ b/core/testdata/format/summarizeSignaturesProperty.md @@ -0,0 +1,14 @@ +[test](../index.md) / [kotlin](./index.md) + +## Package kotlin + +### Types + +| [Array](-array/index.md) | `class Array<T>` | +| [CharArray](-char-array/index.md) | `class CharArray` | +| [IntArray](-int-array/index.md) | `class IntArray` | + +### Properties + +| [foo](foo.md) | `val <T> any_array<T>.foo: Int`<br>Returns true if foo. | + diff --git a/core/testdata/format/suspendParam.kt b/core/testdata/format/suspendParam.kt new file mode 100644 index 000000000..ea3f56f99 --- /dev/null +++ b/core/testdata/format/suspendParam.kt @@ -0,0 +1,3 @@ +fun takesSuspendParam(func: suspend () -> Unit) { + +}
\ No newline at end of file diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md new file mode 100644 index 000000000..ab116140d --- /dev/null +++ b/core/testdata/format/suspendParam.md @@ -0,0 +1,5 @@ +[test](index.md) / [takesSuspendParam](./takes-suspend-param.md) + +# takesSuspendParam + +`fun takesSuspendParam(func: suspend () -> Unit): Unit`
\ No newline at end of file diff --git a/core/testdata/format/suspendParam.package.md b/core/testdata/format/suspendParam.package.md new file mode 100644 index 000000000..92bd7ee7f --- /dev/null +++ b/core/testdata/format/suspendParam.package.md @@ -0,0 +1,8 @@ +[test](./index.md) + +## Package <root> + +### Functions + +| [takesSuspendParam](takes-suspend-param.md) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` | + diff --git a/core/testdata/format/throwsTag.kt b/core/testdata/format/throwsTag.kt new file mode 100644 index 000000000..29a9c3f7a --- /dev/null +++ b/core/testdata/format/throwsTag.kt @@ -0,0 +1,5 @@ +/** + * @throws IllegalArgumentException on Mondays + * @exception NullPointerException on Tuesdays + */ +fun f() {} diff --git a/core/testdata/format/throwsTag.md b/core/testdata/format/throwsTag.md new file mode 100644 index 000000000..70fba512f --- /dev/null +++ b/core/testdata/format/throwsTag.md @@ -0,0 +1,11 @@ +[test](index.md) / [f](./f.md) + +# f + +`fun f(): Unit` + +### Exceptions + +`IllegalArgumentException` - on Mondays + +`NullPointerException` - on Tuesdays
\ No newline at end of file diff --git a/core/testdata/format/tokensInEmphasis.kt b/core/testdata/format/tokensInEmphasis.kt new file mode 100644 index 000000000..39362b377 --- /dev/null +++ b/core/testdata/format/tokensInEmphasis.kt @@ -0,0 +1,10 @@ +/** + * Another emphasised class. + * + * _This class, [Bar] is just meh._ + * + * _For a semicolon; [Bar.foo] is for you!._ + */ +class Bar { + fun foo() = ";" +} diff --git a/core/testdata/format/tokensInEmphasis.md b/core/testdata/format/tokensInEmphasis.md new file mode 100644 index 000000000..a68861de9 --- /dev/null +++ b/core/testdata/format/tokensInEmphasis.md @@ -0,0 +1,20 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +Another emphasised class. + +*This class, [Bar](./index.md) is just meh.* + +*For a semicolon; [Bar.foo](foo.md) is for you!.* + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>Another emphasised class. | + +### Functions + +| [foo](foo.md) | `fun foo(): String` | + diff --git a/core/testdata/format/tokensInHeaders.kt b/core/testdata/format/tokensInHeaders.kt new file mode 100644 index 000000000..df62b024f --- /dev/null +++ b/core/testdata/format/tokensInHeaders.kt @@ -0,0 +1,27 @@ +/** + * Why did the token cross the road? + * + * # Because it was Beer o'clock @ [The.bar] + * + * ## But __waz *\[sic\]* [it](isitbeeroclock.com)__ really? + * + * ### [The.bar] has? [The.foo]est drinks ever! + * + * #### _[The.kotlinz] is [The.bestests], [Bar.none]_ + * + * ##### So many lame code "puns" (in) [The.house] + * + * ###### End of the?? [Bar.line]! - we need to go back! + */ +class The { + object Bar { + fun none() {} + } + + fun bar() {} + fun foo() {} + fun bestests() {} + fun kotlinz() {} + fun house() {} + fun line() {} +} diff --git a/core/testdata/format/tokensInHeaders.md b/core/testdata/format/tokensInHeaders.md new file mode 100644 index 000000000..bd25492e2 --- /dev/null +++ b/core/testdata/format/tokensInHeaders.md @@ -0,0 +1,37 @@ +[test](../index.md) / [The](./index.md) + +# The + +`class The` + +Why did the token cross the road? + +# Because it was Beer o'clock @ [The.bar](bar.md) + +## But **waz *\[sic\]* [it](isitbeeroclock.com)** really? + +### [The.bar](bar.md) has? [The.foo](foo.md)est drinks ever! + +#### *[The.kotlinz](kotlinz.md) is [The.bestests](bestests.md), [Bar.none](-bar/none.md)* + +##### So many lame code "puns" (in) [The.house](house.md) + +###### End of the?? [Bar.line](#)! - we need to go back! + +### Types + +| [Bar](-bar/index.md) | `object Bar` | + +### Constructors + +| [<init>](-init-.md) | `The()`<br>Why did the token cross the road? | + +### Functions + +| [bar](bar.md) | `fun bar(): Unit` | +| [bestests](bestests.md) | `fun bestests(): Unit` | +| [foo](foo.md) | `fun foo(): Unit` | +| [house](house.md) | `fun house(): Unit` | +| [kotlinz](kotlinz.md) | `fun kotlinz(): Unit` | +| [line](line.md) | `fun line(): Unit` | + diff --git a/core/testdata/format/tokensInStrong.kt b/core/testdata/format/tokensInStrong.kt new file mode 100644 index 000000000..596a9ae8d --- /dev/null +++ b/core/testdata/format/tokensInStrong.kt @@ -0,0 +1,10 @@ +/** + * __YASC: [Yasc] Yet Another Strong Class__ + * + * __This class, [Yasc] *is* just meh.__ + * + * __For a semicolon; [Yasc.foo] is for you!.__ + */ +class Yasc { + fun foo() = ";" +} diff --git a/core/testdata/format/tokensInStrong.md b/core/testdata/format/tokensInStrong.md new file mode 100644 index 000000000..2781656cb --- /dev/null +++ b/core/testdata/format/tokensInStrong.md @@ -0,0 +1,20 @@ +[test](../index.md) / [Yasc](./index.md) + +# Yasc + +`class Yasc` + +**YASC: [Yasc](./index.md) Yet Another Strong Class** + +**This class, [Yasc](./index.md) *is* just meh.** + +**For a semicolon; [Yasc.foo](foo.md) is for you!.** + +### Constructors + +| [<init>](-init-.md) | `Yasc()`<br>**YASC: [Yasc](./index.md) Yet Another Strong Class** | + +### Functions + +| [foo](foo.md) | `fun foo(): String` | + diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html new file mode 100644 index 000000000..dacd0567c --- /dev/null +++ b/core/testdata/format/tripleBackticks.html @@ -0,0 +1,16 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>f - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./f.html">f</a><br/> +<br/> +<h1>f</h1> +<a name="$f()"></a> +<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>Description</p> +<pre><code>code sample +</code></pre> +</BODY> +</HTML> diff --git a/core/testdata/format/tripleBackticks.kt b/core/testdata/format/tripleBackticks.kt new file mode 100644 index 000000000..54dfa6d51 --- /dev/null +++ b/core/testdata/format/tripleBackticks.kt @@ -0,0 +1,7 @@ +/** + * Description + * ``` + * code sample + * ``` + */ +fun f() {} diff --git a/core/testdata/format/typeAliases.kt b/core/testdata/format/typeAliases.kt new file mode 100644 index 000000000..9657963e6 --- /dev/null +++ b/core/testdata/format/typeAliases.kt @@ -0,0 +1,27 @@ + +class A +class B +class C<T> + +typealias D = A +typealias E = D + +typealias F = (A) -> B + +typealias G = C<A> +typealias H<T> = C<T> + +typealias I<T> = H<T> +typealias J = H<A> + +typealias K = H<J> + +typealias L = (K, B) -> J + +/** + * Documented + */ +typealias M = A + +@Deprecated("!!!") +typealias N = A
\ No newline at end of file diff --git a/core/testdata/format/typeAliases.md b/core/testdata/format/typeAliases.md new file mode 100644 index 000000000..218c4848b --- /dev/null +++ b/core/testdata/format/typeAliases.md @@ -0,0 +1,104 @@ +<!-- File: test/-a/index.md --> +[test](../index.md) / [A](./index.md) + +# A + +`class A` + +### Constructors + +| [<init>](-init-.md) | `A()` | + +<!-- File: test/-b/index.md --> +[test](../index.md) / [B](./index.md) + +# B + +`class B` + +### Constructors + +| [<init>](-init-.md) | `B()` | + +<!-- File: test/-c/index.md --> +[test](../index.md) / [C](./index.md) + +# C + +`class C<T>` + +### Constructors + +| [<init>](-init-.md) | `C()` | + +<!-- File: test/-d.md --> +[test](index.md) / [D](./-d.md) + +# D + +`typealias D = `[`A`](-a/index.md) +<!-- File: test/-e.md --> +[test](index.md) / [E](./-e.md) + +# E + +`typealias E = `[`D`](-d.md) +<!-- File: test/-f.md --> +[test](index.md) / [F](./-f.md) + +# F + +`typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md) +<!-- File: test/-g.md --> +[test](index.md) / [G](./-g.md) + +# G + +`typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>` +<!-- File: test/-h.md --> +[test](index.md) / [H](./-h.md) + +# H + +`typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>` +<!-- File: test/-i.md --> +[test](index.md) / [I](./-i.md) + +# I + +`typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>` +<!-- File: test/-j.md --> +[test](index.md) / [J](./-j.md) + +# J + +`typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>` +<!-- File: test/-k.md --> +[test](index.md) / [K](./-k.md) + +# K + +`typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>` +<!-- File: test/-l.md --> +[test](index.md) / [L](./-l.md) + +# L + +`typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md) +<!-- File: test/-m.md --> +[test](index.md) / [M](./-m.md) + +# M + +`typealias M = `[`A`](-a/index.md) + +Documented + +<!-- File: test/-n.md --> +[test](index.md) / [N](./-n.md) + +# N + +`typealias ~~N~~ = `[`A`](-a/index.md) +**Deprecated:** !!! + diff --git a/core/testdata/format/typeAliases.package.md b/core/testdata/format/typeAliases.package.md new file mode 100644 index 000000000..199e91c2c --- /dev/null +++ b/core/testdata/format/typeAliases.package.md @@ -0,0 +1,24 @@ +[test](./index.md) + +## Package <root> + +### Types + +| [A](-a/index.md) | `class A` | +| [B](-b/index.md) | `class B` | +| [C](-c/index.md) | `class C<T>` | + +### Type Aliases + +| [D](-d.md) | `typealias D = `[`A`](-a/index.md) | +| [E](-e.md) | `typealias E = `[`D`](-d.md) | +| [F](-f.md) | `typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md) | +| [G](-g.md) | `typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>` | +| [H](-h.md) | `typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>` | +| [I](-i.md) | `typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>` | +| [J](-j.md) | `typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>` | +| [K](-k.md) | `typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>` | +| [L](-l.md) | `typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md) | +| [M](-m.md) | `typealias M = `[`A`](-a/index.md)<br>Documented | +| [N](-n.md) | `typealias ~~N~~ = `[`A`](-a/index.md) | + diff --git a/core/testdata/format/typeLink.html b/core/testdata/format/typeLink.html new file mode 100644 index 000000000..30af8a930 --- /dev/null +++ b/core/testdata/format/typeLink.html @@ -0,0 +1,24 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>Bar - test</title> +</HEAD> +<BODY> +<a href="../index.html">test</a> / <a href="./index.html">Bar</a><br/> +<br/> +<h1>Bar</h1> +<code><span class="keyword">class </span><span class="identifier">Bar</span> <span class="symbol">:</span> <a href="../-foo/index.html"><span class="identifier">Foo</span></a></code> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<p><a href="-init-.html"><init></a></p> +</td> +<td> +<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/typeLink.kt b/core/testdata/format/typeLink.kt new file mode 100644 index 000000000..966e020e6 --- /dev/null +++ b/core/testdata/format/typeLink.kt @@ -0,0 +1,5 @@ +class Foo() { +} + +class Bar(): Foo { +} diff --git a/core/testdata/format/typeParameterBounds.kt b/core/testdata/format/typeParameterBounds.kt new file mode 100644 index 000000000..8604e3b92 --- /dev/null +++ b/core/testdata/format/typeParameterBounds.kt @@ -0,0 +1,7 @@ + +/** + * generic function + * @param T the first type parameter + */ +public fun <T : R, R> generic() { +}
\ No newline at end of file diff --git a/core/testdata/format/typeParameterBounds.md b/core/testdata/format/typeParameterBounds.md new file mode 100644 index 000000000..cf03b3a76 --- /dev/null +++ b/core/testdata/format/typeParameterBounds.md @@ -0,0 +1,11 @@ +[test](index.md) / [generic](./generic.md) + +# generic + +`fun <T : `[`R`](generic.md#R)`, R> generic(): Unit` + +generic function + +### Parameters + +`T` - the first type parameter
\ No newline at end of file diff --git a/core/testdata/format/typeParameterReference.kt b/core/testdata/format/typeParameterReference.kt new file mode 100644 index 000000000..f196112d3 --- /dev/null +++ b/core/testdata/format/typeParameterReference.kt @@ -0,0 +1,6 @@ +/** + * Correct ref to [T] + */ +fun <T> T.tt() { + println("T.tt") +}
\ No newline at end of file diff --git a/core/testdata/format/typeParameterReference.md b/core/testdata/format/typeParameterReference.md new file mode 100644 index 000000000..5001d321b --- /dev/null +++ b/core/testdata/format/typeParameterReference.md @@ -0,0 +1,8 @@ +[test](index.md) / [tt](./tt.md) + +# tt + +`fun <T> `[`T`](tt.md#T)`.tt(): Unit` + +Correct ref to [T](tt.md#T) + diff --git a/core/testdata/format/typeParameterVariance.kt b/core/testdata/format/typeParameterVariance.kt new file mode 100644 index 000000000..d45e7eb37 --- /dev/null +++ b/core/testdata/format/typeParameterVariance.kt @@ -0,0 +1,5 @@ +/** + * @param T the class parameter type + */ +class Foo<out T> { +} diff --git a/core/testdata/format/typeParameterVariance.md b/core/testdata/format/typeParameterVariance.md new file mode 100644 index 000000000..b0615d43b --- /dev/null +++ b/core/testdata/format/typeParameterVariance.md @@ -0,0 +1,14 @@ +[test](../index.md) / [Foo](./index.md) + +# Foo + +`class Foo<out T>` + +### Parameters + +`T` - the class parameter type + +### Constructors + +| [<init>](-init-.md) | `Foo()` | + diff --git a/core/testdata/format/typeProjectionVariance.kt b/core/testdata/format/typeProjectionVariance.kt new file mode 100644 index 000000000..85ee344d0 --- /dev/null +++ b/core/testdata/format/typeProjectionVariance.kt @@ -0,0 +1 @@ +fun <T> Array<out T>.foo() {} diff --git a/core/testdata/format/typeProjectionVariance.md b/core/testdata/format/typeProjectionVariance.md new file mode 100644 index 000000000..d3a55c58b --- /dev/null +++ b/core/testdata/format/typeProjectionVariance.md @@ -0,0 +1,6 @@ +[test](../index.md) / [kotlin.Array](./index.md) + +### Extensions for kotlin.Array + +| [foo](foo.md) | `fun <T> Array<out `[`T`](foo.md#T)`>.foo(): Unit` | + diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.html b/core/testdata/format/uninterpretedEmphasisCharacters.html new file mode 100644 index 000000000..dd338f72a --- /dev/null +++ b/core/testdata/format/uninterpretedEmphasisCharacters.html @@ -0,0 +1,15 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>foo - test</title> +</HEAD> +<BODY> +<a href="index.html">test</a> / <a href="./foo.html">foo</a><br/> +<br/> +<h1>foo</h1> +<a name="$foo()"></a> +<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> +<p>This is <em>emphasized text</em> but text_with_underscores has to preserve the underscores. +Single stars embedded in a word like Embedded*Star have to be preserved as well.</p> +</BODY> +</HTML> diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.kt b/core/testdata/format/uninterpretedEmphasisCharacters.kt new file mode 100644 index 000000000..711bb5674 --- /dev/null +++ b/core/testdata/format/uninterpretedEmphasisCharacters.kt @@ -0,0 +1,5 @@ +/** + * This is _emphasized text_ but text_with_underscores has to preserve the underscores. + * Single stars embedded in a word like Embedded*Star have to be preserved as well. + */ +fun foo() {}
\ No newline at end of file diff --git a/core/testdata/format/unorderedLists.kt b/core/testdata/format/unorderedLists.kt new file mode 100644 index 000000000..a594b89b6 --- /dev/null +++ b/core/testdata/format/unorderedLists.kt @@ -0,0 +1,36 @@ +/** + * Usage summary: + * + * - Rinse + * - Repeat + * + * Usage instructions: + * + * - [Bar.rinse] to rinse + * - Alter any rinse options _(optional)_ + * - To repeat; [Bar.repeat] + * - Can reconfigure options: + * - Soap + * - Elbow Grease + * - Bleach + * + * Rinse options: + * + * - [Bar.useSoap] + * - _recommended_ + * + * - [Bar.useElbowGrease] + * - _warning: requires effort_ + * + * - [Bar.useBleach] + * - __use with caution__ + * + */ +class Bar { + fun rinse() = Unit + fun repeat() = Unit + + var useSoap = false + var useElbowGrease = false + var useBleach = false +} diff --git a/core/testdata/format/unorderedLists.md b/core/testdata/format/unorderedLists.md new file mode 100644 index 000000000..52ad9a71c --- /dev/null +++ b/core/testdata/format/unorderedLists.md @@ -0,0 +1,47 @@ +[test](../index.md) / [Bar](./index.md) + +# Bar + +`class Bar` + +Usage summary: + +* Rinse +* Repeat + +Usage instructions: + +* [Bar.rinse](rinse.md) to rinse +* Alter any rinse options *(optional)* +* To repeat; [Bar.repeat](repeat.md) + * Can reconfigure options: + * Soap + * Elbow Grease + * Bleach + +Rinse options: + +* [Bar.useSoap](use-soap.md) + * *recommended* + +* [Bar.useElbowGrease](use-elbow-grease.md) + * *warning: requires effort* + +* [Bar.useBleach](use-bleach.md) + * **use with caution** + +### Constructors + +| [<init>](-init-.md) | `Bar()`<br>Usage summary: | + +### Properties + +| [useBleach](use-bleach.md) | `var useBleach: Boolean` | +| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` | +| [useSoap](use-soap.md) | `var useSoap: Boolean` | + +### Functions + +| [repeat](repeat.md) | `fun repeat(): Unit` | +| [rinse](rinse.md) | `fun rinse(): Unit` | + diff --git a/core/testdata/format/varargsFunction.kt b/core/testdata/format/varargsFunction.kt new file mode 100644 index 000000000..deea127b1 --- /dev/null +++ b/core/testdata/format/varargsFunction.kt @@ -0,0 +1 @@ +fun f(vararg s: String) {} diff --git a/core/testdata/format/varargsFunction.md b/core/testdata/format/varargsFunction.md new file mode 100644 index 000000000..550202cce --- /dev/null +++ b/core/testdata/format/varargsFunction.md @@ -0,0 +1,5 @@ +[test](index.md) / [f](./f.md) + +# f + +`fun f(vararg s: String): Unit`
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTags/jre7.kt b/core/testdata/format/website-html/dataTags/jre7.kt new file mode 100644 index 000000000..d21b8d7b4 --- /dev/null +++ b/core/testdata/format/website-html/dataTags/jre7.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jre7New() {} + +fun jre7() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTags/js.kt b/core/testdata/format/website-html/dataTags/js.kt new file mode 100644 index 000000000..b22d70886 --- /dev/null +++ b/core/testdata/format/website-html/dataTags/js.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jsNew() {} + +fun js() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTags/jvm.kt b/core/testdata/format/website-html/dataTags/jvm.kt new file mode 100644 index 000000000..02d042261 --- /dev/null +++ b/core/testdata/format/website-html/dataTags/jvm.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jvmNew() {} + +fun jvm() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTags/multiplatform.package.html b/core/testdata/format/website-html/dataTags/multiplatform.package.html new file mode 100644 index 000000000..35453ab10 --- /dev/null +++ b/core/testdata/format/website-html/dataTags/multiplatform.package.html @@ -0,0 +1,71 @@ +<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">foo</a></div> +<h2>Package foo</h2> +<h3>Functions</h3> +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM" data-jre-version="JRE7"><td> +<p><a href="jre7.html">jre7</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td> +<p><a href="jre7-new.html">jre7New</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS"><td> +<p><a href="js.html">js</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td> +<p><a href="js-new.html">jsNew</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td> +<p><a href="jvm.html">jvm</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td> +<p><a href="jvm-new.html">jvmNew</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td> +<p><a href="shared.html">shared</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td> +<p><a href="shared-new.html">sharedNew</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr></tbody> +</table> diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/js.kt b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt new file mode 100644 index 000000000..045f3f0d6 --- /dev/null +++ b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt @@ -0,0 +1,8 @@ +package pack + +class Some { + + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt new file mode 100644 index 000000000..57f36742e --- /dev/null +++ b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html new file mode 100644 index 000000000..3d34fc7e9 --- /dev/null +++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html @@ -0,0 +1,38 @@ +<div class='api-docs-breadcrumbs'><a href="../../index.html">test</a> / <a href="../index.html">pack</a> / <a href="./index.html">Some</a></div> +<h1>Some</h1> +<div class="overload-group" data-platform="JVM"><div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> +<p><strong>Platform and version requirements:</strong> JVM</p> +</div> +<div class="overload-group" data-platform="JS"><div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> +<p><strong>Platform and version requirements:</strong> JS</p> +<h3>Constructors</h3> +<table class="api-docs-table"> +<tbody> +<tr> +<td> +<p><a href="-some/-init-.html"><init></a></p> + +</td> +<td> +<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div> + +</td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table class="api-docs-table"> +<tbody> +<tr> +<td> +<p><a href="-some/magic.html">magic</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr> +</tbody> +</table> +</div> diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html new file mode 100644 index 000000000..c8926a28e --- /dev/null +++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html @@ -0,0 +1,36 @@ +<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">pack</a></div> +<h2>Package pack</h2> +<h3>Types</h3> +<table class="api-docs-table"> +<tbody> +<tr data-platform="JS"><td> +<p><a href="-some/index.html">Some</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td> +<p><a href="-some-cool-jvm-class/index.html">SomeCoolJvmClass</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> +<h3>Type Aliases</h3> +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM"><td> +<p><a href="-some/index.html">Some</a></p> + +</td> +<td> +<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> diff --git a/core/testdata/format/website-html/dropImport.html b/core/testdata/format/website-html/dropImport.html new file mode 100644 index 000000000..e0fcb12bb --- /dev/null +++ b/core/testdata/format/website-html/dropImport.html @@ -0,0 +1,11 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div> +<h1>foo</h1> +<a name="$foo()"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample"><pre><code class="lang-kotlin">import some.* + +fun main(args: Array<String>) { +//sampleStart + +//sampleEnd +}</code></pre></div> diff --git a/core/testdata/format/website-html/dropImport.kt b/core/testdata/format/website-html/dropImport.kt new file mode 100644 index 000000000..7b8d9f4e8 --- /dev/null +++ b/core/testdata/format/website-html/dropImport.kt @@ -0,0 +1,12 @@ +import samples.* +import some.* + +/** + * @sample example1 + */ +fun foo() { +} + +fun example1() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-html/newLinesInImportList.html b/core/testdata/format/website-html/newLinesInImportList.html new file mode 100644 index 000000000..b5a073252 --- /dev/null +++ b/core/testdata/format/website-html/newLinesInImportList.html @@ -0,0 +1,12 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div> +<h1>foo</h1> +<a name="$foo()"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample"><pre><code class="lang-kotlin">import same.* +import some.* + +fun main(args: Array<String>) { +//sampleStart + +//sampleEnd +}</code></pre></div> diff --git a/core/testdata/format/website-html/newLinesInImportList.kt b/core/testdata/format/website-html/newLinesInImportList.kt new file mode 100644 index 000000000..836d9f6f0 --- /dev/null +++ b/core/testdata/format/website-html/newLinesInImportList.kt @@ -0,0 +1,12 @@ +import same.* +import some.* + +/** + * @sample example1 + */ +fun foo() { +} + +fun example1() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-html/newLinesInSamples.html b/core/testdata/format/website-html/newLinesInSamples.html new file mode 100644 index 000000000..50f875da1 --- /dev/null +++ b/core/testdata/format/website-html/newLinesInSamples.html @@ -0,0 +1,19 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div> +<h1>foo</h1> +<a name="$foo()"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample"><pre><code class="lang-kotlin"> + +fun main(args: Array<String>) { +//sampleStart +val words = listOf("a", "abc", "ab", "def", "abcd") +val byLength = words.groupBy { it.length } + +println(byLength.keys) // [1, 3, 2, 4] +println(byLength.values) // [[a], [abc, def], [ab], [abcd]] + +val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } +// same content as in byLength map, but the map is mutable +println("mutableByLength == byLength is ${mutableByLength == byLength}") // true +//sampleEnd +}</code></pre></div> diff --git a/core/testdata/format/website-html/newLinesInSamples.kt b/core/testdata/format/website-html/newLinesInSamples.kt new file mode 100644 index 000000000..ee49aefc7 --- /dev/null +++ b/core/testdata/format/website-html/newLinesInSamples.kt @@ -0,0 +1,19 @@ +fun groupBySample() { + val words = listOf("a", "abc", "ab", "def", "abcd") + val byLength = words.groupBy { it.length } + + assertPrints(byLength.keys, "[1, 3, 2, 4]") + assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]") + + val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } + // same content as in byLength map, but the map is mutable + assertTrue(mutableByLength == byLength) +} + + +/** + * @sample groupBySample + */ +fun foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-html/overloadGroup.html b/core/testdata/format/website-html/overloadGroup.html new file mode 100644 index 000000000..aaba9c96f --- /dev/null +++ b/core/testdata/format/website-html/overloadGroup.html @@ -0,0 +1,16 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./magic.html">magic</a></div> +<h1>magic</h1> +<div class="overload-group"><a name="$magic(kotlin.String)"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> +<h3>Parameters</h3> +<p><a name="spell"></a> +<code>spell</code> - The text of spell, often distributed on scrolls</p> +<p><strong>Return</strong> Spell ID for future casts</p> +</div> +<div class="overload-group"><a name="$magic(kotlin.Int)"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> +<h3>Parameters</h3> +<p><a name="spell"></a> +<code>spell</code> - Spell ID of previously casted spell</p> +<p><strong>Return</strong> Spell ID for future casts</p> +</div> diff --git a/core/testdata/format/website-html/overloadGroup.kt b/core/testdata/format/website-html/overloadGroup.kt new file mode 100644 index 000000000..5bc98e3d4 --- /dev/null +++ b/core/testdata/format/website-html/overloadGroup.kt @@ -0,0 +1,15 @@ +/** + * @param spell The text of spell, often distributed on scrolls + * @return Spell ID for future casts + */ +fun magic(spell: String): Int { + +} + +/** + * @param spell Spell ID of previously casted spell + * @return Spell ID for future casts + */ +fun magic(spell: Int): Int { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-html/returnTag.html b/core/testdata/format/website-html/returnTag.html new file mode 100644 index 000000000..b68785032 --- /dev/null +++ b/core/testdata/format/website-html/returnTag.html @@ -0,0 +1,9 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./index-of.html">indexOf</a></div> +<h1>indexOf</h1> +<a name="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)"></a> +<div class="signature"><code><span class="keyword">fun </span><a href="-foo/index.html"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span> <span class="identifier">Char</span><span class="symbol">, </span> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span> <span class="identifier">Int</span> <span class="symbol">=</span> 0<span class="symbol">, </span> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span> <span class="identifier">Boolean</span> <span class="symbol">=</span> false<span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> +<p>Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.</p> +<h3>Parameters</h3> +<p><a name="ignoreCase"></a> +<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.</p> +<p><strong>Returns</strong> An index of the first occurrence of <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.</p> diff --git a/core/testdata/format/website-html/returnTag.kt b/core/testdata/format/website-html/returnTag.kt new file mode 100644 index 000000000..669c14f90 --- /dev/null +++ b/core/testdata/format/website-html/returnTag.kt @@ -0,0 +1,11 @@ +class Foo + +/** + * Returns the index within this string of the first occurrence of the specified character, starting from the specified [startIndex]. + * + * @param ignoreCase `true` to ignore character case when matching a character. By default `false`. + * @returns An index of the first occurrence of [char] or -1 if none is found. + */ +fun Foo.indexOf(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int { + return -1 +} diff --git a/core/testdata/format/website-html/sample.html b/core/testdata/format/website-html/sample.html new file mode 100644 index 000000000..1fb26e41b --- /dev/null +++ b/core/testdata/format/website-html/sample.html @@ -0,0 +1,19 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div> +<h1>foo</h1> +<div class="overload-group"><a name="$foo()"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> +<p>Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function +applied to each element and returns a map where each group key is associated with a list of corresponding elements.</p> +<div class="sample"><pre><code class="lang-kotlin"> + +fun main(args: Array<String>) { +//sampleStart +if (true) { + println(property) +} +//sampleEnd +}</code></pre></div> +</div> +<div class="overload-group"><a name="$foo(kotlin.Int)"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> +</div> diff --git a/core/testdata/format/website-html/sample.kt b/core/testdata/format/website-html/sample.kt new file mode 100644 index 000000000..a664c2f52 --- /dev/null +++ b/core/testdata/format/website-html/sample.kt @@ -0,0 +1,16 @@ +/** + * Groups elements of the original sequence by the key returned by the given [keySelector] function + * applied to each element and returns a map where each group key is associated with a list of corresponding elements. + * @sample example1 + */ +fun foo(): Int { + return 0 +} + +fun foo(i: Int): Int { + return 1 +} + +fun example1(node: String) = if (true) { + println(property) +} diff --git a/core/testdata/format/website-html/sampleWithAsserts.html b/core/testdata/format/website-html/sampleWithAsserts.html new file mode 100644 index 000000000..e91232f5b --- /dev/null +++ b/core/testdata/format/website-html/sampleWithAsserts.html @@ -0,0 +1,22 @@ +<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./a.html">a</a></div> +<h1>a</h1> +<a name="$a()"></a> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div> +<div class="sample"><pre><code class="lang-kotlin">import java.io.FileNotFoundException +import java.io.File + +fun main(args: Array<String>) { +//sampleStart +println(a()) // Hello, Work +println("a() == b() is ${a() == b()}") // true +// A eq B +println("a() == b() is ${a() == b()}") // true +// readSomeFile(File("some.txt")) // reading file now will fail +// readSomeFile(File("some.txt")) // will fail with FileNotFoundException + +fun indented() { + // A neq B + println("a() != b() is ${a() != b()}") // false +} +//sampleEnd +}</code></pre></div> diff --git a/core/testdata/format/website-html/sampleWithAsserts.kt b/core/testdata/format/website-html/sampleWithAsserts.kt new file mode 100644 index 000000000..b3bce11d8 --- /dev/null +++ b/core/testdata/format/website-html/sampleWithAsserts.kt @@ -0,0 +1,32 @@ +import java.io.FileNotFoundException +import java.io.File + +/** + * @sample sample + */ +fun a(): String { + return "Hello, Work" +} + +fun b(): String { + return "Hello, Rest" +} + +/** + * @throws FileNotFoundException every time + */ +fun readSomeFile(f: File) { + throw FileNotFoundException("BOOM") +} + +fun sample() { + assertPrints(a(), "Hello, Work") + assertTrue(a() == b()) + assertTrue(a() == b(), "A eq B") + assertFails("reading file now") { readSomeFile(File("some.txt")) } + assertFailsWith<FileNotFoundException> { readSomeFile(File("some.txt")) } + + fun indented() { + assertFalse(a() != b(), "A neq B") + } +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/dropImport.kt b/core/testdata/format/website-samples/dropImport.kt new file mode 100644 index 000000000..7b8d9f4e8 --- /dev/null +++ b/core/testdata/format/website-samples/dropImport.kt @@ -0,0 +1,12 @@ +import samples.* +import some.* + +/** + * @sample example1 + */ +fun foo() { +} + +fun example1() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/dropImport.md b/core/testdata/format/website-samples/dropImport.md new file mode 100644 index 000000000..1e05678b8 --- /dev/null +++ b/core/testdata/format/website-samples/dropImport.md @@ -0,0 +1,23 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample" markdown="1"> + +``` kotlin +import some.* + +fun main(args: Array<String>) { +//sampleStart + +//sampleEnd +} +``` + +</div> diff --git a/core/testdata/format/website-samples/newLinesInImportList.kt b/core/testdata/format/website-samples/newLinesInImportList.kt new file mode 100644 index 000000000..836d9f6f0 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInImportList.kt @@ -0,0 +1,12 @@ +import same.* +import some.* + +/** + * @sample example1 + */ +fun foo() { +} + +fun example1() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/newLinesInImportList.md b/core/testdata/format/website-samples/newLinesInImportList.md new file mode 100644 index 000000000..27d796f80 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInImportList.md @@ -0,0 +1,24 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample" markdown="1"> + +``` kotlin +import same.* +import some.* + +fun main(args: Array<String>) { +//sampleStart + +//sampleEnd +} +``` + +</div> diff --git a/core/testdata/format/website-samples/newLinesInSamples.kt b/core/testdata/format/website-samples/newLinesInSamples.kt new file mode 100644 index 000000000..ee49aefc7 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInSamples.kt @@ -0,0 +1,19 @@ +fun groupBySample() { + val words = listOf("a", "abc", "ab", "def", "abcd") + val byLength = words.groupBy { it.length } + + assertPrints(byLength.keys, "[1, 3, 2, 4]") + assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]") + + val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } + // same content as in byLength map, but the map is mutable + assertTrue(mutableByLength == byLength) +} + + +/** + * @sample groupBySample + */ +fun foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/newLinesInSamples.md b/core/testdata/format/website-samples/newLinesInSamples.md new file mode 100644 index 000000000..5344b9838 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInSamples.md @@ -0,0 +1,31 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample" markdown="1"> + +``` kotlin + + +fun main(args: Array<String>) { +//sampleStart +val words = listOf("a", "abc", "ab", "def", "abcd") +val byLength = words.groupBy { it.length } + +println(byLength.keys) // [1, 3, 2, 4] +println(byLength.values) // [[a], [abc, def], [ab], [abcd]] + +val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } +// same content as in byLength map, but the map is mutable +println("mutableByLength == byLength is ${mutableByLength == byLength}") // true +//sampleEnd +} +``` + +</div> diff --git a/core/testdata/format/website-samples/sample.kt b/core/testdata/format/website-samples/sample.kt new file mode 100644 index 000000000..a664c2f52 --- /dev/null +++ b/core/testdata/format/website-samples/sample.kt @@ -0,0 +1,16 @@ +/** + * Groups elements of the original sequence by the key returned by the given [keySelector] function + * applied to each element and returns a map where each group key is associated with a list of corresponding elements. + * @sample example1 + */ +fun foo(): Int { + return 0 +} + +fun foo(i: Int): Int { + return 1 +} + +fun example1(node: String) = if (true) { + println(property) +} diff --git a/core/testdata/format/website-samples/sample.md b/core/testdata/format/website-samples/sample.md new file mode 100644 index 000000000..b29075a78 --- /dev/null +++ b/core/testdata/format/website-samples/sample.md @@ -0,0 +1,39 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function +applied to each element and returns a map where each group key is associated with a list of corresponding elements. + +<div class="sample" markdown="1"> + +``` kotlin + + +fun main(args: Array<String>) { +//sampleStart +if (true) { + println(property) +} +//sampleEnd +} +``` + +</div> + +</div> + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +</div> diff --git a/core/testdata/format/website-samples/sampleWithAsserts.kt b/core/testdata/format/website-samples/sampleWithAsserts.kt new file mode 100644 index 000000000..bb9732d5c --- /dev/null +++ b/core/testdata/format/website-samples/sampleWithAsserts.kt @@ -0,0 +1,15 @@ +/** + * @sample sample + */ +fun a(): String { + return "Hello, Work" +} + +fun b(): String { + return "Hello, Rest" +} + +fun sample() { + assertPrints(a(), "Hello, Work") + assertTrue(a() == b()) +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/sampleWithAsserts.md b/core/testdata/format/website-samples/sampleWithAsserts.md new file mode 100644 index 000000000..c114468a4 --- /dev/null +++ b/core/testdata/format/website-samples/sampleWithAsserts.md @@ -0,0 +1,24 @@ +--- +title: a - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/a">a</a></div> + +# a + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div> +<div class="sample" markdown="1"> + +``` kotlin + + +fun main(args: Array<String>) { +//sampleStart +println(a()) // Hello, Work +println("a() == b() is ${a() == b()}") // true +//sampleEnd +} +``` + +</div> diff --git a/core/testdata/format/website/dataTags/jre7.kt b/core/testdata/format/website/dataTags/jre7.kt new file mode 100644 index 000000000..d21b8d7b4 --- /dev/null +++ b/core/testdata/format/website/dataTags/jre7.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jre7New() {} + +fun jre7() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/js.kt b/core/testdata/format/website/dataTags/js.kt new file mode 100644 index 000000000..b22d70886 --- /dev/null +++ b/core/testdata/format/website/dataTags/js.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jsNew() {} + +fun js() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/jvm.kt b/core/testdata/format/website/dataTags/jvm.kt new file mode 100644 index 000000000..02d042261 --- /dev/null +++ b/core/testdata/format/website/dataTags/jvm.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jvmNew() {} + +fun jvm() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/multiplatform.package.md b/core/testdata/format/website/dataTags/multiplatform.package.md new file mode 100644 index 000000000..1c7fbf669 --- /dev/null +++ b/core/testdata/format/website/dataTags/multiplatform.package.md @@ -0,0 +1,71 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">foo</a></div> + +## Package foo + +### Functions + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/jre7">jre7</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/jre7-new">jre7New</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS"><td markdown="1"> +<a href="test/foo/js">js</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td markdown="1"> +<a href="test/foo/js-new">jsNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td markdown="1"> +<a href="test/foo/jvm">jvm</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td markdown="1"> +<a href="test/foo/jvm-new">jvmNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/shared">shared</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/shared-new">sharedNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr></tbody> +</table> diff --git a/core/testdata/format/website/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt diff --git a/core/testdata/format/website/dataTagsInGroupNode/js.kt b/core/testdata/format/website/dataTagsInGroupNode/js.kt new file mode 100644 index 000000000..045f3f0d6 --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/js.kt @@ -0,0 +1,8 @@ +package pack + +class Some { + + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt new file mode 100644 index 000000000..57f36742e --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md new file mode 100644 index 000000000..78f6adf22 --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md @@ -0,0 +1,56 @@ +--- +title: pack.Some - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a> / <a href="test/pack/-some/index">Some</a></div> + +# Some + +<div class="overload-group" data-platform="JVM" markdown="1"> + +<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> + +**Platform and version requirements:** JVM + +</div> + +<div class="overload-group" data-platform="JS" markdown="1"> + +<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> + +**Platform and version requirements:** JS + +### Constructors + +<table class="api-docs-table"> +<tbody> +<tr> +<td markdown="1"> +<a href="test/pack/-some/-some/-init-"><init></a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div> + +</td> +</tr> +</tbody> +</table> + +### Functions + +<table class="api-docs-table"> +<tbody> +<tr> +<td markdown="1"> +<a href="test/pack/-some/-some/magic">magic</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr> +</tbody> +</table> + +</div> diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md new file mode 100644 index 000000000..a6e7d63bb --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md @@ -0,0 +1,43 @@ +--- +title: pack - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div> + +## Package pack + +### Types + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JS"><td markdown="1"> +<a href="test/pack/-some/index">Some</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td markdown="1"> +<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> + +### Type Aliases + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM"><td markdown="1"> +<a href="test/pack/-some/index">Some</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> diff --git a/core/testdata/format/website/overloadGroup.kt b/core/testdata/format/website/overloadGroup.kt new file mode 100644 index 000000000..5bc98e3d4 --- /dev/null +++ b/core/testdata/format/website/overloadGroup.kt @@ -0,0 +1,15 @@ +/** + * @param spell The text of spell, often distributed on scrolls + * @return Spell ID for future casts + */ +fun magic(spell: String): Int { + +} + +/** + * @param spell Spell ID of previously casted spell + * @return Spell ID for future casts + */ +fun magic(spell: Int): Int { + +}
\ No newline at end of file diff --git a/core/testdata/format/website/overloadGroup.md b/core/testdata/format/website/overloadGroup.md new file mode 100644 index 000000000..d81f86bfc --- /dev/null +++ b/core/testdata/format/website/overloadGroup.md @@ -0,0 +1,34 @@ +--- +title: magic - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/magic">magic</a></div> + +# magic + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span> <span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +### Parameters + +<code>spell</code> - The text of spell, often distributed on scrolls + +**Return** +Spell ID for future casts + +</div> + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +### Parameters + +<code>spell</code> - Spell ID of previously casted spell + +**Return** +Spell ID for future casts + +</div> diff --git a/core/testdata/format/website/returnTag.kt b/core/testdata/format/website/returnTag.kt new file mode 100644 index 000000000..669c14f90 --- /dev/null +++ b/core/testdata/format/website/returnTag.kt @@ -0,0 +1,11 @@ +class Foo + +/** + * Returns the index within this string of the first occurrence of the specified character, starting from the specified [startIndex]. + * + * @param ignoreCase `true` to ignore character case when matching a character. By default `false`. + * @returns An index of the first occurrence of [char] or -1 if none is found. + */ +fun Foo.indexOf(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int { + return -1 +} diff --git a/core/testdata/format/website/returnTag.md b/core/testdata/format/website/returnTag.md new file mode 100644 index 000000000..418babadb --- /dev/null +++ b/core/testdata/format/website/returnTag.md @@ -0,0 +1,20 @@ +--- +title: indexOf - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/index-of">indexOf</a></div> + +# indexOf + +<div class="signature"><code><span class="keyword">fun </span><a href="test/-foo/index"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span><br/> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span> <span class="identifier">Char</span><span class="symbol">, </span><br/> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span> <span class="identifier">Int</span> <span class="symbol">=</span> 0<span class="symbol">, </span><br/> <span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span> <span class="identifier">Boolean</span> <span class="symbol">=</span> false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>. + +### Parameters + +<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>. + +**Returns** +An index of the first occurrence of <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found. + diff --git a/core/testdata/format/website/sample.kt b/core/testdata/format/website/sample.kt new file mode 100644 index 000000000..a664c2f52 --- /dev/null +++ b/core/testdata/format/website/sample.kt @@ -0,0 +1,16 @@ +/** + * Groups elements of the original sequence by the key returned by the given [keySelector] function + * applied to each element and returns a map where each group key is associated with a list of corresponding elements. + * @sample example1 + */ +fun foo(): Int { + return 0 +} + +fun foo(i: Int): Int { + return 1 +} + +fun example1(node: String) = if (true) { + println(property) +} diff --git a/core/testdata/format/website/sample.md b/core/testdata/format/website/sample.md new file mode 100644 index 000000000..7c11790f2 --- /dev/null +++ b/core/testdata/format/website/sample.md @@ -0,0 +1,29 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function +applied to each element and returns a map where each group key is associated with a list of corresponding elements. + +``` kotlin +if (true) { + println(property) +} +``` + +</div> + +<div class="overload-group" markdown="1"> + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span> <span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div> + +</div> diff --git a/core/testdata/functions/annotatedFunction.kt b/core/testdata/functions/annotatedFunction.kt new file mode 100644 index 000000000..f7abbf6c3 --- /dev/null +++ b/core/testdata/functions/annotatedFunction.kt @@ -0,0 +1,2 @@ +@Strictfp fun f() { +} diff --git a/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt b/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt new file mode 100644 index 000000000..e559713ab --- /dev/null +++ b/core/testdata/functions/annotatedFunctionWithAnnotationParameters.kt @@ -0,0 +1,7 @@ +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +public annotation class Fancy(val size: Int) + + +@Fancy(1) fun f() {} diff --git a/core/testdata/functions/function.kt b/core/testdata/functions/function.kt new file mode 100644 index 000000000..3ed81dfa5 --- /dev/null +++ b/core/testdata/functions/function.kt @@ -0,0 +1,5 @@ +/** + * Function fn + */ +fun fn() { +}
\ No newline at end of file diff --git a/core/testdata/functions/functionWithAnnotatedLambdaParam.kt b/core/testdata/functions/functionWithAnnotatedLambdaParam.kt new file mode 100644 index 000000000..4136e03b3 --- /dev/null +++ b/core/testdata/functions/functionWithAnnotatedLambdaParam.kt @@ -0,0 +1,7 @@ +@Target(AnnotationTarget.TYPE) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +public annotation class Fancy + +fun function(notInlined: @Fancy () -> Unit) { +} diff --git a/core/testdata/functions/functionWithAnnotatedParam.kt b/core/testdata/functions/functionWithAnnotatedParam.kt new file mode 100644 index 000000000..f858e671f --- /dev/null +++ b/core/testdata/functions/functionWithAnnotatedParam.kt @@ -0,0 +1,7 @@ +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +public annotation class Fancy + +fun function(@Fancy notInlined: () -> Unit) { +} diff --git a/core/testdata/functions/functionWithDefaultParameter.kt b/core/testdata/functions/functionWithDefaultParameter.kt new file mode 100644 index 000000000..3a3a102fb --- /dev/null +++ b/core/testdata/functions/functionWithDefaultParameter.kt @@ -0,0 +1 @@ +fun f(x: String = "") {} diff --git a/core/testdata/functions/functionWithNoinlineParam.kt b/core/testdata/functions/functionWithNoinlineParam.kt new file mode 100644 index 000000000..640bec837 --- /dev/null +++ b/core/testdata/functions/functionWithNoinlineParam.kt @@ -0,0 +1,2 @@ +fun function(noinline notInlined: () -> Unit) { +} diff --git a/core/testdata/functions/functionWithNotDocumentedAnnotation.kt b/core/testdata/functions/functionWithNotDocumentedAnnotation.kt new file mode 100644 index 000000000..3c7e2ff9b --- /dev/null +++ b/core/testdata/functions/functionWithNotDocumentedAnnotation.kt @@ -0,0 +1,2 @@ +@Suppress("FOO") fun f() { +} diff --git a/core/testdata/functions/functionWithParams.kt b/core/testdata/functions/functionWithParams.kt new file mode 100644 index 000000000..85c493689 --- /dev/null +++ b/core/testdata/functions/functionWithParams.kt @@ -0,0 +1,8 @@ +/** + * Multiline + * + * Function + * Documentation + */ +fun function(/** parameter */ x: Int) { +}
\ No newline at end of file diff --git a/core/testdata/functions/functionWithReceiver.kt b/core/testdata/functions/functionWithReceiver.kt new file mode 100644 index 000000000..c84732515 --- /dev/null +++ b/core/testdata/functions/functionWithReceiver.kt @@ -0,0 +1,11 @@ +/** + * Function with receiver + */ +fun String.fn() { +} + +/** + * Function with receiver + */ +fun String.fn(x: Int) { +} diff --git a/core/testdata/functions/genericFunction.kt b/core/testdata/functions/genericFunction.kt new file mode 100644 index 000000000..05a650707 --- /dev/null +++ b/core/testdata/functions/genericFunction.kt @@ -0,0 +1,5 @@ + +/** + * generic function + */ +private fun <T> generic() {}
\ No newline at end of file diff --git a/core/testdata/functions/genericFunctionWithConstraints.kt b/core/testdata/functions/genericFunctionWithConstraints.kt new file mode 100644 index 000000000..5f22f8c53 --- /dev/null +++ b/core/testdata/functions/genericFunctionWithConstraints.kt @@ -0,0 +1,6 @@ + +/** + * generic function + */ +public fun <T : R, R> generic() { +}
\ No newline at end of file diff --git a/core/testdata/functions/inlineFunction.kt b/core/testdata/functions/inlineFunction.kt new file mode 100644 index 000000000..11c196721 --- /dev/null +++ b/core/testdata/functions/inlineFunction.kt @@ -0,0 +1,2 @@ +inline fun f() { +} diff --git a/core/testdata/functions/sinceKotlin.kt b/core/testdata/functions/sinceKotlin.kt new file mode 100644 index 000000000..cdcd33570 --- /dev/null +++ b/core/testdata/functions/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Quite useful [String] + */ +@SinceKotlin("1.1") +fun `availableSince1.1`(): String = "1.1 rulezz"
\ No newline at end of file diff --git a/core/testdata/issues/errorClasses.kt b/core/testdata/issues/errorClasses.kt new file mode 100644 index 000000000..9e966b3a4 --- /dev/null +++ b/core/testdata/issues/errorClasses.kt @@ -0,0 +1,20 @@ + +class Test(var value: String) { + fun brokenApply(v: String) = apply { value = v } + + fun brokenRun(v: String) = run { + value = v + this + } + + fun brokenLet(v: String) = let { + it.value = v + it + } + + fun brokenGenerics() = listOf("a", "b", "c") + + fun working(v: String) = doSomething() + + fun doSomething(): String = "Hello" +}
\ No newline at end of file diff --git a/core/testdata/java/InheritorLinks.java b/core/testdata/java/InheritorLinks.java new file mode 100644 index 000000000..31658265e --- /dev/null +++ b/core/testdata/java/InheritorLinks.java @@ -0,0 +1,7 @@ +public class InheritorLinks { + public static class Foo { + } + + public static class Bar extends Foo { + } +} diff --git a/core/testdata/java/InnerClass.java b/core/testdata/java/InnerClass.java new file mode 100644 index 000000000..0302da9df --- /dev/null +++ b/core/testdata/java/InnerClass.java @@ -0,0 +1,4 @@ +class InnerClass { + public class D { + } +} diff --git a/core/testdata/java/annotatedAnnotation.java b/core/testdata/java/annotatedAnnotation.java new file mode 100644 index 000000000..6296e4002 --- /dev/null +++ b/core/testdata/java/annotatedAnnotation.java @@ -0,0 +1,6 @@ +import java.lang.annotation.*; + +@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface Attribute { + String value() default ""; +} diff --git a/core/testdata/java/arrayType.java b/core/testdata/java/arrayType.java new file mode 100644 index 000000000..dc42a2077 --- /dev/null +++ b/core/testdata/java/arrayType.java @@ -0,0 +1,5 @@ +class Test { + public String[] arrayToString(int[] data) { + return null; + } +} diff --git a/core/testdata/java/constructors.java b/core/testdata/java/constructors.java new file mode 100644 index 000000000..6f899d182 --- /dev/null +++ b/core/testdata/java/constructors.java @@ -0,0 +1,5 @@ +class Test { + public Test() {} + + public Test(String s) {} +} diff --git a/core/testdata/java/deprecation.java b/core/testdata/java/deprecation.java new file mode 100644 index 000000000..be8decd6e --- /dev/null +++ b/core/testdata/java/deprecation.java @@ -0,0 +1,6 @@ +class C { + /** + * @deprecated This should no longer be used + */ + void fn() {} +}
\ No newline at end of file diff --git a/core/testdata/java/enumValues.java b/core/testdata/java/enumValues.java new file mode 100644 index 000000000..d8dda934a --- /dev/null +++ b/core/testdata/java/enumValues.java @@ -0,0 +1,3 @@ +enum E { + Foo +}
\ No newline at end of file diff --git a/core/testdata/java/field.java b/core/testdata/java/field.java new file mode 100644 index 000000000..d9ae75f90 --- /dev/null +++ b/core/testdata/java/field.java @@ -0,0 +1,4 @@ +class Test { + public int i; + public static final String s; +} diff --git a/core/testdata/java/hideAnnotation.java b/core/testdata/java/hideAnnotation.java new file mode 100644 index 000000000..58b3ac452 --- /dev/null +++ b/core/testdata/java/hideAnnotation.java @@ -0,0 +1,27 @@ +public class ShownClass { + + static int shownInt; + + /** + * @hide + */ + public static int hiddenInt; + + public static void shownMethod() { + + } + + /** + * @hide + */ + public static void hiddenMethod() { + + } +} + +/** + * @hide + */ +public class HiddenClass { + +} diff --git a/core/testdata/java/javaLangObject.java b/core/testdata/java/javaLangObject.java new file mode 100644 index 000000000..be3dd5708 --- /dev/null +++ b/core/testdata/java/javaLangObject.java @@ -0,0 +1,3 @@ +class Test { + public Object fn() { return null; } +} diff --git a/core/testdata/java/member.java b/core/testdata/java/member.java new file mode 100644 index 000000000..d4f4b8d59 --- /dev/null +++ b/core/testdata/java/member.java @@ -0,0 +1,11 @@ +class Test { + /** + * Summary for Function + * @param name is String parameter + * @param value is int parameter + * @author yole + */ + public void fn(String name, int value) { + + } +}
\ No newline at end of file diff --git a/core/testdata/java/memberWithModifiers.java b/core/testdata/java/memberWithModifiers.java new file mode 100644 index 000000000..ea645c218 --- /dev/null +++ b/core/testdata/java/memberWithModifiers.java @@ -0,0 +1,12 @@ +public abstract class Test { + /** + * Summary for Function + * @param name is String parameter + * @param value is int parameter + */ + protected final void fn(String name, int value) { + + } + + protected void openFn() {} +}
\ No newline at end of file diff --git a/core/testdata/java/staticMethod.java b/core/testdata/java/staticMethod.java new file mode 100644 index 000000000..a2ecd7f11 --- /dev/null +++ b/core/testdata/java/staticMethod.java @@ -0,0 +1,4 @@ +class C { + public static void foo() { + } +} diff --git a/core/testdata/java/superClass.java b/core/testdata/java/superClass.java new file mode 100644 index 000000000..31b5fa962 --- /dev/null +++ b/core/testdata/java/superClass.java @@ -0,0 +1,2 @@ +public class Foo extends Exception implements Cloneable { +} diff --git a/core/testdata/java/suppressTag.java b/core/testdata/java/suppressTag.java new file mode 100644 index 000000000..c26194c84 --- /dev/null +++ b/core/testdata/java/suppressTag.java @@ -0,0 +1,10 @@ +class C { + public static void foo() { + } + + /** + * @suppress + */ + public static void bar() { + } +} diff --git a/core/testdata/java/typeParameter.java b/core/testdata/java/typeParameter.java new file mode 100644 index 000000000..5a24b30ac --- /dev/null +++ b/core/testdata/java/typeParameter.java @@ -0,0 +1,3 @@ +class Foo<T extends Comparable<T>> { + public <E> E foo(); +} diff --git a/core/testdata/java/varargs.java b/core/testdata/java/varargs.java new file mode 100644 index 000000000..d184564e8 --- /dev/null +++ b/core/testdata/java/varargs.java @@ -0,0 +1,3 @@ +class Foo { + public void bar(String... x); +} diff --git a/core/testdata/javadoc/blankLineInsideCodeBlock.kt b/core/testdata/javadoc/blankLineInsideCodeBlock.kt new file mode 100644 index 000000000..9430f4d58 --- /dev/null +++ b/core/testdata/javadoc/blankLineInsideCodeBlock.kt @@ -0,0 +1,12 @@ +/** + * ``` + * This is a test + * of Dokka's code blocks. + * Here is a blank line. + * + * The previous line was blank. + * ``` + */ +fun u() { + +}
\ No newline at end of file diff --git a/core/testdata/javadoc/bytearr.kt b/core/testdata/javadoc/bytearr.kt new file mode 100644 index 000000000..84be1a706 --- /dev/null +++ b/core/testdata/javadoc/bytearr.kt @@ -0,0 +1,7 @@ +package foo + +class ByteArray { + fun foo(): IntArray { + return intArrayOf() + } +}
\ No newline at end of file diff --git a/core/testdata/javadoc/companionMethodReference.kt b/core/testdata/javadoc/companionMethodReference.kt new file mode 100644 index 000000000..499e4492f --- /dev/null +++ b/core/testdata/javadoc/companionMethodReference.kt @@ -0,0 +1,13 @@ +package foo + + +/** + * Linking to [test] + */ +class TestClass { + + companion object { + + @JvmStatic fun test(arg: String) {} + } +}
\ No newline at end of file diff --git a/core/testdata/javadoc/deprecated.java b/core/testdata/javadoc/deprecated.java new file mode 100644 index 000000000..5a6cdd77c --- /dev/null +++ b/core/testdata/javadoc/deprecated.java @@ -0,0 +1,28 @@ +package bar; + +/** + * Just a fruit + */ +public class Banana { + private Double weight; + + /** + * Returns weight + * + * @return weight + * @deprecated + */ + public Double getWeight() { + return weight; + } + + /** + * Sets weight + * + * @param weight in grams + * @deprecated with message + */ + public void setWeight(Double weight) { + this.weight = weight; + } +}
\ No newline at end of file diff --git a/core/testdata/javadoc/exception.kt b/core/testdata/javadoc/exception.kt new file mode 100644 index 000000000..ec0a5bbb6 --- /dev/null +++ b/core/testdata/javadoc/exception.kt @@ -0,0 +1,5 @@ +package foo + +class MyException : Exception { + fun foo() = "" +} diff --git a/core/testdata/javadoc/internal.kt b/core/testdata/javadoc/internal.kt new file mode 100644 index 000000000..a57ea3b41 --- /dev/null +++ b/core/testdata/javadoc/internal.kt @@ -0,0 +1,8 @@ +package foo + +data class Person internal constructor( + val name: String = "", + val age: Int = 0 +) { + constructor(age: Int): this("", age) +} diff --git a/core/testdata/javadoc/jvmname.kt b/core/testdata/javadoc/jvmname.kt new file mode 100644 index 000000000..e4774cd67 --- /dev/null +++ b/core/testdata/javadoc/jvmname.kt @@ -0,0 +1,6 @@ +package foo + +class Apple { + @get:JvmName("_tree") + internal val source: Tree +} diff --git a/core/testdata/javadoc/kdocKeywordsOnMethod.kt b/core/testdata/javadoc/kdocKeywordsOnMethod.kt new file mode 100644 index 000000000..df5bbbe0f --- /dev/null +++ b/core/testdata/javadoc/kdocKeywordsOnMethod.kt @@ -0,0 +1,12 @@ +class FireException : Exception + + +/** + * COMM + * @param a Some string + * @return value of a + * @throws FireException in case of fire + */ +@Throws(FireException::class) +fun my(a: String): String = a + diff --git a/core/testdata/javadoc/obj.kt b/core/testdata/javadoc/obj.kt new file mode 100644 index 000000000..1d10a4228 --- /dev/null +++ b/core/testdata/javadoc/obj.kt @@ -0,0 +1,7 @@ +package foo + +class O { + companion object { + + } +} diff --git a/core/testdata/javadoc/paramlink.kt b/core/testdata/javadoc/paramlink.kt new file mode 100644 index 000000000..48972a22d --- /dev/null +++ b/core/testdata/javadoc/paramlink.kt @@ -0,0 +1,10 @@ +package demo + +/** + * You can [eat] it or cut it into slices using [cutIntoPieces] + */ +interface Apple { + fun eat() + + fun cutIntoPieces(pieces: Int) +} diff --git a/core/testdata/javadoc/stringarr.kt b/core/testdata/javadoc/stringarr.kt new file mode 100644 index 000000000..d6cd9dea3 --- /dev/null +++ b/core/testdata/javadoc/stringarr.kt @@ -0,0 +1,8 @@ +package foo + +class Foo { + companion object { + @JvmStatic fun main(args: Array<String>) { + } + } +}
\ No newline at end of file diff --git a/core/testdata/javadoc/suppress.kt b/core/testdata/javadoc/suppress.kt new file mode 100644 index 000000000..90f6c131d --- /dev/null +++ b/core/testdata/javadoc/suppress.kt @@ -0,0 +1,37 @@ +/** + * @suppress + */ +class Some { + +} + + +/** + * @suppress + * @author me + * @see other + */ +class SomeAgain { + +} + +class Same { + /** + * @suppress + */ + fun privateApi() { + + } + + /** + * @suppress + */ + val privateForSomeReason = "" +} + +/** + * @suppress + */ +interface Interface { + +}
\ No newline at end of file diff --git a/core/testdata/javadoc/typealiases.kt b/core/testdata/javadoc/typealiases.kt new file mode 100644 index 000000000..bb09bfada --- /dev/null +++ b/core/testdata/javadoc/typealiases.kt @@ -0,0 +1,11 @@ +class A + +typealias B = A + +class C : B + +typealias D = (A) -> C + +fun some(d: D) { + +}
\ No newline at end of file diff --git a/core/testdata/javadoc/types.kt b/core/testdata/javadoc/types.kt new file mode 100644 index 000000000..55be60587 --- /dev/null +++ b/core/testdata/javadoc/types.kt @@ -0,0 +1,4 @@ +package foo + +fun foo(x: Int, o: Any): String { +} diff --git a/core/testdata/links/linkToConstantWithUnderscores.kt b/core/testdata/links/linkToConstantWithUnderscores.kt new file mode 100644 index 000000000..57011bfa1 --- /dev/null +++ b/core/testdata/links/linkToConstantWithUnderscores.kt @@ -0,0 +1,8 @@ +/** + * This is link to [MY_CONSTANT_VALUE] + */ +class Foo { + companion object { + val MY_CONSTANT_VALUE = 0 + } +}
\ No newline at end of file diff --git a/core/testdata/links/linkToJDK.kt b/core/testdata/links/linkToJDK.kt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/core/testdata/links/linkToJDK.kt diff --git a/core/testdata/links/linkToMember.kt b/core/testdata/links/linkToMember.kt new file mode 100644 index 000000000..b60eaedbe --- /dev/null +++ b/core/testdata/links/linkToMember.kt @@ -0,0 +1,6 @@ +/** + * This is link to [member] + */ +class Foo { + fun member() {} +}
\ No newline at end of file diff --git a/core/testdata/links/linkToPackage.kt b/core/testdata/links/linkToPackage.kt new file mode 100644 index 000000000..3dd866288 --- /dev/null +++ b/core/testdata/links/linkToPackage.kt @@ -0,0 +1,8 @@ +package test.magic + +/** + * Basic implementations of [Magic] are located in [test.magic] package + */ +abstract class Magic { + +} diff --git a/core/testdata/links/linkToParam.kt b/core/testdata/links/linkToParam.kt new file mode 100644 index 000000000..ca42a7428 --- /dev/null +++ b/core/testdata/links/linkToParam.kt @@ -0,0 +1,5 @@ +/** + * This is link to [param] + */ +fun Foo(param: String) { +}
\ No newline at end of file diff --git a/core/testdata/links/linkToQualifiedMember.kt b/core/testdata/links/linkToQualifiedMember.kt new file mode 100644 index 000000000..22c154fe8 --- /dev/null +++ b/core/testdata/links/linkToQualifiedMember.kt @@ -0,0 +1,6 @@ +/** + * This is link to [Foo.member] + */ +class Foo { + fun member() {} +}
\ No newline at end of file diff --git a/core/testdata/links/linkToSelf.kt b/core/testdata/links/linkToSelf.kt new file mode 100644 index 000000000..74395f0ff --- /dev/null +++ b/core/testdata/links/linkToSelf.kt @@ -0,0 +1,6 @@ +/** + * This is link to [Foo] + */ +class Foo { + +}
\ No newline at end of file diff --git a/core/testdata/markdown/spec.txt b/core/testdata/markdown/spec.txt new file mode 100644 index 000000000..fce879240 --- /dev/null +++ b/core/testdata/markdown/spec.txt @@ -0,0 +1,6150 @@ +--- +title: CommonMark Spec +author: +- John MacFarlane +version: 2 +date: 2014-09-19 +... + +# Introduction + +## What is Markdown? + +Markdown is a plain text format for writing structured documents, +based on conventions used for indicating formatting in email and +usenet posts. It was developed in 2004 by John Gruber, who wrote +the first Markdown-to-HTML converter in perl, and it soon became +widely used in websites. By 2014 there were dozens of +implementations in many languages. Some of them extended basic +Markdown syntax with conventions for footnotes, definition lists, +tables, and other constructs, and some allowed output not just in +HTML but in LaTeX and many other formats. + +## Why is a spec needed? + +John Gruber's [canonical description of Markdown's +syntax](http://daringfireball.net/projects/markdown/syntax) +does not specify the syntax unambiguously. Here are some examples of +questions it does not answer: + +1. How much indentation is needed for a sublist? The spec says that + continuation paragraphs need to be indented four spaces, but is + not fully explicit about sublists. It is natural to think that + they, too, must be indented four spaces, but `Markdown.pl` does + not require that. This is hardly a "corner case," and divergences + between implementations on this issue often lead to surprises for + users in real documents. (See [this comment by John + Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).) + +2. Is a blank line needed before a block quote or header? + Most implementations do not require the blank line. However, + this can lead to unexpected results in hard-wrapped text, and + also to ambiguities in parsing (note that some implementations + put the header inside the blockquote, while others do not). + (John Gruber has also spoken [in favor of requiring the blank + lines](http://article.gmane.org/gmane.text.markdown.general/2146).) + +3. Is a blank line needed before an indented code block? + (`Markdown.pl` requires it, but this is not mentioned in the + documentation, and some implementations do not require it.) + + ``` markdown + paragraph + code? + ``` + +4. What is the exact rule for determining when list items get + wrapped in `<p>` tags? Can a list be partially "loose" and partially + "tight"? What should we do with a list like this? + + ``` markdown + 1. one + + 2. two + 3. three + ``` + + Or this? + + ``` markdown + 1. one + - a + + - b + 2. two + ``` + + (There are some relevant comments by John Gruber + [here](http://article.gmane.org/gmane.text.markdown.general/2554).) + +5. Can list markers be indented? Can ordered list markers be right-aligned? + + ``` markdown + 8. item 1 + 9. item 2 + 10. item 2a + ``` + +6. Is this one list with a horizontal rule in its second item, + or two lists separated by a horizontal rule? + + ``` markdown + * a + * * * * * + * b + ``` + +7. When list markers change from numbers to bullets, do we have + two lists or one? (The Markdown syntax description suggests two, + but the perl scripts and many other implementations produce one.) + + ``` markdown + 1. fee + 2. fie + - foe + - fum + ``` + +8. What are the precedence rules for the markers of inline structure? + For example, is the following a valid link, or does the code span + take precedence ? + + ``` markdown + [a backtick (`)](/url) and [another backtick (`)](/url). + ``` + +9. What are the precedence rules for markers of emphasis and strong + emphasis? For example, how should the following be parsed? + + ``` markdown + *foo *bar* baz* + ``` + +10. What are the precedence rules between block-level and inline-level + structure? For example, how should the following be parsed? + + ``` markdown + - `a long code span can contain a hyphen like this + - and it can screw things up` + ``` + +11. Can list items include headers? (`Markdown.pl` does not allow this, + but headers can occur in blockquotes.) + + ``` markdown + - # Heading + ``` + +12. Can link references be defined inside block quotes or list items? + + ``` markdown + > Blockquote [foo]. + > + > [foo]: /url + ``` + +13. If there are multiple definitions for the same reference, which takes + precedence? + + ``` markdown + [foo]: /url1 + [foo]: /url2 + + [foo][] + ``` + +In the absence of a spec, early implementers consulted `Markdown.pl` +to resolve these ambiguities. But `Markdown.pl` was quite buggy, and +gave manifestly bad results in many cases, so it was not a +satisfactory replacement for a spec. + +Because there is no unambiguous spec, implementations have diverged +considerably. As a result, users are often surprised to find that +a document that renders one way on one system (say, a github wiki) +renders differently on another (say, converting to docbook using +pandoc). To make matters worse, because nothing in Markdown counts +as a "syntax error," the divergence often isn't discovered right away. + +## About this document + +This document attempts to specify Markdown syntax unambiguously. +It contains many examples with side-by-side Markdown and +HTML. These are intended to double as conformance tests. An +accompanying script `runtests.pl` can be used to run the tests +against any Markdown program: + + perl runtests.pl spec.txt PROGRAM + +Since this document describes how Markdown is to be parsed into +an abstract syntax tree, it would have made sense to use an abstract +representation of the syntax tree instead of HTML. But HTML is capable +of representing the structural distinctions we need to make, and the +choice of HTML for the tests makes it possible to run the tests against +an implementation without writing an abstract syntax tree renderer. + +This document is generated from a text file, `spec.txt`, written +in Markdown with a small extension for the side-by-side tests. +The script `spec2md.pl` can be used to turn `spec.txt` into pandoc +Markdown, which can then be converted into other formats. + +In the examples, the `→` character is used to represent tabs. + +# Preprocessing + +A [line](#line) <a id="line"></a> +is a sequence of zero or more characters followed by a line +ending (CR, LF, or CRLF) or by the end of +file. + +This spec does not specify an encoding; it thinks of lines as composed +of characters rather than bytes. A conforming parser may be limited +to a certain encoding. + +Tabs in lines are expanded to spaces, with a tab stop of 4 characters: + +. +→foo→baz→→bim +. +<pre><code>foo baz bim +</code></pre> +. + +. + a→a + ὐ→a +. +<pre><code>a a +ὐ a +</code></pre> +. + +Line endings are replaced by newline characters (LF). + +A line containing no characters, or a line containing only spaces (after +tab expansion), is called a [blank line](#blank-line). +<a id="blank-line"></a> + +# Blocks and inlines + +We can think of a document as a sequence of [blocks](#block)<a +id="block"></a>---structural elements like paragraphs, block quotations, +lists, headers, rules, and code blocks. Blocks can contain other +blocks, or they can contain [inline](#inline)<a id="inline"></a> content: +words, spaces, links, emphasized text, images, and inline code. + +## Precedence + +Indicators of block structure always take precedence over indicators +of inline structure. So, for example, the following is a list with +two items, not a list with one item containing a code span: + +. +- `one +- two` +. +<ul> +<li>`one</li> +<li>two`</li> +</ul> +. + +This means that parsing can proceed in two steps: first, the block +structure of the document can be discerned; second, text lines inside +paragraphs, headers, and other block constructs can be parsed for inline +structure. The second step requires information about link reference +definitions that will be available only at the end of the first +step. Note that the first step requires processing lines in sequence, +but the second can be parallelized, since the inline parsing of +one block element does not affect the inline parsing of any other. + +## Container blocks and leaf blocks + +We can divide blocks into two types: +[container blocks](#container-block), <a id="container-block"></a> +which can contain other blocks, and [leaf blocks](#leaf-block), +<a id="leaf-block"></a> which cannot. + +# Leaf blocks + +This section describes the different kinds of leaf block that make up a +Markdown document. + +## Horizontal rules + +A line consisting of 0-3 spaces of indentation, followed by a sequence +of three or more matching `-`, `_`, or `*` characters, each followed +optionally by any number of spaces, forms a [horizontal +rule](#horizontal-rule). <a id="horizontal-rule"></a> + +. +*** +--- +___ +. +<hr /> +<hr /> +<hr /> +. + +Wrong characters: + +. ++++ +. +<p>+++</p> +. + +. +=== +. +<p>===</p> +. + +Not enough characters: + +. +-- +** +__ +. +<p>-- +** +__</p> +. + +One to three spaces indent are allowed: + +. + *** + *** + *** +. +<hr /> +<hr /> +<hr /> +. + +Four spaces is too many: + +. + *** +. +<pre><code>*** +</code></pre> +. + +. +Foo + *** +. +<p>Foo +***</p> +. + +More than three characters may be used: + +. +_____________________________________ +. +<hr /> +. + +Spaces are allowed between the characters: + +. + - - - +. +<hr /> +. + +. + ** * ** * ** * ** +. +<hr /> +. + +. +- - - - +. +<hr /> +. + +Spaces are allowed at the end: + +. +- - - - +. +<hr /> +. + +However, no other characters may occur at the end or the +beginning: + +. +_ _ _ _ a + +a------ +. +<p>_ _ _ _ a</p> +<p>a------</p> +. + +It is required that all of the non-space characters be the same. +So, this is not a horizontal rule: + +. + *-* +. +<p><em>-</em></p> +. + +Horizontal rules do not need blank lines before or after: + +. +- foo +*** +- bar +. +<ul> +<li>foo</li> +</ul> +<hr /> +<ul> +<li>bar</li> +</ul> +. + +Horizontal rules can interrupt a paragraph: + +. +Foo +*** +bar +. +<p>Foo</p> +<hr /> +<p>bar</p> +. + +Note, however, that this is a setext header, not a paragraph followed +by a horizontal rule: + +. +Foo +--- +bar +. +<h2>Foo</h2> +<p>bar</p> +. + +When both a horizontal rule and a list item are possible +interpretations of a line, the horizontal rule is preferred: + +. +* Foo +* * * +* Bar +. +<ul> +<li>Foo</li> +</ul> +<hr /> +<ul> +<li>Bar</li> +</ul> +. + +If you want a horizontal rule in a list item, use a different bullet: + +. +- Foo +- * * * +. +<ul> +<li>Foo</li> +<li><hr /></li> +</ul> +. + +## ATX headers + +An [ATX header](#atx-header) <a id="atx-header"></a> +consists of a string of characters, parsed as inline content, between an +opening sequence of 1--6 unescaped `#` characters and an optional +closing sequence of any number of `#` characters. The opening sequence +of `#` characters cannot be followed directly by a nonspace character. +The closing `#` characters may be followed by spaces only. The opening +`#` character may be indented 0-3 spaces. The raw contents of the +header are stripped of leading and trailing spaces before being parsed +as inline content. The header level is equal to the number of `#` +characters in the opening sequence. + +Simple headers: + +. +# foo +## foo +### foo +#### foo +##### foo +###### foo +. +<h1>foo</h1> +<h2>foo</h2> +<h3>foo</h3> +<h4>foo</h4> +<h5>foo</h5> +<h6>foo</h6> +. + +More than six `#` characters is not a header: + +. +####### foo +. +<p>####### foo</p> +. + +A space is required between the `#` characters and the header's +contents. Note that many implementations currently do not require +the space. However, the space was required by the [original ATX +implementation](http://www.aaronsw.com/2002/atx/atx.py), and it helps +prevent things like the following from being parsed as headers: + +. +#5 bolt +. +<p>#5 bolt</p> +. + +This is not a header, because the first `#` is escaped: + +. +\## foo +. +<p>## foo</p> +. + +Contents are parsed as inlines: + +. +# foo *bar* \*baz\* +. +<h1>foo <em>bar</em> *baz*</h1> +. + +Leading and trailing blanks are ignored in parsing inline content: + +. +# foo +. +<h1>foo</h1> +. + +One to three spaces indentation are allowed: + +. + ### foo + ## foo + # foo +. +<h3>foo</h3> +<h2>foo</h2> +<h1>foo</h1> +. + +Four spaces are too much: + +. + # foo +. +<pre><code># foo +</code></pre> +. + +. +foo + # bar +. +<p>foo +# bar</p> +. + +A closing sequence of `#` characters is optional: + +. +## foo ## + ### bar ### +. +<h2>foo</h2> +<h3>bar</h3> +. + +It need not be the same length as the opening sequence: + +. +# foo ################################## +##### foo ## +. +<h1>foo</h1> +<h5>foo</h5> +. + +Spaces are allowed after the closing sequence: + +. +### foo ### +. +<h3>foo</h3> +. + +A sequence of `#` characters with a nonspace character following it +is not a closing sequence, but counts as part of the contents of the +header: + +. +### foo ### b +. +<h3>foo ### b</h3> +. + +Backslash-escaped `#` characters do not count as part +of the closing sequence: + +. +### foo \### +## foo \#\## +# foo \# +. +<h3>foo #</h3> +<h2>foo ##</h2> +<h1>foo #</h1> +. + +ATX headers need not be separated from surrounding content by blank +lines, and they can interrupt paragraphs: + +. +**** +## foo +**** +. +<hr /> +<h2>foo</h2> +<hr /> +. + +. +Foo bar +# baz +Bar foo +. +<p>Foo bar</p> +<h1>baz</h1> +<p>Bar foo</p> +. + +ATX headers can be empty: + +. +## +# +### ### +. +<h2></h2> +<h1></h1> +<h3></h3> +. + +## Setext headers + +A [setext header](#setext-header) <a id="setext-header"></a> +consists of a line of text, containing at least one nonspace character, +with no more than 3 spaces indentation, followed by a [setext header +underline](#setext-header-underline). A [setext header +underline](#setext-header-underline) <a id="setext-header-underline"></a> +is a sequence of `=` characters or a sequence of `-` characters, with no +more than 3 spaces indentation and any number of trailing +spaces. The header is a level 1 header if `=` characters are used, and +a level 2 header if `-` characters are used. The contents of the header +are the result of parsing the first line as Markdown inline content. + +In general, a setext header need not be preceded or followed by a +blank line. However, it cannot interrupt a paragraph, so when a +setext header comes after a paragraph, a blank line is needed between +them. + +Simple examples: + +. +Foo *bar* +========= + +Foo *bar* +--------- +. +<h1>Foo <em>bar</em></h1> +<h2>Foo <em>bar</em></h2> +. + +The underlining can be any length: + +. +Foo +------------------------- + +Foo += +. +<h2>Foo</h2> +<h1>Foo</h1> +. + +The header content can be indented up to three spaces, and need +not line up with the underlining: + +. + Foo +--- + + Foo +----- + + Foo + === +. +<h2>Foo</h2> +<h2>Foo</h2> +<h1>Foo</h1> +. + +Four spaces indent is too much: + +. + Foo + --- + + Foo +--- +. +<pre><code>Foo +--- + +Foo +</code></pre> +<hr /> +. + +The setext header underline can be indented up to three spaces, and +may have trailing spaces: + +. +Foo + ---- +. +<h2>Foo</h2> +. + +Four spaces is too much: + +. +Foo + --- +. +<p>Foo +---</p> +. + +The setext header underline cannot contain internal spaces: + +. +Foo += = + +Foo +--- - +. +<p>Foo += =</p> +<p>Foo</p> +<hr /> +. + +Trailing spaces in the content line do not cause a line break: + +. +Foo +----- +. +<h2>Foo</h2> +. + +Nor does a backslash at the end: + +. +Foo\ +---- +. +<h2>Foo\</h2> +. + +Since indicators of block structure take precedence over +indicators of inline structure, the following are setext headers: + +. +`Foo +---- +` + +<a title="a lot +--- +of dashes"/> +. +<h2>`Foo</h2> +<p>`</p> +<h2><a title="a lot</h2> +<p>of dashes"/></p> +. + +The setext header underline cannot be a lazy line: + +. +> Foo +--- +. +<blockquote> +<p>Foo</p> +</blockquote> +<hr /> +. + +A setext header cannot interrupt a paragraph: + +. +Foo +Bar +--- + +Foo +Bar +=== +. +<p>Foo +Bar</p> +<hr /> +<p>Foo +Bar +===</p> +. + +But in general a blank line is not required before or after: + +. +--- +Foo +--- +Bar +--- +Baz +. +<hr /> +<h2>Foo</h2> +<h2>Bar</h2> +<p>Baz</p> +. + +Setext headers cannot be empty: + +. + +==== +. +<p>====</p> +. + + +## Indented code blocks + +An [indented code block](#indented-code-block) +<a id="indented-code-block"></a> is composed of one or more +[indented chunks](#indented-chunk) separated by blank lines. +An [indented chunk](#indented-chunk) <a id="indented-chunk"></a> +is a sequence of non-blank lines, each indented four or more +spaces. An indented code block cannot interrupt a paragraph, so +if it occurs before or after a paragraph, there must be an +intervening blank line. The contents of the code block are +the literal contents of the lines, including trailing newlines, +minus four spaces of indentation. An indented code block has no +attributes. + +. + a simple + indented code block +. +<pre><code>a simple + indented code block +</code></pre> +. + +The contents are literal text, and do not get parsed as Markdown: + +. + <a/> + *hi* + + - one +. +<pre><code><a/> +*hi* + +- one +</code></pre> +. + +Here we have three chunks separated by blank lines: + +. + chunk1 + + chunk2 + + + + chunk3 +. +<pre><code>chunk1 + +chunk2 + + + +chunk3 +</code></pre> +. + +Any initial spaces beyond four will be included in the content, even +in interior blank lines: + +. + chunk1 + + chunk2 +. +<pre><code>chunk1 + + chunk2 +</code></pre> +. + +An indented code block cannot interrupt a paragraph. (This +allows hanging indents and the like.) + +. +Foo + bar + +. +<p>Foo +bar</p> +. + +However, any non-blank line with fewer than four leading spaces ends +the code block immediately. So a paragraph may occur immediately +after indented code: + +. + foo +bar +. +<pre><code>foo +</code></pre> +<p>bar</p> +. + +And indented code can occur immediately before and after other kinds of +blocks: + +. +# Header + foo +Header +------ + foo +---- +. +<h1>Header</h1> +<pre><code>foo +</code></pre> +<h2>Header</h2> +<pre><code>foo +</code></pre> +<hr /> +. + +The first line can be indented more than four spaces: + +. + foo + bar +. +<pre><code> foo +bar +</code></pre> +. + +Blank lines preceding or following an indented code block +are not included in it: + +. + + + foo + + +. +<pre><code>foo +</code></pre> +. + +Trailing spaces are included in the code block's content: + +. + foo +. +<pre><code>foo +</code></pre> +. + + +## Fenced code blocks + +A [code fence](#code-fence) <a id="code-fence"></a> is a sequence +of at least three consecutive backtick characters (`` ` ``) or +tildes (`~`). (Tildes and backticks cannot be mixed.) +A [fenced code block](#fenced-code-block) <a id="fenced-code-block"></a> +begins with a code fence, indented no more than three spaces. + +The line with the opening code fence may optionally contain some text +following the code fence; this is trimmed of leading and trailing +spaces and called the [info string](#info-string). +<a id="info-string"></a> The info string may not contain any backtick +characters. (The reason for this restriction is that otherwise +some inline code would be incorrectly interpreted as the +beginning of a fenced code block.) + +The content of the code block consists of all subsequent lines, until +a closing [code fence](#code-fence) of the same type as the code block +began with (backticks or tildes), and with at least as many backticks +or tildes as the opening code fence. If the leading code fence is +indented N spaces, then up to N spaces of indentation are removed from +each line of the content (if present). (If a content line is not +indented, it is preserved unchanged. If it is indented less than N +spaces, all of the indentation is removed.) + +The closing code fence may be indented up to three spaces, and may be +followed only by spaces, which are ignored. If the end of the +containing block (or document) is reached and no closing code fence +has been found, the code block contains all of the lines after the +opening code fence until the end of the containing block (or +document). (An alternative spec would require backtracking in the +event that a closing code fence is not found. But this makes parsing +much less efficient, and there seems to be no real down side to the +behavior described here.) + +A fenced code block may interrupt a paragraph, and does not require +a blank line either before or after. + +The content of a code fence is treated as literal text, not parsed +as inlines. The first word of the info string is typically used to +specify the language of the code sample, and rendered in the `class` +attribute of the `code` tag. However, this spec does not mandate any +particular treatment of the info string. + +Here is a simple example with backticks: + +. +``` +< + > +``` +. +<pre><code>< + > +</code></pre> +. + +With tildes: + +. +~~~ +< + > +~~~ +. +<pre><code>< + > +</code></pre> +. + +The closing code fence must use the same character as the opening +fence: + +. +``` +aaa +~~~ +``` +. +<pre><code>aaa +~~~ +</code></pre> +. + +. +~~~ +aaa +``` +~~~ +. +<pre><code>aaa +``` +</code></pre> +. + +The closing code fence must be at least as long as the opening fence: + +. +```` +aaa +``` +`````` +. +<pre><code>aaa +``` +</code></pre> +. + +. +~~~~ +aaa +~~~ +~~~~ +. +<pre><code>aaa +~~~ +</code></pre> +. + +Unclosed code blocks are closed by the end of the document: + +. +``` +. +<pre><code></code></pre> +. + +. +````` + +``` +aaa +. +<pre><code> +``` +aaa +</code></pre> +. + +A code block can have all empty lines as its content: + +. +``` + + +``` +. +<pre><code> + +</code></pre> +. + +A code block can be empty: + +. +``` +``` +. +<pre><code></code></pre> +. + +Fences can be indented. If the opening fence is indented, +content lines will have equivalent opening indentation removed, +if present: + +. + ``` + aaa +aaa +``` +. +<pre><code>aaa +aaa +</code></pre> +. + +. + ``` +aaa + aaa +aaa + ``` +. +<pre><code>aaa +aaa +aaa +</code></pre> +. + +. + ``` + aaa + aaa + aaa + ``` +. +<pre><code>aaa + aaa +aaa +</code></pre> +. + +Four spaces indentation produces an indented code block: + +. + ``` + aaa + ``` +. +<pre><code>``` +aaa +``` +</code></pre> +. + +Code fences (opening and closing) cannot contain internal spaces: + +. +``` ``` +aaa +. +<p><code></code> +aaa</p> +. + +. +~~~~~~ +aaa +~~~ ~~ +. +<pre><code>aaa +~~~ ~~ +</code></pre> +. + +Fenced code blocks can interrupt paragraphs, and can be followed +directly by paragraphs, without a blank line between: + +. +foo +``` +bar +``` +baz +. +<p>foo</p> +<pre><code>bar +</code></pre> +<p>baz</p> +. + +Other blocks can also occur before and after fenced code blocks +without an intervening blank line: + +. +foo +--- +~~~ +bar +~~~ +# baz +. +<h2>foo</h2> +<pre><code>bar +</code></pre> +<h1>baz</h1> +. + +An [info string](#info-string) can be provided after the opening code fence. +Opening and closing spaces will be stripped, and the first word, prefixed +with `language-`, is used as the value for the `class` attribute of the +`code` element within the enclosing `pre` element. + +. +```ruby +def foo(x) + return 3 +end +``` +. +<pre><code class="language-ruby">def foo(x) + return 3 +end +</code></pre> +. + +. +~~~~ ruby startline=3 $%@#$ +def foo(x) + return 3 +end +~~~~~~~ +. +<pre><code class="language-ruby">def foo(x) + return 3 +end +</code></pre> +. + +. +````; +```` +. +<pre><code class="language-;"></code></pre> +. + +Info strings for backtick code blocks cannot contain backticks: + +. +``` aa ``` +foo +. +<p><code>aa</code> +foo</p> +. + +Closing code fences cannot have info strings: + +. +``` +``` aaa +``` +. +<pre><code>``` aaa +</code></pre> +. + + +## HTML blocks + +An [HTML block tag](#html-block-tag) <a id="html-block-tag"></a> is +an [open tag](#open-tag) or [closing tag](#closing-tag) whose tag +name is one of the following (case-insensitive): +`article`, `header`, `aside`, `hgroup`, `blockquote`, `hr`, `iframe`, +`body`, `li`, `map`, `button`, `object`, `canvas`, `ol`, `caption`, +`output`, `col`, `p`, `colgroup`, `pre`, `dd`, `progress`, `div`, +`section`, `dl`, `table`, `td`, `dt`, `tbody`, `embed`, `textarea`, +`fieldset`, `tfoot`, `figcaption`, `th`, `figure`, `thead`, `footer`, +`footer`, `tr`, `form`, `ul`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, +`video`, `script`, `style`. + +An [HTML block](#html-block) <a id="html-block"></a> begins with an +[HTML block tag](#html-block-tag), [HTML comment](#html-comment), +[processing instruction](#processing-instruction), +[declaration](#declaration), or [CDATA section](#cdata-section). +It ends when a [blank line](#blank-line) or the end of the +input is encountered. The initial line may be indented up to three +spaces, and subsequent lines may have any indentation. The contents +of the HTML block are interpreted as raw HTML, and will not be escaped +in HTML output. + +Some simple examples: + +. +<table> + <tr> + <td> + hi + </td> + </tr> +</table> + +okay. +. +<table> + <tr> + <td> + hi + </td> + </tr> +</table> +<p>okay.</p> +. + +. + <div> + *hello* + <foo><a> +. + <div> + *hello* + <foo><a> +. + +Here we have two code blocks with a Markdown paragraph between them: + +. +<DIV CLASS="foo"> + +*Markdown* + +</DIV> +. +<DIV CLASS="foo"> +<p><em>Markdown</em></p> +</DIV> +. + +In the following example, what looks like a Markdown code block +is actually part of the HTML block, which continues until a blank +line or the end of the document is reached: + +. +<div></div> +``` c +int x = 33; +``` +. +<div></div> +``` c +int x = 33; +``` +. + +A comment: + +. +<!-- Foo +bar + baz --> +. +<!-- Foo +bar + baz --> +. + +A processing instruction: + +. +<?php + echo 'foo' +?> +. +<?php + echo 'foo' +?> +. + +CDATA: + +. +<![CDATA[ +function matchwo(a,b) +{ +if (a < b && a < 0) then + { + return 1; + } +else + { + return 0; + } +} +]]> +. +<![CDATA[ +function matchwo(a,b) +{ +if (a < b && a < 0) then + { + return 1; + } +else + { + return 0; + } +} +]]> +. + +The opening tag can be indented 1-3 spaces, but not 4: + +. + <!-- foo --> + + <!-- foo --> +. + <!-- foo --> +<pre><code><!-- foo --> +</code></pre> +. + +An HTML block can interrupt a paragraph, and need not be preceded +by a blank line. + +. +Foo +<div> +bar +</div> +. +<p>Foo</p> +<div> +bar +</div> +. + +However, a following blank line is always needed, except at the end of +a document: + +. +<div> +bar +</div> +*foo* +. +<div> +bar +</div> +*foo* +. + +An incomplete HTML block tag may also start an HTML block: + +. +<div class +foo +. +<div class +foo +. + +This rule differs from John Gruber's original Markdown syntax +specification, which says: + +> The only restrictions are that block-level HTML elements — +> e.g. `<div>`, `<table>`, `<pre>`, `<p>`, etc. — must be separated from +> surrounding content by blank lines, and the start and end tags of the +> block should not be indented with tabs or spaces. + +In some ways Gruber's rule is more restrictive than the one given +here: + +- It requires that an HTML block be preceded by a blank line. +- It does not allow the start tag to be indented. +- It requires a matching end tag, which it also does not allow to + be indented. + +Indeed, most Markdown implementations, including some of Gruber's +own perl implementations, do not impose these restrictions. + +There is one respect, however, in which Gruber's rule is more liberal +than the one given here, since it allows blank lines to occur inside +an HTML block. There are two reasons for disallowing them here. +First, it removes the need to parse balanced tags, which is +expensive and can require backtracking from the end of the document +if no matching end tag is found. Second, it provides a very simple +and flexible way of including Markdown content inside HTML tags: +simply separate the Markdown from the HTML using blank lines: + +. +<div> + +*Emphasized* text. + +</div> +. +<div> +<p><em>Emphasized</em> text.</p> +</div> +. + +Compare: + +. +<div> +*Emphasized* text. +</div> +. +<div> +*Emphasized* text. +</div> +. + +Some Markdown implementations have adopted a convention of +interpreting content inside tags as text if the open tag has +the attribute `markdown=1`. The rule given above seems a simpler and +more elegant way of achieving the same expressive power, which is also +much simpler to parse. + +The main potential drawback is that one can no longer paste HTML +blocks into Markdown documents with 100% reliability. However, +*in most cases* this will work fine, because the blank lines in +HTML are usually followed by HTML block tags. For example: + +. +<table> + +<tr> + +<td> +Hi +</td> + +</tr> + +</table> +. +<table> +<tr> +<td> +Hi +</td> +</tr> +</table> +. + +Moreover, blank lines are usually not necessary and can be +deleted. The exception is inside `<pre>` tags; here, one can +replace the blank lines with ` ` entities. + +So there is no important loss of expressive power with the new rule. + +## Link reference definitions + +A [link reference definition](#link-reference-definition) +<a id="link-reference-definition"></a> consists of a [link +label](#link-label), indented up to three spaces, followed +by a colon (`:`), optional blank space (including up to one +newline), a [link destination](#link-destination), optional +blank space (including up to one newline), and an optional [link +title](#link-title), which if it is present must be separated +from the [link destination](#link-destination) by whitespace. +No further non-space characters may occur on the line. + +A [link reference-definition](#link-reference-definition) +does not correspond to a structural element of a document. Instead, it +defines a label which can be used in [reference links](#reference-link) +and reference-style [images](#image) elsewhere in the document. [Link +reference definitions] can come either before or after the links that use +them. + +. +[foo]: /url "title" + +[foo] +. +<p><a href="/url" title="title">foo</a></p> +. + +. + [foo]: + /url + 'the title' + +[foo] +. +<p><a href="/url" title="the title">foo</a></p> +. + +. +[Foo*bar\]]:my_(url) 'title (with parens)' + +[Foo*bar\]] +. +<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p> +. + +. +[Foo bar]: +<my url> +'title' + +[Foo bar] +. +<p><a href="my%20url" title="title">Foo bar</a></p> +. + +The title may be omitted: + +. +[foo]: +/url + +[foo] +. +<p><a href="/url">foo</a></p> +. + +The link destination may not be omitted: + +. +[foo]: + +[foo] +. +<p>[foo]:</p> +<p>[foo]</p> +. + +A link can come before its corresponding definition: + +. +[foo] + +[foo]: url +. +<p><a href="url">foo</a></p> +. + +If there are several matching definitions, the first one takes +precedence: + +. +[foo] + +[foo]: first +[foo]: second +. +<p><a href="first">foo</a></p> +. + +As noted in the section on [Links], matching of labels is +case-insensitive (see [matches](#matches)). + +. +[FOO]: /url + +[Foo] +. +<p><a href="/url">Foo</a></p> +. + +. +[ΑΓΩ]: /φου + +[αγω] +. +<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p> +. + +Here is a link reference definition with no corresponding link. +It contributes nothing to the document. + +. +[foo]: /url +. +. + +This is not a link reference definition, because there are +non-space characters after the title: + +. +[foo]: /url "title" ok +. +<p>[foo]: /url "title" ok</p> +. + +This is not a link reference definition, because it is indented +four spaces: + +. + [foo]: /url "title" + +[foo] +. +<pre><code>[foo]: /url "title" +</code></pre> +<p>[foo]</p> +. + +This is not a link reference definition, because it occurs inside +a code block: + +. +``` +[foo]: /url +``` + +[foo] +. +<pre><code>[foo]: /url +</code></pre> +<p>[foo]</p> +. + +A [link reference definition](#link-reference-definition) cannot +interrupt a paragraph. + +. +Foo +[bar]: /baz + +[bar] +. +<p>Foo +[bar]: /baz</p> +<p>[bar]</p> +. + +However, it can directly follow other block elements, such as headers +and horizontal rules, and it need not be followed by a blank line. + +. +# [Foo] +[foo]: /url +> bar +. +<h1><a href="/url">Foo</a></h1> +<blockquote> +<p>bar</p> +</blockquote> +. + +Several [link references](#link-reference) can occur one after another, +without intervening blank lines. + +. +[foo]: /foo-url "foo" +[bar]: /bar-url + "bar" +[baz]: /baz-url + +[foo], +[bar], +[baz] +. +<p><a href="/foo-url" title="foo">foo</a>, +<a href="/bar-url" title="bar">bar</a>, +<a href="/baz-url">baz</a></p> +. + +[Link reference definitions](#link-reference-definition) can occur +inside block containers, like lists and block quotations. They +affect the entire document, not just the container in which they +are defined: + +. +[foo] + +> [foo]: /url +. +<p><a href="/url">foo</a></p> +<blockquote> +</blockquote> +. + + +## Paragraphs + +A sequence of non-blank lines that cannot be interpreted as other +kinds of blocks forms a [paragraph](#paragraph).<a id="paragraph"></a> +The contents of the paragraph are the result of parsing the +paragraph's raw content as inlines. The paragraph's raw content +is formed by concatenating the lines and removing initial and final +spaces. + +A simple example with two paragraphs: + +. +aaa + +bbb +. +<p>aaa</p> +<p>bbb</p> +. + +Paragraphs can contain multiple lines, but no blank lines: + +. +aaa +bbb + +ccc +ddd +. +<p>aaa +bbb</p> +<p>ccc +ddd</p> +. + +Multiple blank lines between paragraph have no effect: + +. +aaa + + +bbb +. +<p>aaa</p> +<p>bbb</p> +. + +Leading spaces are skipped: + +. + aaa + bbb +. +<p>aaa +bbb</p> +. + +Lines after the first may be indented any amount, since indented +code blocks cannot interrupt paragraphs. + +. +aaa + bbb + ccc +. +<p>aaa +bbb +ccc</p> +. + +However, the first line may be indented at most three spaces, +or an indented code block will be triggered: + +. + aaa +bbb +. +<p>aaa +bbb</p> +. + +. + aaa +bbb +. +<pre><code>aaa +</code></pre> +<p>bbb</p> +. + +Final spaces are stripped before inline parsing, so a paragraph +that ends with two or more spaces will not end with a hard line +break: + +. +aaa +bbb +. +<p>aaa<br /> +bbb</p> +. + +## Blank lines + +[Blank lines](#blank-line) between block-level elements are ignored, +except for the role they play in determining whether a [list](#list) +is [tight](#tight) or [loose](#loose). + +Blank lines at the beginning and end of the document are also ignored. + +. + + +aaa + + +# aaa + + +. +<p>aaa</p> +<h1>aaa</h1> +. + + +# Container blocks + +A [container block](#container-block) is a block that has other +blocks as its contents. There are two basic kinds of container blocks: +[block quotes](#block-quote) and [list items](#list-item). +[Lists](#list) are meta-containers for [list items](#list-item). + +We define the syntax for container blocks recursively. The general +form of the definition is: + +> If X is a sequence of blocks, then the result of +> transforming X in such-and-such a way is a container of type Y +> with these blocks as its content. + +So, we explain what counts as a block quote or list item by explaining +how these can be *generated* from their contents. This should suffice +to define the syntax, although it does not give a recipe for *parsing* +these constructions. (A recipe is provided below in the section entitled +[A parsing strategy](#appendix-a-a-parsing-strategy).) + +## Block quotes + +A [block quote marker](#block-quote-marker) <a id="block-quote-marker"></a> +consists of 0-3 spaces of initial indent, plus (a) the character `>` together +with a following space, or (b) a single character `>` not followed by a space. + +The following rules define [block quotes](#block-quote): +<a id="block-quote"></a> + +1. **Basic case.** If a string of lines *Ls* constitute a sequence + of blocks *Bs*, then the result of appending a [block quote + marker](#block-quote-marker) to the beginning of each line in *Ls* + is a [block quote](#block-quote) containing *Bs*. + +2. **Laziness.** If a string of lines *Ls* constitute a [block + quote](#block-quote) with contents *Bs*, then the result of deleting + the initial [block quote marker](#block-quote-marker) from one or + more lines in which the next non-space character after the [block + quote marker](#block-quote-marker) is [paragraph continuation + text](#paragraph-continuation-text) is a block quote with *Bs* as + its content. <a id="paragraph-continuation-text"></a> + [Paragraph continuation text](#paragraph-continuation-text) is text + that will be parsed as part of the content of a paragraph, but does + not occur at the beginning of the paragraph. + +3. **Consecutiveness.** A document cannot contain two [block + quotes](#block-quote) in a row unless there is a [blank + line](#blank-line) between them. + +Nothing else counts as a [block quote](#block-quote). + +Here is a simple example: + +. +> # Foo +> bar +> baz +. +<blockquote> +<h1>Foo</h1> +<p>bar +baz</p> +</blockquote> +. + +The spaces after the `>` characters can be omitted: + +. +># Foo +>bar +> baz +. +<blockquote> +<h1>Foo</h1> +<p>bar +baz</p> +</blockquote> +. + +The `>` characters can be indented 1-3 spaces: + +. + > # Foo + > bar + > baz +. +<blockquote> +<h1>Foo</h1> +<p>bar +baz</p> +</blockquote> +. + +Four spaces gives us a code block: + +. + > # Foo + > bar + > baz +. +<pre><code>> # Foo +> bar +> baz +</code></pre> +. + +The Laziness clause allows us to omit the `>` before a +paragraph continuation line: + +. +> # Foo +> bar +baz +. +<blockquote> +<h1>Foo</h1> +<p>bar +baz</p> +</blockquote> +. + +A block quote can contain some lazy and some non-lazy +continuation lines: + +. +> bar +baz +> foo +. +<blockquote> +<p>bar +baz +foo</p> +</blockquote> +. + +Laziness only applies to lines that are continuations of +paragraphs. Lines containing characters or indentation that indicate +block structure cannot be lazy. + +. +> foo +--- +. +<blockquote> +<p>foo</p> +</blockquote> +<hr /> +. + +. +> - foo +- bar +. +<blockquote> +<ul> +<li>foo</li> +</ul> +</blockquote> +<ul> +<li>bar</li> +</ul> +. + +. +> foo + bar +. +<blockquote> +<pre><code>foo +</code></pre> +</blockquote> +<pre><code>bar +</code></pre> +. + +. +> ``` +foo +``` +. +<blockquote> +<pre><code></code></pre> +</blockquote> +<p>foo</p> +<pre><code></code></pre> +. + +A block quote can be empty: + +. +> +. +<blockquote> +</blockquote> +. + +. +> +> +> +. +<blockquote> +</blockquote> +. + +A block quote can have initial or final blank lines: + +. +> +> foo +> +. +<blockquote> +<p>foo</p> +</blockquote> +. + +A blank line always separates block quotes: + +. +> foo + +> bar +. +<blockquote> +<p>foo</p> +</blockquote> +<blockquote> +<p>bar</p> +</blockquote> +. + +(Most current Markdown implementations, including John Gruber's +original `Markdown.pl`, will parse this example as a single block quote +with two paragraphs. But it seems better to allow the author to decide +whether two block quotes or one are wanted.) + +Consecutiveness means that if we put these block quotes together, +we get a single block quote: + +. +> foo +> bar +. +<blockquote> +<p>foo +bar</p> +</blockquote> +. + +To get a block quote with two paragraphs, use: + +. +> foo +> +> bar +. +<blockquote> +<p>foo</p> +<p>bar</p> +</blockquote> +. + +Block quotes can interrupt paragraphs: + +. +foo +> bar +. +<p>foo</p> +<blockquote> +<p>bar</p> +</blockquote> +. + +In general, blank lines are not needed before or after block +quotes: + +. +> aaa +*** +> bbb +. +<blockquote> +<p>aaa</p> +</blockquote> +<hr /> +<blockquote> +<p>bbb</p> +</blockquote> +. + +However, because of laziness, a blank line is needed between +a block quote and a following paragraph: + +. +> bar +baz +. +<blockquote> +<p>bar +baz</p> +</blockquote> +. + +. +> bar + +baz +. +<blockquote> +<p>bar</p> +</blockquote> +<p>baz</p> +. + +. +> bar +> +baz +. +<blockquote> +<p>bar</p> +</blockquote> +<p>baz</p> +. + +It is a consequence of the Laziness rule that any number +of initial `>`s may be omitted on a continuation line of a +nested block quote: + +. +> > > foo +bar +. +<blockquote> +<blockquote> +<blockquote> +<p>foo +bar</p> +</blockquote> +</blockquote> +</blockquote> +. + +. +>>> foo +> bar +>>baz +. +<blockquote> +<blockquote> +<blockquote> +<p>foo +bar +baz</p> +</blockquote> +</blockquote> +</blockquote> +. + +When including an indented code block in a block quote, +remember that the [block quote marker](#block-quote-marker) includes +both the `>` and a following space. So *five spaces* are needed after +the `>`: + +. +> code + +> not code +. +<blockquote> +<pre><code>code +</code></pre> +</blockquote> +<blockquote> +<p>not code</p> +</blockquote> +. + + +## List items + +A [list marker](#list-marker) <a id="list-marker"></a> is a +[bullet list marker](#bullet-list-marker) or an [ordered list +marker](#ordered-list-marker). + +A [bullet list marker](#bullet-list-marker) <a id="bullet-list-marker"></a> +is a `-`, `+`, or `*` character. + +An [ordered list marker](#ordered-list-marker) <a id="ordered-list-marker"></a> +is a sequence of one of more digits (`0-9`), followed by either a +`.` character or a `)` character. + +The following rules define [list items](#list-item): + +1. **Basic case.** If a sequence of lines *Ls* constitute a sequence of + blocks *Bs* starting with a non-space character and not separated + from each other by more than one blank line, and *M* is a list + marker *M* of width *W* followed by 0 < *N* < 5 spaces, then the result + of prepending *M* and the following spaces to the first line of + *Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a + list item with *Bs* as its contents. The type of the list item + (bullet or ordered) is determined by the type of its list marker. + If the list item is ordered, then it is also assigned a start + number, based on the ordered list marker. + +For example, let *Ls* be the lines + +. +A paragraph +with two lines. + + indented code + +> A block quote. +. +<p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote> +. + +And let *M* be the marker `1.`, and *N* = 2. Then rule #1 says +that the following is an ordered list item with start number 1, +and the same contents as *Ls*: + +. +1. A paragraph + with two lines. + + indented code + + > A block quote. +. +<ol> +<li><p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote></li> +</ol> +. + +The most important thing to notice is that the position of +the text after the list marker determines how much indentation +is needed in subsequent blocks in the list item. If the list +marker takes up two spaces, and there are three spaces between +the list marker and the next nonspace character, then blocks +must be indented five spaces in order to fall under the list +item. + +Here are some examples showing how far content must be indented to be +put under the list item: + +. +- one + + two +. +<ul> +<li>one</li> +</ul> +<p>two</p> +. + +. +- one + + two +. +<ul> +<li><p>one</p> +<p>two</p></li> +</ul> +. + +. + - one + + two +. +<ul> +<li>one</li> +</ul> +<pre><code> two +</code></pre> +. + +. + - one + + two +. +<ul> +<li><p>one</p> +<p>two</p></li> +</ul> +. + +It is tempting to think of this in terms of columns: the continuation +blocks must be indented at least to the column of the first nonspace +character after the list marker. However, that is not quite right. +The spaces after the list marker determine how much relative indentation +is needed. Which column this indentation reaches will depend on +how the list item is embedded in other constructions, as shown by +this example: + +. + > > 1. one +>> +>> two +. +<blockquote> +<blockquote> +<ol> +<li><p>one</p> +<p>two</p></li> +</ol> +</blockquote> +</blockquote> +. + +Here `two` occurs in the same column as the list marker `1.`, +but is actually contained in the list item, because there is +sufficent indentation after the last containing blockquote marker. + +The converse is also possible. In the following example, the word `two` +occurs far to the right of the initial text of the list item, `one`, but +it is not considered part of the list item, because it is not indented +far enough past the blockquote marker: + +. +>>- one +>> + > > two +. +<blockquote> +<blockquote> +<ul> +<li>one</li> +</ul> +<p>two</p> +</blockquote> +</blockquote> +. + +A list item may not contain blocks that are separated by more than +one blank line. Thus, two blank lines will end a list, unless the +two blanks are contained in a [fenced code block](#fenced-code-block). + +. +- foo + + bar + +- foo + + + bar + +- ``` + foo + + + bar + ``` +. +<ul> +<li><p>foo</p> +<p>bar</p></li> +<li><p>foo</p></li> +</ul> +<p>bar</p> +<ul> +<li><pre><code>foo + + +bar +</code></pre></li> +</ul> +. + +A list item may contain any kind of block: + +. +1. foo + + ``` + bar + ``` + + baz + + > bam +. +<ol> +<li><p>foo</p> +<pre><code>bar +</code></pre> +<p>baz</p> +<blockquote> +<p>bam</p> +</blockquote></li> +</ol> +. + +2. **Item starting with indented code.** If a sequence of lines *Ls* + constitute a sequence of blocks *Bs* starting with an indented code + block and not separated from each other by more than one blank line, + and *M* is a list marker *M* of width *W* followed by + one space, then the result of prepending *M* and the following + space to the first line of *Ls*, and indenting subsequent lines of + *Ls* by *W + 1* spaces, is a list item with *Bs* as its contents. + If a line is empty, then it need not be indented. The type of the + list item (bullet or ordered) is determined by the type of its list + marker. If the list item is ordered, then it is also assigned a + start number, based on the ordered list marker. + +An indented code block will have to be indented four spaces beyond +the edge of the region where text will be included in the list item. +In the following case that is 6 spaces: + +. +- foo + + bar +. +<ul> +<li><p>foo</p> +<pre><code>bar +</code></pre></li> +</ul> +. + +And in this case it is 11 spaces: + +. + 10. foo + + bar +. +<ol start="10"> +<li><p>foo</p> +<pre><code>bar +</code></pre></li> +</ol> +. + +If the *first* block in the list item is an indented code block, +then by rule #2, the contents must be indented *one* space after the +list marker: + +. + indented code + +paragraph + + more code +. +<pre><code>indented code +</code></pre> +<p>paragraph</p> +<pre><code>more code +</code></pre> +. + +. +1. indented code + + paragraph + + more code +. +<ol> +<li><pre><code>indented code +</code></pre> +<p>paragraph</p> +<pre><code>more code +</code></pre></li> +</ol> +. + +Note that an additional space indent is interpreted as space +inside the code block: + +. +1. indented code + + paragraph + + more code +. +<ol> +<li><pre><code> indented code +</code></pre> +<p>paragraph</p> +<pre><code>more code +</code></pre></li> +</ol> +. + +Note that rules #1 and #2 only apply to two cases: (a) cases +in which the lines to be included in a list item begin with a nonspace +character, and (b) cases in which they begin with an indented code +block. In a case like the following, where the first block begins with +a three-space indent, the rules do not allow us to form a list item by +indenting the whole thing and prepending a list marker: + +. + foo + +bar +. +<p>foo</p> +<p>bar</p> +. + +. +- foo + + bar +. +<ul> +<li>foo</li> +</ul> +<p>bar</p> +. + +This is not a significant restriction, because when a block begins +with 1-3 spaces indent, the indentation can always be removed without +a change in interpretation, allowing rule #1 to be applied. So, in +the above case: + +. +- foo + + bar +. +<ul> +<li><p>foo</p> +<p>bar</p></li> +</ul> +. + + +3. **Indentation.** If a sequence of lines *Ls* constitutes a list item + according to rule #1 or #2, then the result of indenting each line + of *L* by 1-3 spaces (the same for each line) also constitutes a + list item with the same contents and attributes. If a line is + empty, then it need not be indented. + +Indented one space: + +. + 1. A paragraph + with two lines. + + indented code + + > A block quote. +. +<ol> +<li><p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote></li> +</ol> +. + +Indented two spaces: + +. + 1. A paragraph + with two lines. + + indented code + + > A block quote. +. +<ol> +<li><p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote></li> +</ol> +. + +Indented three spaces: + +. + 1. A paragraph + with two lines. + + indented code + + > A block quote. +. +<ol> +<li><p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote></li> +</ol> +. + +Four spaces indent gives a code block: + +. + 1. A paragraph + with two lines. + + indented code + + > A block quote. +. +<pre><code>1. A paragraph + with two lines. + + indented code + + > A block quote. +</code></pre> +. + + +4. **Laziness.** If a string of lines *Ls* constitute a [list + item](#list-item) with contents *Bs*, then the result of deleting + some or all of the indentation from one or more lines in which the + next non-space character after the indentation is + [paragraph continuation text](#paragraph-continuation-text) is a + list item with the same contents and attributes. + +Here is an example with lazy continuation lines: + +. + 1. A paragraph +with two lines. + + indented code + + > A block quote. +. +<ol> +<li><p>A paragraph +with two lines.</p> +<pre><code>indented code +</code></pre> +<blockquote> +<p>A block quote.</p> +</blockquote></li> +</ol> +. + +Indentation can be partially deleted: + +. + 1. A paragraph + with two lines. +. +<ol> +<li>A paragraph +with two lines.</li> +</ol> +. + +These examples show how laziness can work in nested structures: + +. +> 1. > Blockquote +continued here. +. +<blockquote> +<ol> +<li><blockquote> +<p>Blockquote +continued here.</p> +</blockquote></li> +</ol> +</blockquote> +. + +. +> 1. > Blockquote +> continued here. +. +<blockquote> +<ol> +<li><blockquote> +<p>Blockquote +continued here.</p> +</blockquote></li> +</ol> +</blockquote> +. + + +5. **That's all.** Nothing that is not counted as a list item by rules + #1--4 counts as a [list item](#list-item). + +The rules for sublists follow from the general rules above. A sublist +must be indented the same number of spaces a paragraph would need to be +in order to be included in the list item. + +So, in this case we need two spaces indent: + +. +- foo + - bar + - baz +. +<ul> +<li>foo +<ul> +<li>bar +<ul> +<li>baz</li> +</ul></li> +</ul></li> +</ul> +. + +One is not enough: + +. +- foo + - bar + - baz +. +<ul> +<li>foo</li> +<li>bar</li> +<li>baz</li> +</ul> +. + +Here we need four, because the list marker is wider: + +. +10) foo + - bar +. +<ol start="10"> +<li>foo +<ul> +<li>bar</li> +</ul></li> +</ol> +. + +Three is not enough: + +. +10) foo + - bar +. +<ol start="10"> +<li>foo</li> +</ol> +<ul> +<li>bar</li> +</ul> +. + +A list may be the first block in a list item: + +. +- - foo +. +<ul> +<li><ul> +<li>foo</li> +</ul></li> +</ul> +. + +. +1. - 2. foo +. +<ol> +<li><ul> +<li><ol start="2"> +<li>foo</li> +</ol></li> +</ul></li> +</ol> +. + +A list item may be empty: + +. +- foo +- +- bar +. +<ul> +<li>foo</li> +<li></li> +<li>bar</li> +</ul> +. + +. +- +. +<ul> +<li></li> +</ul> +. + +### Motivation + +John Gruber's Markdown spec says the following about list items: + +1. "List markers typically start at the left margin, but may be indented + by up to three spaces. List markers must be followed by one or more + spaces or a tab." + +2. "To make lists look nice, you can wrap items with hanging indents.... + But if you don't want to, you don't have to." + +3. "List items may consist of multiple paragraphs. Each subsequent + paragraph in a list item must be indented by either 4 spaces or one + tab." + +4. "It looks nice if you indent every line of the subsequent paragraphs, + but here again, Markdown will allow you to be lazy." + +5. "To put a blockquote within a list item, the blockquote's `>` + delimiters need to be indented." + +6. "To put a code block within a list item, the code block needs to be + indented twice — 8 spaces or two tabs." + +These rules specify that a paragraph under a list item must be indented +four spaces (presumably, from the left margin, rather than the start of +the list marker, but this is not said), and that code under a list item +must be indented eight spaces instead of the usual four. They also say +that a block quote must be indented, but not by how much; however, the +example given has four spaces indentation. Although nothing is said +about other kinds of block-level content, it is certainly reasonable to +infer that *all* block elements under a list item, including other +lists, must be indented four spaces. This principle has been called the +*four-space rule*. + +The four-space rule is clear and principled, and if the reference +implementation `Markdown.pl` had followed it, it probably would have +become the standard. However, `Markdown.pl` allowed paragraphs and +sublists to start with only two spaces indentation, at least on the +outer level. Worse, its behavior was inconsistent: a sublist of an +outer-level list needed two spaces indentation, but a sublist of this +sublist needed three spaces. It is not surprising, then, that different +implementations of Markdown have developed very different rules for +determining what comes under a list item. (Pandoc and python-Markdown, +for example, stuck with Gruber's syntax description and the four-space +rule, while discount, redcarpet, marked, PHP Markdown, and others +followed `Markdown.pl`'s behavior more closely.) + +Unfortunately, given the divergences between implementations, there +is no way to give a spec for list items that will be guaranteed not +to break any existing documents. However, the spec given here should +correctly handle lists formatted with either the four-space rule or +the more forgiving `Markdown.pl` behavior, provided they are laid out +in a way that is natural for a human to read. + +The strategy here is to let the width and indentation of the list marker +determine the indentation necessary for blocks to fall under the list +item, rather than having a fixed and arbitrary number. The writer can +think of the body of the list item as a unit which gets indented to the +right enough to fit the list marker (and any indentation on the list +marker). (The laziness rule, #4, then allows continuation lines to be +unindented if needed.) + +This rule is superior, we claim, to any rule requiring a fixed level of +indentation from the margin. The four-space rule is clear but +unnatural. It is quite unintuitive that + +``` markdown +- foo + + bar + + - baz +``` + +should be parsed as two lists with an intervening paragraph, + +``` html +<ul> +<li>foo</li> +</ul> +<p>bar</p> +<ul> +<li>baz</li> +</ul> +``` + +as the four-space rule demands, rather than a single list, + +``` html +<ul> +<li><p>foo</p> +<p>bar</p> +<ul> +<li>baz</li> +</ul></li> +</ul> +``` + +The choice of four spaces is arbitrary. It can be learned, but it is +not likely to be guessed, and it trips up beginners regularly. + +Would it help to adopt a two-space rule? The problem is that such +a rule, together with the rule allowing 1--3 spaces indentation of the +initial list marker, allows text that is indented *less than* the +original list marker to be included in the list item. For example, +`Markdown.pl` parses + +``` markdown + - one + + two +``` + +as a single list item, with `two` a continuation paragraph: + +``` html +<ul> +<li><p>one</p> +<p>two</p></li> +</ul> +``` + +and similarly + +``` markdown +> - one +> +> two +``` + +as + +``` html +<blockquote> +<ul> +<li><p>one</p> +<p>two</p></li> +</ul> +</blockquote> +``` + +This is extremely unintuitive. + +Rather than requiring a fixed indent from the margin, we could require +a fixed indent (say, two spaces, or even one space) from the list marker (which +may itself be indented). This proposal would remove the last anomaly +discussed. Unlike the spec presented above, it would count the following +as a list item with a subparagraph, even though the paragraph `bar` +is not indented as far as the first paragraph `foo`: + +``` markdown + 10. foo + + bar +``` + +Arguably this text does read like a list item with `bar` as a subparagraph, +which may count in favor of the proposal. However, on this proposal indented +code would have to be indented six spaces after the list marker. And this +would break a lot of existing Markdown, which has the pattern: + +``` markdown +1. foo + + indented code +``` + +where the code is indented eight spaces. The spec above, by contrast, will +parse this text as expected, since the code block's indentation is measured +from the beginning of `foo`. + +The one case that needs special treatment is a list item that *starts* +with indented code. How much indentation is required in that case, since +we don't have a "first paragraph" to measure from? Rule #2 simply stipulates +that in such cases, we require one space indentation from the list marker +(and then the normal four spaces for the indented code). This will match the +four-space rule in cases where the list marker plus its initial indentation +takes four spaces (a common case), but diverge in other cases. + +## Lists + +A [list](#list) <a id="list"></a> is a sequence of one or more +list items [of the same type](#of-the-same-type). The list items +may be separated by single [blank lines](#blank-line), but two +blank lines end all containing lists. + +Two list items are [of the same type](#of-the-same-type) +<a id="of-the-same-type"></a> if they begin with a [list +marker](#list-marker) of the same type. Two list markers are of the +same type if (a) they are bullet list markers using the same character +(`-`, `+`, or `*`) or (b) they are ordered list numbers with the same +delimiter (either `.` or `)`). + +A list is an [ordered list](#ordered-list) <a id="ordered-list"></a> +if its constituent list items begin with +[ordered list markers](#ordered-list-marker), and a [bullet +list](#bullet-list) <a id="bullet-list"></a> if its constituent list +items begin with [bullet list markers](#bullet-list-marker). + +The [start number](#start-number) <a id="start-number"></a> +of an [ordered list](#ordered-list) is determined by the list number of +its initial list item. The numbers of subsequent list items are +disregarded. + +A list is [loose](#loose) if it any of its constituent list items are +separated by blank lines, or if any of its constituent list items +directly contain two block-level elements with a blank line between +them. Otherwise a list is [tight](#tight). (The difference in HTML output +is that paragraphs in a loose with are wrapped in `<p>` tags, while +paragraphs in a tight list are not.) + +Changing the bullet or ordered list delimiter starts a new list: + +. +- foo +- bar ++ baz +. +<ul> +<li>foo</li> +<li>bar</li> +</ul> +<ul> +<li>baz</li> +</ul> +. + +. +1. foo +2. bar +3) baz +. +<ol> +<li>foo</li> +<li>bar</li> +</ol> +<ol start="3"> +<li>baz</li> +</ol> +. + +There can be blank lines between items, but two blank lines end +a list: + +. +- foo + +- bar + + +- baz +. +<ul> +<li><p>foo</p></li> +<li><p>bar</p></li> +</ul> +<ul> +<li>baz</li> +</ul> +. + +As illustrated above in the section on [list items](#list-item), +two blank lines between blocks *within* a list item will also end a +list: + +. +- foo + + + bar +- baz +. +<ul> +<li>foo</li> +</ul> +<p>bar</p> +<ul> +<li>baz</li> +</ul> +. + +Indeed, two blank lines will end *all* containing lists: + +. +- foo + - bar + - baz + + + bim +. +<ul> +<li>foo +<ul> +<li>bar +<ul> +<li>baz</li> +</ul></li> +</ul></li> +</ul> +<pre><code> bim +</code></pre> +. + +Thus, two blank lines can be used to separate consecutive lists of +the same type, or to separate a list from an indented code block +that would otherwise be parsed as a subparagraph of the final list +item: + +. +- foo +- bar + + +- baz +- bim +. +<ul> +<li>foo</li> +<li>bar</li> +</ul> +<ul> +<li>baz</li> +<li>bim</li> +</ul> +. + +. +- foo + + notcode + +- foo + + + code +. +<ul> +<li><p>foo</p> +<p>notcode</p></li> +<li><p>foo</p></li> +</ul> +<pre><code>code +</code></pre> +. + +List items need not be indented to the same level. The following +list items will be treated as items at the same list level, +since none is indented enough to belong to the previous list +item: + +. +- a + - b + - c + - d + - e + - f +- g +. +<ul> +<li>a</li> +<li>b</li> +<li>c</li> +<li>d</li> +<li>e</li> +<li>f</li> +<li>g</li> +</ul> +. + +This is a loose list, because there is a blank line between +two of the list items: + +. +- a +- b + +- c +. +<ul> +<li><p>a</p></li> +<li><p>b</p></li> +<li><p>c</p></li> +</ul> +. + +So is this, with a empty second item: + +. +* a +* + +* c +. +<ul> +<li><p>a</p></li> +<li></li> +<li><p>c</p></li> +</ul> +. + +These are loose lists, even though there is no space between the items, +because one of the items directly contains two block-level elements +with a blank line between them: + +. +- a +- b + + c +- d +. +<ul> +<li><p>a</p></li> +<li><p>b</p> +<p>c</p></li> +<li><p>d</p></li> +</ul> +. + +. +- a +- b + + [ref]: /url +- d +. +<ul> +<li><p>a</p></li> +<li><p>b</p></li> +<li><p>d</p></li> +</ul> +. + +This is a tight list, because the blank lines are in a code block: + +. +- a +- ``` + b + + + ``` +- c +. +<ul> +<li>a</li> +<li><pre><code>b + + +</code></pre></li> +<li>c</li> +</ul> +. + +This is a tight list, because the blank line is between two +paragraphs of a sublist. So the inner list is loose while +the other list is tight: + +. +- a + - b + + c +- d +. +<ul> +<li>a +<ul> +<li><p>b</p> +<p>c</p></li> +</ul></li> +<li>d</li> +</ul> +. + +This is a tight list, because the blank line is inside the +block quote: + +. +* a + > b + > +* c +. +<ul> +<li>a +<blockquote> +<p>b</p> +</blockquote></li> +<li>c</li> +</ul> +. + +This list is tight, because the consecutive block elements +are not separated by blank lines: + +. +- a + > b + ``` + c + ``` +- d +. +<ul> +<li>a +<blockquote> +<p>b</p> +</blockquote> +<pre><code>c +</code></pre></li> +<li>d</li> +</ul> +. + +A single-paragraph list is tight: + +. +- a +. +<ul> +<li>a</li> +</ul> +. + +. +- a + - b +. +<ul> +<li>a +<ul> +<li>b</li> +</ul></li> +</ul> +. + +Here the outer list is loose, the inner list tight: + +. +* foo + * bar + + baz +. +<ul> +<li><p>foo</p> +<ul> +<li>bar</li> +</ul> +<p>baz</p></li> +</ul> +. + +. +- a + - b + - c + +- d + - e + - f +. +<ul> +<li><p>a</p> +<ul> +<li>b</li> +<li>c</li> +</ul></li> +<li><p>d</p> +<ul> +<li>e</li> +<li>f</li> +</ul></li> +</ul> +. + +# Inlines + +Inlines are parsed sequentially from the beginning of the character +stream to the end (left to right, in left-to-right languages). +Thus, for example, in + +. +`hi`lo` +. +<p><code>hi</code>lo`</p> +. + +`hi` is parsed as code, leaving the backtick at the end as a literal +backtick. + +## Backslash escapes + +Any ASCII punctuation character may be backslash-escaped: + +. +\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~ +. +<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p> +. + +Backslashes before other characters are treated as literal +backslashes: + +. +\→\A\a\ \3\φ\« +. +<p>\ \A\a\ \3\φ\«</p> +. + +Escaped characters are treated as regular characters and do +not have their usual Markdown meanings: + +. +\*not emphasized* +\<br/> not a tag +\[not a link](/foo) +\`not code` +1\. not a list +\* not a list +\# not a header +\[foo]: /url "not a reference" +. +<p>*not emphasized* +<br/> not a tag +[not a link](/foo) +`not code` +1. not a list +* not a list +# not a header +[foo]: /url "not a reference"</p> +. + +If a backslash is itself escaped, the following character is not: + +. +\\*emphasis* +. +<p>\<em>emphasis</em></p> +. + +A backslash at the end of the line is a hard line break: + +. +foo\ +bar +. +<p>foo<br /> +bar</p> +. + +Backslash escapes do not work in code blocks, code spans, autolinks, or +raw HTML: + +. +`` \[\` `` +. +<p><code>\[\`</code></p> +. + +. + \[\] +. +<pre><code>\[\] +</code></pre> +. + +. +~~~ +\[\] +~~~ +. +<pre><code>\[\] +</code></pre> +. + +. +<http://google.com?find=\*> +. +<p><a href="http://google.com?find=%5C*">http://google.com?find=\*</a></p> +. + +. +<a href="/bar\/)"> +. +<p><a href="/bar\/)"></p> +. + +But they work in all other contexts, including URLs and link titles, +link references, and info strings in [fenced code +blocks](#fenced-code-block): + +. +[foo](/bar\* "ti\*tle") +. +<p><a href="/bar*" title="ti*tle">foo</a></p> +. + +. +[foo] + +[foo]: /bar\* "ti\*tle" +. +<p><a href="/bar*" title="ti*tle">foo</a></p> +. + +. +``` foo\+bar +foo +``` +. +<pre><code class="language-foo+bar">foo +</code></pre> +. + + +## Entities + +With the goal of making this standard as HTML-agnostic as possible, all HTML valid HTML Entities in any +context are recognized as such and converted into their actual values (i.e. the UTF8 characters representing +the entity itself) before they are stored in the AST. + +This allows implementations that target HTML output to trivially escape the entities when generating HTML, +and simplifies the job of implementations targetting other languages, as these will only need to handle the +UTF8 chars and need not be HTML-entity aware. + +[Named entities](#name-entities) <a id="named-entities"></a> consist of `&` ++ any of the valid HTML5 entity names + `;`. The [following document](http://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json) +is used as an authoritative source of the valid entity names and their corresponding codepoints. + +Conforming implementations that target Markdown don't need to generate entities for all the valid +named entities that exist, with the exception of `"` (`"`), `&` (`&`), `<` (`<`) and `>` (`>`), +which always need to be written as entities for security reasons. + +. + & © Æ Ď ¾ ℋ ⅆ ∲ +. +<p> & © Æ Ď ¾ ℋ ⅆ ∲</p> +. + +[Decimal entities](#decimal-entities) <a id="decimal-entities"></a> +consist of `&#` + a string of 1--8 arabic digits + `;`. Again, these entities need to be recognised +and tranformed into their corresponding UTF8 codepoints. Invalid Unicode codepoints will be written +as the "unknown codepoint" character (`0xFFFD`) + +. +# Ӓ Ϡ � +. +<p># Ӓ Ϡ �</p> +. + +[Hexadecimal entities](#hexadecimal-entities) <a id="hexadecimal-entities"></a> +consist of `&#` + either `X` or `x` + a string of 1-8 hexadecimal digits ++ `;`. They will also be parsed and turned into their corresponding UTF8 values in the AST. + +. +" ആ ಫ +. +<p>" ആ ಫ</p> +. + +Here are some nonentities: + +. +  &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?; +. +<p>&nbsp &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;</p> +. + +Although HTML5 does accept some entities without a trailing semicolon +(such as `©`), these are not recognized as entities here, because it makes the grammar too ambiguous: + +. +© +. +<p>&copy</p> +. + +Strings that are not on the list of HTML5 named entities are not recognized as entities either: + +. +&MadeUpEntity; +. +<p>&MadeUpEntity;</p> +. + +Entities are recognized in any context besides code spans or +code blocks, including raw HTML, URLs, [link titles](#link-title), and +[fenced code block](#fenced-code-block) info strings: + +. +<a href="öö.html"> +. +<p><a href="öö.html"></p> +. + +. +[foo](/föö "föö") +. +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> +. + +. +[foo] + +[foo]: /föö "föö" +. +<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p> +. + +. +``` föö +foo +``` +. +<pre><code class="language-föö">foo +</code></pre> +. + +Entities are treated as literal text in code spans and code blocks: + +. +`föö` +. +<p><code>f&ouml;&ouml;</code></p> +. + +. + föfö +. +<pre><code>f&ouml;f&ouml; +</code></pre> +. + +## Code span + +A [backtick string](#backtick-string) <a id="backtick-string"></a> +is a string of one or more backtick characters (`` ` ``) that is neither +preceded nor followed by a backtick. + +A code span begins with a backtick string and ends with a backtick +string of equal length. The contents of the code span are the +characters between the two backtick strings, with leading and trailing +spaces and newlines removed, and consecutive spaces and newlines +collapsed to single spaces. + +This is a simple code span: + +. +`foo` +. +<p><code>foo</code></p> +. + +Here two backticks are used, because the code contains a backtick. +This example also illustrates stripping of leading and trailing spaces: + +. +`` foo ` bar `` +. +<p><code>foo ` bar</code></p> +. + +This example shows the motivation for stripping leading and trailing +spaces: + +. +` `` ` +. +<p><code>``</code></p> +. + +Newlines are treated like spaces: + +. +`` +foo +`` +. +<p><code>foo</code></p> +. + +Interior spaces and newlines are collapsed into single spaces, just +as they would be by a browser: + +. +`foo bar + baz` +. +<p><code>foo bar baz</code></p> +. + +Q: Why not just leave the spaces, since browsers will collapse them +anyway? A: Because we might be targeting a non-HTML format, and we +shouldn't rely on HTML-specific rendering assumptions. + +(Existing implementations differ in their treatment of internal +spaces and newlines. Some, including `Markdown.pl` and +`showdown`, convert an internal newline into a `<br />` tag. +But this makes things difficult for those who like to hard-wrap +their paragraphs, since a line break in the midst of a code +span will cause an unintended line break in the output. Others +just leave internal spaces as they are, which is fine if only +HTML is being targeted.) + +. +`foo `` bar` +. +<p><code>foo `` bar</code></p> +. + +Note that backslash escapes do not work in code spans. All backslashes +are treated literally: + +. +`foo\`bar` +. +<p><code>foo\</code>bar`</p> +. + +Backslash escapes are never needed, because one can always choose a +string of *n* backtick characters as delimiters, where the code does +not contain any strings of exactly *n* backtick characters. + +Code span backticks have higher precedence than any other inline +constructs except HTML tags and autolinks. Thus, for example, this is +not parsed as emphasized text, since the second `*` is part of a code +span: + +. +*foo`*` +. +<p>*foo<code>*</code></p> +. + +And this is not parsed as a link: + +. +[not a `link](/foo`) +. +<p>[not a <code>link](/foo</code>)</p> +. + +But this is a link: + +. +<http://foo.bar.`baz>` +. +<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p> +. + +And this is an HTML tag: + +. +<a href="`">` +. +<p><a href="`">`</p> +. + +When a backtick string is not closed by a matching backtick string, +we just have literal backticks: + +. +```foo`` +. +<p>```foo``</p> +. + +. +`foo +. +<p>`foo</p> +. + +## Emphasis and strong emphasis + +John Gruber's original [Markdown syntax +description](http://daringfireball.net/projects/markdown/syntax#em) says: + +> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of +> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML +> `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML `<strong>` +> tag. + +This is enough for most users, but these rules leave much undecided, +especially when it comes to nested emphasis. The original +`Markdown.pl` test suite makes it clear that triple `***` and +`___` delimiters can be used for strong emphasis, and most +implementations have also allowed the following patterns: + +``` markdown +***strong emph*** +***strong** in emph* +***emph* in strong** +**in strong *emph*** +*in emph **strong*** +``` + +The following patterns are less widely supported, but the intent +is clear and they are useful (especially in contexts like bibliography +entries): + +``` markdown +*emph *with emph* in it* +**strong **with strong** in it** +``` + +Many implementations have also restricted intraword emphasis to +the `*` forms, to avoid unwanted emphasis in words containing +internal underscores. (It is best practice to put these in code +spans, but users often do not.) + +``` markdown +internal emphasis: foo*bar*baz +no emphasis: foo_bar_baz +``` + +The following rules capture all of these patterns, while allowing +for efficient parsing strategies that do not backtrack: + +1. A single `*` character [can open emphasis](#can-open-emphasis) + <a id="can-open-emphasis"></a> iff + + (a) it is not part of a sequence of four or more unescaped `*`s, + (b) it is not followed by whitespace, and + (c) either it is not followed by a `*` character or it is + followed immediately by strong emphasis. + +2. A single `_` character [can open emphasis](#can-open-emphasis) iff + + (a) it is not part of a sequence of four or more unescaped `_`s, + (b) it is not followed by whitespace, + (c) it is not preceded by an ASCII alphanumeric character, and + (d) either it is not followed by a `_` character or it is + followed immediately by strong emphasis. + +3. A single `*` character [can close emphasis](#can-close-emphasis) + <a id="can-close-emphasis"></a> iff + + (a) it is not part of a sequence of four or more unescaped `*`s, and + (b) it is not preceded by whitespace. + +4. A single `_` character [can close emphasis](#can-close-emphasis) iff + + (a) it is not part of a sequence of four or more unescaped `_`s, + (b) it is not preceded by whitespace, and + (c) it is not followed by an ASCII alphanumeric character. + +5. A double `**` [can open strong emphasis](#can-open-strong-emphasis) + <a id="can-open-strong-emphasis" ></a> iff + + (a) it is not part of a sequence of four or more unescaped `*`s, + (b) it is not followed by whitespace, and + (c) either it is not followed by a `*` character or it is + followed immediately by emphasis. + +6. A double `__` [can open strong emphasis](#can-open-strong-emphasis) + iff + + (a) it is not part of a sequence of four or more unescaped `_`s, + (b) it is not followed by whitespace, and + (c) it is not preceded by an ASCII alphanumeric character, and + (d) either it is not followed by a `_` character or it is + followed immediately by emphasis. + +7. A double `**` [can close strong emphasis](#can-close-strong-emphasis) + <a id="can-close-strong-emphasis" ></a> iff + + (a) it is not part of a sequence of four or more unescaped `*`s, and + (b) it is not preceded by whitespace. + +8. A double `__` [can close strong emphasis](#can-close-strong-emphasis) + iff + + (a) it is not part of a sequence of four or more unescaped `_`s, + (b) it is not preceded by whitespace, and + (c) it is not followed by an ASCII alphanumeric character. + +9. Emphasis begins with a delimiter that [can open + emphasis](#can-open-emphasis) and includes inlines parsed + sequentially until a delimiter that [can close + emphasis](#can-close-emphasis), and that uses the same + character (`_` or `*`) as the opening delimiter, is reached. + +10. Strong emphasis begins with a delimiter that [can open strong + emphasis](#can-open-strong-emphasis) and includes inlines parsed + sequentially until a delimiter that [can close strong + emphasis](#can-close-strong-emphasis), and that uses the + same character (`_` or `*`) as the opening delimiter, is reached. + +These rules can be illustrated through a series of examples. + +Simple emphasis: + +. +*foo bar* +. +<p><em>foo bar</em></p> +. + +. +_foo bar_ +. +<p><em>foo bar</em></p> +. + +Simple strong emphasis: + +. +**foo bar** +. +<p><strong>foo bar</strong></p> +. + +. +__foo bar__ +. +<p><strong>foo bar</strong></p> +. + +Emphasis can continue over line breaks: + +. +*foo +bar* +. +<p><em>foo +bar</em></p> +. + +. +_foo +bar_ +. +<p><em>foo +bar</em></p> +. + +. +**foo +bar** +. +<p><strong>foo +bar</strong></p> +. + +. +__foo +bar__ +. +<p><strong>foo +bar</strong></p> +. + +Emphasis can contain other inline constructs: + +. +*foo [bar](/url)* +. +<p><em>foo <a href="/url">bar</a></em></p> +. + +. +_foo [bar](/url)_ +. +<p><em>foo <a href="/url">bar</a></em></p> +. + +. +**foo [bar](/url)** +. +<p><strong>foo <a href="/url">bar</a></strong></p> +. + +. +__foo [bar](/url)__ +. +<p><strong>foo <a href="/url">bar</a></strong></p> +. + +Symbols contained in other inline constructs will not +close emphasis: + +. +*foo [bar*](/url) +. +<p>*foo <a href="/url">bar*</a></p> +. + +. +_foo [bar_](/url) +. +<p>_foo <a href="/url">bar_</a></p> +. + +. +**<a href="**"> +. +<p>**<a href="**"></p> +. + +. +__<a href="__"> +. +<p>__<a href="__"></p> +. + +. +*a `*`* +. +<p><em>a <code>*</code></em></p> +. + +. +_a `_`_ +. +<p><em>a <code>_</code></em></p> +. + +. +**a<http://foo.bar?q=**> +. +<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p> +. + +. +__a<http://foo.bar?q=__> +. +<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p> +. + +This is not emphasis, because the opening delimiter is +followed by white space: + +. +and * foo bar* +. +<p>and * foo bar*</p> +. + +. +_ foo bar_ +. +<p>_ foo bar_</p> +. + +. +and ** foo bar** +. +<p>and ** foo bar**</p> +. + +. +__ foo bar__ +. +<p>__ foo bar__</p> +. + +This is not emphasis, because the closing delimiter is +preceded by white space: + +. +and *foo bar * +. +<p>and *foo bar *</p> +. + +. +and _foo bar _ +. +<p>and _foo bar _</p> +. + +. +and **foo bar ** +. +<p>and **foo bar **</p> +. + +. +and __foo bar __ +. +<p>and __foo bar __</p> +. + +The rules imply that a sequence of four or more unescaped `*` or +`_` characters will always be parsed as a literal string: + +. +****hi**** +. +<p>****hi****</p> +. + +. +_____hi_____ +. +<p>_____hi_____</p> +. + +. +Sign here: _________ +. +<p>Sign here: _________</p> +. + +The rules also imply that there can be no empty emphasis or strong +emphasis: + +. +** is not an empty emphasis +. +<p>** is not an empty emphasis</p> +. + +. +**** is not an empty strong emphasis +. +<p>**** is not an empty strong emphasis</p> +. + +To include `*` or `_` in emphasized sections, use backslash escapes +or code spans: + +. +*here is a \** +. +<p><em>here is a *</em></p> +. + +. +__this is a double underscore (`__`)__ +. +<p><strong>this is a double underscore (<code>__</code>)</strong></p> +. + +`*` delimiters allow intra-word emphasis; `_` delimiters do not: + +. +foo*bar*baz +. +<p>foo<em>bar</em>baz</p> +. + +. +foo_bar_baz +. +<p>foo_bar_baz</p> +. + +. +foo__bar__baz +. +<p>foo__bar__baz</p> +. + +. +_foo_bar_baz_ +. +<p><em>foo_bar_baz</em></p> +. + +. +11*15*32 +. +<p>11<em>15</em>32</p> +. + +. +11_15_32 +. +<p>11_15_32</p> +. + +Internal underscores will be ignored in underscore-delimited +emphasis: + +. +_foo_bar_baz_ +. +<p><em>foo_bar_baz</em></p> +. + +. +__foo__bar__baz__ +. +<p><strong>foo__bar__baz</strong></p> +. + +The rules are sufficient for the following nesting patterns: + +. +***foo bar*** +. +<p><strong><em>foo bar</em></strong></p> +. + +. +___foo bar___ +. +<p><strong><em>foo bar</em></strong></p> +. + +. +***foo** bar* +. +<p><em><strong>foo</strong> bar</em></p> +. + +. +___foo__ bar_ +. +<p><em><strong>foo</strong> bar</em></p> +. + +. +***foo* bar** +. +<p><strong><em>foo</em> bar</strong></p> +. + +. +___foo_ bar__ +. +<p><strong><em>foo</em> bar</strong></p> +. + +. +*foo **bar*** +. +<p><em>foo <strong>bar</strong></em></p> +. + +. +_foo __bar___ +. +<p><em>foo <strong>bar</strong></em></p> +. + +. +**foo *bar*** +. +<p><strong>foo <em>bar</em></strong></p> +. + +. +__foo _bar___ +. +<p><strong>foo <em>bar</em></strong></p> +. + +. +*foo **bar*** +. +<p><em>foo <strong>bar</strong></em></p> +. + +. +_foo __bar___ +. +<p><em>foo <strong>bar</strong></em></p> +. + +. +*foo *bar* baz* +. +<p><em>foo <em>bar</em> baz</em></p> +. + +. +_foo _bar_ baz_ +. +<p><em>foo <em>bar</em> baz</em></p> +. + +. +**foo **bar** baz** +. +<p><strong>foo <strong>bar</strong> baz</strong></p> +. + +. +__foo __bar__ baz__ +. +<p><strong>foo <strong>bar</strong> baz</strong></p> +. + +. +*foo **bar** baz* +. +<p><em>foo <strong>bar</strong> baz</em></p> +. + +. +_foo __bar__ baz_ +. +<p><em>foo <strong>bar</strong> baz</em></p> +. + +. +**foo *bar* baz** +. +<p><strong>foo <em>bar</em> baz</strong></p> +. + +. +__foo _bar_ baz__ +. +<p><strong>foo <em>bar</em> baz</strong></p> +. + +Note that you cannot nest emphasis directly inside emphasis +using the same delimeter, or strong emphasis directly inside +strong emphasis: + +. +**foo** +. +<p><strong>foo</strong></p> +. + +. +****foo**** +. +<p>****foo****</p> +. + +For these nestings, you need to switch delimiters: + +. +*_foo_* +. +<p><em><em>foo</em></em></p> +. + +. +**__foo__** +. +<p><strong><strong>foo</strong></strong></p> +. + +Note that a `*` followed by a `*` can close emphasis, and +a `**` followed by a `*` can close strong emphasis (and +similarly for `_` and `__`): + +. +*foo** +. +<p><em>foo</em>*</p> +. + +. +*foo *bar** +. +<p><em>foo <em>bar</em></em></p> +. + +. +**foo*** +. +<p><strong>foo</strong>*</p> +. + +. +***foo* bar*** +. +<p><strong><em>foo</em> bar</strong>*</p> +. + +. +***foo** bar*** +. +<p><em><strong>foo</strong> bar</em>**</p> +. + +The following contains no strong emphasis, because the opening +delimiter is closed by the first `*` before `bar`: + +. +*foo**bar*** +. +<p><em>foo</em><em>bar</em>**</p> +. + +However, a string of four or more `****` can never close emphasis: + +. +*foo**** +. +<p>*foo****</p> +. + +Note that there are some asymmetries here: + +. +*foo** + +**foo* +. +<p><em>foo</em>*</p> +<p>**foo*</p> +. + +. +*foo *bar** + +**foo* bar* +. +<p><em>foo <em>bar</em></em></p> +<p>**foo* bar*</p> +. + +More cases with mismatched delimiters: + +. +**foo* bar* +. +<p>**foo* bar*</p> +. + +. +*bar*** +. +<p><em>bar</em>**</p> +. + +. +***foo* +. +<p>***foo*</p> +. + +. +**bar*** +. +<p><strong>bar</strong>*</p> +. + +. +***foo** +. +<p>***foo**</p> +. + +. +***foo *bar* +. +<p>***foo <em>bar</em></p> +. + +## Links + +A link contains a [link label](#link-label) (the visible text), +a [destination](#destination) (the URI that is the link destination), +and optionally a [link title](#link-title). There are two basic kinds +of links in Markdown. In [inline links](#inline-links) the destination +and title are given immediately after the label. In [reference +links](#reference-links) the destination and title are defined elsewhere +in the document. + +A [link label](#link-label) <a id="link-label"></a> consists of + +- an opening `[`, followed by +- zero or more backtick code spans, autolinks, HTML tags, link labels, + backslash-escaped ASCII punctuation characters, or non-`]` characters, + followed by +- a closing `]`. + +These rules are motivated by the following intuitive ideas: + +- A link label is a container for inline elements. +- The square brackets bind more tightly than emphasis markers, + but less tightly than `<>` or `` ` ``. +- Link labels may contain material in matching square brackets. + +A [link destination](#link-destination) <a id="link-destination"></a> +consists of either + +- a sequence of zero or more characters between an opening `<` and a + closing `>` that contains no line breaks or unescaped `<` or `>` + characters, or + +- a nonempty sequence of characters that does not include + ASCII space or control characters, and includes parentheses + only if (a) they are backslash-escaped or (b) they are part of + a balanced pair of unescaped parentheses that is not itself + inside a balanced pair of unescaped paretheses. + +A [link title](#link-title) <a id="link-title"></a> consists of either + +- a sequence of zero or more characters between straight double-quote + characters (`"`), including a `"` character only if it is + backslash-escaped, or + +- a sequence of zero or more characters between straight single-quote + characters (`'`), including a `'` character only if it is + backslash-escaped, or + +- a sequence of zero or more characters between matching parentheses + (`(...)`), including a `)` character only if it is backslash-escaped. + +An [inline link](#inline-link) <a id="inline-link"></a> +consists of a [link label](#link-label) followed immediately +by a left parenthesis `(`, optional whitespace, +an optional [link destination](#link-destination), +an optional [link title](#link-title) separated from the link +destination by whitespace, optional whitespace, and a right +parenthesis `)`. The link's text consists of the label (excluding +the enclosing square brackets) parsed as inlines. The link's +URI consists of the link destination, excluding enclosing `<...>` if +present, with backslash-escapes in effect as described above. The +link's title consists of the link title, excluding its enclosing +delimiters, with backslash-escapes in effect as described above. + +Here is a simple inline link: + +. +[link](/uri "title") +. +<p><a href="/uri" title="title">link</a></p> +. + +The title may be omitted: + +. +[link](/uri) +. +<p><a href="/uri">link</a></p> +. + +Both the title and the destination may be omitted: + +. +[link]() +. +<p><a href="">link</a></p> +. + +. +[link](<>) +. +<p><a href="">link</a></p> +. + + +If the destination contains spaces, it must be enclosed in pointy +braces: + +. +[link](/my uri) +. +<p>[link](/my uri)</p> +. + +. +[link](</my uri>) +. +<p><a href="/my%20uri">link</a></p> +. + +The destination cannot contain line breaks, even with pointy braces: + +. +[link](foo +bar) +. +<p>[link](foo +bar)</p> +. + +One level of balanced parentheses is allowed without escaping: + +. +[link]((foo)and(bar)) +. +<p><a href="(foo)and(bar)">link</a></p> +. + +However, if you have parentheses within parentheses, you need to escape +or use the `<...>` form: + +. +[link](foo(and(bar))) +. +<p>[link](foo(and(bar)))</p> +. + +. +[link](foo(and\(bar\))) +. +<p><a href="foo(and(bar))">link</a></p> +. + +. +[link](<foo(and(bar))>) +. +<p><a href="foo(and(bar))">link</a></p> +. + +Parentheses and other symbols can also be escaped, as usual +in Markdown: + +. +[link](foo\)\:) +. +<p><a href="foo):">link</a></p> +. + +URL-escaping and should be left alone inside the destination, as all URL-escaped characters +are also valid URL characters. HTML entities in the destination will be parsed into their UTF8 +codepoints, as usual, and optionally URL-escaped when written as HTML. + +. +[link](foo%20bä) +. +<p><a href="foo%20b%C3%A4">link</a></p> +. + +Note that, because titles can often be parsed as destinations, +if you try to omit the destination and keep the title, you'll +get unexpected results: + +. +[link]("title") +. +<p><a href="%22title%22">link</a></p> +. + +Titles may be in single quotes, double quotes, or parentheses: + +. +[link](/url "title") +[link](/url 'title') +[link](/url (title)) +. +<p><a href="/url" title="title">link</a> +<a href="/url" title="title">link</a> +<a href="/url" title="title">link</a></p> +. + +Backslash escapes and entities may be used in titles: + +. +[link](/url "title \""") +. +<p><a href="/url" title="title """>link</a></p> +. + +Nested balanced quotes are not allowed without escaping: + +. +[link](/url "title "and" title") +. +<p>[link](/url "title "and" title")</p> +. + +But it is easy to work around this by using a different quote type: + +. +[link](/url 'title "and" title') +. +<p><a href="/url" title="title "and" title">link</a></p> +. + +(Note: `Markdown.pl` did allow double quotes inside a double-quoted +title, and its test suite included a test demonstrating this. +But it is hard to see a good rationale for the extra complexity this +brings, since there are already many ways---backslash escaping, +entities, or using a different quote type for the enclosing title---to +write titles containing double quotes. `Markdown.pl`'s handling of +titles has a number of other strange features. For example, it allows +single-quoted titles in inline links, but not reference links. And, in +reference links but not inline links, it allows a title to begin with +`"` and end with `)`. `Markdown.pl` 1.0.1 even allows titles with no closing +quotation mark, though 1.0.2b8 does not. It seems preferable to adopt +a simple, rational rule that works the same way in inline links and +link reference definitions.) + +Whitespace is allowed around the destination and title: + +. +[link]( /uri + "title" ) +. +<p><a href="/uri" title="title">link</a></p> +. + +But it is not allowed between the link label and the +following parenthesis: + +. +[link] (/uri) +. +<p>[link] (/uri)</p> +. + +Note that this is not a link, because the closing `]` occurs in +an HTML tag: + +. +[foo <bar attr="](baz)"> +. +<p>[foo <bar attr="](baz)"></p> +. + + +There are three kinds of [reference links](#reference-link): +<a id="reference-link"></a> + +A [full reference link](#full-reference-link) <a id="full-reference-link"></a> +consists of a [link label](#link-label), optional whitespace, and +another [link label](#link-label) that [matches](#matches) a +[link reference definition](#link-reference-definition) elsewhere in the +document. + +One label [matches](#matches) <a id="matches"></a> +another just in case their normalized forms are equal. To normalize a +label, perform the *unicode case fold* and collapse consecutive internal +whitespace to a single space. If there are multiple matching reference +link definitions, the one that comes first in the document is used. (It +is desirable in such cases to emit a warning.) + +The contents of the first link label are parsed as inlines, which are +used as the link's text. The link's URI and title are provided by the +matching [link reference definition](#link-reference-definition). + +Here is a simple example: + +. +[foo][bar] + +[bar]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +The first label can contain inline content: + +. +[*foo\!*][bar] + +[bar]: /url "title" +. +<p><a href="/url" title="title"><em>foo!</em></a></p> +. + +Matching is case-insensitive: + +. +[foo][BaR] + +[bar]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +Unicode case fold is used: + +. +[Толпой][Толпой] is a Russian word. + +[ТОЛПОЙ]: /url +. +<p><a href="/url">Толпой</a> is a Russian word.</p> +. + +Consecutive internal whitespace is treated as one space for +purposes of determining matching: + +. +[Foo + bar]: /url + +[Baz][Foo bar] +. +<p><a href="/url">Baz</a></p> +. + +There can be whitespace between the two labels: + +. +[foo] [bar] + +[bar]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +. +[foo] +[bar] + +[bar]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +When there are multiple matching [link reference +definitions](#link-reference-definition), the first is used: + +. +[foo]: /url1 + +[foo]: /url2 + +[bar][foo] +. +<p><a href="/url1">bar</a></p> +. + +Note that matching is performed on normalized strings, not parsed +inline content. So the following does not match, even though the +labels define equivalent inline content: + +. +[bar][foo\!] + +[foo!]: /url +. +<p>[bar][foo!]</p> +. + +A [collapsed reference link](#collapsed-reference-link) +<a id="collapsed-reference-link"></a> consists of a [link +label](#link-label) that [matches](#matches) a [link reference +definition](#link-reference-definition) elsewhere in the +document, optional whitespace, and the string `[]`. The contents of the +first link label are parsed as inlines, which are used as the link's +text. The link's URI and title are provided by the matching reference +link definition. Thus, `[foo][]` is equivalent to `[foo][foo]`. + +. +[foo][] + +[foo]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +. +[*foo* bar][] + +[*foo* bar]: /url "title" +. +<p><a href="/url" title="title"><em>foo</em> bar</a></p> +. + +The link labels are case-insensitive: + +. +[Foo][] + +[foo]: /url "title" +. +<p><a href="/url" title="title">Foo</a></p> +. + + +As with full reference links, whitespace is allowed +between the two sets of brackets: + +. +[foo] +[] + +[foo]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +A [shortcut reference link](#shortcut-reference-link) +<a id="shortcut-reference-link"></a> consists of a [link +label](#link-label) that [matches](#matches) a [link reference +definition](#link-reference-definition) elsewhere in the +document and is not followed by `[]` or a link label. +The contents of the first link label are parsed as inlines, +which are used as the link's text. the link's URI and title +are provided by the matching link reference definition. +Thus, `[foo]` is equivalent to `[foo][]`. + +. +[foo] + +[foo]: /url "title" +. +<p><a href="/url" title="title">foo</a></p> +. + +. +[*foo* bar] + +[*foo* bar]: /url "title" +. +<p><a href="/url" title="title"><em>foo</em> bar</a></p> +. + +. +[[*foo* bar]] + +[*foo* bar]: /url "title" +. +<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p> +. + +The link labels are case-insensitive: + +. +[Foo] + +[foo]: /url "title" +. +<p><a href="/url" title="title">Foo</a></p> +. + +If you just want bracketed text, you can backslash-escape the +opening bracket to avoid links: + +. +\[foo] + +[foo]: /url "title" +. +<p>[foo]</p> +. + +Note that this is a link, because link labels bind more tightly +than emphasis: + +. +[foo*]: /url + +*[foo*] +. +<p>*<a href="/url">foo*</a></p> +. + +However, this is not, because link labels bind less +tightly than code backticks: + +. +[foo`]: /url + +[foo`]` +. +<p>[foo<code>]</code></p> +. + +Link labels can contain matched square brackets: + +. +[[[foo]]] + +[[[foo]]]: /url +. +<p><a href="/url">[[foo]]</a></p> +. + +. +[[[foo]]] + +[[[foo]]]: /url1 +[foo]: /url2 +. +<p><a href="/url1">[[foo]]</a></p> +. + +For non-matching brackets, use backslash escapes: + +. +[\[foo] + +[\[foo]: /url +. +<p><a href="/url">[foo</a></p> +. + +Full references take precedence over shortcut references: + +. +[foo][bar] + +[foo]: /url1 +[bar]: /url2 +. +<p><a href="/url2">foo</a></p> +. + +In the following case `[bar][baz]` is parsed as a reference, +`[foo]` as normal text: + +. +[foo][bar][baz] + +[baz]: /url +. +<p>[foo]<a href="/url">bar</a></p> +. + +Here, though, `[foo][bar]` is parsed as a reference, since +`[bar]` is defined: + +. +[foo][bar][baz] + +[baz]: /url1 +[bar]: /url2 +. +<p><a href="/url2">foo</a><a href="/url1">baz</a></p> +. + +Here `[foo]` is not parsed as a shortcut reference, because it +is followed by a link label (even though `[bar]` is not defined): + +. +[foo][bar][baz] + +[baz]: /url1 +[foo]: /url2 +. +<p>[foo]<a href="/url1">bar</a></p> +. + + +## Images + +An (unescaped) exclamation mark (`!`) followed by a reference or +inline link will be parsed as an image. The link label will be +used as the image's alt text, and the link title, if any, will +be used as the image's title. + +. +![foo](/url "title") +. +<p><img src="/url" alt="foo" title="title" /></p> +. + +. +![foo *bar*] + +[foo *bar*]: train.jpg "train & tracks" +. +<p><img src="train.jpg" alt="foo <em>bar</em>" title="train & tracks" /></p> +. + +. +![foo *bar*][] + +[foo *bar*]: train.jpg "train & tracks" +. +<p><img src="train.jpg" alt="foo <em>bar</em>" title="train & tracks" /></p> +. + +. +![foo *bar*][foobar] + +[FOOBAR]: train.jpg "train & tracks" +. +<p><img src="train.jpg" alt="foo <em>bar</em>" title="train & tracks" /></p> +. + +. +![foo](train.jpg) +. +<p><img src="train.jpg" alt="foo" /></p> +. + +. +My ![foo bar](/path/to/train.jpg "title" ) +. +<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p> +. + +. +![foo](<url>) +. +<p><img src="url" alt="foo" /></p> +. + +. +![](/url) +. +<p><img src="/url" alt="" /></p> +. + +Reference-style: + +. +![foo] [bar] + +[bar]: /url +. +<p><img src="/url" alt="foo" /></p> +. + +. +![foo] [bar] + +[BAR]: /url +. +<p><img src="/url" alt="foo" /></p> +. + +Collapsed: + +. +![foo][] + +[foo]: /url "title" +. +<p><img src="/url" alt="foo" title="title" /></p> +. + +. +![*foo* bar][] + +[*foo* bar]: /url "title" +. +<p><img src="/url" alt="<em>foo</em> bar" title="title" /></p> +. + +The labels are case-insensitive: + +. +![Foo][] + +[foo]: /url "title" +. +<p><img src="/url" alt="Foo" title="title" /></p> +. + +As with full reference links, whitespace is allowed +between the two sets of brackets: + +. +![foo] +[] + +[foo]: /url "title" +. +<p><img src="/url" alt="foo" title="title" /></p> +. + +Shortcut: + +. +![foo] + +[foo]: /url "title" +. +<p><img src="/url" alt="foo" title="title" /></p> +. + +. +![*foo* bar] + +[*foo* bar]: /url "title" +. +<p><img src="/url" alt="<em>foo</em> bar" title="title" /></p> +. + +. +![[foo]] + +[[foo]]: /url "title" +. +<p><img src="/url" alt="[foo]" title="title" /></p> +. + +The link labels are case-insensitive: + +. +![Foo] + +[foo]: /url "title" +. +<p><img src="/url" alt="Foo" title="title" /></p> +. + +If you just want bracketed text, you can backslash-escape the +opening `!` and `[`: + +. +\!\[foo] + +[foo]: /url "title" +. +<p>![foo]</p> +. + +If you want a link after a literal `!`, backslash-escape the +`!`: + +. +\![foo] + +[foo]: /url "title" +. +<p>!<a href="/url" title="title">foo</a></p> +. + +## Autolinks + +Autolinks are absolute URIs and email addresses inside `<` and `>`. +They are parsed as links, with the URL or email address as the link +label. + +A [URI autolink](#uri-autolink) <a id="uri-autolink"></a> +consists of `<`, followed by an [absolute +URI](#absolute-uri) not containing `<`, followed by `>`. It is parsed +as a link to the URI, with the URI as the link's label. + +An [absolute URI](#absolute-uri), <a id="absolute-uri"></a> +for these purposes, consists of a [scheme](#scheme) followed by a colon (`:`) +followed by zero or more characters other than ASCII whitespace and +control characters, `<`, and `>`. If the URI includes these characters, +you must use percent-encoding (e.g. `%20` for a space). + +The following [schemes](#scheme) <a id="scheme"></a> +are recognized (case-insensitive): +`coap`, `doi`, `javascript`, `aaa`, `aaas`, `about`, `acap`, `cap`, +`cid`, `crid`, `data`, `dav`, `dict`, `dns`, `file`, `ftp`, `geo`, `go`, +`gopher`, `h323`, `http`, `https`, `iax`, `icap`, `im`, `imap`, `info`, +`ipp`, `iris`, `iris.beep`, `iris.xpc`, `iris.xpcs`, `iris.lwz`, `ldap`, +`mailto`, `mid`, `msrp`, `msrps`, `mtqp`, `mupdate`, `news`, `nfs`, +`ni`, `nih`, `nntp`, `opaquelocktoken`, `pop`, `pres`, `rtsp`, +`service`, `session`, `shttp`, `sieve`, `sip`, `sips`, `sms`, `snmp`,` +soap.beep`, `soap.beeps`, `tag`, `tel`, `telnet`, `tftp`, `thismessage`, +`tn3270`, `tip`, `tv`, `urn`, `vemmi`, `ws`, `wss`, `xcon`, +`xcon-userid`, `xmlrpc.beep`, `xmlrpc.beeps`, `xmpp`, `z39.50r`, +`z39.50s`, `adiumxtra`, `afp`, `afs`, `aim`, `apt`,` attachment`, `aw`, +`beshare`, `bitcoin`, `bolo`, `callto`, `chrome`,` chrome-extension`, +`com-eventbrite-attendee`, `content`, `cvs`,` dlna-playsingle`, +`dlna-playcontainer`, `dtn`, `dvb`, `ed2k`, `facetime`, `feed`, +`finger`, `fish`, `gg`, `git`, `gizmoproject`, `gtalk`, `hcp`, `icon`, +`ipn`, `irc`, `irc6`, `ircs`, `itms`, `jar`, `jms`, `keyparc`, `lastfm`, +`ldaps`, `magnet`, `maps`, `market`,` message`, `mms`, `ms-help`, +`msnim`, `mumble`, `mvn`, `notes`, `oid`, `palm`, `paparazzi`, +`platform`, `proxy`, `psyc`, `query`, `res`, `resource`, `rmi`, `rsync`, +`rtmp`, `secondlife`, `sftp`, `sgn`, `skype`, `smb`, `soldat`, +`spotify`, `ssh`, `steam`, `svn`, `teamspeak`, `things`, `udp`, +`unreal`, `ut2004`, `ventrilo`, `view-source`, `webcal`, `wtai`, +`wyciwyg`, `xfire`, `xri`, `ymsgr`. + +Here are some valid autolinks: + +. +<http://foo.bar.baz> +. +<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p> +. + +. +<http://foo.bar.baz?q=hello&id=22&boolean> +. +<p><a href="http://foo.bar.baz?q=hello&id=22&boolean">http://foo.bar.baz?q=hello&id=22&boolean</a></p> +. + +. +<irc://foo.bar:2233/baz> +. +<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p> +. + +Uppercase is also fine: + +. +<MAILTO:FOO@BAR.BAZ> +. +<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p> +. + +Spaces are not allowed in autolinks: + +. +<http://foo.bar/baz bim> +. +<p><http://foo.bar/baz bim></p> +. + +An [email autolink](#email-autolink) <a id="email-autolink"></a> +consists of `<`, followed by an [email address](#email-address), +followed by `>`. The link's label is the email address, +and the URL is `mailto:` followed by the email address. + +An [email address](#email-address), <a id="email-address"></a> +for these purposes, is anything that matches +the [non-normative regex from the HTML5 +spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29): + + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? + (?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ + +Examples of email autolinks: + +. +<foo@bar.baz.com> +. +<p><a href="mailto:foo@bar.baz.com">foo@bar.baz.com</a></p> +. + +. +<foo+special@Bar.baz-bar0.com> +. +<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p> +. + +These are not autolinks: + +. +<> +. +<p><></p> +. + +. +<heck://bing.bong> +. +<p><heck://bing.bong></p> +. + +. +< http://foo.bar > +. +<p>< http://foo.bar ></p> +. + +. +<foo.bar.baz> +. +<p><foo.bar.baz></p> +. + +. +<localhost:5001/foo> +. +<p><localhost:5001/foo></p> +. + +. +http://google.com +. +<p>http://google.com</p> +. + +. +foo@bar.baz.com +. +<p>foo@bar.baz.com</p> +. + +## Raw HTML + +Text between `<` and `>` that looks like an HTML tag is parsed as a +raw HTML tag and will be rendered in HTML without escaping. +Tag and attribute names are not limited to current HTML tags, +so custom tags (and even, say, DocBook tags) may be used. + +Here is the grammar for tags: + +A [tag name](#tag-name) <a id="tag-name"></a> consists of an ASCII letter +followed by zero or more ASCII letters or digits. + +An [attribute](#attribute) <a id="attribute"></a> consists of whitespace, +an **attribute name**, and an optional **attribute value +specification**. + +An [attribute name](#attribute-name) <a id="attribute-name"></a> +consists of an ASCII letter, `_`, or `:`, followed by zero or more ASCII +letters, digits, `_`, `.`, `:`, or `-`. (Note: This is the XML +specification restricted to ASCII. HTML5 is laxer.) + +An [attribute value specification](#attribute-value-specification) +<a id="attribute-value-specification"></a> consists of optional whitespace, +a `=` character, optional whitespace, and an [attribute +value](#attribute-value). + +An [attribute value](#attribute-value) <a id="attribute-value"></a> +consists of an [unquoted attribute value](#unquoted-attribute-value), +a [single-quoted attribute value](#single-quoted-attribute-value), +or a [double-quoted attribute value](#double-quoted-attribute-value). + +An [unquoted attribute value](#unquoted-attribute-value) +<a id="unquoted-attribute-value"></a> is a nonempty string of characters not +including spaces, `"`, `'`, `=`, `<`, `>`, or `` ` ``. + +A [single-quoted attribute value](#single-quoted-attribute-value) +<a id="single-quoted-attribute-value"></a> consists of `'`, zero or more +characters not including `'`, and a final `'`. + +A [double-quoted attribute value](#double-quoted-attribute-value) +<a id="double-quoted-attribute-value"></a> consists of `"`, zero or more +characters not including `"`, and a final `"`. + +An [open tag](#open-tag) <a id="open-tag"></a> consists of a `<` character, +a [tag name](#tag-name), zero or more [attributes](#attribute), +optional whitespace, an optional `/` character, and a `>` character. + +A [closing tag](#closing-tag) <a id="closing-tag"></a> consists of the +string `</`, a [tag name](#tag-name), optional whitespace, and the +character `>`. + +An [HTML comment](#html-comment) <a id="html-comment"></a> consists of the +string `<!--`, a string of characters not including the string `--`, and +the string `-->`. + +A [processing instruction](#processing-instruction) +<a id="processing-instruction"></a> consists of the string `<?`, a string +of characters not including the string `?>`, and the string +`?>`. + +A [declaration](#declaration) <a id="declaration"></a> consists of the +string `<!`, a name consisting of one or more uppercase ASCII letters, +whitespace, a string of characters not including the character `>`, and +the character `>`. + +A [CDATA section](#cdata-section) <a id="cdata-section"></a> consists of +the string `<![CDATA[`, a string of characters not including the string +`]]>`, and the string `]]>`. + +An [HTML tag](#html-tag) <a id="html-tag"></a> consists of an [open +tag](#open-tag), a [closing tag](#closing-tag), an [HTML +comment](#html-comment), a [processing +instruction](#processing-instruction), an [element type +declaration](#element-type-declaration), or a [CDATA +section](#cdata-section). + +Here are some simple open tags: + +. +<a><bab><c2c> +. +<p><a><bab><c2c></p> +. + +Empty elements: + +. +<a/><b2/> +. +<p><a/><b2/></p> +. + +Whitespace is allowed: + +. +<a /><b2 +data="foo" > +. +<p><a /><b2 +data="foo" ></p> +. + +With attributes: + +. +<a foo="bar" bam = 'baz <em>"</em>' +_boolean zoop:33=zoop:33 /> +. +<p><a foo="bar" bam = 'baz <em>"</em>' +_boolean zoop:33=zoop:33 /></p> +. + +Illegal tag names, not parsed as HTML: + +. +<33> <__> +. +<p><33> <__></p> +. + +Illegal attribute names: + +. +<a h*#ref="hi"> +. +<p><a h*#ref="hi"></p> +. + +Illegal attribute values: + +. +<a href="hi'> <a href=hi'> +. +<p><a href="hi'> <a href=hi'></p> +. + +Illegal whitespace: + +. +< a>< +foo><bar/ > +. +<p>< a>< +foo><bar/ ></p> +. + +Missing whitespace: + +. +<a href='bar'title=title> +. +<p><a href='bar'title=title></p> +. + +Closing tags: + +. +</a> +</foo > +. +<p></a> +</foo ></p> +. + +Illegal attributes in closing tag: + +. +</a href="foo"> +. +<p></a href="foo"></p> +. + +Comments: + +. +foo <!-- this is a +comment - with hyphen --> +. +<p>foo <!-- this is a +comment - with hyphen --></p> +. + +. +foo <!-- not a comment -- two hyphens --> +. +<p>foo <!-- not a comment -- two hyphens --></p> +. + +Processing instructions: + +. +foo <?php echo $a; ?> +. +<p>foo <?php echo $a; ?></p> +. + +Declarations: + +. +foo <!ELEMENT br EMPTY> +. +<p>foo <!ELEMENT br EMPTY></p> +. + +CDATA sections: + +. +foo <![CDATA[>&<]]> +. +<p>foo <![CDATA[>&<]]></p> +. + +Entities are preserved in HTML attributes: + +. +<a href="ö"> +. +<p><a href="ö"></p> +. + +Backslash escapes do not work in HTML attributes: + +. +<a href="\*"> +. +<p><a href="\*"></p> +. + +. +<a href="\""> +. +<p><a href="""></p> +. + +## Hard line breaks + +A line break (not in a code span or HTML tag) that is preceded +by two or more spaces is parsed as a linebreak (rendered +in HTML as a `<br />` tag): + +. +foo +baz +. +<p>foo<br /> +baz</p> +. + +For a more visible alternative, a backslash before the newline may be +used instead of two spaces: + +. +foo\ +baz +. +<p>foo<br /> +baz</p> +. + +More than two spaces can be used: + +. +foo +baz +. +<p>foo<br /> +baz</p> +. + +Leading spaces at the beginning of the next line are ignored: + +. +foo + bar +. +<p>foo<br /> +bar</p> +. + +. +foo\ + bar +. +<p>foo<br /> +bar</p> +. + +Line breaks can occur inside emphasis, links, and other constructs +that allow inline content: + +. +*foo +bar* +. +<p><em>foo<br /> +bar</em></p> +. + +. +*foo\ +bar* +. +<p><em>foo<br /> +bar</em></p> +. + +Line breaks do not occur inside code spans + +. +`code +span` +. +<p><code>code span</code></p> +. + +. +`code\ +span` +. +<p><code>code\ span</code></p> +. + +or HTML tags: + +. +<a href="foo +bar"> +. +<p><a href="foo +bar"></p> +. + +. +<a href="foo\ +bar"> +. +<p><a href="foo\ +bar"></p> +. + +## Soft line breaks + +A regular line break (not in a code span or HTML tag) that is not +preceded by two or more spaces is parsed as a softbreak. (A +softbreak may be rendered in HTML either as a newline or as a space. +The result will be the same in browsers. In the examples here, a +newline will be used.) + +. +foo +baz +. +<p>foo +baz</p> +. + +Spaces at the end of the line and beginning of the next line are +removed: + +. +foo + baz +. +<p>foo +baz</p> +. + +A conforming parser may render a soft line break in HTML either as a +line break or as a space. + +A renderer may also provide an option to render soft line breaks +as hard line breaks. + +## Strings + +Any characters not given an interpretation by the above rules will +be parsed as string content. + +. +hello $.;'there +. +<p>hello $.;'there</p> +. + +. +Foo χρῆν +. +<p>Foo χρῆν</p> +. + +Internal spaces are preserved verbatim: + +. +Multiple spaces +. +<p>Multiple spaces</p> +. + +<!-- END TESTS --> + +# Appendix A: A parsing strategy {-} + +## Overview {-} + +Parsing has two phases: + +1. In the first phase, lines of input are consumed and the block +structure of the document---its division into paragraphs, block quotes, +list items, and so on---is constructed. Text is assigned to these +blocks but not parsed. Link reference definitions are parsed and a +map of links is constructed. + +2. In the second phase, the raw text contents of paragraphs and headers +are parsed into sequences of Markdown inline elements (strings, +code spans, links, emphasis, and so on), using the map of link +references constructed in phase 1. + +## The document tree {-} + +At each point in processing, the document is represented as a tree of +**blocks**. The root of the tree is a `document` block. The `document` +may have any number of other blocks as **children**. These children +may, in turn, have other blocks as children. The last child of a block +is normally considered **open**, meaning that subsequent lines of input +can alter its contents. (Blocks that are not open are **closed**.) +Here, for example, is a possible document tree, with the open blocks +marked by arrows: + +``` tree +-> document + -> block_quote + paragraph + "Lorem ipsum dolor\nsit amet." + -> list (type=bullet tight=true bullet_char=-) + list_item + paragraph + "Qui *quodsi iracundia*" + -> list_item + -> paragraph + "aliquando id" +``` + +## How source lines alter the document tree {-} + +Each line that is processed has an effect on this tree. The line is +analyzed and, depending on its contents, the document may be altered +in one or more of the following ways: + +1. One or more open blocks may be closed. +2. One or more new blocks may be created as children of the + last open block. +3. Text may be added to the last (deepest) open block remaining + on the tree. + +Once a line has been incorporated into the tree in this way, +it can be discarded, so input can be read in a stream. + +We can see how this works by considering how the tree above is +generated by four lines of Markdown: + +``` markdown +> Lorem ipsum dolor +sit amet. +> - Qui *quodsi iracundia* +> - aliquando id +``` + +At the outset, our document model is just + +``` tree +-> document +``` + +The first line of our text, + +``` markdown +> Lorem ipsum dolor +``` + +causes a `block_quote` block to be created as a child of our +open `document` block, and a `paragraph` block as a child of +the `block_quote`. Then the text is added to the last open +block, the `paragraph`: + +``` tree +-> document + -> block_quote + -> paragraph + "Lorem ipsum dolor" +``` + +The next line, + +``` markdown +sit amet. +``` + +is a "lazy continuation" of the open `paragraph`, so it gets added +to the paragraph's text: + +``` tree +-> document + -> block_quote + -> paragraph + "Lorem ipsum dolor\nsit amet." +``` + +The third line, + +``` markdown +> - Qui *quodsi iracundia* +``` + +causes the `paragraph` block to be closed, and a new `list` block +opened as a child of the `block_quote`. A `list_item` is also +added as a child of the `list`, and a `paragraph` as a child of +the `list_item`. The text is then added to the new `paragraph`: + +``` tree +-> document + -> block_quote + paragraph + "Lorem ipsum dolor\nsit amet." + -> list (type=bullet tight=true bullet_char=-) + -> list_item + -> paragraph + "Qui *quodsi iracundia*" +``` + +The fourth line, + +``` markdown +> - aliquando id +``` + +causes the `list_item` (and its child the `paragraph`) to be closed, +and a new `list_item` opened up as child of the `list`. A `paragraph` +is added as a child of the new `list_item`, to contain the text. +We thus obtain the final tree: + +``` tree +-> document + -> block_quote + paragraph + "Lorem ipsum dolor\nsit amet." + -> list (type=bullet tight=true bullet_char=-) + list_item + paragraph + "Qui *quodsi iracundia*" + -> list_item + -> paragraph + "aliquando id" +``` + +## From block structure to the final document {-} + +Once all of the input has been parsed, all open blocks are closed. + +We then "walk the tree," visiting every node, and parse raw +string contents of paragraphs and headers as inlines. At this +point we have seen all the link reference definitions, so we can +resolve reference links as we go. + +``` tree +document + block_quote + paragraph + str "Lorem ipsum dolor" + softbreak + str "sit amet." + list (type=bullet tight=true bullet_char=-) + list_item + paragraph + str "Qui " + emph + str "quodsi iracundia" + list_item + paragraph + str "aliquando id" +``` + +Notice how the newline in the first paragraph has been parsed as +a `softbreak`, and the asterisks in the first list item have become +an `emph`. + +The document can be rendered as HTML, or in any other format, given +an appropriate renderer. + + diff --git a/core/testdata/packagedocs/referenceLinks.kotlin.md b/core/testdata/packagedocs/referenceLinks.kotlin.md new file mode 100644 index 000000000..ac7e4b48a --- /dev/null +++ b/core/testdata/packagedocs/referenceLinks.kotlin.md @@ -0,0 +1,7 @@ + + +Core functions and types +See [ref](http://example.com) +Also, [example](http://example.com) + +
\ No newline at end of file diff --git a/core/testdata/packagedocs/referenceLinks.md b/core/testdata/packagedocs/referenceLinks.md new file mode 100644 index 000000000..7583ee9d7 --- /dev/null +++ b/core/testdata/packagedocs/referenceLinks.md @@ -0,0 +1,17 @@ +# Module refLinks + +## Kotlin Standard Library + +The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections, +text and files. +See [ref] +Also, [example][ref] + +# Package kotlin + +Core functions and types +See [ref] +Also, [example][ref] + +<!-- Refs --> +[ref]: http://example.com diff --git a/core/testdata/packagedocs/referenceLinks.module.md b/core/testdata/packagedocs/referenceLinks.module.md new file mode 100644 index 000000000..ddbdbe2f8 --- /dev/null +++ b/core/testdata/packagedocs/referenceLinks.module.md @@ -0,0 +1,9 @@ + + +## Kotlin Standard Library + +The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections, +text and files. +See [ref](http://example.com) +Also, [example](http://example.com) + diff --git a/core/testdata/packagedocs/stdlib.md b/core/testdata/packagedocs/stdlib.md new file mode 100644 index 000000000..5d7432b50 --- /dev/null +++ b/core/testdata/packagedocs/stdlib.md @@ -0,0 +1,11 @@ +# Module stdlib + +## Kotlin Standard Library + +The Kotlin standard library is a set of functions and types implementing idiomatic patterns when working with collections, +text and files. + +# Package kotlin + +Core functions and types + diff --git a/core/testdata/packages/classInPackage.kt b/core/testdata/packages/classInPackage.kt new file mode 100644 index 000000000..b22273afb --- /dev/null +++ b/core/testdata/packages/classInPackage.kt @@ -0,0 +1,3 @@ +package simple.name + +class Foo {} diff --git a/core/testdata/packages/dottedNamePackage.kt b/core/testdata/packages/dottedNamePackage.kt new file mode 100644 index 000000000..386193104 --- /dev/null +++ b/core/testdata/packages/dottedNamePackage.kt @@ -0,0 +1 @@ +package dot.name
\ No newline at end of file diff --git a/core/testdata/packages/rootPackage.kt b/core/testdata/packages/rootPackage.kt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/core/testdata/packages/rootPackage.kt diff --git a/core/testdata/packages/simpleNamePackage.kt b/core/testdata/packages/simpleNamePackage.kt new file mode 100644 index 000000000..2c29f4c70 --- /dev/null +++ b/core/testdata/packages/simpleNamePackage.kt @@ -0,0 +1 @@ +package simple diff --git a/core/testdata/packages/simpleNamePackage2.kt b/core/testdata/packages/simpleNamePackage2.kt new file mode 100644 index 000000000..2c29f4c70 --- /dev/null +++ b/core/testdata/packages/simpleNamePackage2.kt @@ -0,0 +1 @@ +package simple diff --git a/core/testdata/properties/annotatedProperty.kt b/core/testdata/properties/annotatedProperty.kt new file mode 100644 index 000000000..3c12691b1 --- /dev/null +++ b/core/testdata/properties/annotatedProperty.kt @@ -0,0 +1 @@ +@Strictfp var property = "test"
\ No newline at end of file diff --git a/core/testdata/properties/propertyOverride.kt b/core/testdata/properties/propertyOverride.kt new file mode 100644 index 000000000..625d1da0b --- /dev/null +++ b/core/testdata/properties/propertyOverride.kt @@ -0,0 +1,7 @@ +open class Foo() { + open val xyzzy: Int get() = 0 +} + +class Bar(): Foo() { + override val xyzzy: Int get() = 1 +} diff --git a/core/testdata/properties/propertyWithReceiver.kt b/core/testdata/properties/propertyWithReceiver.kt new file mode 100644 index 000000000..e282f6bd9 --- /dev/null +++ b/core/testdata/properties/propertyWithReceiver.kt @@ -0,0 +1,2 @@ +val String.foobar: Int + get() = size() * 2 diff --git a/core/testdata/properties/sinceKotlin.kt b/core/testdata/properties/sinceKotlin.kt new file mode 100644 index 000000000..e96f2349f --- /dev/null +++ b/core/testdata/properties/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Quite useful [String] + */ +@SinceKotlin("1.1") +val `availableSince1.1`: String = "1.1 rulezz"
\ No newline at end of file diff --git a/core/testdata/properties/valueProperty.kt b/core/testdata/properties/valueProperty.kt new file mode 100644 index 000000000..b87cce575 --- /dev/null +++ b/core/testdata/properties/valueProperty.kt @@ -0,0 +1 @@ +val property = "test"
\ No newline at end of file diff --git a/core/testdata/properties/valuePropertyWithGetter.kt b/core/testdata/properties/valuePropertyWithGetter.kt new file mode 100644 index 000000000..64d3848c5 --- /dev/null +++ b/core/testdata/properties/valuePropertyWithGetter.kt @@ -0,0 +1,2 @@ +val property: String + get() = "test"
\ No newline at end of file diff --git a/core/testdata/properties/variableProperty.kt b/core/testdata/properties/variableProperty.kt new file mode 100644 index 000000000..54ab45959 --- /dev/null +++ b/core/testdata/properties/variableProperty.kt @@ -0,0 +1 @@ +var property = "test"
\ No newline at end of file diff --git a/core/testdata/properties/variablePropertyWithAccessors.kt b/core/testdata/properties/variablePropertyWithAccessors.kt new file mode 100644 index 000000000..152fb7d0a --- /dev/null +++ b/core/testdata/properties/variablePropertyWithAccessors.kt @@ -0,0 +1,4 @@ +var property: String + get() = "test" + set(value) { + }
\ No newline at end of file diff --git a/core/testdata/typealias/asTypeBoundWithVariance.kt b/core/testdata/typealias/asTypeBoundWithVariance.kt new file mode 100644 index 000000000..1aef84d69 --- /dev/null +++ b/core/testdata/typealias/asTypeBoundWithVariance.kt @@ -0,0 +1,7 @@ +package _typealias.astypebound +class A + +typealias B = A + +class C<out T : B> +class D<in T : B>
\ No newline at end of file diff --git a/core/testdata/typealias/chain.kt b/core/testdata/typealias/chain.kt new file mode 100644 index 000000000..520be5535 --- /dev/null +++ b/core/testdata/typealias/chain.kt @@ -0,0 +1,8 @@ +package _typealias.chain + +class A + +typealias B = A + +typealias C = B + diff --git a/core/testdata/typealias/deprecated.kt b/core/testdata/typealias/deprecated.kt new file mode 100644 index 000000000..b53d3a206 --- /dev/null +++ b/core/testdata/typealias/deprecated.kt @@ -0,0 +1,7 @@ +package _typealias.deprecated + +class Lol + +@Deprecated("Not mainstream now") +typealias Kek = Lol + diff --git a/core/testdata/typealias/documented.kt b/core/testdata/typealias/documented.kt new file mode 100644 index 000000000..3ca110e5f --- /dev/null +++ b/core/testdata/typealias/documented.kt @@ -0,0 +1,9 @@ +package _typealias.documented + +class A + +/** + * Just typealias + */ +typealias B = A + diff --git a/core/testdata/typealias/functional.kt b/core/testdata/typealias/functional.kt new file mode 100644 index 000000000..dadafa5e4 --- /dev/null +++ b/core/testdata/typealias/functional.kt @@ -0,0 +1,10 @@ +package _typealias.functional + +class A +class B + +typealias Spell = (A) -> B + +fun magic(spell: Spell) { + +}
\ No newline at end of file diff --git a/core/testdata/typealias/generic.kt b/core/testdata/typealias/generic.kt new file mode 100644 index 000000000..43bc0e23f --- /dev/null +++ b/core/testdata/typealias/generic.kt @@ -0,0 +1,7 @@ +package _typealias.generic + +interface A<T> + +typealias B = A<Any> + +typealias C<T> = A<T>
\ No newline at end of file diff --git a/core/testdata/typealias/inheritanceFromTypeAlias.kt b/core/testdata/typealias/inheritanceFromTypeAlias.kt new file mode 100644 index 000000000..f929ecd09 --- /dev/null +++ b/core/testdata/typealias/inheritanceFromTypeAlias.kt @@ -0,0 +1,7 @@ +package _typealias.inheritance + +open class Some + +typealias Same = Some + +class My : Same
\ No newline at end of file diff --git a/core/testdata/typealias/simple.kt b/core/testdata/typealias/simple.kt new file mode 100644 index 000000000..d688a84d1 --- /dev/null +++ b/core/testdata/typealias/simple.kt @@ -0,0 +1,5 @@ +package _typealias.simple + +class A + +typealias B = A
\ No newline at end of file diff --git a/core/testdata/typealias/sinceKotlin.kt b/core/testdata/typealias/sinceKotlin.kt new file mode 100644 index 000000000..5b76f63ad --- /dev/null +++ b/core/testdata/typealias/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Documentation + */ +@SinceKotlin("1.1") +typealias `Since 1.1` = String
\ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..2117d734f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,22 @@ +dokka_version=0.9.17-g014 +dokka_publication_channel=dokka + +#Kotlin compiler and plugin +bundled_kotlin_compiler_version=1.3.61 +kotlin_version=1.3.61 +kotlin_plugin_version=1.3.61-release-180 +idea_version=192.5728.98 +kotlin_for_gradle_runtime_version=1.3.61 + +ant_version=1.9.6 + +#Maven plugin dependencies +maven_version=3.5.0 +maven_archiver_version=2.5 +plexus_utils_version=3.0.22 +plexus_archiver_version=3.4 +maven_plugin_tools_version=3.5 + +#For CI +mvn=mvn +re diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 000000000..736fb7d3f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..bd24854fe --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..cccdd3d51 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..f9553162f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/integration/build.gradle b/integration/build.gradle new file mode 100644 index 000000000..24d59edf0 --- /dev/null +++ b/integration/build.gradle @@ -0,0 +1,26 @@ +buildscript { + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' + + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + languageVersion = "1.2" + apiVersion = "1.1" + jvmTarget = "1.8" + } +} + +dependencies { + compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version + compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version + compile('com.github.yole:jkid:8fc7f12e1a') { + transitive = false + } +}
\ No newline at end of file diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt new file mode 100644 index 000000000..b78eb9c65 --- /dev/null +++ b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka + +import java.util.function.BiConsumer + +interface DokkaBootstrap { + + fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String) + + fun generate() +}
\ No newline at end of file diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt b/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt new file mode 100644 index 000000000..1984a3e5a --- /dev/null +++ b/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt @@ -0,0 +1,72 @@ +package org.jetbrains.dokka + +import kotlin.reflect.* +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.isAccessible + +object ReflectDsl { + + class CallOrPropAccess(private val receiver: Any?, + private val clz: KClass<*>, + private val selector: String) { + + @Suppress("UNCHECKED_CAST") + operator fun <T : Any?> invoke(vararg a: Any?): T { + return func!!.call(receiver, *a) as T + } + + operator fun get(s: String): CallOrPropAccess { + return v<Any?>()!![s] + } + + val func: KFunction<*>? by lazy { clz.memberFunctions.find { it.name == selector } } + val prop: KProperty<*>? by lazy { clz.memberProperties.find { it.name == selector } } + + fun takeIfIsFunc(): CallOrPropAccess? = if (func != null) this else null + + fun takeIfIsProp(): CallOrPropAccess? = if (prop != null) this else null + + @Suppress("UNCHECKED_CAST") + fun <T : Any?> v(): T { + val prop = prop!! + return try { + prop.getter.apply { isAccessible = true }.call(receiver) as T + } catch (e: KotlinNullPointerException) { + // Hack around kotlin-reflect bug KT-18480 + val jclass = clz.java + val customGetterName = prop.getter.name + val getterName = if (customGetterName.startsWith("<")) "get" + prop.name.capitalize() else customGetterName + val getter = jclass.getDeclaredMethod(getterName) + getter.isAccessible = true + + getter.invoke(receiver) as T + + } + } + + @Suppress("UNCHECKED_CAST") + fun v(x: Any?) { + (prop as KMutableProperty).setter.apply { isAccessible = true }.call(receiver, x) + } + + + } + + operator fun Any.get(s: String): CallOrPropAccess { + val clz = this.javaClass.kotlin + return CallOrPropAccess(this, clz, s) + } + + operator fun Any.get(s: String, clz: Class<*>): CallOrPropAccess { + val kclz = clz.kotlin + return CallOrPropAccess(this, kclz, s) + } + + operator fun Any.get(s: String, clz: KClass<*>): CallOrPropAccess { + return CallOrPropAccess(this, clz, s) + } + + inline infix fun Any.isInstance(clz: Class<*>?): Boolean = clz != null && clz.isAssignableFrom(this.javaClass) + inline infix fun Any.isNotInstance(clz: Class<*>?): Boolean = !(this isInstance clz) +}
\ No newline at end of file diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt new file mode 100644 index 000000000..9eacf5ec6 --- /dev/null +++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt @@ -0,0 +1,122 @@ +package org.jetbrains.dokka + +import ru.yole.jkid.CustomSerializer +import ru.yole.jkid.ValueSerializer +import ru.yole.jkid.deserialization.JKidException +import java.io.Serializable +import java.net.URL + + +class UrlSerializer : ValueSerializer<URL?> { + override fun fromJsonValue(jsonValue: Any?): URL? { + if (jsonValue !is String?) + throw JKidException("Expected string representation of URL, got: $jsonValue") + return jsonValue?.let { URL(jsonValue) } + } + + override fun toJsonValue(value: URL?): Any? = value?.toExternalForm() +} + +interface DokkaConfiguration { + val moduleName: String + val classpath: List<String> + val sourceRoots: List<SourceRoot> + val samples: List<String> + val includes: List<String> + val outputDir: String + val format: String + val includeNonPublic: Boolean + val includeRootPackage: Boolean + val reportUndocumented: Boolean + val skipEmptyPackages: Boolean + val skipDeprecated: Boolean + val jdkVersion: Int + val generateClassIndexPage: Boolean + val generatePackageIndexPage: Boolean + val sourceLinks: List<SourceLinkDefinition> + val impliedPlatforms: List<String> + val perPackageOptions: List<PackageOptions> + val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> + val languageVersion: String? + val apiVersion: String? + val noStdlibLink: Boolean + val noJdkLink: Boolean + val cacheRoot: String? + val suppressedFiles: List<String> + val collectInheritedExtensionsFromLibraries: Boolean + val outlineRoot: String + val dacRoot: String + + interface SourceRoot { + val path: String + val platforms: List<String> + } + + interface SourceLinkDefinition { + val path: String + val url: String + val lineSuffix: String? + } + + interface PackageOptions { + val prefix: String + val includeNonPublic: Boolean + val reportUndocumented: Boolean + val skipDeprecated: Boolean + val suppress: Boolean + } + + interface ExternalDocumentationLink { + @CustomSerializer(UrlSerializer::class) val url: URL + @CustomSerializer(UrlSerializer::class) val packageListUrl: URL + + open class Builder(open var url: URL? = null, + open var packageListUrl: URL? = null) { + + constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) }) + + fun build(): DokkaConfiguration.ExternalDocumentationLink = + if (packageListUrl != null && url != null) + ExternalDocumentationLinkImpl(url!!, packageListUrl!!) + else if (url != null) + ExternalDocumentationLinkImpl(url!!, URL(url!!, "package-list")) + else + throw IllegalArgumentException("url or url && packageListUrl must not be null for external documentation link") + } + } +} + +data class SerializeOnlyDokkaConfiguration( + override val moduleName: String, + override val classpath: List<String>, + override val sourceRoots: List<DokkaConfiguration.SourceRoot>, + override val samples: List<String>, + override val includes: List<String>, + override val outputDir: String, + override val format: String, + override val includeNonPublic: Boolean, + override val includeRootPackage: Boolean, + override val reportUndocumented: Boolean, + override val skipEmptyPackages: Boolean, + override val skipDeprecated: Boolean, + override val jdkVersion: Int, + override val generateClassIndexPage: Boolean, + override val generatePackageIndexPage: Boolean, + override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>, + override val impliedPlatforms: List<String>, + override val perPackageOptions: List<DokkaConfiguration.PackageOptions>, + override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>, + override val noStdlibLink: Boolean, + override val noJdkLink: Boolean, + override val cacheRoot: String?, + override val suppressedFiles: List<String>, + override val languageVersion: String?, + override val apiVersion: String?, + override val collectInheritedExtensionsFromLibraries: Boolean, + override val outlineRoot: String, + override val dacRoot: String +) : DokkaConfiguration + + +data class ExternalDocumentationLinkImpl(@CustomSerializer(UrlSerializer::class) override val url: URL, + @CustomSerializer(UrlSerializer::class) override val packageListUrl: URL) : Serializable, DokkaConfiguration.ExternalDocumentationLink
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar Binary files differdeleted file mode 100644 index 26ab40c6f..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5 deleted file mode 100644 index 10df6b20e..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -8ace5e5ab08ad920213fb38f7ef81a9c
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1 deleted file mode 100644 index 8457be521..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -25d19744222bcd2add5c5d9029db4820355c097a
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar Binary files differdeleted file mode 100644 index 00ceebcca..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5 deleted file mode 100644 index 5736f3f12..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -9a0e3701cd3af6d83cd4c908278df7a8
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1 deleted file mode 100644 index 5eb6136e3..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cdf18a8d0a20a0c6fa76a2fb5788d68fff9e7335
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom deleted file mode 100644 index f00c61277..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-android-gradle-plugin</artifactId> - <version>0.9.17-g001</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g001</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5 deleted file mode 100644 index 95d2b0761..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -dd11486f187fd3d4bb6b72790b9d37bc
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1 deleted file mode 100644 index 3b3881501..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g001/dokka-android-gradle-plugin-0.9.17-g001.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -16b7f1c0e34a5b8b16d36afe90f81786fc49ff2a
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar Binary files differdeleted file mode 100644 index dbb67198d..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5 deleted file mode 100644 index 86c5ed506..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -5486aa0ab4976a5ff8dcb15d4ee2f7d1
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1 deleted file mode 100644 index ae375f522..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c8f65513ff029530d2e680310bfc7358765e4bfe
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar Binary files differdeleted file mode 100644 index 0b8d0a1f9..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5 deleted file mode 100644 index 61ad006bd..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -16c1b1350e5d416c790c0d778e7cef46
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1 deleted file mode 100644 index 831005de9..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2317a2a7351c48f759ac6194a32a66dc7b0cadea
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom deleted file mode 100644 index 95b493e58..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-android-gradle-plugin</artifactId> - <version>0.9.17-g002</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g002</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5 deleted file mode 100644 index 9fd980a80..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -b1215e8f6e57314dafc6c145e74f351d
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1 deleted file mode 100644 index d32d53808..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g002/dokka-android-gradle-plugin-0.9.17-g002.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -957e110ad89134389355af585988d464a8525614
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar Binary files differdeleted file mode 100644 index e497d725b..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5 deleted file mode 100644 index bd34722d7..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -6e8becd2d093a3525324b965df802428
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 deleted file mode 100644 index ec2207666..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2526367fbadfdf2f968b344f74a310e313a76772
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar Binary files differdeleted file mode 100644 index 7047ab158..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5 deleted file mode 100644 index 2772e427f..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -9a70b8755a3e6240c61e802956a6d9b3
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1 deleted file mode 100644 index 19b522a64..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6f22b8e132e92691a8babdaae182ad8686c3070b
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom deleted file mode 100644 index 9dd716383..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-android-gradle-plugin</artifactId> - <version>0.9.17-g20190326</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g20190326</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5 deleted file mode 100644 index 6dccae919..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -a2e2583a875ffcfa13d071cbdf775e45
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1 deleted file mode 100644 index 131bd66b1..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/0.9.17-g20190326/dokka-android-gradle-plugin-0.9.17-g20190326.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -aaed6c55109462aa32d10619e3c9f4d549fc1102
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml deleted file mode 100644 index 96198ecdb..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<metadata> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-android-gradle-plugin</artifactId> - <versioning> - <release>0.9.17-g002</release> - <versions> - <version>0.9.17-g001</version> - <version>0.9.17-g002</version> - </versions> - <lastUpdated>20190426203004</lastUpdated> - </versioning> -</metadata> diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5 deleted file mode 100644 index 193b2a256..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -44af56929dc8cb7f77b27048a7c70af6
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1 deleted file mode 100644 index 0fc74b899..000000000 --- a/maven/org/jetbrains/dokka/dokka-android-gradle-plugin/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -fd61be9ee19d3261be71750198b244f5de95a27a
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar Binary files differdeleted file mode 100644 index 7f5be648b..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5 deleted file mode 100644 index a3d8376f4..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -c0b51fe955e9dec5c2ccd65e32129394
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1 deleted file mode 100644 index 036da77ea..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9fff93a4b0c962e4c35e44d647cb7585025966e2
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom deleted file mode 100644 index a76869b64..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g001</version> - <dependencies/> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5 deleted file mode 100644 index afa61ea38..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -08372135fd740612250978da64378304
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1 deleted file mode 100644 index 47fb2e13c..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g001/dokka-fatjar-0.9.17-g001.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec6f8732761fa40cef30a61564544650999cddad
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar Binary files differdeleted file mode 100644 index b4912535a..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5 deleted file mode 100644 index b73763fbd..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -723f6e53434c3f246ef246b9d54546ec
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1 deleted file mode 100644 index 92c579151..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cbbc1abbb490506c288b04bef4a474f66c14248d
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom deleted file mode 100644 index 4874d56fe..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g002</version> - <dependencies/> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5 deleted file mode 100644 index b58616e9b..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -1d3438e4bda3d0462a188e22925c2cf9
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1 deleted file mode 100644 index 036f12e8f..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g002/dokka-fatjar-0.9.17-g002.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -5696c0c820310f2bd06031e539fe45db6c049fe6
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar Binary files differdeleted file mode 100644 index ca4e5ba38..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5 deleted file mode 100644 index b5615a67d..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -74bfd2075d61e4e1ea72b3d0bed67af7
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1 deleted file mode 100644 index b573931d0..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -b86602c25c8af1eabaf684c5a3de6de9d3872741
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom deleted file mode 100644 index 23850c8ec..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g20190326</version> - <dependencies/> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5 deleted file mode 100644 index e272866a4..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -707d062402bd7a0c30f9cd733b74dbbf
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1 deleted file mode 100644 index ee721350a..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/0.9.17-g20190326/dokka-fatjar-0.9.17-g20190326.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -be9593d35a7a4a01dbb6fcc5a8892b84834bb893
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml deleted file mode 100644 index ade0f21c2..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<metadata> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <versioning> - <release>0.9.17-g002</release> - <versions> - <version>0.9.17-g001</version> - <version>0.9.17-g002</version> - </versions> - <lastUpdated>20190426203021</lastUpdated> - </versioning> -</metadata> diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5 deleted file mode 100644 index ee1dcd92e..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -19f5a9e35cb5a2f976732d2c6fdce072
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1 deleted file mode 100644 index b254d0895..000000000 --- a/maven/org/jetbrains/dokka/dokka-fatjar/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -a3b637586a30b734c693dddd32b6dfb539a9bf20
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar Binary files differdeleted file mode 100644 index 7b9c5c9d8..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5 deleted file mode 100644 index 5c3f32d12..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -3d825d4d14f6305a023289c2ff495d2b
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1 deleted file mode 100644 index c63a877d5..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0bb166c615756d749781d4338bfdec813db76567
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar Binary files differdeleted file mode 100644 index 80e198636..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5 deleted file mode 100644 index 3131604a0..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -932e2c32ecbd0e9f0c79737d529c6c62
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1 deleted file mode 100644 index 63e5fb1a7..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -88dd53edbec4b233709a74c76f9124769fd803b9
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom deleted file mode 100644 index ec009cdaa..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g001</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-stdlib</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-reflect</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5 deleted file mode 100644 index 431abfb94..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -a88943835bcd6e862d353fd5dd11e019
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1 deleted file mode 100644 index 91b2f9449..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g001/dokka-gradle-plugin-0.9.17-g001.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -41a055d22c61aebf9f7c31b5426784441826d685
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar Binary files differdeleted file mode 100644 index cd6250be5..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5 deleted file mode 100644 index 3ec5d5f19..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -a09db7759638bb894b986015ca0c9169
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1 deleted file mode 100644 index 2a8c3e1c3..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -faae7e10f5d63640ac1e79dd80bafc53ed5d9d11
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar Binary files differdeleted file mode 100644 index 516fce5e7..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5 deleted file mode 100644 index a95403c11..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -8679d9b687e2cdec9c1f052b3d63e3a8
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1 deleted file mode 100644 index ae1c25413..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -96dfd696c5e565840b10c30be31a64dc9275df28
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom deleted file mode 100644 index 125e1552c..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g002</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-stdlib</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-reflect</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5 deleted file mode 100644 index baeb693e0..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -1cd7c4c2bc752eb14251473cae7dc37d
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1 deleted file mode 100644 index 592845b2e..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g002/dokka-gradle-plugin-0.9.17-g002.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -4266039913bb663843eb33b5195f2bff8878b602
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar Binary files differdeleted file mode 100644 index 393581ad5..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5 deleted file mode 100644 index 7b50864d9..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -3e87fce121909b7173f16fb9346afeb2
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 deleted file mode 100644 index 8eec44da4..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a835d1ef0bf4e54991c9463e3cbea34c850616b3
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar Binary files differdeleted file mode 100644 index 6ec858dd8..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5 deleted file mode 100644 index eb6d174a1..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -d34bf35dae1cb45e9a0ae493ffe6fc0e
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1 deleted file mode 100644 index 080780e55..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -7ca1fe874dc743a98575e0b49c5a37d552d5f482
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom deleted file mode 100644 index 8c86f20f8..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <version>0.9.17-g20190326</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-stdlib</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>org.jetbrains.kotlin</groupId> - <artifactId>kotlin-reflect</artifactId> - <version>1.1.60</version> - <scope>runtime</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5 deleted file mode 100644 index db144612b..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -a01c99c82aa71ff958d90428edd840b7
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1 deleted file mode 100644 index bdbd81570..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/0.9.17-g20190326/dokka-gradle-plugin-0.9.17-g20190326.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -fd114549ed5130489609230f1c2b1eacd1cd4f62
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml deleted file mode 100644 index d6b334d92..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<metadata> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-gradle-plugin</artifactId> - <versioning> - <release>0.9.17-g002</release> - <versions> - <version>0.9.17-g001</version> - <version>0.9.17-g002</version> - </versions> - <lastUpdated>20190426203021</lastUpdated> - </versioning> -</metadata> diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5 deleted file mode 100644 index bf2a8a50d..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -5c37b739b4a301ae5c4279cc220773e3
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1 deleted file mode 100644 index 4380925d3..000000000 --- a/maven/org/jetbrains/dokka/dokka-gradle-plugin/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -3ed1dce91921552fdacaf7ead753200532693cad
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar Binary files differdeleted file mode 100644 index f178b2da6..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5 deleted file mode 100644 index 8ee52efde..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -9036720995e5d8c15f77ae2f8340484c
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1 deleted file mode 100644 index 277844714..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -add6e40581be0278abbd9daf156a52c3c783662e
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar Binary files differdeleted file mode 100644 index ec7a671da..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5 deleted file mode 100644 index eeaa4e7da..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -7161ca58f1b468b2784a192cb3a0a07a
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1 deleted file mode 100644 index 8cc09184a..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -b0c00e2c8aa4566ff19632eb893ec38d8a23a36d
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom deleted file mode 100644 index 710e33ea0..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-maven-plugin</artifactId> - <version>0.9.17-g001</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g001</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-core</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-model</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-plugin-api</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-archiver</artifactId> - <version>2.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-utils</artifactId> - <version>3.0.22</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-archiver</artifactId> - <version>3.4</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven.plugin-tools</groupId> - <artifactId>maven-plugin-annotations</artifactId> - <version>3.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>com.github.olivergondza</groupId> - <artifactId>maven-jdk-tools-wrapper</artifactId> - <version>0.1</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5 deleted file mode 100644 index 308e20fc5..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -ffce86cc8e9d0a08103418b9f1621217
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1 deleted file mode 100644 index a1ccd5f70..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g001/dokka-maven-plugin-0.9.17-g001.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -96ecac18be7e6e9356def15951c7018c82b141cc
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar Binary files differdeleted file mode 100644 index 89f439d16..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5 deleted file mode 100644 index e6b7b106d..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -d0cfa37683b71cebe36a9ddcf4b252b0
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1 deleted file mode 100644 index c2e0928b4..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -960cdd9df9bfe11653c45186ee660e42d793f506
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar Binary files differdeleted file mode 100644 index c22827274..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5 deleted file mode 100644 index 5c495b966..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -c91e19b5fbacb31dc3db20dfb5741011
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1 deleted file mode 100644 index d905e6fcd..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -820dfe4fb5bbf9e2176ade510230cf72649f0473
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom deleted file mode 100644 index 7fad14a11..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-maven-plugin</artifactId> - <version>0.9.17-g002</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g002</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-core</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-model</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-plugin-api</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-archiver</artifactId> - <version>2.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-utils</artifactId> - <version>3.0.22</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-archiver</artifactId> - <version>3.4</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven.plugin-tools</groupId> - <artifactId>maven-plugin-annotations</artifactId> - <version>3.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>com.github.olivergondza</groupId> - <artifactId>maven-jdk-tools-wrapper</artifactId> - <version>0.1</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5 deleted file mode 100644 index f981b25e3..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -01340b7038e1b92680be03d55f31aa90
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1 deleted file mode 100644 index 4be27f05d..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g002/dokka-maven-plugin-0.9.17-g002.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -bd28aee1f70e6ae4e94756a521a297735471bccf
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar Binary files differdeleted file mode 100644 index de270a9d4..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5 deleted file mode 100644 index 2936c5d7d..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -acfe18028be05579b8a7f1981e37b716
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1 deleted file mode 100644 index c9ce7bb6a..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326-sources.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6da950a8b364820e0d563f2731bc838e54955267
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar Binary files differdeleted file mode 100644 index 647031bff..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar +++ /dev/null diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5 deleted file mode 100644 index 68da64848..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.md5 +++ /dev/null @@ -1 +0,0 @@ -3e63f51357d560c3cddefc6cfe6c884d
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1 deleted file mode 100644 index ee0cd3ae5..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d0a449a89d6286dab97401370d0f531cf00a5f47
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom deleted file mode 100644 index 1e197b851..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom +++ /dev/null @@ -1,63 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-maven-plugin</artifactId> - <version>0.9.17-g20190326</version> - <dependencies> - <dependency> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-fatjar</artifactId> - <version>0.9.17-g20190326</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-core</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-model</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-plugin-api</artifactId> - <version>3.5.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven</groupId> - <artifactId>maven-archiver</artifactId> - <version>2.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-utils</artifactId> - <version>3.0.22</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.codehaus.plexus</groupId> - <artifactId>plexus-archiver</artifactId> - <version>3.4</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>org.apache.maven.plugin-tools</groupId> - <artifactId>maven-plugin-annotations</artifactId> - <version>3.5</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>com.github.olivergondza</groupId> - <artifactId>maven-jdk-tools-wrapper</artifactId> - <version>0.1</version> - <scope>compile</scope> - </dependency> - </dependencies> -</project> diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5 deleted file mode 100644 index f5a6cc764..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.md5 +++ /dev/null @@ -1 +0,0 @@ -12ca67b1a18088cc21b421d2b063d369
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1 deleted file mode 100644 index a1c9f269a..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/0.9.17-g20190326/dokka-maven-plugin-0.9.17-g20190326.pom.sha1 +++ /dev/null @@ -1 +0,0 @@ -7eb7fa4d959e31cb8cb53e72e32302b40942b919
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml deleted file mode 100644 index 67cd845e3..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<metadata> - <groupId>org.jetbrains.dokka</groupId> - <artifactId>dokka-maven-plugin</artifactId> - <versioning> - <release>0.9.17-g002</release> - <versions> - <version>0.9.17-g001</version> - <version>0.9.17-g002</version> - </versions> - <lastUpdated>20190426203024</lastUpdated> - </versioning> -</metadata> diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5 b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5 deleted file mode 100644 index 0621c34dd..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.md5 +++ /dev/null @@ -1 +0,0 @@ -2237ba14abe5abe9aeb68717818ca30f
\ No newline at end of file diff --git a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1 b/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1 deleted file mode 100644 index fea4ccee9..000000000 --- a/maven/org/jetbrains/dokka/dokka-maven-plugin/maven-metadata.xml.sha1 +++ /dev/null @@ -1 +0,0 @@ -4311edf2ba014d211159a400af0db5a96091aa97
\ No newline at end of file diff --git a/package-list b/package-list new file mode 100644 index 000000000..7cd2ff10c --- /dev/null +++ b/package-list @@ -0,0 +1,217 @@ +$dokka.format:kotlin-website-html +$dokka.linkExtension:html +$dokka.location:kotlin$and(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/and.html +$dokka.location:kotlin$dec(java.math.BigDecimal)kotlin/java.math.-big-decimal/dec.html +$dokka.location:kotlin$dec(java.math.BigInteger)kotlin/java.math.-big-integer/dec.html +$dokka.location:kotlin$div(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/div.html +$dokka.location:kotlin$div(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/div.html +$dokka.location:kotlin$inc(java.math.BigDecimal)kotlin/java.math.-big-decimal/inc.html +$dokka.location:kotlin$inc(java.math.BigInteger)kotlin/java.math.-big-integer/inc.html +$dokka.location:kotlin$inv(java.math.BigInteger)kotlin/java.math.-big-integer/inv.html +$dokka.location:kotlin$minus(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/minus.html +$dokka.location:kotlin$minus(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/minus.html +$dokka.location:kotlin$mod(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/mod.html +$dokka.location:kotlin$or(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/or.html +$dokka.location:kotlin$plus(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/plus.html +$dokka.location:kotlin$plus(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/plus.html +$dokka.location:kotlin$rem(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/rem.html +$dokka.location:kotlin$rem(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/rem.html +$dokka.location:kotlin$shl(java.math.BigInteger, kotlin.Int)kotlin/java.math.-big-integer/shl.html +$dokka.location:kotlin$shr(java.math.BigInteger, kotlin.Int)kotlin/java.math.-big-integer/shr.html +$dokka.location:kotlin$times(java.math.BigDecimal, java.math.BigDecimal)kotlin/java.math.-big-decimal/times.html +$dokka.location:kotlin$times(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/times.html +$dokka.location:kotlin$toBigDecimal(java.math.BigInteger)kotlin/java.math.-big-integer/to-big-decimal.html +$dokka.location:kotlin$toBigDecimal(java.math.BigInteger, kotlin.Int, java.math.MathContext)kotlin/java.math.-big-integer/to-big-decimal.html +$dokka.location:kotlin$unaryMinus(java.math.BigDecimal)kotlin/java.math.-big-decimal/unary-minus.html +$dokka.location:kotlin$unaryMinus(java.math.BigInteger)kotlin/java.math.-big-integer/unary-minus.html +$dokka.location:kotlin$xor(java.math.BigInteger, java.math.BigInteger)kotlin/java.math.-big-integer/xor.html +$dokka.location:kotlin.ArithmeticExceptionkotlin/-arithmetic-exception/index.html +$dokka.location:kotlin.AssertionErrorkotlin/-assertion-error/index.html +$dokka.location:kotlin.ClassCastExceptionkotlin/-class-cast-exception/index.html +$dokka.location:kotlin.Comparatorkotlin/-comparator/index.html +$dokka.location:kotlin.ConcurrentModificationExceptionkotlin/-concurrent-modification-exception/index.html +$dokka.location:kotlin.Errorkotlin/-error/index.html +$dokka.location:kotlin.Exceptionkotlin/-exception/index.html +$dokka.location:kotlin.IllegalArgumentExceptionkotlin/-illegal-argument-exception/index.html +$dokka.location:kotlin.IllegalStateExceptionkotlin/-illegal-state-exception/index.html +$dokka.location:kotlin.IndexOutOfBoundsExceptionkotlin/-index-out-of-bounds-exception/index.html +$dokka.location:kotlin.NoSuchElementExceptionkotlin/-no-such-element-exception/index.html +$dokka.location:kotlin.NullPointerExceptionkotlin/-null-pointer-exception/index.html +$dokka.location:kotlin.NumberFormatExceptionkotlin/-number-format-exception/index.html +$dokka.location:kotlin.RuntimeExceptionkotlin/-runtime-exception/index.html +$dokka.location:kotlin.Synchronizedkotlin/-synchronized/index.html +$dokka.location:kotlin.UnsupportedOperationExceptionkotlin/-unsupported-operation-exception/index.html +$dokka.location:kotlin.Volatilekotlin/-volatile/index.html +$dokka.location:kotlin.collections$getOrPut(java.util.concurrent.ConcurrentMap((kotlin.collections.getOrPut.K, kotlin.collections.getOrPut.V)), kotlin.collections.getOrPut.K, kotlin.Function0((kotlin.collections.getOrPut.V)))kotlin.collections/java.util.concurrent.-concurrent-map/get-or-put.html +$dokka.location:kotlin.collections$iterator(java.util.Enumeration((kotlin.collections.iterator.T)))kotlin.collections/java.util.-enumeration/iterator.html +$dokka.location:kotlin.collections$toList(java.util.Enumeration((kotlin.collections.toList.T)))kotlin.collections/java.util.-enumeration/to-list.html +$dokka.location:kotlin.collections.ArrayListkotlin.collections/-array-list/index.html +$dokka.location:kotlin.collections.HashMapkotlin.collections/-hash-map/index.html +$dokka.location:kotlin.collections.HashSetkotlin.collections/-hash-set/index.html +$dokka.location:kotlin.collections.LinkedHashMapkotlin.collections/-linked-hash-map/index.html +$dokka.location:kotlin.collections.LinkedHashSetkotlin.collections/-linked-hash-set/index.html +$dokka.location:kotlin.concurrent$getOrSet(java.lang.ThreadLocal((kotlin.concurrent.getOrSet.T)), kotlin.Function0((kotlin.concurrent.getOrSet.T)))kotlin.concurrent/java.lang.-thread-local/get-or-set.html +$dokka.location:kotlin.concurrent$read(java.util.concurrent.locks.ReentrantReadWriteLock, kotlin.Function0((kotlin.concurrent.read.T)))kotlin.concurrent/java.util.concurrent.locks.-reentrant-read-write-lock/read.html +$dokka.location:kotlin.concurrent$schedule(java.util.Timer, java.util.Date, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html +$dokka.location:kotlin.concurrent$schedule(java.util.Timer, java.util.Date, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html +$dokka.location:kotlin.concurrent$schedule(java.util.Timer, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html +$dokka.location:kotlin.concurrent$schedule(java.util.Timer, kotlin.Long, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule.html +$dokka.location:kotlin.concurrent$scheduleAtFixedRate(java.util.Timer, java.util.Date, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule-at-fixed-rate.html +$dokka.location:kotlin.concurrent$scheduleAtFixedRate(java.util.Timer, kotlin.Long, kotlin.Long, kotlin.Function1((java.util.TimerTask, kotlin.Unit)))kotlin.concurrent/java.util.-timer/schedule-at-fixed-rate.html +$dokka.location:kotlin.concurrent$withLock(java.util.concurrent.locks.Lock, kotlin.Function0((kotlin.concurrent.withLock.T)))kotlin.concurrent/java.util.concurrent.locks.-lock/with-lock.html +$dokka.location:kotlin.concurrent$write(java.util.concurrent.locks.ReentrantReadWriteLock, kotlin.Function0((kotlin.concurrent.write.T)))kotlin.concurrent/java.util.concurrent.locks.-reentrant-read-write-lock/write.html +$dokka.location:kotlin.io$appendBytes(java.io.File, kotlin.ByteArray)kotlin.io/java.io.-file/append-bytes.html +$dokka.location:kotlin.io$appendText(java.io.File, kotlin.String, java.nio.charset.Charset)kotlin.io/java.io.-file/append-text.html +$dokka.location:kotlin.io$buffered(java.io.InputStream, kotlin.Int)kotlin.io/java.io.-input-stream/buffered.html +$dokka.location:kotlin.io$buffered(java.io.OutputStream, kotlin.Int)kotlin.io/java.io.-output-stream/buffered.html +$dokka.location:kotlin.io$buffered(java.io.Reader, kotlin.Int)kotlin.io/java.io.-reader/buffered.html +$dokka.location:kotlin.io$buffered(java.io.Writer, kotlin.Int)kotlin.io/java.io.-writer/buffered.html +$dokka.location:kotlin.io$bufferedReader(java.io.File, java.nio.charset.Charset, kotlin.Int)kotlin.io/java.io.-file/buffered-reader.html +$dokka.location:kotlin.io$bufferedReader(java.io.InputStream, java.nio.charset.Charset)kotlin.io/java.io.-input-stream/buffered-reader.html +$dokka.location:kotlin.io$bufferedWriter(java.io.File, java.nio.charset.Charset, kotlin.Int)kotlin.io/java.io.-file/buffered-writer.html +$dokka.location:kotlin.io$bufferedWriter(java.io.OutputStream, java.nio.charset.Charset)kotlin.io/java.io.-output-stream/buffered-writer.html +$dokka.location:kotlin.io$copyRecursively(java.io.File, java.io.File, kotlin.Boolean, kotlin.Function2((java.io.File, java.io.IOException, kotlin.io.OnErrorAction)))kotlin.io/java.io.-file/copy-recursively.html +$dokka.location:kotlin.io$copyTo(java.io.File, java.io.File, kotlin.Boolean, kotlin.Int)kotlin.io/java.io.-file/copy-to.html +$dokka.location:kotlin.io$copyTo(java.io.InputStream, java.io.OutputStream, kotlin.Int)kotlin.io/java.io.-input-stream/copy-to.html +$dokka.location:kotlin.io$copyTo(java.io.Reader, java.io.Writer, kotlin.Int)kotlin.io/java.io.-reader/copy-to.html +$dokka.location:kotlin.io$deleteRecursively(java.io.File)kotlin.io/java.io.-file/delete-recursively.html +$dokka.location:kotlin.io$endsWith(java.io.File, java.io.File)kotlin.io/java.io.-file/ends-with.html +$dokka.location:kotlin.io$endsWith(java.io.File, kotlin.String)kotlin.io/java.io.-file/ends-with.html +$dokka.location:kotlin.io$extension#java.io.Filekotlin.io/java.io.-file/extension.html +$dokka.location:kotlin.io$forEachBlock(java.io.File, kotlin.Function2((kotlin.ByteArray, kotlin.Int, kotlin.Unit)))kotlin.io/java.io.-file/for-each-block.html +$dokka.location:kotlin.io$forEachBlock(java.io.File, kotlin.Int, kotlin.Function2((kotlin.ByteArray, kotlin.Int, kotlin.Unit)))kotlin.io/java.io.-file/for-each-block.html +$dokka.location:kotlin.io$forEachLine(java.io.File, java.nio.charset.Charset, kotlin.Function1((kotlin.String, kotlin.Unit)))kotlin.io/java.io.-file/for-each-line.html +$dokka.location:kotlin.io$forEachLine(java.io.Reader, kotlin.Function1((kotlin.String, kotlin.Unit)))kotlin.io/java.io.-reader/for-each-line.html +$dokka.location:kotlin.io$inputStream(java.io.File)kotlin.io/java.io.-file/input-stream.html +$dokka.location:kotlin.io$invariantSeparatorsPath#java.io.Filekotlin.io/java.io.-file/invariant-separators-path.html +$dokka.location:kotlin.io$isRooted#java.io.Filekotlin.io/java.io.-file/is-rooted.html +$dokka.location:kotlin.io$iterator(java.io.BufferedInputStream)kotlin.io/java.io.-buffered-input-stream/iterator.html +$dokka.location:kotlin.io$lineSequence(java.io.BufferedReader)kotlin.io/java.io.-buffered-reader/line-sequence.html +$dokka.location:kotlin.io$nameWithoutExtension#java.io.Filekotlin.io/java.io.-file/name-without-extension.html +$dokka.location:kotlin.io$normalize(java.io.File)kotlin.io/java.io.-file/normalize.html +$dokka.location:kotlin.io$outputStream(java.io.File)kotlin.io/java.io.-file/output-stream.html +$dokka.location:kotlin.io$printWriter(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/print-writer.html +$dokka.location:kotlin.io$readBytes(java.io.File)kotlin.io/java.io.-file/read-bytes.html +$dokka.location:kotlin.io$readBytes(java.io.InputStream)kotlin.io/java.io.-input-stream/read-bytes.html +$dokka.location:kotlin.io$readBytes(java.io.InputStream, kotlin.Int)kotlin.io/java.io.-input-stream/read-bytes.html +$dokka.location:kotlin.io$readBytes(java.net.URL)kotlin.io/java.net.-u-r-l/read-bytes.html +$dokka.location:kotlin.io$readLines(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/read-lines.html +$dokka.location:kotlin.io$readLines(java.io.Reader)kotlin.io/java.io.-reader/read-lines.html +$dokka.location:kotlin.io$readText(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/read-text.html +$dokka.location:kotlin.io$readText(java.io.Reader)kotlin.io/java.io.-reader/read-text.html +$dokka.location:kotlin.io$readText(java.net.URL, java.nio.charset.Charset)kotlin.io/java.net.-u-r-l/read-text.html +$dokka.location:kotlin.io$reader(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/reader.html +$dokka.location:kotlin.io$reader(java.io.InputStream, java.nio.charset.Charset)kotlin.io/java.io.-input-stream/reader.html +$dokka.location:kotlin.io$relativeTo(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to.html +$dokka.location:kotlin.io$relativeToOrNull(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to-or-null.html +$dokka.location:kotlin.io$relativeToOrSelf(java.io.File, java.io.File)kotlin.io/java.io.-file/relative-to-or-self.html +$dokka.location:kotlin.io$resolve(java.io.File, java.io.File)kotlin.io/java.io.-file/resolve.html +$dokka.location:kotlin.io$resolve(java.io.File, kotlin.String)kotlin.io/java.io.-file/resolve.html +$dokka.location:kotlin.io$resolveSibling(java.io.File, java.io.File)kotlin.io/java.io.-file/resolve-sibling.html +$dokka.location:kotlin.io$resolveSibling(java.io.File, kotlin.String)kotlin.io/java.io.-file/resolve-sibling.html +$dokka.location:kotlin.io$startsWith(java.io.File, java.io.File)kotlin.io/java.io.-file/starts-with.html +$dokka.location:kotlin.io$startsWith(java.io.File, kotlin.String)kotlin.io/java.io.-file/starts-with.html +$dokka.location:kotlin.io$toRelativeString(java.io.File, java.io.File)kotlin.io/java.io.-file/to-relative-string.html +$dokka.location:kotlin.io$useLines(java.io.File, java.nio.charset.Charset, kotlin.Function1((kotlin.sequences.Sequence((kotlin.String)), kotlin.io.useLines.T)))kotlin.io/java.io.-file/use-lines.html +$dokka.location:kotlin.io$useLines(java.io.Reader, kotlin.Function1((kotlin.sequences.Sequence((kotlin.String)), kotlin.io.useLines.T)))kotlin.io/java.io.-reader/use-lines.html +$dokka.location:kotlin.io$walk(java.io.File, kotlin.io.FileWalkDirection)kotlin.io/java.io.-file/walk.html +$dokka.location:kotlin.io$walkBottomUp(java.io.File)kotlin.io/java.io.-file/walk-bottom-up.html +$dokka.location:kotlin.io$walkTopDown(java.io.File)kotlin.io/java.io.-file/walk-top-down.html +$dokka.location:kotlin.io$writeBytes(java.io.File, kotlin.ByteArray)kotlin.io/java.io.-file/write-bytes.html +$dokka.location:kotlin.io$writeText(java.io.File, kotlin.String, java.nio.charset.Charset)kotlin.io/java.io.-file/write-text.html +$dokka.location:kotlin.io$writer(java.io.File, java.nio.charset.Charset)kotlin.io/java.io.-file/writer.html +$dokka.location:kotlin.io$writer(java.io.OutputStream, java.nio.charset.Charset)kotlin.io/java.io.-output-stream/writer.html +$dokka.location:kotlin.jvm$kotlin#java.lang.Class((kotlin.jvm.kotlin.T))kotlin.jvm/java.lang.-class/kotlin.html +$dokka.location:kotlin.random$asKotlinRandom(java.util.Random)kotlin.random/java.util.-random/as-kotlin-random.html +$dokka.location:kotlin.reflect.KAnnotatedElementkotlin.reflect/-k-annotated-element/index.html +$dokka.location:kotlin.reflect.KDeclarationContainerkotlin.reflect/-k-declaration-container/index.html +$dokka.location:kotlin.reflect.KFunctionkotlin.reflect/-k-function/index.html +$dokka.location:kotlin.reflect.KMutablePropertykotlin.reflect/-k-mutable-property/index.html +$dokka.location:kotlin.reflect.KPropertykotlin.reflect/-k-property/index.html +$dokka.location:kotlin.reflect.jvm$kotlinFunction#java.lang.reflect.Constructor((kotlin.reflect.jvm.kotlinFunction.T))kotlin.reflect.jvm/java.lang.reflect.-constructor/kotlin-function.html +$dokka.location:kotlin.reflect.jvm$kotlinFunction#java.lang.reflect.Methodkotlin.reflect.jvm/java.lang.reflect.-method/kotlin-function.html +$dokka.location:kotlin.reflect.jvm$kotlinProperty#java.lang.reflect.Fieldkotlin.reflect.jvm/java.lang.reflect.-field/kotlin-property.html +$dokka.location:kotlin.sequences$asSequence(java.util.Enumeration((kotlin.sequences.asSequence.T)))kotlin.sequences/java.util.-enumeration/as-sequence.html +$dokka.location:kotlin.streams$asSequence(java.util.stream.DoubleStream)kotlin.streams/java.util.stream.-double-stream/as-sequence.html +$dokka.location:kotlin.streams$asSequence(java.util.stream.IntStream)kotlin.streams/java.util.stream.-int-stream/as-sequence.html +$dokka.location:kotlin.streams$asSequence(java.util.stream.LongStream)kotlin.streams/java.util.stream.-long-stream/as-sequence.html +$dokka.location:kotlin.streams$asSequence(java.util.stream.Stream((kotlin.streams.asSequence.T)))kotlin.streams/java.util.stream.-stream/as-sequence.html +$dokka.location:kotlin.streams$asStream(kotlin.sequences.Sequence((kotlin.streams.asStream.T)))kotlin.streams/kotlin.sequences.-sequence/as-stream.html +$dokka.location:kotlin.streams$toList(java.util.stream.DoubleStream)kotlin.streams/java.util.stream.-double-stream/to-list.html +$dokka.location:kotlin.streams$toList(java.util.stream.IntStream)kotlin.streams/java.util.stream.-int-stream/to-list.html +$dokka.location:kotlin.streams$toList(java.util.stream.LongStream)kotlin.streams/java.util.stream.-long-stream/to-list.html +$dokka.location:kotlin.streams$toList(java.util.stream.Stream((kotlin.streams.toList.T)))kotlin.streams/java.util.stream.-stream/to-list.html +$dokka.location:kotlin.text$appendln(java.lang.Appendable)kotlin.text/java.lang.-appendable/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.Appendable, kotlin.Char)kotlin.text/java.lang.-appendable/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.Appendable, kotlin.CharSequence)kotlin.text/java.lang.-appendable/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, java.lang.StringBuffer)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Any)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Boolean)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Byte)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Char)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.CharArray)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.CharSequence)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Double)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Float)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Int)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Long)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.Short)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$appendln(java.lang.StringBuilder, kotlin.String)kotlin.text/java.lang.-string-builder/appendln.html +$dokka.location:kotlin.text$clear(java.lang.StringBuilder)kotlin.text/java.lang.-string-builder/clear.html +$dokka.location:kotlin.text$set(java.lang.StringBuilder, kotlin.Int, kotlin.Char)kotlin.text/java.lang.-string-builder/set.html +$dokka.location:kotlin.text$toRegex(java.util.regex.Pattern)kotlin.text/java.util.regex.-pattern/to-regex.html +$dokka.location:kotlin.text.Appendablekotlin.text/-appendable/index.html +$dokka.location:kotlin.text.CharacterCodingExceptionkotlin.text/-character-coding-exception/index.html +$dokka.location:kotlin.text.StringBuilderkotlin.text/-string-builder/index.html +kotlin +kotlin.annotation +kotlin.browser +kotlin.collections +kotlin.comparisons +kotlin.concurrent +kotlin.contracts +kotlin.coroutines +kotlin.coroutines.experimental +kotlin.coroutines.experimental.intrinsics +kotlin.coroutines.intrinsics +kotlin.dom +kotlin.experimental +kotlin.io +kotlin.js +kotlin.jvm +kotlin.math +kotlin.native +kotlin.native.concurrent +kotlin.native.ref +kotlin.properties +kotlin.random +kotlin.ranges +kotlin.reflect +kotlin.reflect.full +kotlin.reflect.jvm +kotlin.sequences +kotlin.streams +kotlin.system +kotlin.text +kotlinx.cinterop +kotlinx.cinterop.internal +kotlinx.wasm.jsinterop +org.khronos.webgl +org.w3c.css.masking +org.w3c.dom +org.w3c.dom.clipboard +org.w3c.dom.css +org.w3c.dom.events +org.w3c.dom.mediacapture +org.w3c.dom.parsing +org.w3c.dom.pointerevents +org.w3c.dom.svg +org.w3c.dom.url +org.w3c.fetch +org.w3c.files +org.w3c.notifications +org.w3c.performance +org.w3c.workers +org.w3c.xhr diff --git a/runners/android-gradle-plugin/build.gradle b/runners/android-gradle-plugin/build.gradle new file mode 100644 index 000000000..22403dc63 --- /dev/null +++ b/runners/android-gradle-plugin/build.gradle @@ -0,0 +1,102 @@ +import com.gradle.publish.DependenciesBuilder +import org.jetbrains.CorrectShadowPublishing + +apply plugin: 'java' +apply plugin: 'kotlin' + + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: "com.gradle.plugin-publish" + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + languageVersion = "1.2" + apiVersion = "1.1" + jvmTarget = "1.8" + } +} + +repositories { + jcenter() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + + shadow project(path: ':runners:gradle-plugin', configuration: 'shadow') + compileOnly project(':integration') + + compileOnly gradleApi() + compileOnly localGroovy() +} + +task sourceJar(type: Jar) { + from sourceSets.main.allSource +} + +processResources { + inputs.property("dokka_version", dokka_version) + eachFile { + if (it.name == "org.jetbrains.dokka-android.properties") { + it.filter { line -> + line.replace("<version>", dokka_version) + } + } + } +} + +shadowJar { + baseName = 'dokka-android-gradle-plugin' + classifier = '' +} + +apply plugin: 'maven-publish' + +publishing { + publications { + dokkaAndroidGradlePlugin(MavenPublication) { MavenPublication publication -> + artifactId = 'dokka-android-gradle-plugin' + + artifact sourceJar { + classifier "sources" + } + + CorrectShadowPublishing.configure(publication, project) + } + } +} + +bintrayPublication(project, ['dokkaAndroidGradlePlugin']) + +configurations.archives.artifacts.clear() +artifacts { + archives shadowJar +} + +pluginBundle { + website = 'http://www.kotlinlang.org/' + vcsUrl = 'https://github.com/kotlin/dokka.git' + description = 'Dokka, the Kotlin documentation tool' + tags = ['dokka', 'kotlin', 'kdoc', 'android'] + + plugins { + dokkaAndroidGradlePlugin { + id = 'org.jetbrains.dokka-android' + displayName = 'Dokka Android plugin' + } + } + + withDependencies { List<Dependency> list -> + list.clear() + def builder = new DependenciesBuilder() + builder.addUniqueScopedDependencies(list, configurations.shadow, "compile") + } + + mavenCoordinates { + groupId = "org.jetbrains.dokka" + artifactId = "dokka-android-gradle-plugin" + } +} diff --git a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt new file mode 100644 index 000000000..bd2e88c23 --- /dev/null +++ b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder +import java.io.File + +open class DokkaAndroidPlugin : Plugin<Project> { + override fun apply(project: Project) { + DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties")) + project.tasks.create("dokka", DokkaAndroidTask::class.java).apply { + moduleName = project.name + outputDirectory = File(project.buildDir, "dokka").absolutePath + } + } +} + +private val ANDROID_REFERENCE_URL = Builder("https://developer.android.com/reference/").build() + +open class DokkaAndroidTask : DokkaTask() { + + @Input var noAndroidSdkLink: Boolean = false + + override fun collectSuppressedFiles(sourceRoots: List<SourceRoot>): List<String> { + val generatedRoot = project.buildDir.resolve("generated").absoluteFile + return sourceRoots + .map { File(it.path) } + .filter { it.startsWith(generatedRoot) } + .flatMap { it.walk().toList() } + .map { it.absolutePath } + } + + init { + project.afterEvaluate { + if (!noAndroidSdkLink) externalDocumentationLinks.add(ANDROID_REFERENCE_URL) + } + } +} diff --git a/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties b/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties new file mode 100644 index 000000000..b204da7b3 --- /dev/null +++ b/runners/android-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka-android.properties @@ -0,0 +1,2 @@ +implementation-class=org.jetbrains.dokka.gradle.DokkaAndroidPlugin +dokka-version=<version>
\ No newline at end of file diff --git a/runners/ant/build.gradle b/runners/ant/build.gradle new file mode 100644 index 000000000..e7dcd441e --- /dev/null +++ b/runners/ant/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'kotlin' + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + languageVersion = "1.2" + apiVersion = languageVersion + jvmTarget = "1.8" + } +} + +dependencies { + compile project(":core") + compileOnly group: 'org.apache.ant', name: 'ant', version: ant_version +} + diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt new file mode 100644 index 000000000..20cf8c42f --- /dev/null +++ b/runners/ant/src/main/kotlin/ant/dokka.kt @@ -0,0 +1,152 @@ +package org.jetbrains.dokka.ant + +import org.apache.tools.ant.BuildException +import org.apache.tools.ant.Project +import org.apache.tools.ant.Task +import org.apache.tools.ant.types.Path +import org.apache.tools.ant.types.Reference +import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink +import java.io.File + +class AntLogger(val task: Task): DokkaLogger { + override fun info(message: String) = task.log(message, Project.MSG_INFO) + override fun warn(message: String) = task.log(message, Project.MSG_WARN) + override fun error(message: String) = task.log(message, Project.MSG_ERR) +} + +class AntSourceLinkDefinition(var path: String? = null, var url: String? = null, var lineSuffix: String? = null) + +class AntSourceRoot(var path: String? = null, var platforms: String? = null) { + fun toSourceRoot(): SourceRootImpl? = path?.let { + path -> + SourceRootImpl(path, platforms?.split(',').orEmpty()) + } +} + +class AntPackageOptions( + override var prefix: String = "", + override var includeNonPublic: Boolean = false, + override var reportUndocumented: Boolean = true, + override var skipDeprecated: Boolean = false, + override var suppress: Boolean = false) : DokkaConfiguration.PackageOptions + + +class DokkaAntTask: Task() { + var moduleName: String? = null + var outputDir: String? = null + var outputFormat: String = "html" + var impliedPlatforms: String = "" + var jdkVersion: Int = 6 + + var noStdlibLink: Boolean = false + var noJdkLink: Boolean = false + + var skipDeprecated: Boolean = false + + var cacheRoot: String? = null + + var languageVersion: String? = null + var apiVersion: String? = null + + var generateClassIndexPage: Boolean = true + var generatePackageIndexPage: Boolean = true + var outlineRoot: String = "" + var dacRoot: String = "" + + val compileClasspath: Path by lazy { Path(getProject()) } + val sourcePath: Path by lazy { Path(getProject()) } + val samplesPath: Path by lazy { Path(getProject()) } + val includesPath: Path by lazy { Path(getProject()) } + + val antSourceLinks: MutableList<AntSourceLinkDefinition> = arrayListOf() + val antSourceRoots: MutableList<AntSourceRoot> = arrayListOf() + val antPackageOptions: MutableList<AntPackageOptions> = arrayListOf() + val antExternalDocumentationLinks = mutableListOf<ExternalDocumentationLink.Builder>() + + fun setClasspath(classpath: Path) { + compileClasspath.append(classpath) + } + + fun setClasspathRef(ref: Reference) { + compileClasspath.createPath().refid = ref + } + + fun setSrc(src: Path) { + sourcePath.append(src) + } + + fun setSrcRef(ref: Reference) { + sourcePath.createPath().refid = ref + } + + fun setSamples(samples: Path) { + samplesPath.append(samples) + } + + fun setSamplesRef(ref: Reference) { + samplesPath.createPath().refid = ref + } + + fun setInclude(include: Path) { + includesPath.append(include) + } + + fun createSourceLink(): AntSourceLinkDefinition { + val def = AntSourceLinkDefinition() + antSourceLinks.add(def) + return def + } + + fun createSourceRoot(): AntSourceRoot = AntSourceRoot().apply { antSourceRoots.add(this) } + + fun createPackageOptions(): AntPackageOptions = AntPackageOptions().apply { antPackageOptions.add(this) } + + fun createExternalDocumentationLink() = ExternalDocumentationLink.Builder().apply { antExternalDocumentationLinks.add(this) } + + override fun execute() { + if (sourcePath.list().isEmpty() && antSourceRoots.isEmpty()) { + throw BuildException("At least one source path needs to be specified") + } + if (moduleName == null) { + throw BuildException("Module name needs to be specified") + } + if (outputDir == null) { + throw BuildException("Output directory needs to be specified") + } + val sourceLinks = antSourceLinks.map { + val path = it.path ?: throw BuildException("'path' attribute of a <sourceLink> element is required") + val url = it.url ?: throw BuildException("'url' attribute of a <sourceLink> element is required") + SourceLinkDefinitionImpl(File(path).canonicalFile.absolutePath, url, it.lineSuffix) + } + + val generator = DokkaGenerator( + AntLogger(this), + compileClasspath.list().toList(), + sourcePath.list().map { SourceRootImpl(it) } + antSourceRoots.mapNotNull { it.toSourceRoot() }, + samplesPath.list().toList(), + includesPath.list().toList(), + moduleName!!, + DocumentationOptions( + outputDir!!, + outputFormat, + skipDeprecated = skipDeprecated, + sourceLinks = sourceLinks, + jdkVersion = jdkVersion, + impliedPlatforms = impliedPlatforms.split(','), + perPackageOptions = antPackageOptions, + externalDocumentationLinks = antExternalDocumentationLinks.map { it.build() }, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + cacheRoot = cacheRoot, + languageVersion = languageVersion, + apiVersion = apiVersion, + generatePackageIndexPage = generatePackageIndexPage, + generateClassIndexPage = generateClassIndexPage, + outlineRoot = outlineRoot, + dacRoot = dacRoot + ) + ) + generator.generate() + } +}
\ No newline at end of file diff --git a/runners/ant/src/main/resources/dokka-antlib.xml b/runners/ant/src/main/resources/dokka-antlib.xml new file mode 100644 index 000000000..9c3373d52 --- /dev/null +++ b/runners/ant/src/main/resources/dokka-antlib.xml @@ -0,0 +1,3 @@ +<antlib> + <taskdef name="dokka" classname="org.jetbrains.dokka.ant.DokkaAntTask"/> +</antlib> diff --git a/runners/build.gradle b/runners/build.gradle new file mode 100644 index 000000000..23d232d2e --- /dev/null +++ b/runners/build.gradle @@ -0,0 +1,7 @@ +subprojects { + buildscript { + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } + } +}
\ No newline at end of file diff --git a/runners/cli/build.gradle b/runners/cli/build.gradle new file mode 100644 index 000000000..7f733140e --- /dev/null +++ b/runners/cli/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'kotlin' + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + languageVersion = "1.2" + apiVersion = languageVersion + jvmTarget = "1.8" + } +} + +dependencies { + compile project(":core") + compile "com.github.spullara.cli-parser:cli-parser:1.1.1" +} diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt new file mode 100644 index 000000000..37be6e289 --- /dev/null +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -0,0 +1,221 @@ +package org.jetbrains.dokka + + +import com.sampullara.cli.Args +import com.sampullara.cli.Argument +import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink +import org.jetbrains.dokka.Utilities.DownloadSamples + +import java.io.File +import java.net.MalformedURLException +import java.net.URL +import java.net.URLClassLoader + +class DokkaArguments { + @set:Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)") + var src: String = "" + + @set:Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code") + var srcLink: String = "" + + @set:Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)") + var include: String = "" + + @set:Argument(value = "samples", description = "Source root for samples") + var samples: String = "" + + @set:Argument(value = "useSamplesURL", description = "Download samples from URL into root folder") + var useSamplesURL: Boolean = false + + @set:Argument(value = "output", description = "Output directory path") + var outputDir: String = "out/doc/" + + @set:Argument(value = "format", description = "Output format (text, html, markdown, jekyll, kotlin-website)") + var outputFormat: String = "html" + + @set:Argument(value = "module", description = "Name of the documentation module") + var moduleName: String = "" + + @set:Argument(value = "classpath", description = "Classpath for symbol resolution") + var classpath: String = "" + + @set:Argument(value = "nodeprecated", description = "Exclude deprecated members from documentation") + var nodeprecated: Boolean = false + + @set:Argument(value = "jdkVersion", description = "Version of JDK to use for linking to JDK JavaDoc") + var jdkVersion: Int = 6 + + @set:Argument(value = "impliedPlatforms", description = "List of implied platforms (comma-separated)") + var impliedPlatforms: String = "" + + @set:Argument(value = "packageOptions", description = "List of package options in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" ") + var packageOptions: String = "" + + @set:Argument(value = "links", description = "External documentation links in format url^packageListUrl^^url2...") + var links: String = "" + + @set:Argument(value = "noStdlibLink", description = "Disable documentation link to stdlib") + var noStdlibLink: Boolean = false + + @set:Argument(value = "noJdkLink", description = "Disable documentation link to jdk") + var noJdkLink: Boolean = false + + @set:Argument(value = "cacheRoot", description = "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled") + var cacheRoot: String? = null + + @set:Argument(value = "languageVersion", description = "Language Version to pass to Kotlin Analysis") + var languageVersion: String? = null + + @set:Argument(value = "apiVersion", description = "Kotlin Api Version to pass to Kotlin Analysis") + var apiVersion: String? = null + + @set:Argument(value = "collectInheritedExtensionsFromLibraries", description = "Search for applicable extensions in libraries") + var collectInheritedExtensionsFromLibraries: Boolean = false + + @set:Argument(value = "generateClassIndexPage", description = "Generate classes.html index page. [Deprecated]: use noGenerateClassIndexPage instead.") + @Deprecated("There is no way to set this to false.", replaceWith = ReplaceWith( + expression ="noGenerateClassIndexPage")) + var generateClassIndexPage: Boolean = true + + @set:Argument(value = "generatePackageIndexPage", description = "Generate packages.html index page. [Deprecated]: use noGeneratePackageIndexPage instead.") + @Deprecated("There is no way to set this to false.", replaceWith = ReplaceWith( + expression ="noGeneratePackageIndexPage")) + var generatePackageIndexPage: Boolean = true + + @set:Argument(value = "noGenerateClassIndexPage", description = "Disable classes.html index page.") + var noGenerateClassIndexPage: Boolean = false + + @set:Argument(value = "noGeneratePackageIndexPage", description = "Disable packages.html index page.") + var noGeneratePackageIndexPage: Boolean = false + + @set:Argument(value = "outlineRoot", description = "Relative root directory the outline files. I.e. androidx/core/") + var outlineRoot: String = "" + + @set:Argument(value = "dacRoot", description = "Root directory in DAC of this library. I.e: /reference/kotlin") + var dacRoot: String = "" +} + + +object MainKt { + + fun parseLinks(links: String): List<ExternalDocumentationLink> { + val (parsedLinks, parsedOfflineLinks) = links.split("^^") + .map { it.split("^").map { it.trim() }.filter { it.isNotBlank() } } + .filter { it.isNotEmpty() } + .partition { it.size == 1 } + + return parsedLinks.map { (root) -> ExternalDocumentationLink.Builder(root).build() } + + parsedOfflineLinks.map { (root, packageList) -> + val rootUrl = URL(root) + val packageListUrl = + try { + URL(packageList) + } catch (ex: MalformedURLException) { + File(packageList).toURI().toURL() + } + ExternalDocumentationLink.Builder(rootUrl, packageListUrl).build() + } + } + + @JvmStatic + fun entry(args: Array<String>) { + val arguments = DokkaArguments() + val freeArgs: List<String> = Args.parse(arguments, args) ?: listOf() + val sources = if (arguments.src.isNotEmpty()) arguments.src.split(File.pathSeparatorChar).toList() + freeArgs else freeArgs + val samples = if (arguments.samples.isNotEmpty()) arguments.samples.split(File.pathSeparatorChar).toList() else listOf() + val includes = if (arguments.include.isNotEmpty()) arguments.include.split(File.pathSeparatorChar).toList() else listOf() + + val sourceLinks = if (arguments.srcLink.isNotEmpty() && arguments.srcLink.contains("=")) + listOf(SourceLinkDefinitionImpl.parseSourceLinkDefinition(arguments.srcLink)) + else { + if (arguments.srcLink.isNotEmpty()) { + println("Warning: Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.") + } + listOf() + } + + val classPath = arguments.classpath.split(File.pathSeparatorChar).toList() + + if (arguments.useSamplesURL) DownloadSamples.downloadSamples() + + val documentationOptions = DocumentationOptions( + arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' }, + arguments.outputFormat, + skipDeprecated = arguments.nodeprecated, + sourceLinks = sourceLinks, + impliedPlatforms = arguments.impliedPlatforms.split(','), + perPackageOptions = parsePerPackageOptions(arguments.packageOptions), + jdkVersion = arguments.jdkVersion, + externalDocumentationLinks = parseLinks(arguments.links), + noStdlibLink = arguments.noStdlibLink, + cacheRoot = arguments.cacheRoot, + languageVersion = arguments.languageVersion, + apiVersion = arguments.apiVersion, + collectInheritedExtensionsFromLibraries = arguments.collectInheritedExtensionsFromLibraries, + generateClassIndexPage = !arguments.noGenerateClassIndexPage, + generatePackageIndexPage = !arguments.noGeneratePackageIndexPage, + outlineRoot = arguments.outlineRoot, + dacRoot = arguments.dacRoot, + noJdkLink = arguments.noJdkLink + ) + + val generator = DokkaGenerator( + DokkaConsoleLogger, + classPath, + sources.map(SourceRootImpl.Companion::parseSourceRoot), + samples, + includes, + arguments.moduleName, + documentationOptions) + + generator.generate() + DokkaConsoleLogger.report() + } + + fun findToolsJar(): File { + val javaHome = System.getProperty("java.home") + val default = File(javaHome, "../lib/tools.jar") + val mac = File(javaHome, "../Classes/classes.jar") + when { + default.exists() -> return default + mac.exists() -> return mac + else -> { + throw Exception("tools.jar not found, please check it, also you can provide it manually, using -cp") + } + } + } + + fun createClassLoaderWithTools(): ClassLoader { + val toolsJar = findToolsJar().canonicalFile.toURI().toURL() + val originalUrls = (javaClass.classLoader as? URLClassLoader)?.urLs + val dokkaJar = javaClass.protectionDomain.codeSource.location + val urls = if (originalUrls != null) arrayOf(toolsJar, *originalUrls) else arrayOf(toolsJar, dokkaJar) + return URLClassLoader(urls, ClassLoader.getSystemClassLoader().parent) + } + + fun startWithToolsJar(args: Array<String>) { + try { + javaClass.classLoader.loadClass("com.sun.tools.doclets.formats.html.HtmlDoclet") + entry(args) + } catch (e: ClassNotFoundException) { + val classLoader = createClassLoaderWithTools() + classLoader.loadClass("org.jetbrains.dokka.MainKt") + .methods.find { it.name == "entry" }!! + .invoke(null, args) + } + } + + @JvmStatic + fun main(args: Array<String>) { + val arguments = DokkaArguments() + Args.parse(arguments, args) + + if (arguments.outputFormat == "javadoc") + startWithToolsJar(args) + else + entry(args) + } +} + + + diff --git a/runners/fatjar/build.gradle b/runners/fatjar/build.gradle new file mode 100644 index 000000000..4ce0416ca --- /dev/null +++ b/runners/fatjar/build.gradle @@ -0,0 +1,50 @@ +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import org.jetbrains.PluginXmlTransformer + +apply plugin: 'java' +apply plugin: 'com.github.johnrengelman.shadow' + +dependencies { + compile project(":runners:cli") + compile project(":runners:ant") +} + +jar { + manifest { + attributes 'Main-Class': 'org.jetbrains.dokka.MainKt' + } +} + +shadowJar { + baseName = 'dokka-fatjar' + classifier = '' + + configurations { + exclude compileOnly + } + + transform(ServiceFileTransformer) + transform(PluginXmlTransformer) + + exclude 'colorScheme/**' + exclude 'fileTemplates/**' + exclude 'inspectionDescriptions/**' + exclude 'intentionDescriptions/**' + + exclude 'src/**' + + relocate('kotlin.reflect.full', 'kotlin.reflect') +} + +apply plugin: 'maven-publish' + +publishing { + publications { + dokkaFatJar(MavenPublication) { publication -> + artifactId = 'dokka-fatjar' + project.shadow.component(publication) + } + } +} + +bintrayPublication(project, ["dokkaFatJar"])
\ No newline at end of file diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-license b/runners/gradle-integration-tests/android-licenses/android-sdk-license new file mode 100644 index 000000000..c311cf48c --- /dev/null +++ b/runners/gradle-integration-tests/android-licenses/android-sdk-license @@ -0,0 +1,2 @@ + +d56f5187479451eabf01fb78af6dfcb131a6481e
\ No newline at end of file diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license b/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license new file mode 100644 index 000000000..da4552d2c --- /dev/null +++ b/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license @@ -0,0 +1,2 @@ + +84831b9409646a918e30573bab4c9c91346d8abd
\ No newline at end of file diff --git a/runners/gradle-integration-tests/build.gradle b/runners/gradle-integration-tests/build.gradle new file mode 100644 index 000000000..a681c82e3 --- /dev/null +++ b/runners/gradle-integration-tests/build.gradle @@ -0,0 +1,59 @@ + + +apply plugin: 'kotlin' + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + languageVersion = "1.2" + apiVersion = "1.0" + jvmTarget = "1.8" + } +} + +configurations { + dokkaPlugin + dokkaAndroidPlugin + dokkaFatJar +} + +dependencies { + + testCompileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version + testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_for_gradle_runtime_version + testCompile ideaRT() + + dokkaPlugin project(path: ':runners:gradle-plugin', configuration: 'shadow') + dokkaAndroidPlugin project(path: ':runners:android-gradle-plugin', configuration: 'shadow') + dokkaFatJar project(path: ":runners:fatjar", configuration: 'shadow') + + testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile gradleTestKit() +} + + + +task createClasspathManifest { + def outputDir = file("$buildDir/$name") + + inputs.files(configurations.dokkaPlugin + configurations.dokkaAndroidPlugin + configurations.dokkaFatJar) + outputs.dir outputDir + + doLast { + outputDir.mkdirs() + file("$outputDir/dokka-plugin-classpath.txt").text = configurations.dokkaPlugin.join("\n") + file("$outputDir/android-dokka-plugin-classpath.txt").text = configurations.dokkaAndroidPlugin.join("\n") + file("$outputDir/fatjar.txt").text = configurations.dokkaFatJar.join("\n") + } +} + + +createClasspathManifest.mustRunAfter project(":runners:fatjar").shadowJar +testClasses.dependsOn project(":runners:fatjar").shadowJar +testClasses.dependsOn createClasspathManifest + +test { + systemProperty "android.licenses.overwrite", project.findProperty("android.licenses.overwrite") ?: "" +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt new file mode 100644 index 000000000..c3fe2ea95 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import kotlin.test.assertEquals + +abstract class AbstractAndroidAppTest(val testDataRootPath: String) : AbstractDokkaAndroidGradleTest() { + + fun prepareTestData() { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.resolve("app").copy(tmpRoot.resolve("app")) + testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) + testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) + + androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) + } + + + data class AndroidPluginParams(val pluginVersion: String, val buildToolsVersion: String, val compileSdk: Int) { + fun asArguments(): List<String> = listOf( + "-Pabt_plugin_version=$pluginVersion", + "-Pabt_version=$buildToolsVersion", + "-Psdk_version=$compileSdk" + ) + } + + + protected fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) { + prepareTestData() + + val result = configure(gradleVersion, kotlinVersion, + arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments()) + .build() + + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome) + + val docsOutput = "app/build/dokka" + + checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput) + + checkNoErrorClasses(docsOutput) + checkNoUnresolvedLinks(docsOutput) + + checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>", + """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""") + } + +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt new file mode 100644 index 000000000..06753342b --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.gradle + +import org.junit.BeforeClass +import java.io.File + +abstract class AbstractDokkaAndroidGradleTest : AbstractDokkaGradleTest() { + + override val pluginClasspath: List<File> = androidPluginClasspathData.toFile().readLines().map { File(it) } + + companion object { + + @JvmStatic + @BeforeClass + fun acceptAndroidSdkLicenses() { + val sdkDir = androidLocalProperties?.toFile()?.let { + val lines = it.readLines().map { it.trim() } + val sdkDirLine = lines.firstOrNull { "sdk.dir" in it } + sdkDirLine?.substringAfter("=")?.trim() + } ?: System.getenv("ANDROID_HOME") + + if (sdkDir == null || sdkDir.isEmpty()) { + error("Android SDK home not set, " + + "try setting \$ANDROID_HOME " + + "or sdk.dir in runners/gradle-integration-tests/testData/android.local.properties") + } + val sdkDirFile = File(sdkDir) + if (!sdkDirFile.exists()) error("\$ANDROID_HOME and android.local.properties points to non-existing location") + val sdkLicensesDir = sdkDirFile.resolve("licenses") + + val acceptedLicenses = File("android-licenses") + acceptedLicenses.listFiles().forEach { licenseFile -> + val target = sdkLicensesDir.resolve(licenseFile.name) + if(!target.exists() || target.readText() != licenseFile.readText()) { + val overwrite = System.getProperty("android.licenses.overwrite", "false").toBoolean() + if (!target.exists() || overwrite) { + licenseFile.copyTo(target, true) + println("Accepted ${licenseFile.name}, by copying $licenseFile to $target") + } + } + + } + } + + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt new file mode 100644 index 000000000..255138a99 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt @@ -0,0 +1,108 @@ +package org.jetbrains.dokka.gradle + + +import com.intellij.rt.execution.junit.FileComparisonFailure +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + + +val testDataFolder = Paths.get("testData") + +val pluginClasspathData = Paths.get("build", "createClasspathManifest", "dokka-plugin-classpath.txt") +val androidPluginClasspathData = pluginClasspathData.resolveSibling("android-dokka-plugin-classpath.txt") + +val dokkaFatJarPathData = pluginClasspathData.resolveSibling("fatjar.txt") + +val androidLocalProperties = testDataFolder.resolve("android.local.properties").let { if (Files.exists(it)) it else null } + +abstract class AbstractDokkaGradleTest { + @get:Rule val testProjectDir = TemporaryFolder() + + open val pluginClasspath: List<File> = pluginClasspathData.toFile().readLines().map { File(it) } + + fun checkOutputStructure(expected: String, actualSubpath: String) { + val expectedPath = testDataFolder.resolve(expected) + val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() + + assertEqualsIgnoringSeparators(expectedPath.toFile(), buildString { + actualPath.toFile().writeStructure(this, File(actualPath.toFile(), ".")) + }) + } + + fun checkNoErrorClasses(actualSubpath: String, extension: String = "html", errorClassMarker: String = "ERROR CLASS") { + val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() + var checked = 0 + Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { + val text = it.toFile().readText() + + val noErrorClasses = text.replace(errorClassMarker, "?!") + + if (noErrorClasses != text) { + throw FileComparisonFailure("", noErrorClasses, text, null) + } + + checked++ + } + println("$checked files checked for error classes") + } + + fun checkNoUnresolvedLinks(actualSubpath: String, extension: String = "html", marker: Regex = "[\"']#[\"']".toRegex()) { + val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() + var checked = 0 + Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { + val text = it.toFile().readText() + + val noErrorClasses = text.replace(marker, "?!") + + if (noErrorClasses != text) { + throw FileComparisonFailure("", noErrorClasses, text, null) + } + + checked++ + } + println("$checked files checked for unresolved links") + } + + fun checkExternalLink(actualSubpath: String, linkBody: String, fullLink: String, extension: String = "html") { + val match = "!!match!!" + val notMatch = "!!not-match!!" + + val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() + var checked = 0 + var totalEntries = 0 + Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { + val text = it.toFile().readText() + + val textWithoutMatches = text.replace(fullLink, match) + + val textWithoutNonMatches = textWithoutMatches.replace(linkBody, notMatch) + + if (textWithoutNonMatches != textWithoutMatches) { + + val expected = textWithoutNonMatches.replace(notMatch, fullLink).replace(match, fullLink) + val actual = textWithoutMatches.replace(match, fullLink) + + throw FileComparisonFailure("", expected, actual, null) + } + if (text != textWithoutMatches) + totalEntries++ + + checked++ + } + println("$checked files checked for valid external links '$linkBody', found $totalEntries links") + } + + fun configure(gradleVersion: String = "3.5", kotlinVersion: String = "1.1.2", arguments: Array<String>): GradleRunner { + val fatjar = dokkaFatJarPathData.toFile().readText() + + return GradleRunner.create().withProjectDir(testProjectDir.root) + .withArguments("-Pdokka_fatjar=$fatjar", "-Ptest_kotlin_version=$kotlinVersion", *arguments) + .withPluginClasspath(pluginClasspath) + .withGradleVersion(gradleVersion) + .withDebug(true) + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt new file mode 100644 index 000000000..0ac4cbd96 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt @@ -0,0 +1,34 @@ +package org.jetbrains.dokka.gradle + +import org.junit.Ignore +import org.junit.Test + +class AndroidAppTest : AbstractAndroidAppTest("androidApp") { + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { + doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { + doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { + doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { + doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt new file mode 100644 index 000000000..cdd3fa848 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt @@ -0,0 +1,52 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Ignore +import org.junit.Test +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.test.assertEquals + +class AndroidLibDependsOnJavaLibTest: AbstractDokkaAndroidGradleTest() { + + private val testDataRootPath = "androidLibDependsOnJavaLib" + + private fun prepareTestData() { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.copy(tmpRoot) + + androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) + } + + + private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AbstractAndroidAppTest.AndroidPluginParams) { + prepareTestData() + + val result = configure(gradleVersion, kotlinVersion, + arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments()) + .build() + + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":lib:dokka")?.outcome) + + val docsOutput = "lib/build/dokka" + + checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput) + + checkNoErrorClasses(docsOutput) + checkNoUnresolvedLinks(docsOutput) + + checkExternalLink(docsOutput, "<span class=\"identifier\">LibClz</span>", + """<a href="https://example.com/example/jlib/LibClz.html"><span class="identifier">LibClz</span></a>""") + } + + // TODO: add test back + @Ignore + @Test + fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { + doTest("4.5", "1.2.20", AbstractAndroidAppTest.AndroidPluginParams("3.0.1", "27.0.0", 27)) + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt new file mode 100644 index 000000000..b0257f8bf --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt @@ -0,0 +1,73 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.jetbrains.dokka.gradle.AbstractAndroidAppTest.AndroidPluginParams +import org.junit.Ignore +import org.junit.Test +import kotlin.test.assertEquals + +class AndroidMultiFlavourAppTest : AbstractDokkaAndroidGradleTest() { + + fun prepareTestData(testDataRootPath: String) { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.resolve("app").copy(tmpRoot.resolve("app")) + testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) + testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) + + androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) + } + + private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) { + prepareTestData("androidMultiFlavourApp") + + val result = configure(gradleVersion, kotlinVersion, + arguments = arrayOf("dokka", "dokkaFullFlavourOnly", "--stacktrace") + androidPluginParams.asArguments()) + .build() + + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome) + assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokkaFullFlavourOnly")?.outcome) + + val docsOutput = "app/build/dokka" + + checkOutputStructure("androidMultiFlavourApp/fileTree.txt", docsOutput) + + checkNoErrorClasses(docsOutput) + checkNoUnresolvedLinks(docsOutput) + + checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>", + """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""") + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { + doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { + doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { + doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) + } + + // TODO FIXME + @Ignore + @Test + fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { + doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) + } + +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt new file mode 100644 index 000000000..ebaf16538 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt @@ -0,0 +1,55 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test +import kotlin.test.assertEquals + +class BasicTest : AbstractDokkaGradleTest() { + + fun prepareTestData(testDataRootPath: String) { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.resolve("src").copy(tmpRoot.resolve("src")) + testDataRoot.resolve("classDir").copy(tmpRoot.resolve("classDir")) + testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) + testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) + } + + private fun doTest(gradleVersion: String, kotlinVersion: String) { + + prepareTestData("basic") + + val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build() + + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) + + val docsOutput = "build/dokka" + + checkOutputStructure("basic/fileTree.txt", docsOutput) + + checkNoErrorClasses(docsOutput) + checkNoUnresolvedLinks(docsOutput) + + checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>", + """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""") + } + + @Test fun `test kotlin 1_1_2 and gradle 3_5`() { + doTest("3.5", "1.1.2") + } + + @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() { + doTest("2.14.1", "1.0.7") + } + + @Test fun `test kotlin 1_1_2 and gradle 4_0`() { + doTest("4.0", "1.1.2") + } + + @Test fun `test kotlin 1_2_20 and gradle 4_5`() { + doTest("4.5", "1.2.20") + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt new file mode 100644 index 000000000..18c20ed20 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka.gradle + +import org.junit.Test + +// TODO: add tests back +class JavadocRSuppressionTest : AbstractAndroidAppTest("androidAppJavadoc") { +// @Test +// fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { +// doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) +// } +// +// @Test +// fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { +// doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) +// } +// +// @Test +// fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { +// doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) +// } +// +// @Test fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { +// doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) +// } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt new file mode 100644 index 000000000..9458528cd --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt @@ -0,0 +1,57 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test +import kotlin.test.assertEquals + +class MultiProjectSingleOutTest : AbstractDokkaGradleTest() { + + fun prepareTestData(testDataRootPath: String) { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.apply { + resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) + resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) + resolve("subA").copy(tmpRoot.resolve("subA")) + resolve("subB").copy(tmpRoot.resolve("subB")) + } + } + + private fun doTest(gradleVersion: String, kotlinVersion: String) { + + prepareTestData("multiProjectSingleOut") + + val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build() + + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) + + val docsOutput = "build/dokka" + + checkOutputStructure("multiProjectSingleOut/fileTree.txt", docsOutput) + + checkNoErrorClasses(docsOutput) + checkNoUnresolvedLinks(docsOutput) + + checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>", + """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""") + } + + @Test fun `test kotlin 1_1_2 and gradle 3_5`() { + doTest("3.5", "1.1.2") + } + + @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() { + doTest("2.14.1", "1.0.7") + } + + @Test fun `test kotlin 1_1_2 and gradle 4_0`() { + doTest("4.0", "1.1.2") + } + + @Test fun `test kotlin 1_2_20 and gradle 4_5`() { + doTest("4.5", "1.2.20") + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt new file mode 100644 index 000000000..f712998c1 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt @@ -0,0 +1,74 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Test +import java.nio.file.Path +import kotlin.test.assertEquals + +class RebuildAfterSourceChangeTest : AbstractDokkaGradleTest() { + + fun prepareTestData(testDataRootPath: String): Pair<Path, Path> { + val testDataRoot = testDataFolder.resolve(testDataRootPath) + val tmpRoot = testProjectDir.root.toPath() + + testDataRoot.resolve("src").copy(tmpRoot.resolve("src")) + testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) + testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) + + return testDataRoot to tmpRoot + } + + private fun doTest(gradleVersion: String, kotlinVersion: String) { + + val (testDataRoot, tmpRoot) = prepareTestData("sourcesChange") + val docsOutput = "build/dokka" + + configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result -> + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) + } + + + configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result -> + println(result.output) + + assertEquals(TaskOutcome.UP_TO_DATE, result.task(":dokka")?.outcome) + } + + checkOutputStructure("sourcesChange/fileTree.txt", docsOutput) + + testDataRoot.resolve("src1").copy(tmpRoot.resolve("src")) + + configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result -> + println(result.output) + + assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) + } + + + checkOutputStructure("sourcesChange/fileTree1.txt", docsOutput) + + } + + + @Test + fun `test kotlin 1_1_2 and gradle 3_5`() { + doTest("3.5", "1.1.2") + } + + @Test + fun `test kotlin 1_0_7 and gradle 2_14_1`() { + doTest("2.14.1", "1.0.7") + } + + @Test + fun `test kotlin 1_1_2 and gradle 4_0`() { + doTest("4.0", "1.1.2") + } + + @Test + fun `test kotlin 1_2_20 and gradle 4_5`() { + doTest("4.5", "1.2.20") + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt new file mode 100644 index 000000000..6f17af228 --- /dev/null +++ b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt @@ -0,0 +1,56 @@ +package org.jetbrains.dokka.gradle + +import com.intellij.rt.execution.junit.FileComparisonFailure +import java.io.File +import java.io.IOException +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes + + +fun File.writeStructure(builder: StringBuilder, relativeTo: File = this, spaces: Int = 0) { + builder.append(" ".repeat(spaces)) + val out = if (this != relativeTo) this.relativeTo(relativeTo) else this + + builder.append(out) + if (this.isDirectory) { + builder.appendln("/") + this.listFiles().sortedBy { it.name }.forEach { it.writeStructure(builder, this, spaces + 4) } + } else { + builder.appendln() + } +} + +fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) { + if (!expectedFile.exists()) expectedFile.createNewFile() + val expectedText = expectedFile.readText().replace("\r\n", "\n") + val actualText = output.replace("\r\n", "\n") + + if (expectedText != actualText) + throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath) +} + +class CopyFileVisitor(private var sourcePath: Path?, private val targetPath: Path) : SimpleFileVisitor<Path>() { + + @Throws(IOException::class) + override fun preVisitDirectory(dir: Path, + attrs: BasicFileAttributes): FileVisitResult { + if (sourcePath == null) { + sourcePath = dir + } else { + Files.createDirectories(targetPath.resolve(sourcePath?.relativize(dir))) + } + return FileVisitResult.CONTINUE + } + + @Throws(IOException::class) + override fun visitFile(file: Path, + attrs: BasicFileAttributes): FileVisitResult { + Files.copy(file, targetPath.resolve(sourcePath?.relativize(file)), StandardCopyOption.REPLACE_EXISTING) + return FileVisitResult.CONTINUE + } +} + +fun Path.copy(to: Path) { + Files.walkFileTree(this, CopyFileVisitor(this, to)) +} + diff --git a/runners/gradle-integration-tests/testData/androidApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidApp/app/build.gradle new file mode 100644 index 000000000..1555de9fc --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + jcenter() + mavenLocal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + +plugins { + id 'org.jetbrains.dokka-android' +} + + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'org.jetbrains.dokka-android' + +android { + compileSdkVersion Integer.parseInt(sdk_version) + buildToolsVersion abt_version + + defaultConfig { + applicationId "org.example.kotlin.mixed" + minSdkVersion 14 + targetSdkVersion Integer.parseInt(sdk_version) + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt') + } + } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" +} + + +dokka { + dokkaFatJar = new File(dokka_fatjar) +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b4e1a892b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.example.kotlin.mixed" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + + <activity + android:name=".JavaActivity" + android:label="@string/title_activity_main_activity1" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity + android:name=".KotlinActivity" + android:label="@string/title_activity_main_activity2" /> + + </application> + +</manifest> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java new file mode 100644 index 000000000..3668c594f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java @@ -0,0 +1,34 @@ +package org.example.kotlin.mixed; + +import android.content.Intent; +import android.os.Bundle; +import android.app.Activity; +import android.view.Menu; +import android.view.View; +import android.widget.Button; + +public class JavaActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button next = (Button) findViewById(R.id.Button01); + next.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); + startActivityForResult(myIntent, 0); + } + }); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + +} diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt new file mode 100644 index 000000000..ca2f27b02 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt @@ -0,0 +1,28 @@ +package org.example.kotlin.mixed + +import android.content.Intent +import android.os.Bundle +import android.app.Activity +import android.view.Menu +import android.widget.Button + +class KotlinActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main2) + + val next = findViewById(R.id.Button02) as Button + next.setOnClickListener { + val intent: Intent = Intent() + setResult(RESULT_OK, intent) + finish() + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.main_activity2, menu) + return true + } +} diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..ede57c390 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 1" /> + + <Button android:text="Next" + android:id="@+id/Button01" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml new file mode 100644 index 000000000..d707536a9 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 2" /> + + <Button android:text="Next" + android:id="@+id/Button02" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..47c822467 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..d8f08bc2a --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">AndroidSample</string> + <string name="action_settings">Settings</string> + <string name="hello_world">Hello world!</string> + <string name="title_activity_main_activity1">JavaActivity</string> + <string name="title_activity_main_activity2">KotlinActivity</string> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..6ce89c7ba --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/build.gradle b/runners/gradle-integration-tests/testData/androidApp/build.gradle new file mode 100644 index 000000000..59477b520 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/build.gradle @@ -0,0 +1,21 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + maven { url 'https://maven.google.com' } + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "com.android.tools.build:gradle:$abt_plugin_version" + } +} + +allprojects { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } +} diff --git a/runners/gradle-integration-tests/testData/androidApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt new file mode 100644 index 000000000..3827b69e8 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt @@ -0,0 +1,19 @@ +/ + app/ + alltypes/ + index.html + index-outline.html + index.html + org.example.kotlin.mixed/ + -java-activity/ + -init-.html + index.html + on-create-options-menu.html + -kotlin-activity/ + -init-.html + index.html + on-create-options-menu.html + on-create.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/androidApp/settings.gradle b/runners/gradle-integration-tests/testData/androidApp/settings.gradle new file mode 100644 index 000000000..1feb28670 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidApp/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = "androidApp" + +include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle new file mode 100644 index 000000000..6a053a5e6 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + jcenter() + mavenLocal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + +plugins { + id 'org.jetbrains.dokka-android' +} + + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'org.jetbrains.dokka-android' + +android { + compileSdkVersion Integer.parseInt(sdk_version) + buildToolsVersion abt_version + + defaultConfig { + applicationId "org.example.kotlin.mixed" + minSdkVersion 14 + targetSdkVersion Integer.parseInt(sdk_version) + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt') + } + } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" +} + + +dokka { + outputFormat = "javadoc" + dokkaFatJar = new File(dokka_fatjar) +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b4e1a892b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.example.kotlin.mixed" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + + <activity + android:name=".JavaActivity" + android:label="@string/title_activity_main_activity1" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity + android:name=".KotlinActivity" + android:label="@string/title_activity_main_activity2" /> + + </application> + +</manifest> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java new file mode 100644 index 000000000..3668c594f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java @@ -0,0 +1,34 @@ +package org.example.kotlin.mixed; + +import android.content.Intent; +import android.os.Bundle; +import android.app.Activity; +import android.view.Menu; +import android.view.View; +import android.widget.Button; + +public class JavaActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button next = (Button) findViewById(R.id.Button01); + next.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); + startActivityForResult(myIntent, 0); + } + }); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + +} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt new file mode 100644 index 000000000..ca2f27b02 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt @@ -0,0 +1,28 @@ +package org.example.kotlin.mixed + +import android.content.Intent +import android.os.Bundle +import android.app.Activity +import android.view.Menu +import android.widget.Button + +class KotlinActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main2) + + val next = findViewById(R.id.Button02) as Button + next.setOnClickListener { + val intent: Intent = Intent() + setResult(RESULT_OK, intent) + finish() + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.main_activity2, menu) + return true + } +} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..ede57c390 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 1" /> + + <Button android:text="Next" + android:id="@+id/Button01" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml new file mode 100644 index 000000000..d707536a9 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 2" /> + + <Button android:text="Next" + android:id="@+id/Button02" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..47c822467 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..d8f08bc2a --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">AndroidSample</string> + <string name="action_settings">Settings</string> + <string name="hello_world">Hello world!</string> + <string name="title_activity_main_activity1">JavaActivity</string> + <string name="title_activity_main_activity2">KotlinActivity</string> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..6ce89c7ba --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle new file mode 100644 index 000000000..59477b520 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle @@ -0,0 +1,21 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + maven { url 'https://maven.google.com' } + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "com.android.tools.build:gradle:$abt_plugin_version" + } +} + +allprojects { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } +} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt b/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt new file mode 100644 index 000000000..c5e79eba5 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt @@ -0,0 +1,21 @@ +/ + allclasses-frame.html + allclasses-noframe.html + constant-values.html + deprecated-list.html + help-doc.html + index-all.html + index.html + org/ + example/ + kotlin/ + mixed/ + JavaActivity.html + KotlinActivity.html + package-frame.html + package-summary.html + package-tree.html + overview-tree.html + package-list + script.js + stylesheet.css diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle new file mode 100644 index 000000000..a4e67fea0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = "androidAppJavadoc" + +include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle new file mode 100644 index 000000000..736668ab1 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle @@ -0,0 +1,20 @@ +subprojects { + buildscript { + repositories { + mavenCentral() + jcenter() + maven { url 'https://maven.google.com' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + + } + + repositories { + mavenCentral() + jcenter() + maven { url 'https://maven.google.com' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt new file mode 100644 index 000000000..6c96a01ce --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt @@ -0,0 +1,14 @@ +/ + lib/ + alltypes/ + index.html + example/ + -lib-clz-use/ + -init-.html + f.html + index.html + index.html + index-outline.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle new file mode 100644 index 000000000..bbfeb03c2 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle @@ -0,0 +1 @@ +apply plugin: 'java' diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java new file mode 100644 index 000000000..1d9a6fb22 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java @@ -0,0 +1,5 @@ +package example.jlib; + +public class LibClz { + +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle new file mode 100644 index 000000000..0f27d365c --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle @@ -0,0 +1,39 @@ +buildscript { + dependencies { + classpath "com.android.tools.build:gradle:$abt_plugin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + + +plugins { + id 'org.jetbrains.dokka-android' +} + + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'org.jetbrains.dokka-android' + + +android { + compileSdkVersion Integer.parseInt(sdk_version) + buildToolsVersion abt_version + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } +} + +dependencies { + api(project(":jlib")) +} + +dokka { + dokkaFatJar = new File(dokka_fatjar) + + externalDocumentationLink { + url = new URL("https://example.com") + packageListUrl = file("$rootDir/package-list").toURI().toURL() + } +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml new file mode 100644 index 000000000..267f6efd1 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="example"> +</manifest> diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt new file mode 100644 index 000000000..d034a3a92 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt @@ -0,0 +1,13 @@ +package example + +import example.jlib.LibClz + +/** + * Uses jlib + */ +class LibClzUse { + /** + * Returns LibClz + */ + fun f(): LibClz = LibClz() +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list new file mode 100644 index 000000000..bf76058e1 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list @@ -0,0 +1 @@ +example.jlib
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle new file mode 100644 index 000000000..5b4250a03 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = "androidLibDependsOnJavaLib" + + +include(":lib") +include(":jlib")
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle new file mode 100644 index 000000000..ee68ba6d6 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle @@ -0,0 +1,75 @@ +buildscript { + repositories { + jcenter() + mavenLocal() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + +plugins { + id 'org.jetbrains.dokka-android' +} + + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'org.jetbrains.dokka-android' + +android { + compileSdkVersion Integer.parseInt(sdk_version) + buildToolsVersion abt_version + + defaultConfig { + applicationId "org.example.kotlin.mixed" + minSdkVersion 14 + targetSdkVersion Integer.parseInt(sdk_version) + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt') + } + } + + flavorDimensions "mode" + productFlavors { + free { + dimension "mode" + applicationIdSuffix ".free" + versionNameSuffix "-free" + } + full { + dimension "mode" + applicationIdSuffix ".full" + versionNameSuffix "-full" + } + } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + free.java.srcDirs += 'src/free/kotlin' + } +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" +} + + +dokka { + outputDirectory = "$buildDir/dokka/all" + dokkaFatJar = new File(dokka_fatjar) +} + +task dokkaFullFlavourOnly(type: org.jetbrains.dokka.gradle.DokkaAndroidTask) { + kotlinTasks { + ["compileFullReleaseKotlin"] + } + dokkaFatJar = new File(dokka_fatjar) + outputDirectory = "$buildDir/dokka/fullOnly" + moduleName = "full" +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml new file mode 100644 index 000000000..3ecbcd3a4 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml @@ -0,0 +1,9 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <application> + <activity + android:name="org.example.kotlin.mixed.free.AdActivity" + android:label="@string/title_activity_ad" + android:theme="@style/AppTheme"></activity> + </application> +</manifest> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt new file mode 100644 index 000000000..b0b980fda --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt @@ -0,0 +1,14 @@ +package org.example.kotlin.mixed.free + +import android.os.Bundle +import android.app.Activity +import org.example.kotlin.mixed.R + +class AdActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_ad) + } + +} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml new file mode 100644 index 000000000..e6443d053 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".free.AdActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Advertisment" /> + + <Button android:text="Next" + android:id="@+id/Button02" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml new file mode 100644 index 000000000..bbdf2d067 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="title_activity_ad">AdActivity</string> +</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b4e1a892b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.example.kotlin.mixed" > + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + + <activity + android:name=".JavaActivity" + android:label="@string/title_activity_main_activity1" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity + android:name=".KotlinActivity" + android:label="@string/title_activity_main_activity2" /> + + </application> + +</manifest> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java new file mode 100644 index 000000000..3668c594f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java @@ -0,0 +1,34 @@ +package org.example.kotlin.mixed; + +import android.content.Intent; +import android.os.Bundle; +import android.app.Activity; +import android.view.Menu; +import android.view.View; +import android.widget.Button; + +public class JavaActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + Button next = (Button) findViewById(R.id.Button01); + next.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); + startActivityForResult(myIntent, 0); + } + }); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + +} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt new file mode 100644 index 000000000..ca2f27b02 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt @@ -0,0 +1,28 @@ +package org.example.kotlin.mixed + +import android.content.Intent +import android.os.Bundle +import android.app.Activity +import android.view.Menu +import android.widget.Button + +class KotlinActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main2) + + val next = findViewById(R.id.Button02) as Button + next.setOnClickListener { + val intent: Intent = Intent() + setResult(RESULT_OK, intent) + finish() + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.main_activity2, menu) + return true + } +} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..ede57c390 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 1" /> + + <Button android:text="Next" + android:id="@+id/Button01" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml new file mode 100644 index 000000000..d707536a9 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml @@ -0,0 +1,24 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="Activity 2" /> + + <Button android:text="Next" + android:id="@+id/Button02" + android:layout_width="250px" + android:textSize="18px" + android:layout_height="55px"> + </Button> + +</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml new file mode 100644 index 000000000..f3b10b6c0 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml new file mode 100644 index 000000000..47c822467 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..d8f08bc2a --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">AndroidSample</string> + <string name="action_settings">Settings</string> + <string name="hello_world">Hello world!</string> + <string name="title_activity_main_activity1">JavaActivity</string> + <string name="title_activity_main_activity2">KotlinActivity</string> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..6ce89c7ba --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle new file mode 100644 index 000000000..59477b520 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle @@ -0,0 +1,21 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + maven { url 'https://maven.google.com' } + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "com.android.tools.build:gradle:$abt_plugin_version" + } +} + +allprojects { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } +} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt new file mode 100644 index 000000000..5e969d8b9 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt @@ -0,0 +1,45 @@ +/ + all/ + app/ + alltypes/ + index.html + index-outline.html + index.html + org.example.kotlin.mixed/ + -java-activity/ + -init-.html + index.html + on-create-options-menu.html + -kotlin-activity/ + -init-.html + index.html + on-create-options-menu.html + on-create.html + index.html + org.example.kotlin.mixed.free/ + -ad-activity/ + -init-.html + index.html + on-create.html + index.html + package-list + style.css + fullOnly/ + full/ + alltypes/ + index.html + index-outline.html + index.html + org.example.kotlin.mixed/ + -java-activity/ + -init-.html + index.html + on-create-options-menu.html + -kotlin-activity/ + -init-.html + index.html + on-create-options-menu.html + on-create.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle new file mode 100644 index 000000000..1feb28670 --- /dev/null +++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = "androidApp" + +include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/build.gradle b/runners/gradle-integration-tests/testData/basic/build.gradle new file mode 100644 index 000000000..4a259f500 --- /dev/null +++ b/runners/gradle-integration-tests/testData/basic/build.gradle @@ -0,0 +1,40 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + +plugins { + id 'org.jetbrains.dokka' +} + +apply plugin: 'kotlin' +apply plugin: 'org.jetbrains.dokka' + +repositories { + mavenCentral() + jcenter() + maven { + url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" + } + maven { + url "https://dl.bintray.com/kotlin/kotlin-dev" + } +} + +dependencies { + compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version + compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version +} + + +dokka { + dokkaFatJar = new File(dokka_fatjar) + classpath += files("$projectDir/classDir") +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class Binary files differnew file mode 100644 index 000000000..ccfff3007 --- /dev/null +++ b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class diff --git a/runners/gradle-integration-tests/testData/basic/fileTree.txt b/runners/gradle-integration-tests/testData/basic/fileTree.txt new file mode 100644 index 000000000..2ceae3715 --- /dev/null +++ b/runners/gradle-integration-tests/testData/basic/fileTree.txt @@ -0,0 +1,33 @@ +/ + basic/ + alltypes/ + index.html + demo/ + -a/ + -init-.html + index.html + p.html + -greeter/ + -init-.html + greet.html + index.html + name.html + -some-interface.html + -some-sub-type/ + -init-.html + index.html + -some-type/ + -init-.html + index.html + constructor.html + index.html + main.html + p1.-my-binary-class/ + index.html + test.html + str.html + x.html + index-outline.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/basic/settings.gradle b/runners/gradle-integration-tests/testData/basic/settings.gradle new file mode 100644 index 000000000..c36a146ca --- /dev/null +++ b/runners/gradle-integration-tests/testData/basic/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "basic"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 000000000..3d7bcb51d --- /dev/null +++ b/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,45 @@ +package demo + +import p1.MyBinaryClass + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} + +val str = "Hello! ".repeat(4) +val x: (a: String, b: Int) -> Int = { a, b -> 0 } + +interface SomeInterface +private class SomeImpl : SomeInterface + +fun SomeInterface.constructor(): SomeInterface { + return SomeImpl() +} + +open class SomeType +class SomeSubType : SomeType() + +fun SomeType.constructor(): SomeType { + return SomeSubType() +} + + +annotation class A(val p: String) + +val MyBinaryClass.test get() = s() + diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle new file mode 100644 index 000000000..68d93e30c --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'org.jetbrains.dokka' +} + +subprojects { + buildscript { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } + } + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } +} + +apply plugin: 'org.jetbrains.dokka' + +dokka { + kotlinTasks { + [":subA:compileKotlin", ":subB:compileKotlin"] + } + dokkaFatJar = new File(dokka_fatjar) +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt new file mode 100644 index 000000000..5624fca66 --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt @@ -0,0 +1,33 @@ +/ + multi-project-root/ + alltypes/ + index.html + index-outline.html + index.html + package-list + s1/ + -my-class/ + -init-.html + index.html + otherworks.html + -super/ + -init-.html + bar.html + foo.html + index.html + index.html + some-cool-thing.html + s2/ + -cooler/ + -init-.html + a.html + coolest.html + index.html + my-class.html + -superful/ + -init-.html + bar.html + index.html + index.html + main.html + style.css diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle new file mode 100644 index 000000000..283cc526b --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = "multiProjectRoot" + +include 'subA', 'subB'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle new file mode 100644 index 000000000..0600411e2 --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'kotlin' + +dependencies { + compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version + compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version +} diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt new file mode 100644 index 000000000..126d7f3e2 --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt @@ -0,0 +1,31 @@ +package s1 + +/** + * Coolest one + */ +fun someCoolThing(s: String) = s.repeat(2) + +/** + * Just a class + */ +class MyClass { + /** + * Ultimate answer to all questions + */ + fun otherworks(): Int = 42 +} + +/** + * Just a SUPER class + */ +open class Super { + /** + * Same as [MyClass.otherworks] + */ + fun foo(i: Int = 21) = i * 2 + + /** + * magic + */ + open fun bar() = foo() +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle new file mode 100644 index 000000000..7b8ff9f3c --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle @@ -0,0 +1,7 @@ +apply plugin: 'kotlin' + +dependencies { + compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version + compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version + compile project(":subA") +} diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt new file mode 100644 index 000000000..8a87590ab --- /dev/null +++ b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt @@ -0,0 +1,31 @@ +package s2 + +import s1.Super +import s1.MyClass +import s1.someCoolThing + +/** + * Just an entry-point + */ +fun main(args: Array<String>) { + +} + +/** + * Take a glass of hot water + */ +class Cooler { + val myClass = MyClass() + val a = myClass.otherworks() + val coolest = someCoolThing() +} + +/** + * Powerful + */ +class Superful : Super() { + /** + * Overriden magic + */ + override fun bar() = foo(20) * 2 +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/build.gradle b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle new file mode 100644 index 000000000..bc20e1cf8 --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle @@ -0,0 +1,39 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" } + maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" + } +} + +plugins { + id 'org.jetbrains.dokka' +} + +apply plugin: 'kotlin' +apply plugin: 'org.jetbrains.dokka' + +repositories { + mavenCentral() + jcenter() + maven { + url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" + } + maven { + url "https://dl.bintray.com/kotlin/kotlin-dev" + } +} + +dependencies { + compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version + compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version +} + + +dokka { + dokkaFatJar = new File(dokka_fatjar) +}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt new file mode 100644 index 000000000..09f3724be --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt @@ -0,0 +1,10 @@ +/ + sources-change/ + alltypes.html + demo/ + hello.html + index.html + index-outline.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt new file mode 100644 index 000000000..eeb377f7d --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt @@ -0,0 +1,11 @@ +/ + sources-change/ + alltypes.html + demo/ + hello.html + index.html + world.html + index-outline.html + index.html + package-list + style.css diff --git a/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle b/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle new file mode 100644 index 000000000..3fb032bfa --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "sourcesChange"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 000000000..c54dea508 --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,6 @@ +package demo + +/** + * @return Hello + */ +fun hello(): String = "Hello"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 000000000..53f22ff5c --- /dev/null +++ b/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,11 @@ +package demo + +/** + * @return Hello + */ +fun hello(): String = "Hello" + +/** + * @return World + */ +fun world(): String = "World"
\ No newline at end of file diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle new file mode 100644 index 000000000..4742dda5d --- /dev/null +++ b/runners/gradle-plugin/build.gradle @@ -0,0 +1,121 @@ +import com.gradle.publish.DependenciesBuilder + +apply plugin: 'java' +apply plugin: 'kotlin' + + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: "com.gradle.plugin-publish" + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + languageVersion = "1.2" + apiVersion = "1.1" + jvmTarget = "1.8" + } +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + + shadow group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version + shadow group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version + + compile project(":integration") + + compileOnly gradleApi() + compileOnly localGroovy() +} + +task sourceJar(type: Jar) { + from sourceSets.main.allSource +} + +processResources { + inputs.property("dokka_version", dokka_version) + eachFile { + if (it.name == "org.jetbrains.dokka.properties") { + it.filter { line -> + line.replace("<version>", dokka_version) + } + } + } +} + +shadowJar { + baseName = 'dokka-gradle-plugin' + classifier = '' +} + +apply plugin: 'maven-publish' + +publishing { + publications { + dokkaGradlePlugin(MavenPublication) { publication -> + + artifactId = 'dokka-gradle-plugin' + + artifact sourceJar { + classifier "sources" + } + + project.shadow.component(publication) + publication.pom { pom -> + // Add dokka-fatjar as a runtime dependency. + // This is a workaround until the Shadow jar can put project dependencies into the .pom: https://github.com/johnrengelman/shadow/commit/da82b37522b349aff414f571d2037682acd84f27 + pom.withXml { xml -> + def node = xml.asNode() + def deps = null + node.children().each { child -> + if (child.name().toString() == "dependencies") { + deps = child + } + } + if (deps == null) { + deps = node.appendNode("dependencies") + } + def dep = deps.appendNode("dependency") + dep.appendNode("groupId", "org.jetbrains.dokka") + dep.appendNode("artifactId", "dokka-fatjar") + dep.appendNode("version", dokka_version) + dep.appendNode("scope", "runtime") + } + } + } + } +} + +bintrayPublication(project, ['dokkaGradlePlugin']) + +configurations.archives.artifacts.clear() +artifacts { + archives shadowJar +} + +pluginBundle { + website = 'http://www.kotlinlang.org/' + vcsUrl = 'https://github.com/kotlin/dokka.git' + description = 'Dokka, the Kotlin documentation tool' + tags = ['dokka', 'kotlin', 'kdoc'] + + plugins { + dokkaGradlePlugin { + id = 'org.jetbrains.dokka' + displayName = 'Dokka plugin' + } + } + + withDependencies { List<Dependency> list -> + list.clear() + def builder = new DependenciesBuilder() + builder.addUniqueScopedDependencies(list, configurations.shadow, "compile") + } + + mavenCoordinates { + groupId = "org.jetbrains.dokka" + artifactId = "dokka-gradle-plugin" + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt b/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt new file mode 100644 index 000000000..7bdf2f9d1 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt @@ -0,0 +1,46 @@ +package org.jetbrains.dokka + +import java.lang.reflect.InvocationHandler +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.Proxy + + +/** + * Warning! Hard reflection magic used here. + * + * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm, + * to create access proxy for [delegate] into [targetClassLoader]. + */ +@Suppress("UNCHECKED_CAST") +inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, delegate: Any): T = + automagicProxy(targetClassLoader, T::class.java, delegate) as T + + +/** + * Warning! Hard reflection magic used here. + * + * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm, + * to create access proxy for [delegate] into [targetClassLoader]. + * + */ +fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any = + Proxy.newProxyInstance( + targetClassLoader, + arrayOf(targetType), + DelegatedInvocationHandler(delegate) + ) + +class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler { + + @Throws(Throwable::class) + override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any? { + val delegateMethod = delegate.javaClass.getMethod(method.name, *method.parameterTypes) + try { + delegateMethod.isAccessible = true + return delegateMethod.invoke(delegate, *(args ?: emptyArray())) + } catch (ex: InvocationTargetException) { + throw ex.targetException + } + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt new file mode 100644 index 000000000..9555fa828 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/main.kt @@ -0,0 +1,478 @@ +package org.jetbrains.dokka.gradle + +import groovy.lang.Closure +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginConvention +import org.gradle.api.tasks.* +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.compile.AbstractCompile +import org.jetbrains.dokka.* +import org.jetbrains.dokka.ReflectDsl.isNotInstance +import org.jetbrains.dokka.gradle.ClassloaderContainer.fatJarClassLoader +import org.jetbrains.dokka.gradle.DokkaVersion.version +import ru.yole.jkid.JsonExclude +import ru.yole.jkid.serialization.serialize +import java.io.File +import java.io.InputStream +import java.io.Serializable +import java.net.URLClassLoader +import java.util.* +import java.util.concurrent.Callable +import java.util.function.BiConsumer + +open class DokkaPlugin : Plugin<Project> { + + override fun apply(project: Project) { + DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties")) + project.tasks.create("dokka", DokkaTask::class.java).apply { + moduleName = project.name + outputDirectory = File(project.buildDir, "dokka").absolutePath + } + } +} + +object DokkaVersion { + var version: String? = null + + fun loadFrom(stream: InputStream) { + version = Properties().apply { + load(stream) + }.getProperty("dokka-version") + } +} + + +object ClassloaderContainer { + @JvmField + var fatJarClassLoader: ClassLoader? = null +} + +const val `deprecationMessage reportNotDocumented` = "Will be removed in 0.9.17, see dokka#243" + +open class DokkaTask : DefaultTask() { + + fun defaultKotlinTasks() = with(ReflectDsl) { + val abstractKotlinCompileClz = try { + project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE) + } catch (cnfe: ClassNotFoundException) { + logger.warn("$ABSTRACT_KOTLIN_COMPILE class not found, default kotlin tasks ignored") + return@with emptyList<Task>() + } + + return@with project.tasks.filter { it isInstance abstractKotlinCompileClz }.filter { "Test" !in it.name } + } + + init { + group = JavaBasePlugin.DOCUMENTATION_GROUP + description = "Generates dokka documentation for Kotlin" + + @Suppress("LeakingThis") + dependsOn(Callable { kotlinTasks.map { it.taskDependencies } }) + } + + @Input + var moduleName: String = "" + @Input + var outputFormat: String = "html" + @get:Internal // handled by getOutputDirectoryAsFile + var outputDirectory: String = "" + + + @Deprecated("Going to be removed in 0.9.16, use classpath + sourceDirs instead if kotlinTasks is not suitable for you") + @Input var processConfigurations: List<Any?> = emptyList() + + @InputFiles var classpath: Iterable<File> = arrayListOf() + + @Input + var includes: List<Any?> = arrayListOf() + @Input + var linkMappings: ArrayList<LinkMapping> = arrayListOf() + @Input + var samples: List<Any?> = arrayListOf() + @Input + var jdkVersion: Int = 6 + + @Input + var generateClassIndexPage = true + + @Input + var generatePackageIndexPage = true + + @Input + var sourceDirs: Iterable<File> = emptyList() + + @Input + var sourceRoots: MutableList<SourceRoot> = arrayListOf() + + @Input + var dokkaFatJar: Any = "org.jetbrains.dokka:dokka-fatjar:$version" + + @Input var includeNonPublic = false + @Input var skipDeprecated = false + @Input var skipEmptyPackages = true + + @Input var outlineRoot: String = "" + @Input var dacRoot: String = "" + + @get:Input + @Deprecated(`deprecationMessage reportNotDocumented`, replaceWith = ReplaceWith("reportUndocumented")) + var reportNotDocumented + get() = reportUndocumented + set(value) { + logger.warn("Dokka: reportNotDocumented is deprecated and " + `deprecationMessage reportNotDocumented`.decapitalize()) + reportUndocumented = value + } + + @Input var reportUndocumented = true + @Input var perPackageOptions: MutableList<PackageOptions> = arrayListOf() + @Input var impliedPlatforms: MutableList<String> = arrayListOf() + + @Input var externalDocumentationLinks = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>() + + @Input var noStdlibLink: Boolean = false + + @Input + var noJdkLink: Boolean = false + + @Optional @Input + var cacheRoot: String? = null + + + @Optional @Input + var languageVersion: String? = null + + @Optional @Input + var apiVersion: String? = null + + @Input + var collectInheritedExtensionsFromLibraries: Boolean = false + + @get:Internal + internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() } + + + private var kotlinTasksConfigurator: () -> List<Any?>? = { defaultKotlinTasks() } + private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks() } + + fun kotlinTasks(closure: Closure<Any?>) { + kotlinTasksConfigurator = { closure.call() as? List<Any?> } + } + + fun linkMapping(closure: Closure<Unit>) { + val mapping = LinkMapping() + closure.delegate = mapping + closure.call() + + if (mapping.path.isEmpty()) { + throw IllegalArgumentException("Link mapping should have dir") + } + if (mapping.url.isEmpty()) { + throw IllegalArgumentException("Link mapping should have url") + } + + linkMappings.add(mapping) + } + + fun sourceRoot(closure: Closure<Unit>) { + val sourceRoot = SourceRoot() + closure.delegate = sourceRoot + closure.call() + sourceRoots.add(sourceRoot) + } + + fun packageOptions(closure: Closure<Unit>) { + val packageOptions = PackageOptions() + closure.delegate = packageOptions + closure.call() + perPackageOptions.add(packageOptions) + } + + fun externalDocumentationLink(closure: Closure<Unit>) { + val builder = DokkaConfiguration.ExternalDocumentationLink.Builder() + closure.delegate = builder + closure.call() + externalDocumentationLinks.add(builder.build()) + } + + fun tryResolveFatJar(project: Project): File { + return try { + val dependency = project.buildscript.dependencies.create(dokkaFatJar) + val configuration = project.buildscript.configurations.detachedConfiguration(dependency) + configuration.description = "Dokka main jar" + configuration.resolve().first() + } catch (e: Exception) { + project.parent?.let { tryResolveFatJar(it) } ?: throw e + } + } + + fun loadFatJar() { + if (fatJarClassLoader == null) { + val fatjar = if (dokkaFatJar is File) + dokkaFatJar as File + else + tryResolveFatJar(project) + fatJarClassLoader = URLClassLoader(arrayOf(fatjar.toURI().toURL()), ClassLoader.getSystemClassLoader().parent) + } + } + + internal data class ClasspathAndSourceRoots(val classpathFileCollection: FileCollection, val sourceRoots: List<File>) : Serializable + + private fun extractKotlinCompileTasks(): List<Task> { + val inputList = (kotlinTasksConfigurator.invoke() ?: emptyList()).filterNotNull() + val (paths, other) = inputList.partition { it is String } + + val taskContainer = project.tasks + + val tasksByPath = paths.map { taskContainer.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") } + + other + .filter { it !is Task || it isNotInstance getAbstractKotlinCompileFor(it) } + .forEach { throw IllegalArgumentException("Illegal entry in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE or String, but was $it") } + + tasksByPath + .filter { it == null || it isNotInstance getAbstractKotlinCompileFor(it) } + .forEach { throw IllegalArgumentException("Illegal task path in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE, but was $it") } + + + return (tasksByPath + other) as List<Task> + } + + private fun extractClasspathAndSourceRootsFromKotlinTasks(): ClasspathAndSourceRoots { + + val allTasks = kotlinTasks + + val allClasspath = mutableSetOf<File>() + var allClasspathFileCollection: FileCollection = project.files() + val allSourceRoots = mutableSetOf<File>() + + allTasks.forEach { + + logger.debug("Dokka found AbstractKotlinCompile task: $it") + with(ReflectDsl) { + val taskSourceRoots: List<File> = it["sourceRootsContainer"]["sourceRoots"].v() + + val abstractKotlinCompileClz = getAbstractKotlinCompileFor(it)!! + + val taskClasspath: Iterable<File> = + (it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke() + ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v() + ?: it["getClasspath", abstractKotlinCompileClz]()) + + if (taskClasspath is FileCollection) { + allClasspathFileCollection += taskClasspath + } else { + allClasspath += taskClasspath + } + allSourceRoots += taskSourceRoots.filter { it.exists() } + } + } + + return ClasspathAndSourceRoots(allClasspathFileCollection + project.files(allClasspath), allSourceRoots.toList()) + } + + private fun Iterable<File>.toSourceRoots(): List<SourceRoot> = this.filter { it.exists() }.map { SourceRoot().apply { path = it.path } } + + protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>): List<String> = emptyList() + + @TaskAction + fun generate() { + val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false" + System.setProperty(COLORS_ENABLED_PROPERTY, "false") + try { + loadFatJar() + + val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots + + val project = project + val sourceRoots = collectSourceRoots() + tasksSourceRoots.toSourceRoots() + + if (sourceRoots.isEmpty()) { + logger.warn("No source directories found: skipping dokka generation") + return + } + + val fullClasspath = collectClasspathFromOldSources() + tasksClasspath + classpath + + val bootstrapClass = fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl") + + val bootstrapInstance = bootstrapClass.constructors.first().newInstance() + + val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance) + + val configuration = SerializeOnlyDokkaConfiguration( + moduleName, + fullClasspath.map { it.absolutePath }, + sourceRoots, + samples.filterNotNull().map { project.file(it).absolutePath }, + includes.filterNotNull().map { project.file(it).absolutePath }, + outputDirectory, + outputFormat, + includeNonPublic, + false, + reportUndocumented, + skipEmptyPackages, + skipDeprecated, + jdkVersion, + generateClassIndexPage, + generatePackageIndexPage, + linkMappings, + impliedPlatforms, + perPackageOptions, + externalDocumentationLinks, + noStdlibLink, + noJdkLink, + cacheRoot, + collectSuppressedFiles(sourceRoots), + languageVersion, + apiVersion, + collectInheritedExtensionsFromLibraries, + outlineRoot, + dacRoot) + + + bootstrapProxy.configure( + BiConsumer { level, message -> + when (level) { + "info" -> logger.info(message) + "warn" -> logger.warn(message) + "error" -> logger.error(message) + } + }, + serialize(configuration) + ) + + bootstrapProxy.generate() + + } finally { + System.setProperty(COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore) + } + } + + private fun collectClasspathFromOldSources(): List<File> { + + val allConfigurations = project.configurations + + val fromConfigurations = + processConfigurations.flatMap { allConfigurations.getByName(it.toString()) } + + return fromConfigurations + } + + private fun collectSourceRoots(): List<SourceRoot> { + val sourceDirs = if (sourceDirs.any()) { + logger.info("Dokka: Taking source directories provided by the user") + sourceDirs.toSet() + } else if (kotlinTasks.isEmpty()) { + project.convention.findPlugin(JavaPluginConvention::class.java)?.let { javaPluginConvention -> + logger.info("Dokka: Taking source directories from default java plugin") + val sourceSets = javaPluginConvention.sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME) + sourceSets?.allSource?.srcDirs + } + } else { + emptySet() + } + + return sourceRoots + (sourceDirs?.toSourceRoots() ?: emptyList()) + } + + + @Classpath + fun getInputClasspath(): FileCollection { + val (classpathFileCollection) = extractClasspathAndSourceRootsFromKotlinTasks() + return project.files(collectClasspathFromOldSources() + classpath) + classpathFileCollection + } + + @InputFiles + fun getInputFiles(): FileCollection { + val (_, tasksSourceRoots) = extractClasspathAndSourceRootsFromKotlinTasks() + return project.files(tasksSourceRoots.map { project.fileTree(it) }) + + project.files(collectSourceRoots().map { project.fileTree(File(it.path)) }) + + project.files(includes) + + project.files(samples.filterNotNull().map { project.fileTree(it) }) + } + + @OutputDirectory + fun getOutputDirectoryAsFile(): File = project.file(outputDirectory) + + companion object { + const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled" + const val ABSTRACT_KOTLIN_COMPILE = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile" + + private fun getAbstractKotlinCompileFor(task: Task) = try { + task.project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE) + } catch (e: ClassNotFoundException) { + null + } + } +} + +class SourceRoot : DokkaConfiguration.SourceRoot, Serializable { + override var path: String = "" + set(value) { + field = File(value).absolutePath + } + + override var platforms: List<String> = arrayListOf() + + override fun toString(): String { + return "${platforms.joinToString()}::$path" + } +} + +open class LinkMapping : Serializable, DokkaConfiguration.SourceLinkDefinition { + @JsonExclude + var dir: String + get() = path + set(value) { + path = value + } + + override var path: String = "" + override var url: String = "" + + @JsonExclude + var suffix: String? + get() = lineSuffix + set(value) { + lineSuffix = value + } + + override var lineSuffix: String? = null + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as LinkMapping + + if (path != other.path) return false + if (url != other.url) return false + if (lineSuffix != other.lineSuffix) return false + + return true + } + + override fun hashCode(): Int { + var result = path.hashCode() + result = 31 * result + url.hashCode() + result = 31 * result + (lineSuffix?.hashCode() ?: 0) + return result + } + + companion object { + const val serialVersionUID: Long = -8133501684312445981L + } +} + +class PackageOptions : Serializable, DokkaConfiguration.PackageOptions { + override var prefix: String = "" + override var includeNonPublic: Boolean = false + override var reportUndocumented: Boolean = true + override var skipDeprecated: Boolean = false + override var suppress: Boolean = false +} diff --git a/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties b/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties new file mode 100644 index 000000000..068bd3522 --- /dev/null +++ b/runners/gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.jetbrains.dokka.properties @@ -0,0 +1,2 @@ +implementation-class=org.jetbrains.dokka.gradle.DokkaPlugin +dokka-version=<version>
\ No newline at end of file diff --git a/runners/maven-plugin/build.gradle b/runners/maven-plugin/build.gradle new file mode 100644 index 000000000..acd3415d7 --- /dev/null +++ b/runners/maven-plugin/build.gradle @@ -0,0 +1,112 @@ +import groovy.io.FileType +import org.jetbrains.CorrectShadowPublishing +import org.jetbrains.CrossPlatformExec + +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +apply plugin: 'kotlin' +apply plugin: 'com.github.johnrengelman.shadow' + +sourceCompatibility = 1.8 + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + languageVersion = "1.2" + apiVersion = languageVersion + jvmTarget = "1.8" + } +} + +dependencies { + shadow project(":runners:fatjar") + shadow "org.apache.maven:maven-core:$maven_version" + shadow "org.apache.maven:maven-model:$maven_version" + shadow "org.apache.maven:maven-plugin-api:$maven_version" + shadow "org.apache.maven:maven-archiver:$maven_archiver_version" + shadow "org.codehaus.plexus:plexus-utils:$plexus_utils_version" + shadow "org.codehaus.plexus:plexus-archiver:$plexus_archiver_version" + shadow "org.apache.maven.plugin-tools:maven-plugin-annotations:$maven_plugin_tools_version" + shadow "com.github.olivergondza:maven-jdk-tools-wrapper:0.1" +} + +task generatePom() { + inputs.file(new File(projectDir, "pom.tpl.xml")) + inputs.property("dokka_version", dokka_version) + inputs.property("maven_version", maven_version) + inputs.property("maven_plugin_tools_version", maven_plugin_tools_version) + outputs.file(new File(buildDir, "pom.xml")) + doLast { + final pomTemplate = new File(projectDir, "pom.tpl.xml") + final pom = new File(buildDir, "pom.xml") + pom.text = pomTemplate.text.replace("<version>dokka_version</version>", "<version>$dokka_version</version>") + .replace("<maven.version></maven.version>", "<maven.version>$maven_version</maven.version>") + .replace("<version>maven-plugin-plugin</version>", "<version>$maven_plugin_tools_version</version>") + } +} + +task mergeClassOutputs doLast { + def sourceDir = new File(buildDir, "classes/kotlin") + def targetDir = new File(buildDir, "classes/java") + + sourceDir.eachFileRecurse FileType.ANY, { + def filePath = it.toPath() + def targetFilePath = targetDir.toPath().resolve(sourceDir.toPath().relativize(filePath)) + if (it.isFile()) { + Files.move(filePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING) + } else if (it.isDirectory()) { + targetFilePath.toFile().mkdirs() + } + } +} + +task pluginDescriptor(type: CrossPlatformExec) { + workingDir buildDir + commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:descriptor' + + dependsOn mergeClassOutputs +} + +task helpMojo(type: CrossPlatformExec) { + workingDir buildDir + commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:helpmojo' + + dependsOn mergeClassOutputs +} + +helpMojo.dependsOn generatePom +sourceSets.main.java.srcDir("$buildDir/generated-sources/plugin") +compileJava.dependsOn helpMojo + +pluginDescriptor.dependsOn generatePom + +shadowJar { + baseName = 'dokka-maven-plugin' + classifier = '' +} + +shadowJar.dependsOn pluginDescriptor + + +task sourceJar(type: Jar) { + from sourceSets.main.allSource +} + +apply plugin: 'maven-publish' + +publishing { + publications { + dokkaMavenPlugin(MavenPublication) { MavenPublication publication -> + artifactId = 'dokka-maven-plugin' + + artifact sourceJar { + classifier "sources" + } + + CorrectShadowPublishing.configure(publication, project) + } + } +} + +bintrayPublication(project, ['dokkaMavenPlugin']) diff --git a/runners/maven-plugin/pom.tpl.xml b/runners/maven-plugin/pom.tpl.xml new file mode 100644 index 000000000..c5883c6a6 --- /dev/null +++ b/runners/maven-plugin/pom.tpl.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>dokka-maven-plugin</artifactId> + <version>dokka_version</version> + <packaging>maven-plugin</packaging> + <properties> + <maven.version></maven.version> + </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-plugin-plugin</artifactId> + <version>maven-plugin-plugin</version> + <configuration> + <helpPackageName>org.jetbrains.dokka.maven</helpPackageName> + </configuration> + </plugin> + </plugins> + <directory>./</directory> + <outputDirectory>./classes/java/main</outputDirectory> + </build> +</project> diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt new file mode 100644 index 000000000..dcb9ac2c2 --- /dev/null +++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt @@ -0,0 +1,252 @@ +package org.jetbrains.dokka.maven + +import org.apache.maven.archiver.MavenArchiveConfiguration +import org.apache.maven.archiver.MavenArchiver +import org.apache.maven.execution.MavenSession +import org.apache.maven.plugin.AbstractMojo +import org.apache.maven.plugins.annotations.* +import org.apache.maven.project.MavenProject +import org.apache.maven.project.MavenProjectHelper +import org.codehaus.plexus.archiver.Archiver +import org.codehaus.plexus.archiver.jar.JarArchiver +import org.jetbrains.dokka.* +import java.io.File +import java.net.URL + +class SourceLinkMapItem { + @Parameter(name = "dir", required = true) + var dir: String = "" + + @Parameter(name = "url", required = true) + var url: String = "" + + @Parameter(name = "urlSuffix") + var urlSuffix: String? = null +} + +class ExternalDocumentationLinkBuilder : DokkaConfiguration.ExternalDocumentationLink.Builder() { + + @Parameter(name = "url", required = true) + override var url: URL? = null + @Parameter(name = "packageListUrl", required = true) + override var packageListUrl: URL? = null +} + +abstract class AbstractDokkaMojo : AbstractMojo() { + class SourceRoot : DokkaConfiguration.SourceRoot { + @Parameter(required = true) + override var path: String = "" + + @Parameter + override var platforms: List<String> = emptyList() + } + + class PackageOptions : DokkaConfiguration.PackageOptions { + @Parameter + override var prefix: String = "" + @Parameter + override var includeNonPublic: Boolean = false + @Parameter + override var reportUndocumented: Boolean = true + @Parameter + override var skipDeprecated: Boolean = false + @Parameter + override var suppress: Boolean = false + } + + @Parameter(required = true, defaultValue = "\${project.compileSourceRoots}") + var sourceDirectories: List<String> = emptyList() + + @Parameter + var sourceRoots: List<SourceRoot> = emptyList() + + @Parameter + var samplesDirs: List<String> = emptyList() + + @Parameter + @Deprecated("Use <includes> instead") + var includeDirs: List<String> = emptyList() + + @Parameter + var includes: List<String> = emptyList() + + @Parameter(required = true, defaultValue = "\${project.compileClasspathElements}") + var classpath: List<String> = emptyList() + + @Parameter + var sourceLinks: Array<SourceLinkMapItem> = emptyArray() + + @Parameter(required = true, defaultValue = "\${project.artifactId}") + var moduleName: String = "" + + @Parameter(required = false, defaultValue = "false") + var skip: Boolean = false + + @Parameter(required = false, defaultValue = "6") + var jdkVersion: Int = 6 + + @Parameter + var skipDeprecated = false + @Parameter + var skipEmptyPackages = true + @Parameter + var reportNotDocumented = true + + @Parameter + var impliedPlatforms: List<String> = emptyList() + + @Parameter + var perPackageOptions: List<PackageOptions> = emptyList() + + @Parameter + var externalDocumentationLinks: List<ExternalDocumentationLinkBuilder> = emptyList() + + @Parameter(defaultValue = "false") + var noStdlibLink: Boolean = false + + @Parameter(defaultValue = "false") + var noJdkLink: Boolean = false + + @Parameter + var cacheRoot: String? = null + + @Parameter + var languageVersion: String? = null + + @Parameter + var apiVersion: String? = null + + protected abstract fun getOutDir(): String + protected abstract fun getOutFormat(): String + + override fun execute() { + if (skip) { + log.info("Dokka skip parameter is true so no dokka output will be produced") + return + } + + val gen = DokkaGenerator( + MavenDokkaLogger(log), + classpath, + sourceDirectories.map { SourceRootImpl(it) } + sourceRoots, + samplesDirs, + includeDirs + includes, + moduleName, + DocumentationOptions(getOutDir(), getOutFormat(), + sourceLinks = sourceLinks.map { SourceLinkDefinitionImpl(it.dir, it.url, it.urlSuffix) }, + jdkVersion = jdkVersion, + skipDeprecated = skipDeprecated, + skipEmptyPackages = skipEmptyPackages, + reportUndocumented = reportNotDocumented, + impliedPlatforms = impliedPlatforms, + perPackageOptions = perPackageOptions, + externalDocumentationLinks = externalDocumentationLinks.map { it.build() }, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + cacheRoot = cacheRoot, + languageVersion = languageVersion, + apiVersion = apiVersion + ) + ) + + gen.generate() + } +} + +@Mojo(name = "dokka", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) +class DokkaMojo : AbstractDokkaMojo() { + @Parameter(required = true, defaultValue = "html") + var outputFormat: String = "html" + + @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokka") + var outputDir: String = "" + + override fun getOutFormat() = outputFormat + override fun getOutDir() = outputDir +} + +@Mojo(name = "javadoc", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) +class DokkaJavadocMojo : AbstractDokkaMojo() { + @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadoc") + var outputDir: String = "" + + override fun getOutFormat() = "javadoc" + override fun getOutDir() = outputDir +} + +@Mojo(name = "javadocJar", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) +class DokkaJavadocJarMojo : AbstractDokkaMojo() { + @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadocJar") + var outputDir: String = "" + + /** + * Specifies the directory where the generated jar file will be put. + */ + @Parameter(property = "project.build.directory") + private var jarOutputDirectory: String? = null + + /** + * Specifies the filename that will be used for the generated jar file. Please note that `-javadoc` + * or `-test-javadoc` will be appended to the file name. + */ + @Parameter(property = "project.build.finalName") + private var finalName: String? = null + + /** + * Specifies whether to attach the generated artifact to the project helper. + */ + @Parameter(property = "attach", defaultValue = "true") + private val attach: Boolean = false + + /** + * The archive configuration to use. + * See [Maven Archiver Reference](http://maven.apache.org/shared/maven-archiver/index.html) + */ + @Parameter + private val archive = MavenArchiveConfiguration() + + @Parameter(property = "maven.javadoc.classifier", defaultValue = "javadoc", required = true) + private var classifier: String? = null + + @Parameter(defaultValue = "\${session}", readonly = true, required = true) + protected var session: MavenSession? = null + + @Parameter(defaultValue = "\${project}", readonly = true, required = true) + protected var project: MavenProject? = null + + @Component + private var projectHelper: MavenProjectHelper? = null + + @Component(role = Archiver::class, hint = "jar") + private var jarArchiver: JarArchiver? = null + + override fun getOutFormat() = "javadoc" + override fun getOutDir() = outputDir + + override fun execute() { + super.execute() + if(!File(outputDir).exists()) { + log.warn("No javadoc generated so no javadoc jar will be generated") + return + } + val outputFile = generateArchive("$finalName-$classifier.jar") + if (attach) { + projectHelper?.attachArtifact(project, "javadoc", classifier, outputFile) + } + } + + private fun generateArchive(jarFileName: String): File { + val javadocJar = File(jarOutputDirectory, jarFileName) + + val archiver = MavenArchiver() + archiver.setArchiver(jarArchiver) + archiver.setOutputFile(javadocJar) + archiver.archiver.addDirectory(File(outputDir), arrayOf("**/**"), arrayOf()) + + archive.setAddMavenDescriptor(false) + archiver.createArchive(session, project, archive) + + return javadocJar + } +} + diff --git a/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt new file mode 100644 index 000000000..a535c8076 --- /dev/null +++ b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt @@ -0,0 +1,18 @@ +package org.jetbrains.dokka.maven + +import org.apache.maven.plugin.logging.Log +import org.jetbrains.dokka.DokkaLogger + +class MavenDokkaLogger(val log: Log) : DokkaLogger { + override fun error(message: String) { + log.error(message) + } + + override fun info(message: String) { + log.info(message) + } + + override fun warn(message: String) { + log.warn(message) + } +}
\ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..712f03744 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,12 @@ +rootProject.name = "dokka" + +include 'core', + 'integration', + 'runners:fatjar', + 'runners:ant', + 'runners:cli', + // Google's fork does not use maven-plugin. Removing it from build for convenience. + // 'runners:maven-plugin', + 'runners:gradle-plugin', + 'runners:android-gradle-plugin', + 'runners:gradle-integration-tests' diff --git a/test/playground.kt b/test/playground.kt new file mode 100644 index 000000000..5206e10e3 --- /dev/null +++ b/test/playground.kt @@ -0,0 +1,99 @@ +// this file is not included in sources or tests, you can play with it for debug purposes +// Console run configuration will analyse it and provide lots of debug output +package dokka.playground + +fun topLevelFunction() { +} + +val topLevelConstantValue = "Hello" + +val topLevelValue: String + get() = "Bye bye" + +var topLevelVariable: String + get() = "Modify me!" + set(value) { + } + +/** + * This is a class + */ +class Class { + fun memberFunction() { + } + + val memberValue = "Member" +} + +/** + * This is a class with constructor and space after doc + */ + +class ClassWithConstructor( + /** Doc at parameter */ val name: Class) + +/** + * This is data class with constructor and two properties + * Also look at [Employee] + * + * $name Person's name + * $age Person's age + * + */ +data class Person(val name: ClassWithConstructor, val age: Int) {} + +data class Employee(val name: ClassWithConstructor, val age: Int) {} + +object Object { + throws(javaClass<IllegalArgumentException>()) + fun objectFunction() { + } + + val objectValue: String + /** one line getter doc */ + get() = "Member" + + public val String.valueWithReceiver: Int + get() = 1 + +} + +enum class Color(r: Int, g: Int, b: Int) { + Red : Color(100,0,0) + Green : Color(0,100,0) + Blue : Color(0,0,100) +} + +class OuterClass { + + /** + * $T type of the item + */ + class NestedClass<T> { + fun nestedClassFunction(item: T) { + } + + fun String.functionWithReceiver(): Int = 1 + + } + + inner class InnerClass { + open fun innerClassFunction< + /** doc for R1 type param */ + R1, + /** doc for R2 type param */ + R2 + >() { + } + } + + object NestedObject { + protected open fun nestedObjectFunction() { + } + } +} + +trait Interface { + fun worker() + val extra: String +}
\ No newline at end of file |