diff --git a/documentation/docs/static-page/links.md b/documentation/docs/static-page/links.md index 96438dd..61934aa 100644 --- a/documentation/docs/static-page/links.md +++ b/documentation/docs/static-page/links.md @@ -6,7 +6,15 @@ title: Links ## Links Our side supports standard markdown links: - - TODO Using standard [urls](https://pl.wikipedia.org/wiki/Uniform_Resource_Locator) - - TODO To [other (html) pages](/docs/static-page/samples/plain_html_file.html) (or [md based files](/docs/static-page/samples/plain_md_file.md)) in our documentation that using paths relative to root od documentation e.g. `/docs/static-page/sample/plain_html_file.html` for this project - - TODO To [other (html) pages](samples/plain_html_file.html) (or [md based files](samples/plain_md_file.md)) in our documentation that using paths relative to this file e.g. using `samples/plain_html_file.html` - - TODO To API using `[fully.quallifty.Name]` + - Using standard [urls](https://pl.wikipedia.org/wiki/Uniform_Resource_Locator) + - To [other (html) pages](/docs/static-page/samples/plain_html_file.html) (or [md based files](/docs/static-page/samples/plain_md_file.md)) in our documentation that using paths relative to root od documentation e.g. `/docs/static-page/sample/plain_html_file.html` for this project + - To [other (html) pages](samples/plain_html_file.html) (or [md based files](samples/plain_md_file.md)) in our documentation that using paths relative to this file e.g. using `samples/plain_html_file.html` + - To API (using `[fully.quallifty.Name]`) + - [package](com.virtuslab.dokka.site) + - [class](com.virtuslab.dokka.site#StaticSiteContext) + - [val](com.virtuslab.dokka.site::apiPageDRI) + - [prop](com.virtuslab.dokka.site#AContentPage::content) + - TODO [top level fun](com.virtuslab.dokka.site::loadTemplateFile(java.io.File)) + - TODO [member fun](com.virtuslab.dokka.site#TemplateFile::resolveMarkdown(com.virtuslab.dokka.site.RenderingContext)) + - TODO [member fun (no params)](com.virtuslab.dokka.site#StaticSiteContext::resolveLinkToApi) + diff --git a/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt b/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt index b6603d2..9299fe5 100644 --- a/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt +++ b/src/main/kotlin/com/virtuslab/dokka/site/StaticSiteContext.kt @@ -1,7 +1,10 @@ package com.virtuslab.dokka.site +import org.jetbrains.dokka.base.parsers.MarkdownParser import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.JavaClassReference import org.jetbrains.dokka.model.doc.DocTag import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.model.properties.PropertyContainer @@ -12,8 +15,11 @@ import org.jetbrains.dokka.pages.DCI import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.plugability.DokkaContext import java.io.File +import java.net.MalformedURLException +import java.net.URL +import java.nio.file.Path -class StaticSiteContext(val root: File, cxt: DokkaContext){ +class StaticSiteContext(val root: File, cxt: DokkaContext) { val docsFile = File(root, "docs") fun indexPage(): BaseStaticSiteProcessor.StaticPageNode? { @@ -63,22 +69,18 @@ class StaticSiteContext(val root: File, cxt: DokkaContext){ null } - private fun parseMarkdown(page: PreResolvedPage, dri: DRI, allDRIs: Map): ContentNode { + private fun parseMarkdown( + page: PreResolvedPage, + dri: DRI, + allDRIs: Map, + proceededFilePath: String + ): ContentNode { val nodes = if (page.hasMarkdown) { - val parser = ExtendableMarkdownParser(page.code) { link -> - val driKey = if (link.startsWith("/")) { - // handle root related links - link.replace('/', '.').removePrefix(".") - } else { - val unSuffixedDri = dri.packageName!!.removeSuffix(".html").removeSuffix(".md") - val parentDri = unSuffixedDri.take(unSuffixedDri.indexOfLast('.'::equals)).removePrefix("_.") - "${parentDri}.${link.replace('/', '.')}" - } - allDRIs[driKey] - } + val externalDri = getExternalDriResolver(allDRIs, proceededFilePath) + val parser = MarkdownParser(externalDri) val docTag = try { - parser.parse() + parser.parseStringToDocNode(page.code) } catch (e: Throwable) { val msg = "Error rendering (dri = $dri): ${e.message}" println("ERROR: $msg") // TODO (#14): provide proper error handling @@ -105,14 +107,20 @@ class StaticSiteContext(val root: File, cxt: DokkaContext){ PropertyContainer.empty() ) - fun loadFiles(files: List, customChildren: List = emptyList()): List { + fun loadFiles( + files: List, + customChildren: List = emptyList() + ): List { val all = files.mapNotNull { loadTemplate(it) } fun flatten(it: BaseStaticSiteProcessor.LoadedTemplate): List = listOf(it.relativePath(root)) + it.children.flatMap { flatten(it) } fun pathToDri(path: String) = DRI("_.$path") - val driMap = all.flatMap { flatten(it) }.map { it to pathToDri(it) }.toMap() + val driMap = all.flatMap(::flatten) + .flatMap(::createBothMdAndHtmlKeys) + .map { Path.of(it).normalize().run { this to pathToDri(this.toString()) } } + .toMap() fun templateToPage(myTemplate: BaseStaticSiteProcessor.LoadedTemplate): BaseStaticSiteProcessor.StaticPageNode { val dri = pathToDri(myTemplate.relativePath(root)) @@ -126,8 +134,13 @@ class StaticSiteContext(val root: File, cxt: DokkaContext){ println("ERROR: $msg") // TODO (#14): provide proper error handling PreResolvedPage("", null, true) } - val content = parseMarkdown(page, dri, driMap) - val children = myTemplate.children.map { templateToPage(it) } + + val proceededFilePath = myTemplate.file.path + .removePrefix("documentation") + .dropLastWhile { x -> x != File.separatorChar } + .removeSuffix(File.separator) + val content = parseMarkdown(page, dri, driMap, proceededFilePath) + val children = myTemplate.children.map(::templateToPage) return BaseStaticSiteProcessor.StaticPageNode( myTemplate.templateFile.title(), children + customChildren, @@ -137,7 +150,66 @@ class StaticSiteContext(val root: File, cxt: DokkaContext){ content ) } - return all.map { templateToPage(it) } + return all.map(::templateToPage) } -} + private fun createBothMdAndHtmlKeys(x: String) = + listOf( + if (x.endsWith(".md")) x.removeSuffix(".md").plus(".html") else x, + if (x.endsWith(".html")) x.removeSuffix(".html").plus(".md") else x + ) + + private fun getExternalDriResolver(allDRIs: Map, proceededFilePath: String): (String) -> DRI? = { it -> + try { + URL(it) + null + } catch (e: MalformedURLException) { + val resolvePath = { x: String -> + if (it.startsWith('/')) docsFile.resolve(x) + else File(proceededFilePath).resolve(x) + } + val path = Path.of(it).normalize().toString() + .let { s -> resolvePath(s) } + .toString() + .removePrefix(File.separator) + .replace(File.separatorChar, '.') + .let(Path::of) + + allDRIs[path] ?: it.replace("\\s".toRegex(), "").resolveLinkToApi() + } + } + + private fun String.resolveLinkToApi() = when { + '#' in this -> { + val (packageName, classNameAndRest) = split('#') + when { + "::" in classNameAndRest -> { + val (className, callableAndParams) = classNameAndRest.split("::") + makeApiDRI(callableAndParams, packageName, className) + } + else -> DRI(packageName = packageName, classNames = classNameAndRest) + } + } + "::" in this -> { + val (packageName, callableAndParams) = split("::") + makeApiDRI(callableAndParams, packageName) + } + else -> DRI(packageName = this) + } + + private fun makeApiDRI( + callableAndParams: String, + packageName: String, + className: String? = null + ): DRI { + val callableName = callableAndParams.takeWhile { it != '(' } + val params = callableAndParams.dropWhile { it != '(' } + .removePrefix("(") + .removeSuffix(")") + .split(',') + .filter(String::isNotBlank) + .map(::JavaClassReference) + val callable = Callable(name = callableName, params = params) + return DRI(packageName = packageName, classNames = className, callable = callable) + } +} diff --git a/src/main/kotlin/com/virtuslab/dokka/site/parser.kt b/src/main/kotlin/com/virtuslab/dokka/site/parser.kt deleted file mode 100644 index 541d5c1..0000000 --- a/src/main/kotlin/com/virtuslab/dokka/site/parser.kt +++ /dev/null @@ -1,359 +0,0 @@ -package com.virtuslab.dokka.site - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.ast.ASTNode -import org.intellij.markdown.ast.CompositeASTNode -import org.intellij.markdown.ast.LeafASTNode -import org.intellij.markdown.ast.impl.ListItemCompositeNode -import org.intellij.markdown.flavours.gfm.GFMElementTypes -import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor -import org.intellij.markdown.flavours.gfm.GFMTokenTypes -import org.intellij.markdown.parser.MarkdownParser -import org.jetbrains.dokka.base.parsers.factories.DocTagsFromIElementFactory -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import java.net.MalformedURLException -import java.net.URL - - -// TODO (#24): This is a code taken from dokka, will be removed after PR with fixes is merged into dokka -// The dokka logic was pulled to `externalDir` callback and `linksHandler` was simplified -// Bug in imagesHandler was fixed (NullPointer) -open class ExtendableMarkdownParser(private val text: String, private val externalDri: (String) -> DRI?) { - - fun parse(): DocTag { - val flavourDescriptor = GFMFlavourDescriptor() - val markdownAstRoot: ASTNode = MarkdownParser(flavourDescriptor).buildMarkdownTreeFromString(text) - return visitNode(markdownAstRoot) - } - - private fun headersHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, - visitNode(node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT } - ?: throw IllegalStateException("Wrong AST Tree. ATX Header does not contain expected content")).children - ) - - private fun horizontalRulesHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance(MarkdownTokenTypes.HORIZONTAL_RULE) - - private fun emphasisHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, - children = node.children.evaluateChildrenWithDroppedEnclosingTokens(1) - ) - - private fun strongHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, - children = node.children.evaluateChildrenWithDroppedEnclosingTokens(2) - ) - - private fun List.evaluateChildrenWithDroppedEnclosingTokens(count: Int) = - drop(count).dropLast(count).evaluateChildren() - - private fun blockquotesHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, children = node.children - .filterIsInstance() - .evaluateChildren() - ) - - private fun listsHandler(node: ASTNode): DocTag { - - val children = node.children.filterIsInstance().flatMap { - if (it.children.last().type in listOf( - MarkdownElementTypes.ORDERED_LIST, - MarkdownElementTypes.UNORDERED_LIST - ) - ) { - val nestedList = it.children.last() - (it.children as MutableList).removeAt(it.children.lastIndex) - listOf(it, nestedList) - } else - listOf(it) - } - - return DocTagsFromIElementFactory.getInstance( - node.type, - children = - children - .map { - if (it.type == MarkdownElementTypes.LIST_ITEM) - DocTagsFromIElementFactory.getInstance( - it.type, - children = it - .children - .filterIsInstance() - .evaluateChildren() - ) - else - visitNode(it) - }, - params = - if (node.type == MarkdownElementTypes.ORDERED_LIST) { - val listNumberNode = node.children.first().children.first() - mapOf( - "start" to text.substring( - listNumberNode.startOffset, - listNumberNode.endOffset - ).trim().dropLast(1) - ) - } else - emptyMap() - ) - } - - private fun resolveDRI(mdLink: String): DRI? = - mdLink - .removePrefix("[") - .removeSuffix("]") - .let { link -> - try { - URL(link) - null - } catch (e: MalformedURLException) { - externalDri(link) - } - } - - private fun Collection.sorted() = sortedWith( - compareBy( - { it is ClassDescriptor }, - { (it as? FunctionDescriptor)?.name }, - { (it as? FunctionDescriptor)?.valueParameters?.size }, - { (it as? FunctionDescriptor)?.valueParameters?.joinToString { it.type.toString() } } - ) - ) - - private fun referenceLinksHandler(node: ASTNode): DocTag { - val linkLabel = node.children.find { it.type == MarkdownElementTypes.LINK_LABEL } - ?: throw IllegalStateException("Wrong AST Tree. Reference link does not contain expected content") - val linkText = node.children.findLast { it.type == MarkdownElementTypes.LINK_TEXT } ?: linkLabel - - val linkKey = text.substring(linkLabel.startOffset, linkLabel.endOffset) - - return linksHandler(linkText, linkKey) - } - - private fun inlineLinksHandler(node: ASTNode): DocTag { - val linkText = node.children.find { it.type == MarkdownElementTypes.LINK_TEXT } - ?: throw IllegalStateException("Wrong AST Tree. Inline link does not contain expected content") - val linkDestination = node.children.find { it.type == MarkdownElementTypes.LINK_DESTINATION } - ?: throw IllegalStateException("Wrong AST Tree. Inline link does not contain expected content") - val linkTitle = node.children.find { it.type == MarkdownElementTypes.LINK_TITLE } - - val link = text.substring(linkDestination.startOffset, linkDestination.endOffset) - - return linksHandler(linkText, link, linkTitle) - } - - private fun autoLinksHandler(node: ASTNode): DocTag { - val link = text.substring(node.startOffset + 1, node.endOffset - 1) - - return linksHandler(node, link) - } - - private fun linksHandler(linkText: ASTNode, link: String, linkTitle: ASTNode? = null): DocTag { - val dri: DRI? = resolveDRI(link) - val linkTextString = - if (linkTitle == null) link else text.substring(linkTitle.startOffset + 1, linkTitle.endOffset - 1) - - val params = if (linkTitle == null) - mapOf("href" to link) - else - mapOf("href" to link, "title" to linkTextString) - - return DocTagsFromIElementFactory.getInstance( - MarkdownElementTypes.INLINE_LINK, - params = params, - children = linkText.children.drop(1).dropLast(1).evaluateChildren(), - dri = dri - ) - } - - private fun imagesHandler(node: ASTNode): DocTag { - val linkNode = - node.children.last().children.find { it.type == MarkdownElementTypes.LINK_LABEL }?.children?.lastOrNull() - val link = linkNode?.let{ text.substring(it.startOffset, it.endOffset)} ?: "_" - val src = mapOf("src" to link, "href" to link) - return DocTagsFromIElementFactory.getInstance( - node.type, - params = src, - children = listOf(visitNode(node.children.last().children.find { it.type == MarkdownElementTypes.LINK_TEXT }!!)) - ) - } - - private fun codeSpansHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, - children = listOf( - DocTagsFromIElementFactory.getInstance( - MarkdownTokenTypes.TEXT, - body = text.substring(node.startOffset + 1, node.endOffset - 1).replace('\n', ' ').trimIndent() - ) - - ) - ) - - private fun codeFencesHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - node.type, - children = node - .children - .dropWhile { it.type != MarkdownTokenTypes.CODE_FENCE_CONTENT } - .dropLastWhile { it.type != MarkdownTokenTypes.CODE_FENCE_CONTENT } - .map { - if (it.type == MarkdownTokenTypes.EOL) - LeafASTNode(MarkdownTokenTypes.HARD_LINE_BREAK, 0, 0) - else - it - }.evaluateChildren(), - params = node - .children - .find { it.type == MarkdownTokenTypes.FENCE_LANG } - ?.let { mapOf("lang" to text.substring(it.startOffset, it.endOffset)) } - ?: emptyMap() - ) - - private fun codeBlocksHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance(node.type, children = node.children.mergeLeafASTNodes().map { - DocTagsFromIElementFactory.getInstance( - MarkdownTokenTypes.TEXT, - body = text.substring(it.startOffset, it.endOffset) - ) - }) - - private fun defaultHandler(node: ASTNode): DocTag = - DocTagsFromIElementFactory.getInstance( - MarkdownElementTypes.PARAGRAPH, - children = node.children.evaluateChildren() - ) - - fun visitNode(node: ASTNode): DocTag = - when (node.type) { - MarkdownElementTypes.ATX_1, - MarkdownElementTypes.ATX_2, - MarkdownElementTypes.ATX_3, - MarkdownElementTypes.ATX_4, - MarkdownElementTypes.ATX_5, - MarkdownElementTypes.ATX_6 -> headersHandler(node) - MarkdownTokenTypes.HORIZONTAL_RULE -> horizontalRulesHandler(node) - MarkdownElementTypes.STRONG -> strongHandler(node) - MarkdownElementTypes.EMPH -> emphasisHandler(node) - MarkdownElementTypes.FULL_REFERENCE_LINK, - MarkdownElementTypes.SHORT_REFERENCE_LINK -> referenceLinksHandler(node) - MarkdownElementTypes.INLINE_LINK -> inlineLinksHandler(node) - MarkdownElementTypes.AUTOLINK -> autoLinksHandler(node) - MarkdownElementTypes.BLOCK_QUOTE -> blockquotesHandler(node) - MarkdownElementTypes.UNORDERED_LIST, - MarkdownElementTypes.ORDERED_LIST -> listsHandler(node) - MarkdownElementTypes.CODE_BLOCK -> codeBlocksHandler(node) - MarkdownElementTypes.CODE_FENCE -> codeFencesHandler(node) - MarkdownElementTypes.CODE_SPAN -> codeSpansHandler(node) - MarkdownElementTypes.IMAGE -> imagesHandler(node) - MarkdownTokenTypes.HARD_LINE_BREAK -> DocTagsFromIElementFactory.getInstance(node.type) - MarkdownTokenTypes.CODE_FENCE_CONTENT, - MarkdownTokenTypes.CODE_LINE -> DocTagsFromIElementFactory.getInstance( - MarkdownElementTypes.CODE_BLOCK, - body = text.substring(node.startOffset, node.endOffset) - ) - MarkdownTokenTypes.TEXT -> DocTagsFromIElementFactory.getInstance( - MarkdownTokenTypes.TEXT, - body = text.substring(node.startOffset, node.endOffset).transform() - ) - MarkdownElementTypes.MARKDOWN_FILE -> if (node.children.size == 1) visitNode(node.children.first()) else defaultHandler( - node - ) - GFMElementTypes.STRIKETHROUGH -> DocTagsFromIElementFactory.getInstance( - GFMElementTypes.STRIKETHROUGH, - body = text - .substring(node.startOffset, node.endOffset).transform() - ) - GFMElementTypes.TABLE -> DocTagsFromIElementFactory.getInstance( - GFMElementTypes.TABLE, - children = node.children.filterTabSeparators().evaluateChildren() - ) - GFMElementTypes.HEADER -> DocTagsFromIElementFactory.getInstance( - GFMElementTypes.HEADER, - children = node.children.filterTabSeparators().evaluateChildren() - ) - GFMElementTypes.ROW -> DocTagsFromIElementFactory.getInstance( - GFMElementTypes.ROW, - children = node.children.filterTabSeparators().evaluateChildren() - ) - else -> defaultHandler(node) - } - - private fun List.filterTabSeparators() = - this.filterNot { it.type == GFMTokenTypes.TABLE_SEPARATOR } - - private fun List.evaluateChildren(): List = - this.removeUselessTokens().mergeLeafASTNodes().map { visitNode(it) } - - private fun List.removeUselessTokens(): List = - this.filterIndexed { index, node -> - !(node.type == MarkdownElementTypes.LINK_DEFINITION || ( - node.type == MarkdownTokenTypes.EOL && - this.getOrNull(index - 1)?.type == MarkdownTokenTypes.HARD_LINE_BREAK - )) - } - - private val notLeafNodes = listOf(MarkdownTokenTypes.HORIZONTAL_RULE, MarkdownTokenTypes.HARD_LINE_BREAK) - - private fun List.isNotLeaf(index: Int): Boolean = - if (index in 0..this.lastIndex) - (this[index] is CompositeASTNode) || this[index].type in notLeafNodes - else - false - - private fun List.mergeLeafASTNodes(): List { - val children: MutableList = mutableListOf() - var index = 0 - while (index <= this.lastIndex) { - if (this.isNotLeaf(index)) { - children += this[index] - } else { - val startOffset = this[index].startOffset - val sIndex = index - while (index < this.lastIndex) { - if (this.isNotLeaf(index + 1) || this[index + 1].startOffset != this[index].endOffset) { - mergedLeafNode(this, index, startOffset, sIndex)?.run { - children += this - } - break - } - index++ - } - if (index == this.lastIndex) { - mergedLeafNode(this, index, startOffset, sIndex)?.run { - children += this - } - } - } - index++ - } - return children - } - - private fun mergedLeafNode(nodes: List, index: Int, startOffset: Int, sIndex: Int): LeafASTNode? { - val endOffset = nodes[index].endOffset - if (text.substring(startOffset, endOffset).transform().trim().isNotEmpty()) { - val type = if (nodes.subList(sIndex, index) - .any { it.type == MarkdownTokenTypes.CODE_LINE } - ) MarkdownTokenTypes.CODE_LINE else MarkdownTokenTypes.TEXT - return LeafASTNode(type, startOffset, endOffset) - } - return null - } - - private fun String.transform() = this - .replace(Regex("\n\n+"), "") // Squashing new lines between paragraphs - .replace(Regex("\n"), " ") - .replace(Regex(" >+ +"), " ") // Replacement used in blockquotes, get rid of garbage -} - diff --git a/src/test/kotlin/parserTests.kt b/src/test/kotlin/parserTests.kt index 87a2e2b..eabad14 100644 --- a/src/test/kotlin/parserTests.kt +++ b/src/test/kotlin/parserTests.kt @@ -1,21 +1,25 @@ package com.virtuslab.dokka.site +import org.intellij.markdown.MarkdownElementTypes.MARKDOWN_FILE +import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.base.parsers.factories.DocTagsFromIElementFactory import org.jetbrains.dokka.model.doc.* import org.junit.Assert.assertEquals import org.junit.Test class ParserTest { - private fun runTest(md: String, expected: DocTag) { - val parser = ExtendableMarkdownParser(md) { null } - val compiled = parser.parse() - assertEquals(expected, compiled) + private fun runTest(md: String, expected: List) { + val parser = MarkdownParser { null } + val compiled = parser.parseStringToDocNode(md) + val expectedWrapped = DocTagsFromIElementFactory.getInstance(MARKDOWN_FILE, expected) + assertEquals(expectedWrapped, compiled) } @Test - fun simpleTest() = runTest("ala", P(listOf(Text("ala")))) + fun simpleTest() = runTest("ala", listOf(P(listOf(Text("ala"))))) @Test fun code() = runTest( @@ -24,7 +28,7 @@ class ParserTest { def ala() = 123 ``` """.trimIndent(), - CodeBlock(listOf(Text("def ala() = 123")), mapOf("lang" to "scala")) + listOf(CodeBlock(listOf(Text("def ala() = 123")), mapOf("lang" to "scala"))) ) @Test @@ -32,7 +36,7 @@ class ParserTest { """ [link](ala/maKota.md) """.trimIndent(), - P(listOf(A(listOf(Text("link")), mapOf("href" to "ala/maKota.md")))) + listOf(P(listOf(Text(body = "ala/maKota.md", listOf(Text("link")), mapOf("href" to "ala/maKota.md"))))) ) @Test @@ -43,16 +47,14 @@ class ParserTest { - element 1 - element 2 """.trimIndent(), - P( - listOf( - P(listOf(Text("List:"))), - Ul( - listOf( - Li(listOf(P(listOf(Text("element 1"))))), - Li(listOf(P(listOf(Text("element 2"))))) - ) + listOf( + P(listOf(Text("List:"))), + Ul( + listOf( + Li(listOf(P(listOf(Text("element 1"))))), + Li(listOf(P(listOf(Text("element 2"))))) ) ) ) ) -} \ No newline at end of file +}