diff options
author | Ilya Ryzhenkov <orangy@jetbrains.com> | 2014-09-19 22:25:27 +0300 |
---|---|---|
committer | Ilya Ryzhenkov <orangy@jetbrains.com> | 2014-09-19 22:25:27 +0300 |
commit | 455d74a6a6245969ebd7ec20071b691ed20a2628 (patch) | |
tree | 601e80982ddc289d1745a5570347a15512f4fe7d | |
parent | ff0c8d7514742b505acff2f2ba51dc21aedbd949 (diff) | |
download | dokka-455d74a6a6245969ebd7ec20071b691ed20a2628.tar.gz dokka-455d74a6a6245969ebd7ec20071b691ed20a2628.tar.bz2 dokka-455d74a6a6245969ebd7ec20071b691ed20a2628.zip |
Convert content to RichString and establish resolution service for links.
-rw-r--r-- | src/Formats/HtmlFormatService.kt | 20 | ||||
-rw-r--r-- | src/Formats/HtmlTemplateService.kt | 28 | ||||
-rw-r--r-- | src/Formats/JekyllFormatService.kt | 9 | ||||
-rw-r--r-- | src/Formats/MarkdownFormatService.kt | 10 | ||||
-rw-r--r-- | src/Formats/StructuredFormatService.kt | 33 | ||||
-rw-r--r-- | src/Formats/TextFormatService.kt | 2 | ||||
-rw-r--r-- | src/Generation/FileGenerator.kt | 1 | ||||
-rw-r--r-- | src/Model/DocumentationContent.kt | 65 | ||||
-rw-r--r-- | src/Resolution/ResolutionService.kt | 5 | ||||
-rw-r--r-- | src/RichContent/RichString.kt | 83 |
10 files changed, 224 insertions, 32 deletions
diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt index e6d09991..550899ff 100644 --- a/src/Formats/HtmlFormatService.kt +++ b/src/Formats/HtmlFormatService.kt @@ -1,7 +1,10 @@ package org.jetbrains.dokka -public open class HtmlFormatService(locationService: LocationService, signatureGenerator: LanguageService) -: StructuredFormatService(locationService, signatureGenerator) { +public open class HtmlFormatService(locationService: LocationService, + resolutionService: ResolutionService, + signatureGenerator: LanguageService, + val templateService: HtmlTemplateService = HtmlTemplateService.default()) +: StructuredFormatService(locationService, resolutionService, signatureGenerator) { override val extension: String = "html" override public fun formatText(text: String): String { @@ -10,13 +13,13 @@ public open class HtmlFormatService(locationService: LocationService, signatureG override fun appendBlockCode(to: StringBuilder, line: String) { to.appendln("<code>") - to.appendln(line) + to.appendln(line.htmlEscape()) to.appendln("</code>") } override fun appendBlockCode(to: StringBuilder, lines: Iterable<String>) { to.appendln("<code>") - to.appendln(lines.map { it }.join("\n")) + to.appendln(lines.map { it.htmlEscape() }.join("\n")) to.appendln("</code>") } @@ -75,13 +78,20 @@ public open class HtmlFormatService(locationService: LocationService, signatureG } override fun formatCode(code: String): String { - return "<code>${code}</code>" + return "<code>${code.htmlEscape()}</code>" } override fun formatBreadcrumbs(items: Iterable<FormatLink>): String { return items.map { formatLink(it) }.joinToString(" / ") } + + override fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) { + templateService.appendHeader(to) + super<StructuredFormatService>.appendNodes(to, nodes) + templateService.appendFooter(to) + } + override fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>) { } override fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode) { diff --git a/src/Formats/HtmlTemplateService.kt b/src/Formats/HtmlTemplateService.kt new file mode 100644 index 00000000..5bb03fbd --- /dev/null +++ b/src/Formats/HtmlTemplateService.kt @@ -0,0 +1,28 @@ +package org.jetbrains.dokka + +public trait HtmlTemplateService { + fun appendHeader(to: StringBuilder) + fun appendFooter(to: StringBuilder) + + class object { + public fun default(css: String? = null): HtmlTemplateService { + return object : HtmlTemplateService { + override fun appendFooter(to: StringBuilder) { + to.appendln("</BODY>") + to.appendln("</HTML>") + } + override fun appendHeader(to: StringBuilder) { + to.appendln("<HTML>") + to.appendln("<HEAD>") + if (css != null) { + to.appendln("<link rel=\"stylesheet\" href=\"$css\">") + } + to.appendln("</HEAD>") + to.appendln("<BODY>") + } + } + } + } +} + + diff --git a/src/Formats/JekyllFormatService.kt b/src/Formats/JekyllFormatService.kt index aeb1e9ef..459b5113 100644 --- a/src/Formats/JekyllFormatService.kt +++ b/src/Formats/JekyllFormatService.kt @@ -1,12 +1,13 @@ package org.jetbrains.dokka -public class JekyllFormatService(locationService: LocationService, signatureGenerator: LanguageService) -: MarkdownFormatService(locationService, signatureGenerator) { +public class JekyllFormatService(locationService: LocationService, + resolutionService: ResolutionService, + signatureGenerator: LanguageService) +: MarkdownFormatService(locationService, resolutionService, signatureGenerator) { override fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, "html") - override fun appendNodes(to: StringBuilder, - nodes: Iterable<DocumentationNode>) { + override fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) { to.appendln("---") to.appendln("layout: api") to.appendln("title: ${nodes.first().name}") diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index 60e38290..d7530a25 100644 --- a/src/Formats/MarkdownFormatService.kt +++ b/src/Formats/MarkdownFormatService.kt @@ -1,8 +1,10 @@ package org.jetbrains.dokka -public open class MarkdownFormatService(locationService: LocationService, signatureGenerator: LanguageService) -: StructuredFormatService(locationService, signatureGenerator) { +public open class MarkdownFormatService(locationService: LocationService, + resolutionService: ResolutionService, + signatureGenerator: LanguageService) +: StructuredFormatService(locationService, resolutionService, signatureGenerator) { override val extension: String = "md" @@ -14,6 +16,10 @@ public open class MarkdownFormatService(locationService: LocationService, signat return text.htmlEscape() } + override public fun formatText(text: RichString): String { + return text.toString().htmlEscape() + } + override public fun formatCode(code: String): String { return "`$code`" } diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index 5ec86486..339ccf73 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -5,6 +5,7 @@ import java.util.LinkedHashMap public data class FormatLink(val text: String, val location: Location) public abstract class StructuredFormatService(val locationService: LocationService, + val resolutionService: ResolutionService, val languageService: LanguageService) : FormatService { abstract public fun appendBlockCode(to: StringBuilder, line: String) @@ -27,6 +28,24 @@ public abstract class StructuredFormatService(val locationService: LocationServi public abstract fun formatCode(code: String): String public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String + open fun formatText(text: RichString): String { + return StringBuilder { + for (slice in text.slices) { + val style = slice.style + when (style) { + is NormalStyle -> append(slice.text) + is BoldStyle -> append(formatBold(slice.text)) + is CodeStyle -> append(formatCode(slice.text)) + is LinkStyle -> { + val node = resolutionService.resolve(style.link) + val location = locationService.location(node) + append(formatLink(slice.text, location)) + } + } + } + }.toString() + } + open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension) open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink { @@ -34,7 +53,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi } open public fun appendDescription(to: StringBuilder, nodes: Iterable<DocumentationNode>) { - val described = nodes.filter { it.doc.hasDescription } + val described = nodes.filter { !it.doc.isEmpty } if (described.any()) { val single = described.size == 1 appendHeader(to, "Description", 3) @@ -59,8 +78,9 @@ public abstract class StructuredFormatService(val locationService: LocationServi } for ((summary, items) in breakdownBySummary) { - appendLine(to, summary) appendBlockCode(to, items.map { languageService.render(it) }) + appendLine(to, formatText(summary)) + appendLine(to) } } @@ -101,13 +121,14 @@ public abstract class StructuredFormatService(val locationService: LocationServi appendTableCell(to) { val breakdownBySummary = members.groupBy { it.doc.summary } for ((summary, items) in breakdownBySummary) { + val signatures = items.map { formatCode("${languageService.render(it)}") } + for (signature in signatures) { + appendText(to, signature) + } + if (!summary.isEmpty()) { appendText(to, formatText(summary)) - to.append("<br/>") // TODO: hardcoded } - - val signatures = items.map { formatBold(formatCode("${languageService.render(it)}")) } - to.append(signatures.join("<br/>")) // TODO: hardcoded } } } diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt index b7863c25..29f01a74 100644 --- a/src/Formats/TextFormatService.kt +++ b/src/Formats/TextFormatService.kt @@ -9,7 +9,7 @@ public class TextFormatService(val signatureGenerator: LanguageService) : Format appendln(signatureGenerator.render(node)) appendln() appendln(node.doc.summary) - for (n in node.doc.summary.indices) + for (n in 0..node.doc.summary.length()) append("=") for (section in node.doc.sections) { diff --git a/src/Generation/FileGenerator.kt b/src/Generation/FileGenerator.kt index 3e410d39..df6bbd79 100644 --- a/src/Generation/FileGenerator.kt +++ b/src/Generation/FileGenerator.kt @@ -2,7 +2,6 @@ package org.jetbrains.dokka import java.io.FileOutputStream import java.io.OutputStreamWriter -import java.util.LinkedHashMap public class FileGenerator(val signatureGenerator: LanguageService, val locationService: LocationService, diff --git a/src/Model/DocumentationContent.kt b/src/Model/DocumentationContent.kt index 5ea745c5..e8f32a33 100644 --- a/src/Model/DocumentationContent.kt +++ b/src/Model/DocumentationContent.kt @@ -3,13 +3,15 @@ package org.jetbrains.dokka import org.jetbrains.jet.lang.descriptors.* import org.jetbrains.jet.lang.resolve.BindingContext -class DocumentationContentSection(val label: String, val text: String) { +public class DocumentationContentSection(public val label: String, public val text: RichString) { override fun toString(): String { return "$label = $text" } } -class DocumentationContent(val summary: String, val description: String, val sections: List<DocumentationContentSection>) { +public class DocumentationContent(public val summary: RichString, + public val description: RichString, + public val sections: List<DocumentationContentSection>) { override fun equals(other: Any?): Boolean { if (other !is DocumentationContent) @@ -31,15 +33,15 @@ class DocumentationContent(val summary: String, val description: String, val sec override fun toString(): String { if (sections.isEmpty()) - return summary + return summary.toString() return "$summary | " + sections.joinToString() } - val hasDescription : Boolean - get() = !description.isEmpty() || sections.any() + val isEmpty: Boolean + get() = description.isEmpty() && sections.none() class object { - val Empty = DocumentationContent("", "", listOf()) + val Empty = DocumentationContent(RichString.empty, RichString.empty, listOf()) } } @@ -47,8 +49,27 @@ class DocumentationContent(val summary: String, val description: String, val sec fun BindingContext.getDocumentation(descriptor: DeclarationDescriptor): DocumentationContent { val docText = getDocumentationElements(descriptor).map { it.extractText() }.join("\n") val sections = docText.parseSections() - val content = sections.first().text - return DocumentationContent(content.substringBefore('\n'), content.substringAfter('\n'), sections.drop(1)) + val (summary, description) = sections.extractSummaryAndDescription() + return DocumentationContent(summary, description, sections.drop(1)) +} + +fun List<DocumentationContentSection>.extractSummaryAndDescription() : Pair<RichString, RichString> { + val summary = firstOrNull { it.label == "\$summary" } + if (summary != null) { + val description = firstOrNull { it.label == "\$description" } + return Pair(summary.text, description?.text ?: RichString.empty) + } + + val description = firstOrNull { it.label == "\$description" } + if (description != null) { + return Pair(RichString.empty, description.text) + } + + val default = firstOrNull { it.label == "" }?.text + if (default == null) + return Pair(RichString.empty, RichString.empty) + + return default.splitBy("\n") } fun String.parseLabel(index: Int): Pair<String, Int> { @@ -62,9 +83,17 @@ fun String.parseLabel(index: Int): Pair<String, Int> { } return substring(index, length) to length } + c == '$' -> { + for (end in index + 1..length - 1) { + if (Character.isWhitespace(get(end))) { + return substring(index, end) to end + } + } + return substring(index, length) to length + } c == '{' -> { val end = indexOf('}', index + 1) - return substring(index + 1, end) to end + 2 + return substring(index + 1, end) to end + 1 } } return "" to -1 @@ -79,12 +108,14 @@ fun String.parseSections(): List<DocumentationContentSection> { while (currentIndex < length) { if (get(currentIndex) == '$') { val (label, index) = parseLabel(currentIndex + 1) - if (index != -1) { + if (index != -1 && index < length() && get(index) == ':') { // section starts, add previous section - sections.add(DocumentationContentSection(currentLabel, substring(currentSectionStart, currentIndex).trim())) + val currentContent = substring(currentSectionStart, currentIndex).trim() + val section = DocumentationContentSection(currentLabel, currentContent.toRichString()) + sections.add(section) currentLabel = label - currentIndex = index + currentIndex = index + 1 currentSectionStart = currentIndex continue } @@ -93,6 +124,14 @@ fun String.parseSections(): List<DocumentationContentSection> { } - sections.add(DocumentationContentSection(currentLabel, substring(currentSectionStart, currentIndex).trim())) + val currentContent = substring(currentSectionStart, currentIndex).trim() + val section = DocumentationContentSection(currentLabel, currentContent.toRichString()) + sections.add(section) return sections +} + +fun String.toRichString() : RichString { + val content = RichString() + content.addSlice(this, NormalStyle) + return content }
\ No newline at end of file diff --git a/src/Resolution/ResolutionService.kt b/src/Resolution/ResolutionService.kt new file mode 100644 index 00000000..f13d1ee0 --- /dev/null +++ b/src/Resolution/ResolutionService.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka + +public trait ResolutionService { + fun resolve(text: String): DocumentationNode +}
\ No newline at end of file diff --git a/src/RichContent/RichString.kt b/src/RichContent/RichString.kt new file mode 100644 index 00000000..f09e4715 --- /dev/null +++ b/src/RichContent/RichString.kt @@ -0,0 +1,83 @@ +package org.jetbrains.dokka + +public class RichString { + private val sliceList = arrayListOf<RichStringSlice>() + public val slices: List<RichStringSlice> get() = sliceList + + public fun addSlice(slice: RichStringSlice) { + sliceList.add(slice) + } + + public fun addSlice(text: String, style: RichStringStyle) { + // TODO: decide on semantics + // empty slices makes it hard to compare rich strings + if (text.length > 0) + sliceList.add(RichStringSlice(text, style)) + } + + public fun isEmpty(): Boolean = sliceList.isEmpty() + + public fun length(): Int = sliceList.fold(0) {(acc, value) -> return acc + value.text.length } + + public override fun toString(): String { + return sliceList.joinToString("", "&") + } + + override fun equals(other: Any?): Boolean { + if (other !is RichString) + return false + if (sliceList.size != other.sliceList.size) + return false + for (index in sliceList.indices) + if (!sliceList[index].equals(other.sliceList[index])) + return false + + return true + } + + override fun hashCode(): Int { + return sliceList.map { it.hashCode() }.sum() + } + + class object { + public val empty: RichString = RichString() + } +} + +public data class RichStringSlice(public val text: String, public val style: RichStringStyle) { + public override fun toString(): String { + return text + } +} + +public trait RichStringStyle +public object NormalStyle : RichStringStyle +public object BoldStyle : RichStringStyle +public object CodeStyle : RichStringStyle +public data class LinkStyle(val link: String) : RichStringStyle + +public fun RichString.splitBy(delimiter: String): Pair<RichString, RichString> { + var index = 0 + while (index < slices.size && !slices[index].text.contains(delimiter)) index++ + if (index == slices.size) + return Pair(this, RichString.empty) + + val first = RichString() + val second = RichString() + + for (i in 0..index - 1) { + first.addSlice(slices[i]) + } + + val splitSlice = slices[index] + val firstText = splitSlice.text.substringBefore(delimiter) + val secondText = splitSlice.text.substringAfter(delimiter) + first.addSlice(firstText, splitSlice.style) + second.addSlice(secondText, splitSlice.style) + + for (i in index + 1..slices.size - 1) { + second.addSlice(slices[i]) + } + + return Pair(first, second) +} |