diff options
-rw-r--r-- | src/Formats/FormatService.kt | 7 | ||||
-rw-r--r-- | src/Formats/HtmlFormatService.kt | 11 | ||||
-rw-r--r-- | src/Formats/JekyllFormatService.kt | 2 | ||||
-rw-r--r-- | src/Formats/MarkdownFormatService.kt | 27 | ||||
-rw-r--r-- | src/Formats/StructuredFormatService.kt | 26 | ||||
-rw-r--r-- | src/Formats/TextFormatService.kt | 5 | ||||
-rw-r--r-- | src/Generation/ConsoleGenerator.kt | 2 | ||||
-rw-r--r-- | src/Generation/FileGenerator.kt | 21 | ||||
-rw-r--r-- | src/Languages/JavaLanguageService.kt | 148 | ||||
-rw-r--r-- | src/Languages/KotlinLanguageService.kt | 167 | ||||
-rw-r--r-- | src/Languages/LanguageService.kt | 17 | ||||
-rw-r--r-- | src/main.kt | 7 |
12 files changed, 415 insertions, 25 deletions
diff --git a/src/Formats/FormatService.kt b/src/Formats/FormatService.kt index b106692a..f890c34a 100644 --- a/src/Formats/FormatService.kt +++ b/src/Formats/FormatService.kt @@ -2,8 +2,9 @@ package org.jetbrains.dokka public trait FormatService { val extension: String - fun appendNodes(to: StringBuilder, - nodes: Iterable<DocumentationNode>) + fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) + fun appendOutline(to: StringBuilder, nodes: Iterable<DocumentationNode>) } -fun FormatService.format(nodes: Iterable<DocumentationNode>): String = StringBuilder { appendNodes(this, nodes) }.toString()
\ No newline at end of file +fun FormatService.format(nodes: Iterable<DocumentationNode>): String = StringBuilder { appendNodes(this, nodes) }.toString() +fun FormatService.formatOutline(nodes: Iterable<DocumentationNode>): String = StringBuilder { appendOutline(this, nodes) }.toString()
\ No newline at end of file diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt index 6c004509..6f81e8eb 100644 --- a/src/Formats/HtmlFormatService.kt +++ b/src/Formats/HtmlFormatService.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka -public open class HtmlFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) +public open class HtmlFormatService(locationService: LocationService, signatureGenerator: LanguageService) : StructuredFormatService(locationService, signatureGenerator) { override val extension: String = "html" @@ -32,8 +32,8 @@ public open class HtmlFormatService(locationService: LocationService, signatureG to.appendln("<br/>") } - override fun formatLink(link: FormatLink): String { - return "<a href=\"${link.location.path}\">${link.text}</a>" + override fun formatLink(text: String, location: Location): String { + return "<a href=\"${location.path}\">${text}</a>" } override fun formatBold(text: String): String { @@ -47,4 +47,9 @@ public open class HtmlFormatService(locationService: LocationService, signatureG override fun formatBreadcrumbs(items: Iterable<FormatLink>): String { return items.map { formatLink(it) }.joinToString(" / ") } + + override fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>) { + } + override fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode) { + } }
\ No newline at end of file diff --git a/src/Formats/JekyllFormatService.kt b/src/Formats/JekyllFormatService.kt index ff53827c..aeb1e9ef 100644 --- a/src/Formats/JekyllFormatService.kt +++ b/src/Formats/JekyllFormatService.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka -public class JekyllFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) +public class JekyllFormatService(locationService: LocationService, signatureGenerator: LanguageService) : MarkdownFormatService(locationService, signatureGenerator) { override fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, "html") diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index 2a91a4a3..6a76343d 100644 --- a/src/Formats/MarkdownFormatService.kt +++ b/src/Formats/MarkdownFormatService.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka -public open class MarkdownFormatService(locationService: LocationService, signatureGenerator: SignatureGenerator) +public open class MarkdownFormatService(locationService: LocationService, signatureGenerator: LanguageService) : StructuredFormatService(locationService, signatureGenerator) { override val extension: String = "md" @@ -18,8 +18,8 @@ public open class MarkdownFormatService(locationService: LocationService, signat return "**$text**" } - override public fun formatLink(link: FormatLink): String { - return "[${link.text}](${link.location.path})" + override public fun formatLink(text: String, location: Location): String { + return "[${text}](${location.path})" } override public fun appendLine(to: StringBuilder) { @@ -52,4 +52,25 @@ public open class MarkdownFormatService(locationService: LocationService, signat appendLine(to, line) appendLine(to, "```") } + + var outlineLevel = 0 + override fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode) { + val indent = " ".repeat(outlineLevel) + appendLine(to, "$indent- title: ${languageService.renderName(node)}") + appendLine(to, "$indent url: ${locationService.location(node).path}") + } + + override fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>) { + val indent = " ".repeat(outlineLevel) + appendLine(to, "$indent content:") + outlineLevel++ + for (node in nodes) { + appendOutlineHeader(to, node) + if (node.members.any()) { + appendOutlineChildren(to, node.members) + } + appendLine(to) + } + outlineLevel-- + } } diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index a490df67..bca53f4f 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -6,7 +6,7 @@ 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 { + val languageService: LanguageService) : FormatService { abstract public fun appendBlockCode(to: StringBuilder, line: String) abstract public fun appendBlockCode(to: StringBuilder, lines: Iterable<String>) @@ -14,7 +14,10 @@ public abstract class StructuredFormatService(val locationService: LocationServi 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 formatLink(text: String, location: Location): String + public open fun formatLink(link: FormatLink): String = formatLink(link.text, link.location) + public abstract fun formatBold(text: String): String public abstract fun formatCode(code: String): String public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String @@ -30,7 +33,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi if (described.any()) { appendHeader(to, "Description") for (node in described) { - appendBlockCode(to, signatureGenerator.render(node)) + appendBlockCode(to, languageService.render(node)) appendLine(to, node.doc.description) appendLine(to) for (section in node.doc.sections) { @@ -49,7 +52,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi for ((summary, items) in breakdownBySummary) { appendLine(to, summary) - appendBlockCode(to, items.map { signatureGenerator.render(it) }) + appendBlockCode(to, items.map { languageService.render(it) }) } } @@ -86,7 +89,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi val mainMember = members.first() val displayName = when (mainMember.kind) { Kind.Constructor -> "*.init*" - else -> signatureGenerator.renderName(mainMember).htmlEscape() + else -> languageService.renderName(mainMember).htmlEscape() } appendText(to, "|${formatLink(location)}|") @@ -94,7 +97,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi val breakdownBySummary = members.groupByTo(LinkedHashMap()) { it.doc.summary } for ((summary, items) in breakdownBySummary) { appendLine(to, summary) - appendBlockCode(to, items.map { formatBold("${signatureGenerator.render(it)}") }) + appendBlockCode(to, items.map { formatBold("${languageService.render(it)}") }) } appendLine(to, "|") @@ -104,4 +107,15 @@ public abstract class StructuredFormatService(val locationService: LocationServi } } + abstract public fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode) + abstract public fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>) + + override public fun appendOutline(to: StringBuilder, nodes: Iterable<DocumentationNode>) { + for (node in nodes) { + appendOutlineHeader(to, node) + if (node.members.any()) { + appendOutlineChildren(to, node.members) + } + } + } }
\ No newline at end of file diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt index f309ad96..b7863c25 100644 --- a/src/Formats/TextFormatService.kt +++ b/src/Formats/TextFormatService.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka -public class TextFormatService(val signatureGenerator: SignatureGenerator) : FormatService { +public class TextFormatService(val signatureGenerator: LanguageService) : FormatService { override val extension: String = "txt" override fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) { @@ -19,4 +19,7 @@ public class TextFormatService(val signatureGenerator: SignatureGenerator) : For } } } + + override fun appendOutline(to: StringBuilder, nodes: Iterable<DocumentationNode>) { + } }
\ No newline at end of file diff --git a/src/Generation/ConsoleGenerator.kt b/src/Generation/ConsoleGenerator.kt index 17e6957b..78164bb9 100644 --- a/src/Generation/ConsoleGenerator.kt +++ b/src/Generation/ConsoleGenerator.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka -public class ConsoleGenerator(val signatureGenerator: SignatureGenerator, val locationService: LocationService) { +public class ConsoleGenerator(val signatureGenerator: LanguageService, val locationService: LocationService) { val IndentStep = " " public fun generate(node: DocumentationNode, indent: String = "") { diff --git a/src/Generation/FileGenerator.kt b/src/Generation/FileGenerator.kt index 6d2c0a3d..dbd631cb 100644 --- a/src/Generation/FileGenerator.kt +++ b/src/Generation/FileGenerator.kt @@ -4,13 +4,14 @@ import java.io.FileOutputStream import java.io.OutputStreamWriter import java.util.LinkedHashMap -public class FileGenerator(val signatureGenerator: SignatureGenerator, +public class FileGenerator(val signatureGenerator: LanguageService, val locationService: LocationService, val formatService: FormatService) { - public fun generate(node: DocumentationNode): Unit = generate(listOf(node)) + public fun buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)) + public fun buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node)) - public fun generate(nodes: Iterable<DocumentationNode>) { + public fun buildPages(nodes: Iterable<DocumentationNode>) { for ((location, items) in nodes.groupByTo(LinkedHashMap()) { locationService.location(it) }) { val file = location.file.appendExtension(formatService.extension) file.getParentFile()?.mkdirs() @@ -19,7 +20,19 @@ public class FileGenerator(val signatureGenerator: SignatureGenerator, it.write(formatService.format(items)) } } - generate(items.flatMap { it.members }) + buildPages(items.flatMap { it.members }) + } + } + + public fun buildOutlines(nodes: Iterable<DocumentationNode>) { + for ((location, items) in nodes.groupByTo(LinkedHashMap()) { locationService.location(it) }) { + val file = location.file.appendExtension("yml") + file.getParentFile()?.mkdirs() + FileOutputStream(file).use { + OutputStreamWriter(it, defaultCharset).use { + it.write(formatService.formatOutline(items)) + } + } } } }
\ No newline at end of file diff --git a/src/Languages/JavaLanguageService.kt b/src/Languages/JavaLanguageService.kt new file mode 100644 index 00000000..8cc185f4 --- /dev/null +++ b/src/Languages/JavaLanguageService.kt @@ -0,0 +1,148 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.DocumentationNode.* + +class JavaLanguageService : LanguageService { + override fun render(node: DocumentationNode): String { + return when (node.kind) { + Kind.Package -> renderPackage(node) + Kind.Class, + Kind.Interface, + Kind.Enum, + Kind.EnumItem, + Kind.Object -> renderClass(node) + + Kind.TypeParameter -> renderTypeParameter(node) + Kind.Type, + Kind.UpperBound -> renderType(node) + + Kind.Constructor, + Kind.Function -> renderFunction(node) + Kind.Property -> renderProperty(node) + else -> "${node.kind}: ${node.name}" + } + } + + override fun renderName(node: DocumentationNode): String { + return when (node.kind) { + Kind.Constructor -> node.owner!!.name + else -> node.name + } + } + + override fun renderPackage(node: DocumentationNode): String { + return "package ${node.name}" + } + + override fun renderModifier(node: DocumentationNode): String { + return when (node.name) { + "open" -> "" + "internal" -> "" + else -> node.name + } + } + + override fun renderType(node: DocumentationNode): String { + return when (node.name) { + "Unit" -> "void" + "Int" -> "int" + "Long" -> "long" + "Double" -> "double" + "Float" -> "float" + "Char" -> "char" + "Boolean" -> "bool" + // TODO: render arrays + else -> node.name + } + } + + override fun renderTypeParameter(node: DocumentationNode): String { + val constraints = node.details(Kind.UpperBound) + return if (constraints.none()) + node.name + else { + node.name + " extends " + constraints.map { renderType(node) }.join() + } + } + + override fun renderParameter(node: DocumentationNode): String { + return "${renderType(node.detail(Kind.Type))} ${node.name}" + } + + override fun renderTypeParametersForNode(node: DocumentationNode): String { + return StringBuilder { + val typeParameters = node.details(Kind.TypeParameter) + if (typeParameters.any()) { + append("<") + append(typeParameters.map { renderTypeParameter(it) }.join()) + append("> ") + } + }.toString() + } + + override fun renderModifiersForNode(node: DocumentationNode): String { + val modifiers = node.details(Kind.Modifier).map { renderModifier(it) }.filter { it != "" } + if (modifiers.none()) + return "" + return modifiers.join(" ", postfix = " ") + } + + override fun renderClass(node: DocumentationNode): String { + return StringBuilder { + when (node.kind) { + Kind.Class -> append("class ") + Kind.Interface -> append("interface ") + Kind.Enum -> append("enum ") + Kind.EnumItem -> append("enum value ") + Kind.Object -> append("class ") + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + + append(node.name) + append(renderTypeParametersForNode(node)) + }.toString() + } + + override fun renderFunction(node: DocumentationNode): String { + return StringBuilder { + when (node.kind) { + Kind.Constructor -> append(node.owner?.name) + Kind.Function -> { + append(renderTypeParametersForNode(node)) + append(renderType(node.detail(Kind.Type))) + append(" ") + append(node.name) + } + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + + val receiver = node.details(Kind.Receiver).firstOrNull() // TODO: replace with singleOrNull when fixed + append("(") + if (receiver != null) + append((listOf(receiver) + node.details(Kind.Parameter)).map { renderParameter(it) }.join()) + else + append(node.details(Kind.Parameter).map { renderParameter(it) }.join()) + + append(")") + }.toString() + } + + override fun renderProperty(node: DocumentationNode): String { + return StringBuilder { + when (node.kind) { + Kind.Property -> append("val ") + else -> throw IllegalArgumentException("Node $node is not a property") + } + append(renderTypeParametersForNode(node)) + val receiver = node.details(Kind.Receiver).firstOrNull() // TODO: replace with singleOrNull when fixed + if (receiver != null) { + append(renderType(receiver.detail(Kind.Type))) + append(".") + } + + append(node.name) + append(": ") + append(renderType(node.detail(Kind.Type))) + }.toString() + } +}
\ No newline at end of file diff --git a/src/Languages/KotlinLanguageService.kt b/src/Languages/KotlinLanguageService.kt new file mode 100644 index 00000000..7ae043b0 --- /dev/null +++ b/src/Languages/KotlinLanguageService.kt @@ -0,0 +1,167 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.DocumentationNode.* + +class KotlinLanguageService : LanguageService { + override fun render(node: DocumentationNode): String { + return when (node.kind) { + Kind.Package -> renderPackage(node) + Kind.Class, + Kind.Interface, + Kind.Enum, + Kind.EnumItem, + Kind.Object -> renderClass(node) + + Kind.TypeParameter -> renderTypeParameter(node) + Kind.Type, + Kind.UpperBound -> renderType(node) + + Kind.Modifier -> renderModifier(node) + Kind.Constructor, + Kind.Function -> renderFunction(node) + Kind.Property -> renderProperty(node) + else -> "${node.kind}: ${node.name}" + } + } + + override fun renderName(node: DocumentationNode): String { + return when (node.kind) { + Kind.Constructor -> node.owner!!.name + else -> node.name + } + } + + override fun renderPackage(node: DocumentationNode): String { + return "package ${node.name}" + } + + override fun renderType(node: DocumentationNode): String { + val typeArguments = node.details(Kind.Type) + val renders = typeArguments.map { renderType(it) } + + if (node.name == "Function${typeArguments.count() - 1}") { + // lambda + return "(${renders.take(renders.size - 1).join()})->${renders.last()}" + } + if (node.name == "ExtensionFunction${typeArguments.count() - 2}") { + // extension lambda + return "${renders.first()}.(${renders.drop(1).take(renders.size - 2).join()})->${renders.last()}" + } + if (typeArguments.none()) + return node.name + return "${node.name}<${renders.join()}>" + } + + override fun renderModifier(node: DocumentationNode): String { + return when (node.name) { + "final" -> "" + "internal" -> "" + else -> node.name + } + } + + override fun renderTypeParameter(node: DocumentationNode): String { + val constraints = node.details(Kind.UpperBound) + return if (constraints.none()) + node.name + else { + node.name + " : " + constraints.map { renderType(node) }.join() + } + } + + override fun renderParameter(node: DocumentationNode): String { + return node.name + ": " + renderType(node.detail(Kind.Type)) + } + + override fun renderTypeParametersForNode(node: DocumentationNode): String { + return StringBuilder { + val typeParameters = node.details(Kind.TypeParameter) + if (typeParameters.any()) { + append("<") + append(typeParameters.map { renderTypeParameter(it) }.join()) + append("> ") + } + }.toString() + } + + override fun renderModifiersForNode(node: DocumentationNode): String { + val modifiers = node.details(Kind.Modifier).map { renderModifier(it) }.filter { it != "" } + if (modifiers.none()) + return "" + return modifiers.join(" ", postfix = " ") + } + + override fun renderClass(node: DocumentationNode): String { + return StringBuilder { + append(renderModifiersForNode(node)) + when (node.kind) { + Kind.Class -> append("class ") + Kind.Interface -> append("trait ") + Kind.Enum -> append("enum class ") + Kind.EnumItem -> append("enum val ") + Kind.Object -> append("object ") + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + + append(node.name) + append(renderTypeParametersForNode(node)) + + /* + val constructors = node.members(Kind.Constructor) + if (constructors.count() == 1) { + append("(") + append(constructors[0].details(Kind.Parameter).map { renderParameter(it) }.join()) + append(")") + } + */ + }.toString() + } + + override fun renderFunction(node: DocumentationNode): String { + return StringBuilder { + append(renderModifiersForNode(node)) + when (node.kind) { + Kind.Constructor -> append(node.owner!!.name) + Kind.Function -> append("fun ") + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + append(renderTypeParametersForNode(node)) + val receiver = node.details(Kind.Receiver).firstOrNull() // TODO: replace with singleOrNull when fixed + if (receiver != null) { + append(renderType(receiver.detail(Kind.Type))) + append(".") + } + + if (node.kind != Kind.Constructor) + append(node.name) + + append("(") + append(node.details(Kind.Parameter).map { renderParameter(it) }.join()) + append(")") + if (node.kind != Kind.Constructor) { + append(": ") + append(renderType(node.detail(Kind.Type))) + } + }.toString() + } + + override fun renderProperty(node: DocumentationNode): String { + return StringBuilder { + append(renderModifiersForNode(node)) + when (node.kind) { + Kind.Property -> append("val ") + else -> throw IllegalArgumentException("Node $node is not a property") + } + append(renderTypeParametersForNode(node)) + val receiver = node.details(Kind.Receiver).firstOrNull() // TODO: replace with singleOrNull when fixed + if (receiver != null) { + append(renderType(receiver.detail(Kind.Type))) + append(".") + } + + append(node.name) + append(": ") + append(renderType(node.detail(Kind.Type))) + }.toString() + } +}
\ No newline at end of file diff --git a/src/Languages/LanguageService.kt b/src/Languages/LanguageService.kt new file mode 100644 index 00000000..728503b0 --- /dev/null +++ b/src/Languages/LanguageService.kt @@ -0,0 +1,17 @@ +package org.jetbrains.dokka + +trait LanguageService { + fun render(node: DocumentationNode): String + fun renderName(node: DocumentationNode): String + fun renderFunction(node: DocumentationNode): String + fun renderClass(node: DocumentationNode): String + fun renderTypeParametersForNode(node: DocumentationNode): String + fun renderTypeParameter(node: DocumentationNode): String + fun renderParameter(node: DocumentationNode): String + fun renderType(node: DocumentationNode): String + fun renderPackage(node: DocumentationNode): String + fun renderProperty(node: DocumentationNode): String + fun renderModifier(node: DocumentationNode): String + fun renderModifiersForNode(node: DocumentationNode): String +} + diff --git a/src/main.kt b/src/main.kt index f9b734a3..0f8cdfce 100644 --- a/src/main.kt +++ b/src/main.kt @@ -55,10 +55,11 @@ public fun main(args: Array<String>) { context.createDocumentationModule(arguments.moduleName, module, packageSet) } - val signatureGenerator = KotlinSignatureGenerator() + val signatureGenerator = KotlinLanguageService() val locationService = FoldersLocationService(arguments.outputDir) - val formatter = HtmlFormatService(locationService, signatureGenerator) + val formatter = JekyllFormatService(locationService, signatureGenerator) val generator = FileGenerator(signatureGenerator, locationService, formatter) - generator.generate(documentation) + generator.buildPage(documentation) + generator.buildOutline(documentation) Disposer.dispose(environment) }
\ No newline at end of file |