aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorDmitry Jemerov <yole@jetbrains.com>2016-07-04 19:48:27 +0200
committerDmitry Jemerov <yole@jetbrains.com>2016-07-04 19:48:27 +0200
commitc0064d2b43ec30237d0d39bf5a9aaa4ce5e14744 (patch)
tree603144496856c6730ef3e1076f5ecc4cc94a11f4 /core/src
parent625ea7d5d679399a24877d4f6988d58ce2662a8c (diff)
downloaddokka-c0064d2b43ec30237d0d39bf5a9aaa4ce5e14744.tar.gz
dokka-c0064d2b43ec30237d0d39bf5a9aaa4ce5e14744.tar.bz2
dokka-c0064d2b43ec30237d0d39bf5a9aaa4ce5e14744.zip
Rewrite output generation; much cleaner Markdown and HTML generated. Resolves #71, #72
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/kotlin/Formats/GFMFormatService.kt29
-rw-r--r--core/src/main/kotlin/Formats/HtmlFormatService.kt122
-rw-r--r--core/src/main/kotlin/Formats/HtmlTemplateService.kt3
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt81
-rw-r--r--core/src/main/kotlin/Formats/MarkdownFormatService.kt187
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt397
-rw-r--r--core/src/main/kotlin/Java/JavadocParser.kt2
-rw-r--r--core/src/test/kotlin/format/GFMFormatTest.kt4
-rw-r--r--core/src/test/kotlin/model/JavaTest.kt4
9 files changed, 494 insertions, 335 deletions
diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt
index b57fa932..cfb7fc03 100644
--- a/core/src/main/kotlin/Formats/GFMFormatService.kt
+++ b/core/src/main/kotlin/Formats/GFMFormatService.kt
@@ -9,11 +9,38 @@ open class GFMOutputBuilder(to: StringBuilder,
extension: String)
: MarkdownOutputBuilder(to, location, locationService, languageService, extension)
{
- override fun appendTable(to: StringBuilder, vararg columns: String, body: () -> Unit) {
+ 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(locationService: LocationService,
diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt
index fff8f553..de41d4c6 100644
--- a/core/src/main/kotlin/Formats/HtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt
@@ -14,108 +14,77 @@ open class HtmlOutputBuilder(to: StringBuilder,
val templateService: HtmlTemplateService)
: StructuredOutputBuilder(to, location, locationService, languageService, extension)
{
- override fun formatText(text: String): String {
- return text.htmlEscape()
+ override fun appendText(text: String) {
+ to.append(text.htmlEscape())
}
- override fun formatSymbol(text: String): String {
- return "<span class=\"symbol\">${formatText(text)}</span>"
+ override fun appendSymbol(text: String) {
+ to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
}
- override fun formatKeyword(text: String): String {
- return "<span class=\"keyword\">${formatText(text)}</span>"
+ override fun appendKeyword(text: String) {
+ to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
}
- override fun formatIdentifier(text: String, kind: IdentifierKind, signature: String?): String {
+ override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
val id = signature?.let { " id=\"$it\"" }.orEmpty()
- return "<span class=\"identifier\"$id>${formatText(text)}</span>"
+ to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>")
}
- override fun appendBlockCode(to: StringBuilder, lines: List<String>, language: String) {
- to.append("<pre><code>")
- to.append(lines.joinToString("\n"))
- to.append("</code></pre>")
- }
-
- override fun appendHeader(to: StringBuilder, text: String, level: Int) {
- to.appendln("<h$level>${text}</h$level>")
- }
+ override fun appendBlockCode(language: String, body: () -> Unit) = wrap("<pre><code>", "</code></pre>", body)
- override fun appendParagraph(to: StringBuilder, text: String) {
- to.appendln("<p>${text}</p>")
- }
+ 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 appendLine(to: StringBuilder, text: String) {
- to.appendln("$text<br/>")
+ override fun appendLine() {
+ to.appendln("<br/>")
}
- override fun appendAnchor(to: StringBuilder, anchor: String) {
+ override fun appendAnchor(anchor: String) {
to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>")
}
- override fun appendTable(to: StringBuilder, vararg columns: String, body: () -> Unit) {
- to.appendln("<table>")
- body()
- to.appendln("</table>")
- }
-
- override fun appendTableBody(to: StringBuilder, body: () -> Unit) {
- to.appendln("<tbody>")
- body()
- to.appendln("</tbody>")
- }
-
- override fun appendTableRow(to: StringBuilder, body: () -> Unit) {
- to.appendln("<tr>")
- body()
- to.appendln("</tr>")
- }
-
- override fun appendTableCell(to: StringBuilder, body: () -> Unit) {
- to.appendln("<td>")
- body()
- to.appendln("</td>")
- }
-
- override fun formatLink(text: String, href: String): String {
- return "<a href=\"${href}\">${text}</a>"
- }
-
- override fun formatStrong(text: String): String {
- return "<strong>${text}</strong>"
- }
-
- override fun formatEmphasis(text: String): String {
- return "<emph>${text}</emph>"
- }
+ 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 formatStrikethrough(text: String): String {
- return "<s>${text}</s>"
- }
+ override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
- override fun formatCode(code: String): String {
- return "<code>${code}</code>"
- }
+ override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body)
+ override fun appendEmphasis(body: () -> Unit) = wrapInTag("emph", body)
+ override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
+ override fun appendCode(body: () -> Unit) = wrapInTag("code", body)
- override fun formatUnorderedList(text: String): String = "<ul>${text}</ul>"
- override fun formatOrderedList(text: String): String = "<ol>${text}</ol>"
+ 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 formatListItem(text: String, kind: ListKind): String {
- return "<li>${text}</li>"
+ override fun appendBreadcrumbSeparator() {
+ to.append("&nbsp;/&nbsp;")
}
- override fun formatBreadcrumbs(items: Iterable<FormatLink>): String {
- return items.map { formatLink(it) }.joinToString("&nbsp;/&nbsp;")
- }
-
-
override fun appendNodes(nodes: Iterable<DocumentationNode>) {
templateService.appendHeader(to, getPageTitle(nodes), locationService.calcPathToRoot(location))
super.appendNodes(nodes)
templateService.appendFooter(to)
}
- override fun formatNonBreakingSpace(): String = "&nbsp;"
+ override fun appendNonBreakingSpace() {
+ to.append("&nbsp;")
+ }
+
+ override fun ensureParagraph() {
+ if (!to.endsWith("<p>") && !to.endsWith("</p>")) {
+ to.append("\n<p>")
+ }
+ }
}
open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService,
@@ -143,8 +112,9 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi
override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
val link = ContentNodeDirectLink(node)
link.append(languageService.render(node, LanguageService.RenderMode.FULL))
- val signature = createOutputBuilder(to, location).formatText(location, link)
- to.appendln("<a href=\"${location.path}\">${signature}</a><br/>")
+ val tempBuilder = StringBuilder()
+ createOutputBuilder(tempBuilder, location).appendContent(link)
+ to.appendln("<a href=\"${location.path}\">${tempBuilder.toString()}</a><br/>")
}
override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {
diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
index 13587b05..7efb94a1 100644
--- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt
+++ b/core/src/main/kotlin/Formats/HtmlTemplateService.kt
@@ -10,6 +10,9 @@ interface HtmlTemplateService {
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>")
}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
index e3229523..03cf7fc8 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -18,21 +18,17 @@ class KotlinWebsiteOutputBuilder(to: StringBuilder,
to.appendln("layout: api")
}
- override fun formatBreadcrumbs(items: Iterable<FormatLink>): String {
- items.drop(1)
-
- if (items.count() > 1) {
- return "<div class='api-docs-breadcrumbs'>" +
- items.map { formatLink(it) }.joinToString(" / ") +
- "</div>"
+ override fun appendBreadcrumbs(path: Iterable<FormatLink>) {
+ if (path.count() > 1) {
+ to.append("<div class='api-docs-breadcrumbs'>")
+ super.appendBreadcrumbs(path)
+ to.append("</div>")
}
-
- return ""
}
- override fun formatCode(code: String): String = if (code.length > 0) "<code>$code</code>" else ""
+ override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body)
- override fun formatStrikethrough(text: String): String = "<s>$text</s>"
+ override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body)
private fun div(to: StringBuilder, cssClass: String, block: () -> Unit) {
to.append("<div class=\"$cssClass\">")
@@ -42,7 +38,7 @@ class KotlinWebsiteOutputBuilder(to: StringBuilder,
to.append("</div>\n")
}
- override fun appendAsSignature(to: StringBuilder, node: ContentNode, block: () -> Unit) {
+ override fun appendAsSignature(node: ContentNode, block: () -> Unit) {
val contentLength = node.textLength
if (contentLength == 0) return
div(to, "signature") {
@@ -60,84 +56,81 @@ class KotlinWebsiteOutputBuilder(to: StringBuilder,
to.append("<div class=\"overload-group\"></div>")
}
- override fun formatLink(text: String, href: String): String {
- return "<a href=\"${href}\">${text}</a>"
- }
+ override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
- override fun appendHeader(to: StringBuilder, text: String, level: Int) {
+ override fun appendHeader(level: Int, body: () -> Unit) {
if (insideDiv > 0) {
- to.appendln("<h$level>${text}</h$level>")
+ wrapInTag("p", body, newlineAfterClose = true)
}
else {
- super.appendHeader(to, text, level)
+ super.appendHeader(level, body)
}
}
- override fun appendLine(to: StringBuilder, text: String) {
+ override fun appendLine() {
if (insideDiv > 0) {
- to.appendln("$text<br/>")
+ to.appendln("<br/>")
}
else {
- super.appendLine(to, text)
+ super.appendLine()
}
}
- override fun appendTable(to: StringBuilder, vararg columns: String, body: () -> Unit) {
+ override fun appendTable(vararg columns: String, body: () -> Unit) {
to.appendln("<table class=\"api-docs-table\">")
body()
to.appendln("</table>")
}
- override fun appendTableBody(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableBody(body: () -> Unit) {
to.appendln("<tbody>")
body()
to.appendln("</tbody>")
}
- override fun appendTableRow(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableRow(body: () -> Unit) {
to.appendln("<tr>")
body()
to.appendln("</tr>")
}
- override fun appendTableCell(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableCell(body: () -> Unit) {
to.appendln("<td markdown=\"1\">")
body()
to.appendln("\n</td>")
}
- override fun appendBlockCode(to: StringBuilder, lines: List<String>, language: String) {
+ override fun appendBlockCode(language: String, body: () -> Unit) {
if (language.isNotEmpty()) {
- super.appendBlockCode(to, lines, language)
+ super.appendBlockCode(language, body)
} else {
- to.append("<pre markdown=\"1\">")
- to.append(lines.joinToString { "\n" }.trimStart())
- to.append("</pre>")
+ wrap("<pre markdown=\"1\">", "</pre>", body)
}
}
- override fun formatSymbol(text: String): String {
- return "<span class=\"symbol\">${formatText(text)}</span>"
+ override fun appendSymbol(text: String) {
+ to.append("<span class=\"symbol\">${text.htmlEscape()}</span>")
}
- override fun formatKeyword(text: String): String {
- return "<span class=\"keyword\">${formatText(text)}</span>"
+ override fun appendKeyword(text: String) {
+ to.append("<span class=\"keyword\">${text.htmlEscape()}</span>")
}
- override fun formatIdentifier(text: String, kind: IdentifierKind, signature: String?): String {
+ override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
val id = signature?.let { " id=\"$it\"" }.orEmpty()
- return "<span class=\"${identifierClassName(kind)}\">${formatText(text)}</span>"
+ to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>")
}
- override fun formatSoftLineBreak(): String = if (needHardLineBreaks)
- "<br/>"
- else
- ""
+ override fun appendSoftLineBreak() {
+ if (needHardLineBreaks)
+ to.append("<br/>")
- override fun formatIndentedSoftLineBreak(): String = if (needHardLineBreaks)
- "<br/>&nbsp;&nbsp;&nbsp;&nbsp;"
- else
- ""
+ }
+ override fun appendIndentedSoftLineBreak() {
+ if (needHardLineBreaks) {
+ to.append("<br/>&nbsp;&nbsp;&nbsp;&nbsp;")
+ }
+ }
private fun identifierClassName(kind: IdentifierKind) = when(kind) {
IdentifierKind.ParameterName -> "parameterName"
diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
index 5ddb7f1f..6f2ab327 100644
--- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt
+++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt
@@ -1,7 +1,12 @@
package org.jetbrains.dokka
import com.google.inject.Inject
+import java.util.*
+enum class ListKind {
+ Ordered,
+ Unordered
+}
open class MarkdownOutputBuilder(to: StringBuilder,
location: Location,
@@ -10,94 +15,178 @@ open class MarkdownOutputBuilder(to: StringBuilder,
extension: String)
: StructuredOutputBuilder(to, location, locationService, languageService, extension)
{
- override fun formatBreadcrumbs(items: Iterable<FormatLink>): String {
- return items.map { formatLink(it) }.joinToString(" / ")
- }
-
- override fun formatText(text: String): String = text.htmlEscape()
- override fun formatSymbol(text: String): String = text.htmlEscape()
- override fun formatKeyword(text: String): String = text.htmlEscape()
- override fun formatIdentifier(text: String, kind: IdentifierKind, signature: String?): String = text.htmlEscape()
-
- override fun formatCode(code: String): String {
- return "`$code`"
+ private val listKindStack = Stack<ListKind>()
+ protected var inTableCell = false
+ protected var inCodeBlock = false
+ private var lastTableCellStart = -1
+
+ private fun appendNewline() {
+ while (to.endsWith(' ')) {
+ to.setLength(to.length - 1)
+ }
+ to.appendln()
}
- override fun formatUnorderedList(text: String): String = text + "\n"
- override fun formatOrderedList(text: String): String = text + "\n"
-
- override fun formatListItem(text: String, kind: ListKind): String {
- val itemText = if (text.endsWith("\n")) text else text + "\n"
- return if (kind == ListKind.Unordered) "* $itemText" else "1. $itemText"
+ private fun ensureNewline() {
+ if (inTableCell && listKindStack.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("\n\n")) {
+ if (!to.endsWith('\n')) {
+ appendNewline()
+ }
+ appendNewline()
+ }
+ }
+ override fun appendBreadcrumbSeparator() {
+ to.append(" / ")
+ }
+
+ override fun appendText(text: String) {
+ if (inCodeBlock) {
+ to.append(text)
+ }
+ else {
+ to.append(text.htmlEscape())
+ }
+ }
+
+ override fun appendCode(body: () -> Unit) {
+ inCodeBlock = true
+ wrapIfNotEmpty("`", "`", body, checkEndsWith = true)
+ inCodeBlock = false
+ }
+
+ override fun appendUnorderedList(body: () -> Unit) {
+ listKindStack.push(ListKind.Unordered)
+ body()
+ listKindStack.pop()
+ ensureNewline()
}
- override fun formatStrong(text: String): String {
- return "**$text**"
+ override fun appendOrderedList(body: () -> Unit) {
+ listKindStack.push(ListKind.Ordered)
+ body()
+ listKindStack.pop()
+ ensureNewline()
}
- override fun formatEmphasis(text: String): String {
- return "*$text*"
+ override fun appendListItem(body: () -> Unit) {
+ ensureNewline()
+ to.append(if (listKindStack.peek() == ListKind.Unordered) "* " else "1. ")
+ body()
+ ensureNewline()
}
- override fun formatStrikethrough(text: String): String {
- return "~~$text~~"
- }
+ override fun appendStrong(body: () -> Unit) = wrap("**", "**", body)
+ override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body)
+ override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body)
- override fun formatLink(text: String, href: String): String {
- return "[$text]($href)"
+ override fun appendLink(href: String, body: () -> Unit) {
+ if (inCodeBlock) {
+ wrap("`[`", "`]($href)`", body)
+ }
+ else {
+ wrap("[", "]($href)", body)
+ }
}
- override fun appendLine(to: StringBuilder, text: String) {
- to.appendln(text)
+ override fun appendLine() {
+ if (inTableCell) {
+ to.append("<br>")
+ }
+ else {
+ appendNewline()
+ }
}
- override fun appendAnchor(to: StringBuilder, anchor: String) {
+ override fun appendAnchor(anchor: String) {
// no anchors in Markdown
}
- override fun appendParagraph(to: StringBuilder, text: String) {
- to.appendln()
- to.appendln(text)
- to.appendln()
+ override fun appendParagraph(body: () -> Unit) {
+ if (inTableCell) {
+ ensureNewline()
+ body()
+ }
+ else {
+ ensureParagraph()
+ body()
+ ensureParagraph()
+ }
}
- override fun appendHeader(to: StringBuilder, text: String, level: Int) {
- appendLine(to)
- appendLine(to, "${"#".repeat(level)} $text")
- appendLine(to)
+ override fun appendHeader(level: Int, body: () -> Unit) {
+ ensureParagraph()
+ to.append("${"#".repeat(level)} ")
+ body()
+ ensureParagraph()
}
- override fun appendBlockCode(to: StringBuilder, lines: List<String>, language: String) {
- appendLine(to)
+ override fun appendBlockCode(language: String, body: () -> Unit) {
+ ensureParagraph()
to.appendln(if (language.isEmpty()) "```" else "``` $language")
- to.appendln(lines.joinToString("\n"))
+ body()
+ ensureNewline()
to.appendln("```")
- appendLine(to)
+ appendLine()
}
- override fun appendTable(to: StringBuilder, vararg columns: String, body: () -> Unit) {
- to.appendln()
+ override fun appendTable(vararg columns: String, body: () -> Unit) {
+ ensureParagraph()
body()
- to.appendln()
+ ensureParagraph()
}
- override fun appendTableBody(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableBody(body: () -> Unit) {
body()
}
- override fun appendTableRow(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableRow(body: () -> Unit) {
to.append("|")
body()
- to.appendln()
+ appendNewline()
}
- override fun appendTableCell(to: StringBuilder, body: () -> Unit) {
+ override fun appendTableCell(body: () -> Unit) {
to.append(" ")
+ inTableCell = true
+ lastTableCellStart = to.length
body()
+ inTableCell = false
to.append(" |")
}
- override fun formatNonBreakingSpace(): String = "&nbsp;"
+ override fun appendNonBreakingSpace() {
+ if (inCodeBlock) {
+ to.append(" ")
+ }
+ else {
+ to.append("&nbsp;")
+ }
+ }
}
open class MarkdownFormatService(locationService: LocationService,
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 407c7018..f24c9c2d 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -5,95 +5,162 @@ import java.util.*
data class FormatLink(val text: String, val href: String)
-enum class ListKind {
- Ordered,
- Unordered
-}
-
abstract class StructuredOutputBuilder(val to: StringBuilder,
val location: Location,
val locationService: LocationService,
val languageService: LanguageService,
val extension: String) : FormattedOutputBuilder {
- abstract fun appendBlockCode(to: StringBuilder, lines: List<String>, language: String)
- abstract fun appendHeader(to: StringBuilder, text: String, level: Int = 1)
- abstract fun appendParagraph(to: StringBuilder, text: String)
- abstract fun appendLine(to: StringBuilder, text: String = "")
- abstract fun appendAnchor(to: StringBuilder, anchor: String)
-
- abstract fun appendTable(to: StringBuilder, vararg columns: String, body: () -> Unit)
- abstract fun appendTableBody(to: StringBuilder, body: () -> Unit)
- abstract fun appendTableRow(to: StringBuilder, body: () -> Unit)
- abstract fun appendTableCell(to: StringBuilder, body: () -> Unit)
-
- abstract fun formatText(text: String): String
- abstract fun formatSymbol(text: String): String
- abstract fun formatKeyword(text: String): String
- abstract fun formatIdentifier(text: String, kind: IdentifierKind, signature: String?): String
- fun formatEntity(text: String): String = text
- abstract fun formatLink(text: String, href: String): String
- open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.href)
- abstract fun formatStrong(text: String): String
- abstract fun formatStrikethrough(text: String): String
- abstract fun formatEmphasis(text: String): String
- abstract fun formatCode(code: String): String
- abstract fun formatUnorderedList(text: String): String
- abstract fun formatOrderedList(text: String): String
- abstract fun formatListItem(text: String, kind: ListKind): String
- abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
- abstract fun formatNonBreakingSpace(): String
- open fun formatSoftLineBreak(): String = ""
- open fun formatIndentedSoftLineBreak(): String = ""
-
- open fun formatText(location: Location, nodes: Iterable<ContentNode>, listKind: ListKind = ListKind.Unordered): String {
- return nodes.map { formatText(location, it, listKind) }.joinToString("")
+ 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()
+
+ abstract fun appendBlockCode(language: String, body: () -> Unit)
+ abstract fun appendHeader(level: Int = 1, body: () -> Unit)
+ abstract fun appendParagraph(body: () -> Unit)
+ 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 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 formatText(location: Location, content: ContentNode, listKind: ListKind = ListKind.Unordered): String {
- return StringBuilder().apply { formatText(location, content, this, listKind) }.toString()
+ fun appendContent(content: List<ContentNode>) {
+ for (contentNode in content) {
+ appendContent(contentNode)
+ }
}
- open fun formatText(location: Location, content: ContentNode, to: StringBuilder, listKind: ListKind = ListKind.Unordered) {
+ open fun appendContent(content: ContentNode) {
when (content) {
- is ContentText -> to.append(formatText(content.text))
- is ContentSymbol -> to.append(formatSymbol(content.text))
- is ContentKeyword -> to.append(formatKeyword(content.text))
- is ContentIdentifier -> to.append(formatIdentifier(content.text, content.kind, content.signature))
- is ContentNonBreakingSpace -> to.append(formatNonBreakingSpace())
- is ContentSoftLineBreak -> to.append(formatSoftLineBreak())
- is ContentIndentedSoftLineBreak -> to.append(formatIndentedSoftLineBreak())
- is ContentEntity -> to.append(formatEntity(content.text))
- is ContentStrong -> to.append(formatStrong(formatText(location, content.children)))
- is ContentStrikethrough -> to.append(formatStrikethrough(formatText(location, content.children)))
- is ContentCode -> to.append(formatCode(formatText(location, content.children)))
- is ContentEmphasis -> to.append(formatEmphasis(formatText(location, content.children)))
- is ContentUnorderedList -> to.append(formatUnorderedList(formatText(location, content.children, ListKind.Unordered)))
- is ContentOrderedList -> to.append(formatOrderedList(formatText(location, content.children, ListKind.Ordered)))
- is ContentListItem -> to.append(formatListItem(formatText(location, content.children), listKind))
+ 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 "#"
- val linkText = formatText(location, content.children)
- if (linkTo == ".") {
- to.append(linkText)
- } else {
- to.append(formatLink(linkText, linkTo))
+ appendLinkIfNotThisPage(linkTo, content)
+ }
+ is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content)
+
+ is ContentParagraph -> {
+ if (!content.isEmpty()) {
+ appendParagraph { appendContent(content.children) }
}
}