aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2017-03-09 17:47:09 +0300
committerSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2017-03-20 18:20:19 +0300
commit2eb805e47505388c0e47102e9257f6f79681e699 (patch)
treeb50cc77717f4c9565579419ad197f21ec7938d24
parent8eff2cf33d1ba671191d6e2873f005e6b5f7057e (diff)
downloaddokka-2eb805e47505388c0e47102e9257f6f79681e699.tar.gz
Create HTML based format for kotlin-website
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt193
-rw-r--r--core/src/main/kotlin/Formats/StandardFormats.kt6
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt2
-rw-r--r--core/src/main/resources/dokka/format/kotlin-website-html.properties2
-rw-r--r--core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt74
-rw-r--r--core/testdata/format/website-html/dataTags/jre7.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/js.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/jvm.kt11
-rw-r--r--core/testdata/format/website-html/dataTags/multiplatform.package.html63
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt0
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/js.kt8
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt9
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html38
-rw-r--r--core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html33
-rw-r--r--core/testdata/format/website-html/dropImport.html11
-rw-r--r--core/testdata/format/website-html/dropImport.kt12
-rw-r--r--core/testdata/format/website-html/newLinesInImportList.html12
-rw-r--r--core/testdata/format/website-html/newLinesInImportList.kt12
-rw-r--r--core/testdata/format/website-html/newLinesInSamples.html19
-rw-r--r--core/testdata/format/website-html/newLinesInSamples.kt19
-rw-r--r--core/testdata/format/website-html/overloadGroup.html18
-rw-r--r--core/testdata/format/website-html/overloadGroup.kt15
-rw-r--r--core/testdata/format/website-html/returnTag.html9
-rw-r--r--core/testdata/format/website-html/returnTag.kt11
-rw-r--r--core/testdata/format/website-html/sample.html21
-rw-r--r--core/testdata/format/website-html/sample.kt16
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.html12
-rw-r--r--core/testdata/format/website-html/sampleWithAsserts.kt15
28 files changed, 662 insertions, 1 deletions
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
new file mode 100644
index 000000000..d29629110
--- /dev/null
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -0,0 +1,193 @@
+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.nio.file.Path
+
+
+private object EmptyHtmlTemplateServie : HtmlTemplateService {
+ override fun appendFooter(to: StringBuilder) {
+
+ }
+
+ override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) {
+
+ }
+}
+
+
+open class KotlinWebsiteHtmlOutputBuilder(to: StringBuilder,
+ location: Location,
+ locationService: LocationService,
+ languageService: LanguageService,
+ extension: String,
+ impliedPlatforms: List<String>)
+ : HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, EmptyHtmlTemplateServie) {
+ 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)
+
+ override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", 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)) {
+ 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 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>) {", "}") {
+ wrap("\n//sampleStart\n", "\n//sampleEnd\n", body)
+ }
+ }
+ }
+ }
+}
+
+class KotlinWebsiteHtmlFormatService @Inject constructor(locationService: LocationService,
+ signatureGenerator: LanguageService,
+ @Named(impliedPlatformsName) impliedPlatforms: List<String>)
+ : HtmlFormatService(locationService, signatureGenerator, EmptyHtmlTemplateServie, impliedPlatforms) {
+
+ override fun enumerateSupportFiles(callback: (String, String) -> Unit) {
+
+ }
+
+ override fun createOutputBuilder(to: StringBuilder, location: Location) =
+ KotlinWebsiteHtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms)
+}
+
diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt
index 6b077444a..c67386af0 100644
--- a/core/src/main/kotlin/Formats/StandardFormats.kt
+++ b/core/src/main/kotlin/Formats/StandardFormats.kt
@@ -40,6 +40,12 @@ class KotlinWebsiteFormatRunnableSamplesDescriptor : KotlinFormatDescriptorBase(
override val outlineServiceClass = YamlOutlineService::class
}
+class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() {
+ override val formatServiceClass = KotlinWebsiteHtmlFormatService::class
+ override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class
+ override val outlineServiceClass = YamlOutlineService::class
+}
+
class JekyllFormatDescriptor : KotlinFormatDescriptorBase() {
override val formatServiceClass = JekyllFormatService::class
}
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 84f91d9cc..743a6ac81 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -253,7 +253,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
for ((path, nodes) in breakdownByLocation) {
- if (!noHeader) {
+ if (!noHeader && path.isNotEmpty()) {
appendBreadcrumbs(path)
appendLine()
appendLine()
diff --git a/core/src/main/resources/dokka/format/kotlin-website-html.properties b/core/src/main/resources/dokka/format/kotlin-website-html.properties
new file mode 100644
index 000000000..f4c320b9f
--- /dev/null
+++ b/core/src/main/resources/dokka/format/kotlin-website-html.properties
@@ -0,0 +1,2 @@
+class=org.jetbrains.dokka.Formats.KotlinWebsiteHtmlFormatDescriptor
+description=Generates Kotlin website documentation \ No newline at end of file
diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
new file mode 100644
index 000000000..5c2fbe75b
--- /dev/null
+++ b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt
@@ -0,0 +1,74 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.*
+import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
+import org.junit.Test
+
+class KotlinWebSiteHtmlFormatTest {
+ private val kwsService = KotlinWebsiteHtmlFormatService(InMemoryLocationService, KotlinLanguageService(), listOf())
+
+
+ @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 ->
+ kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.find { it.kind == NodeKind.GroupNode }.singletonOrEmptyList())
+ }
+ verifyMultiplatformPackage(module, path)
+ }
+
+ private fun verifyKWSNodeByName(fileName: String, name: String) {
+ verifyOutput("testdata/format/website-html/$fileName.kt", ".html", format = "kotlin-website-html") { model, output ->
+ kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name })
+ }
+ }
+
+ private fun buildMultiplePlatforms(path: String): DocumentationModule {
+ val module = DocumentationModule("test")
+ val options = DocumentationOptions("", "html", generateIndexPages = false)
+ 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 ->
+ kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members)
+ }
+ }
+
+}
diff --git a/core/testdata/format/website-html/dataTags/jre7.kt b/core/testdata/format/website-html/dataTags/jre7.kt
new file mode 100644
index 000000000..d21b8d7b4
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/jre7.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jre7New() {}
+
+fun jre7() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/js.kt b/core/testdata/format/website-html/dataTags/js.kt
new file mode 100644
index 000000000..b22d70886
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/js.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jsNew() {}
+
+fun js() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/jvm.kt b/core/testdata/format/website-html/dataTags/jvm.kt
new file mode 100644
index 000000000..02d042261
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/jvm.kt
@@ -0,0 +1,11 @@
+package foo
+
+@SinceKotlin("1.1")
+fun jvmNew() {}
+
+fun jvm() {}
+
+fun shared() {}
+
+@SinceKotlin("1.1")
+fun sharedNew() {} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTags/multiplatform.package.html b/core/testdata/format/website-html/dataTags/multiplatform.package.html
new file mode 100644
index 000000000..0430c6f2e
--- /dev/null
+++ b/core/testdata/format/website-html/dataTags/multiplatform.package.html
@@ -0,0 +1,63 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">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>
+<a href="test/foo/jre7">jre7</a>
+</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>
+<a href="test/foo/jre7-new">jre7New</a>
+</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>
+<a href="test/foo/js">js</a>
+</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>
+<a href="test/foo/js-new">jsNew</a>
+</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>
+<a href="test/foo/jvm">jvm</a>
+</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>
+<a href="test/foo/jvm-new">jvmNew</a>
+</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>
+<a href="test/foo/shared">shared</a>
+</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>
+<a href="test/foo/shared-new">sharedNew</a>
+</td>
+<td>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/jre7.kt
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/js.kt b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt
new file mode 100644
index 000000000..045f3f0d6
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/js.kt
@@ -0,0 +1,8 @@
+package pack
+
+class Some {
+
+ fun magic() {
+
+ }
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt
new file mode 100644
index 000000000..57f36742e
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/jvm.kt
@@ -0,0 +1,9 @@
+package pack
+
+class SomeCoolJvmClass {
+ fun magic() {
+
+ }
+}
+
+typealias Some = SomeCoolJvmClass \ No newline at end of file
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
new file mode 100644
index 000000000..bca7d36d9
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.html
@@ -0,0 +1,38 @@
+<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>
+<h1>Some</h1>
+<p><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>
+</p>
+<p><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>
+<p>Constructors</p>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<a href="test/pack/-some/-some/-init-">&lt;init&gt;</a>
+</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>
+<p>Functions</p>
+<table class="api-docs-table">
+<tbody>
+<tr>
+<td>
+<a href="test/pack/-some/-some/magic">magic</a>
+</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>
+</p>
diff --git a/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
new file mode 100644
index 000000000..985f9f04b
--- /dev/null
+++ b/core/testdata/format/website-html/dataTagsInGroupNode/multiplatform.package.html
@@ -0,0 +1,33 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div>
+<h2>Package pack</h2>
+<h3>Types</h3>
+<table class="api-docs-table">
+<tbody>
+<tr data-platform="JS"><td>
+<a href="test/pack/-some/index">Some</a>
+</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>
+<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a>
+</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>
+<a href="test/pack/-some/index">Some</a>
+</td>
+<td>
+<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="identifier">SomeCoolJvmClass</span></code></div>
+
+</td>
+</tr></tbody>
+</table>
diff --git a/core/testdata/format/website-html/dropImport.html b/core/testdata/format/website-html/dropImport.html
new file mode 100644
index 000000000..cfcb8cd5e
--- /dev/null
+++ b/core/testdata/format/website-html/dropImport.html
@@ -0,0 +1,11 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import some.*
+
+fun main(args: Array<String>) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/dropImport.kt b/core/testdata/format/website-html/dropImport.kt
new file mode 100644
index 000000000..7b8d9f4e8
--- /dev/null
+++ b/core/testdata/format/website-html/dropImport.kt
@@ -0,0 +1,12 @@
+import samples.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/newLinesInImportList.html b/core/testdata/format/website-html/newLinesInImportList.html
new file mode 100644
index 000000000..4862b314e
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInImportList.html
@@ -0,0 +1,12 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">import same.*
+import some.*
+
+fun main(args: Array<String>) {
+//sampleStart
+
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInImportList.kt b/core/testdata/format/website-html/newLinesInImportList.kt
new file mode 100644
index 000000000..836d9f6f0
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInImportList.kt
@@ -0,0 +1,12 @@
+import same.*
+import some.*
+
+/**
+ * @sample example1
+ */
+fun foo() {
+}
+
+fun example1() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/newLinesInSamples.html b/core/testdata/format/website-html/newLinesInSamples.html
new file mode 100644
index 000000000..0babe1228
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInSamples.html
@@ -0,0 +1,19 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<h1>foo</h1>
+<a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array<String>) {
+//sampleStart
+val words = listOf("a", "abc", "ab", "def", "abcd")
+val byLength = words.groupBy { it.length }
+
+println(byLength.keys) // [1, 3, 2, 4]
+println(byLength.values) // [[a], [abc, def], [ab], [abcd]]
+
+val mutableByLength: MutableMap&lt;Int, MutableList&lt;String&gt;&gt; = words.groupByTo(mutableMapOf()) { it.length }
+// same content as in byLength map, but the map is mutable
+println("mutableByLength == byLength is ${mutableByLength == byLength}") // true
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/newLinesInSamples.kt b/core/testdata/format/website-html/newLinesInSamples.kt
new file mode 100644
index 000000000..ee49aefc7
--- /dev/null
+++ b/core/testdata/format/website-html/newLinesInSamples.kt
@@ -0,0 +1,19 @@
+fun groupBySample() {
+ val words = listOf("a", "abc", "ab", "def", "abcd")
+ val byLength = words.groupBy { it.length }
+
+ assertPrints(byLength.keys, "[1, 3, 2, 4]")
+ assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]")
+
+ val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length }
+ // same content as in byLength map, but the map is mutable
+ assertTrue(mutableByLength == byLength)
+}
+
+
+/**
+ * @sample groupBySample
+ */
+fun foo() {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/overloadGroup.html b/core/testdata/format/website-html/overloadGroup.html
new file mode 100644
index 000000000..2482f8fb6
--- /dev/null
+++ b/core/testdata/format/website-html/overloadGroup.html
@@ -0,0 +1,18 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/magic">magic</a></div>
+<h1>magic</h1>
+<p><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>
+<p>Parameters</p>
+<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>
+</p>
+<p><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>
+<p>Parameters</p>
+<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>
+</p>
diff --git a/core/testdata/format/website-html/overloadGroup.kt b/core/testdata/format/website-html/overloadGroup.kt
new file mode 100644
index 000000000..5bc98e3d4
--- /dev/null
+++ b/core/testdata/format/website-html/overloadGroup.kt
@@ -0,0 +1,15 @@
+/**
+ * @param spell The text of spell, often distributed on scrolls
+ * @return Spell ID for future casts
+ */
+fun magic(spell: String): Int {
+
+}
+
+/**
+ * @param spell Spell ID of previously casted spell
+ * @return Spell ID for future casts
+ */
+fun magic(spell: Int): Int {
+
+} \ No newline at end of file
diff --git a/core/testdata/format/website-html/returnTag.html b/core/testdata/format/website-html/returnTag.html
new file mode 100644
index 000000000..922531307
--- /dev/null
+++ b/core/testdata/format/website-html/returnTag.html
@@ -0,0 +1,9 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/index-of">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="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>
+<p>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>.</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="test/index-of#$indexOf(Foo, kotlin.Char, kotlin.Int, kotlin.Boolean)/char">char</a> or -1 if none is found.</p>
diff --git a/core/testdata/format/website-html/returnTag.kt b/core/testdata/format/website-html/returnTag.kt
new file mode 100644
index 000000000..669c14f90
--- /dev/null
+++ b/core/testdata/format/website-html/returnTag.kt
@@ -0,0 +1,11 @@
+class Foo
+
+/**
+ * Returns the index within this string of the first occurrence of the specified character, starting from the specified [startIndex].
+ *
+ * @param ignoreCase `true` to ignore character case when matching a character. By default `false`.
+ * @returns An index of the first occurrence of [char] or -1 if none is found.
+ */
+fun Foo.indexOf(char: Char, startIndex: Int = 0, ignoreCase: Boolean = false): Int {
+ return -1
+}
diff --git a/core/testdata/format/website-html/sample.html b/core/testdata/format/website-html/sample.html
new file mode 100644
index 000000000..00646a113
--- /dev/null
+++ b/core/testdata/format/website-html/sample.html
@@ -0,0 +1,21 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div>
+<h1>foo</h1>
+<p><div class="overload-group"><a name="$foo()"></a>
+<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Int</span></code></div>
+<p>Groups elements of the original sequence by the key returned by the given <a href="#">keySelector</a> function
+applied to each element and returns a map where each group key is associated with a list of corresponding elements.</p>
+<div class="sample"><pre><code class="lang-kotlin">
+
+fun main(args: Array<String>) {
+//sampleStart
+if (true) {
+ println(property)
+}
+//sampleEnd
+}</code></pre></div>
+</div>
+</p>
+<p><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>
+</p>
diff --git a/core/testdata/format/website-html/sample.kt b/core/testdata/format/website-html/sample.kt
new file mode 100644
index 000000000..a664c2f52
--- /dev/null
+++ b/core/testdata/format/website-html/sample.kt
@@ -0,0 +1,16 @@
+/**
+ * Groups elements of the original sequence by the key returned by the given [keySelector] function
+ * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
+ * @sample example1
+ */
+fun foo(): Int {
+ return 0
+}
+
+fun foo(i: Int): Int {
+ return 1
+}
+
+fun example1(node: String) = if (true) {
+ println(property)
+}
diff --git a/core/testdata/format/website-html/sampleWithAsserts.html b/core/testdata/format/website-html/sampleWithAsserts.html
new file mode 100644
index 000000000..012f91ab7
--- /dev/null
+++ b/core/testdata/format/website-html/sampleWithAsserts.html
@@ -0,0 +1,12 @@
+<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/a">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">
+
+fun main(args: Array<String>) {
+//sampleStart
+println(a()) // Hello, Work
+println("a() == b() is ${a() == b()}") // true
+//sampleEnd
+}</code></pre></div>
diff --git a/core/testdata/format/website-html/sampleWithAsserts.kt b/core/testdata/format/website-html/sampleWithAsserts.kt
new file mode 100644
index 000000000..bb9732d5c
--- /dev/null
+++ b/core/testdata/format/website-html/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