From 62cb509a1a5adf0e5f9ba8d8e7545a93eb8516b2 Mon Sep 17 00:00:00 2001 From: Ilya Ryzhenkov Date: Tue, 15 Jul 2014 15:54:05 +0400 Subject: Refactor formatting service to detach grouping logic from markup --- src/Formats/FormatService.kt | 5 +- src/Formats/HtmlFormatService.kt | 94 +++++++++++-------------- src/Formats/JekyllFormatService.kt | 11 +-- src/Formats/MarkdownFormatService.kt | 123 +++++++++++---------------------- src/Formats/StructuredFormatService.kt | 107 ++++++++++++++++++++++++++++ src/Formats/TextFormatService.kt | 3 +- src/Locations/LocationService.kt | 9 ++- src/main.kt | 4 +- 8 files changed, 207 insertions(+), 149 deletions(-) create mode 100644 src/Formats/StructuredFormatService.kt (limited to 'src') diff --git a/src/Formats/FormatService.kt b/src/Formats/FormatService.kt index 3708e9a7..b106692a 100644 --- a/src/Formats/FormatService.kt +++ b/src/Formats/FormatService.kt @@ -2,7 +2,8 @@ package org.jetbrains.dokka public trait FormatService { val extension: String - fun format(nodes: Iterable, to: StringBuilder) + fun appendNodes(to: StringBuilder, + nodes: Iterable) } -fun FormatService.format(node: Iterable): String = StringBuilder { format(node, this) }.toString() \ No newline at end of file +fun FormatService.format(nodes: Iterable): String = StringBuilder { appendNodes(this, nodes) }.toString() \ No newline at end of file diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt index f3d71215..6c004509 100644 --- a/src/Formats/HtmlFormatService.kt +++ b/src/Formats/HtmlFormatService.kt @@ -1,64 +1,50 @@ package org.jetbrains.dokka -public class HtmlFormatService(val locationService: LocationService, - val signatureGenerator: SignatureGenerator) : FormatService { +public open class HtmlFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) +: StructuredFormatService(locationService, signatureGenerator) { override val extension: String = "html" - override fun format(nodes: Iterable, to: StringBuilder) { - for (node in nodes) { - with (to) { - appendln("

") - appendln("Summary for ${node.name}") - appendln("

") - appendln("") - appendln(signatureGenerator.render(node)) - appendln("") - appendln() - appendln("

") - appendln(node.doc.summary) - appendln("

") - appendln("
") - for (section in node.doc.sections) { - appendln("

") - appendln(section.label) - appendln("

") - appendln("

") - appendln(section.text) - appendln("

") - } + override fun appendBlockCode(to: StringBuilder, line: String) { + to.appendln("") + to.appendln(line) + to.appendln("") + } + + override fun appendBlockCode(to: StringBuilder, lines: Iterable) { + to.appendln("") + to.appendln(lines.join("\n")) + to.appendln("") + } + + override fun appendHeader(to: StringBuilder, text: String, level: Int) { + to.appendln("$text") + } + + override fun appendText(to: StringBuilder, text: String) { + to.appendln("

$text

") + } - appendln("

") - appendln("Members") - appendln("

") - appendln("") + override fun appendLine(to: StringBuilder, text: String) { + to.appendln("$text
") + } + + override fun appendLine(to: StringBuilder) { + to.appendln("
") + } - appendln("") - appendln("") - appendln("") - appendln("") - appendln("") - appendln("") - appendln("") + override fun formatLink(link: FormatLink): String { + return "${link.text}" + } - appendln("") - for (member in node.members.sortBy { it.name }) { - val relativePath = locationService.relativeLocation(node, member, extension) - appendln("") - appendln("") - appendln("") - appendln("") - appendln("") - } - appendln("") - appendln("
MemberSignatureSummary
") - append("${member.name}") - appendln("") - append("${signatureGenerator.render(member)}") - appendln("") - append("${member.doc.summary}") - appendln("
") + override fun formatBold(text: String): String { + return "$text" + } + + override fun formatCode(code: String): String { + return "$code" + } - } - } + override fun formatBreadcrumbs(items: Iterable): String { + return items.map { formatLink(it) }.joinToString(" / ") } } \ No newline at end of file diff --git a/src/Formats/JekyllFormatService.kt b/src/Formats/JekyllFormatService.kt index 1b855b01..ff53827c 100644 --- a/src/Formats/JekyllFormatService.kt +++ b/src/Formats/JekyllFormatService.kt @@ -2,12 +2,15 @@ package org.jetbrains.dokka public class JekyllFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) : MarkdownFormatService(locationService, signatureGenerator) { - override val extension: String = "md" - override fun format(nodes: Iterable, to: StringBuilder) { + + override fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, "html") + + override fun appendNodes(to: StringBuilder, + nodes: Iterable) { to.appendln("---") - to.appendln("layout: post") + to.appendln("layout: api") to.appendln("title: ${nodes.first().name}") to.appendln("---") - super.format(nodes, to) + super.appendNodes(to, nodes) } } \ No newline at end of file diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index 0fcfa444..2a91a4a3 100644 --- a/src/Formats/MarkdownFormatService.kt +++ b/src/Formats/MarkdownFormatService.kt @@ -1,98 +1,55 @@ package org.jetbrains.dokka -import org.jetbrains.dokka.DocumentationNode.Kind -import java.util.LinkedHashMap -public open class MarkdownFormatService(val locationService: LocationService, - val signatureGenerator: SignatureGenerator) : FormatService { +public open class MarkdownFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) +: StructuredFormatService(locationService, signatureGenerator) { + override val extension: String = "md" - override fun format(nodes: Iterable, to: StringBuilder) { - with (to) { - val breakdown = nodes.groupByTo(LinkedHashMap()) { node -> - node.path.map { "[${it.name}](${locationService.relativeLocation(node, it, extension)})" }.joinToString(" / ") - } - for ((path, items) in breakdown) { - appendln(path) - appendln() - formatLocation(items) - } - for (node in nodes) { - if (node.members.any()) { - appendln("## Members") - appendln("| Name | Summary |") - appendln("|------|---------|") - val children = node.members.sortBy { it.name } - val membersMap = children.groupByTo(LinkedHashMap()) { locationService.relativeLocation(node, it, extension) } - for ((location, members) in membersMap) { - val mainMember = members.first() - val displayName = when (mainMember.kind) { - Kind.Constructor -> "*.init*" - else -> signatureGenerator.renderName(mainMember).htmlEscape() - } - append("|[${displayName}](${location})|") - append(members.groupByTo(LinkedHashMap()) { it.doc.summary }.map { group -> - val (summary, items) = group - StringBuilder { - if (!summary.isEmpty()) { - append("${summary}
") - } - for (item in items) { - append("  `${signatureGenerator.render(item)}`
") - } - }.toString() - }.joinToString("
")) - appendln("|") - } - } + override public fun formatBreadcrumbs(items: Iterable): String { + return items.map { formatLink(it) }.joinToString(" / ") + } + + override public fun formatCode(code: String): String { + return "`$code`" + } - } - } + override public fun formatBold(text: String): String { + return "**$text**" } + override public fun formatLink(link: FormatLink): String { + return "[${link.text}](${link.location.path})" + } - private fun StringBuilder.formatLocation(nodes: Iterable) { - val breakdown = nodes.groupByTo(LinkedHashMap()) { node -> - node.name - } - for ((name, items) in breakdown) { - appendln("# ${name}") - formatSummary(items) - } + override public fun appendLine(to: StringBuilder) { + to.appendln() } - private fun StringBuilder.formatSummary(nodes: Iterable) { - val breakdown = nodes.groupByTo(LinkedHashMap()) { node -> - node.doc.summary - } - for ((summary, items) in breakdown) { - appendln(summary) - appendln("```") - for (item in items) - appendln(signatureGenerator.render(item)) - appendln("```") - } + override public fun appendLine(to: StringBuilder, text: String) { + to.appendln(text) + } - val described = nodes.filter { it.doc.hasDescription } - if (described.any()) { - appendln("## Description") - for (node in described) { - appendln("```") - appendln(signatureGenerator.render(node)) - appendln("```") - appendln(node.doc.description) - appendln() - for (section in node.doc.sections) { - append("**") - append(section.label) - append("**") - appendln() - append(section.text) - appendln() - appendln() - } - } - } + override public fun appendText(to: StringBuilder, text: String) { + to.append(text) } + override public fun appendHeader(to: StringBuilder, text: String, level: Int) { + appendLine(to) + appendLine(to, "${"#".repeat(level)} $text") + appendLine(to) + } + + override public fun appendBlockCode(to: StringBuilder, lines: Iterable) { + appendLine(to, "```") + for (line in lines) + appendLine(to, line) + appendLine(to, "```") + } + + override public fun appendBlockCode(to: StringBuilder, line: String) { + appendLine(to, "```") + appendLine(to, line) + appendLine(to, "```") + } } diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt new file mode 100644 index 00000000..a490df67 --- /dev/null +++ b/src/Formats/StructuredFormatService.kt @@ -0,0 +1,107 @@ +package org.jetbrains.dokka + +import java.util.LinkedHashMap +import org.jetbrains.dokka.DocumentationNode.Kind + +public data class FormatLink(val text: String, val location: Location) + +public abstract class StructuredFormatService(val locationService: LocationService, + val signatureGenerator: SignatureGenerator) : FormatService { + + abstract public fun appendBlockCode(to: StringBuilder, line: String) + abstract public fun appendBlockCode(to: StringBuilder, lines: Iterable) + abstract public fun appendHeader(to: StringBuilder, text: String, level: Int = 1) + abstract public fun appendText(to: StringBuilder, text: String) + abstract public fun appendLine(to: StringBuilder, text: String) + public abstract fun appendLine(to: StringBuilder) + public abstract fun formatLink(link: FormatLink): String + public abstract fun formatBold(text: String): String + public abstract fun formatCode(code: String): String + public abstract fun formatBreadcrumbs(items: Iterable): String + + open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension) + + open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink { + return FormatLink(to.name, locationService.relativeLocation(from, to, extension)) + } + + open public fun appendDescription(to: StringBuilder, nodes: Iterable) { + val described = nodes.filter { it.doc.hasDescription } + if (described.any()) { + appendHeader(to, "Description") + for (node in described) { + appendBlockCode(to, signatureGenerator.render(node)) + appendLine(to, node.doc.description) + appendLine(to) + for (section in node.doc.sections) { + appendLine(to, formatBold(section.label)) + appendLine(to, section.text) + appendLine(to) + } + } + } + } + + open public fun appendSummary(to: StringBuilder, nodes: Iterable) { + val breakdownBySummary = nodes.groupByTo(LinkedHashMap()) { node -> + node.doc.summary + } + + for ((summary, items) in breakdownBySummary) { + appendLine(to, summary) + appendBlockCode(to, items.map { signatureGenerator.render(it) }) + } + } + + open public fun appendLocation(to: StringBuilder, nodes: Iterable) { + val breakdownByName = nodes.groupByTo(LinkedHashMap()) { node -> node.name } + for ((name, items) in breakdownByName) { + appendHeader(to, "${name}") + appendSummary(to, items) + appendDescription(to, items) + } + } + + override fun appendNodes(to: StringBuilder, nodes: Iterable) { + val breakdownByLocation = nodes.groupByTo(LinkedHashMap()) { node -> + formatBreadcrumbs(node.path.map { link(node, it) }) + } + + for ((breadcrumbs, items) in breakdownByLocation) { + appendLine(to, breadcrumbs) + appendLine(to) + appendLocation(to, items) + } + + for (node in nodes) { + if (node.members.any()) { + appendHeader(to, "Members") + + appendLine(to, "| Name | Summary |") + appendLine(to, "|------|---------|") + val children = node.members.sortBy { it.name } + val membersMap = children.groupByTo(LinkedHashMap()) { link(node, it) } + + for ((location, members) in membersMap) { + val mainMember = members.first() + val displayName = when (mainMember.kind) { + Kind.Constructor -> "*.init*" + else -> signatureGenerator.renderName(mainMember).htmlEscape() + } + + appendText(to, "|${formatLink(location)}|") + + val breakdownBySummary = members.groupByTo(LinkedHashMap()) { it.doc.summary } + for ((summary, items) in breakdownBySummary) { + appendLine(to, summary) + appendBlockCode(to, items.map { formatBold("${signatureGenerator.render(it)}") }) + } + + appendLine(to, "|") + } + } + + } + } + +} \ No newline at end of file diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt index 2fb2a0a3..f309ad96 100644 --- a/src/Formats/TextFormatService.kt +++ b/src/Formats/TextFormatService.kt @@ -2,7 +2,8 @@ package org.jetbrains.dokka public class TextFormatService(val signatureGenerator: SignatureGenerator) : FormatService { override val extension: String = "txt" - override fun format(nodes: Iterable, to: StringBuilder) { + override fun appendNodes(to: StringBuilder, + nodes: Iterable) { for (node in nodes) { with (to) { appendln(signatureGenerator.render(node)) diff --git a/src/Locations/LocationService.kt b/src/Locations/LocationService.kt index 03f22d08..6472e906 100644 --- a/src/Locations/LocationService.kt +++ b/src/Locations/LocationService.kt @@ -2,7 +2,10 @@ package org.jetbrains.dokka import java.io.File -data class Location(val file: File) +public data class Location(val file: File) { + public val path : String + get() = file.path +} public trait LocationService { fun location(node: DocumentationNode): Location @@ -11,8 +14,8 @@ public trait LocationService { public fun escapeUri(path: String): String = path.replace('<', '_').replace('>', '_') -fun LocationService.relativeLocation(node: DocumentationNode, link: DocumentationNode, extension: String): File { +fun LocationService.relativeLocation(node: DocumentationNode, link: DocumentationNode, extension: String): Location { val ownerFolder = location(node).file.getParentFile()!! val memberPath = location(link).file.appendExtension(extension) - return ownerFolder.getRelativePath(memberPath) + return Location(ownerFolder.getRelativePath(memberPath)) } diff --git a/src/main.kt b/src/main.kt index 9da545d7..f9b734a3 100644 --- a/src/main.kt +++ b/src/main.kt @@ -57,8 +57,8 @@ public fun main(args: Array) { val signatureGenerator = KotlinSignatureGenerator() val locationService = FoldersLocationService(arguments.outputDir) - val markdown = JekyllFormatService(locationService, signatureGenerator) - val generator = FileGenerator(signatureGenerator, locationService, markdown) + val formatter = HtmlFormatService(locationService, signatureGenerator) + val generator = FileGenerator(signatureGenerator, locationService, formatter) generator.generate(documentation) Disposer.dispose(environment) } \ No newline at end of file -- cgit