aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Formats/HtmlFormatService.kt20
-rw-r--r--src/Formats/HtmlTemplateService.kt28
-rw-r--r--src/Formats/JekyllFormatService.kt9
-rw-r--r--src/Formats/MarkdownFormatService.kt10
-rw-r--r--src/Formats/StructuredFormatService.kt33
-rw-r--r--src/Formats/TextFormatService.kt2
-rw-r--r--src/Generation/FileGenerator.kt1
-rw-r--r--src/Model/DocumentationContent.kt65
-rw-r--r--src/Resolution/ResolutionService.kt5
-rw-r--r--src/RichContent/RichString.kt83
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("&nbsp;/&nbsp;")
}
+
+ 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)
+}