diff options
Diffstat (limited to 'plugins/templating/src')
8 files changed, 146 insertions, 34 deletions
diff --git a/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt b/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt index 3e7e1290..9531c279 100644 --- a/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt +++ b/plugins/templating/src/main/kotlin/templates/AddToNavigationCommandHandler.kt @@ -13,12 +13,12 @@ import java.util.concurrent.ConcurrentHashMap class AddToNavigationCommandHandler(val context: DokkaContext) : CommandHandler { private val navigationFragments = ConcurrentHashMap<String, Element>() - override fun handleCommand(element: Element, command: Command, input: File, output: File) { + override fun handleCommandAsTag(command: Command, body: Element, input: File, output: File) { command as AddToNavigationCommand context.configuration.modules.find { it.name == command.moduleName } ?.relativePathToOutputDirectory ?.relativeToOrSelf(context.configuration.outputDir) - ?.let { key -> navigationFragments[key.toString()] = element } + ?.let { key -> navigationFragments[key.toString()] = body } } override fun canHandle(command: Command) = command is AddToNavigationCommand diff --git a/plugins/templating/src/main/kotlin/templates/CommandHandler.kt b/plugins/templating/src/main/kotlin/templates/CommandHandler.kt index d72092a1..1956310b 100644 --- a/plugins/templating/src/main/kotlin/templates/CommandHandler.kt +++ b/plugins/templating/src/main/kotlin/templates/CommandHandler.kt @@ -2,10 +2,18 @@ package org.jetbrains.dokka.templates import org.jetbrains.dokka.base.templating.Command import org.jsoup.nodes.Element +import org.jsoup.nodes.Node import java.io.File -interface CommandHandler { - fun handleCommand(element: Element, command: Command, input: File, output: File) + +interface CommandHandler { + @Deprecated("This was renamed to handleCommandAsTag", ReplaceWith("handleCommandAsTag(command, element, input, output)")) + fun handleCommand(element: Element, command: Command, input: File, output: File) { } + + @Suppress("DEPRECATION") + fun handleCommandAsTag(command: Command, body: Element, input: File, output: File) = + handleCommand(body, command, input, output) + fun handleCommandAsComment(command: Command, body: List<Node>, input: File, output: File) { } fun canHandle(command: Command): Boolean fun finish(output: File) {} }
\ No newline at end of file diff --git a/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt index 2b4951a1..7ef4cb10 100644 --- a/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt +++ b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt @@ -1,13 +1,19 @@ package org.jetbrains.dokka.templates import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.renderers.html.TEMPLATE_COMMAND_BEGIN_BORDER +import org.jetbrains.dokka.base.renderers.html.TEMPLATE_COMMAND_END_BORDER +import org.jetbrains.dokka.base.renderers.html.TEMPLATE_COMMAND_SEPARATOR import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.base.templating.parseJson import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.query import org.jsoup.Jsoup +import org.jsoup.nodes.Comment import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode import java.io.File import java.nio.file.Files @@ -20,20 +26,68 @@ class DirectiveBasedHtmlTemplateProcessingStrategy(private val context: DokkaCon if (input.isFile && input.extension == "html") { val document = Jsoup.parse(input, "UTF-8") document.outputSettings().indentAmount(0).prettyPrint(false) + document.select("dokka-template-command").forEach { - handleCommand(it, parseJson(it.attr("data")), input, output) + handleCommandAsTag(it, parseJson(it.attr("data")), input, output) + } + extractCommandsFromComments(document) { command, body -> + val bodyTrimed = + body.dropWhile { node -> (node is TextNode && node.isBlank).also { if (it) node.remove() } } + .dropLastWhile { node -> (node is TextNode && node.isBlank).also { if (it) node.remove() } } + handleCommandAsComment(command, bodyTrimed, input, output) } + Files.write(output.toPath(), listOf(document.outerHtml())) true } else false - fun handleCommand(element: Element, command: Command, input: File, output: File) { + fun handleCommandAsTag(element: Element, command: Command, input: File, output: File) { + traverseHandlers(command) { handleCommandAsTag(command, element, input, output) } + } + + fun handleCommandAsComment(command: Command, body: List<Node>, input: File, output: File) { + traverseHandlers(command) { handleCommandAsComment(command, body, input, output) } + } + + private fun traverseHandlers(command: Command, action: CommandHandler.() -> Unit) { val handlers = directiveBasedCommandHandlers.filter { it.canHandle(command) } if (handlers.isEmpty()) context.logger.warn("Unknown templating command $command") else - handlers.forEach { it.handleCommand(element, command, input, output) } + handlers.forEach(action) + } + private fun extractCommandsFromComments( + node: Node, + startFrom: Int = 0, + handler: (command: Command, body: List<Node>) -> Unit + ) { + val nodes: MutableList<Node> = mutableListOf() + var lastStartBorder: Comment? = null + var firstStartBorder: Comment? = null + for (index in startFrom until node.childNodeSize()) { + when (val currentChild = node.childNode(index)) { + is Comment -> if (currentChild.data?.startsWith(TEMPLATE_COMMAND_BEGIN_BORDER) == true) { + lastStartBorder = currentChild + firstStartBorder = firstStartBorder ?: currentChild + nodes.clear() + } else if (lastStartBorder != null && currentChild.data?.startsWith(TEMPLATE_COMMAND_END_BORDER) == true) { + lastStartBorder.remove() + val cmd: Command? = + lastStartBorder.data?.removePrefix("$TEMPLATE_COMMAND_BEGIN_BORDER$TEMPLATE_COMMAND_SEPARATOR")?.let { parseJson(it) } + cmd?.let { handler(it, nodes) } + currentChild.remove() + extractCommandsFromComments(node, firstStartBorder?.siblingIndex() ?: 0, handler) + return + } else { + if (lastStartBorder != null) nodes.add(currentChild) + } + else -> { + extractCommandsFromComments(currentChild, handler = handler) + if (lastStartBorder != null) nodes.add(currentChild) + } + } + } } override fun finish(output: File) { diff --git a/plugins/templating/src/main/kotlin/templates/ReplaceVersionCommandHandler.kt b/plugins/templating/src/main/kotlin/templates/ReplaceVersionCommandHandler.kt index 8035fc83..02570849 100644 --- a/plugins/templating/src/main/kotlin/templates/ReplaceVersionCommandHandler.kt +++ b/plugins/templating/src/main/kotlin/templates/ReplaceVersionCommandHandler.kt @@ -12,10 +12,10 @@ class ReplaceVersionCommandHandler(private val context: DokkaContext) : CommandH override fun canHandle(command: Command): Boolean = command is ReplaceVersionsCommand - override fun handleCommand(element: Element, command: Command, input: File, output: File) { - val position = element.elementSiblingIndex() - val parent = element.parent() - element.remove() + override fun handleCommandAsTag(command: Command, body: Element, input: File, output: File) { + val position = body.elementSiblingIndex() + val parent = body.parent() + body.remove() context.configuration.moduleVersion?.takeIf { it.isNotEmpty() } ?.let { parent.insertChildren(position, TextNode(it)) } } diff --git a/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt b/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt index c7b15137..178f52dc 100644 --- a/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt +++ b/plugins/templating/src/main/kotlin/templates/SubstitutionCommandHandler.kt @@ -13,28 +13,35 @@ import java.io.File class SubstitutionCommandHandler(context: DokkaContext) : CommandHandler { - override fun handleCommand(element: Element, command: Command, input: File, output: File) { + override fun handleCommandAsTag(command: Command, body: Element, input: File, output: File) { command as SubstitutionCommand - substitute(element, TemplatingContext(input, output, element, command)) + val childrenCopy = body.children().toList() + substitute(childrenCopy, TemplatingContext(input, output, childrenCopy, command)) + + val position = body.elementSiblingIndex() + val parent = body.parent() + body.remove() + + parent?.insertChildren(position, childrenCopy) + } + + override fun handleCommandAsComment(command: Command, body: List<Node>, input: File, output: File) { + command as SubstitutionCommand + substitute(body, TemplatingContext(input, output, body, command)) } override fun canHandle(command: Command): Boolean = command is SubstitutionCommand + override fun finish(output: File) { } + private val substitutors = context.plugin<TemplatingPlugin>().query { substitutor } private fun findSubstitution(commandContext: TemplatingContext<SubstitutionCommand>, match: MatchResult): String = substitutors.asSequence().mapNotNull { it.trySubstitute(commandContext, match) }.firstOrNull() ?: match.value - private fun substitute(element: Element, commandContext: TemplatingContext<SubstitutionCommand>) { + private fun substitute(elements: List<Node>, commandContext: TemplatingContext<SubstitutionCommand>) { val regex = commandContext.command.pattern.toRegex() - element.children().forEach { it.traverseToSubstitute(regex, commandContext) } - - val childrenCopy = element.children().toList() - val position = element.elementSiblingIndex() - val parent = element.parent() - element.remove() - - parent.insertChildren(position, childrenCopy) + elements.forEach { it.traverseToSubstitute(regex, commandContext) } } private fun Node.traverseToSubstitute(regex: Regex, commandContext: TemplatingContext<SubstitutionCommand>) { diff --git a/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt index 5f36530b..01c10067 100644 --- a/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt +++ b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt @@ -12,7 +12,7 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.query import org.jetbrains.dokka.plugability.querySingle -import org.jsoup.nodes.Element +import org.jsoup.nodes.Node import java.io.File interface TemplateProcessor @@ -88,7 +88,7 @@ class DefaultMultiModuleTemplateProcessor( data class TemplatingContext<out T : Command>( val input: File, val output: File, - val element: Element, + val body: List<Node>, val command: T, ) diff --git a/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt index 7001b1ba..aea01970 100644 --- a/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt +++ b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt @@ -7,6 +7,7 @@ import templates.ProjectNameSubstitutor import templates.ReplaceVersionCommandHandler import templates.SourcesetDependencyProcessingStrategy +@Suppress("unused") class TemplatingPlugin : DokkaPlugin() { val submoduleTemplateProcessor by extensionPoint<SubmoduleTemplateProcessor>() diff --git a/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt index ce2a8afd..44acf340 100644 --- a/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt +++ b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt @@ -3,13 +3,15 @@ package org.jetbrains.dokka.templates import kotlinx.html.a import kotlinx.html.div import kotlinx.html.id +import kotlinx.html.span import kotlinx.html.stream.createHTML import org.jetbrains.dokka.DokkaModuleDescriptionImpl import org.jetbrains.dokka.base.renderers.html.templateCommand +import org.jetbrains.dokka.base.renderers.html.templateCommandAsHtmlComment import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand import org.junit.Rule -import org.junit.rules.TemporaryFolder import org.junit.jupiter.api.Test +import org.junit.rules.TemporaryFolder import utils.assertHtmlEqualsIgnoringWhitespace import java.io.File @@ -36,7 +38,56 @@ class SubstitutionCommandResolutionTest : TemplatingAbstractTest() { id = "logo" } } + checkSubstitutedResult(template, expected) + } + + @Test + fun `should handle PathToRootCommand as HTML comment`() { + val template = createHTML().span { + templateCommandAsHtmlComment(PathToRootSubstitutionCommand(pattern = "###", default = "default")) { + this@span.a { + href = "###index.html" + div { + id = "logo" + } + } + templateCommandAsHtmlComment(PathToRootSubstitutionCommand(pattern = "####", default = "default")) { + this@span.a { + href = "####index.html" + div { + id = "logo" + } + } + } + } + } + + val expected = createHTML().span { + a { + href = "../index.html" + div { + id = "logo" + } + } + a { + href = "../index.html" + div { + id = "logo" + } + } + } + checkSubstitutedResult(template, expected) + } + + private fun createDirectoriesAndWriteContent(content: String): File { + folder.create() + val module1 = folder.newFolder("module1") + val module1Content = module1.resolve("index.html") + module1Content.writeText(content) + return module1Content + } + private fun checkSubstitutedResult(template: String, expected:String) { val testedFile = createDirectoriesAndWriteContent(template) val configuration = dokkaConfiguration { @@ -57,13 +108,4 @@ class SubstitutionCommandResolutionTest : TemplatingAbstractTest() { } } } - - private fun createDirectoriesAndWriteContent(content: String): File { - folder.create() - val module1 = folder.newFolder("module1") - val module1Content = module1.resolve("index.html") - module1Content.writeText(content) - return module1Content - } - } |