Snap for 5735642 from 786b902592f53ce6fd98f260c60938d988e1a8ef to sdk-release

Change-Id: I4fbb38967cc001c412f15bc234b35764d93e5ab1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7aae4ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,102 @@
+
+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 0000000..c8bb43f
--- /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 0000000..5998df7
--- /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 0000000..b636e09
--- /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 0000000..0f0eee5
--- /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 0000000..ba49a93
--- /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 0000000..79ee123
--- /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 0000000..9700d3c
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,45 @@
+<?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>
+    <annotationProcessing>
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="dokka-maven-plugin" />
+      </profile>
+    </annotationProcessing>
+    <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="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 0000000..f4aa08e
--- /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 0000000..f758959
--- /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 0000000..06bda8c
--- /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/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..b072dc0
--- /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.1" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..537aa8e
--- /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 0000000..7e6aaef
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,39 @@
+<?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/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/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 0000000..922003b
--- /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/README.android b/README.android
new file mode 100644
index 0000000..199cbcf
--- /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 0000000..b6e40cf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,487 @@
+dokka [![TeamCity (build status)](https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:KotlinTools_Dokka_DokkaGradleDacDist)/statusIcon)](http://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_Dokka_DokkaGradleDacDist&branch_KotlinTools_Dokka=%3Cdefault%3E&guest=1)
+=====
+
+**_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.**
+The main working branch in this fork is
+[`devsite-with-java-layout-html`](https://github.com/google/dokka/tree/devsite-with-java-layout-html).
+See the [Repository Notes](https://github.com/google/dokka/wiki/Repository-notes)
+Wiki page for additional project details.
+
+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 0000000..b46353b
--- /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 0000000..d282aeb
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,128 @@
+import org.jetbrains.DependenciesVersionGetter
+
+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://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"
+}
+
+def versions = DependenciesVersionGetter.getVersions(project, bundled_kotlin_compiler_version)
+
+ext.ideaVersion = versions["idea.build.id"]
+ext.markdownVersion = versions["markdown.build.id"].replace("%20", " ")
+
+configurations {
+    ideaIC
+    intellijCore
+}
+
+repositories {
+    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:$ideaVersion"
+    ideaIC "com.jetbrains.intellij.idea:ideaIC:$ideaVersion"
+}
+
+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 0000000..874dabc
--- /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 0000000..58cfdcf
--- /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.publish.internal.ProjectDependencyPublicationResolver
+import org.gradle.api.publish.maven.MavenPom
+import org.gradle.api.publish.maven.MavenPublication
+
+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 SelfResolvingDependency)) {
+                if (it instanceof ProjectDependency) {
+                    def projectDependencyResolver = project.gradle.services.get(ProjectDependencyPublicationResolver)
+                    ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(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 0000000..d3973a8
--- /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 0000000..194f11a
--- /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 0000000..e711388
--- /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/core/build.gradle b/core/build.gradle
new file mode 100644
index 0000000..5c80fc8
--- /dev/null
+++ b/core/build.gradle
@@ -0,0 +1,55 @@
+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 "teamcity:kotlin-ide-common:$bundled_kotlin_compiler_version"
+    compile "org.jetbrains:markdown:$markdownVersion"
+
+    implementation "com.squareup.okhttp3:okhttp:4.0.0-RC1"
+
+    compile intellijCoreAnalysis()
+
+//    Google version of the library in the libs folder. Fixing 129528660
+//    compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8'
+
+    //tools.jar
+    def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") })
+    compileOnly toolsJar
+    testCompile toolsJar
+
+    compile project(":integration")
+
+    testCompile group: 'junit', name: 'junit', version: '4.12'
+    testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_version
+    testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
+
+    testCompile ideaRT()
+}
\ No newline at end of file
diff --git a/core/libs/kotlinx-html-jvm-0.6.8-google.jar b/core/libs/kotlinx-html-jvm-0.6.8-google.jar
new file mode 100644
index 0000000..08d3c2e
--- /dev/null
+++ b/core/libs/kotlinx-html-jvm-0.6.8-google.jar
Binary files differ
diff --git a/core/samples/memberWithModifiers.java b/core/samples/memberWithModifiers.java
new file mode 100644
index 0000000..ea645c2
--- /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 0000000..b47f8bd
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt
@@ -0,0 +1,328 @@
+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.search.GlobalSearchScope
+import com.intellij.util.io.URLUtil
+import org.jetbrains.kotlin.analyzer.*
+import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
+import org.jetbrains.kotlin.cli.common.config.ContentRoot
+import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
+import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
+import org.jetbrains.kotlin.cli.common.messages.MessageCollector
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
+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.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.BindingTrace
+import org.jetbrains.kotlin.resolve.CompilerEnvironment
+import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
+import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade
+import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
+import org.jetbrains.kotlin.util.slicedMap.WritableSlice
+import java.io.File
+
+/**
+ * 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)
+
+        projectComponentManager.registerService(ProjectFileIndex::class.java,
+                projectFileIndex)
+        projectComponentManager.registerService(ProjectRootManager::class.java,
+                CoreProjectRootManager(projectFileIndex))
+        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)
+        val sourceFiles = environment.getSourceFiles()
+
+
+        val library = object : ModuleInfo {
+            override val name: Name = Name.special("<library>")
+            override fun dependencies(): List<ModuleInfo> = listOf(this)
+        }
+        val module = object : ModuleInfo {
+            override val name: Name = Name.special("<module>")
+            override fun dependencies(): List<ModuleInfo> = listOf(this, library)
+        }
+
+        val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles)
+
+        val builtIns = JvmBuiltIns(projectContext.storageManager)
+
+
+        val javaRoots = classpath
+                .mapNotNull {
+                    val rootFile = when {
+                        it.extension == "jar" ->
+                            StandardFileSystems.jar().findFileByPath("${it.absolutePath}${URLUtil.JAR_SEPARATOR}")
+                        else ->
+                            StandardFileSystems.local().findFileByPath(it.absolutePath)
+                    }
+
+                    rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) }
+                }
+
+        val resolverForProject = ResolverForProjectImpl(
+            "Dokka",
+            projectContext,
+            listOf(library, module),
+            {
+                when (it) {
+                    library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope))
+                    module -> ModuleContent(it, emptyList(), sourcesScope)
+                    else -> throw IllegalArgumentException("Unexpected module info")
+                }
+            },
+            {
+                JvmPlatform.multiTargetPlatform
+            },
+            LanguageSettingsProvider.Default /* TODO: Fix this */,
+            { JvmAnalyzerFacade },
+            {
+                JvmPlatformParameters({ content ->
+                    JvmPackagePartProvider(configuration.languageVersionSettings, content.moduleContentScope).apply {
+                        addRoots(javaRoots, messageCollector)
+                    }
+                }, {
+                    val file = (it as JavaClassImpl).psi.containingFile.virtualFile
+                    if (file in sourcesScope)
+                        module
+                    else
+                        library
+                })
+            },
+            CompilerEnvironment,
+            builtIns = builtIns
+        )
+
+        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 0000000..31b8ffc
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt
@@ -0,0 +1,30 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiFile
+import org.jetbrains.kotlin.analyzer.ModuleInfo
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.resolve.TargetPlatform
+import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache
+
+
+class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService {
+    override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
+        return resolutionFacade
+    }
+
+    override fun getResolutionFacadeByFile(file: PsiFile, platform: TargetPlatform): ResolutionFacade {
+        return resolutionFacade
+    }
+
+    override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? {
+        return resolutionFacade
+    }
+
+    override fun getSuppressionCache(): KotlinSuppressCache {
+        throw UnsupportedOperationException()
+    }
+
+}
+
diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt
new file mode 100644
index 0000000..319d85b
--- /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.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
+                .map { StandardFileSystems.local().findFileByPath(it.file.path) }
+                .filterNotNull()
+                .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)
+            StandardFileSystems.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
+}
diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
new file mode 100644
index 0000000..4dc6b36
--- /dev/null
+++ b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("JavaResolutionUtils")
+
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import org.jetbrains.kotlin.asJava.classes.KtLightClass
+import org.jetbrains.kotlin.asJava.unwrapped
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
+import org.jetbrains.kotlin.load.java.structure.*
+import org.jetbrains.kotlin.load.java.structure.impl.*
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
+import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+
+// TODO: Remove that file
+
+@JvmOverloads
+fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+    val method = originalElement as? PsiMethod ?: return null
+    if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null
+    val resolver = method.getJavaDescriptorResolver(resolutionFacade)
+    return when {
+        method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method))
+        else -> resolver?.resolveMethod(JavaMethodImpl(method))
+    }
+}
+
+@JvmOverloads
+fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? {
+    val psiClass = originalElement as? PsiClass ?: return null
+    return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass))
+}
+
+@JvmOverloads
+fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? {
+    val field = originalElement as? PsiField ?: return null
+    return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field))
+}
+
+@JvmOverloads
+fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+    return when (this) {
+        is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade)
+        is PsiClass -> getJavaClassDescriptor(resolutionFacade)
+        is PsiMethod -> getJavaMethodDescriptor(resolutionFacade)
+        is PsiField -> getJavaFieldDescriptor(resolutionFacade)
+        else -> null
+    }
+}
+
+@JvmOverloads
+fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? {
+    val callable = unwrapped
+    return when (callable) {
+        is PsiMember -> getJavaMemberDescriptor(resolutionFacade)
+        is KtDeclaration -> {
+            val descriptor = resolutionFacade.resolveToDescriptor(callable)
+            if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor
+        }
+        else -> null
+    }
+}
+
+private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? {
+    return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java)
+}
+
+private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? {
+    return getContainingScope(method)
+            ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES)
+            ?.filterIsInstance<DeclarationDescriptorWithSource>()
+            ?.findByJavaElement(method)
+}
+
+private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? {
+    return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor)
+}
+
+private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? {
+    return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field)
+}
+
+private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? {
+    val containingClass = resolveClass(member.containingClass)
+    return if (member.isStatic)
+        containingClass?.staticScope
+    else
+        containingClass?.defaultType?.memberScope
+}
+
+private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElement(javaElement: JavaElement): T? {
+    return firstOrNull { member ->
+        val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement
+        when {
+            memberJavaElement == javaElement ->
+                true
+            memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> ->
+                memberJavaElement.psi.isEquivalentTo(javaElement.psi)
+            else ->
+                false
+        }
+    }
+}
+
+fun PsiElement.javaResolutionFacade() =
+    KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform)!!
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
new file mode 100644
index 0000000..4020181
--- /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 0000000..d78d4a0
--- /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 0000000..ca25f20
--- /dev/null
+++ b/core/src/main/kotlin/Formats/DacHtmlFormat.kt
@@ -0,0 +1,902 @@
+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.Utilities.firstSentence
+import org.w3c.dom.html.HTMLElement
+import java.lang.Math.max
+import java.net.URI
+import java.util.Collections.emptyMap
+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.name
+            }
+            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 {
+                                        td {
+                                            colSpan = "2"
+                                            metaMarkup(it.children)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    ContentTags.Parameters -> {
+                        table(classes = "responsive") {
+                            tbody {
+                                tr {
+                                    th {
+                                        colSpan = "2"
+                                        +name
+                                    }
+                                }
+                                sections.forEach {
+                                    tr {
+                                        td {
+                                            code {
+                                                it.subjectName?.let { +it }
+                                            }
+                                        }
+                                        td {
+                                            metaMarkup(it.children)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    ContentTags.SeeAlso -> {
+                        div {
+                            p {
+                                b {
+                                    +name
+                                }
+                            }
+                            ul(classes = "nolist") {
+                                sections.forEach {
+                                    li {
+                                        code {
+                                            metaMarkup(it.children)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    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) {
+        summaryNodeGroup(
+                nestedClasses,
+                header = "Nested classes",
+                summaryId = "nestedclasses",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) {
+            nestedClassSummaryRow(it)
+        }
+
+        summaryNodeGroup(
+            attributes,
+            header="XML attributes",
+            summaryId="lattrs",
+            tableClass = "responsive",
+            headerAsRow = true
+        ) {
+            xmlAttributeRow(it)
+        }
+
+        expandableSummaryNodeGroupForInheritedMembers(
+                superClasses = inheritedAttributes.entries,
+                header="Inherited XML attributes",
+                tableId="inhattrs",
+                tableClass = "responsive",
+                row = { inheritedXmlAttributeRow(it)}
+        )
+
+        summaryNodeGroup(
+                constants,
+                header = "Constants",
+                summaryId = "constants",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) { propertyLikeSummaryRow(it) }
+
+        expandableSummaryNodeGroupForInheritedMembers(
+                superClasses = inheritedConstants.entries,
+                header = "Inherited constants",
+                tableId = "inhconstants",
+                tableClass = "responsive constants inhtable",
+                row = { inheritedMemberRow(it) }
+        )
+
+        constructors.forEach { (visibility, group) ->
+            summaryNodeGroup(
+                    group,
+                    header = "${visibility.capitalize()} constructors",
+                    summaryId = "${visibility.take(3)}ctors",
+                    tableClass = "responsive",
+                    headerAsRow = true
+            ) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+        summaryNodeGroup(
+            enumValues,
+            header = "Enum values",
+            summaryId = "enumvalues",
+            tableClass = "responsive",
+            headerAsRow = true
+        ) {
+            propertyLikeSummaryRow(it, showSignature = false)
+        }
+
+        functions.forEach { (visibility, group) ->
+            summaryNodeGroup(
+                    group,
+                    header = "${visibility.capitalize()} methods",
+                    summaryId = "${visibility.take(3)}methods",
+                    tableClass = "responsive",
+                    headerAsRow = true
+            ) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+        summaryNodeGroup(
+                companionFunctions,
+                header = "Companion functions",
+                summaryId = "compmethods",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) {
+            functionLikeSummaryRow(it)
+        }
+
+        expandableSummaryNodeGroupForInheritedMembers(
+                superClasses = inheritedFunctionsByReceiver.entries,
+                header = "Inherited functions",
+                tableId = "inhmethods",
+                tableClass = "responsive",
+                row = { inheritedMemberRow(it) }
+        )
+
+        summaryNodeGroup(
+                extensionFunctions.entries,
+                header = "Extension functions",
+                summaryId = "extmethods",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+                inheritedExtensionFunctions.entries,
+                header = "Inherited extension functions",
+                summaryId = "inhextmethods",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+        fields.forEach { (visibility, group) ->
+            summaryNodeGroup(
+                group,
+                header = "${visibility.capitalize()} fields",
+                summaryId = "${visibility.take(3)}fields",
+                tableClass = "responsive",
+                headerAsRow = true
+            ) { propertyLikeSummaryRow(it) }
+        }
+
+        expandableSummaryNodeGroupForInheritedMembers(
+            superClasses = inheritedFieldsByReceiver.entries,
+            header = "Inherited fields",
+            tableId = "inhfields",
+            tableClass = "responsive properties inhtable",
+            row = { inheritedMemberRow(it) }
+        )
+
+        summaryNodeGroup(
+                properties,
+                header = "Properties",
+                summaryId = "properties",
+                tableClass = "responsive",
+                headerAsRow = true
+        ) { propertyLikeSummaryRow(it) }
+
+
+        summaryNodeGroup(
+                companionProperties,
+                "Companion properties",
+                headerAsRow = true
+        ) {
+            propertyLikeSummaryRow(it)
+        }
+
+        expandableSummaryNodeGroupForInheritedMembers(
+                superClasses = inheritedPropertiesByReceiver.entries,
+                header = "Inherited properties",
+                tableId = "inhfields",
+                tableClass = "responsive properties inhtable",
+                row = { inheritedMemberRow(it) }
+        )
+
+        summaryNodeGroup(
+                extensionProperties.entries,
+                "Extension properties",
+                headerAsRow = true
+        ) {
+            extensionRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+
+        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, summaryId)
+                }
+                nodes.forEach { node ->
+                    row(node)
+                }
+            }
+        }
+    }
+
+    override fun generatePackage(page: Page.PackagePage) = templateService.composePage(
+            page,
+            htmlConsumer,
+            headContent = {
+
+            },
+            bodyContent = {
+                h1 { +page.node.name }
+                nodeContent(page.node)
+                summaryNodeGroup(page.classes.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Classes", headerAsRow = false) { classLikeRow(it) }
+                summaryNodeGroup(page.exceptions.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+                summaryNodeGroup(page.typeAliases.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+                summaryNodeGroup(page.annotations.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Annotations", headerAsRow = false) { classLikeRow(it) }
+                summaryNodeGroup(page.enums.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+                summaryNodeGroup(
+                        page.constants.sortedBy { it.name },
+                        "Top-level constants summary",
+                        headerAsRow = false
+                ) {
+                    propertyLikeSummaryRow(it)
+                }
+
+                summaryNodeGroup(
+                        page.functions.sortedBy { it.name },
+                        "Top-level functions summary",
+                        headerAsRow = false
+                ) {
+                    functionLikeSummaryRow(it)
+                }
+
+                summaryNodeGroup(
+                        page.properties.sortedBy { it.name },
+                        "Top-level properties summary",
+                        headerAsRow = false
+                ) {
+                    propertyLikeSummaryRow(it)
+                }
+
+                summaryNodeGroupForExtensions("Extension functions summary", page.extensionFunctions.entries)
+                summaryNodeGroupForExtensions("Extension properties summary", page.extensionProperties.entries)
+
+                fullMemberDocs(page.constants.sortedBy { it.name }, "Top-level constants")
+                fullMemberDocs(page.functions.sortedBy { it.name }, "Top-level functions")
+                fullMemberDocs(page.properties.sortedBy { it.name }, "Top-level properties")
+                fullMemberDocs(page.extensionFunctions.values.flatten().sortedBy { it.name }, "Extension functions")
+                fullMemberDocs(page.extensionProperties.values.flatten().sortedBy { it.name }, "Extension properties")
+            }
+    )
+
+    private fun TBODY.inheritedXmlAttributeRow(inheritedMember: DocumentationNode) {
+        tr(classes = "api apilevel-${inheritedMember.attributeRef!!.apiLevel.name}") {
+            attributes["data-version-added"] = "${inheritedMember.apiLevel}"
+            td {
+                code {
+                    a(href = inheritedMember) { +inheritedMember.attributeRef!!.name }
+                }
+            }
+            td {
+                attributes["width"] = "100%"
+                p {
+                    nodeContent(inheritedMember.attributeRef!!, inheritedMember)
+                }
+            }
+        }
+    }
+
+    private fun TBODY.inheritedMemberRow(inheritedMember: DocumentationNode) {
+        tr(classes = "api apilevel-${inheritedMember.apiLevel.name}") {
+            attributes["data-version-added"] = "${inheritedMember.apiLevel}"
+            val type = inheritedMember.detailOrNull(NodeKind.Type)
+            td {
+                code {
+                    type?.let {
+                        renderedSignature(it, LanguageService.RenderMode.SUMMARY)
+                    }
+                }
+            }
+            td {
+                attributes["width"] = "100%"
+                code {
+                    a(href = inheritedMember) { +inheritedMember.name }
+                    if (inheritedMember.kind == NodeKind.Function) {
+                        shortFunctionParametersList(inheritedMember)
+                    }
+                }
+                p {
+                    nodeContent(inheritedMember)
+                }
+            }
+        }
+    }
+
+    private fun FlowContent.expandableSummaryNodeGroupForInheritedMembers(
+            tableId: String,
+            header: String,
+            tableClass: String,
+            superClasses: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>,
+            row: TBODY.(inheritedMember: DocumentationNode) -> Unit
+    ) {
+        if (superClasses.none()) return
+        table(classes = tableClass) {
+            attributes["id"] = tableId
+            tbody {
+                developerHeading(header)
+                superClasses.forEach { (superClass, members) ->
+                    tr(classes = "api apilevel-${superClass.apiLevel.name}") {
+                        td {
+                            attributes["colSpan"] = "2"
+                            div(classes = "expandable jd-inherited-apis") {
+                                span(classes = "expand-control exw-expanded") {
+                                    +"From class "
+                                    code {
+                                        a(href = superClass) { +superClass.name }
+                                    }
+                                }
+                                table(classes = "responsive exw-expanded-content") {
+                                    tbody {
+                                        members.forEach { inheritedMember ->
+                                            row(inheritedMember)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private fun FlowContent.summaryNodeGroupForExtensions(
+            header: String,
+            receivers: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>
+    ) {
+        if (receivers.none()) return
+        h2 { +header }
+        div {
+            receivers.forEach {
+                table {
+                    tr {
+                        td {
+                            attributes["colSpan"] = "2"
+                            +"For "
+                            a(href = it.key) { +it.key.name }
+                        }
+                    }
+                    it.value.forEach { node ->
+                        tr {
+                            if (node.kind != NodeKind.Constructor) {
+                                td {
+                                    modifiers(node)
+                                    renderedSignature(node.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
+                                }
+                            }
+                            td {
+                                div {
+                                    code {
+                                        val receiver = node.detailOrNull(NodeKind.Receiver)
+                                        if (receiver != null) {
+                                            renderedSignature(receiver.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
+                                            +"."
+                                        }
+                                        a(href = node) { +node.name }
+                                        shortFunctionParametersList(node)
+                                    }
+                                }
+
+                                nodeSummary(node)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    override fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
+            page,
+            htmlConsumer,
+            headContent = {
+
+            },
+            bodyContent = {
+                h1 { +"Package Index" }
+                table {
+                    tbody {
+                        for (node in page.packages) {
+                            tr {
+                                td {
+                                    a(href = uriProvider.linkTo(node, uri)) { +node.name }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+    )
+
+    override fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
+            page,
+            htmlConsumer,
+            headContent = {
+
+            },
+            bodyContent = {
+                h1 { +"Class Index" }
+
+                p {
+                    +"These are all the API classes. See all "
+                    a(href="packages.html") {
+                        +"API packages."
+                    }
+                }
+
+                div(classes = "jd-letterlist") {
+                    page.classesByFirstLetter.forEach { (letter) ->
+                        +"\n        "
+                        a(href = "#letter_$letter") { +letter }
+                        unsafe {
+                            raw("&nbsp;&nbsp;")
+                        }
+                    }
+                    +"\n    "
+                }
+
+                page.classesByFirstLetter.forEach { (letter, classes) ->
+                    h2 {
+                        id = "letter_$letter"
+                        +letter
+                    }
+                    table {
+                        tbody {
+                            for (node in classes) {
+                                tr {
+                                    td {
+                                        a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
+                                    }
+                                    td {
+                                        if (!deprecatedIndexSummary(node)) {
+                                            nodeSummary(node)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+    )
+
+    override fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
+        table(classes = "jd-inheritance-table") {
+            var level = superclasses.size
+            superclasses.forEach {
+                tr {
+                    var spaceColumns = max(superclasses.size - 1 - level, 0)
+                    while (spaceColumns > 0) {
+                        td(classes = "jd-inheritance-space") {
+                            +" "
+                        }
+                        spaceColumns--
+                    }
+                    if (it != superclasses.first()) {
+                        td(classes = "jd-inheritance-space") {
+                            +"   ↳"
+                        }
+                    }
+                    td(classes = "jd-inheritance-class-cell") {
+                        attributes["colSpan"] = "$level"
+                        qualifiedTypeReference(it)
+                    }
+                }
+                level--
+            }
+        }
+    }
+
+    override fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
+        if (inheritors.isEmpty()) return
+
+        // The number of subclasses in collapsed view before truncating and adding a "and xx others".
+        // See https://developer.android.com/reference/android/view/View for an example.
+        val numToShow = 12
+
+        table(classes = "jd-sumtable jd-sumtable-subclasses") {
+            tbody {
+                tr {
+                    td {
+                        div(classes = "expandable") {
+                            span(classes = "expand-control") {
+                                if (direct)
+                                    +"Known Direct Subclasses"
+                                else
+                                    +"Known Indirect Subclasses"
+                            }
+                            div(classes = "showalways") {
+                                attributes["id"] = if (direct) "subclasses-direct" else "subclasses-indirect"
+
+                                inheritors.take(numToShow).forEach { inheritor ->
+                                    a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+                                    if (inheritor != inheritors.last()) +", "
+                                }
+
+                                if (inheritors.size > numToShow) {
+                                    +"and ${inheritors.size - numToShow} others."
+                                }
+                            }
+                            div(classes = "exw-expanded-content") {
+                                attributes["id"] = if (direct) "subclasses-direct-summary" else "subclasses-indirect-summary"
+                                table(classes = "jd-sumtable-expando") {
+                                    inheritors.forEach { inheritor ->
+                                        tr(classes = "api api-level-${inheritor.apiLevel.name}") {
+                                            attributes["data-version-added"] = inheritor.apiLevel.name
+                                            td(classes = "jd-linkcol") {
+                                                a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+                                            }
+                                            td(classes = "jd-descrcol") {
+                                                attributes["width"] = "100%"
+                                                nodeSummary(inheritor)
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    fun DocumentationNode.firstSentenceOfSummary(): ContentNode {
+
+        fun Sequence<ContentNode>.flatten(): Sequence<ContentNode> {
+            return flatMap {
+                when (it) {
+                    is ContentParagraph -> it.children.asSequence().flatten()
+                    else -> sequenceOf(it)
+                }
+            }
+        }
+
+        fun ContentNode.firstSentence(): ContentText? = when(this) {
+            is ContentText -> ContentText(text.firstSentence())
+            else -> null
+        }
+
+        val elements = sequenceOf(summary).flatten()
+        fun containsDot(it: ContentNode) = (it as? ContentText)?.text?.contains(".") == true
+
+        val paragraph = ContentParagraph()
+        (elements.takeWhile { !containsDot(it) } + elements.firstOrNull { containsDot(it) }?.firstSentence()).forEach {
+            if (it != null) {
+                paragraph.append(it)
+            }
+        }
+        if (paragraph.isEmpty()) {
+            return ContentEmpty
+        }
+
+        return paragraph
+    }
+
+    fun DocumentationNode.firstSentence(): String {
+        val sb = StringBuilder()
+        addContentNodeToStringBuilder(content, sb)
+        return sb.toString().firstSentence()
+    }
+
+    private fun addContentNodesToStringBuilder(content: List<ContentNode>, sb: StringBuilder): Unit =
+        content.forEach { addContentNodeToStringBuilder(it, sb) }
+
+    private fun addContentNodeToStringBuilder(content: ContentNode, sb: StringBuilder) {
+        when (content) {
+            is ContentText -> sb.appendWith(content.text)
+            is ContentSymbol -> sb.appendWith(content.text)
+            is ContentKeyword -> sb.appendWith(content.text)
+            is ContentIdentifier -> sb.appendWith(content.text)
+            is ContentEntity -> sb.appendWith(content.text)
+
+            is ContentHeading -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentStrong -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentStrikethrough -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentEmphasis -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentOrderedList -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentUnorderedList -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentListItem -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentCode -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentBlockSampleCode -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentBlockCode -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentParagraph -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentNodeLink -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentBookmark -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentExternalLink -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentLocalLink -> addContentNodesToStringBuilder(content.children, sb)
+            is ContentSection -> { }
+            is ContentBlock -> addContentNodesToStringBuilder(content.children, sb)
+        }
+    }
+
+    private fun StringBuilder.appendWith(text: String, delimiter: String = " ") {
+        if (this.length == 0) {
+            append(text)
+        } else {
+            append(delimiter)
+            append(text)
+        }
+    }
+}
+
+fun TBODY.developerHeading(
+    header: String,
+    summaryId: String? = null
+) {
+    tr {
+        th {
+            attributes["colSpan"] = "2"
+            dheading {
+                attributes["ds-is"] = "heading"
+                attributes["text"] = header
+                attributes["id"] = summaryId ?: header.replace("\\s".toRegex(), "-").toLowerCase()
+                attributes["level"] = "h3"
+                attributes["toc"] = ""
+                attributes["class"] = ""
+                h3 {
+                    attributes["is-upgraded"] = ""
+                    +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
+}
+
+
+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
+}
+
+fun FlowOrPhrasingContent.dheading(block : DHEADING.() -> Unit = {}) : Unit = DHEADING(consumer).visit(block)
+
+class DHEADING(consumer: TagConsumer<*>) :
+    HTMLTag("devsite-heading", consumer, emptyMap(), inlineTag = false, emptyTag = false), HtmlBlockTag
diff --git a/core/src/main/kotlin/Formats/DacOutlineService.kt b/core/src/main/kotlin/Formats/DacOutlineService.kt
new file mode 100644
index 0000000..e249c39
--- /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 0000000..e4eeac0
--- /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 0000000..b497fb0
--- /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 0000000..63f2500
--- /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 0000000..036ec85
--- /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 0000000..0ad946b
--- /dev/null
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -0,0 +1,168 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import java.io.File
+
+open class HtmlOutputBuilder(to: StringBuilder,
+                             location: Location,
+                             generator: NodeLocationAwareGenerator,
+                             languageService: LanguageService,
+                             extension: String,
+                             impliedPlatforms: List<String>,
+                             val templateService: HtmlTemplateService)
+    : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+{
+    override fun appendText(text: String) {
+        to.append(text.htmlEscape())
+    }
+
+    override fun appendSymbol(text: String) {
+        to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendKeyword(text: String) {
+        to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+        val id = signature?.let { " id=\"$it\"" }.orEmpty()
+        to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>")
+    }
+
+    override fun appendBlockCode(language: String, body: () -> Unit) {
+        val openTags = if (language.isNotBlank())
+            "<pre><code class=\"lang-$language\">"
+        else
+            "<pre><code>"
+        wrap(openTags, "</code></pre>", body)
+    }
+
+    override fun appendHeader(level: Int, body: () -> Unit) =
+        wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true)
+    override fun appendParagraph(body: () -> Unit) =
+        wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true)
+
+    override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body)
+
+    override fun appendLine() {
+        to.appendln("<br/>")
+    }
+
+    override fun appendAnchor(anchor: String) {
+        to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>")
+    }
+
+    override fun appendTable(vararg columns: String, body: () -> Unit) =
+            wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true)
+    override fun appendTableBody(body: () -> Unit) =
+            wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true)
+    override fun appendTableRow(body: () -> Unit) =
+            wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true)
+    override fun appendTableCell(body: () -> Unit) =
+            wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true)
+
+    override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
+
+    override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body)
+    override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body)
+    override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
+    override fun appendCode(body: () -> Unit) = wrapInTag("code", body)
+
+    override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true)
+    override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true)
+    override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true)
+
+    override fun appendBreadcrumbSeparator() {
+        to.append("&nbsp;/&nbsp;")
+    }
+
+    override fun appendNodes(nodes: Iterable<DocumentationNode>) {
+        templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location))
+        super.appendNodes(nodes)
+        templateService.appendFooter(to)
+    }
+
+    override fun appendNonBreakingSpace() {
+        to.append("&nbsp;")
+    }
+
+    override fun ensureParagraph() {
+
+    }
+}
+
+open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator,
+                                                 signatureGenerator: LanguageService,
+                                                 val templateService: HtmlTemplateService,
+                                                 @Named(impliedPlatformsName) val impliedPlatforms: List<String>)
+: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService {
+
+    override fun enumerateSupportFiles(callback: (String, String) -> Unit) {
+        callback("/dokka/styles/style.css", "style.css")
+    }
+
+    override fun createOutputBuilder(to: StringBuilder, location: Location) =
+        HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
+
+    override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
+        templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location))
+        super.appendOutline(location, to, nodes)
+        templateService.appendFooter(to)
+    }
+
+    override fun getOutlineFileName(location: Location): File {
+        return File("${location.path}-outline.html")
+    }
+
+    override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
+        val link = ContentNodeDirectLink(node)
+        link.append(languageService.render(node, LanguageService.RenderMode.FULL))
+        val tempBuilder = StringBuilder()
+        createOutputBuilder(tempBuilder, location).appendContent(link)
+        to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>")
+    }
+
+    override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
+        to.appendln("<ul>")
+        body()
+        to.appendln("</ul>")
+    }
+}
+
+fun getPageTitle(nodes: Iterable<DocumentationNode>): String? {
+    val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) }
+    return breakdownByLocation.keys.singleOrNull()
+}
+
+fun formatPageTitle(node: DocumentationNode): String {
+    val path = node.path
+    val moduleName = path.first().name
+    if (path.size == 1) {
+        return moduleName
+    }
+
+    val qName = qualifiedNameForPageTitle(node)
+    return qName + " - " + moduleName
+}
+
+private fun qualifiedNameForPageTitle(node: DocumentationNode): String {
+    if (node.kind == NodeKind.Package) {
+        var packageName = node.qualifiedName()
+        if (packageName.isEmpty()) {
+            packageName = "root package"
+        }
+        return packageName
+    }
+
+    val path = node.path
+    var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike }
+    if (pathFromToplevelMember.isEmpty()) {
+        pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function }
+    }
+    if (pathFromToplevelMember.isNotEmpty()) {
+        return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".")
+    }
+    return node.qualifiedName()
+}
diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
new file mode 100644
index 0000000..a65a7b1
--- /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 0000000..66ded62
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
@@ -0,0 +1,145 @@
+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}").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(name)
+            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) }
+    }
+}
+
+fun DocumentationNode.classNodeNameWithOuterClass(): String {
+    assert(kind in NodeKind.classLike)
+    return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name }
+}
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 0000000..b62e51a
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
@@ -0,0 +1,1156 @@
+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) }
+
+    private fun FlowContent.contentNodeToMarkup(content: ContentNode, contextUri: 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 { 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.name }
+                    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)
+            summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+            summaryNodeGroup(
+                page.constants,
+                "Top-level constants summary",
+                headerAsRow = false
+            ) {
+                propertyLikeSummaryRow(it)
+            }
+
+            summaryNodeGroup(
+                page.functions,
+                "Top-level functions summary",
+                headerAsRow = false
+            ) {
+                functionLikeSummaryRow(it)
+            }
+
+            summaryNodeGroup(
+                page.properties,
+                "Top-level properties summary",
+                headerAsRow = false
+            ) {
+                propertyLikeSummaryRow(it)
+            }
+
+            summaryNodeGroup(
+                page.extensionFunctions.entries,
+                "Extension functions summary",
+                headerAsRow = false
+            ) {
+                extensionByReceiverRow(it) {
+                    functionLikeSummaryRow(it)
+                }
+            }
+
+            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.singleOrNull()
+
+        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) {
+        summaryNodeGroup(
+            nestedClasses,
+            "Nested classes",
+            headerAsRow = true
+        ) {
+            nestedClassSummaryRow(it)
+        }
+
+        summaryNodeGroup(enumValues, "Enum values") {
+            propertyLikeSummaryRow(it)
+        }
+
+        summaryNodeGroup(constants, "Constants") { propertyLikeSummaryRow(it) }
+
+        constructors.forEach { (visibility, group) ->
+            summaryNodeGroup(
+                    group,
+                    "${visibility.capitalize()} constructors",
+                    headerAsRow = true
+            ) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+        functions.forEach { (visibility, group) ->
+            summaryNodeGroup(
+                    group,
+                    "${visibility.capitalize()} functions",
+                    headerAsRow = true
+            ) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+        summaryNodeGroup(
+            companionFunctions,
+            "Companion functions",
+            headerAsRow = true
+        ) {
+            functionLikeSummaryRow(it)
+        }
+        summaryNodeGroup(
+            inheritedFunctionsByReceiver.entries,
+            "Inherited functions",
+            headerAsRow = true
+        ) {
+            inheritRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            extensionFunctions.entries,
+            "Extension functions",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            inheritedExtensionFunctions.entries,
+            "Inherited extension functions",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+
+        summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
+        summaryNodeGroup(
+            companionProperties,
+            "Companion properties",
+            headerAsRow = true
+        ) {
+            propertyLikeSummaryRow(it)
+        }
+
+        summaryNodeGroup(
+            inheritedPropertiesByReceiver.entries,
+            "Inherited properties",
+            headerAsRow = true
+        ) {
+            inheritRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            extensionProperties.entries,
+            "Extension properties",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+        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 deprecatedLevelExists = node.deprecatedLevel.name.isNotEmpty()
+        if (apiLevelExists || 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 (deprecatedLevelExists) {
+                        br
+                    }
+                }
+                if (deprecatedLevelExists) {
+                    +"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().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 == 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 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(NodeKind.ExternalLink).firstOrNull()
+                                ?: receiverType.links.first { it.kind in NodeKind.classLike}
+                    }
+
+            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 ?: ""
\ No newline at end of file
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 0000000..115e3f7
--- /dev/null
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
@@ -0,0 +1,160 @@
+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('.', '/') + '/')
+            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)
+        val 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 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("/"))
+            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 0000000..ce05fe8
--- /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 0000000..a948dfa
--- /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 0000000..a98002d
--- /dev/null
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -0,0 +1,224 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
+
+
+open class KotlinWebsiteOutputBuilder(
+        to: StringBuilder,
+        location: Location,
+        generator: NodeLocationAwareGenerator,
+        languageService: LanguageService,
+        extension: String,
+        impliedPlatforms: List<String>
+) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
+    private var needHardLineBreaks = false
+    private var insideDiv = 0
+
+    override fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) {
+        super.appendFrontMatter(nodes, to)
+        to.appendln("layout: api")
+    }
+
+    override fun appendBreadcrumbs(path: Iterable<FormatLink>) {
+        if (path.count() > 1) {
+            to.append("<div class='api-docs-breadcrumbs'>")
+            super.appendBreadcrumbs(path)
+            to.append("</div>")
+        }
+    }
+
+    override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body)
+
+    override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
+
+    protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) {
+        to.append("<div class=\"$cssClass\"$otherAttributes")
+        if (markdown) to.append(" markdown=\"1\"")
+        to.append(">")
+        if (!markdown) insideDiv++
+        block()
+        if (!markdown) insideDiv--
+        to.append("</div>\n")
+    }
+
+    override fun appendAsSignature(node: ContentNode, block: () -> Unit) {
+        val contentLength = node.textLength
+        if (contentLength == 0) return
+        div(to, "signature") {
+            needHardLineBreaks = contentLength >= 62
+            try {
+                block()
+            } finally {
+                needHardLineBreaks = false
+            }
+        }
+    }
+
+    override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+        div(to, "overload-group", calculateDataAttributes(platforms), true) {
+            ensureParagraph()
+            block()
+            ensureParagraph()
+        }
+    }
+
+    override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
+
+    override fun appendHeader(level: Int, body: () -> Unit) {
+        if (insideDiv > 0) {
+            wrapInTag("p", body, newlineAfterClose = true)
+        } else {
+            super.appendHeader(level, body)
+        }
+    }
+
+    override fun appendLine() {
+        if (insideDiv > 0) {
+            to.appendln("<br/>")
+        } else {
+            super.appendLine()
+        }
+    }
+
+    override fun appendTable(vararg columns: String, body: () -> Unit) {
+        to.appendln("<table class=\"api-docs-table\">")
+        body()
+        to.appendln("</table>")
+    }
+
+    override fun appendTableBody(body: () -> Unit) {
+        to.appendln("<tbody>")
+        body()
+        to.appendln("</tbody>")
+    }
+
+    override fun appendTableRow(body: () -> Unit) {
+        to.appendln("<tr>")
+        body()
+        to.appendln("</tr>")
+    }
+
+    override fun appendTableCell(body: () -> Unit) {
+        to.appendln("<td markdown=\"1\">")
+        body()
+        to.appendln("\n</td>")
+    }
+
+    override fun appendBlockCode(language: String, body: () -> Unit) {
+        if (language.isNotEmpty()) {
+            super.appendBlockCode(language, body)
+        } else {
+            wrap("<pre markdown=\"1\">", "</pre>", body)
+        }
+    }
+
+    override fun appendSymbol(text: String) {
+        to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendKeyword(text: String) {
+        to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+        val id = signature?.let { " id=\"$it\"" }.orEmpty()
+        to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>")
+    }
+
+    override fun appendSoftLineBreak() {
+        if (needHardLineBreaks)
+            to.append("<br/>")
+
+    }
+
+    override fun appendIndentedSoftLineBreak() {
+        if (needHardLineBreaks) {
+            to.append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;")
+        }
+    }
+
+    private fun identifierClassName(kind: IdentifierKind) = when (kind) {
+        IdentifierKind.ParameterName -> "parameterName"
+        IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
+        else -> "identifier"
+    }
+
+    fun calculateDataAttributes(platforms: Set<String>): String {
+        fun String.isKotlinVersion() = this.startsWith("Kotlin")
+        fun String.isJREVersion() = this.startsWith("JRE")
+        val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)
+        val jreVersion = platforms.singleOrNull(String::isJREVersion)
+        val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() }
+
+        val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: ""
+        val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: ""
+        val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: ""
+        return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
+    }
+
+    override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+        if (platforms.isNotEmpty())
+            wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+        else
+            appendTableRow(block)
+    }
+
+    override fun appendPlatforms(platforms: Set<String>) {
+
+    }
+}
+
+class KotlinWebsiteFormatService @Inject constructor(
+        generator: NodeLocationAwareGenerator,
+        signatureGenerator: LanguageService,
+        @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+        logger: DokkaLogger
+) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) {
+    init {
+        logger.warn("Format kotlin-website deprecated and will be removed in next release")
+    }
+
+    override fun createOutputBuilder(to: StringBuilder, location: Location) =
+            KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+}
+
+
+class KotlinWebsiteRunnableSamplesOutputBuilder(
+        to: StringBuilder,
+        location: Location,
+        generator: NodeLocationAwareGenerator,
+        languageService: LanguageService,
+        extension: String,
+        impliedPlatforms: List<String>
+) : KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) {
+
+    override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
+        div(to, "sample", markdown = true) {
+            appendBlockCode(language) {
+                imports()
+                wrap("\n\nfun main(args: Array<String>) {", "}") {
+                    wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
+                }
+            }
+        }
+    }
+}
+
+class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(
+        generator: NodeLocationAwareGenerator,
+        signatureGenerator: LanguageService,
+        @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+        logger: DokkaLogger
+) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) {
+
+    init {
+        logger.warn("Format kotlin-website-samples deprecated and will be removed in next release")
+    }
+
+    override fun createOutputBuilder(to: StringBuilder, location: Location) =
+            KotlinWebsiteRunnableSamplesOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+}
+
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
new file mode 100644
index 0000000..6ced75b
--- /dev/null
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -0,0 +1,186 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
+import java.io.File
+
+
+object EmptyHtmlTemplateService : HtmlTemplateService {
+    override fun appendFooter(to: StringBuilder) {}
+
+    override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {}
+}
+
+
+open class KotlinWebsiteHtmlOutputBuilder(
+        to: StringBuilder,
+        location: Location,
+        generator: NodeLocationAwareGenerator,
+        languageService: LanguageService,
+        extension: String,
+        impliedPlatforms: List<String>,
+        templateService: HtmlTemplateService
+) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) {
+    private var needHardLineBreaks = false
+    private var insideDiv = 0
+
+    override fun appendLine() {}
+
+    override fun appendBreadcrumbs(path: Iterable<FormatLink>) {
+        if (path.count() > 1) {
+            to.append("<div class='api-docs-breadcrumbs'>")
+            super.appendBreadcrumbs(path)
+            to.append("</div>")
+        }
+    }
+
+    override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body)
+
+    protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) {
+        to.append("<div class=\"$cssClass\"$otherAttributes")
+        to.append(">")
+        insideDiv++
+        block()
+        insideDiv--
+        to.append("</div>\n")
+    }
+
+    override fun appendAsSignature(node: ContentNode, block: () -> Unit) {
+        val contentLength = node.textLength
+        if (contentLength == 0) return
+        div(to, "signature") {
+            needHardLineBreaks = contentLength >= 62
+            try {
+                block()
+            } finally {
+                needHardLineBreaks = false
+            }
+        }
+    }
+
+    override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+        div(to, "overload-group", calculateDataAttributes(platforms)) {
+            block()
+        }
+    }
+
+    override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
+
+    override fun appendTable(vararg columns: String, body: () -> Unit) {
+        to.appendln("<table class=\"api-docs-table\">")
+        body()
+        to.appendln("</table>")
+    }
+
+    override fun appendTableBody(body: () -> Unit) {
+        to.appendln("<tbody>")
+        body()
+        to.appendln("</tbody>")
+    }
+
+    override fun appendTableRow(body: () -> Unit) {
+        to.appendln("<tr>")
+        body()
+        to.appendln("</tr>")
+    }
+
+    override fun appendTableCell(body: () -> Unit) {
+        to.appendln("<td>")
+        body()
+        to.appendln("\n</td>")
+    }
+
+    override fun appendSymbol(text: String) {
+        to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendKeyword(text: String) {
+        to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
+    }
+
+    override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
+        val id = signature?.let { " id=\"$it\"" }.orEmpty()
+        to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>")
+    }
+
+    override fun appendSoftLineBreak() {
+        if (needHardLineBreaks)
+            to.append("<br/>")
+    }
+
+    override fun appendIndentedSoftLineBreak() {
+        if (needHardLineBreaks) {
+            to.append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;")
+        }
+    }
+
+    private fun identifierClassName(kind: IdentifierKind) = when (kind) {
+        IdentifierKind.ParameterName -> "parameterName"
+        IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
+        else -> "identifier"
+    }
+
+    fun calculateDataAttributes(platforms: Set<String>): String {
+        fun String.isKotlinVersion() = this.startsWith("Kotlin")
+        fun String.isJREVersion() = this.startsWith("JRE")
+        val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)
+        val jreVersion = platforms.singleOrNull(String::isJREVersion)
+        val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() }
+
+        val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: ""
+        val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: ""
+        val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: ""
+        return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
+    }
+
+    override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+        if (platforms.isNotEmpty())
+            wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+        else
+            appendTableRow(block)
+    }
+
+    override fun appendPlatforms(platforms: Set<String>) {}
+
+    override fun appendBreadcrumbSeparator() {
+        to.append(" / ")
+    }
+
+    override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
+        div(to, "sample") {
+            appendBlockCode(language) {
+                imports()
+                wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") {
+                    wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
+                }
+            }
+        }
+    }
+
+    override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body)
+
+
+    override fun appendSectionWithTag(section: ContentSection) {
+        appendParagraph {
+            appendStrong { appendText(section.tag) }
+            appendText(" ")
+            appendContent(section)
+        }
+    }
+}
+
+class KotlinWebsiteHtmlFormatService @Inject constructor(
+        generator: NodeLocationAwareGenerator,
+        signatureGenerator: LanguageService,
+        @Named(impliedPlatformsName) impliedPlatforms: List<String>,
+        templateService: HtmlTemplateService
+) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) {
+
+    override fun enumerateSupportFiles(callback: (String, String) -> Unit) {}
+
+    override fun createOutputBuilder(to: StringBuilder, location: Location) =
+            KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
+}
+
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
new file mode 100644
index 0000000..4265394
--- /dev/null
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -0,0 +1,239 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.name.Named
+import org.jetbrains.dokka.Utilities.impliedPlatformsName
+import java.util.*
+
+enum class ListKind {
+    Ordered,
+    Unordered
+}
+
+private class ListState(val kind: ListKind, var size: Int = 1) {
+    fun getTagAndIncrement() = when (kind) {
+        ListKind.Ordered -> "${size++}. "
+        else -> "* "
+    }
+}
+
+private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator()
+
+open class MarkdownOutputBuilder(to: StringBuilder,
+                                 location: Location,
+                                 generator: NodeLocationAwareGenerator,
+                                 languageService: LanguageService,
+                                 extension: String,
+                                 impliedPlatforms: List<String>)
+    : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+{
+    private val listStack = ArrayDeque<ListState>()
+    protected var inTableCell = false
+    protected var inCodeBlock = false
+    private var lastTableCellStart = -1
+    private var maxBackticksInCodeBlock = 0
+
+    private fun appendNewline() {
+        while (to.endsWith(' ')) {
+            to.setLength(to.length - 1)
+        }
+        to.appendln()
+    }
+
+    private fun ensureNewline() {
+        if (inTableCell && listStack.isEmpty()) {
+            if (to.length != lastTableCellStart && !to.endsWith("<br>")) {
+                to.append("<br>")
+            }
+        }
+        else {
+            if (!endsWithNewline()) {
+                appendNewline()
+            }
+        }
+    }
+
+    private fun endsWithNewline(): Boolean {
+        var index = to.length - 1
+        while (index > 0) {
+            val c = to[index]
+            if (c != ' ') {
+                return c == '\n'
+            }
+            index--
+        }
+        return false
+    }
+
+    override fun ensureParagraph() {
+        if (!to.endsWith(TWO_LINE_BREAKS)) {
+            if (!to.endsWith('\n')) {
+                appendNewline()
+            }
+            appendNewline()
+        }
+    }
+    override fun appendBreadcrumbSeparator() {
+        to.append(" / ")
+    }
+
+    private val backTickFindingRegex = """(`+)""".toRegex()
+
+    override fun appendText(text: String) {
+        if (inCodeBlock) {
+            to.append(text)
+            val backTicks = backTickFindingRegex.findAll(text)
+            val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0
+            maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun)
+        }
+        else {
+            if (text == "\n" && inTableCell) {
+                to.append(" ")
+            } else {
+                to.append(text.htmlEscape())
+            }
+        }
+    }
+
+    override fun appendCode(body: () -> Unit) {
+        inCodeBlock = true
+        val codeBlockStart = to.length
+        maxBackticksInCodeBlock = 0
+
+        wrapIfNotEmpty("`", "`", body, checkEndsWith = true)
+
+        if (maxBackticksInCodeBlock > 0) {
+            val extraBackticks = "`".repeat(maxBackticksInCodeBlock)
+            to.insert(codeBlockStart, extraBackticks)
+            to.append(extraBackticks)
+        }
+
+        inCodeBlock = false
+    }
+
+    override fun appendUnorderedList(body: () -> Unit) {
+        listStack.push(ListState(ListKind.Unordered))
+        body()
+        listStack.pop()
+        ensureNewline()
+    }
+
+    override fun appendOrderedList(body: () -> Unit) {
+        listStack.push(ListState(ListKind.Ordered))
+        body()
+        listStack.pop()
+        ensureNewline()
+    }
+
+    override fun appendListItem(body: () -> Unit) {
+        ensureNewline()
+        to.append(listStack.peek()?.getTagAndIncrement())
+        body()
+        ensureNewline()
+    }
+
+    override fun appendStrong(body: () -> Unit) = wrap("**", "**", body)
+    override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body)
+    override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body)
+
+    override fun appendLink(href: String, body: () -> Unit) {
+        if (inCodeBlock) {
+            wrap("`[`", "`]($href)`", body)
+        }
+        else {
+            wrap("[", "]($href)", body)
+        }
+    }
+
+    override fun appendLine() {
+        if (inTableCell) {
+            to.append("<br>")
+        }
+        else {
+            appendNewline()
+        }
+    }
+
+    override fun appendAnchor(anchor: String) {
+        // no anchors in Markdown
+    }
+
+    override fun appendParagraph(body: () -> Unit) {
+        if (inTableCell) {
+            ensureNewline()
+            body()
+        } else if (listStack.isNotEmpty()) {
+            body()
+            ensureNewline()
+        } else {
+            ensureParagraph()
+            body()
+            ensureParagraph()
+        }
+    }
+
+    override fun appendHeader(level: Int, body: () -> Unit) {
+        ensureParagraph()
+        to.append("${"#".repeat(level)} ")
+        body()
+        ensureParagraph()
+    }
+
+    override fun appendBlockCode(language: String, body: () -> Unit) {
+        inCodeBlock = true
+        ensureParagraph()
+        to.appendln(if (language.isEmpty()) "```" else "``` $language")
+        body()
+        ensureNewline()
+        to.appendln("```")
+        appendLine()
+        inCodeBlock = false
+    }
+
+    override fun appendTable(vararg columns: String, body: () -> Unit) {
+        ensureParagraph()
+        body()
+        ensureParagraph()
+    }
+
+    override fun appendTableBody(body: () -> Unit) {
+        body()
+    }
+
+    override fun appendTableRow(body: () -> Unit) {
+        to.append("|")
+        body()
+        appendNewline()
+    }
+
+    override fun appendTableCell(body: () -> Unit) {
+        to.append(" ")
+        inTableCell = true
+        lastTableCellStart = to.length
+        body()
+        inTableCell = false
+        to.append(" |")
+    }
+
+    override fun appendNonBreakingSpace() {
+        if (inCodeBlock) {
+            to.append(" ")
+        }
+        else {
+            to.append("&nbsp;")
+        }
+    }
+}
+
+open class MarkdownFormatService(generator: NodeLocationAwareGenerator,
+                                 signatureGenerator: LanguageService,
+                                 linkExtension: String,
+                                 val impliedPlatforms: List<String>)
+: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) {
+    @Inject constructor(generator: NodeLocationAwareGenerator,
+                        signatureGenerator: LanguageService,
+                        @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms)
+
+    override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder =
+        MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms)
+}
diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt
new file mode 100644
index 0000000..958e93a
--- /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 0000000..7b68098
--- /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 0000000..dd67ac9
--- /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 0000000..a2c9078
--- /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 0000000..c36f98e
--- /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 0000000..6f063b5
--- /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 0000000..2d202db
--- /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 0000000..23286e2
--- /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 0000000..c8f93c5
--- /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 0000000..6865400
--- /dev/null
+++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt
@@ -0,0 +1,354 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.*
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator
+import com.intellij.psi.util.InheritanceUtil
+import com.intellij.psi.util.PsiTreeUtil
+import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
+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()
+    else -> mapTypeName(this)
+}
+
+private fun mapTypeName(psiType: PsiType): String = when (psiType) {
+    is PsiPrimitiveType -> psiType.canonicalText
+    is PsiClassType -> psiType.resolve()?.qualifiedName ?: 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
+
+    @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)
+    }
+
+    constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) {
+        this.options = options
+        this.refGraph = refGraph
+        this.docParser = docParser
+    }
+
+    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, 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()
+                    node.append(annotation,
+                            if (it.qualifiedName == "java.lang.Deprecated") RefKind.Deprecation else 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)
+        }
+        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 && 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 {
+            node.appendType(it, NodeKind.Supertype)
+            val superClass = it.resolve()
+            if (superClass != null) {
+                link(superClass, node, RefKind.Inheritor)
+            }
+        }
+        node.appendDetails(typeParameters) { build() }
+        node.appendMembers(methods) { 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(),
+                if (isConstructor) "<init>" else 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
+        }
+        append(psiType.build(kind), RefKind.Detail)
+    }
+
+    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 0000000..6f00597
--- /dev/null
+++ b/core/src/main/kotlin/Java/JavadocParser.kt
@@ -0,0 +1,675 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl
+import com.intellij.psi.impl.source.tree.JavaDocElementType
+import com.intellij.psi.javadoc.*
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.IncorrectOperationException
+import com.intellij.util.containers.isNullOrEmpty
+import org.jetbrains.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 deprecatedLevel: DocumentationNode? = null,
+    val artifactId: DocumentationNode? = null,
+    val attribute: DocumentationNode? = null
+) {
+    companion object {
+        val Empty = JavadocParseResult(Content.Empty,
+            null,
+            emptyList(),
+            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 { it.kind == NodeKind.Parameter }?.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 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)
+                }
+                "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, 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 CorePsiDocTagValueImpl) {
+                val docTagValue = data[0]
+                if (docTagValue.firstChild != null) {
+                    val apiLevel = docTagValue.firstChild
+                    return apiLevel.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" -> ScriptBlock(element.attr("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 -> {
+                val linkNode =
+                    ContentNodeLazyLink(
+                        (tag.valueElement ?: linkElement).text,
+                        { -> refGraph.lookupOrWarn(linkSignature, logger) }
+                    )
+                linkNode.append(text)
+                linkNode
+            }
+            else -> text
+        }
+        seeSection.append(node)
+    }
+
+    private fun convertInlineDocTag(tag: PsiInlineDocTag, 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) {
+                val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text
+                val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\""
+                val link = "<a $linkTarget>${labelText.htmlEscape()}</a>"
+                if (tag.name == "link") "<code>$link</code>" else link
+            } else if (valueElement != null) {
+                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 script from CDN, ScriptBlock objects constructs HTML object
+        "usesMathJax" -> {
+            "<script type=\"text/javascript\" async src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/" +
+                    "latest.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.isNullOrEmpty()) {
+                return method
+            }
+        }
+        for (method in superMethods) {
+            val result = findFirstSuperMethodWithDocumentation(method)
+            if (result != null) {
+                return result
+            }
+        }
+
+        return null
+    }
+
+    private fun findFirstSuperMethodWithDocumentationforTag(
+        elementToExpand: PsiDocTag,
+        current: PsiMethod
+    ): Pair<PsiMethod, PsiDocTag>? {
+        val superMethods = current.findSuperMethods()
+        val mappedFilteredTags = superMethods.map {
+            it to it.docComment?.tags?.filter { it.name == elementToExpand.name }
+        }
+
+        for ((method, tags) in mappedFilteredTags) {
+            tags ?: continue
+            for (tag in tags) {
+                val (tagSubject, elementSubject) = when (tag.name) {
+                    "throws" -> {
+                        // match class names only for throws, ignore possibly fully qualified path
+                        // TODO: Always match exactly here
+                        tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last()
+                    }
+                    else -> {
+                        tag.getSubjectName() to elementToExpand.getSubjectName()
+                    }
+                }
+
+                if (tagSubject == elementSubject) {
+                    return method to tag
+                }
+            }
+        }
+
+        for (method in superMethods) {
+            val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method)
+            if (result != null) {
+                return result
+            }
+        }
+        return null
+    }
+
+    /**
+     * 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")
+            }
+        }
+
+    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 0000000..c60625a
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
@@ -0,0 +1,188 @@
+package org.jetbrains.dokka
+
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.html.entities.EntityConverter
+import org.intellij.markdown.parser.LinkMap
+import java.util.*
+
+class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) {
+    fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel)
+    fun resolve(href: String): ContentBlock = contentFactory(href)
+}
+
+fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent {
+    val result = MutableContent()
+    if (inline) {
+        buildInlineContentTo(tree, result, linkResolver)
+    } else {
+        buildContentTo(tree, result, linkResolver)
+    }
+    return result
+}
+
+fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+//    println(tree.toTestString())
+    val nodeStack = ArrayDeque<ContentBlock>()
+    nodeStack.push(target)
+
+    tree.visit { node, processChildren ->
+        val parent = nodeStack.peek()
+
+        fun appendNodeWithChildren(content: ContentBlock) {
+            nodeStack.push(content)
+            processChildren()
+            parent.append(nodeStack.pop())
+        }
+
+        when (node.type) {
+            MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1))
+            MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2))
+            MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3))
+            MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4))
+            MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5))
+            MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6))
+            MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList())
+            MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList())
+            MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem())
+            MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis())
+            MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong())
+            MarkdownElementTypes.CODE_SPAN -> {
+                val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text
+                if (startDelimiter != null) {
+                    val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter)
+                    val codeSpan = ContentCode().apply { append(ContentText(text)) }
+                    parent.append(codeSpan)
+                }
+            }
+            MarkdownElementTypes.CODE_BLOCK,
+            MarkdownElementTypes.CODE_FENCE -> {
+                val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: ""
+                appendNodeWithChildren(ContentBlockCode(language))
+            }
+            MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph())
+
+            MarkdownElementTypes.INLINE_LINK -> {
+                val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT)
+                val destination = node.child(MarkdownElementTypes.LINK_DESTINATION)
+                if (linkTextNode != null) {
+                    if (destination != null) {
+                        val link = ContentExternalLink(destination.text)
+                        renderLinkTextTo(linkTextNode, link, linkResolver)
+                        parent.append(link)
+                    } else {
+                        val link = ContentExternalLink(linkTextNode.getLabelText())
+                        renderLinkTextTo(linkTextNode, link, linkResolver)
+                        parent.append(link)
+                    }
+                }
+            }
+            MarkdownElementTypes.SHORT_REFERENCE_LINK,
+            MarkdownElementTypes.FULL_REFERENCE_LINK -> {
+                val labelElement = node.child(MarkdownElementTypes.LINK_LABEL)
+                if (labelElement != null) {
+                    val linkInfo = linkResolver.getLinkInfo(labelElement.text)
+                    val labelText = labelElement.getLabelText()
+                    val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText)
+                    val linkText = node.child(MarkdownElementTypes.LINK_TEXT)
+                    if (linkText != null) {
+                        renderLinkTextTo(linkText, link, linkResolver)
+                    } else {
+                        link.append(ContentText(labelText))
+                    }
+                    parent.append(link)
+                }
+            }
+            MarkdownTokenTypes.WHITE_SPACE -> {
+                // Don't append first space if start of header (it is added during formatting later)
+                //                   v
+                //               #### Some Heading
+                if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) {
+                    parent.append(ContentText(node.text))
+                }
+            }
+            MarkdownTokenTypes.EOL -> {
+                if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) ||
+                        // Keep extra blank lines when processing lists (affects Markdown formatting)
+                        (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) {
+                    parent.append(ContentText(node.text))
+                }
+            }
+
+            MarkdownTokenTypes.CODE_LINE -> {
+                val content = ContentText(node.text)
+                if (parent is ContentBlockCode) {
+                    parent.append(content)
+                } else {
+                    parent.append(ContentBlockCode().apply { append(content) })
+                }
+            }
+
+            MarkdownTokenTypes.TEXT -> {
+                fun createEntityOrText(text: String): ContentNode {
+                    if (text == "&amp;" || text == "&quot;" || text == "&lt;" || text == "&gt;") {
+                        return ContentEntity(text)
+                    }
+                    if (text == "&") {
+                        return ContentEntity("&amp;")
+                    }
+                    val decodedText = EntityConverter.replaceEntities(text, true, true)
+                    if (decodedText != text) {
+                        return ContentEntity(text)
+                    }
+                    return ContentText(text)
+                }
+
+                parent.append(createEntityOrText(node.text))
+            }
+
+            MarkdownTokenTypes.EMPH -> {
+                val parentNodeType = node.parent?.type
+                if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) {
+                    parent.append(ContentText(node.text))
+                }
+            }
+
+            MarkdownTokenTypes.COLON,
+            MarkdownTokenTypes.SINGLE_QUOTE,
+            MarkdownTokenTypes.DOUBLE_QUOTE,
+            MarkdownTokenTypes.LT,
+            MarkdownTokenTypes.GT,
+            MarkdownTokenTypes.LPAREN,
+            MarkdownTokenTypes.RPAREN,
+            MarkdownTokenTypes.LBRACKET,
+            MarkdownTokenTypes.RBRACKET,
+            MarkdownTokenTypes.EXCLAMATION_MARK,
+            MarkdownTokenTypes.BACKTICK,
+            MarkdownTokenTypes.CODE_FENCE_CONTENT -> {
+                parent.append(ContentText(node.text))
+            }
+
+            MarkdownElementTypes.LINK_DEFINITION -> {
+            }
+
+            else -> {
+                processChildren()
+            }
+        }
+    }
+}
+
+private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text }
+
+private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode
+private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList
+
+fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+    val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree)
+    inlineContent.forEach {
+        buildContentTo(it, target, linkResolver)
+    }
+}
+
+fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
+    val linkTextNodes = tree.children.drop(1).dropLast(1)
+    linkTextNodes.forEach {
+        buildContentTo(it, target, linkResolver)
+    }
+}
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
new file mode 100644
index 0000000..d73bef4
--- /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 0000000..8762f29
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -0,0 +1,276 @@
+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.KtDeclaration
+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
+
+        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))
+                    KDocKnownTag.SEE ->
+                        content.addTagToSeeAlso(contextDescriptor, it)
+                    else -> {
+                        val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName())
+                        val sectionContent = it.getContent()
+                        val markdownNode = parseMarkdown(sectionContent)
+                        buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(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() == "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.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
+    }
+
+}
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
new file mode 100644
index 0000000..adb8fa4
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -0,0 +1,1172 @@
+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.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)
+                }
+            } else {
+                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)
+                }
+
+            }
+        }
+    }
+
+    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.containsErrorType(this)) {
+            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(): 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 { (name, value) ->
+            val valueNode = value.toDocumentationNode()
+            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(): DocumentationNode? = value?.let { value ->
+        when (value) {
+            is String ->
+                "\"" + 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 0000000..a812979
--- /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 { createDirectories() }
+
+    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)
+
+            if (cacheEntry.exists()) {
+                try {
+                    val connection = packageListUrl.doOpenConnectionToReadContent()
+                    val originModifiedDate = connection.date
+                    val cacheDate = cacheEntry.lastModified().toMillis()
+                    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 JavaClassDescriptor) {
+                return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html"
+            } else if (symbol is JavaCallableMemberDescriptor) {
+                val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
+                val containingClassLink = getPath(containingClass)
+                if (containingClassLink != null) {
+                    if (symbol is JavaMethodDescriptor || 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 0000000..e1cda87
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -0,0 +1,66 @@
+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)
+
+        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 0000000..20ea179
--- /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 0000000..bcac018
--- /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 0000000..6dd43ea
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -0,0 +1,464 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.Formats.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)
+        renderList(parameters) {
+            indentedSoftLineBreak()
+            renderParameter(it, renderMode)
+        }
+        if (needReturnType(node)) {
+            if (parameters.isNotEmpty()) {
+                softLineBreak()
+            }
+            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() ?: 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 0000000..e7c6518
--- /dev/null
+++ b/core/src/main/kotlin/Languages/CommonLanguageService.kt
@@ -0,0 +1,93 @@
+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.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 0000000..4b3979b
--- /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 0000000..e430819
--- /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 0000000..a4b3de5
--- /dev/null
+++ b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt
@@ -0,0 +1,240 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.Formats.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 true
+    }
+
+    override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
+        return content {
+            (when (node.kind) {
+                NodeKind.Package -> renderPackage(node)
+                in NodeKind.classLike -> renderClass(node, renderMode)
+
+                NodeKind.Modifier -> renderModifier(this, node, renderMode)
+                NodeKind.TypeParameter -> renderTypeParameter(node)
+                NodeKind.Type,
+                NodeKind.UpperBound -> renderType(node)
+                NodeKind.Parameter -> renderParameter(node)
+                NodeKind.Constructor,
+                NodeKind.Function -> renderFunction(node)
+                NodeKind.Property -> renderProperty(node)
+                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 -> 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) {
+        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)
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt
new file mode 100644
index 0000000..4cb0ac3
--- /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 0000000..2c8f7a7
--- /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 0000000..17b5062
--- /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 0000000..3732547
--- /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() : 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 0000000..e8a85a2
--- /dev/null
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -0,0 +1,281 @@
+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,
+
+    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 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 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 == NodeKind.Package || it.kind == NodeKind.Module }.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}."
+}
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
new file mode 100644
index 0000000..cc582a0
--- /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 0000000..e8fdde6
--- /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 0000000..5b62891
--- /dev/null
+++ b/core/src/main/kotlin/Model/PackageDocs.kt
@@ -0,0 +1,135 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiFileFactory
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.util.LocalTimeCounter
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.parser.LinkMap
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
+import java.io.File
+
+@Singleton
+class PackageDocs
+        @Inject constructor(val linkResolver: DeclarationLinkResolver?,
+                            val logger: DokkaLogger,
+                            val environment: KotlinCoreEnvironment,
+                            val refGraph: NodeReferenceGraph,
+                            val elementSignatureProvider: ElementSignatureProvider)
+{
+    val moduleContent: MutableContent = MutableContent()
+    private val _packageContent: MutableMap<String, MutableContent> = hashMapOf()
+    val packageContent: Map<String, Content>
+        get() = _packageContent
+
+    fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) {
+        val file = File(fileName)
+        if (file.exists()) {
+            val text = file.readText()
+            val tree = parseMarkdown(text)
+            val linkMap = LinkMap.buildLinkMap(tree.node, text)
+            var targetContent: MutableContent = moduleContent
+            tree.children.forEach {
+                if (it.type == MarkdownElementTypes.ATX_1) {
+                    val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text
+                    if (headingText != null) {
+                        targetContent = findTargetContent(headingText.trimStart())
+                    }
+                } else {
+                    buildContentTo(it, targetContent, LinkResolver(linkMap, { resolveContentLink(fileName, it, linkResolveContext) }))
+                }
+            }
+        } else {
+            logger.warn("Include file $file was not found.")
+        }
+    }
+
+    private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) {
+        val javadocText = text
+                .replace("*/", "*&#47;")
+                .removeSurrounding("<html>", "</html>", true).trim()
+                .removeSurrounding("<body>", "</body>", true)
+                .lineSequence()
+                .map { "* $it" }
+                .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/")
+        parseJavadoc(javadocText, packageName, file)
+    }
+
+    private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence {
+        if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) {
+            return subSequence(prefix.length, length - suffix.length)
+        }
+        return subSequence(0, length)
+    }
+
+
+    private fun parseJavadoc(text: String, packageName: String, file: File) {
+
+        val psiFileFactory = PsiFileFactory.getInstance(environment.project)
+        val psiFile = psiFileFactory.createFileFromText(
+                file.nameWithoutExtension + ".java",
+                JavaFileType.INSTANCE,
+                "package $packageName; $text\npublic class C {}",
+                LocalTimeCounter.currentTime(),
+                false,
+                true
+        )
+
+        val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!!
+        val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!)
+        findOrCreatePackageContent(packageName).apply {
+            val content = parser.parseDocumentation(psiClass).content
+            children.addAll(content.children)
+            content.sections.forEach {
+                addSection(it.tag, it.subjectName).children.addAll(it.children)
+            }
+        }
+    }
+
+
+    fun parseJava(fileName: String, packageName: String) {
+        val file = File(fileName)
+        if (file.exists()) {
+            val text = file.readText()
+
+            val trimmedText = text.trim()
+
+            if (trimmedText.startsWith("/**")) {
+                parseJavadoc(text, packageName, file)
+            } else if (trimmedText.toLowerCase().startsWith("<html>")) {
+                parseHtmlAsJavadoc(trimmedText, packageName, file)
+            }
+        }
+    }
+
+    private fun findTargetContent(heading: String): MutableContent {
+        if (heading.startsWith("Module") || heading.startsWith("module")) {
+            return moduleContent
+        }
+        if (heading.startsWith("Package") || heading.startsWith("package")) {
+            return findOrCreatePackageContent(heading.substring("package".length).trim())
+        }
+        return findOrCreatePackageContent(heading)
+    }
+
+    private fun findOrCreatePackageContent(packageName: String) =
+        _packageContent.getOrPut(packageName) { -> MutableContent() }
+
+    private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock {
+        if (linkResolver != null) {
+            linkResolveContext
+                    .asSequence()
+                    .map { p -> linkResolver.tryResolveContentLink(p, href) }
+                    .filterNotNull()
+                    .firstOrNull()
+                    ?.let { return it }
+        }
+        logger.warn("Unresolved link to `$href` in include ($fileName)")
+        return ContentExternalLink("#")
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt
new file mode 100644
index 0000000..2c75cfd
--- /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 0000000..116a5c0
--- /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/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt
new file mode 100644
index 0000000..b0988c3
--- /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 0000000..86c917c
--- /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 0000000..1ef5283
--- /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 0000000..7c8e5c3
--- /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 0000000..3c28d6c
--- /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 0000000..d9463c5
--- /dev/null
+++ b/core/src/main/kotlin/Utilities/Html.kt
@@ -0,0 +1,18 @@
+package org.jetbrains.dokka
+
+import java.net.URI
+
+
+/**
+ * Replaces symbols reserved in HTML with their respective entities.
+ * Replaces & with &amp;, < with &lt; and > with &gt;
+ */
+fun String.htmlEscape(): String = replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+// A URI consists of several parts (as described in https://docs.oracle.com/javase/7/docs/api/java/net/URI.html ):
+// [scheme:][//authority][path][?query][#fragment]
+//
+// The anchorEnchoded() function encodes the given string to make it a legal value for <fragment>
+fun String.anchorEncoded(): String {
+    return URI(null, null, this).getRawFragment()
+}
diff --git a/core/src/main/kotlin/Utilities/Path.kt b/core/src/main/kotlin/Utilities/Path.kt
new file mode 100644
index 0000000..0583849
--- /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 0000000..173389d
--- /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 0000000..83c4c65
--- /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 0000000..98f8c80
--- /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 0000000..9827c62
--- /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 0000000..76d6338
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/docbase.kt
@@ -0,0 +1,523 @@
+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.isNotEmpty() -> TypeAdapter(module, node.links.first()).asClassDoc()
+                    else -> ClassDocumentationNodeAdapter(module, node) // TODO ?
+                }
+            }
+
+    override fun asTypeVariable(): TypeVariable? = if (node.kind == NodeKind.TypeParameter) TypeVariableAdapter(module, node) else null
+    override fun asParameterizedType(): ParameterizedType? =
+            if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null)
+                ParameterizedTypeAdapter(module, node)
+            else
+                null
+
+    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 0000000..483fb3c
--- /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 0000000..fc38368
--- /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 0000000..6125f96
--- /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 0000000..95c6e87
--- /dev/null
+++ b/core/src/main/kotlin/javadoc/tags.kt
@@ -0,0 +1,240 @@
+package org.jetbrains.dokka.javadoc
+
+import com.sun.javadoc.*
+import org.jetbrains.dokka.*
+import java.util.*
+
+class TagImpl(val holder: Doc, val name: String, val text: String): Tag {
+    override fun text(): String? = text
+
+    override fun holder(): Doc = holder
+    override fun firstSentenceTags(): Array<out Tag>? = arrayOf()
+    override fun inlineTags(): Array<out Tag>?  = arrayOf()
+
+    override fun name(): String = name
+    override fun kind(): String = name
+
+    override fun position(): SourcePosition = holder.position()
+}
+
+class TextTag(val holder: Doc, val content: ContentText) : Tag {
+    val plainText: String
+        get() = content.text
+
+    override fun name(): String = "Text"
+    override fun kind(): String = name()
+    override fun text(): String? = plainText
+    override fun inlineTags(): Array<out Tag> = arrayOf(this)
+    override fun holder(): Doc = holder
+    override fun firstSentenceTags(): Array<out Tag> = arrayOf(this)
+    override fun position(): SourcePosition = holder.position()
+}
+
+abstract class SeeTagAdapter(val holder: Doc, val content: ContentNodeLink) : SeeTag {
+    override fun position(): SourcePosition? = holder.position()
+    override fun name(): String = "@see"
+    override fun kind(): String = "@see"
+    override fun holder(): Doc = holder
+
+    override fun text(): String? = content.node?.name ?: "(?)"
+}
+
+class SeeExternalLinkTagAdapter(val holder: Doc, val link: ContentExternalLink) : SeeTag {
+    override fun position(): SourcePosition = holder.position()
+    override fun text(): String = label()
+    override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
+
+    override fun label(): String {
+        val label = link.asText() ?: link.href
+        return "<a href=\"${link.href}\">$label</a>"
+    }
+
+    override fun referencedPackage(): PackageDoc? = null
+    override fun referencedClass(): ClassDoc? = null
+    override fun referencedMemberName(): String? = null
+    override fun referencedClassName(): String? = null
+    override fun referencedMember(): MemberDoc? = null
+    override fun holder(): Doc = holder
+    override fun firstSentenceTags(): Array<out Tag> = inlineTags()
+    override fun name(): String = "@link"
+    override fun kind(): String = "@see"
+}
+
+fun ContentBlock.asText(): String? {
+    val contentText = children.singleOrNull() as? ContentText
+    return contentText?.text
+}
+
+class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) {
+    override fun referencedMember(): MemberDoc = method
+    override fun referencedMemberName(): String = method.name()
+    override fun referencedPackage(): PackageDoc? = null
+    override fun referencedClass(): ClassDoc? = method.containingClass()
+    override fun referencedClassName(): String = method.containingClass()?.name() ?: ""
+    override fun label(): String = "${method.containingClass()?.name()}.${method.name()}"
+
+    override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
+    override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
+}
+
+class SeeClassTagAdapter(holder: Doc, val clazz: ClassDocumentationNodeAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) {
+    override fun referencedMember(): MemberDoc? = null
+    override fun referencedMemberName(): String? = null
+    override fun referencedPackage(): PackageDoc? = null
+    override fun referencedClass(): ClassDoc = clazz
+    override fun referencedClassName(): String = clazz.name()
+    override fun label(): String = "${clazz.classNode.kind.name.toLowerCase()} ${clazz.name()}"
+
+    override fun inlineTags(): Array<out Tag> = emptyArray() // TODO
+    override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO
+}
+
+class ParamTagAdapter(val module: ModuleNodeAdapter,
+                      val holder: Doc,
+                      val parameterName: String,
+                      val typeParameter: Boolean,
+                      val content: List<ContentNode>) : ParamTag {
+
+    constructor(module: ModuleNodeAdapter, holder: Doc, parameterName: String, isTypeParameter: Boolean, content: ContentNode)
+        : this(module, holder, parameterName, isTypeParameter, listOf(content)) {
+    }
+
+    override fun name(): String = "@param"
+    override fun kind(): String = name()
+    override fun holder(): Doc = holder
+    override fun position(): SourcePosition? = holder.position()
+
+    override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug
+    override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray()
+    override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text())))
+
+    override fun isTypeParameter(): Boolean = typeParameter
+    override fun parameterComment(): String = content.toString() // TODO
+    override fun parameterName(): String = parameterName
+}
+
+
+class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag {
+    override fun name(): String = "@throws"
+    override fun kind(): String = name()
+    override fun holder(): Doc = holder
+    override fun position(): SourcePosition? = holder.position()
+
+    override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}"
+    override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray()
+    override fun firstSentenceTags(): Array<out Tag> = emptyArray()
+
+    override fun exceptionComment(): String = content.toString()
+    override fun exceptionType(): Type = type
+    override fun exception(): ClassDoc = type
+    override fun exceptionName(): String = type.qualifiedTypeName()
+}
+
+class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag {
+    override fun name(): String = "@return"
+    override fun kind() = name()
+    override fun holder() = holder
+    override fun position(): SourcePosition? = holder.position()
+
+    override fun text(): String = "@return $content" // Seems has no effect, so used for debug
+    override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray()
+    override fun firstSentenceTags(): Array<Tag> = inlineTags()
+}
+
+fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } }
+
+fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) }
+
+private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) {
+    nodes.forEach {
+        buildInlineTags(module, holder, it, result)
+    }
+}
+
+
+private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, node: ContentNode, result: MutableList<Tag>) {
+    fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentBlock, result: MutableList<Tag>) {
+        if (node.children.isNotEmpty()) {
+            val open = TextTag(holder, ContentText(prefix))
+            val close = TextTag(holder, ContentText(postfix))
+
+            result.add(open)
+            buildInlineTags(module, holder, node.children, result)
+
+            if (result.last() === open) {
+                result.removeAt(result.lastIndex)
+            } else {
+                result.add(close)
+            }
+        }
+    }
+
+    fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentNode, result: MutableList<Tag>) {
+        if (node !is ContentEmpty) {
+            val open = TextTag(holder, ContentText(prefix))
+            val close = TextTag(holder, ContentText(postfix))
+
+            result.add(open)
+            buildInlineTags(module, holder, node, result)
+            if (result.last() === open) {
+                result.removeAt(result.lastIndex)
+            } else {
+                result.add(close)
+            }
+        }
+    }
+
+    when (node) {
+        is ContentText -> result.add(TextTag(holder, node))
+        is ContentNodeLink -> {
+            val target = node.node
+            when (target?.kind) {
+                NodeKind.Function -> result.add(SeeMethodTagAdapter(holder, MethodAdapter(module, node.node!!), node))
+
+                in NodeKind.classLike -> result.add(SeeClassTagAdapter(holder, ClassDocumentationNodeAdapter(module, node.node!!), node))
+
+                else -> buildInlineTags(module, holder, node.children, result)
+            }
+        }
+        is ContentExternalLink -> result.add(SeeExternalLinkTagAdapter(holder, node))
+        is ContentSpecialReference -> surroundWith(module, holder, "<aside class=\"note\">", "</aside>", node, result)
+        is ContentCode -> surroundWith(module, holder, "<code>", "</code>", node, result)
+        is ContentBlockCode -> surroundWith(module, holder, "<code><pre>", "</pre></code>", node, result)
+        is ContentEmpty -> {}
+        is ContentEmphasis -> surroundWith(module, holder, "<em>", "</em>", node, result)
+        is ContentHeading -> surroundWith(module, holder, "<h${node.level}>", "</h${node.level}>", node, result)
+        is ContentEntity -> result.add(TextTag(holder, ContentText(node.text))) // TODO ??
+        is ContentIdentifier -> result.add(TextTag(holder, ContentText(node.text))) // TODO
+        is ContentKeyword -> result.add(TextTag(holder, ContentText(node.text))) // TODO
+        is ContentListItem ->  surroundWith(module, holder, "<li>", "</li>", node, result)
+        is ContentOrderedList -> surroundWith(module, holder, "<ol>", "</ol>", node, result)
+        is ContentUnorderedList -> surroundWith(module, holder, "<ul>", "</ul>", node, result)
+        is ContentParagraph -> surroundWith(module, holder, "<p>", "</p>", node, result)
+
+        is ContentDescriptionList -> surroundWith(module, holder, "<dl>", "</dl>", node, result)
+        is ContentDescriptionTerm -> surroundWith(module, holder, "<dt>", "</dt>", node, result)
+        is ContentDescriptionDefinition -> surroundWith(module, holder, "<dd>", "</dd>", node, result)
+
+        is ContentTable -> surroundWith(module, holder, "<table>", "</table>", node, result)
+        is ContentTableBody -> surroundWith(module, holder, "<tbody>", "</tbody>", node, result)
+        is ContentTableRow -> surroundWith(module, holder, "<tr>", "</tr>", node, result)
+        is ContentTableHeader -> surroundWith(module, holder, "<th>", "</th>", node, result)
+        is ContentTableCell -> surroundWith(module, holder, "<td>", "</td>", node, result)
+
+        is ContentSection -> surroundWith(module, holder, "<p>", "</p>", node, result) // TODO how section should be represented?
+        is ContentNonBreakingSpace -> result.add(TextTag(holder, ContentText("&nbsp;")))
+        is ContentStrikethrough -> surroundWith(module, holder, "<strike>", "</strike>", node, result)
+        is ContentStrong -> surroundWith(module, holder, "<strong>", "</strong>", node, result)
+        is ContentSymbol -> result.add(TextTag(holder, ContentText(node.text))) // TODO?
+        is Content -> {
+            surroundWith(module, holder, "<p>", "</p>", node.summary, result)
+            surroundWith(module, holder, "<p>", "</p>", node.description, result)
+        }
+        is ContentBlock -> {
+            surroundWith(module, holder, "", "", node, result)
+        }
+        is ContentHardLineBreak -> result.add(TextTag(holder, ContentText("<br/>")))
+
+        else -> result.add(TextTag(holder, ContentText("$node")))
+    }
+}
\ No newline at end of file
diff --git a/core/src/main/resources/META-INF/MANIFEST.MF b/core/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..78fabdd
--- /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.DokkaPackage
+
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 0000000..29e05b3
--- /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 0000000..52b1909
--- /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 0000000..5e8f7aa
--- /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 0000000..f598f37
--- /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 0000000..7881dfa
--- /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 0000000..7d178ba
--- /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 0000000..fbb2bbe
--- /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 0000000..a0d8a94
--- /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 0000000..b11401a
--- /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 0000000..f4c320b
--- /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 0000000..bda616a
--- /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 0000000..c13e767
--- /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 0000000..6217a6d
--- /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 0000000..c484a92
--- /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 0000000..3b61eab
--- /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 0000000..0d5d7d1
--- /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 0000000..914be69
--- /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 0000000..ae3e671
--- /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 0000000..1a2b114
--- /dev/null
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -0,0 +1,300 @@
+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,
+                    verifier: (DocumentationModule) -> Unit) {
+    val tempDir = FileUtil.createTempDirectory("dokka", "")
+    try {
+        val sourceFile = File(source)
+        FileUtil.copy(sourceFile, File(tempDir, sourceFile.name))
+        verifyModel(JavaSourceRoot(tempDir, null), withJdk = true, withKotlinRuntime = withKotlinRuntime, verifier = verifier)
+    }
+    finally {
+        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,
+                     outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+    verifyJavaModel(path, withKotlinRuntime) { 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/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt
new file mode 100644
index 0000000..ef9e815
--- /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 0000000..b90ab2b
--- /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 0000000..01e9b3c
--- /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 0000000..59746b1
--- /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 0000000..620f10d
--- /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 0000000..01ac58d
--- /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 0000000..63d7d57
--- /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 0000000..453b1de
--- /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 0000000..08d4679
--- /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 0000000..b7fff1e
--- /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 0000000..d610881
--- /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 0000000..74265cb
--- /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("java.lang.String", type.qualifiedTypeName())
+            assertEquals("java.lang.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("kotlin.jvm.functions.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 0000000..b0ec68f
--- /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 0000000..56a38fd
--- /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)
+                    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 0000000..7869837
--- /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 0000000..3291068
--- /dev/null
+++ b/core/src/test/kotlin/model/FunctionTest.kt
@@ -0,0 +1,237 @@
+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)
+            }
+        }
+    }
+}
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
new file mode 100644
index 0000000..267d611
--- /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:String,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 0000000..d24d8bd
--- /dev/null
+++ b/core/src/test/kotlin/model/KotlinAsJavaTest.kt
@@ -0,0 +1,40 @@
+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()
+            assertEquals("fn", fn.name)
+            assertEquals(NodeKind.Function, fn.kind)
+        }
+    }
+
+    @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 0000000..6b72525
--- /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 0000000..3936fb4
--- /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 0000000..41c3a4c
--- /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 0000000..c653ac8
--- /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 0000000..80c18df
--- /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 0000000..ca6ee9c
--- /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 0000000..1b58f56
--- /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 0000000..930d6a6
--- /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 0000000..fdbd915
--- /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 0000000..0751d57
--- /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 0000000..a981cfb
--- /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 0000000..2a84957
--- /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 0000000..4b47137
--- /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 0000000..62c6f0e
--- /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 0000000..abd20cc
--- /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 0000000..4138bf3
--- /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 0000000..db20ff7
--- /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 0000000..8d091b8
--- /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 0000000..3c6e497
--- /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 0000000..9560014
--- /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 0000000..edee2c1
--- /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 0000000..df43b5f
--- /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 0000000..9335039
--- /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 0000000..e5cb255
--- /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 0000000..1025cf0
--- /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 0000000..aa5f5ff
--- /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 0000000..b27f5a4
--- /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 0000000..b87cce5
--- /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 0000000..ceb2475
--- /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 0000000..47d6b1a
--- /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 0000000..31cfa3a
--- /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 0000000..88d2264
--- /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 0000000..6ef4df2
--- /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 0000000..92a40c6
--- /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 0000000..c846793
--- /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 0000000..e364c41
--- /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 0000000..47ae689
--- /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 0000000..7c763b4
--- /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 0000000..e280793
--- /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 0000000..892ef63
--- /dev/null
+++ b/core/testdata/format/JavaSupertype.html
@@ -0,0 +1,36 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>JavaSupertype.Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">JavaSupertype</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="return-foo.html">returnFoo</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">returnFoo</span><span class="symbol">(</span><span class="identifier" id="JavaSupertype.Bar$returnFoo(JavaSupertype.Foo)/foo">foo</span><span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span><span class="symbol">)</span><span class="symbol">: </span><a href="../-foo/index.html"><span class="identifier">JavaSupertype.Foo</span></a><span class="symbol">!</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/JavaSupertype.java b/core/testdata/format/JavaSupertype.java
new file mode 100644
index 0000000..2045573
--- /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 0000000..5a4d174
--- /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 0000000..190e853
--- /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 0000000..cc3bfc1
--- /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 0000000..aa622ea
--- /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 0000000..89d494f
--- /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 0000000..55fda40
--- /dev/null
+++ b/core/testdata/format/annotationClass.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [fancy](./index.md)
+
+# fancy
+
+`annotation class fancy`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `fancy()` |
+
diff --git a/core/testdata/format/annotationClass.package.md b/core/testdata/format/annotationClass.package.md
new file mode 100644
index 0000000..c8aff7a
--- /dev/null
+++ b/core/testdata/format/annotationClass.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Annotations
+
+| [fancy](fancy/index.md) | `annotation class fancy` |
+
diff --git a/core/testdata/format/annotationParams.kt b/core/testdata/format/annotationParams.kt
new file mode 100644
index 0000000..f259a74
--- /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 0000000..cfa3b82
--- /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 0000000..57f7624
--- /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 0000000..2e1604d
--- /dev/null
+++ b/core/testdata/format/annotations.md
@@ -0,0 +1,18 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`data class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Properties
+
+| [x](x.md) | `val x: Int` |
+
+### Functions
+
+| [bar](bar.md) | `fun bar(notInlined: () -> Unit): Unit` |
+
diff --git a/core/testdata/format/arrayAverage.kt b/core/testdata/format/arrayAverage.kt
new file mode 100644
index 0000000..1f9e12d
--- /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 0000000..2c6927d
--- /dev/null
+++ b/core/testdata/format/arrayAverage.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [XArray](./index.md)
+
+# XArray
+
+`class XArray<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `XArray()` |
+
+### Extension Functions
+
+| [average](../average.md) | `fun `[`XArray`](./index.md)`<out Byte>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Double>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Float>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Int>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Long>.average(): Double`<br>`fun `[`XArray`](./index.md)`<out Short>.average(): Double` |
+
diff --git a/core/testdata/format/backtickInCodeBlock.kt b/core/testdata/format/backtickInCodeBlock.kt
new file mode 100644
index 0000000..b457efb
--- /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 0000000..830539a
--- /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 0000000..f0351d7
--- /dev/null
+++ b/core/testdata/format/blankLineInsideCodeBlock.html
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>u - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./u.html">u</a><br/>
+<br/>
+<h1>u</h1>
+<a name="$u()"></a>
+<code><span class="keyword">fun </span><span class="identifier">u</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><pre><code>This is a test
+    of Dokka's code blocks.
+Here is a blank line.
+
+The previous line was blank.
+</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/blankLineInsideCodeBlock.kt b/core/testdata/format/blankLineInsideCodeBlock.kt
new file mode 100644
index 0000000..9430f4d
--- /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 0000000..956f895
--- /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 0000000..01aaaf0
--- /dev/null
+++ b/core/testdata/format/bracket.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>bar[]</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/bracket.kt b/core/testdata/format/bracket.kt
new file mode 100644
index 0000000..d41b007
--- /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 0000000..c598a73
--- /dev/null
+++ b/core/testdata/format/brokenLink.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This references <a href="#">noSuchIdentifier</a>.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/brokenLink.kt b/core/testdata/format/brokenLink.kt
new file mode 100644
index 0000000..268a986
--- /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 0000000..88feea5
--- /dev/null
+++ b/core/testdata/format/classWithCompanionObject.html
@@ -0,0 +1,48 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Klass - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Klass</a><br/>
+<br/>
+<h1>Klass</h1>
+<code><span class="keyword">class </span><span class="identifier">Klass</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Klass</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Companion Object Properties</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="x.html">x</a></p>
+</td>
+<td>
+<code><span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Companion Object Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/classWithCompanionObject.kt b/core/testdata/format/classWithCompanionObject.kt
new file mode 100644
index 0000000..fdbd915
--- /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 0000000..40f605b
--- /dev/null
+++ b/core/testdata/format/classWithCompanionObject.md
@@ -0,0 +1,18 @@
+[test](../index.md) / [Klass](./index.md)
+
+# Klass
+
+`class Klass`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Klass()` |
+
+### Companion Object Properties
+
+| [x](x.md) | `val x: Int` |
+
+### Companion Object Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+
diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html
new file mode 100644
index 0000000..5ef09d6
--- /dev/null
+++ b/core/testdata/format/codeBlock.html
@@ -0,0 +1,62 @@
+<!-- File: test/-it-does-some-obfuscated-thing/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>ItDoesSomeObfuscatedThing - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">ItDoesSomeObfuscatedThing</a><br/>
+<br/>
+<h1>ItDoesSomeObfuscatedThing</h1>
+<code><span class="keyword">class </span><span class="identifier">ItDoesSomeObfuscatedThing</span></code>
+<p>Check output of</p>
+<pre><code class="lang-brainfuck">++++++++++[&gt;+++++++&gt;++++++++++&gt;+++&gt;+&lt;&lt;&lt;&lt;-]&gt;++.&gt;+.+++++++..+++.&gt;++.&lt;&lt;+++++++++++++++.&gt;.+++.------.--------.&gt;+.&gt;.
+</code></pre>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">ItDoesSomeObfuscatedThing</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Check output of</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-throws/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Throws - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Throws</a><br/>
+<br/>
+<h1>Throws</h1>
+<code><span class="keyword">class </span><span class="identifier">Throws</span></code>
+<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
+<p>Example:</p>
+<pre><code>Throws(IOException::class)
+fun readFile(name: String): String {...}
+</code></pre>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Throws</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/codeBlock.kt b/core/testdata/format/codeBlock.kt
new file mode 100644
index 0000000..633bf41
--- /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 0000000..d697e43
--- /dev/null
+++ b/core/testdata/format/codeBlock.md
@@ -0,0 +1,37 @@
+<!-- File: test/-it-does-some-obfuscated-thing/index.md -->
+[test](../index.md) / [ItDoesSomeObfuscatedThing](./index.md)
+
+# ItDoesSomeObfuscatedThing
+
+`class ItDoesSomeObfuscatedThing`
+
+Check output of
+
+``` brainfuck
+++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `ItDoesSomeObfuscatedThing()`<br>Check output of |
+
+<!-- File: test/-throws/index.md -->
+[test](../index.md) / [Throws](./index.md)
+
+# Throws
+
+`class Throws`
+
+This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.
+
+Example:
+
+```
+Throws(IOException::class)
+fun readFile(name: String): String {...}
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Throws()`<br>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method. |
+
diff --git a/core/testdata/format/codeBlockNoHtmlEscape.kt b/core/testdata/format/codeBlockNoHtmlEscape.kt
new file mode 100644
index 0000000..5f48b39
--- /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 0000000..548dac4
--- /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 0000000..bfe93f3
--- /dev/null
+++ b/core/testdata/format/codeSpan.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This is a <code>code span</code>.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/codeSpan.kt b/core/testdata/format/codeSpan.kt
new file mode 100644
index 0000000..645f454
--- /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 0000000..154ef9b
--- /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 0000000..aac7b3e
--- /dev/null
+++ b/core/testdata/format/companionImplements.md
@@ -0,0 +1,16 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+Correct ref [Foo.Companion](-companion.md)
+
+### Types
+
+| [Companion](-companion.md) | `companion object Companion : `[`Bar`](../-bar.md) |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>Correct ref [Foo.Companion](-companion.md) |
+
diff --git a/core/testdata/format/companionObjectExtension.kt b/core/testdata/format/companionObjectExtension.kt
new file mode 100644
index 0000000..f452de2
--- /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 0000000..c0e268e
--- /dev/null
+++ b/core/testdata/format/companionObjectExtension.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Companion Object Extension Properties
+
+| [x](../x.md) | `val Foo.Default.x: Int`<br>The default object property. |
+
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
new file mode 100644
index 0000000..8842969
--- /dev/null
+++ b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../../index.html">test</a>&nbsp;/&nbsp;<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>See <a href="../-foo/xyzzy.html">xyzzy</a></p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Inherited Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="../-foo/xyzzy.html">xyzzy</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">xyzzy</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt b/core/testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt
new file mode 100644
index 0000000..102782f
--- /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 0000000..7c14303
--- /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/deprecated.class.html b/core/testdata/format/deprecated.class.html
new file mode 100644
index 0000000..540060d
--- /dev/null
+++ b/core/testdata/format/deprecated.class.html
@@ -0,0 +1,59 @@
+<!-- File: test/-c/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>C - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
+<br/>
+<h1>C</h1>
+<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code><br/>
+<strong>Deprecated:</strong> This class sucks<br/>
+<br/>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<strong>Deprecated:</strong> This function sucks<br/>
+<br/>
+</BODY>
+</HTML>
+<!-- File: test/p.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>p - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./p.html">p</a><br/>
+<br/>
+<h1>p</h1>
+<a name="$p"></a>
+<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code><br/>
+<strong>Deprecated:</strong> This property sucks<br/>
+<br/>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/deprecated.kt b/core/testdata/format/deprecated.kt
new file mode 100644
index 0000000..4fc568c
--- /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 0000000..3506de6
--- /dev/null
+++ b/core/testdata/format/deprecated.package.html
@@ -0,0 +1,47 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>root package - test</title>
+</HEAD>
+<BODY>
+<a href="./index.html">test</a><br/>
+<br/>
+<h2>Package &lt;root&gt;</h2>
+<h3>Types</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-c/index.html">C</a></p>
+</td>
+<td>
+<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Properties</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="p.html">p</a></p>
+</td>
+<td>
+<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="f.html">f</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/dynamicExtension.kt b/core/testdata/format/dynamicExtension.kt
new file mode 100644
index 0000000..5c83bf2
--- /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 0000000..382cf97
--- /dev/null
+++ b/core/testdata/format/dynamicExtension.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/dynamicType.kt b/core/testdata/format/dynamicType.kt
new file mode 100644
index 0000000..9d557ac
--- /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 0000000..07a1d10
--- /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 0000000..3ed81df
--- /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 0000000..5d56d96
--- /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 0000000..639f290
--- /dev/null
+++ b/core/testdata/format/entity.html
@@ -0,0 +1,27 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Copyright &copy; JetBrains 2015 &#x22;</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Copyright &copy; JetBrains 2015 &#x22;</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/entity.kt b/core/testdata/format/entity.kt
new file mode 100644
index 0000000..163d2ee
--- /dev/null
+++ b/core/testdata/format/entity.kt
@@ -0,0 +1,4 @@
+/**
+ * Copyright &copy; JetBrains 2015 &#x22;
+ */
+class Bar {}
diff --git a/core/testdata/format/enumClass.kt b/core/testdata/format/enumClass.kt
new file mode 100644
index 0000000..3c2a49f
--- /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 0000000..76c83b9
--- /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 0000000..590577d
--- /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 0000000..5c0b2de
--- /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 0000000..8b2a665
--- /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 0000000..d005bd8
--- /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 0000000..1e928bb
--- /dev/null
+++ b/core/testdata/format/exceptionClass.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [MyException](./index.md)
+
+# MyException
+
+`class MyException : Exception`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `MyException()` |
+
diff --git a/core/testdata/format/exceptionClass.package.md b/core/testdata/format/exceptionClass.package.md
new file mode 100644
index 0000000..8716df0
--- /dev/null
+++ b/core/testdata/format/exceptionClass.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Exceptions
+
+| [MyException](-my-exception/index.md) | `class MyException : Exception` |
+
diff --git a/core/testdata/format/exclInCodeBlock.kt b/core/testdata/format/exclInCodeBlock.kt
new file mode 100644
index 0000000..62b234d
--- /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 0000000..d665c41
--- /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 0000000..bfb344b
--- /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 0000000..e1e8582
--- /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 0000000..9f3130b
--- /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 0000000..ea765bd
--- /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 0000000..37fc09d
--- /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 0000000..0679ac8
--- /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 0000000..b8fa200
--- /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 0000000..6f2eff9
--- /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 0000000..ad89511
--- /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 0000000..4ca0ee2
--- /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 0000000..38ffde7
--- /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 0000000..3a3a102
--- /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 0000000..05f7fbe
--- /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 0000000..82a9cc0
--- /dev/null
+++ b/core/testdata/format/functionalTypeWithNamedParameters.html
@@ -0,0 +1,103 @@
+<!-- File: test/-a/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>A - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">A</a><br/>
+<br/>
+<h1>A</h1>
+<code><span class="keyword">class </span><span class="identifier">A</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">A</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-b/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>B - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">B</a><br/>
+<br/>
+<h1>B</h1>
+<code><span class="keyword">class </span><span class="identifier">B</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">B</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/-c/index.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>C - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">C</a><br/>
+<br/>
+<h1>C</h1>
+<code><span class="keyword">class </span><span class="identifier">C</span></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
+<!-- File: test/accept-function-type-with-named-arguments.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>acceptFunctionTypeWithNamedArguments - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./accept-function-type-with-named-arguments.html">acceptFunctionTypeWithNamedArguments</a><br/>
+<br/>
+<h1>acceptFunctionTypeWithNamedArguments</h1>
+<a name="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))"></a>
+<code><span class="keyword">fun </span><span class="identifier">acceptFunctionTypeWithNamedArguments</span><span class="symbol">(</span><span class="identifier" id="$acceptFunctionTypeWithNamedArguments(kotlin.Function2((B, A, C)))/f">f</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">bb</span><span class="symbol">:</span>&nbsp;<a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">aa</span><span class="symbol">:</span>&nbsp;<a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="-c/index.html"><span class="identifier">C</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+</BODY>
+</HTML>
+<!-- File: test/f.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f"></a>
+<code><span class="keyword">val </span><span class="identifier">f</span><span class="symbol">: </span><span class="symbol">(</span><span class="identifier">a</span><span class="symbol">:</span>&nbsp;<a href="-a/index.html"><span class="identifier">A</span></a><span class="symbol">,</span>&nbsp;<span class="identifier">b</span><span class="symbol">:</span>&nbsp;<a href="-b/index.html"><span class="identifier">B</span></a><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="-c/index.html"><span class="identifier">C</span></a></code>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/functionalTypeWithNamedParameters.kt b/core/testdata/format/functionalTypeWithNamedParameters.kt
new file mode 100644
index 0000000..3dada27
--- /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 0000000..2255c7a
--- /dev/null
+++ b/core/testdata/format/functionalTypeWithNamedParameters.md
@@ -0,0 +1,45 @@
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
+
+# A
+
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
+
+# B
+
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/accept-function-type-with-named-arguments.md -->
+[test](index.md) / [acceptFunctionTypeWithNamedArguments](./accept-function-type-with-named-arguments.md)
+
+# acceptFunctionTypeWithNamedArguments
+
+`fun acceptFunctionTypeWithNamedArguments(f: (bb: `[`B`](-b/index.md)`, aa: `[`A`](-a/index.md)`) -> `[`C`](-c/index.md)`): Unit`
+<!-- File: test/f.md -->
+[test](index.md) / [f](./f.md)
+
+# f
+
+`val f: (a: `[`A`](-a/index.md)`, b: `[`B`](-b/index.md)`) -> `[`C`](-c/index.md)
\ No newline at end of file
diff --git a/core/testdata/format/genericInheritedExtensions.kt b/core/testdata/format/genericInheritedExtensions.kt
new file mode 100644
index 0000000..4c07e1e
--- /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 0000000..8d0e316
--- /dev/null
+++ b/core/testdata/format/genericInheritedExtensions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar<T> : `[`Foo`](../-foo/index.md)`<`[`T`](index.md#T)`>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [first](../first.md) | `fun <T> `[`Foo`](../-foo/index.md)`<`[`T`](../first.md#T)`>.first(): Unit` |
+| [second](../second.md) | `fun <T> `[`Bar`](./index.md)`<`[`T`](../second.md#T)`>.second(): Unit` |
+
diff --git a/core/testdata/format/gfm/listInTableCell.kt b/core/testdata/format/gfm/listInTableCell.kt
new file mode 100644
index 0000000..2f4fdf5
--- /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 0000000..359ad91
--- /dev/null
+++ b/core/testdata/format/gfm/listInTableCell.md
@@ -0,0 +1,17 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+### Constructors
+
+| Name | Summary |
+|---|---|
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Functions
+
+| Name | Summary |
+|---|---|
+| [foo](foo.md) | `fun foo(): Unit`<ol><li>Foo</li><li>Bar</li></ol> |
diff --git a/core/testdata/format/gfm/sample.kt b/core/testdata/format/gfm/sample.kt
new file mode 100644
index 0000000..3300d2c
--- /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 0000000..2b08229
--- /dev/null
+++ b/core/testdata/format/gfm/sample.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+The class Foo.
+
+### Constructors
+
+| Name | Summary |
+|---|---|
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>The class Foo. |
+
+### Functions
+
+| Name | Summary |
+|---|---|
+| [bar](bar.md) | `fun bar(): Unit`<br>The method bar. |
+| [baz](baz.md) | `fun baz(): Unit`<br>The method baz. |
diff --git a/core/testdata/format/htmlEscaping.html b/core/testdata/format/htmlEscaping.html
new file mode 100644
index 0000000..bd64454
--- /dev/null
+++ b/core/testdata/format/htmlEscaping.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>x - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./x.html">x</a><br/>
+<br/>
+<h1>x</h1>
+<a name="$x()"></a>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">x</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="x.html#T"><span class="identifier">T</span></a><span class="symbol">?</span></code>
+<p>Special characters: &lt; is "less than", &gt; is "greater than", &amp; is "ampersand"</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/htmlEscaping.kt b/core/testdata/format/htmlEscaping.kt
new file mode 100644
index 0000000..8778d8a
--- /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 0000000..d2c65b4
--- /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 0000000..08fc273
--- /dev/null
+++ b/core/testdata/format/inapplicableExtensionFunctions.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)`<Char>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/indentedCodeBlock.html b/core/testdata/format/indentedCodeBlock.html
new file mode 100644
index 0000000..86c129f
--- /dev/null
+++ b/core/testdata/format/indentedCodeBlock.html
@@ -0,0 +1,17 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Create a new Foo value as follows:</p>
+<pre><code>    val foo = Foo.create {
+        type { "ABC" }
+    }</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/indentedCodeBlock.kt b/core/testdata/format/indentedCodeBlock.kt
new file mode 100644
index 0000000..19c5365
--- /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 0000000..77b0630
--- /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 0000000..74a3749
--- /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 0000000..ab8f0aa
--- /dev/null
+++ b/core/testdata/format/inheritedCompanionObjectProperties.md
@@ -0,0 +1,30 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C : `[`A`](../-a/index.md)
+
+### Types
+
+| [Companion](-companion/index.md) | `companion object Companion : `[`B`](../-b/index.md) |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+### Functions
+
+| [xyzzy](xyzzy.md) | `fun xyzzy(): Unit` |
+
+### Inherited Functions
+
+| [foo](../-a/foo.md) | `fun foo(): Unit` |
+
+### Companion Object Functions
+
+| [shazam](shazam.md) | `fun shazam(): Unit` |
+
+### Inherited Companion Object Functions
+
+| [bar](../-b/bar.md) | `fun bar(): Unit` |
+
diff --git a/core/testdata/format/inheritedExtensions.kt b/core/testdata/format/inheritedExtensions.kt
new file mode 100644
index 0000000..e38fe00
--- /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 0000000..97a7366
--- /dev/null
+++ b/core/testdata/format/inheritedExtensions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [first](../first.md) | `fun `[`Foo`](../-foo/index.md)`.first(): Unit` |
+| [second](../second.md) | `fun `[`Bar`](./index.md)`.second(): Unit` |
+
diff --git a/core/testdata/format/inheritedLink.1.kt b/core/testdata/format/inheritedLink.1.kt
new file mode 100644
index 0000000..29cc12b
--- /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 0000000..86b8f4e
--- /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 0000000..e5af326
--- /dev/null
+++ b/core/testdata/format/inheritedLink.md
@@ -0,0 +1,17 @@
+<!-- File: test/p2/-foo-bar/-init-.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [&lt;init&gt;](./-init-.md)
+
+# &lt;init&gt;
+
+`FooBar()`
+<!-- File: test/p2/-foo-bar/say-hello.md -->
+[test](../../index.md) / [p2](../index.md) / [FooBar](index.md) / [sayHello](./say-hello.md)
+
+# sayHello
+
+`fun sayHello(): String`
+
+Overrides [Foo.sayHello](../../p1/-foo/say-hello.md)
+
+Says hello - [LinkedList](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html).
+
diff --git a/core/testdata/format/inheritedMembers.kt b/core/testdata/format/inheritedMembers.kt
new file mode 100644
index 0000000..2d0c4ca
--- /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 0000000..334df36
--- /dev/null
+++ b/core/testdata/format/inheritedMembers.md
@@ -0,0 +1,26 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Properties
+
+| [secondProp](second-prop.md) | `val secondProp: Int` |
+
+### Inherited Properties
+
+| [firstProp](../-foo/first-prop.md) | `val firstProp: Int` |
+
+### Functions
+
+| [second](second.md) | `fun second(): Unit` |
+
+### Inherited Functions
+
+| [first](../-foo/first.md) | `fun first(): Unit` |
+
diff --git a/core/testdata/format/java-layout-html/ConstJava.html b/core/testdata/format/java-layout-html/ConstJava.html
new file mode 100644
index 0000000..26c9aa2
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.html
@@ -0,0 +1,93 @@
+<!-- File: /test/p/ConstJava.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>ConstJava</h1>
+    <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">ConstJava</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.ConstJava</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">static</span> <span class="identifier">String</span></td>
+          <td>
+            <div><code><a href="#myStringConst%3Akotlin.String">myStringConst</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><span class="keyword">static</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#myIntConst%3Akotlin.Int">myIntConst</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">static</span> <a href="#"><span class="identifier">ConstJava</span></a></td>
+          <td>
+            <div><code><a href="#myConstObjConst%3Ap.ConstJava">myConstObjConst</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="myStringConst:kotlin.String">
+      <h3>myStringConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myStringConst</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+      <pre>Value: <code>&quot;&quot;</code></pre>
+    </div>
+    <div id="myIntConst:kotlin.Int">
+      <h3>myIntConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myIntConst</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">ConstJava</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+    <h2>Properties</h2>
+    <div id="myConstObjConst:p.ConstJava">
+      <h3>myConstObjConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myConstObjConst</span><span class="symbol">: </span><a href="#"><span class="identifier">ConstJava</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/ConstJava.java b/core/testdata/format/java-layout-html/ConstJava.java
new file mode 100644
index 0000000..eb5bb2b
--- /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 0000000..1d6bc6c
--- /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 0000000..de0c794
--- /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 0000000..89cb572
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/G.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>G</h1>
+    <pre><span class="keyword">object </span><span class="identifier">G</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.G</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#y%3Akotlin.Int">y</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="y:kotlin.Int">
+      <h3>y</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">y</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+  </body>
+</html>
+<!-- File: /test/p/D.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>D</h1>
+    <pre><span class="keyword">class </span><span class="identifier">D</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.D</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#Companion.z%3Akotlin.Int">z</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="Companion.z:kotlin.Int">
+      <h3>z</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">z</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">D</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/const.kt b/core/testdata/format/java-layout-html/const.kt
new file mode 100644
index 0000000..a6a0884
--- /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 0000000..50f0653
--- /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 0000000..04415cc
--- /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 0000000..39db6c8
--- /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 0000000..0cbd034
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/Some.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>Some</h1>
+    <pre><span class="keyword">class </span><span class="identifier">Some</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.Some</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="package-summary.html">p</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.extFun.T%29.extFun%28%29">extFun</a>()</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29">nullableExtFun</a>()</code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="package-summary.html">p</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.extVal.T%29.extVal%3Akotlin.String">extVal</a></code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.nullableExtVal.T%29.nullableExtVal%3Akotlin.String">nullableExtVal</a></code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.kt b/core/testdata/format/java-layout-html/genericExtension.kt
new file mode 100644
index 0000000..0a9d74e
--- /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 0000000..610ebb2
--- /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 0000000..7025fc7
--- /dev/null
+++ b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.html
@@ -0,0 +1,80 @@
+<!-- File: /test/bar/X.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>X</h1>
+    <pre><span class="keyword">class </span><span class="identifier">X</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">bar.X</a></td>
+      </tr>
+    </table>
+    <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+            <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="identifier">String</span></td>
+          <td>
+            <div><code><a href="file:/foo/G.html#"><span class="identifier">G</span></a>.<a href="#%28foo.G%29.ext%28%29">ext</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">X</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+      <p>See <a href="file:/foo/#foobar%28%29">foo.foobar</a>
+See <a href="file:/foo/#v%3Akotlin.Int">foo.v</a>
+See <a href="file:/foo/G.html#">foo.G</a>
+See <a href="file:/foo/G.html#oo%28%29">foo.G.oo</a>
+See <a href="file:/foo/G.html#og%3Akotlin.Int">foo.G.og</a>
+See <a href="file:/foo/G.Companion.html#Companion.dg%28%29">foo.G.Companion.dg</a>
+See <a href="file:/foo/G.Companion.html#Companion.dv%3Akotlin.Int">foo.G.Companion.dv</a></p>
+    </div>
+    <h2>Functions</h2>
+    <div id="(foo.G).ext()">
+      <h3>ext</h3>
+      <pre><span class="keyword">fun </span><a href="file:/foo/G.html#"><span class="identifier">G</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt b/core/testdata/format/java-layout-html/inboundLinksInKotlinMode.kt
new file mode 100644
index 0000000..6420d78
--- /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 0000000..64d25c3
--- /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 0000000..c4c0469
--- /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 0000000..51358d2
--- /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 0000000..0b82c49
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.html
@@ -0,0 +1,177 @@
+<!-- File: /test/p/Foo.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>Foo</h1>
+    <pre><span class="keyword">class </span><span class="identifier">Foo</span></pre>
+    <table>
+      <tr>
+        <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">kotlin.Any</a></td>
+      </tr>
+      <tr>
+        <td>   ↳</td>
+        <td><a href="#">p.Foo</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="#s%28%29">s</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html">kotlin</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">T</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html">also</a>(<span class="identifier" id="kotlin$also(kotlin.also.T, kotlin.Function1((kotlin.also.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html">apply</a>(<span class="identifier" id="kotlin$apply(kotlin.apply.T, kotlin.Function1((kotlin.apply.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">R</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html">let</a>(<span class="identifier" id="kotlin$let(kotlin.let.T, kotlin.Function1((kotlin.let.T, kotlin.let.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">R</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html">run</a>(<span class="identifier" id="kotlin$run(kotlin.run.T, kotlin.Function1((kotlin.run.T, kotlin.run.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span><span class="symbol">?</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html">takeIf</a>(<span class="identifier" id="kotlin$takeIf(kotlin.takeIf.T, kotlin.Function1((kotlin.takeIf.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span><span class="symbol">?</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html">takeUnless</a>(<span class="identifier" id="kotlin$takeUnless(kotlin.takeUnless.T, kotlin.Function1((kotlin.takeUnless.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html"><span class="identifier">Pair</span></a><span class="symbol">&lt;</span><span class="identifier">A</span><span class="symbol">,</span>&nbsp;<span class="identifier">B</span><span class="symbol">&gt;</span></td>
+                  <td>
+                    <div><code><span class="identifier">A</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html">to</a>(<span class="identifier" id="kotlin$to(kotlin.to.A, kotlin.to.B)/that">that</span><span class="symbol">:</span>&nbsp;<span class="identifier">B</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+                  <td>
+                    <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html"><span class="identifier">Any</span></a><span class="symbol">?</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to-string.html">toString</a>()</code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#g%3Akotlin.String">g</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html">kotlin.jvm</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html"><span class="identifier">Class</span></a><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span></td>
+                  <td>
+                    <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html">javaClass</a></code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+    <h2>Functions</h2>
+    <div id="s()">
+      <h3>s</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">s</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <h2>Properties</h2>
+    <div id="g:kotlin.String">
+      <h3>g</h3>
+      <pre><span class="keyword">val </span><span class="identifier">g</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/simple.kt b/core/testdata/format/java-layout-html/simple.kt
new file mode 100644
index 0000000..89789c4
--- /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 0000000..85b1a43
--- /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 0000000..03665c6
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.package-summary.html
@@ -0,0 +1,97 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>p</h1>
+    <h2>Classes</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="Some.html#">Some</a></td>
+          <td></td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level constants summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelConst%3Akotlin.String">topLevelConst</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level functions summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="#topLevelFun%28%29">topLevelFun</a>()</code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="Some.html#"><span class="identifier">Some</span></a>.<a href="#%28p.Some%29.topLevelExtFun%28%29">topLevelExtFun</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level properties summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelVal%3Akotlin.String">topLevelVal</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelGetVal%3Akotlin.String">topLevelGetVal</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#%28p.Some%29.topLevelExtVal%3Akotlin.String">topLevelExtVal</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level constants</h2>
+    <div id="topLevelConst:kotlin.String">
+      <h3>topLevelConst</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">topLevelConst</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+      <pre>Value: <code>&quot;&quot;</code></pre>
+    </div>
+    <h2>Top-level functions</h2>
+    <div id="topLevelFun()">
+      <h3>topLevelFun</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">topLevelFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <div id="(p.Some).topLevelExtFun()">
+      <h3>topLevelExtFun</h3>
+      <pre><span class="keyword">fun </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <h2>Top-level properties</h2>
+    <div id="topLevelVal:kotlin.String">
+      <h3>topLevelVal</h3>
+      <pre><span class="keyword">val </span><span class="identifier">topLevelVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+    <div id="topLevelGetVal:kotlin.String">
+      <h3>topLevelGetVal</h3>
+      <pre><span class="keyword">val </span><span class="identifier">topLevelGetVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+    <div id="(p.Some).topLevelExtVal:kotlin.String">
+      <h3>topLevelExtVal</h3>
+      <pre><span class="keyword">val </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.html b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
new file mode 100644
index 0000000..eca242c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
@@ -0,0 +1,63 @@
+<!-- File: /test/p/MyException.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>MyException</h1>
+    <pre><span class="keyword">class </span><span class="identifier">MyException</span>&nbsp;<span class="symbol">:</span>&nbsp;<span class="identifier">Exception</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.MyException</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="#">p.java.lang.Exception</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">Unit</span></td>
+                  <td>
+                    <div><code><span class="identifier">Exception</span>.<a href="package-summary.html#%28kotlin.Exception%29.ext%28%29">ext</a>()</code></div>
+                    <p>Some docs...</p>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">MyException</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.kt b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
new file mode 100644
index 0000000..0831dac
--- /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 0000000..73025fc
--- /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 0000000..319c6d8
--- /dev/null
+++ b/core/testdata/format/javaCodeInParam.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C<T : Any>`
+
+### Parameters
+
+`T` - this is `some code` and other text
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
diff --git a/core/testdata/format/javaCodeLiteralTags.java b/core/testdata/format/javaCodeLiteralTags.java
new file mode 100644
index 0000000..e71ddaa
--- /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 0000000..b36be04
--- /dev/null
+++ b/core/testdata/format/javaCodeLiteralTags.md
@@ -0,0 +1,16 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+`A<B>C`
+
+
+
+A&lt;B&gt;C
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>`A<B>C` <br>A&lt;B&gt;C |
+
diff --git a/core/testdata/format/javaDeprecated.html b/core/testdata/format/javaDeprecated.html
new file mode 100644
index 0000000..6d45fc9
--- /dev/null
+++ b/core/testdata/format/javaDeprecated.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo.foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="index.html">Foo</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="Foo$foo()"></a>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><s><span class="identifier">foo</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<strong>Deprecated:</strong> use <code><a href="bar.html">bar</a></code> instead
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaDeprecated.java b/core/testdata/format/javaDeprecated.java
new file mode 100644
index 0000000..9694a44
--- /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 0000000..9a0baaa
--- /dev/null
+++ b/core/testdata/format/javaLinkTag.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Call <code><a href="bar.html">bar()</a></code> to do the job.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaLinkTag.java b/core/testdata/format/javaLinkTag.java
new file mode 100644
index 0000000..84f761c
--- /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 0000000..51917f7
--- /dev/null
+++ b/core/testdata/format/javaLinkTagWithLabel.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">protected</span> <span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Call <code><a href="bar.html">this wonderful method</a></code> to do the job.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaLinkTagWithLabel.java b/core/testdata/format/javaLinkTagWithLabel.java
new file mode 100644
index 0000000..1db5ad7
--- /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 0000000..b9fb32a
--- /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 0000000..fa17577
--- /dev/null
+++ b/core/testdata/format/javaSample.md
@@ -0,0 +1,41 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+```
+public abstract class Test {
+    /**
+     * Summary for Function
+     * @param name is String parameter
+     * @param value is int parameter
+     */
+    protected final void fn(String name, int value) {
+
+    }
+
+    protected void openFn() {}
+}
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>
+
+```
+public abstract class Test {
+    /**
+     * Summary for Function
+     * @param name is String parameter
+     * @param value is int parameter
+     */
+    protected final void fn(String name, int value) {
+
+    }
+
+    protected void openFn() {}
+}
+<br>```
+<br> |
+
diff --git a/core/testdata/format/javaSeeTag.html b/core/testdata/format/javaSeeTag.html
new file mode 100644
index 0000000..f8866dc
--- /dev/null
+++ b/core/testdata/format/javaSeeTag.html
@@ -0,0 +1,38 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Foo - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Foo</a><br/>
+<br/>
+<h1>Foo</h1>
+<code><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">Foo</span></code>
+<p><strong>See Also</strong><br/>
+<a href="bar.html">#bar</a></p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="bar.html">bar</a></p>
+</td>
+<td>
+<code><span class="keyword">open</span> <span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/javaSeeTag.java b/core/testdata/format/javaSeeTag.java
new file mode 100644
index 0000000..94a2460
--- /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 0000000..f980ae0
--- /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 0000000..1d2251d
--- /dev/null
+++ b/core/testdata/format/javaSpaceInAuthor.md
@@ -0,0 +1,13 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`protected open class C`
+
+**Author**
+Dmitry Jemerov
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
diff --git a/core/testdata/format/javadocCodeMultiline.java b/core/testdata/format/javadocCodeMultiline.java
new file mode 100644
index 0000000..a8b8de6
--- /dev/null
+++ b/core/testdata/format/javadocCodeMultiline.java
@@ -0,0 +1,10 @@
+/**
+ * <pre>
+ * &lt;Button
+ *     android:id="@+id/button_id"
+ *     android:layout_height="wrap_content"
+ *     android:layout_width="wrap_content"
+ *     android:text="@string/self_destruct" /&gt;</pre>
+ */
+public class C {
+}
diff --git a/core/testdata/format/javadocCodeMultiline.md b/core/testdata/format/javadocCodeMultiline.md
new file mode 100644
index 0000000..35dd1ac
--- /dev/null
+++ b/core/testdata/format/javadocCodeMultiline.md
@@ -0,0 +1,26 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`open class C`
+
+```
+<Button
+      android:id="@+id/button_id"
+      android:layout_height="wrap_content"
+      android:layout_width="wrap_content"
+      android:text="@string/self_destruct" />
+```
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>
+
+```
+<Button
+      android:id="@+id/button_id"
+      android:layout_height="wrap_content"
+      android:layout_width="wrap_content"
+      android:text="@string/self_destruct" /><br>```
+<br> |
+
diff --git a/core/testdata/format/javadocHtml.java b/core/testdata/format/javadocHtml.java
new file mode 100644
index 0000000..622116b
--- /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 0000000..a3c1baf
--- /dev/null
+++ b/core/testdata/format/javadocHtml.md
@@ -0,0 +1,28 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`open class C`
+
+**Bold** **Strong** *Italic* *Emphasized*
+
+Paragraph
+
+ ~~Strikethrough~~ ~~Deleted~~ `Code`
+
+```
+Block code
+```
+
+ * List Item
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>**Bold** **Strong** *Italic* *Emphasized* <br>Paragraph ~~Strikethrough~~ ~~Deleted~~ `Code`
+
+```
+Block code<br>```
+<br>
+* List Item
+<br> |
+
diff --git a/core/testdata/format/javadocOrderedList.java b/core/testdata/format/javadocOrderedList.java
new file mode 100644
index 0000000..c32d903
--- /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 0000000..88d5f5b
--- /dev/null
+++ b/core/testdata/format/javadocOrderedList.md
@@ -0,0 +1,17 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`open class Bar`
+
+ 1. Rinse
+ 2. Repeat
+
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>
+1. Rinse
+ 2. Repeat
+ <br> |
+
diff --git a/core/testdata/format/jdkLinks.kt b/core/testdata/format/jdkLinks.kt
new file mode 100644
index 0000000..660fb4c
--- /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 0000000..7498171
--- /dev/null
+++ b/core/testdata/format/jdkLinks.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C : `[`ClassLoader`](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html)
+
+This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String))
+
+You can print something to [java.lang.System.out](http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#out) now!
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()`<br>This is a [ClassLoader](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html) and I can get its [ClassLoader.getResource](http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)) |
+
diff --git a/core/testdata/format/linkWithLabel.html b/core/testdata/format/linkWithLabel.html
new file mode 100644
index 0000000..59bc6dd
--- /dev/null
+++ b/core/testdata/format/linkWithLabel.html
@@ -0,0 +1,39 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Use <a href="foo.html">this method</a> for best results.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/linkWithLabel.kt b/core/testdata/format/linkWithLabel.kt
new file mode 100644
index 0000000..4a85c50
--- /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 0000000..e1b6e09
--- /dev/null
+++ b/core/testdata/format/linkWithStarProjection.html
@@ -0,0 +1,24 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>KClassLoader - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">KClassLoader</a><br/>
+<br/>
+<h1>KClassLoader</h1>
+<code><span class="keyword">object </span><span class="identifier">KClassLoader</span></code>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="foo.html">foo</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="KClassLoader$foo(kotlin.Enum(()))/c">c</span><span class="symbol">:</span>&nbsp;<span class="identifier">Enum</span><span class="symbol">&lt;</span><span class="identifier">*</span><span class="symbol">&gt;</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/linkWithStarProjection.kt b/core/testdata/format/linkWithStarProjection.kt
new file mode 100644
index 0000000..6da6c59
--- /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 0000000..3e2017d
--- /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 0000000..d0ae70c
--- /dev/null
+++ b/core/testdata/format/linksInEmphasis.md
@@ -0,0 +1,23 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+An emphasised class.
+
+*This class [Bar](./index.md) is awesome.*
+
+*Even more awesomer is the function [Bar.foo](foo.md)*
+
+*[Bar.hello](hello.md) is also OK*
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>An emphasised class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+
diff --git a/core/testdata/format/linksInHeaders.kt b/core/testdata/format/linksInHeaders.kt
new file mode 100644
index 0000000..18efd34
--- /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 0000000..1dc7d18
--- /dev/null
+++ b/core/testdata/format/linksInHeaders.md
@@ -0,0 +1,34 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Some class with really useless documentation.
+
+# Beer o'clock - time to go to the [Bar](./index.md)
+
+## But **is [it](isitbeeroclock.com)** really?
+
+### [Bar.hello](hello.md) to the [Bar.world](world.md)!
+
+#### *Kotlin is amazing, [Bar.none](none.md)*
+
+##### We need to go [Bar.deeper](deeper.md)
+
+###### End of the [Bar.line](line.md) - we need to go back!
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Some class with really useless documentation. |
+
+### Functions
+
+| [deeper](deeper.md) | `fun deeper(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+| [kotlin](kotlin.md) | `fun kotlin(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
+| [none](none.md) | `fun none(): Unit` |
+| [world](world.md) | `fun world(): Unit` |
+
diff --git a/core/testdata/format/linksInStrong.kt b/core/testdata/format/linksInStrong.kt
new file mode 100644
index 0000000..b9e295e
--- /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 0000000..5b44112
--- /dev/null
+++ b/core/testdata/format/linksInStrong.md
@@ -0,0 +1,23 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+A strong class.
+
+**This class [Bar](./index.md) is awesome.**
+
+**Even more awesomer is the function [Bar.foo](foo.md)**
+
+**[Bar.hello](hello.md) is also OK**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>A strong class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Unit` |
+| [hello](hello.md) | `fun hello(): Unit` |
+
diff --git a/core/testdata/format/markdownInLinks.html b/core/testdata/format/markdownInLinks.html
new file mode 100644
index 0000000..596cca7
--- /dev/null
+++ b/core/testdata/format/markdownInLinks.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p><a href="http://www.ibm.com">a<strong>b</strong><strong>d</strong> kas </a></p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/markdownInLinks.kt b/core/testdata/format/markdownInLinks.kt
new file mode 100644
index 0000000..67b6311
--- /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 0000000..955794d
--- /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 0000000..0ec1fda
--- /dev/null
+++ b/core/testdata/format/memberExtension.md
@@ -0,0 +1,10 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo : `[`X`](../-x/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt
new file mode 100644
index 0000000..d7fbf92
--- /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 0000000..57f3674
--- /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 0000000..37e943a
--- /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 0000000..045f3f0
--- /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 0000000..57f3674
--- /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 0000000..74d464c
--- /dev/null
+++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md
@@ -0,0 +1,20 @@
+[test](../../index.md) / [pack](../index.md) / [Some](./index.md)
+
+# Some
+
+`typealias Some = SomeCoolJvmClass`
+
+**Platform and version requirements:** JVM
+
+`class Some`
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+| [&lt;init&gt;](-some/-init-.md) | `Some()` |
+
+### Functions
+
+| [magic](-some/magic.md) | `fun magic(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md
new file mode 100644
index 0000000..5708795
--- /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 0000000..fca2aff
--- /dev/null
+++ b/core/testdata/format/multiplatform/implied/foo.md
@@ -0,0 +1,24 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJs](prop-js.md)<br>(JS) | `val propJs: String` |
+| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` |
+
+### Functions
+
+| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| [js](js.md)<br>(JS) | `fun js(): Unit` |
+| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/implied/js.kt b/core/testdata/format/multiplatform/implied/js.kt
new file mode 100644
index 0000000..dd2de5b
--- /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 0000000..8d73ce2
--- /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 0000000..bbf1dd7
--- /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 0000000..cb77273
--- /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 0000000..ea78b5a
--- /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 0000000..7f41b7d
--- /dev/null
+++ b/core/testdata/format/multiplatform/mergeMembers/foo.md
@@ -0,0 +1,26 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM, JS
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJs](prop-js.md)<br>(JS) | `val propJs: String` |
+| [propJvm](prop-jvm.md)<br>(JVM) | `val propJvm: String` |
+| [propJvmAndJs](prop-jvm-and-js.md) | `val propJvmAndJs: Int` |
+
+### Functions
+
+| [bothJvmAndJs](both-jvm-and-js.md) | `fun bothJvmAndJs(): Unit` |
+| [js](js.md)<br>(JS) | `fun js(): Unit` |
+| [jvm](jvm.md)<br>(JVM) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/mergeMembers/js.kt b/core/testdata/format/multiplatform/mergeMembers/js.kt
new file mode 100644
index 0000000..dd2de5b
--- /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 0000000..8d73ce2
--- /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 0000000..a20b14c
--- /dev/null
+++ b/core/testdata/format/multiplatform/omitRedundant/foo.md
@@ -0,0 +1,22 @@
+[test](../../index.md) / [foo](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo`
+
+**Platform and version requirements:** JVM
+
+This is a foo.
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()`<br>This is a foo. |
+
+### Properties
+
+| [propJvm](prop-jvm.md) | `val propJvm: String` |
+
+### Functions
+
+| [jvm](jvm.md) | `fun jvm(): Unit` |
+
diff --git a/core/testdata/format/multiplatform/omitRedundant/js.kt b/core/testdata/format/multiplatform/omitRedundant/js.kt
new file mode 100644
index 0000000..d1b1429
--- /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 0000000..35e3c08
--- /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 0000000..86d0928
--- /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 0000000..86d0928
--- /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 0000000..6f45342
--- /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 0000000..4ddfe2e
--- /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 0000000..27ab1b3
--- /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 0000000..f4186b6
--- /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 0000000..ff480b5
--- /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 0000000..e6d66ff
--- /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 0000000..cb77273
--- /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 0000000..fad7e90
--- /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 0000000..9d085c3
--- /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 0000000..78586ac
--- /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 0000000..83217f8
--- /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 0000000..fb25bc3
--- /dev/null
+++ b/core/testdata/format/nestedLists.md
@@ -0,0 +1,43 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Usage instructions:
+
+* **Rinse**
+  1. Alter any rinse options *(optional)*
+       * Recommended to [Bar.useSoap](use-soap.md)
+       * Optionally apply [Bar.elbowGrease](#) for best results
+  2. [Bar.rinse](rinse.md) to begin rinse
+       1. Thus you should call [Bar.rinse](rinse.md)
+       2. *Then* call [Bar.repeat](repeat.md)
+           * Don't forget to use:
+              * Soap
+              * Elbow Grease
+       3. Finally, adjust soap usage [Bar.useSoap](use-soap.md) as needed
+  3. Repeat with [Bar.repeat](repeat.md)
+
+* **Repeat**
+  * Will use previously used rinse options
+  * [Bar.rinse](rinse.md) must have been called once before
+  * Can be repeated any number of times
+  * Options include:
+      * [Bar.useSoap](use-soap.md)
+      * [Bar.useElbowGrease](use-elbow-grease.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage instructions: |
+
+### Properties
+
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
+
+### Functions
+
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
+
diff --git a/core/testdata/format/newlineInTableCell.kt b/core/testdata/format/newlineInTableCell.kt
new file mode 100644
index 0000000..3e0616f
--- /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 0000000..53716db
--- /dev/null
+++ b/core/testdata/format/newlineInTableCell.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Types
+
+| [A](-a/index.md) | `class A`<br>There is `long long int` story full of new lines |
+
diff --git a/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt b/core/testdata/format/notPublishedTypeAliasAutoExpansion.kt
new file mode 100644
index 0000000..1f29e11
--- /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 0000000..ca95093
--- /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 0000000..d1d4545
--- /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 0000000..7b81c25
--- /dev/null
+++ b/core/testdata/format/nullability.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): Comparable<`[`T`](index.md#T)`>?` |
+
diff --git a/core/testdata/format/operatorOverloading.kt b/core/testdata/format/operatorOverloading.kt
new file mode 100644
index 0000000..6fe78e4
--- /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 0000000..0a4c87b
--- /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 0000000..6f735bf
--- /dev/null
+++ b/core/testdata/format/orderedList.html
@@ -0,0 +1,30 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span></code>
+<p>Usage instructions:</p>
+<ol><li>Rinse</li>
+<li>Repeat</li>
+</ol>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Usage instructions:</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/orderedList.kt b/core/testdata/format/orderedList.kt
new file mode 100644
index 0000000..03681c7
--- /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 0000000..feda82e
--- /dev/null
+++ b/core/testdata/format/overloads.html
@@ -0,0 +1,26 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>root package - test</title>
+</HEAD>
+<BODY>
+<a href="./index.html">test</a><br/>
+<br/>
+<h2>Package &lt;root&gt;</h2>
+<h3>Functions</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="f.html">f</a></p>
+</td>
+<td>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloads.kt b/core/testdata/format/overloads.kt
new file mode 100644
index 0000000..dcd2d09
--- /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 0000000..16b03f7
--- /dev/null
+++ b/core/testdata/format/overloadsWithDescription.html
@@ -0,0 +1,20 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f(kotlin.Int)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><br/>
+<a name="$f(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on <a href="f.html#$f(kotlin.Int)/x">x</a>.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the value to perform the action on.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloadsWithDescription.kt b/core/testdata/format/overloadsWithDescription.kt
new file mode 100644
index 0000000..740e642
--- /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 0000000..4c4f7f7
--- /dev/null
+++ b/core/testdata/format/overloadsWithDifferentDescriptions.html
@@ -0,0 +1,25 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f(kotlin.Int)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.Int)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the int value to perform the action on.</p>
+<a name="$f(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="identifier" id="$f(kotlin.String)/x">x</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Performs an action on x.</p>
+<p>This is a long description.</p>
+<h3>Parameters</h3>
+<p><a name="x"></a>
+<code>x</code> - the string value to perform the action on.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/overloadsWithDifferentDescriptions.kt b/core/testdata/format/overloadsWithDifferentDescriptions.kt
new file mode 100644
index 0000000..ad3169b
--- /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 0000000..d732948
--- /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 0000000..d0ec82f
--- /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 0000000..47e471f
--- /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 0000000..7cc33d2
--- /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 0000000..a4ae099
--- /dev/null
+++ b/core/testdata/format/parameterAnchor.html
@@ -0,0 +1,17 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>processFiles - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./process-files.html">processFiles</a><br/>
+<br/>
+<h1>processFiles</h1>
+<a name="$processFiles(kotlin.Function0((processFiles.T)))"></a>
+<code><span class="keyword">fun </span><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span> <span class="identifier">processFiles</span><span class="symbol">(</span><span class="identifier" id="$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">List</span><span class="symbol">&lt;</span><a href="process-files.html#T"><span class="identifier">T</span></a><span class="symbol">&gt;</span></code>
+<p>Runs <a href="process-files.html#$processFiles(kotlin.Function0((processFiles.T)))/processor">processor</a> for each file and collects its results into single list</p>
+<h3>Parameters</h3>
+<p><a name="processor"></a>
+<code>processor</code> - function to receive context for symbol resolution and file for processing</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/parameterAnchor.kt b/core/testdata/format/parameterAnchor.kt
new file mode 100644
index 0000000..ae36ee4
--- /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 0000000..c63154c
--- /dev/null
+++ b/core/testdata/format/parenthesis.html
@@ -0,0 +1,14 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>foo (bar)</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/parenthesis.kt b/core/testdata/format/parenthesis.kt
new file mode 100644
index 0000000..b906f64
--- /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 0000000..88be1a7
--- /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 0000000..887d25a
--- /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 0000000..be82a99
--- /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 0000000..92fa8f7
--- /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 0000000..2b5f6f1
--- /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 0000000..978dc0f
--- /dev/null
+++ b/core/testdata/format/receiverParameterTypeBound.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`open class Foo`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
+### Extension Functions
+
+| [xyzzy](../xyzzy.md) | `fun <T : `[`Foo`](./index.md)`> `[`T`](../xyzzy.md#T)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/receiverReference.kt b/core/testdata/format/receiverReference.kt
new file mode 100644
index 0000000..3e6e205
--- /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 0000000..bdcce32
--- /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 0000000..c6550f0
--- /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 0000000..ee910cb
--- /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 0000000..00fa1dc
--- /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 0000000..40dbed7
--- /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 0000000..84f78df
--- /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 0000000..4b5f3a6
--- /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 0000000..fe1d031
--- /dev/null
+++ b/core/testdata/format/returnWithLink.html
@@ -0,0 +1,15 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo(kotlin.String)"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="identifier" id="$foo(kotlin.String)/s1">s1</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code>
+<p><strong>Return</strong><br/>
+Returns <a href="foo.html#$foo(kotlin.String)/s1">s1</a> and does nothing else.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/returnWithLink.kt b/core/testdata/format/returnWithLink.kt
new file mode 100644
index 0000000..63d1770
--- /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 0000000..2c0af09
--- /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 0000000..7093179
--- /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 0000000..2e03310
--- /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 0000000..7093179
--- /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 0000000..7483250
--- /dev/null
+++ b/core/testdata/format/see.html
@@ -0,0 +1,46 @@
+<!-- File: test/bar.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>bar - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./bar.html">bar</a><br/>
+<br/>
+<h1>bar</h1>
+<a name="$bar()"></a>
+<code><span class="keyword">fun </span><span class="identifier">bar</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+</BODY>
+</HTML>
+<!-- File: test/foo.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+</BODY>
+</HTML>
+<!-- File: test/quux.html -->
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>quux - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./quux.html">quux</a><br/>
+<br/>
+<h1>quux</h1>
+<a name="$quux()"></a>
+<code><span class="keyword">fun </span><span class="identifier">quux</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p><strong>See Also</strong><br/>
+<p><a href="foo.html">foo</a></p>
+<p><a href="bar.html">bar</a></p>
+</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/see.kt b/core/testdata/format/see.kt
new file mode 100644
index 0000000..a0b153b
--- /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 0000000..64df1ec
--- /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 0000000..f900ecb
--- /dev/null
+++ b/core/testdata/format/shadowedExtensionFunctions.md
@@ -0,0 +1,15 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar : `[`Foo`](../-foo/index.md)
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()` |
+
+### Extension Functions
+
+| [shazam](../shazam.md) | `fun `[`Bar`](./index.md)`.shazam(i: Int): Unit`<br>`fun `[`Foo`](../-foo/index.md)`.shazam(): Unit` |
+| [xyzzy](../xyzzy.md) | `fun `[`Bar`](./index.md)`.xyzzy(): Unit` |
+
diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html
new file mode 100644
index 0000000..32988de
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.html
@@ -0,0 +1,28 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Since1.1 - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Since1.1</a><br/>
+<br/>
+<h1>Since1.1</h1>
+<code><span class="keyword">class </span><span class="identifier">Since1.1</span></code>
+<p><strong>Platform and version requirements:</strong> Kotlin 1.1</p>
+<p>Useful</p>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code>
+<p>Useful</p>
+</td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/sinceKotlin.kt b/core/testdata/format/sinceKotlin.kt
new file mode 100644
index 0000000..1025cf0
--- /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 0000000..df96db0
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Since1.1](./index.md)
+
+# Since1.1
+
+`class Since1.1`
+
+**Platform and version requirements:** Kotlin 1.1
+
+Useful
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Since1.1()`<br>Useful |
+
diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md
new file mode 100644
index 0000000..eabf88d
--- /dev/null
+++ b/core/testdata/format/sinceKotlin.package.md
@@ -0,0 +1,10 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+**Platform and version requirements:** Kotlin 1.1
+
+### Types
+
+| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+
diff --git a/core/testdata/format/sinceKotlinWide.kt b/core/testdata/format/sinceKotlinWide.kt
new file mode 100644
index 0000000..fa1eb7d
--- /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 0000000..58a5045
--- /dev/null
+++ b/core/testdata/format/sinceKotlinWide.package.md
@@ -0,0 +1,11 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+**Platform and version requirements:** Kotlin 1.1+
+
+### Types
+
+| [Since1.1](-since1.1/index.md)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful |
+| [Since1.2](-since1.2/index.md)<br>(Kotlin 1.2) | `class Since1.2`<br>Useful also |
+
diff --git a/core/testdata/format/starProjection.kt b/core/testdata/format/starProjection.kt
new file mode 100644
index 0000000..48d53e4
--- /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 0000000..5a53e5b
--- /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 0000000..1d875a5
--- /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 0000000..4f49416
--- /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 0000000..fbbdd32
--- /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 0000000..507ad6a
--- /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 0000000..ea3f56f
--- /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 0000000..ab11614
--- /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 0000000..92bd7ee
--- /dev/null
+++ b/core/testdata/format/suspendParam.package.md
@@ -0,0 +1,8 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Functions
+
+| [takesSuspendParam](takes-suspend-param.md) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` |
+
diff --git a/core/testdata/format/throwsTag.kt b/core/testdata/format/throwsTag.kt
new file mode 100644
index 0000000..29a9c3f
--- /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 0000000..70fba51
--- /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 0000000..39362b3
--- /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 0000000..a68861d
--- /dev/null
+++ b/core/testdata/format/tokensInEmphasis.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Another emphasised class.
+
+*This class, [Bar](./index.md) is just meh.*
+
+*For a semicolon; [Bar.foo](foo.md) is for you!.*
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Another emphasised class. |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): String` |
+
diff --git a/core/testdata/format/tokensInHeaders.kt b/core/testdata/format/tokensInHeaders.kt
new file mode 100644
index 0000000..df62b02
--- /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 0000000..bd25492
--- /dev/null
+++ b/core/testdata/format/tokensInHeaders.md
@@ -0,0 +1,37 @@
+[test](../index.md) / [The](./index.md)
+
+# The
+
+`class The`
+
+Why did the token cross the road?
+
+# Because it was Beer o'clock @ [The.bar](bar.md)
+
+## But **waz *\[sic\]* [it](isitbeeroclock.com)** really?
+
+### [The.bar](bar.md) has? [The.foo](foo.md)est drinks ever!
+
+#### *[The.kotlinz](kotlinz.md) is [The.bestests](bestests.md), [Bar.none](-bar/none.md)*
+
+##### So many lame code "puns" (in) [The.house](house.md)
+
+###### End of the?? [Bar.line](#)! - we need to go back!
+
+### Types
+
+| [Bar](-bar/index.md) | `object Bar` |
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `The()`<br>Why did the token cross the road? |
+
+### Functions
+
+| [bar](bar.md) | `fun bar(): Unit` |
+| [bestests](bestests.md) | `fun bestests(): Unit` |
+| [foo](foo.md) | `fun foo(): Unit` |
+| [house](house.md) | `fun house(): Unit` |
+| [kotlinz](kotlinz.md) | `fun kotlinz(): Unit` |
+| [line](line.md) | `fun line(): Unit` |
+
diff --git a/core/testdata/format/tokensInStrong.kt b/core/testdata/format/tokensInStrong.kt
new file mode 100644
index 0000000..596a9ae
--- /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 0000000..2781656
--- /dev/null
+++ b/core/testdata/format/tokensInStrong.md
@@ -0,0 +1,20 @@
+[test](../index.md) / [Yasc](./index.md)
+
+# Yasc
+
+`class Yasc`
+
+**YASC: [Yasc](./index.md) Yet Another Strong Class**
+
+**This class, [Yasc](./index.md) *is* just meh.**
+
+**For a semicolon; [Yasc.foo](foo.md) is for you!.**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Yasc()`<br>**YASC: [Yasc](./index.md) Yet Another Strong Class** |
+
+### Functions
+
+| [foo](foo.md) | `fun foo(): String` |
+
diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html
new file mode 100644
index 0000000..dacd056
--- /dev/null
+++ b/core/testdata/format/tripleBackticks.html
@@ -0,0 +1,16 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>f - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./f.html">f</a><br/>
+<br/>
+<h1>f</h1>
+<a name="$f()"></a>
+<code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>Description</p>
+<pre><code>code sample
+</code></pre>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/tripleBackticks.kt b/core/testdata/format/tripleBackticks.kt
new file mode 100644
index 0000000..54dfa6d
--- /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 0000000..9657963
--- /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 0000000..218c484
--- /dev/null
+++ b/core/testdata/format/typeAliases.md
@@ -0,0 +1,104 @@
+<!-- File: test/-a/index.md -->
+[test](../index.md) / [A](./index.md)
+
+# A
+
+`class A`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `A()` |
+
+<!-- File: test/-b/index.md -->
+[test](../index.md) / [B](./index.md)
+
+# B
+
+`class B`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `B()` |
+
+<!-- File: test/-c/index.md -->
+[test](../index.md) / [C](./index.md)
+
+# C
+
+`class C<T>`
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `C()` |
+
+<!-- File: test/-d.md -->
+[test](index.md) / [D](./-d.md)
+
+# D
+
+`typealias D = `[`A`](-a/index.md)
+<!-- File: test/-e.md -->
+[test](index.md) / [E](./-e.md)
+
+# E
+
+`typealias E = `[`D`](-d.md)
+<!-- File: test/-f.md -->
+[test](index.md) / [F](./-f.md)
+
+# F
+
+`typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md)
+<!-- File: test/-g.md -->
+[test](index.md) / [G](./-g.md)
+
+# G
+
+`typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-h.md -->
+[test](index.md) / [H](./-h.md)
+
+# H
+
+`typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>`
+<!-- File: test/-i.md -->
+[test](index.md) / [I](./-i.md)
+
+# I
+
+`typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>`
+<!-- File: test/-j.md -->
+[test](index.md) / [J](./-j.md)
+
+# J
+
+`typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>`
+<!-- File: test/-k.md -->
+[test](index.md) / [K](./-k.md)
+
+# K
+
+`typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>`
+<!-- File: test/-l.md -->
+[test](index.md) / [L](./-l.md)
+
+# L
+
+`typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md)
+<!-- File: test/-m.md -->
+[test](index.md) / [M](./-m.md)
+
+# M
+
+`typealias M = `[`A`](-a/index.md)
+
+Documented
+
+<!-- File: test/-n.md -->
+[test](index.md) / [N](./-n.md)
+
+# N
+
+`typealias ~~N~~ = `[`A`](-a/index.md)
+**Deprecated:** !!!
+
diff --git a/core/testdata/format/typeAliases.package.md b/core/testdata/format/typeAliases.package.md
new file mode 100644
index 0000000..199e91c
--- /dev/null
+++ b/core/testdata/format/typeAliases.package.md
@@ -0,0 +1,24 @@
+[test](./index.md)
+
+## Package &lt;root&gt;
+
+### Types
+
+| [A](-a/index.md) | `class A` |
+| [B](-b/index.md) | `class B` |
+| [C](-c/index.md) | `class C<T>` |
+
+### Type Aliases
+
+| [D](-d.md) | `typealias D = `[`A`](-a/index.md) |
+| [E](-e.md) | `typealias E = `[`D`](-d.md) |
+| [F](-f.md) | `typealias F = (`[`A`](-a/index.md)`) -> `[`B`](-b/index.md) |
+| [G](-g.md) | `typealias G = `[`C`](-c/index.md)`<`[`A`](-a/index.md)`>` |
+| [H](-h.md) | `typealias H<T> = `[`C`](-c/index.md)`<`[`T`](-h.md#T)`>` |
+| [I](-i.md) | `typealias I<T> = `[`H`](-h.md)`<`[`T`](-i.md#T)`>` |
+| [J](-j.md) | `typealias J = `[`H`](-h.md)`<`[`A`](-a/index.md)`>` |
+| [K](-k.md) | `typealias K = `[`H`](-h.md)`<`[`J`](-j.md)`>` |
+| [L](-l.md) | `typealias L = (`[`K`](-k.md)`, `[`B`](-b/index.md)`) -> `[`J`](-j.md) |
+| [M](-m.md) | `typealias M = `[`A`](-a/index.md)<br>Documented |
+| [N](-n.md) | `typealias ~~N~~ = `[`A`](-a/index.md) |
+
diff --git a/core/testdata/format/typeLink.html b/core/testdata/format/typeLink.html
new file mode 100644
index 0000000..30af8a9
--- /dev/null
+++ b/core/testdata/format/typeLink.html
@@ -0,0 +1,24 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>Bar - test</title>
+</HEAD>
+<BODY>
+<a href="../index.html">test</a>&nbsp;/&nbsp;<a href="./index.html">Bar</a><br/>
+<br/>
+<h1>Bar</h1>
+<code><span class="keyword">class </span><span class="identifier">Bar</span>&nbsp;<span class="symbol">:</span>&nbsp;<a href="../-foo/index.html"><span class="identifier">Foo</span></a></code>
+<h3>Constructors</h3>
+<table>
+<tbody>
+<tr>
+<td>
+<p><a href="-init-.html">&lt;init&gt;</a></p>
+</td>
+<td>
+<code><span class="identifier">Bar</span><span class="symbol">(</span><span class="symbol">)</span></code></td>
+</tr>
+</tbody>
+</table>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/typeLink.kt b/core/testdata/format/typeLink.kt
new file mode 100644
index 0000000..966e020
--- /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 0000000..8604e3b
--- /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 0000000..cf03b3a
--- /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 0000000..f196112
--- /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 0000000..5001d32
--- /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 0000000..d45e7eb
--- /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 0000000..b0615d4
--- /dev/null
+++ b/core/testdata/format/typeParameterVariance.md
@@ -0,0 +1,14 @@
+[test](../index.md) / [Foo](./index.md)
+
+# Foo
+
+`class Foo<out T>`
+
+### Parameters
+
+`T` - the class parameter type
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Foo()` |
+
diff --git a/core/testdata/format/typeProjectionVariance.kt b/core/testdata/format/typeProjectionVariance.kt
new file mode 100644
index 0000000..85ee344
--- /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 0000000..d3a55c5
--- /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 0000000..dd338f7
--- /dev/null
+++ b/core/testdata/format/uninterpretedEmphasisCharacters.html
@@ -0,0 +1,15 @@
+<HTML>
+<HEAD>
+<meta charset="UTF-8">
+<title>foo - test</title>
+</HEAD>
+<BODY>
+<a href="index.html">test</a>&nbsp;/&nbsp;<a href="./foo.html">foo</a><br/>
+<br/>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code>
+<p>This is <em>emphasized text</em> but text_with_underscores has to preserve the underscores.
+Single stars embedded in a word like Embedded*Star have to be preserved as well.</p>
+</BODY>
+</HTML>
diff --git a/core/testdata/format/uninterpretedEmphasisCharacters.kt b/core/testdata/format/uninterpretedEmphasisCharacters.kt
new file mode 100644
index 0000000..711bb56
--- /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 0000000..a594b89
--- /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 0000000..52ad9a7
--- /dev/null
+++ b/core/testdata/format/unorderedLists.md
@@ -0,0 +1,47 @@
+[test](../index.md) / [Bar](./index.md)
+
+# Bar
+
+`class Bar`
+
+Usage summary:
+
+* Rinse
+* Repeat
+
+Usage instructions:
+
+* [Bar.rinse](rinse.md) to rinse
+* Alter any rinse options *(optional)*
+* To repeat; [Bar.repeat](repeat.md)
+  * Can reconfigure options:
+      * Soap
+      * Elbow Grease
+      * Bleach
+
+Rinse options:
+
+* [Bar.useSoap](use-soap.md)
+  * *recommended*
+
+* [Bar.useElbowGrease](use-elbow-grease.md)
+  * *warning: requires effort*
+
+* [Bar.useBleach](use-bleach.md)
+  * **use with caution**
+
+### Constructors
+
+| [&lt;init&gt;](-init-.md) | `Bar()`<br>Usage summary: |
+
+### Properties
+
+| [useBleach](use-bleach.md) | `var useBleach: Boolean` |
+| [useElbowGrease](use-elbow-grease.md) | `var useElbowGrease: Boolean` |
+| [useSoap](use-soap.md) | `var useSoap: Boolean` |
+
+### Functions
+
+| [repeat](repeat.md) | `fun repeat(): Unit` |
+| [rinse](rinse.md) | `fun rinse(): Unit` |
+
diff --git a/core/testdata/format/varargsFunction.kt b/core/testdata/format/varargsFunction.kt
new file mode 100644
index 0000000..deea127
--- /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 0000000..550202c
--- /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 0000000..d21b8d7
--- /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 0000000..b22d708
--- /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 0000000..02d0422
--- /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 0000000..35453ab
--- /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 0000000..e69de29
--- /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 0000000..045f3f0
--- /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 0000000..57f3674
--- /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 0000000..3d34fc7
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
@@ -0,0 +1,38 @@
+<div class='api-docs-breadcrumbs'><a href="../../index.html">test</a> / <a href="../index.html">pack</a> / <a href="./index.html">Some</a></div>
+<h1>Some</h1>
+<div class="overload-group" data-platform="JVM"><div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+<p><strong>Platform and version requirements:</strong> JVM</p>
+</div>
+<div class="overload-group" data-platform="JS"><div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+<p><strong>Platform and version requirements:</strong> JS</p>
+<h3>Constructors</h3>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<p><a href="-some/-init-.html">&lt;init&gt;</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+<h3>Functions</h3>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<p><a href="-some/magic.html">magic</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+</div>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
new file mode 100644
index 0000000..c8926a2
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
@@ -0,0 +1,36 @@
+<div class='api-docs-breadcrumbs'><a href="../index.html">test</a> / <a href="./index.html">pack</a></div>
+<h2>Package pack</h2>
+<h3>Types</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td>
+<p><a href="-some/index.html">Some</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td>
+<p><a href="-some-cool-jvm-class/index.html">SomeCoolJvmClass</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
+<h3>Type Aliases</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM"><td>
+<p><a href="-some/index.html">Some</a></p>
+
+</td>
+<td>
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website-html/dropImport.html b/core/testdata/format/website-html/dropImport.html
new file mode 100644
index 0000000..e0fcb12
--- /dev/null
+++ b/core/testdata/format/website-html/dropImport.html
@@ -0,0 +1,11 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import some.*
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/dropImport.kt b/core/testdata/format/website-html/dropImport.kt
new file mode 100644
index 0000000..7b8d9f4
--- /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 0000000..b5a0732
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInImportList.html
@@ -0,0 +1,12 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import same.*
+import some.*
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInImportList.kt b/core/testdata/format/website-html/newLinesInImportList.kt
new file mode 100644
index 0000000..836d9f6
--- /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 0000000..50f875d
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInSamples.html
@@ -0,0 +1,19 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+val words = listOf("a", "abc", "ab", "def", "abcd")
+val byLength = words.groupBy { it.length }
+
+println(byLength.keys) // [1, 3, 2, 4]
+println(byLength.values) // [[a], [abc, def], [ab], [abcd]]
+
+val mutableByLength: MutableMap&lt;Int, MutableList&lt;String&gt;&gt; = words.groupByTo(mutableMapOf()) { it.length }
+// same content as in byLength map, but the map is mutable
+println("mutableByLength == byLength is ${mutableByLength == byLength}") // true
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInSamples.kt b/core/testdata/format/website-html/newLinesInSamples.kt
new file mode 100644
index 0000000..ee49aef
--- /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 0000000..aaba9c9
--- /dev/null
+++ b/core/testdata/format/website-html/overloadGroup.html
@@ -0,0 +1,16 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./magic.html">magic</a></div>
+<h1>magic</h1>
+<div class="overload-group"><a name="$magic(kotlin.String)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<h3>Parameters</h3>
+<p><a name="spell"></a>
+<code>spell</code> - The text of spell, often distributed on scrolls</p>
+<p><strong>Return</strong> Spell ID for future casts</p>
+</div>
+<div class="overload-group"><a name="$magic(kotlin.Int)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<h3>Parameters</h3>
+<p><a name="spell"></a>
+<code>spell</code> - Spell ID of previously casted spell</p>
+<p><strong>Return</strong> Spell ID for future casts</p>
+</div>
diff --git a/core/testdata/format/website-html/overloadGroup.kt b/core/testdata/format/website-html/overloadGroup.kt
new file mode 100644
index 0000000..5bc98e3
--- /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 0000000..7724eaa
--- /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><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="index-of.html#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.</p>
+<h3>Parameters</h3>
+<p><a name="ignoreCase"></a>
+<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.</p>
+<p><strong>Returns</strong> An index of the first occurrence of <a href="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 0000000..669c14f
--- /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 0000000..1fb26e4
--- /dev/null
+++ b/core/testdata/format/website-html/sample.html
@@ -0,0 +1,19 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./foo.html">foo</a></div>
+<h1>foo</h1>
+<div class="overload-group"><a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.</p>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+if (true) {
+    println(property)
+}
+//sampleEnd
+}</code></pre></div>
+</div>
+<div class="overload-group"><a name="$foo(kotlin.Int)"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+</div>
diff --git a/core/testdata/format/website-html/sample.kt b/core/testdata/format/website-html/sample.kt
new file mode 100644
index 0000000..a664c2f
--- /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 0000000..e91232f
--- /dev/null
+++ b/core/testdata/format/website-html/sampleWithAsserts.html
@@ -0,0 +1,22 @@
+<div class='api-docs-breadcrumbs'><a href="index.html">test</a> / <a href="./a.html">a</a></div>
+<h1>a</h1>
+<a name="$a()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">a</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">String</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import java.io.FileNotFoundException
+import java.io.File
+
+fun main(args: Array&lt;String&gt;) {
+//sampleStart
+println(a()) // Hello, Work
+println("a() == b() is ${a() == b()}") // true
+// A eq B
+println("a() == b() is ${a() == b()}") // true
+// readSomeFile(File("some.txt")) // reading file now will fail
+// readSomeFile(File("some.txt")) // will fail with FileNotFoundException
+
+fun indented() {
+    // A neq B
+    println("a() != b() is ${a() != b()}") // false
+}
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/sampleWithAsserts.kt b/core/testdata/format/website-html/sampleWithAsserts.kt
new file mode 100644
index 0000000..b3bce11
--- /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 0000000..7b8d9f4
--- /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 0000000..1e05678
--- /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 0000000..836d9f6
--- /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 0000000..27d796f
--- /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 0000000..ee49aef
--- /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 0000000..5344b98
--- /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 0000000..a664c2f
--- /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 0000000..b29075a
--- /dev/null
+++ b/core/testdata/format/website-samples/sample.md
@@ -0,0 +1,39 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+
+<div class="sample" markdown="1">
+
+``` kotlin
+
+
+fun main(args: Array<String>) {
+//sampleStart
+if (true) {
+    println(property)
+}
+//sampleEnd
+}
+```
+
+</div>
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+</div>
diff --git a/core/testdata/format/website-samples/sampleWithAsserts.kt b/core/testdata/format/website-samples/sampleWithAsserts.kt
new file mode 100644
index 0000000..bb9732d
--- /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 0000000..c114468
--- /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 0000000..d21b8d7
--- /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 0000000..b22d708
--- /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 0000000..02d0422
--- /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 0000000..1c7fbf6
--- /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 0000000..e69de29
--- /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 0000000..045f3f0
--- /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 0000000..57f3674
--- /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 0000000..78f6adf
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md
@@ -0,0 +1,56 @@
+---
+title: pack.Some - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a> / <a href="test/pack/-some/index">Some</a></div>
+
+# Some
+
+<div class="overload-group" data-platform="JVM" markdown="1">
+
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+**Platform and version requirements:** JVM
+
+</div>
+
+<div class="overload-group" data-platform="JS" markdown="1">
+
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+**Platform and version requirements:** JS
+
+### Constructors
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/-init-">&lt;init&gt;</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+### Functions
+
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td markdown="1">
+<a href="test/pack/-some/-some/magic">magic</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr>
+</tbody>
+</table>
+
+</div>
diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
new file mode 100644
index 0000000..a6e7d63
--- /dev/null
+++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md
@@ -0,0 +1,43 @@
+---
+title: pack - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div>
+
+## Package pack
+
+### Types
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div>
+
+</td>
+</tr><tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
+
+### Type Aliases
+
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JVM"><td markdown="1">
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td markdown="1">
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website/overloadGroup.kt b/core/testdata/format/website/overloadGroup.kt
new file mode 100644
index 0000000..5bc98e3
--- /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 0000000..d81f86b
--- /dev/null
+++ b/core/testdata/format/website/overloadGroup.md
@@ -0,0 +1,34 @@
+---
+title: magic - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/magic">magic</a></div>
+
+# magic
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.String)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">String</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+### Parameters
+
+<code>spell</code> - The text of spell, often distributed on scrolls
+
+**Return**
+Spell ID for future casts
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="parameterName" id="$magic(kotlin.Int)/spell">spell</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+### Parameters
+
+<code>spell</code> - Spell ID of previously casted spell
+
+**Return**
+Spell ID for future casts
+
+</div>
diff --git a/core/testdata/format/website/returnTag.kt b/core/testdata/format/website/returnTag.kt
new file mode 100644
index 0000000..669c14f
--- /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 0000000..418baba
--- /dev/null
+++ b/core/testdata/format/website/returnTag.md
@@ -0,0 +1,20 @@
+---
+title: indexOf - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/index-of">indexOf</a></div>
+
+# indexOf
+
+<div class="signature"><code><span class="keyword">fun </span><a href="test/-foo/index"><span class="identifier">Foo</span></a><span class="symbol">.</span><span class="identifier">indexOf</span><span class="symbol">(</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</span><span class="symbol">:</span>&nbsp;<span class="identifier">Char</span><span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span>&nbsp;<span class="symbol">=</span>&nbsp;0<span class="symbol">, </span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="parameterName" id="$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/ignoreCase">ignoreCase</span><span class="symbol">:</span>&nbsp;<span class="identifier">Boolean</span>&nbsp;<span class="symbol">=</span>&nbsp;false<br/><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Returns the index within this string of the first occurrence of the specified character, starting from the specified <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/startIndex">startIndex</a>.
+
+### Parameters
+
+<code>ignoreCase</code> - <code>true</code> to ignore character case when matching a character. By default <code>false</code>.
+
+**Returns**
+An index of the first occurrence of <a href="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.
+
diff --git a/core/testdata/format/website/sample.kt b/core/testdata/format/website/sample.kt
new file mode 100644
index 0000000..a664c2f
--- /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 0000000..7c11790
--- /dev/null
+++ b/core/testdata/format/website/sample.md
@@ -0,0 +1,29 @@
+---
+title: foo - test
+layout: api
+---
+
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+
+# foo
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+
+``` kotlin
+if (true) {
+    println(property)
+}
+```
+
+</div>
+
+<div class="overload-group" markdown="1">
+
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="parameterName" id="$foo(kotlin.Int)/i">i</span><span class="symbol">:</span>&nbsp;<span class="identifier">Int</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+
+</div>
diff --git a/core/testdata/functions/annotatedFunction.kt b/core/testdata/functions/annotatedFunction.kt
new file mode 100644
index 0000000..f7abbf6
--- /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 0000000..e559713
--- /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 0000000..3ed81df
--- /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/functionWithAnnotatedParam.kt b/core/testdata/functions/functionWithAnnotatedParam.kt
new file mode 100644
index 0000000..f858e67
--- /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 0000000..3a3a102
--- /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 0000000..640bec8
--- /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 0000000..3c7e2ff
--- /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 0000000..85c4936
--- /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 0000000..c847325
--- /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 0000000..05a6507
--- /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 0000000..5f22f8c
--- /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 0000000..11c1967
--- /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 0000000..cdcd335
--- /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 0000000..9e966b3
--- /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 0000000..3165826
--- /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 0000000..0302da9
--- /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 0000000..6296e40
--- /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 0000000..dc42a20
--- /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 0000000..6f899d1
--- /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 0000000..be8decd
--- /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 0000000..d8dda93
--- /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 0000000..d9ae75f
--- /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 0000000..58b3ac4
--- /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 0000000..be3dd57
--- /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 0000000..d4f4b8d
--- /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 0000000..ea645c2
--- /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 0000000..a2ecd7f
--- /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 0000000..31b5fa9
--- /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 0000000..c26194c
--- /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 0000000..5a24b30
--- /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 0000000..d184564
--- /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 0000000..9430f4d
--- /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 0000000..84be1a7
--- /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 0000000..499e449
--- /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 0000000..5a6cdd7
--- /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 0000000..ec0a5bb
--- /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 0000000..a57ea3b
--- /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 0000000..e4774cd
--- /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 0000000..df5bbbe
--- /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 0000000..1d10a42
--- /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 0000000..48972a2
--- /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 0000000..d6cd9de
--- /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 0000000..90f6c13
--- /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 0000000..bb09bfa
--- /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 0000000..55be605
--- /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 0000000..57011bf
--- /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 0000000..e69de29
--- /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 0000000..b60eaed
--- /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 0000000..3dd8662
--- /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 0000000..ca42a74
--- /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 0000000..22c154f
--- /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 0000000..74395f0
--- /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 0000000..fce8792
--- /dev/null
+++ b/core/testdata/markdown/spec.txt
@@ -0,0 +1,6150 @@
+---
+title: CommonMark Spec
+author:
+- John MacFarlane
+version: 2
+date: 2014-09-19
+...
+
+# Introduction
+
+## What is Markdown?
+
+Markdown is a plain text format for writing structured documents,
+based on conventions used for indicating formatting in email and
+usenet posts.  It was developed in 2004 by John Gruber, who wrote
+the first Markdown-to-HTML converter in perl, and it soon became
+widely used in websites.  By 2014 there were dozens of
+implementations in many languages.  Some of them extended basic
+Markdown syntax with conventions for footnotes, definition lists,
+tables, and other constructs, and some allowed output not just in
+HTML but in LaTeX and many other formats.
+
+## Why is a spec needed?
+
+John Gruber's [canonical description of Markdown's
+syntax](http://daringfireball.net/projects/markdown/syntax)
+does not specify the syntax unambiguously.  Here are some examples of
+questions it does not answer:
+
+1.  How much indentation is needed for a sublist?  The spec says that
+    continuation paragraphs need to be indented four spaces, but is
+    not fully explicit about sublists.  It is natural to think that
+    they, too, must be indented four spaces, but `Markdown.pl` does
+    not require that.  This is hardly a "corner case," and divergences
+    between implementations on this issue often lead to surprises for
+    users in real documents. (See [this comment by John
+    Gruber](http://article.gmane.org/gmane.text.markdown.general/1997).)
+
+2.  Is a blank line needed before a block quote or header?
+    Most implementations do not require the blank line.  However,
+    this can lead to unexpected results in hard-wrapped text, and
+    also to ambiguities in parsing (note that some implementations
+    put the header inside the blockquote, while others do not).
+    (John Gruber has also spoken [in favor of requiring the blank
+    lines](http://article.gmane.org/gmane.text.markdown.general/2146).)
+
+3.  Is a blank line needed before an indented code block?
+    (`Markdown.pl` requires it, but this is not mentioned in the
+    documentation, and some implementations do not require it.)
+
+    ``` markdown
+    paragraph
+        code?
+    ```
+
+4.  What is the exact rule for determining when list items get
+    wrapped in `<p>` tags?  Can a list be partially "loose" and partially
+    "tight"?  What should we do with a list like this?
+
+    ``` markdown
+    1. one
+
+    2. two
+    3. three
+    ```
+
+    Or this?
+
+    ``` markdown
+    1.  one
+        - a
+
+        - b
+    2.  two
+    ```
+
+    (There are some relevant comments by John Gruber
+    [here](http://article.gmane.org/gmane.text.markdown.general/2554).)
+
+5.  Can list markers be indented?  Can ordered list markers be right-aligned?
+
+    ``` markdown
+     8. item 1
+     9. item 2
+    10. item 2a
+    ```
+
+6.  Is this one list with a horizontal rule in its second item,
+    or two lists separated by a horizontal rule?
+
+    ``` markdown
+    * a
+    * * * * *
+    * b
+    ```
+
+7.  When list markers change from numbers to bullets, do we have
+    two lists or one?  (The Markdown syntax description suggests two,
+    but the perl scripts and many other implementations produce one.)
+
+    ``` markdown
+    1. fee
+    2. fie
+    -  foe
+    -  fum
+    ```
+
+8.  What are the precedence rules for the markers of inline structure?
+    For example, is the following a valid link, or does the code span
+    take precedence ?
+
+    ``` markdown
+    [a backtick (`)](/url) and [another backtick (`)](/url).
+    ```
+
+9.  What are the precedence rules for markers of emphasis and strong
+    emphasis?  For example, how should the following be parsed?
+
+    ``` markdown
+    *foo *bar* baz*
+    ```
+
+10. What are the precedence rules between block-level and inline-level
+    structure?  For example, how should the following be parsed?
+
+    ``` markdown
+    - `a long code span can contain a hyphen like this
+      - and it can screw things up`
+    ```
+
+11. Can list items include headers?  (`Markdown.pl` does not allow this,
+    but headers can occur in blockquotes.)
+
+    ``` markdown
+    - # Heading
+    ```
+
+12. Can link references be defined inside block quotes or list items?
+
+    ``` markdown
+    > Blockquote [foo].
+    >
+    > [foo]: /url
+    ```
+
+13. If there are multiple definitions for the same reference, which takes
+    precedence?
+
+    ``` markdown
+    [foo]: /url1
+    [foo]: /url2
+
+    [foo][]
+    ```
+
+In the absence of a spec, early implementers consulted `Markdown.pl`
+to resolve these ambiguities.  But `Markdown.pl` was quite buggy, and
+gave manifestly bad results in many cases, so it was not a
+satisfactory replacement for a spec.
+
+Because there is no unambiguous spec, implementations have diverged
+considerably.  As a result, users are often surprised to find that
+a document that renders one way on one system (say, a github wiki)
+renders differently on another (say, converting to docbook using
+pandoc).  To make matters worse, because nothing in Markdown counts
+as a "syntax error," the divergence often isn't discovered right away.
+
+## About this document
+
+This document attempts to specify Markdown syntax unambiguously.
+It contains many examples with side-by-side Markdown and
+HTML.  These are intended to double as conformance tests.  An
+accompanying script `runtests.pl` can be used to run the tests
+against any Markdown program:
+
+    perl runtests.pl spec.txt PROGRAM
+
+Since this document describes how Markdown is to be parsed into
+an abstract syntax tree, it would have made sense to use an abstract
+representation of the syntax tree instead of HTML.  But HTML is capable
+of representing the structural distinctions we need to make, and the
+choice of HTML for the tests makes it possible to run the tests against
+an implementation without writing an abstract syntax tree renderer.
+
+This document is generated from a text file, `spec.txt`, written
+in Markdown with a small extension for the side-by-side tests.
+The script `spec2md.pl` can be used to turn `spec.txt` into pandoc
+Markdown, which can then be converted into other formats.
+
+In the examples, the `→` character is used to represent tabs.
+
+# Preprocessing
+
+A [line](#line) <a id="line"></a>
+is a sequence of zero or more characters followed by a line
+ending (CR, LF, or CRLF) or by the end of
+file.
+
+This spec does not specify an encoding; it thinks of lines as composed
+of characters rather than bytes.  A conforming parser may be limited
+to a certain encoding.
+
+Tabs in lines are expanded to spaces, with a tab stop of 4 characters:
+
+.
+→foo→baz→→bim
+.
+<pre><code>foo baz     bim
+</code></pre>
+.
+
+.
+    a→a
+    ὐ→a
+.
+<pre><code>a   a
+ὐ   a
+</code></pre>
+.
+
+Line endings are replaced by newline characters (LF).
+
+A line containing no characters, or a line containing only spaces (after
+tab expansion), is called a [blank line](#blank-line).
+<a id="blank-line"></a>
+
+# Blocks and inlines
+
+We can think of a document as a sequence of [blocks](#block)<a
+id="block"></a>---structural elements like paragraphs, block quotations,
+lists, headers, rules, and code blocks.  Blocks can contain other
+blocks, or they can contain [inline](#inline)<a id="inline"></a> content:
+words, spaces, links, emphasized text, images, and inline code.
+
+## Precedence
+
+Indicators of block structure always take precedence over indicators
+of inline structure.  So, for example, the following is a list with
+two items, not a list with one item containing a code span:
+
+.
+- `one
+- two`
+.
+<ul>
+<li>`one</li>
+<li>two`</li>
+</ul>
+.
+
+This means that parsing can proceed in two steps:  first, the block
+structure of the document can be discerned; second, text lines inside
+paragraphs, headers, and other block constructs can be parsed for inline
+structure.  The second step requires information about link reference
+definitions that will be available only at the end of the first
+step.  Note that the first step requires processing lines in sequence,
+but the second can be parallelized, since the inline parsing of
+one block element does not affect the inline parsing of any other.
+
+## Container blocks and leaf blocks
+
+We can divide blocks into two types:
+[container blocks](#container-block), <a id="container-block"></a>
+which can contain other blocks, and [leaf blocks](#leaf-block),
+<a id="leaf-block"></a> which cannot.
+
+# Leaf blocks
+
+This section describes the different kinds of leaf block that make up a
+Markdown document.
+
+## Horizontal rules
+
+A line consisting of 0-3 spaces of indentation, followed by a sequence
+of three or more matching `-`, `_`, or `*` characters, each followed
+optionally by any number of spaces, forms a [horizontal
+rule](#horizontal-rule). <a id="horizontal-rule"></a>
+
+.
+***
+---
+___
+.
+<hr />
+<hr />
+<hr />
+.
+
+Wrong characters:
+
+.
++++
+.
+<p>+++</p>
+.
+
+.
+===
+.
+<p>===</p>
+.
+
+Not enough characters:
+
+.
+--
+**
+__
+.
+<p>--
+**
+__</p>
+.
+
+One to three spaces indent are allowed:
+
+.
+ ***
+  ***
+   ***
+.
+<hr />
+<hr />
+<hr />
+.
+
+Four spaces is too many:
+
+.
+    ***
+.
+<pre><code>***
+</code></pre>
+.
+
+.
+Foo
+    ***
+.
+<p>Foo
+***</p>
+.
+
+More than three characters may be used:
+
+.
+_____________________________________
+.
+<hr />
+.
+
+Spaces are allowed between the characters:
+
+.
+ - - -
+.
+<hr />
+.
+
+.
+ **  * ** * ** * **
+.
+<hr />
+.
+
+.
+-     -      -      -
+.
+<hr />
+.
+
+Spaces are allowed at the end:
+
+.
+- - - -    
+.
+<hr />
+.
+
+However, no other characters may occur at the end or the
+beginning:
+
+.
+_ _ _ _ a
+
+a------
+.
+<p>_ _ _ _ a</p>
+<p>a------</p>
+.
+
+It is required that all of the non-space characters be the same.
+So, this is not a horizontal rule:
+
+.
+ *-*
+.
+<p><em>-</em></p>
+.
+
+Horizontal rules do not need blank lines before or after:
+
+.
+- foo
+***
+- bar
+.
+<ul>
+<li>foo</li>
+</ul>
+<hr />
+<ul>
+<li>bar</li>
+</ul>
+.
+
+Horizontal rules can interrupt a paragraph:
+
+.
+Foo
+***
+bar
+.
+<p>Foo</p>
+<hr />
+<p>bar</p>
+.
+
+Note, however, that this is a setext header, not a paragraph followed
+by a horizontal rule:
+
+.
+Foo
+---
+bar
+.
+<h2>Foo</h2>
+<p>bar</p>
+.
+
+When both a horizontal rule and a list item are possible
+interpretations of a line, the horizontal rule is preferred:
+
+.
+* Foo
+* * *
+* Bar
+.
+<ul>
+<li>Foo</li>
+</ul>
+<hr />
+<ul>
+<li>Bar</li>
+</ul>
+.
+
+If you want a horizontal rule in a list item, use a different bullet:
+
+.
+- Foo
+- * * *
+.
+<ul>
+<li>Foo</li>
+<li><hr /></li>
+</ul>
+.
+
+## ATX headers
+
+An [ATX header](#atx-header) <a id="atx-header"></a>
+consists of a string of characters, parsed as inline content, between an
+opening sequence of 1--6 unescaped `#` characters and an optional
+closing sequence of any number of `#` characters.  The opening sequence
+of `#` characters cannot be followed directly by a nonspace character.
+The closing `#` characters may be followed by spaces only.  The opening
+`#` character may be indented 0-3 spaces.  The raw contents of the
+header are stripped of leading and trailing spaces before being parsed
+as inline content.  The header level is equal to the number of `#`
+characters in the opening sequence.
+
+Simple headers:
+
+.
+# foo
+## foo
+### foo
+#### foo
+##### foo
+###### foo
+.
+<h1>foo</h1>
+<h2>foo</h2>
+<h3>foo</h3>
+<h4>foo</h4>
+<h5>foo</h5>
+<h6>foo</h6>
+.
+
+More than six `#` characters is not a header:
+
+.
+####### foo
+.
+<p>####### foo</p>
+.
+
+A space is required between the `#` characters and the header's
+contents.  Note that many implementations currently do not require
+the space.  However, the space was required by the [original ATX
+implementation](http://www.aaronsw.com/2002/atx/atx.py), and it helps
+prevent things like the following from being parsed as headers:
+
+.
+#5 bolt
+.
+<p>#5 bolt</p>
+.
+
+This is not a header, because the first `#` is escaped:
+
+.
+\## foo
+.
+<p>## foo</p>
+.
+
+Contents are parsed as inlines:
+
+.
+# foo *bar* \*baz\*
+.
+<h1>foo <em>bar</em> *baz*</h1>
+.
+
+Leading and trailing blanks are ignored in parsing inline content:
+
+.
+#                  foo                     
+.
+<h1>foo</h1>
+.
+
+One to three spaces indentation are allowed:
+
+.
+ ### foo
+  ## foo
+   # foo
+.
+<h3>foo</h3>
+<h2>foo</h2>
+<h1>foo</h1>
+.
+
+Four spaces are too much:
+
+.
+    # foo
+.
+<pre><code># foo
+</code></pre>
+.
+
+.
+foo
+    # bar
+.
+<p>foo
+# bar</p>
+.
+
+A closing sequence of `#` characters is optional:
+
+.
+## foo ##
+  ###   bar    ###
+.
+<h2>foo</h2>
+<h3>bar</h3>
+.
+
+It need not be the same length as the opening sequence:
+
+.
+# foo ##################################
+##### foo ##
+.
+<h1>foo</h1>
+<h5>foo</h5>
+.
+
+Spaces are allowed after the closing sequence:
+
+.
+### foo ###     
+.
+<h3>foo</h3>
+.
+
+A sequence of `#` characters with a nonspace character following it
+is not a closing sequence, but counts as part of the contents of the
+header:
+
+.
+### foo ### b
+.
+<h3>foo ### b</h3>
+.
+
+Backslash-escaped `#` characters do not count as part
+of the closing sequence:
+
+.
+### foo \###
+## foo \#\##
+# foo \#
+.
+<h3>foo #</h3>
+<h2>foo ##</h2>
+<h1>foo #</h1>
+.
+
+ATX headers need not be separated from surrounding content by blank
+lines, and they can interrupt paragraphs:
+
+.
+****
+## foo
+****
+.
+<hr />
+<h2>foo</h2>
+<hr />
+.
+
+.
+Foo bar
+# baz
+Bar foo
+.
+<p>Foo bar</p>
+<h1>baz</h1>
+<p>Bar foo</p>
+.
+
+ATX headers can be empty:
+
+.
+## 
+#
+### ###
+.
+<h2></h2>
+<h1></h1>
+<h3></h3>
+.
+
+## Setext headers
+
+A [setext header](#setext-header) <a id="setext-header"></a>
+consists of a line of text, containing at least one nonspace character,
+with no more than 3 spaces indentation, followed by a [setext header
+underline](#setext-header-underline).  A [setext header
+underline](#setext-header-underline) <a id="setext-header-underline"></a>
+is a sequence of `=` characters or a sequence of `-` characters, with no
+more than 3 spaces indentation and any number of trailing
+spaces.  The header is a level 1 header if `=` characters are used, and
+a level 2 header if `-` characters are used.  The contents of the header
+are the result of parsing the first line as Markdown inline content.
+
+In general, a setext header need not be preceded or followed by a
+blank line.  However, it cannot interrupt a paragraph, so when a
+setext header comes after a paragraph, a blank line is needed between
+them.
+
+Simple examples:
+
+.
+Foo *bar*
+=========
+
+Foo *bar*
+---------
+.
+<h1>Foo <em>bar</em></h1>
+<h2>Foo <em>bar</em></h2>
+.
+
+The underlining can be any length:
+
+.
+Foo
+-------------------------
+
+Foo
+=
+.
+<h2>Foo</h2>
+<h1>Foo</h1>
+.
+
+The header content can be indented up to three spaces, and need
+not line up with the underlining:
+
+.
+   Foo
+---
+
+  Foo
+-----
+
+  Foo
+  ===
+.
+<h2>Foo</h2>
+<h2>Foo</h2>
+<h1>Foo</h1>
+.
+
+Four spaces indent is too much:
+
+.
+    Foo
+    ---
+
+    Foo
+---
+.
+<pre><code>Foo
+---
+
+Foo
+</code></pre>
+<hr />
+.
+
+The setext header underline can be indented up to three spaces, and
+may have trailing spaces:
+
+.
+Foo
+   ----      
+.
+<h2>Foo</h2>
+.
+
+Four spaces is too much:
+
+.
+Foo
+     ---
+.
+<p>Foo
+---</p>
+.
+
+The setext header underline cannot contain internal spaces:
+
+.
+Foo
+= =
+
+Foo
+--- -
+.
+<p>Foo
+= =</p>
+<p>Foo</p>
+<hr />
+.
+
+Trailing spaces in the content line do not cause a line break:
+
+.
+Foo  
+-----
+.
+<h2>Foo</h2>
+.
+
+Nor does a backslash at the end:
+
+.
+Foo\
+----
+.
+<h2>Foo\</h2>
+.
+
+Since indicators of block structure take precedence over
+indicators of inline structure, the following are setext headers:
+
+.
+`Foo
+----
+`
+
+<a title="a lot
+---
+of dashes"/>
+.
+<h2>`Foo</h2>
+<p>`</p>
+<h2>&lt;a title=&quot;a lot</h2>
+<p>of dashes&quot;/&gt;</p>
+.
+
+The setext header underline cannot be a lazy line:
+
+.
+> Foo
+---
+.
+<blockquote>
+<p>Foo</p>
+</blockquote>
+<hr />
+.
+
+A setext header cannot interrupt a paragraph:
+
+.
+Foo
+Bar
+---
+
+Foo
+Bar
+===
+.
+<p>Foo
+Bar</p>
+<hr />
+<p>Foo
+Bar
+===</p>
+.
+
+But in general a blank line is not required before or after:
+
+.
+---
+Foo
+---
+Bar
+---
+Baz
+.
+<hr />
+<h2>Foo</h2>
+<h2>Bar</h2>
+<p>Baz</p>
+.
+
+Setext headers cannot be empty:
+
+.
+
+====
+.
+<p>====</p>
+.
+
+
+## Indented code blocks
+
+An [indented code block](#indented-code-block)
+<a id="indented-code-block"></a> is composed of one or more
+[indented chunks](#indented-chunk) separated by blank lines.
+An [indented chunk](#indented-chunk) <a id="indented-chunk"></a>
+is a sequence of non-blank lines, each indented four or more
+spaces.  An indented code block cannot interrupt a paragraph, so
+if it occurs before or after a paragraph, there must be an
+intervening blank line.  The contents of the code block are
+the literal contents of the lines, including trailing newlines,
+minus four spaces of indentation. An indented code block has no
+attributes.
+
+.
+    a simple
+      indented code block
+.
+<pre><code>a simple
+  indented code block
+</code></pre>
+.
+
+The contents are literal text, and do not get parsed as Markdown:
+
+.
+    <a/>
+    *hi*
+
+    - one
+.
+<pre><code>&lt;a/&gt;
+*hi*
+
+- one
+</code></pre>
+.
+
+Here we have three chunks separated by blank lines:
+
+.
+    chunk1
+
+    chunk2
+  
+ 
+ 
+    chunk3
+.
+<pre><code>chunk1
+
+chunk2
+
+
+
+chunk3
+</code></pre>
+.
+
+Any initial spaces beyond four will be included in the content, even
+in interior blank lines:
+
+.
+    chunk1
+      
+      chunk2
+.
+<pre><code>chunk1
+  
+  chunk2
+</code></pre>
+.
+
+An indented code block cannot interrupt a paragraph.  (This
+allows hanging indents and the like.)
+
+.
+Foo
+    bar
+
+.
+<p>Foo
+bar</p>
+.
+
+However, any non-blank line with fewer than four leading spaces ends
+the code block immediately.  So a paragraph may occur immediately
+after indented code:
+
+.
+    foo
+bar
+.
+<pre><code>foo
+</code></pre>
+<p>bar</p>
+.
+
+And indented code can occur immediately before and after other kinds of
+blocks:
+
+.
+# Header
+    foo
+Header
+------
+    foo
+----
+.
+<h1>Header</h1>
+<pre><code>foo
+</code></pre>
+<h2>Header</h2>
+<pre><code>foo
+</code></pre>
+<hr />
+.
+
+The first line can be indented more than four spaces:
+
+.
+        foo
+    bar
+.
+<pre><code>    foo
+bar
+</code></pre>
+.
+
+Blank lines preceding or following an indented code block
+are not included in it:
+
+.
+
+    
+    foo
+    
+
+.
+<pre><code>foo
+</code></pre>
+.
+
+Trailing spaces are included in the code block's content:
+
+.
+    foo  
+.
+<pre><code>foo  
+</code></pre>
+.
+
+
+## Fenced code blocks
+
+A [code fence](#code-fence) <a id="code-fence"></a> is a sequence
+of at least three consecutive backtick characters (`` ` ``) or
+tildes (`~`).  (Tildes and backticks cannot be mixed.)
+A [fenced code block](#fenced-code-block) <a id="fenced-code-block"></a>
+begins with a code fence, indented no more than three spaces.
+
+The line with the opening code fence may optionally contain some text
+following the code fence; this is trimmed of leading and trailing
+spaces and called the [info string](#info-string).
+<a id="info-string"></a> The info string may not contain any backtick
+characters.  (The reason for this restriction is that otherwise
+some inline code would be incorrectly interpreted as the
+beginning of a fenced code block.)
+
+The content of the code block consists of all subsequent lines, until
+a closing [code fence](#code-fence) of the same type as the code block
+began with (backticks or tildes), and with at least as many backticks
+or tildes as the opening code fence.  If the leading code fence is
+indented N spaces, then up to N spaces of indentation are removed from
+each line of the content (if present).  (If a content line is not
+indented, it is preserved unchanged.  If it is indented less than N
+spaces, all of the indentation is removed.)
+
+The closing code fence may be indented up to three spaces, and may be
+followed only by spaces, which are ignored.  If the end of the
+containing block (or document) is reached and no closing code fence
+has been found, the code block contains all of the lines after the
+opening code fence until the end of the containing block (or
+document).  (An alternative spec would require backtracking in the
+event that a closing code fence is not found.  But this makes parsing
+much less efficient, and there seems to be no real down side to the
+behavior described here.)
+
+A fenced code block may interrupt a paragraph, and does not require
+a blank line either before or after.
+
+The content of a code fence is treated as literal text, not parsed
+as inlines.  The first word of the info string is typically used to
+specify the language of the code sample, and rendered in the `class`
+attribute of the `code` tag.  However, this spec does not mandate any
+particular treatment of the info string.
+
+Here is a simple example with backticks:
+
+.
+```
+<
+ >
+```
+.
+<pre><code>&lt;
+ &gt;
+</code></pre>
+.
+
+With tildes:
+
+.
+~~~
+<
+ >
+~~~
+.
+<pre><code>&lt;
+ &gt;
+</code></pre>
+.
+
+The closing code fence must use the same character as the opening
+fence:
+
+.
+```
+aaa
+~~~
+```
+.
+<pre><code>aaa
+~~~
+</code></pre>
+.
+
+.
+~~~
+aaa
+```
+~~~
+.
+<pre><code>aaa
+```
+</code></pre>
+.
+
+The closing code fence must be at least as long as the opening fence:
+
+.
+````
+aaa
+```
+``````
+.
+<pre><code>aaa
+```
+</code></pre>
+.
+
+.
+~~~~
+aaa
+~~~
+~~~~
+.
+<pre><code>aaa
+~~~
+</code></pre>
+.
+
+Unclosed code blocks are closed by the end of the document:
+
+.
+```
+.
+<pre><code></code></pre>
+.
+
+.
+`````
+
+```
+aaa
+.
+<pre><code>
+```
+aaa
+</code></pre>
+.
+
+A code block can have all empty lines as its content:
+
+.
+```
+
+  
+```
+.
+<pre><code>
+  
+</code></pre>
+.
+
+A code block can be empty:
+
+.
+```
+```
+.
+<pre><code></code></pre>
+.
+
+Fences can be indented.  If the opening fence is indented,
+content lines will have equivalent opening indentation removed,
+if present:
+
+.
+ ```
+ aaa
+aaa
+```
+.
+<pre><code>aaa
+aaa
+</code></pre>
+.
+
+.
+  ```
+aaa
+  aaa
+aaa
+  ```
+.
+<pre><code>aaa
+aaa
+aaa
+</code></pre>
+.
+
+.
+   ```
+   aaa
+    aaa
+  aaa
+   ```
+.
+<pre><code>aaa
+ aaa
+aaa
+</code></pre>
+.
+
+Four spaces indentation produces an indented code block:
+
+.
+    ```
+    aaa
+    ```
+.
+<pre><code>```
+aaa
+```
+</code></pre>
+.
+
+Code fences (opening and closing) cannot contain internal spaces:
+
+.
+``` ```
+aaa
+.
+<p><code></code>
+aaa</p>
+.
+
+.
+~~~~~~
+aaa
+~~~ ~~
+.
+<pre><code>aaa
+~~~ ~~
+</code></pre>
+.
+
+Fenced code blocks can interrupt paragraphs, and can be followed
+directly by paragraphs, without a blank line between:
+
+.
+foo
+```
+bar
+```
+baz
+.
+<p>foo</p>
+<pre><code>bar
+</code></pre>
+<p>baz</p>
+.
+
+Other blocks can also occur before and after fenced code blocks
+without an intervening blank line:
+
+.
+foo
+---
+~~~
+bar
+~~~
+# baz
+.
+<h2>foo</h2>
+<pre><code>bar
+</code></pre>
+<h1>baz</h1>
+.
+
+An [info string](#info-string) can be provided after the opening code fence.
+Opening and closing spaces will be stripped, and the first word, prefixed
+with `language-`, is used as the value for the `class` attribute of the
+`code` element within the enclosing `pre` element.
+
+.
+```ruby
+def foo(x)
+  return 3
+end
+```
+.
+<pre><code class="language-ruby">def foo(x)
+  return 3
+end
+</code></pre>
+.
+
+.
+~~~~    ruby startline=3 $%@#$
+def foo(x)
+  return 3
+end
+~~~~~~~
+.
+<pre><code class="language-ruby">def foo(x)
+  return 3
+end
+</code></pre>
+.
+
+.
+````;
+````
+.
+<pre><code class="language-;"></code></pre>
+.
+
+Info strings for backtick code blocks cannot contain backticks:
+
+.
+``` aa ```
+foo
+.
+<p><code>aa</code>
+foo</p>
+.
+
+Closing code fences cannot have info strings:
+
+.
+```
+``` aaa
+```
+.
+<pre><code>``` aaa
+</code></pre>
+.
+
+
+## HTML blocks
+
+An [HTML block tag](#html-block-tag) <a id="html-block-tag"></a> is
+an [open tag](#open-tag) or [closing tag](#closing-tag) whose tag
+name is one of the following (case-insensitive):
+`article`, `header`, `aside`, `hgroup`, `blockquote`, `hr`, `iframe`,
+`body`, `li`, `map`, `button`, `object`, `canvas`, `ol`, `caption`,
+`output`, `col`, `p`, `colgroup`, `pre`, `dd`, `progress`, `div`,
+`section`, `dl`, `table`, `td`, `dt`, `tbody`, `embed`, `textarea`,
+`fieldset`, `tfoot`, `figcaption`, `th`, `figure`, `thead`, `footer`,
+`footer`, `tr`, `form`, `ul`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`,
+`video`, `script`, `style`.
+
+An [HTML block](#html-block) <a id="html-block"></a> begins with an
+[HTML block tag](#html-block-tag), [HTML comment](#html-comment),
+[processing instruction](#processing-instruction),
+[declaration](#declaration), or [CDATA section](#cdata-section).
+It ends when a [blank line](#blank-line) or the end of the
+input is encountered.  The initial line may be indented up to three
+spaces, and subsequent lines may have any indentation.  The contents
+of the HTML block are interpreted as raw HTML, and will not be escaped
+in HTML output.
+
+Some simple examples:
+
+.
+<table>
+  <tr>
+    <td>
+           hi
+    </td>
+  </tr>
+</table>
+
+okay.
+.
+<table>
+  <tr>
+    <td>
+           hi
+    </td>
+  </tr>
+</table>
+<p>okay.</p>
+.
+
+.
+ <div>
+  *hello*
+         <foo><a>
+.
+ <div>
+  *hello*
+         <foo><a>
+.
+
+Here we have two code blocks with a Markdown paragraph between them:
+
+.
+<DIV CLASS="foo">
+
+*Markdown*
+
+</DIV>
+.
+<DIV CLASS="foo">
+<p><em>Markdown</em></p>
+</DIV>
+.
+
+In the following example, what looks like a Markdown code block
+is actually part of the HTML block, which continues until a blank
+line or the end of the document is reached:
+
+.
+<div></div>
+``` c
+int x = 33;
+```
+.
+<div></div>
+``` c
+int x = 33;
+```
+.
+
+A comment:
+
+.
+<!-- Foo
+bar
+   baz -->
+.
+<!-- Foo
+bar
+   baz -->
+.
+
+A processing instruction:
+
+.
+<?php
+  echo 'foo'
+?>
+.
+<?php
+  echo 'foo'
+?>
+.
+
+CDATA:
+
+.
+<![CDATA[
+function matchwo(a,b)
+{
+if (a < b && a < 0) then
+  {
+  return 1;
+  }
+else
+  {
+  return 0;
+  }
+}
+]]>
+.
+<![CDATA[
+function matchwo(a,b)
+{
+if (a < b && a < 0) then
+  {
+  return 1;
+  }
+else
+  {
+  return 0;
+  }
+}
+]]>
+.
+
+The opening tag can be indented 1-3 spaces, but not 4:
+
+.
+  <!-- foo -->
+
+    <!-- foo -->
+.
+  <!-- foo -->
+<pre><code>&lt;!-- foo --&gt;
+</code></pre>
+.
+
+An HTML block can interrupt a paragraph, and need not be preceded
+by a blank line.
+
+.
+Foo
+<div>
+bar
+</div>
+.
+<p>Foo</p>
+<div>
+bar
+</div>
+.
+
+However, a following blank line is always needed, except at the end of
+a document:
+
+.
+<div>
+bar
+</div>
+*foo*
+.
+<div>
+bar
+</div>
+*foo*
+.
+
+An incomplete HTML block tag may also start an HTML block:
+
+.
+<div class
+foo
+.
+<div class
+foo
+.
+
+This rule differs from John Gruber's original Markdown syntax
+specification, which says:
+
+> The only restrictions are that block-level HTML elements —
+> e.g. `<div>`, `<table>`, `<pre>`, `<p>`, etc. — must be separated from
+> surrounding content by blank lines, and the start and end tags of the
+> block should not be indented with tabs or spaces.
+
+In some ways Gruber's rule is more restrictive than the one given
+here:
+
+- It requires that an HTML block be preceded by a blank line.
+- It does not allow the start tag to be indented.
+- It requires a matching end tag, which it also does not allow to
+  be indented.
+
+Indeed, most Markdown implementations, including some of Gruber's
+own perl implementations, do not impose these restrictions.
+
+There is one respect, however, in which Gruber's rule is more liberal
+than the one given here, since it allows blank lines to occur inside
+an HTML block.  There are two reasons for disallowing them here.
+First, it removes the need to parse balanced tags, which is
+expensive and can require backtracking from the end of the document
+if no matching end tag is found. Second, it provides a very simple
+and flexible way of including Markdown content inside HTML tags:
+simply separate the Markdown from the HTML using blank lines:
+
+.
+<div>
+
+*Emphasized* text.
+
+</div>
+.
+<div>
+<p><em>Emphasized</em> text.</p>
+</div>
+.
+
+Compare:
+
+.
+<div>
+*Emphasized* text.
+</div>
+.
+<div>
+*Emphasized* text.
+</div>
+.
+
+Some Markdown implementations have adopted a convention of
+interpreting content inside tags as text if the open tag has
+the attribute `markdown=1`.  The rule given above seems a simpler and
+more elegant way of achieving the same expressive power, which is also
+much simpler to parse.
+
+The main potential drawback is that one can no longer paste HTML
+blocks into Markdown documents with 100% reliability.  However,
+*in most cases* this will work fine, because the blank lines in
+HTML are usually followed by HTML block tags.  For example:
+
+.
+<table>
+
+<tr>
+
+<td>
+Hi
+</td>
+
+</tr>
+
+</table>
+.
+<table>
+<tr>
+<td>
+Hi
+</td>
+</tr>
+</table>
+.
+
+Moreover, blank lines are usually not necessary and can be
+deleted.  The exception is inside `<pre>` tags; here, one can
+replace the blank lines with `&#10;` entities.
+
+So there is no important loss of expressive power with the new rule.
+
+## Link reference definitions
+
+A [link reference definition](#link-reference-definition)
+<a id="link-reference-definition"></a> consists of a [link
+label](#link-label), indented up to three spaces, followed
+by a colon (`:`), optional blank space (including up to one
+newline), a [link destination](#link-destination), optional
+blank space (including up to one newline), and an optional [link
+title](#link-title), which if it is present must be separated
+from the [link destination](#link-destination) by whitespace.
+No further non-space characters may occur on the line.
+
+A [link reference-definition](#link-reference-definition)
+does not correspond to a structural element of a document.  Instead, it
+defines a label which can be used in [reference links](#reference-link)
+and reference-style [images](#image) elsewhere in the document.  [Link
+reference definitions] can come either before or after the links that use
+them.
+
+.
+[foo]: /url "title"
+
+[foo]
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+   [foo]: 
+      /url  
+           'the title'  
+
+[foo]
+.
+<p><a href="/url" title="the title">foo</a></p>
+.
+
+.
+[Foo*bar\]]:my_(url) 'title (with parens)'
+
+[Foo*bar\]]
+.
+<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
+.
+
+.
+[Foo bar]:
+<my url>
+'title'
+
+[Foo bar]
+.
+<p><a href="my%20url" title="title">Foo bar</a></p>
+.
+
+The title may be omitted:
+
+.
+[foo]:
+/url
+
+[foo]
+.
+<p><a href="/url">foo</a></p>
+.
+
+The link destination may not be omitted:
+
+.
+[foo]:
+
+[foo]
+.
+<p>[foo]:</p>
+<p>[foo]</p>
+.
+
+A link can come before its corresponding definition:
+
+.
+[foo]
+
+[foo]: url
+.
+<p><a href="url">foo</a></p>
+.
+
+If there are several matching definitions, the first one takes
+precedence:
+
+.
+[foo]
+
+[foo]: first
+[foo]: second
+.
+<p><a href="first">foo</a></p>
+.
+
+As noted in the section on [Links], matching of labels is
+case-insensitive (see [matches](#matches)).
+
+.
+[FOO]: /url
+
+[Foo]
+.
+<p><a href="/url">Foo</a></p>
+.
+
+.
+[ΑΓΩ]: /φου
+
+[αγω]
+.
+<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>
+.
+
+Here is a link reference definition with no corresponding link.
+It contributes nothing to the document.
+
+.
+[foo]: /url
+.
+.
+
+This is not a link reference definition, because there are
+non-space characters after the title:
+
+.
+[foo]: /url "title" ok
+.
+<p>[foo]: /url &quot;title&quot; ok</p>
+.
+
+This is not a link reference definition, because it is indented
+four spaces:
+
+.
+    [foo]: /url "title"
+
+[foo]
+.
+<pre><code>[foo]: /url &quot;title&quot;
+</code></pre>
+<p>[foo]</p>
+.
+
+This is not a link reference definition, because it occurs inside
+a code block:
+
+.
+```
+[foo]: /url
+```
+
+[foo]
+.
+<pre><code>[foo]: /url
+</code></pre>
+<p>[foo]</p>
+.
+
+A [link reference definition](#link-reference-definition) cannot
+interrupt a paragraph.
+
+.
+Foo
+[bar]: /baz
+
+[bar]
+.
+<p>Foo
+[bar]: /baz</p>
+<p>[bar]</p>
+.
+
+However, it can directly follow other block elements, such as headers
+and horizontal rules, and it need not be followed by a blank line.
+
+.
+# [Foo]
+[foo]: /url
+> bar
+.
+<h1><a href="/url">Foo</a></h1>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+Several [link references](#link-reference) can occur one after another,
+without intervening blank lines.
+
+.
+[foo]: /foo-url "foo"
+[bar]: /bar-url
+  "bar"
+[baz]: /baz-url
+
+[foo],
+[bar],
+[baz]
+.
+<p><a href="/foo-url" title="foo">foo</a>,
+<a href="/bar-url" title="bar">bar</a>,
+<a href="/baz-url">baz</a></p>
+.
+
+[Link reference definitions](#link-reference-definition) can occur
+inside block containers, like lists and block quotations.  They
+affect the entire document, not just the container in which they
+are defined:
+
+.
+[foo]
+
+> [foo]: /url
+.
+<p><a href="/url">foo</a></p>
+<blockquote>
+</blockquote>
+.
+
+
+## Paragraphs
+
+A sequence of non-blank lines that cannot be interpreted as other
+kinds of blocks forms a [paragraph](#paragraph).<a id="paragraph"></a>
+The contents of the paragraph are the result of parsing the
+paragraph's raw content as inlines.  The paragraph's raw content
+is formed by concatenating the lines and removing initial and final
+spaces.
+
+A simple example with two paragraphs:
+
+.
+aaa
+
+bbb
+.
+<p>aaa</p>
+<p>bbb</p>
+.
+
+Paragraphs can contain multiple lines, but no blank lines:
+
+.
+aaa
+bbb
+
+ccc
+ddd
+.
+<p>aaa
+bbb</p>
+<p>ccc
+ddd</p>
+.
+
+Multiple blank lines between paragraph have no effect:
+
+.
+aaa
+
+
+bbb
+.
+<p>aaa</p>
+<p>bbb</p>
+.
+
+Leading spaces are skipped:
+
+.
+  aaa
+ bbb
+.
+<p>aaa
+bbb</p>
+.
+
+Lines after the first may be indented any amount, since indented
+code blocks cannot interrupt paragraphs.
+
+.
+aaa
+             bbb
+                                       ccc
+.
+<p>aaa
+bbb
+ccc</p>
+.
+
+However, the first line may be indented at most three spaces,
+or an indented code block will be triggered:
+
+.
+   aaa
+bbb
+.
+<p>aaa
+bbb</p>
+.
+
+.
+    aaa
+bbb
+.
+<pre><code>aaa
+</code></pre>
+<p>bbb</p>
+.
+
+Final spaces are stripped before inline parsing, so a paragraph
+that ends with two or more spaces will not end with a hard line
+break:
+
+.
+aaa     
+bbb     
+.
+<p>aaa<br />
+bbb</p>
+.
+
+## Blank lines
+
+[Blank lines](#blank-line) between block-level elements are ignored,
+except for the role they play in determining whether a [list](#list)
+is [tight](#tight) or [loose](#loose).
+
+Blank lines at the beginning and end of the document are also ignored.
+
+.
+  
+
+aaa
+  
+
+# aaa
+
+  
+.
+<p>aaa</p>
+<h1>aaa</h1>
+.
+
+
+# Container blocks
+
+A [container block](#container-block) is a block that has other
+blocks as its contents.  There are two basic kinds of container blocks:
+[block quotes](#block-quote) and [list items](#list-item).
+[Lists](#list) are meta-containers for [list items](#list-item).
+
+We define the syntax for container blocks recursively.  The general
+form of the definition is:
+
+> If X is a sequence of blocks, then the result of
+> transforming X in such-and-such a way is a container of type Y
+> with these blocks as its content.
+
+So, we explain what counts as a block quote or list item by explaining
+how these can be *generated* from their contents. This should suffice
+to define the syntax, although it does not give a recipe for *parsing*
+these constructions.  (A recipe is provided below in the section entitled
+[A parsing strategy](#appendix-a-a-parsing-strategy).)
+
+## Block quotes
+
+A [block quote marker](#block-quote-marker) <a id="block-quote-marker"></a>
+consists of 0-3 spaces of initial indent, plus (a) the character `>` together
+with a following space, or (b) a single character `>` not followed by a space.
+
+The following rules define [block quotes](#block-quote):
+<a id="block-quote"></a>
+
+1.  **Basic case.**  If a string of lines *Ls* constitute a sequence
+    of blocks *Bs*, then the result of appending a [block quote
+    marker](#block-quote-marker) to the beginning of each line in *Ls*
+    is a [block quote](#block-quote) containing *Bs*.
+
+2.  **Laziness.**  If a string of lines *Ls* constitute a [block
+    quote](#block-quote) with contents *Bs*, then the result of deleting
+    the initial [block quote marker](#block-quote-marker) from one or
+    more lines in which the next non-space character after the [block
+    quote marker](#block-quote-marker) is [paragraph continuation
+    text](#paragraph-continuation-text) is a block quote with *Bs* as
+    its content.  <a id="paragraph-continuation-text"></a>
+    [Paragraph continuation text](#paragraph-continuation-text) is text
+    that will be parsed as part of the content of a paragraph, but does
+    not occur at the beginning of the paragraph.
+
+3.  **Consecutiveness.**  A document cannot contain two [block
+    quotes](#block-quote) in a row unless there is a [blank
+    line](#blank-line) between them.
+
+Nothing else counts as a [block quote](#block-quote).
+
+Here is a simple example:
+
+.
+> # Foo
+> bar
+> baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+The spaces after the `>` characters can be omitted:
+
+.
+># Foo
+>bar
+> baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+The `>` characters can be indented 1-3 spaces:
+
+.
+   > # Foo
+   > bar
+ > baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+Four spaces gives us a code block:
+
+.
+    > # Foo
+    > bar
+    > baz
+.
+<pre><code>&gt; # Foo
+&gt; bar
+&gt; baz
+</code></pre>
+.
+
+The Laziness clause allows us to omit the `>` before a
+paragraph continuation line:
+
+.
+> # Foo
+> bar
+baz
+.
+<blockquote>
+<h1>Foo</h1>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+A block quote can contain some lazy and some non-lazy
+continuation lines:
+
+.
+> bar
+baz
+> foo
+.
+<blockquote>
+<p>bar
+baz
+foo</p>
+</blockquote>
+.
+
+Laziness only applies to lines that are continuations of
+paragraphs. Lines containing characters or indentation that indicate
+block structure cannot be lazy.
+
+.
+> foo
+---
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+<hr />
+.
+
+.
+> - foo
+- bar
+.
+<blockquote>
+<ul>
+<li>foo</li>
+</ul>
+</blockquote>
+<ul>
+<li>bar</li>
+</ul>
+.
+
+.
+>     foo
+    bar
+.
+<blockquote>
+<pre><code>foo
+</code></pre>
+</blockquote>
+<pre><code>bar
+</code></pre>
+.
+
+.
+> ```
+foo
+```
+.
+<blockquote>
+<pre><code></code></pre>
+</blockquote>
+<p>foo</p>
+<pre><code></code></pre>
+.
+
+A block quote can be empty:
+
+.
+>
+.
+<blockquote>
+</blockquote>
+.
+
+.
+>
+>  
+> 
+.
+<blockquote>
+</blockquote>
+.
+
+A block quote can have initial or final blank lines:
+
+.
+>
+> foo
+>  
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+.
+
+A blank line always separates block quotes:
+
+.
+> foo
+
+> bar
+.
+<blockquote>
+<p>foo</p>
+</blockquote>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+(Most current Markdown implementations, including John Gruber's
+original `Markdown.pl`, will parse this example as a single block quote
+with two paragraphs.  But it seems better to allow the author to decide
+whether two block quotes or one are wanted.)
+
+Consecutiveness means that if we put these block quotes together,
+we get a single block quote:
+
+.
+> foo
+> bar
+.
+<blockquote>
+<p>foo
+bar</p>
+</blockquote>
+.
+
+To get a block quote with two paragraphs, use:
+
+.
+> foo
+>
+> bar
+.
+<blockquote>
+<p>foo</p>
+<p>bar</p>
+</blockquote>
+.
+
+Block quotes can interrupt paragraphs:
+
+.
+foo
+> bar
+.
+<p>foo</p>
+<blockquote>
+<p>bar</p>
+</blockquote>
+.
+
+In general, blank lines are not needed before or after block
+quotes:
+
+.
+> aaa
+***
+> bbb
+.
+<blockquote>
+<p>aaa</p>
+</blockquote>
+<hr />
+<blockquote>
+<p>bbb</p>
+</blockquote>
+.
+
+However, because of laziness, a blank line is needed between
+a block quote and a following paragraph:
+
+.
+> bar
+baz
+.
+<blockquote>
+<p>bar
+baz</p>
+</blockquote>
+.
+
+.
+> bar
+
+baz
+.
+<blockquote>
+<p>bar</p>
+</blockquote>
+<p>baz</p>
+.
+
+.
+> bar
+>
+baz
+.
+<blockquote>
+<p>bar</p>
+</blockquote>
+<p>baz</p>
+.
+
+It is a consequence of the Laziness rule that any number
+of initial `>`s may be omitted on a continuation line of a
+nested block quote:
+
+.
+> > > foo
+bar
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>foo
+bar</p>
+</blockquote>
+</blockquote>
+</blockquote>
+.
+
+.
+>>> foo
+> bar
+>>baz
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>foo
+bar
+baz</p>
+</blockquote>
+</blockquote>
+</blockquote>
+.
+
+When including an indented code block in a block quote,
+remember that the [block quote marker](#block-quote-marker) includes
+both the `>` and a following space.  So *five spaces* are needed after
+the `>`:
+
+.
+>     code
+
+>    not code
+.
+<blockquote>
+<pre><code>code
+</code></pre>
+</blockquote>
+<blockquote>
+<p>not code</p>
+</blockquote>
+.
+
+
+## List items
+
+A [list marker](#list-marker) <a id="list-marker"></a> is a
+[bullet list marker](#bullet-list-marker) or an [ordered list
+marker](#ordered-list-marker).
+
+A [bullet list marker](#bullet-list-marker) <a id="bullet-list-marker"></a>
+is a `-`, `+`, or `*` character.
+
+An [ordered list marker](#ordered-list-marker) <a id="ordered-list-marker"></a>
+is a sequence of one of more digits (`0-9`), followed by either a
+`.` character or a `)` character.
+
+The following rules define [list items](#list-item):
+
+1.  **Basic case.**  If a sequence of lines *Ls* constitute a sequence of
+    blocks *Bs* starting with a non-space character and not separated
+    from each other by more than one blank line, and *M* is a list
+    marker *M* of width *W* followed by 0 < *N* < 5 spaces, then the result
+    of prepending *M* and the following spaces to the first line of
+    *Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
+    list item with *Bs* as its contents.  The type of the list item
+    (bullet or ordered) is determined by the type of its list marker.
+    If the list item is ordered, then it is also assigned a start
+    number, based on the ordered list marker.
+
+For example, let *Ls* be the lines
+
+.
+A paragraph
+with two lines.
+
+    indented code
+
+> A block quote.
+.
+<p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote>
+.
+
+And let *M* be the marker `1.`, and *N* = 2.  Then rule #1 says
+that the following is an ordered list item with start number 1,
+and the same contents as *Ls*:
+
+.
+1.  A paragraph
+    with two lines.
+
+        indented code
+
+    > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+The most important thing to notice is that the position of
+the text after the list marker determines how much indentation
+is needed in subsequent blocks in the list item.  If the list
+marker takes up two spaces, and there are three spaces between
+the list marker and the next nonspace character, then blocks
+must be indented five spaces in order to fall under the list
+item.
+
+Here are some examples showing how far content must be indented to be
+put under the list item:
+
+.
+- one
+
+ two
+.
+<ul>
+<li>one</li>
+</ul>
+<p>two</p>
+.
+
+.
+- one
+
+  two
+.
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+.
+
+.
+ -    one
+
+     two
+.
+<ul>
+<li>one</li>
+</ul>
+<pre><code> two
+</code></pre>
+.
+
+.
+ -    one
+
+      two
+.
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+.
+
+It is tempting to think of this in terms of columns:  the continuation
+blocks must be indented at least to the column of the first nonspace
+character after the list marker.  However, that is not quite right.
+The spaces after the list marker determine how much relative indentation
+is needed.  Which column this indentation reaches will depend on
+how the list item is embedded in other constructions, as shown by
+this example:
+
+.
+   > > 1.  one
+>>
+>>     two
+.
+<blockquote>
+<blockquote>
+<ol>
+<li><p>one</p>
+<p>two</p></li>
+</ol>
+</blockquote>
+</blockquote>
+.
+
+Here `two` occurs in the same column as the list marker `1.`,
+but is actually contained in the list item, because there is
+sufficent indentation after the last containing blockquote marker.
+
+The converse is also possible.  In the following example, the word `two`
+occurs far to the right of the initial text of the list item, `one`, but
+it is not considered part of the list item, because it is not indented
+far enough past the blockquote marker:
+
+.
+>>- one
+>>
+  >  > two
+.
+<blockquote>
+<blockquote>
+<ul>
+<li>one</li>
+</ul>
+<p>two</p>
+</blockquote>
+</blockquote>
+.
+
+A list item may not contain blocks that are separated by more than
+one blank line.  Thus, two blank lines will end a list, unless the
+two blanks are contained in a [fenced code block](#fenced-code-block).
+
+.
+- foo
+
+  bar
+
+- foo
+
+
+  bar
+
+- ```
+  foo
+
+
+  bar
+  ```
+.
+<ul>
+<li><p>foo</p>
+<p>bar</p></li>
+<li><p>foo</p></li>
+</ul>
+<p>bar</p>
+<ul>
+<li><pre><code>foo
+
+
+bar
+</code></pre></li>
+</ul>
+.
+
+A list item may contain any kind of block:
+
+.
+1.  foo
+
+    ```
+    bar
+    ```
+
+    baz
+
+    > bam
+.
+<ol>
+<li><p>foo</p>
+<pre><code>bar
+</code></pre>
+<p>baz</p>
+<blockquote>
+<p>bam</p>
+</blockquote></li>
+</ol>
+.
+
+2.  **Item starting with indented code.**  If a sequence of lines *Ls*
+    constitute a sequence of blocks *Bs* starting with an indented code
+    block and not separated from each other by more than one blank line,
+    and *M* is a list marker *M* of width *W* followed by
+    one space, then the result of prepending *M* and the following
+    space to the first line of *Ls*, and indenting subsequent lines of
+    *Ls* by *W + 1* spaces, is a list item with *Bs* as its contents.
+    If a line is empty, then it need not be indented.  The type of the
+    list item (bullet or ordered) is determined by the type of its list
+    marker.  If the list item is ordered, then it is also assigned a
+    start number, based on the ordered list marker.
+
+An indented code block will have to be indented four spaces beyond
+the edge of the region where text will be included in the list item.
+In the following case that is 6 spaces:
+
+.
+- foo
+
+      bar
+.
+<ul>
+<li><p>foo</p>
+<pre><code>bar
+</code></pre></li>
+</ul>
+.
+
+And in this case it is 11 spaces:
+
+.
+  10.  foo
+
+           bar
+.
+<ol start="10">
+<li><p>foo</p>
+<pre><code>bar
+</code></pre></li>
+</ol>
+.
+
+If the *first* block in the list item is an indented code block,
+then by rule #2, the contents must be indented *one* space after the
+list marker:
+
+.
+    indented code
+
+paragraph
+
+    more code
+.
+<pre><code>indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre>
+.
+
+.
+1.     indented code
+
+   paragraph
+
+       more code
+.
+<ol>
+<li><pre><code>indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre></li>
+</ol>
+.
+
+Note that an additional space indent is interpreted as space
+inside the code block:
+
+.
+1.      indented code
+
+   paragraph
+
+       more code
+.
+<ol>
+<li><pre><code> indented code
+</code></pre>
+<p>paragraph</p>
+<pre><code>more code
+</code></pre></li>
+</ol>
+.
+
+Note that rules #1 and #2 only apply to two cases:  (a) cases
+in which the lines to be included in a list item begin with a nonspace
+character, and (b) cases in which they begin with an indented code
+block.  In a case like the following, where the first block begins with
+a three-space indent, the rules do not allow us to form a list item by
+indenting the whole thing and prepending a list marker:
+
+.
+   foo
+
+bar
+.
+<p>foo</p>
+<p>bar</p>
+.
+
+.
+-    foo
+
+  bar
+.
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+.
+
+This is not a significant restriction, because when a block begins
+with 1-3 spaces indent, the indentation can always be removed without
+a change in interpretation, allowing rule #1 to be applied.  So, in
+the above case:
+
+.
+-  foo
+
+   bar
+.
+<ul>
+<li><p>foo</p>
+<p>bar</p></li>
+</ul>
+.
+
+
+3.  **Indentation.**  If a sequence of lines *Ls* constitutes a list item
+    according to rule #1 or #2, then the result of indenting each line
+    of *L* by 1-3 spaces (the same for each line) also constitutes a
+    list item with the same contents and attributes.  If a line is
+    empty, then it need not be indented.
+
+Indented one space:
+
+.
+ 1.  A paragraph
+     with two lines.
+
+         indented code
+
+     > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indented two spaces:
+
+.
+  1.  A paragraph
+      with two lines.
+
+          indented code
+
+      > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indented three spaces:
+
+.
+   1.  A paragraph
+       with two lines.
+
+           indented code
+
+       > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Four spaces indent gives a code block:
+
+.
+    1.  A paragraph
+        with two lines.
+
+            indented code
+
+        > A block quote.
+.
+<pre><code>1.  A paragraph
+    with two lines.
+
+        indented code
+
+    &gt; A block quote.
+</code></pre>
+.
+
+
+4.  **Laziness.**  If a string of lines *Ls* constitute a [list
+    item](#list-item) with contents *Bs*, then the result of deleting
+    some or all of the indentation from one or more lines in which the
+    next non-space character after the indentation is
+    [paragraph continuation text](#paragraph-continuation-text) is a
+    list item with the same contents and attributes.
+
+Here is an example with lazy continuation lines:
+
+.
+  1.  A paragraph
+with two lines.
+
+          indented code
+
+      > A block quote.
+.
+<ol>
+<li><p>A paragraph
+with two lines.</p>
+<pre><code>indented code
+</code></pre>
+<blockquote>
+<p>A block quote.</p>
+</blockquote></li>
+</ol>
+.
+
+Indentation can be partially deleted:
+
+.
+  1.  A paragraph
+    with two lines.
+.
+<ol>
+<li>A paragraph
+with two lines.</li>
+</ol>
+.
+
+These examples show how laziness can work in nested structures:
+
+.
+> 1. > Blockquote
+continued here.
+.
+<blockquote>
+<ol>
+<li><blockquote>
+<p>Blockquote
+continued here.</p>
+</blockquote></li>
+</ol>
+</blockquote>
+.
+
+.
+> 1. > Blockquote
+> continued here.
+.
+<blockquote>
+<ol>
+<li><blockquote>
+<p>Blockquote
+continued here.</p>
+</blockquote></li>
+</ol>
+</blockquote>
+.
+
+
+5.  **That's all.** Nothing that is not counted as a list item by rules
+    #1--4 counts as a [list item](#list-item).
+
+The rules for sublists follow from the general rules above.  A sublist
+must be indented the same number of spaces a paragraph would need to be
+in order to be included in the list item.
+
+So, in this case we need two spaces indent:
+
+.
+- foo
+  - bar
+    - baz
+.
+<ul>
+<li>foo
+<ul>
+<li>bar
+<ul>
+<li>baz</li>
+</ul></li>
+</ul></li>
+</ul>
+.
+
+One is not enough:
+
+.
+- foo
+ - bar
+  - baz
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+<li>baz</li>
+</ul>
+.
+
+Here we need four, because the list marker is wider:
+
+.
+10) foo
+    - bar
+.
+<ol start="10">
+<li>foo
+<ul>
+<li>bar</li>
+</ul></li>
+</ol>
+.
+
+Three is not enough:
+
+.
+10) foo
+   - bar
+.
+<ol start="10">
+<li>foo</li>
+</ol>
+<ul>
+<li>bar</li>
+</ul>
+.
+
+A list may be the first block in a list item:
+
+.
+- - foo
+.
+<ul>
+<li><ul>
+<li>foo</li>
+</ul></li>
+</ul>
+.
+
+.
+1. - 2. foo
+.
+<ol>
+<li><ul>
+<li><ol start="2">
+<li>foo</li>
+</ol></li>
+</ul></li>
+</ol>
+.
+
+A list item may be empty:
+
+.
+- foo
+-
+- bar
+.
+<ul>
+<li>foo</li>
+<li></li>
+<li>bar</li>
+</ul>
+.
+
+.
+-
+.
+<ul>
+<li></li>
+</ul>
+.
+
+### Motivation
+
+John Gruber's Markdown spec says the following about list items:
+
+1. "List markers typically start at the left margin, but may be indented
+   by up to three spaces. List markers must be followed by one or more
+   spaces or a tab."
+
+2. "To make lists look nice, you can wrap items with hanging indents....
+   But if you don't want to, you don't have to."
+
+3. "List items may consist of multiple paragraphs. Each subsequent
+   paragraph in a list item must be indented by either 4 spaces or one
+   tab."
+
+4. "It looks nice if you indent every line of the subsequent paragraphs,
+   but here again, Markdown will allow you to be lazy."
+
+5. "To put a blockquote within a list item, the blockquote's `>`
+   delimiters need to be indented."
+
+6. "To put a code block within a list item, the code block needs to be
+   indented twice — 8 spaces or two tabs."
+
+These rules specify that a paragraph under a list item must be indented
+four spaces (presumably, from the left margin, rather than the start of
+the list marker, but this is not said), and that code under a list item
+must be indented eight spaces instead of the usual four.  They also say
+that a block quote must be indented, but not by how much; however, the
+example given has four spaces indentation.  Although nothing is said
+about other kinds of block-level content, it is certainly reasonable to
+infer that *all* block elements under a list item, including other
+lists, must be indented four spaces.  This principle has been called the
+*four-space rule*.
+
+The four-space rule is clear and principled, and if the reference
+implementation `Markdown.pl` had followed it, it probably would have
+become the standard.  However, `Markdown.pl` allowed paragraphs and
+sublists to start with only two spaces indentation, at least on the
+outer level.  Worse, its behavior was inconsistent: a sublist of an
+outer-level list needed two spaces indentation, but a sublist of this
+sublist needed three spaces.  It is not surprising, then, that different
+implementations of Markdown have developed very different rules for
+determining what comes under a list item.  (Pandoc and python-Markdown,
+for example, stuck with Gruber's syntax description and the four-space
+rule, while discount, redcarpet, marked, PHP Markdown, and others
+followed `Markdown.pl`'s behavior more closely.)
+
+Unfortunately, given the divergences between implementations, there
+is no way to give a spec for list items that will be guaranteed not
+to break any existing documents.  However, the spec given here should
+correctly handle lists formatted with either the four-space rule or
+the more forgiving `Markdown.pl` behavior, provided they are laid out
+in a way that is natural for a human to read.
+
+The strategy here is to let the width and indentation of the list marker
+determine the indentation necessary for blocks to fall under the list
+item, rather than having a fixed and arbitrary number.  The writer can
+think of the body of the list item as a unit which gets indented to the
+right enough to fit the list marker (and any indentation on the list
+marker).  (The laziness rule, #4, then allows continuation lines to be
+unindented if needed.)
+
+This rule is superior, we claim, to any rule requiring a fixed level of
+indentation from the margin.  The four-space rule is clear but
+unnatural. It is quite unintuitive that
+
+``` markdown
+- foo
+
+  bar
+
+  - baz
+```
+
+should be parsed as two lists with an intervening paragraph,
+
+``` html
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul>
+```
+
+as the four-space rule demands, rather than a single list,
+
+``` html
+<ul>
+<li><p>foo</p>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul></li>
+</ul>
+```
+
+The choice of four spaces is arbitrary.  It can be learned, but it is
+not likely to be guessed, and it trips up beginners regularly.
+
+Would it help to adopt a two-space rule?  The problem is that such
+a rule, together with the rule allowing 1--3 spaces indentation of the
+initial list marker, allows text that is indented *less than* the
+original list marker to be included in the list item. For example,
+`Markdown.pl` parses
+
+``` markdown
+   - one
+
+  two
+```
+
+as a single list item, with `two` a continuation paragraph:
+
+``` html
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+```
+
+and similarly
+
+``` markdown
+>   - one
+>
+>  two
+```
+
+as
+
+``` html
+<blockquote>
+<ul>
+<li><p>one</p>
+<p>two</p></li>
+</ul>
+</blockquote>
+```
+
+This is extremely unintuitive.
+
+Rather than requiring a fixed indent from the margin, we could require
+a fixed indent (say, two spaces, or even one space) from the list marker (which
+may itself be indented).  This proposal would remove the last anomaly
+discussed.  Unlike the spec presented above, it would count the following
+as a list item with a subparagraph, even though the paragraph `bar`
+is not indented as far as the first paragraph `foo`:
+
+``` markdown
+ 10. foo
+
+   bar  
+```
+
+Arguably this text does read like a list item with `bar` as a subparagraph,
+which may count in favor of the proposal.  However, on this proposal indented
+code would have to be indented six spaces after the list marker.  And this
+would break a lot of existing Markdown, which has the pattern:
+
+``` markdown
+1.  foo
+
+        indented code
+```
+
+where the code is indented eight spaces.  The spec above, by contrast, will
+parse this text as expected, since the code block's indentation is measured
+from the beginning of `foo`.
+
+The one case that needs special treatment is a list item that *starts*
+with indented code.  How much indentation is required in that case, since
+we don't have a "first paragraph" to measure from?  Rule #2 simply stipulates
+that in such cases, we require one space indentation from the list marker
+(and then the normal four spaces for the indented code).  This will match the
+four-space rule in cases where the list marker plus its initial indentation
+takes four spaces (a common case), but diverge in other cases.
+
+## Lists
+
+A [list](#list) <a id="list"></a> is a sequence of one or more
+list items [of the same type](#of-the-same-type).  The list items
+may be separated by single [blank lines](#blank-line), but two
+blank lines end all containing lists.
+
+Two list items are [of the same type](#of-the-same-type)
+<a id="of-the-same-type"></a> if they begin with a [list
+marker](#list-marker) of the same type.  Two list markers are of the
+same type if (a) they are bullet list markers using the same character
+(`-`, `+`, or `*`) or (b) they are ordered list numbers with the same
+delimiter (either `.` or `)`).
+
+A list is an [ordered list](#ordered-list) <a id="ordered-list"></a>
+if its constituent list items begin with
+[ordered list markers](#ordered-list-marker), and a [bullet
+list](#bullet-list) <a id="bullet-list"></a> if its constituent list
+items begin with [bullet list markers](#bullet-list-marker).
+
+The [start number](#start-number) <a id="start-number"></a>
+of an [ordered list](#ordered-list) is determined by the list number of
+its initial list item.  The numbers of subsequent list items are
+disregarded.
+
+A list is [loose](#loose) if it any of its constituent list items are
+separated by blank lines, or if any of its constituent list items
+directly contain two block-level elements with a blank line between
+them.  Otherwise a list is [tight](#tight).  (The difference in HTML output
+is that paragraphs in a loose with are wrapped in `<p>` tags, while
+paragraphs in a tight list are not.)
+
+Changing the bullet or ordered list delimiter starts a new list:
+
+.
+- foo
+- bar
++ baz
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+</ul>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+.
+1. foo
+2. bar
+3) baz
+.
+<ol>
+<li>foo</li>
+<li>bar</li>
+</ol>
+<ol start="3">
+<li>baz</li>
+</ol>
+.
+
+There can be blank lines between items, but two blank lines end
+a list:
+
+.
+- foo
+
+- bar
+
+
+- baz
+.
+<ul>
+<li><p>foo</p></li>
+<li><p>bar</p></li>
+</ul>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+As illustrated above in the section on [list items](#list-item),
+two blank lines between blocks *within* a list item will also end a
+list:
+
+.
+- foo
+
+
+  bar
+- baz
+.
+<ul>
+<li>foo</li>
+</ul>
+<p>bar</p>
+<ul>
+<li>baz</li>
+</ul>
+.
+
+Indeed, two blank lines will end *all* containing lists:
+
+.
+- foo
+  - bar
+    - baz
+
+
+      bim
+.
+<ul>
+<li>foo
+<ul>
+<li>bar
+<ul>
+<li>baz</li>
+</ul></li>
+</ul></li>
+</ul>
+<pre><code>  bim
+</code></pre>
+.
+
+Thus, two blank lines can be used to separate consecutive lists of
+the same type, or to separate a list from an indented code block
+that would otherwise be parsed as a subparagraph of the final list
+item:
+
+.
+- foo
+- bar
+
+
+- baz
+- bim
+.
+<ul>
+<li>foo</li>
+<li>bar</li>
+</ul>
+<ul>
+<li>baz</li>
+<li>bim</li>
+</ul>
+.
+
+.
+-   foo
+
+    notcode
+
+-   foo
+
+
+    code
+.
+<ul>
+<li><p>foo</p>
+<p>notcode</p></li>
+<li><p>foo</p></li>
+</ul>
+<pre><code>code
+</code></pre>
+.
+
+List items need not be indented to the same level.  The following
+list items will be treated as items at the same list level,
+since none is indented enough to belong to the previous list
+item:
+
+.
+- a
+ - b
+  - c
+   - d
+  - e
+ - f
+- g
+.
+<ul>
+<li>a</li>
+<li>b</li>
+<li>c</li>
+<li>d</li>
+<li>e</li>
+<li>f</li>
+<li>g</li>
+</ul>
+.
+
+This is a loose list, because there is a blank line between
+two of the list items:
+
+.
+- a
+- b
+
+- c
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p></li>
+<li><p>c</p></li>
+</ul>
+.
+
+So is this, with a empty second item:
+
+.
+* a
+*
+
+* c
+.
+<ul>
+<li><p>a</p></li>
+<li></li>
+<li><p>c</p></li>
+</ul>
+.
+
+These are loose lists, even though there is no space between the items,
+because one of the items directly contains two block-level elements
+with a blank line between them:
+
+.
+- a
+- b
+
+  c
+- d
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p>
+<p>c</p></li>
+<li><p>d</p></li>
+</ul>
+.
+
+.
+- a
+- b
+
+  [ref]: /url
+- d
+.
+<ul>
+<li><p>a</p></li>
+<li><p>b</p></li>
+<li><p>d</p></li>
+</ul>
+.
+
+This is a tight list, because the blank lines are in a code block:
+
+.
+- a
+- ```
+  b
+
+
+  ```
+- c
+.
+<ul>
+<li>a</li>
+<li><pre><code>b
+
+
+</code></pre></li>
+<li>c</li>
+</ul>
+.
+
+This is a tight list, because the blank line is between two
+paragraphs of a sublist.  So the inner list is loose while
+the other list is tight:
+
+.
+- a
+  - b
+
+    c
+- d
+.
+<ul>
+<li>a
+<ul>
+<li><p>b</p>
+<p>c</p></li>
+</ul></li>
+<li>d</li>
+</ul>
+.
+
+This is a tight list, because the blank line is inside the
+block quote:
+
+.
+* a
+  > b
+  >
+* c
+.
+<ul>
+<li>a
+<blockquote>
+<p>b</p>
+</blockquote></li>
+<li>c</li>
+</ul>
+.
+
+This list is tight, because the consecutive block elements
+are not separated by blank lines:
+
+.
+- a
+  > b
+  ```
+  c
+  ```
+- d
+.
+<ul>
+<li>a
+<blockquote>
+<p>b</p>
+</blockquote>
+<pre><code>c
+</code></pre></li>
+<li>d</li>
+</ul>
+.
+
+A single-paragraph list is tight:
+
+.
+- a
+.
+<ul>
+<li>a</li>
+</ul>
+.
+
+.
+- a
+  - b
+.
+<ul>
+<li>a
+<ul>
+<li>b</li>
+</ul></li>
+</ul>
+.
+
+Here the outer list is loose, the inner list tight:
+
+.
+* foo
+  * bar
+
+  baz
+.
+<ul>
+<li><p>foo</p>
+<ul>
+<li>bar</li>
+</ul>
+<p>baz</p></li>
+</ul>
+.
+
+.
+- a
+  - b
+  - c
+
+- d
+  - e
+  - f
+.
+<ul>
+<li><p>a</p>
+<ul>
+<li>b</li>
+<li>c</li>
+</ul></li>
+<li><p>d</p>
+<ul>
+<li>e</li>
+<li>f</li>
+</ul></li>
+</ul>
+.
+
+# Inlines
+
+Inlines are parsed sequentially from the beginning of the character
+stream to the end (left to right, in left-to-right languages).
+Thus, for example, in
+
+.
+`hi`lo`
+.
+<p><code>hi</code>lo`</p>
+.
+
+`hi` is parsed as code, leaving the backtick at the end as a literal
+backtick.
+
+## Backslash escapes
+
+Any ASCII punctuation character may be backslash-escaped:
+
+.
+\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
+.
+<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
+.
+
+Backslashes before other characters are treated as literal
+backslashes:
+
+.
+\→\A\a\ \3\φ\«
+.
+<p>\   \A\a\ \3\φ\«</p>
+.
+
+Escaped characters are treated as regular characters and do
+not have their usual Markdown meanings:
+
+.
+\*not emphasized*
+\<br/> not a tag
+\[not a link](/foo)
+\`not code`
+1\. not a list
+\* not a list
+\# not a header
+\[foo]: /url "not a reference"
+.
+<p>*not emphasized*
+&lt;br/&gt; not a tag
+[not a link](/foo)
+`not code`
+1. not a list
+* not a list
+# not a header
+[foo]: /url &quot;not a reference&quot;</p>
+.
+
+If a backslash is itself escaped, the following character is not:
+
+.
+\\*emphasis*
+.
+<p>\<em>emphasis</em></p>
+.
+
+A backslash at the end of the line is a hard line break:
+
+.
+foo\
+bar
+.
+<p>foo<br />
+bar</p>
+.
+
+Backslash escapes do not work in code blocks, code spans, autolinks, or
+raw HTML:
+
+.
+`` \[\` ``
+.
+<p><code>\[\`</code></p>
+.
+
+.
+    \[\]
+.
+<pre><code>\[\]
+</code></pre>
+.
+
+.
+~~~
+\[\]
+~~~
+.
+<pre><code>\[\]
+</code></pre>
+.
+
+.
+<http://google.com?find=\*>
+.
+<p><a href="http://google.com?find=%5C*">http://google.com?find=\*</a></p>
+.
+
+.
+<a href="/bar\/)">
+.
+<p><a href="/bar\/)"></p>
+.
+
+But they work in all other contexts, including URLs and link titles,
+link references, and info strings in [fenced code
+blocks](#fenced-code-block):
+
+.
+[foo](/bar\* "ti\*tle")
+.
+<p><a href="/bar*" title="ti*tle">foo</a></p>
+.
+
+.
+[foo]
+
+[foo]: /bar\* "ti\*tle"
+.
+<p><a href="/bar*" title="ti*tle">foo</a></p>
+.
+
+.
+``` foo\+bar
+foo
+```
+.
+<pre><code class="language-foo+bar">foo
+</code></pre>
+.
+
+
+## Entities
+
+With the goal of making this standard as HTML-agnostic as possible, all HTML valid HTML Entities in any
+context are recognized as such and converted into their actual values (i.e. the UTF8 characters representing
+the entity itself) before they are stored in the AST.
+
+This allows implementations that target HTML output to trivially escape the entities when generating HTML,
+and simplifies the job of implementations targetting other languages, as these will only need to handle the
+UTF8 chars and need not be HTML-entity aware.
+
+[Named entities](#name-entities) <a id="named-entities"></a> consist of `&`
++ any of the valid HTML5 entity names + `;`. The [following document](http://www.whatwg.org/specs/web-apps/current-work/multipage/entities.json)
+is used as an authoritative source of the valid entity names and their corresponding codepoints.
+
+Conforming implementations that target Markdown don't need to generate entities for all the valid
+named entities that exist, with the exception of `"` (`&quot;`), `&` (`&amp;`), `<` (`&lt;`) and `>` (`&gt;`),
+which always need to be written as entities for security reasons.
+
+.
+&nbsp; &amp; &copy; &AElig; &Dcaron; &frac34; &HilbertSpace; &DifferentialD; &ClockwiseContourIntegral;
+.
+<p>  &amp; © Æ Ď ¾ ℋ ⅆ ∲</p>
+.
+
+[Decimal entities](#decimal-entities) <a id="decimal-entities"></a>
+consist of `&#` + a string of 1--8 arabic digits + `;`. Again, these entities need to be recognised
+and tranformed into their corresponding UTF8 codepoints. Invalid Unicode codepoints will be written
+as the "unknown codepoint" character (`0xFFFD`)
+
+.
+&#35; &#1234; &#992; &#98765432;
+.
+<p># Ӓ Ϡ �</p>
+.
+
+[Hexadecimal entities](#hexadecimal-entities) <a id="hexadecimal-entities"></a>
+consist of `&#` + either `X` or `x` + a string of 1-8 hexadecimal digits
++ `;`. They will also be parsed and turned into their corresponding UTF8 values in the AST.
+
+.
+&#X22; &#XD06; &#xcab;
+.
+<p>&quot; ആ ಫ</p>
+.
+
+Here are some nonentities:
+
+.
+&nbsp &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
+.
+<p>&amp;nbsp &amp;x; &amp;#; &amp;#x; &amp;ThisIsWayTooLongToBeAnEntityIsntIt; &amp;hi?;</p>
+.
+
+Although HTML5 does accept some entities without a trailing semicolon
+(such as `&copy`), these are not recognized as entities here, because it makes the grammar too ambiguous:
+
+.
+&copy
+.
+<p>&amp;copy</p>
+.
+
+Strings that are not on the list of HTML5 named entities are not recognized as entities either:
+
+.
+&MadeUpEntity;
+.
+<p>&amp;MadeUpEntity;</p>
+.
+
+Entities are recognized in any context besides code spans or
+code blocks, including raw HTML, URLs, [link titles](#link-title), and
+[fenced code block](#fenced-code-block) info strings:
+
+.
+<a href="&ouml;&ouml;.html">
+.
+<p><a href="&ouml;&ouml;.html"></p>
+.
+
+.
+[foo](/f&ouml;&ouml; "f&ouml;&ouml;")
+.
+<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
+.
+
+.
+[foo]
+
+[foo]: /f&ouml;&ouml; "f&ouml;&ouml;"
+.
+<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
+.
+
+.
+``` f&ouml;&ouml;
+foo
+```
+.
+<pre><code class="language-föö">foo
+</code></pre>
+.
+
+Entities are treated as literal text in code spans and code blocks:
+
+.
+`f&ouml;&ouml;`
+.
+<p><code>f&amp;ouml;&amp;ouml;</code></p>
+.
+
+.
+    f&ouml;f&ouml;
+.
+<pre><code>f&amp;ouml;f&amp;ouml;
+</code></pre>
+.
+
+## Code span
+
+A [backtick string](#backtick-string) <a id="backtick-string"></a>
+is a string of one or more backtick characters (`` ` ``) that is neither
+preceded nor followed by a backtick.
+
+A code span begins with a backtick string and ends with a backtick
+string of equal length.  The contents of the code span are the
+characters between the two backtick strings, with leading and trailing
+spaces and newlines removed, and consecutive spaces and newlines
+collapsed to single spaces.
+
+This is a simple code span:
+
+.
+`foo`
+.
+<p><code>foo</code></p>
+.
+
+Here two backticks are used, because the code contains a backtick.
+This example also illustrates stripping of leading and trailing spaces:
+
+.
+`` foo ` bar  ``
+.
+<p><code>foo ` bar</code></p>
+.
+
+This example shows the motivation for stripping leading and trailing
+spaces:
+
+.
+` `` `
+.
+<p><code>``</code></p>
+.
+
+Newlines are treated like spaces:
+
+.
+``
+foo
+``
+.
+<p><code>foo</code></p>
+.
+
+Interior spaces and newlines are collapsed into single spaces, just
+as they would be by a browser:
+
+.
+`foo   bar
+  baz`
+.
+<p><code>foo bar baz</code></p>
+.
+
+Q: Why not just leave the spaces, since browsers will collapse them
+anyway?  A:  Because we might be targeting a non-HTML format, and we
+shouldn't rely on HTML-specific rendering assumptions.
+
+(Existing implementations differ in their treatment of internal
+spaces and newlines.  Some, including `Markdown.pl` and
+`showdown`, convert an internal newline into a `<br />` tag.
+But this makes things difficult for those who like to hard-wrap
+their paragraphs, since a line break in the midst of a code
+span will cause an unintended line break in the output.  Others
+just leave internal spaces as they are, which is fine if only
+HTML is being targeted.)
+
+.
+`foo `` bar`
+.
+<p><code>foo `` bar</code></p>
+.
+
+Note that backslash escapes do not work in code spans. All backslashes
+are treated literally:
+
+.
+`foo\`bar`
+.
+<p><code>foo\</code>bar`</p>
+.
+
+Backslash escapes are never needed, because one can always choose a
+string of *n* backtick characters as delimiters, where the code does
+not contain any strings of exactly *n* backtick characters.
+
+Code span backticks have higher precedence than any other inline
+constructs except HTML tags and autolinks.  Thus, for example, this is
+not parsed as emphasized text, since the second `*` is part of a code
+span:
+
+.
+*foo`*`
+.
+<p>*foo<code>*</code></p>
+.
+
+And this is not parsed as a link:
+
+.
+[not a `link](/foo`)
+.
+<p>[not a <code>link](/foo</code>)</p>
+.
+
+But this is a link:
+
+.
+<http://foo.bar.`baz>`
+.
+<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
+.
+
+And this is an HTML tag:
+
+.
+<a href="`">`
+.
+<p><a href="`">`</p>
+.
+
+When a backtick string is not closed by a matching backtick string,
+we just have literal backticks:
+
+.
+```foo``
+.
+<p>```foo``</p>
+.
+
+.
+`foo
+.
+<p>`foo</p>
+.
+
+## Emphasis and strong emphasis
+
+John Gruber's original [Markdown syntax
+description](http://daringfireball.net/projects/markdown/syntax#em) says:
+
+> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML
+> `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML `<strong>`
+> tag.
+
+This is enough for most users, but these rules leave much undecided,
+especially when it comes to nested emphasis.  The original
+`Markdown.pl` test suite makes it clear that triple `***` and
+`___` delimiters can be used for strong emphasis, and most
+implementations have also allowed the following patterns:
+
+``` markdown
+***strong emph***
+***strong** in emph*
+***emph* in strong**
+**in strong *emph***
+*in emph **strong***
+```
+
+The following patterns are less widely supported, but the intent
+is clear and they are useful (especially in contexts like bibliography
+entries):
+
+``` markdown
+*emph *with emph* in it*
+**strong **with strong** in it**
+```
+
+Many implementations have also restricted intraword emphasis to
+the `*` forms, to avoid unwanted emphasis in words containing
+internal underscores.  (It is best practice to put these in code
+spans, but users often do not.)
+
+``` markdown
+internal emphasis: foo*bar*baz
+no emphasis: foo_bar_baz
+```
+
+The following rules capture all of these patterns, while allowing
+for efficient parsing strategies that do not backtrack:
+
+1.  A single `*` character [can open emphasis](#can-open-emphasis)
+    <a id="can-open-emphasis"></a> iff
+
+    (a) it is not part of a sequence of four or more unescaped `*`s,
+    (b) it is not followed by whitespace, and
+    (c) either it is not followed by a `*` character or it is
+        followed immediately by strong emphasis.
+
+2.  A single `_` character [can open emphasis](#can-open-emphasis) iff
+
+    (a) it is not part of a sequence of four or more unescaped `_`s,
+    (b) it is not followed by whitespace,
+    (c) it is not preceded by an ASCII alphanumeric character, and
+    (d) either it is not followed by a `_` character or it is
+        followed immediately by strong emphasis.
+
+3.  A single `*` character [can close emphasis](#can-close-emphasis)
+    <a id="can-close-emphasis"></a> iff
+
+    (a) it is not part of a sequence of four or more unescaped `*`s, and
+    (b) it is not preceded by whitespace.
+
+4.  A single `_` character [can close emphasis](#can-close-emphasis) iff
+
+    (a) it is not part of a sequence of four or more unescaped `_`s,
+    (b) it is not preceded by whitespace, and
+    (c) it is not followed by an ASCII alphanumeric character.
+
+5.  A double `**` [can open strong emphasis](#can-open-strong-emphasis)
+    <a id="can-open-strong-emphasis" ></a> iff
+
+    (a) it is not part of a sequence of four or more unescaped `*`s,
+    (b) it is not followed by whitespace, and
+    (c) either it is not followed by a `*` character or it is
+        followed immediately by emphasis.
+
+6.  A double `__` [can open strong emphasis](#can-open-strong-emphasis)
+    iff
+
+    (a) it is not part of a sequence of four or more unescaped `_`s,
+    (b) it is not followed by whitespace, and
+    (c) it is not preceded by an ASCII alphanumeric character, and
+    (d) either it is not followed by a `_` character or it is
+        followed immediately by emphasis.
+
+7.  A double `**` [can close strong emphasis](#can-close-strong-emphasis)
+    <a id="can-close-strong-emphasis" ></a> iff
+
+    (a) it is not part of a sequence of four or more unescaped `*`s, and
+    (b) it is not preceded by whitespace.
+
+8.  A double `__` [can close strong emphasis](#can-close-strong-emphasis)
+    iff
+
+    (a) it is not part of a sequence of four or more unescaped `_`s,
+    (b) it is not preceded by whitespace, and
+    (c) it is not followed by an ASCII alphanumeric character.
+
+9.  Emphasis begins with a delimiter that [can open
+    emphasis](#can-open-emphasis) and includes inlines parsed
+    sequentially until a delimiter that [can close
+    emphasis](#can-close-emphasis), and that uses the same
+    character (`_` or `*`) as the opening delimiter, is reached.
+
+10. Strong emphasis begins with a delimiter that [can open strong
+    emphasis](#can-open-strong-emphasis) and includes inlines parsed
+    sequentially until a delimiter that [can close strong
+    emphasis](#can-close-strong-emphasis), and that uses the
+    same character (`_` or `*`) as the opening delimiter, is reached.
+
+These rules can be illustrated through a series of examples.
+
+Simple emphasis:
+
+.
+*foo bar*
+.
+<p><em>foo bar</em></p>
+.
+
+.
+_foo bar_
+.
+<p><em>foo bar</em></p>
+.
+
+Simple strong emphasis:
+
+.
+**foo bar**
+.
+<p><strong>foo bar</strong></p>
+.
+
+.
+__foo bar__
+.
+<p><strong>foo bar</strong></p>
+.
+
+Emphasis can continue over line breaks:
+
+.
+*foo
+bar*
+.
+<p><em>foo
+bar</em></p>
+.
+
+.
+_foo
+bar_
+.
+<p><em>foo
+bar</em></p>
+.
+
+.
+**foo
+bar**
+.
+<p><strong>foo
+bar</strong></p>
+.
+
+.
+__foo
+bar__
+.
+<p><strong>foo
+bar</strong></p>
+.
+
+Emphasis can contain other inline constructs:
+
+.
+*foo [bar](/url)*
+.
+<p><em>foo <a href="/url">bar</a></em></p>
+.
+
+.
+_foo [bar](/url)_
+.
+<p><em>foo <a href="/url">bar</a></em></p>
+.
+
+.
+**foo [bar](/url)**
+.
+<p><strong>foo <a href="/url">bar</a></strong></p>
+.
+
+.
+__foo [bar](/url)__
+.
+<p><strong>foo <a href="/url">bar</a></strong></p>
+.
+
+Symbols contained in other inline constructs will not
+close emphasis:
+
+.
+*foo [bar*](/url)
+.
+<p>*foo <a href="/url">bar*</a></p>
+.
+
+.
+_foo [bar_](/url)
+.
+<p>_foo <a href="/url">bar_</a></p>
+.
+
+.
+**<a href="**">
+.
+<p>**<a href="**"></p>
+.
+
+.
+__<a href="__">
+.
+<p>__<a href="__"></p>
+.
+
+.
+*a `*`*
+.
+<p><em>a <code>*</code></em></p>
+.
+
+.
+_a `_`_
+.
+<p><em>a <code>_</code></em></p>
+.
+
+.
+**a<http://foo.bar?q=**>
+.
+<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p>
+.
+
+.
+__a<http://foo.bar?q=__>
+.
+<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p>
+.
+
+This is not emphasis, because the opening delimiter is
+followed by white space:
+
+.
+and * foo bar*
+.
+<p>and * foo bar*</p>
+.
+
+.
+_ foo bar_
+.
+<p>_ foo bar_</p>
+.
+
+.
+and ** foo bar**
+.
+<p>and ** foo bar**</p>
+.
+
+.
+__ foo bar__
+.
+<p>__ foo bar__</p>
+.
+
+This is not emphasis, because the closing delimiter is
+preceded by white space:
+
+.
+and *foo bar *
+.
+<p>and *foo bar *</p>
+.
+
+.
+and _foo bar _
+.
+<p>and _foo bar _</p>
+.
+
+.
+and **foo bar **
+.
+<p>and **foo bar **</p>
+.
+
+.
+and __foo bar __
+.
+<p>and __foo bar __</p>
+.
+
+The rules imply that a sequence of four or more unescaped `*` or
+`_` characters will always be parsed as a literal string:
+
+.
+****hi****
+.
+<p>****hi****</p>
+.
+
+.
+_____hi_____
+.
+<p>_____hi_____</p>
+.
+
+.
+Sign here: _________
+.
+<p>Sign here: _________</p>
+.
+
+The rules also imply that there can be no empty emphasis or strong
+emphasis:
+
+.
+** is not an empty emphasis
+.
+<p>** is not an empty emphasis</p>
+.
+
+.
+**** is not an empty strong emphasis
+.
+<p>**** is not an empty strong emphasis</p>
+.
+
+To include `*` or `_` in emphasized sections, use backslash escapes
+or code spans:
+
+.
+*here is a \**
+.
+<p><em>here is a *</em></p>
+.
+
+.
+__this is a double underscore (`__`)__
+.
+<p><strong>this is a double underscore (<code>__</code>)</strong></p>
+.
+
+`*` delimiters allow intra-word emphasis; `_` delimiters do not:
+
+.
+foo*bar*baz
+.
+<p>foo<em>bar</em>baz</p>
+.
+
+.
+foo_bar_baz
+.
+<p>foo_bar_baz</p>
+.
+
+.
+foo__bar__baz
+.
+<p>foo__bar__baz</p>
+.
+
+.
+_foo_bar_baz_
+.
+<p><em>foo_bar_baz</em></p>
+.
+
+.
+11*15*32
+.
+<p>11<em>15</em>32</p>
+.
+
+.
+11_15_32
+.
+<p>11_15_32</p>
+.
+
+Internal underscores will be ignored in underscore-delimited
+emphasis:
+
+.
+_foo_bar_baz_
+.
+<p><em>foo_bar_baz</em></p>
+.
+
+.
+__foo__bar__baz__
+.
+<p><strong>foo__bar__baz</strong></p>
+.
+
+The rules are sufficient for the following nesting patterns:
+
+.
+***foo bar***
+.
+<p><strong><em>foo bar</em></strong></p>
+.
+
+.
+___foo bar___
+.
+<p><strong><em>foo bar</em></strong></p>
+.
+
+.
+***foo** bar*
+.
+<p><em><strong>foo</strong> bar</em></p>
+.
+
+.
+___foo__ bar_
+.
+<p><em><strong>foo</strong> bar</em></p>
+.
+
+.
+***foo* bar**
+.
+<p><strong><em>foo</em> bar</strong></p>
+.
+
+.
+___foo_ bar__
+.
+<p><strong><em>foo</em> bar</strong></p>
+.
+
+.
+*foo **bar***
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+_foo __bar___
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+**foo *bar***
+.
+<p><strong>foo <em>bar</em></strong></p>
+.
+
+.
+__foo _bar___
+.
+<p><strong>foo <em>bar</em></strong></p>
+.
+
+.
+*foo **bar***
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+_foo __bar___
+.
+<p><em>foo <strong>bar</strong></em></p>
+.
+
+.
+*foo *bar* baz*
+.
+<p><em>foo <em>bar</em> baz</em></p>
+.
+
+.
+_foo _bar_ baz_
+.
+<p><em>foo <em>bar</em> baz</em></p>
+.
+
+.
+**foo **bar** baz**
+.
+<p><strong>foo <strong>bar</strong> baz</strong></p>
+.
+
+.
+__foo __bar__ baz__
+.
+<p><strong>foo <strong>bar</strong> baz</strong></p>
+.
+
+.
+*foo **bar** baz*
+.
+<p><em>foo <strong>bar</strong> baz</em></p>
+.
+
+.
+_foo __bar__ baz_
+.
+<p><em>foo <strong>bar</strong> baz</em></p>
+.
+
+.
+**foo *bar* baz**
+.
+<p><strong>foo <em>bar</em> baz</strong></p>
+.
+
+.
+__foo _bar_ baz__
+.
+<p><strong>foo <em>bar</em> baz</strong></p>
+.
+
+Note that you cannot nest emphasis directly inside emphasis
+using the same delimeter, or strong emphasis directly inside
+strong emphasis:
+
+.
+**foo**
+.
+<p><strong>foo</strong></p>
+.
+
+.
+****foo****
+.
+<p>****foo****</p>
+.
+
+For these nestings, you need to switch delimiters:
+
+.
+*_foo_*
+.
+<p><em><em>foo</em></em></p>
+.
+
+.
+**__foo__**
+.
+<p><strong><strong>foo</strong></strong></p>
+.
+
+Note that a `*` followed by a `*` can close emphasis, and
+a `**` followed by a `*` can close strong emphasis (and
+similarly for `_` and `__`):
+
+.
+*foo**
+.
+<p><em>foo</em>*</p>
+.
+
+.
+*foo *bar**
+.
+<p><em>foo <em>bar</em></em></p>
+.
+
+.
+**foo***
+.
+<p><strong>foo</strong>*</p>
+.
+
+.
+***foo* bar***
+.
+<p><strong><em>foo</em> bar</strong>*</p>
+.
+
+.
+***foo** bar***
+.
+<p><em><strong>foo</strong> bar</em>**</p>
+.
+
+The following contains no strong emphasis, because the opening
+delimiter is closed by the first `*` before `bar`:
+
+.
+*foo**bar***
+.
+<p><em>foo</em><em>bar</em>**</p>
+.
+
+However, a string of four or more `****` can never close emphasis:
+
+.
+*foo****
+.
+<p>*foo****</p>
+.
+
+Note that there are some asymmetries here:
+
+.
+*foo**
+
+**foo*
+.
+<p><em>foo</em>*</p>
+<p>**foo*</p>
+.
+
+.
+*foo *bar**
+
+**foo* bar*
+.
+<p><em>foo <em>bar</em></em></p>
+<p>**foo* bar*</p>
+.
+
+More cases with mismatched delimiters:
+
+.
+**foo* bar*
+.
+<p>**foo* bar*</p>
+.
+
+.
+*bar***
+.
+<p><em>bar</em>**</p>
+.
+
+.
+***foo*
+.
+<p>***foo*</p>
+.
+
+.
+**bar***
+.
+<p><strong>bar</strong>*</p>
+.
+
+.
+***foo**
+.
+<p>***foo**</p>
+.
+
+.
+***foo *bar*
+.
+<p>***foo <em>bar</em></p>
+.
+
+## Links
+
+A link contains a [link label](#link-label) (the visible text),
+a [destination](#destination) (the URI that is the link destination),
+and optionally a [link title](#link-title).  There are two basic kinds
+of links in Markdown.  In [inline links](#inline-links) the destination
+and title are given immediately after the label.  In [reference
+links](#reference-links) the destination and title are defined elsewhere
+in the document.
+
+A [link label](#link-label) <a id="link-label"></a>  consists of
+
+- an opening `[`, followed by
+- zero or more backtick code spans, autolinks, HTML tags, link labels,
+  backslash-escaped ASCII punctuation characters, or non-`]` characters,
+  followed by
+- a closing `]`.
+
+These rules are motivated by the following intuitive ideas:
+
+- A link label is a container for inline elements.
+- The square brackets bind more tightly than emphasis markers,
+  but less tightly than `<>` or `` ` ``.
+- Link labels may contain material in matching square brackets.
+
+A [link destination](#link-destination) <a id="link-destination"></a>
+consists of either
+
+- a sequence of zero or more characters between an opening `<` and a
+  closing `>` that contains no line breaks or unescaped `<` or `>`
+  characters, or
+
+- a nonempty sequence of characters that does not include
+  ASCII space or control characters, and includes parentheses
+  only if (a) they are backslash-escaped or (b) they are part of
+  a balanced pair of unescaped parentheses that is not itself
+  inside a balanced pair of unescaped paretheses.
+
+A [link title](#link-title) <a id="link-title"></a>  consists of either
+
+- a sequence of zero or more characters between straight double-quote
+  characters (`"`), including a `"` character only if it is
+  backslash-escaped, or
+
+- a sequence of zero or more characters between straight single-quote
+  characters (`'`), including a `'` character only if it is
+  backslash-escaped, or
+
+- a sequence of zero or more characters between matching parentheses
+  (`(...)`), including a `)` character only if it is backslash-escaped.
+
+An [inline link](#inline-link) <a id="inline-link"></a>
+consists of a [link label](#link-label) followed immediately
+by a left parenthesis `(`, optional whitespace,
+an optional [link destination](#link-destination),
+an optional [link title](#link-title) separated from the link
+destination by whitespace, optional whitespace, and a right
+parenthesis `)`.  The link's text consists of the label (excluding
+the enclosing square brackets) parsed as inlines.  The link's
+URI consists of the link destination, excluding enclosing `<...>` if
+present, with backslash-escapes in effect as described above.  The
+link's title consists of the link title, excluding its enclosing
+delimiters, with backslash-escapes in effect as described above.
+
+Here is a simple inline link:
+
+.
+[link](/uri "title")
+.
+<p><a href="/uri" title="title">link</a></p>
+.
+
+The title may be omitted:
+
+.
+[link](/uri)
+.
+<p><a href="/uri">link</a></p>
+.
+
+Both the title and the destination may be omitted:
+
+.
+[link]()
+.
+<p><a href="">link</a></p>
+.
+
+.
+[link](<>)
+.
+<p><a href="">link</a></p>
+.
+
+
+If the destination contains spaces, it must be enclosed in pointy
+braces:
+
+.
+[link](/my uri)
+.
+<p>[link](/my uri)</p>
+.
+
+.
+[link](</my uri>)
+.
+<p><a href="/my%20uri">link</a></p>
+.
+
+The destination cannot contain line breaks, even with pointy braces:
+
+.
+[link](foo
+bar)
+.
+<p>[link](foo
+bar)</p>
+.
+
+One level of balanced parentheses is allowed without escaping:
+
+.
+[link]((foo)and(bar))
+.
+<p><a href="(foo)and(bar)">link</a></p>
+.
+
+However, if you have parentheses within parentheses, you need to escape
+or use the `<...>` form:
+
+.
+[link](foo(and(bar)))
+.
+<p>[link](foo(and(bar)))</p>
+.
+
+.
+[link](foo(and\(bar\)))
+.
+<p><a href="foo(and(bar))">link</a></p>
+.
+
+.
+[link](<foo(and(bar))>)
+.
+<p><a href="foo(and(bar))">link</a></p>
+.
+
+Parentheses and other symbols can also be escaped, as usual
+in Markdown:
+
+.
+[link](foo\)\:)
+.
+<p><a href="foo):">link</a></p>
+.
+
+URL-escaping and should be left alone inside the destination, as all URL-escaped characters
+are also valid URL characters. HTML entities in the destination will be parsed into their UTF8
+codepoints, as usual, and optionally URL-escaped when written as HTML.
+
+.
+[link](foo%20b&auml;)
+.
+<p><a href="foo%20b%C3%A4">link</a></p>
+.
+
+Note that, because titles can often be parsed as destinations,
+if you try to omit the destination and keep the title, you'll
+get unexpected results:
+
+.
+[link]("title")
+.
+<p><a href="%22title%22">link</a></p>
+.
+
+Titles may be in single quotes, double quotes, or parentheses:
+
+.
+[link](/url "title")
+[link](/url 'title')
+[link](/url (title))
+.
+<p><a href="/url" title="title">link</a>
+<a href="/url" title="title">link</a>
+<a href="/url" title="title">link</a></p>
+.
+
+Backslash escapes and entities may be used in titles:
+
+.
+[link](/url "title \"&quot;")
+.
+<p><a href="/url" title="title &quot;&quot;">link</a></p>
+.
+
+Nested balanced quotes are not allowed without escaping:
+
+.
+[link](/url "title "and" title")
+.
+<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>
+.
+
+But it is easy to work around this by using a different quote type:
+
+.
+[link](/url 'title "and" title')
+.
+<p><a href="/url" title="title &quot;and&quot; title">link</a></p>
+.
+
+(Note:  `Markdown.pl` did allow double quotes inside a double-quoted
+title, and its test suite included a test demonstrating this.
+But it is hard to see a good rationale for the extra complexity this
+brings, since there are already many ways---backslash escaping,
+entities, or using a different quote type for the enclosing title---to
+write titles containing double quotes.  `Markdown.pl`'s handling of
+titles has a number of other strange features.  For example, it allows
+single-quoted titles in inline links, but not reference links.  And, in
+reference links but not inline links, it allows a title to begin with
+`"` and end with `)`.  `Markdown.pl` 1.0.1 even allows titles with no closing
+quotation mark, though 1.0.2b8 does not.  It seems preferable to adopt
+a simple, rational rule that works the same way in inline links and
+link reference definitions.)
+
+Whitespace is allowed around the destination and title:
+
+.
+[link](   /uri
+  "title"  )
+.
+<p><a href="/uri" title="title">link</a></p>
+.
+
+But it is not allowed between the link label and the
+following parenthesis:
+
+.
+[link] (/uri)
+.
+<p>[link] (/uri)</p>
+.
+
+Note that this is not a link, because the closing `]` occurs in
+an HTML tag:
+
+.
+[foo <bar attr="](baz)">
+.
+<p>[foo <bar attr="](baz)"></p>
+.
+
+
+There are three kinds of [reference links](#reference-link):
+<a id="reference-link"></a>
+
+A [full reference link](#full-reference-link) <a id="full-reference-link"></a>
+consists of a [link label](#link-label), optional whitespace, and
+another [link label](#link-label) that [matches](#matches) a
+[link reference definition](#link-reference-definition) elsewhere in the
+document.
+
+One label [matches](#matches) <a id="matches"></a>
+another just in case their normalized forms are equal.  To normalize a
+label, perform the *unicode case fold* and collapse consecutive internal
+whitespace to a single space.  If there are multiple matching reference
+link definitions, the one that comes first in the document is used.  (It
+is desirable in such cases to emit a warning.)
+
+The contents of the first link label are parsed as inlines, which are
+used as the link's text.  The link's URI and title are provided by the
+matching [link reference definition](#link-reference-definition).
+
+Here is a simple example:
+
+.
+[foo][bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+The first label can contain inline content:
+
+.
+[*foo\!*][bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo!</em></a></p>
+.
+
+Matching is case-insensitive:
+
+.
+[foo][BaR]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+Unicode case fold is used:
+
+.
+[Толпой][Толпой] is a Russian word.
+
+[ТОЛПОЙ]: /url
+.
+<p><a href="/url">Толпой</a> is a Russian word.</p>
+.
+
+Consecutive internal whitespace is treated as one space for
+purposes of determining matching:
+
+.
+[Foo
+  bar]: /url
+
+[Baz][Foo bar]
+.
+<p><a href="/url">Baz</a></p>
+.
+
+There can be whitespace between the two labels:
+
+.
+[foo] [bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[foo]
+[bar]
+
+[bar]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+When there are multiple matching [link reference
+definitions](#link-reference-definition), the first is used:
+
+.
+[foo]: /url1
+
+[foo]: /url2
+
+[bar][foo]
+.
+<p><a href="/url1">bar</a></p>
+.
+
+Note that matching is performed on normalized strings, not parsed
+inline content.  So the following does not match, even though the
+labels define equivalent inline content:
+
+.
+[bar][foo\!]
+
+[foo!]: /url
+.
+<p>[bar][foo!]</p>
+.
+
+A [collapsed reference link](#collapsed-reference-link)
+<a id="collapsed-reference-link"></a> consists of a [link
+label](#link-label) that [matches](#matches) a [link reference
+definition](#link-reference-definition) elsewhere in the
+document, optional whitespace, and the string `[]`.  The contents of the
+first link label are parsed as inlines, which are used as the link's
+text.  The link's URI and title are provided by the matching reference
+link definition.  Thus, `[foo][]` is equivalent to `[foo][foo]`.
+
+.
+[foo][]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[*foo* bar][]
+
+[*foo* bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo</em> bar</a></p>
+.
+
+The link labels are case-insensitive:
+
+.
+[Foo][]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">Foo</a></p>
+.
+
+
+As with full reference links, whitespace is allowed
+between the two sets of brackets:
+
+.
+[foo] 
+[]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+A [shortcut reference link](#shortcut-reference-link)
+<a id="shortcut-reference-link"></a> consists of a [link
+label](#link-label) that [matches](#matches) a [link reference
+definition](#link-reference-definition)  elsewhere in the
+document and is not followed by `[]` or a link label.
+The contents of the first link label are parsed as inlines,
+which are used as the link's text.  the link's URI and title
+are provided by the matching link reference definition.
+Thus, `[foo]` is equivalent to `[foo][]`.
+
+.
+[foo]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">foo</a></p>
+.
+
+.
+[*foo* bar]
+
+[*foo* bar]: /url "title"
+.
+<p><a href="/url" title="title"><em>foo</em> bar</a></p>
+.
+
+.
+[[*foo* bar]]
+
+[*foo* bar]: /url "title"
+.
+<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
+.
+
+The link labels are case-insensitive:
+
+.
+[Foo]
+
+[foo]: /url "title"
+.
+<p><a href="/url" title="title">Foo</a></p>
+.
+
+If you just want bracketed text, you can backslash-escape the
+opening bracket to avoid links:
+
+.
+\[foo]
+
+[foo]: /url "title"
+.
+<p>[foo]</p>
+.
+
+Note that this is a link, because link labels bind more tightly
+than emphasis:
+
+.
+[foo*]: /url
+
+*[foo*]
+.
+<p>*<a href="/url">foo*</a></p>
+.
+
+However, this is not, because link labels bind less
+tightly than code backticks:
+
+.
+[foo`]: /url
+
+[foo`]`
+.
+<p>[foo<code>]</code></p>
+.
+
+Link labels can contain matched square brackets:
+
+.
+[[[foo]]]
+
+[[[foo]]]: /url
+.
+<p><a href="/url">[[foo]]</a></p>
+.
+
+.
+[[[foo]]]
+
+[[[foo]]]: /url1
+[foo]: /url2
+.
+<p><a href="/url1">[[foo]]</a></p>
+.
+
+For non-matching brackets, use backslash escapes:
+
+.
+[\[foo]
+
+[\[foo]: /url
+.
+<p><a href="/url">[foo</a></p>
+.
+
+Full references take precedence over shortcut references:
+
+.
+[foo][bar]
+
+[foo]: /url1
+[bar]: /url2
+.
+<p><a href="/url2">foo</a></p>
+.
+
+In the following case `[bar][baz]` is parsed as a reference,
+`[foo]` as normal text:
+
+.
+[foo][bar][baz]
+
+[baz]: /url
+.
+<p>[foo]<a href="/url">bar</a></p>
+.
+
+Here, though, `[foo][bar]` is parsed as a reference, since
+`[bar]` is defined:
+
+.
+[foo][bar][baz]
+
+[baz]: /url1
+[bar]: /url2
+.
+<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
+.
+
+Here `[foo]` is not parsed as a shortcut reference, because it
+is followed by a link label (even though `[bar]` is not defined):
+
+.
+[foo][bar][baz]
+
+[baz]: /url1
+[foo]: /url2
+.
+<p>[foo]<a href="/url1">bar</a></p>
+.
+
+
+## Images
+
+An (unescaped) exclamation mark (`!`) followed by a reference or
+inline link will be parsed as an image.  The link label will be
+used as the image's alt text, and the link title, if any, will
+be used as the image's title.
+
+.
+![foo](/url "title")
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![foo *bar*]
+
+[foo *bar*]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo *bar*][]
+
+[foo *bar*]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo *bar*][foobar]
+
+[FOOBAR]: train.jpg "train & tracks"
+.
+<p><img src="train.jpg" alt="foo &lt;em&gt;bar&lt;/em&gt;" title="train &amp; tracks" /></p>
+.
+
+.
+![foo](train.jpg)
+.
+<p><img src="train.jpg" alt="foo" /></p>
+.
+
+.
+My ![foo bar](/path/to/train.jpg  "title"   )
+.
+<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
+.
+
+.
+![foo](<url>)
+.
+<p><img src="url" alt="foo" /></p>
+.
+
+.
+![](/url)
+.
+<p><img src="/url" alt="" /></p>
+.
+
+Reference-style:
+
+.
+![foo] [bar]
+
+[bar]: /url
+.
+<p><img src="/url" alt="foo" /></p>
+.
+
+.
+![foo] [bar]
+
+[BAR]: /url
+.
+<p><img src="/url" alt="foo" /></p>
+.
+
+Collapsed:
+
+.
+![foo][]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![*foo* bar][]
+
+[*foo* bar]: /url "title"
+.
+<p><img src="/url" alt="&lt;em&gt;foo&lt;/em&gt; bar" title="title" /></p>
+.
+
+The labels are case-insensitive:
+
+.
+![Foo][]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="Foo" title="title" /></p>
+.
+
+As with full reference links, whitespace is allowed
+between the two sets of brackets:
+
+.
+![foo] 
+[]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+Shortcut:
+
+.
+![foo]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="foo" title="title" /></p>
+.
+
+.
+![*foo* bar]
+
+[*foo* bar]: /url "title"
+.
+<p><img src="/url" alt="&lt;em&gt;foo&lt;/em&gt; bar" title="title" /></p>
+.
+
+.
+![[foo]]
+
+[[foo]]: /url "title"
+.
+<p><img src="/url" alt="[foo]" title="title" /></p>
+.
+
+The link labels are case-insensitive:
+
+.
+![Foo]
+
+[foo]: /url "title"
+.
+<p><img src="/url" alt="Foo" title="title" /></p>
+.
+
+If you just want bracketed text, you can backslash-escape the
+opening `!` and `[`:
+
+.
+\!\[foo]
+
+[foo]: /url "title"
+.
+<p>![foo]</p>
+.
+
+If you want a link after a literal `!`, backslash-escape the
+`!`:
+
+.
+\![foo]
+
+[foo]: /url "title"
+.
+<p>!<a href="/url" title="title">foo</a></p>
+.
+
+## Autolinks
+
+Autolinks are absolute URIs and email addresses inside `<` and `>`.
+They are parsed as links, with the URL or email address as the link
+label.
+
+A [URI autolink](#uri-autolink) <a id="uri-autolink"></a>
+consists of `<`, followed by an [absolute
+URI](#absolute-uri) not containing `<`, followed by `>`.  It is parsed
+as a link to the URI, with the URI as the link's label.
+
+An [absolute URI](#absolute-uri), <a id="absolute-uri"></a>
+for these purposes, consists of a [scheme](#scheme) followed by a colon (`:`)
+followed by zero or more characters other than ASCII whitespace and
+control characters, `<`, and `>`.  If the URI includes these characters,
+you must use percent-encoding (e.g. `%20` for a space).
+
+The following [schemes](#scheme) <a id="scheme"></a>
+are recognized (case-insensitive):
+`coap`, `doi`, `javascript`, `aaa`, `aaas`, `about`, `acap`, `cap`,
+`cid`, `crid`, `data`, `dav`, `dict`, `dns`, `file`, `ftp`, `geo`, `go`,
+`gopher`, `h323`, `http`, `https`, `iax`, `icap`, `im`, `imap`, `info`,
+`ipp`, `iris`, `iris.beep`, `iris.xpc`, `iris.xpcs`, `iris.lwz`, `ldap`,
+`mailto`, `mid`, `msrp`, `msrps`, `mtqp`, `mupdate`, `news`, `nfs`,
+`ni`, `nih`, `nntp`, `opaquelocktoken`, `pop`, `pres`, `rtsp`,
+`service`, `session`, `shttp`, `sieve`, `sip`, `sips`, `sms`, `snmp`,`
+soap.beep`, `soap.beeps`, `tag`, `tel`, `telnet`, `tftp`, `thismessage`,
+`tn3270`, `tip`, `tv`, `urn`, `vemmi`, `ws`, `wss`, `xcon`,
+`xcon-userid`, `xmlrpc.beep`, `xmlrpc.beeps`, `xmpp`, `z39.50r`,
+`z39.50s`, `adiumxtra`, `afp`, `afs`, `aim`, `apt`,` attachment`, `aw`,
+`beshare`, `bitcoin`, `bolo`, `callto`, `chrome`,` chrome-extension`,
+`com-eventbrite-attendee`, `content`, `cvs`,` dlna-playsingle`,
+`dlna-playcontainer`, `dtn`, `dvb`, `ed2k`, `facetime`, `feed`,
+`finger`, `fish`, `gg`, `git`, `gizmoproject`, `gtalk`, `hcp`, `icon`,
+`ipn`, `irc`, `irc6`, `ircs`, `itms`, `jar`, `jms`, `keyparc`, `lastfm`,
+`ldaps`, `magnet`, `maps`, `market`,` message`, `mms`, `ms-help`,
+`msnim`, `mumble`, `mvn`, `notes`, `oid`, `palm`, `paparazzi`,
+`platform`, `proxy`, `psyc`, `query`, `res`, `resource`, `rmi`, `rsync`,
+`rtmp`, `secondlife`, `sftp`, `sgn`, `skype`, `smb`, `soldat`,
+`spotify`, `ssh`, `steam`, `svn`, `teamspeak`, `things`, `udp`,
+`unreal`, `ut2004`, `ventrilo`, `view-source`, `webcal`, `wtai`,
+`wyciwyg`, `xfire`, `xri`, `ymsgr`.
+
+Here are some valid autolinks:
+
+.
+<http://foo.bar.baz>
+.
+<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
+.
+
+.
+<http://foo.bar.baz?q=hello&id=22&boolean>
+.
+<p><a href="http://foo.bar.baz?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz?q=hello&amp;id=22&amp;boolean</a></p>
+.
+
+.
+<irc://foo.bar:2233/baz>
+.
+<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
+.
+
+Uppercase is also fine:
+
+.
+<MAILTO:FOO@BAR.BAZ>
+.
+<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
+.
+
+Spaces are not allowed in autolinks:
+
+.
+<http://foo.bar/baz bim>
+.
+<p>&lt;http://foo.bar/baz bim&gt;</p>
+.
+
+An [email autolink](#email-autolink) <a id="email-autolink"></a>
+consists of `<`, followed by an [email address](#email-address),
+followed by `>`.  The link's label is the email address,
+and the URL is `mailto:` followed by the email address.
+
+An [email address](#email-address), <a id="email-address"></a>
+for these purposes, is anything that matches
+the [non-normative regex from the HTML5
+spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-%28type=email%29):
+
+    /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
+    (?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
+
+Examples of email autolinks:
+
+.
+<foo@bar.baz.com>
+.
+<p><a href="mailto:foo@bar.baz.com">foo@bar.baz.com</a></p>
+.
+
+.
+<foo+special@Bar.baz-bar0.com>
+.
+<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
+.
+
+These are not autolinks:
+
+.
+<>
+.
+<p>&lt;&gt;</p>
+.
+
+.
+<heck://bing.bong>
+.
+<p>&lt;heck://bing.bong&gt;</p>
+.
+
+.
+< http://foo.bar >
+.
+<p>&lt; http://foo.bar &gt;</p>
+.
+
+.
+<foo.bar.baz>
+.
+<p>&lt;foo.bar.baz&gt;</p>
+.
+
+.
+<localhost:5001/foo>
+.
+<p>&lt;localhost:5001/foo&gt;</p>
+.
+
+.
+http://google.com
+.
+<p>http://google.com</p>
+.
+
+.
+foo@bar.baz.com
+.
+<p>foo@bar.baz.com</p>
+.
+
+## Raw HTML
+
+Text between `<` and `>` that looks like an HTML tag is parsed as a
+raw HTML tag and will be rendered in HTML without escaping.
+Tag and attribute names are not limited to current HTML tags,
+so custom tags (and even, say, DocBook tags) may be used.
+
+Here is the grammar for tags:
+
+A [tag name](#tag-name) <a id="tag-name"></a> consists of an ASCII letter
+followed by zero or more ASCII letters or digits.
+
+An [attribute](#attribute) <a id="attribute"></a> consists of whitespace,
+an **attribute name**, and an optional **attribute value
+specification**.
+
+An [attribute name](#attribute-name) <a id="attribute-name"></a>
+consists of an ASCII letter, `_`, or `:`, followed by zero or more ASCII
+letters, digits, `_`, `.`, `:`, or `-`.  (Note:  This is the XML
+specification restricted to ASCII.  HTML5 is laxer.)
+
+An [attribute value specification](#attribute-value-specification)
+<a id="attribute-value-specification"></a> consists of optional whitespace,
+a `=` character, optional whitespace, and an [attribute
+value](#attribute-value).
+
+An [attribute value](#attribute-value) <a id="attribute-value"></a>
+consists of an [unquoted attribute value](#unquoted-attribute-value),
+a [single-quoted attribute value](#single-quoted-attribute-value),
+or a [double-quoted attribute value](#double-quoted-attribute-value).
+
+An [unquoted attribute value](#unquoted-attribute-value)
+<a id="unquoted-attribute-value"></a> is a nonempty string of characters not
+including spaces, `"`, `'`, `=`, `<`, `>`, or `` ` ``.
+
+A [single-quoted attribute value](#single-quoted-attribute-value)
+<a id="single-quoted-attribute-value"></a> consists of `'`, zero or more
+characters not including `'`, and a final `'`.
+
+A [double-quoted attribute value](#double-quoted-attribute-value)
+<a id="double-quoted-attribute-value"></a> consists of `"`, zero or more
+characters not including `"`, and a final `"`.
+
+An [open tag](#open-tag) <a id="open-tag"></a> consists of a `<` character,
+a [tag name](#tag-name), zero or more [attributes](#attribute),
+optional whitespace, an optional `/` character, and a `>` character.
+
+A [closing tag](#closing-tag) <a id="closing-tag"></a> consists of the
+string `</`, a [tag name](#tag-name), optional whitespace, and the
+character `>`.
+
+An [HTML comment](#html-comment) <a id="html-comment"></a> consists of the
+string `<!--`, a string of characters not including the string `--`, and
+the string `-->`.
+
+A [processing instruction](#processing-instruction)
+<a id="processing-instruction"></a> consists of the string `<?`, a string
+of characters not including the string `?>`, and the string
+`?>`.
+
+A [declaration](#declaration) <a id="declaration"></a> consists of the
+string `<!`, a name consisting of one or more uppercase ASCII letters,
+whitespace, a string of characters not including the character `>`, and
+the character `>`.
+
+A [CDATA section](#cdata-section) <a id="cdata-section"></a> consists of
+the string `<![CDATA[`, a string of characters not including the string
+`]]>`, and the string `]]>`.
+
+An [HTML tag](#html-tag) <a id="html-tag"></a> consists of an [open
+tag](#open-tag), a [closing tag](#closing-tag), an [HTML
+comment](#html-comment), a [processing
+instruction](#processing-instruction), an [element type
+declaration](#element-type-declaration), or a [CDATA
+section](#cdata-section).
+
+Here are some simple open tags:
+
+.
+<a><bab><c2c>
+.
+<p><a><bab><c2c></p>
+.
+
+Empty elements:
+
+.
+<a/><b2/>
+.
+<p><a/><b2/></p>
+.
+
+Whitespace is allowed:
+
+.
+<a  /><b2
+data="foo" >
+.
+<p><a  /><b2
+data="foo" ></p>
+.
+
+With attributes:
+
+.
+<a foo="bar" bam = 'baz <em>"</em>'
+_boolean zoop:33=zoop:33 />
+.
+<p><a foo="bar" bam = 'baz <em>"</em>'
+_boolean zoop:33=zoop:33 /></p>
+.
+
+Illegal tag names, not parsed as HTML:
+
+.
+<33> <__>
+.
+<p>&lt;33&gt; &lt;__&gt;</p>
+.
+
+Illegal attribute names:
+
+.
+<a h*#ref="hi">
+.
+<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>
+.
+
+Illegal attribute values:
+
+.
+<a href="hi'> <a href=hi'>
+.
+<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>
+.
+
+Illegal whitespace:
+
+.
+< a><
+foo><bar/ >
+.
+<p>&lt; a&gt;&lt;
+foo&gt;&lt;bar/ &gt;</p>
+.
+
+Missing whitespace:
+
+.
+<a href='bar'title=title>
+.
+<p>&lt;a href='bar'title=title&gt;</p>
+.
+
+Closing tags:
+
+.
+</a>
+</foo >
+.
+<p></a>
+</foo ></p>
+.
+
+Illegal attributes in closing tag:
+
+.
+</a href="foo">
+.
+<p>&lt;/a href=&quot;foo&quot;&gt;</p>
+.
+
+Comments:
+
+.
+foo <!-- this is a
+comment - with hyphen -->
+.
+<p>foo <!-- this is a
+comment - with hyphen --></p>
+.
+
+.
+foo <!-- not a comment -- two hyphens -->
+.
+<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
+.
+
+Processing instructions:
+
+.
+foo <?php echo $a; ?>
+.
+<p>foo <?php echo $a; ?></p>
+.
+
+Declarations:
+
+.
+foo <!ELEMENT br EMPTY>
+.
+<p>foo <!ELEMENT br EMPTY></p>
+.
+
+CDATA sections:
+
+.
+foo <![CDATA[>&<]]>
+.
+<p>foo <![CDATA[>&<]]></p>
+.
+
+Entities are preserved in HTML attributes:
+
+.
+<a href="&ouml;">
+.
+<p><a href="&ouml;"></p>
+.
+
+Backslash escapes do not work in HTML attributes:
+
+.
+<a href="\*">
+.
+<p><a href="\*"></p>
+.
+
+.
+<a href="\"">
+.
+<p>&lt;a href=&quot;&quot;&quot;&gt;</p>
+.
+
+## Hard line breaks
+
+A line break (not in a code span or HTML tag) that is preceded
+by two or more spaces is parsed as a linebreak (rendered
+in HTML as a `<br />` tag):
+
+.
+foo  
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+For a more visible alternative, a backslash before the newline may be
+used instead of two spaces:
+
+.
+foo\
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+More than two spaces can be used:
+
+.
+foo       
+baz
+.
+<p>foo<br />
+baz</p>
+.
+
+Leading spaces at the beginning of the next line are ignored:
+
+.
+foo  
+     bar
+.
+<p>foo<br />
+bar</p>
+.
+
+.
+foo\
+     bar
+.
+<p>foo<br />
+bar</p>
+.
+
+Line breaks can occur inside emphasis, links, and other constructs
+that allow inline content:
+
+.
+*foo  
+bar*
+.
+<p><em>foo<br />
+bar</em></p>
+.
+
+.
+*foo\
+bar*
+.
+<p><em>foo<br />
+bar</em></p>
+.
+
+Line breaks do not occur inside code spans
+
+.
+`code  
+span`
+.
+<p><code>code span</code></p>
+.
+
+.
+`code\
+span`
+.
+<p><code>code\ span</code></p>
+.
+
+or HTML tags:
+
+.
+<a href="foo  
+bar">
+.
+<p><a href="foo  
+bar"></p>
+.
+
+.
+<a href="foo\
+bar">
+.
+<p><a href="foo\
+bar"></p>
+.
+
+## Soft line breaks
+
+A regular line break (not in a code span or HTML tag) that is not
+preceded by two or more spaces is parsed as a softbreak.  (A
+softbreak may be rendered in HTML either as a newline or as a space.
+The result will be the same in browsers. In the examples here, a
+newline will be used.)
+
+.
+foo
+baz
+.
+<p>foo
+baz</p>
+.
+
+Spaces at the end of the line and beginning of the next line are
+removed:
+
+.
+foo 
+ baz
+.
+<p>foo
+baz</p>
+.
+
+A conforming parser may render a soft line break in HTML either as a
+line break or as a space.
+
+A renderer may also provide an option to render soft line breaks
+as hard line breaks.
+
+## Strings
+
+Any characters not given an interpretation by the above rules will
+be parsed as string content.
+
+.
+hello $.;'there
+.
+<p>hello $.;'there</p>
+.
+
+.
+Foo χρῆν
+.
+<p>Foo χρῆν</p>
+.
+
+Internal spaces are preserved verbatim:
+
+.
+Multiple     spaces
+.
+<p>Multiple     spaces</p>
+.
+
+<!-- END TESTS -->
+
+# Appendix A: A parsing strategy {-}
+
+## Overview {-}
+
+Parsing has two phases:
+
+1. In the first phase, lines of input are consumed and the block
+structure of the document---its division into paragraphs, block quotes,
+list items, and so on---is constructed.  Text is assigned to these
+blocks but not parsed. Link reference definitions are parsed and a
+map of links is constructed.
+
+2. In the second phase, the raw text contents of paragraphs and headers
+are parsed into sequences of Markdown inline elements (strings,
+code spans, links, emphasis, and so on), using the map of link
+references constructed in phase 1.
+
+## The document tree {-}
+
+At each point in processing, the document is represented as a tree of
+**blocks**.  The root of the tree is a `document` block.  The `document`
+may have any number of other blocks as **children**.  These children
+may, in turn, have other blocks as children.  The last child of a block
+is normally considered **open**, meaning that subsequent lines of input
+can alter its contents.  (Blocks that are not open are **closed**.)
+Here, for example, is a possible document tree, with the open blocks
+marked by arrows:
+
+``` tree
+-> document
+  -> block_quote
+       paragraph
+         "Lorem ipsum dolor\nsit amet."
+    -> list (type=bullet tight=true bullet_char=-)
+         list_item
+           paragraph
+             "Qui *quodsi iracundia*"
+      -> list_item
+        -> paragraph
+             "aliquando id"
+```
+
+## How source lines alter the document tree {-}
+
+Each line that is processed has an effect on this tree.  The line is
+analyzed and, depending on its contents, the document may be altered
+in one or more of the following ways:
+
+1. One or more open blocks may be closed.
+2. One or more new blocks may be created as children of the
+   last open block.
+3. Text may be added to the last (deepest) open block remaining
+   on the tree.
+
+Once a line has been incorporated into the tree in this way,
+it can be discarded, so input can be read in a stream.
+
+We can see how this works by considering how the tree above is
+generated by four lines of Markdown:
+
+``` markdown
+> Lorem ipsum dolor
+sit amet.
+> - Qui *quodsi iracundia*
+> - aliquando id
+```
+
+At the outset, our document model is just
+
+``` tree
+-> document
+```
+
+The first line of our text,
+
+``` markdown
+> Lorem ipsum dolor
+```
+
+causes a `block_quote` block to be created as a child of our
+open `document` block, and a `paragraph` block as a child of
+the `block_quote`.  Then the text is added to the last open
+block, the `paragraph`:
+
+``` tree
+-> document
+  -> block_quote
+    -> paragraph
+         "Lorem ipsum dolor"
+```
+
+The next line,
+
+``` markdown
+sit amet.
+```
+
+is a "lazy continuation" of the open `paragraph`, so it gets added
+to the paragraph's text:
+
+``` tree
+-> document
+  -> block_quote
+    -> paragraph
+         "Lorem ipsum dolor\nsit amet."
+```
+
+The third line,
+
+``` markdown
+> - Qui *quodsi iracundia*
+```
+
+causes the `paragraph` block to be closed, and a new `list` block
+opened as a child of the `block_quote`.  A `list_item` is also
+added as a child of the `list`, and a `paragraph` as a child of
+the `list_item`.  The text is then added to the new `paragraph`:
+
+``` tree
+-> document
+  -> block_quote
+       paragraph
+         "Lorem ipsum dolor\nsit amet."
+    -> list (type=bullet tight=true bullet_char=-)
+      -> list_item
+        -> paragraph
+             "Qui *quodsi iracundia*"
+```
+
+The fourth line,
+
+``` markdown
+> - aliquando id
+```
+
+causes the `list_item` (and its child the `paragraph`) to be closed,
+and a new `list_item` opened up as child of the `list`.  A `paragraph`
+is added as a child of the new `list_item`, to contain the text.
+We thus obtain the final tree:
+
+``` tree
+-> document
+  -> block_quote
+       paragraph
+         "Lorem ipsum dolor\nsit amet."
+    -> list (type=bullet tight=true bullet_char=-)
+         list_item
+           paragraph
+             "Qui *quodsi iracundia*"
+      -> list_item
+        -> paragraph
+             "aliquando id"
+```
+
+## From block structure to the final document {-}
+
+Once all of the input has been parsed, all open blocks are closed.
+
+We then "walk the tree," visiting every node, and parse raw
+string contents of paragraphs and headers as inlines.  At this
+point we have seen all the link reference definitions, so we can
+resolve reference links as we go.
+
+``` tree
+document
+  block_quote
+    paragraph
+      str "Lorem ipsum dolor"
+      softbreak
+      str "sit amet."
+    list (type=bullet tight=true bullet_char=-)
+      list_item
+        paragraph
+          str "Qui "
+          emph
+            str "quodsi iracundia"
+      list_item
+        paragraph
+          str "aliquando id"
+```
+
+Notice how the newline in the first paragraph has been parsed as
+a `softbreak`, and the asterisks in the first list item have become
+an `emph`.
+
+The document can be rendered as HTML, or in any other format, given
+an appropriate renderer.
+
+
diff --git a/core/testdata/packagedocs/referenceLinks.kotlin.md b/core/testdata/packagedocs/referenceLinks.kotlin.md
new file mode 100644
index 0000000..ac7e4b4
--- /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 0000000..7583ee9
--- /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 0000000..ddbdbe2
--- /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 0000000..5d7432b
--- /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 0000000..b22273a
--- /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 0000000..3861931
--- /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 0000000..e69de29
--- /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 0000000..2c29f4c
--- /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 0000000..2c29f4c
--- /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 0000000..3c12691
--- /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 0000000..625d1da
--- /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 0000000..e282f6b
--- /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 0000000..e96f234
--- /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 0000000..b87cce5
--- /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 0000000..64d3848
--- /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 0000000..54ab459
--- /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 0000000..152fb7d
--- /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 0000000..1aef84d
--- /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 0000000..520be55
--- /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 0000000..b53d3a2
--- /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 0000000..3ca110e
--- /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 0000000..dadafa5
--- /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 0000000..43bc0e2
--- /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 0000000..f929ecd
--- /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 0000000..d688a84
--- /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 0000000..5b76f63
--- /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 0000000..4d517e4
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,20 @@
+dokka_version=0.9.17-g002
+dokka_publication_channel=dokka
+
+#Kotlin compiler and plugin
+bundled_kotlin_compiler_version=1.3.20-dev-564
+kotlin_version=1.2.21
+kotlin_for_gradle_runtime_version=1.1.60
+
+ant_version=1.9.6
+
+#Maven plugin dependencies
+maven_version=3.5.0
+maven_archiver_version=2.5
+plexus_utils_version=3.0.22
+plexus_archiver_version=3.4
+maven_plugin_tools_version=3.5
+
+#For CI
+mvn=mvn
+re
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..736fb7d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..610ad4c
--- /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.5-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /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 0000000..f955316
--- /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 0000000..24d59ed
--- /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 0000000..b78eb9c
--- /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 0000000..1984a3e
--- /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 0000000..9eacf5e
--- /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/runners/android-gradle-plugin/build.gradle b/runners/android-gradle-plugin/build.gradle
new file mode 100644
index 0000000..72d1be9
--- /dev/null
+++ b/runners/android-gradle-plugin/build.gradle
@@ -0,0 +1,101 @@
+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 {
+    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"
+    }
+}
\ No newline at end of file
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 0000000..bd2e88c
--- /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 0000000..b204da7
--- /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 0000000..e7dcd44
--- /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 0000000..20cf8c4
--- /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 0000000..9c3373d
--- /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 0000000..23d232d
--- /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 0000000..7f73314
--- /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 0000000..e7fb76f
--- /dev/null
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -0,0 +1,211 @@
+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.")
+    var generateClassIndexPage: Boolean = true
+
+    @set:Argument(value = "generatePackageIndexPage", description = "Generate packages.html index page.")
+    var generatePackageIndexPage: Boolean = true
+
+    @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.generateClassIndexPage,
+            generatePackageIndexPage = arguments.generatePackageIndexPage,
+            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 0000000..4ce0416
--- /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 0000000..c311cf4
--- /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 0000000..da4552d
--- /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 0000000..a681c82
--- /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 0000000..c3fe2ea
--- /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 0000000..0675334
--- /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 0000000..255138a
--- /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 0000000..0ac4cbd
--- /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 0000000..cdd3fa8
--- /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 0000000..b0257f8
--- /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 0000000..ebaf165
--- /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 0000000..18c20ed
--- /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 0000000..9458528
--- /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 0000000..f712998
--- /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 0000000..6f17af2
--- /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 0000000..1555de9
--- /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 0000000..b4e1a89
--- /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 0000000..3668c59
--- /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 0000000..ca2f27b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main2)
+
+        val next = findViewById(R.id.Button02) as Button
+        next.setOnClickListener {
+            val intent: Intent = Intent()
+            setResult(RESULT_OK, intent)
+            finish()
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        menuInflater.inflate(R.menu.main_activity2, menu)
+        return true
+    }
+}
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ede57c3
--- /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 0000000..d707536
--- /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 0000000..f3b10b6
--- /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 0000000..f3b10b6
--- /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 0000000..47c8224
--- /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 0000000..d8f08bc
--- /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 0000000..6ce89c7
--- /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 0000000..59477b5
--- /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 0000000..3827b69
--- /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 0000000..1feb286
--- /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 0000000..6a053a5
--- /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 0000000..b4e1a89
--- /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 0000000..3668c59
--- /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 0000000..ca2f27b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main2)
+
+        val next = findViewById(R.id.Button02) as Button
+        next.setOnClickListener {
+            val intent: Intent = Intent()
+            setResult(RESULT_OK, intent)
+            finish()
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        menuInflater.inflate(R.menu.main_activity2, menu)
+        return true
+    }
+}
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ede57c3
--- /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 0000000..d707536
--- /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 0000000..f3b10b6
--- /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 0000000..f3b10b6
--- /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 0000000..47c8224
--- /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 0000000..d8f08bc
--- /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 0000000..6ce89c7
--- /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 0000000..59477b5
--- /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 0000000..c5e79eb
--- /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 0000000..a4e67fe
--- /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 0000000..736668a
--- /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 0000000..6c96a01
--- /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 0000000..bbfeb03
--- /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 0000000..1d9a6fb
--- /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 0000000..0f27d36
--- /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 0000000..267f6ef
--- /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 0000000..d034a3a
--- /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 0000000..bf76058
--- /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 0000000..5b4250a
--- /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 0000000..ee68ba6
--- /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 0000000..3ecbcd3
--- /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 0000000..b0b980f
--- /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 0000000..e6443d0
--- /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 0000000..bbdf2d0
--- /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 0000000..b4e1a89
--- /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 0000000..3668c59
--- /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 0000000..ca2f27b
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt
@@ -0,0 +1,28 @@
+package org.example.kotlin.mixed
+
+import android.content.Intent
+import android.os.Bundle
+import android.app.Activity
+import android.view.Menu
+import android.widget.Button
+
+class KotlinActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main2)
+
+        val next = findViewById(R.id.Button02) as Button
+        next.setOnClickListener {
+            val intent: Intent = Intent()
+            setResult(RESULT_OK, intent)
+            finish()
+        }
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        menuInflater.inflate(R.menu.main_activity2, menu)
+        return true
+    }
+}
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ede57c3
--- /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 0000000..d707536
--- /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 0000000..f3b10b6
--- /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 0000000..f3b10b6
--- /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 0000000..47c8224
--- /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 0000000..d8f08bc
--- /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 0000000..6ce89c7
--- /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 0000000..59477b5
--- /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 0000000..5e969d8
--- /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 0000000..1feb286
--- /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 0000000..4a259f5
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/build.gradle
@@ -0,0 +1,40 @@
+buildscript {
+    repositories {
+        mavenCentral()
+        jcenter()
+        maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
+        maven { url "https://dl.bintray.com/kotlin/kotlin-dev" }
+    }
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version"
+    }
+}
+
+plugins {
+    id 'org.jetbrains.dokka'
+}
+
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+
+repositories {
+    mavenCentral()
+    jcenter()
+    maven {
+        url "http://dl.bintray.com/kotlin/kotlin-eap-1.1"
+    }
+    maven {
+        url "https://dl.bintray.com/kotlin/kotlin-dev"
+    }
+}
+
+dependencies {
+    compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version
+    compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version
+}
+
+
+dokka {
+    dokkaFatJar = new File(dokka_fatjar)
+    classpath += files("$projectDir/classDir")
+}
\ No newline at end of file
diff --git a/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class
new file mode 100644
index 0000000..ccfff30
--- /dev/null
+++ b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class
Binary files differ
diff --git a/runners/gradle-integration-tests/testData/basic/fileTree.txt b/runners/gradle-integration-tests/testData/basic/fileTree.txt
new file mode 100644
index 0000000..2ceae37
--- /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 0000000..c36a146
--- /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 0000000..3d7bcb5
--- /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 0000000..68d93e3
--- /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 0000000..5624fca
--- /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 0000000..283cc52
--- /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 0000000..0600411
--- /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 0000000..126d7f3
--- /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 0000000..7b8ff9f
--- /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 0000000..8a87590
--- /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 0000000..bc20e1c
--- /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 0000000..09f3724
--- /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 0000000..eeb377f
--- /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 0000000..3fb032b
--- /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 0000000..c54dea5
--- /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 0000000..53f22ff
--- /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 0000000..661d432
--- /dev/null
+++ b/runners/gradle-plugin/build.gradle
@@ -0,0 +1,99 @@
+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 {
+    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)
+        }
+    }
+}
+
+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"
+    }
+}
\ No newline at end of file
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 0000000..7bdf2f9
--- /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 0000000..74a2af4
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -0,0 +1,476 @@
+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"
+    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 = ""
+
+    @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 0000000..068bd35
--- /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 0000000..79a8c22
--- /dev/null
+++ b/runners/maven-plugin/build.gradle
@@ -0,0 +1,109 @@
+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"))
+    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 0000000..c5883c6
--- /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 0000000..dcb9ac2
--- /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 0000000..a535c80
--- /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 0000000..ef5f986
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,11 @@
+rootProject.name = "dokka"
+
+include 'core',
+        'integration',
+        'runners:fatjar',
+        'runners:ant',
+        'runners:cli',
+        '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 0000000..5206e10
--- /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