diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Analysis/CommentsAPI.kt | 2 | ||||
-rw-r--r-- | src/Formats/MarkdownFormatService.kt | 4 | ||||
-rw-r--r-- | src/Formats/StructuredFormatService.kt | 24 | ||||
-rw-r--r-- | src/Formats/TextFormatService.kt | 3 | ||||
-rw-r--r-- | src/Kotlin/ContentBuilder.kt | 79 | ||||
-rw-r--r-- | src/Kotlin/Diagnostics.kt (renamed from src/Model/Diagnostics.kt) | 4 | ||||
-rw-r--r-- | src/Kotlin/DocumentationBuildingVisitor.kt (renamed from src/Model/DocumentationBuildingVisitor.kt) | 1 | ||||
-rw-r--r-- | src/Kotlin/DocumentationContext.kt | 47 | ||||
-rw-r--r-- | src/Kotlin/DocumentationNodeBuilder.kt (renamed from src/Model/DocumentationNodeBuilder.kt) | 94 | ||||
-rw-r--r-- | src/Markdown/MarkdownProcessor.kt | 62 | ||||
-rw-r--r-- | src/Markdown/_MarkdownLexer.flex | 40 | ||||
-rw-r--r-- | src/Markdown/markdown.bnf | 42 | ||||
-rw-r--r-- | src/Model/Content.kt | 88 | ||||
-rw-r--r-- | src/Model/DocumentationContent.kt | 150 | ||||
-rw-r--r-- | src/Model/DocumentationModule.kt | 23 | ||||
-rw-r--r-- | src/Model/DocumentationNode.kt | 7 |
16 files changed, 350 insertions, 320 deletions
diff --git a/src/Analysis/CommentsAPI.kt b/src/Analysis/CommentsAPI.kt index 5df4bd38..a17b6aa4 100644 --- a/src/Analysis/CommentsAPI.kt +++ b/src/Analysis/CommentsAPI.kt @@ -31,5 +31,5 @@ fun KDoc?.extractText(): String { comment.substring(0, comment.length - 2) else comment).trim() - }.filter { it.any() }.join("\n") + }.join("\n") }
\ No newline at end of file diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index d7530a25..3b87835f 100644 --- a/src/Formats/MarkdownFormatService.kt +++ b/src/Formats/MarkdownFormatService.kt @@ -16,10 +16,6 @@ public open class MarkdownFormatService(locationService: LocationService, 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 0c58f553..b1e614f6 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -28,19 +28,17 @@ 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 { + open fun formatText(nodes: Iterable<ContentNode>): String { + return nodes.map { formatText(it) }.join("") + } + + open fun formatText(text: ContentNode): 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)) - } + for (node in text.children) { + when (node) { + is ContentText -> append(node.text) + is ContentEmphasis -> append(formatBold(formatText(node.children))) + else -> append(formatText(node.children)) } } }.toString() @@ -67,7 +65,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi if (label.startsWith("$")) continue appendLine(to, formatBold(formatText(label))) - appendLine(to, formatText(section.text)) + appendLine(to, formatText(section)) appendLine(to) } } diff --git a/src/Formats/TextFormatService.kt b/src/Formats/TextFormatService.kt index 77a0bb65..8fea5a6a 100644 --- a/src/Formats/TextFormatService.kt +++ b/src/Formats/TextFormatService.kt @@ -9,12 +9,9 @@ public class TextFormatService(val signatureGenerator: LanguageService) : Format appendln(signatureGenerator.render(node)) appendln() appendln(node.doc.summary) - for (n in 0..node.doc.summary.length()) - append("=") for ((label,section) in node.doc.sections) { appendln(label) - appendln(section.text) } } } diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt new file mode 100644 index 00000000..78bd7eaf --- /dev/null +++ b/src/Kotlin/ContentBuilder.kt @@ -0,0 +1,79 @@ +package org.jetbrains.dokka + +import org.jetbrains.markdown.MarkdownElementTypes +import java.util.ArrayDeque + +public fun MarkdownTree.toContent(): Content { + val nodeStack = ArrayDeque<ContentNode>() + nodeStack.push(Content()) + + visit {(node, text, processChildren) -> + val parent = nodeStack.peek()!! + val nodeType = node.getTokenType() + val nodeText = getNodeText(node) + when (nodeType) { + MarkdownElementTypes.BULLET_LIST -> { + nodeStack.push(ContentList()) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.ORDERED_LIST -> { + nodeStack.push(ContentList()) // TODO: add list kind + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.HORIZONTAL_RULE -> { + } + MarkdownElementTypes.LIST_BLOCK -> { + nodeStack.push(ContentBlock()) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.EMPH -> { + nodeStack.push(ContentEmphasis()) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.STRONG -> { + nodeStack.push(ContentStrong()) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.ANONYMOUS_SECTION -> { + nodeStack.push(ContentSection("")) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.NAMED_SECTION -> { + val label = findChildByType(node, MarkdownElementTypes.SECTION_NAME)?.let { getNodeText(it) } ?: "" + nodeStack.push(ContentSection(label)) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.PLAIN_TEXT -> { + nodeStack.push(ContentText(nodeText)) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.END_LINE -> { + nodeStack.push(ContentText(nodeText)) + processChildren() + parent.children.add(nodeStack.pop()) + } + MarkdownElementTypes.BLANK_LINE -> { + processChildren() + } + MarkdownElementTypes.PARA -> { + nodeStack.push(ContentBlock()) + processChildren() + parent.children.add(nodeStack.pop()) + } + else -> { + processChildren() + } + } + } + return nodeStack.pop() as Content +} + + diff --git a/src/Model/Diagnostics.kt b/src/Kotlin/Diagnostics.kt index 1077da7c..5548bc01 100644 --- a/src/Model/Diagnostics.kt +++ b/src/Kotlin/Diagnostics.kt @@ -4,11 +4,11 @@ import org.jetbrains.jet.lang.descriptors.* import org.jetbrains.jet.lang.resolve.name.* import org.jetbrains.jet.lang.resolve.BindingContext -fun BindingContext.checkResolveChildren(node: DocumentationNode) { +fun DocumentationContext.checkResolveChildren(node: DocumentationNode) { if (node.kind != DocumentationNode.Kind.Module && node.kind != DocumentationNode.Kind.Package) { // TODO: we don't resolve packages and modules for now - val parentScope = getResolutionScope(node.descriptor) + val parentScope = getResolutionScope(node) for (item in node.details + node.members) { val symbolName = item.name val symbol: DeclarationDescriptor? = when (item.kind) { diff --git a/src/Model/DocumentationBuildingVisitor.kt b/src/Kotlin/DocumentationBuildingVisitor.kt index 6118b0f5..5654903b 100644 --- a/src/Model/DocumentationBuildingVisitor.kt +++ b/src/Kotlin/DocumentationBuildingVisitor.kt @@ -5,6 +5,7 @@ import org.jetbrains.jet.lang.resolve.name.* import org.jetbrains.jet.lang.resolve.* public data class DocumentationOptions(val includeNonPublic : Boolean = false) + class DocumentationBuildingVisitor(val context: BindingContext, val options: DocumentationOptions, private val worker: DeclarationDescriptorVisitor<DocumentationNode, DocumentationNode>) diff --git a/src/Kotlin/DocumentationContext.kt b/src/Kotlin/DocumentationContext.kt new file mode 100644 index 00000000..1491aa2d --- /dev/null +++ b/src/Kotlin/DocumentationContext.kt @@ -0,0 +1,47 @@ +package org.jetbrains.dokka + +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.resolve.BindingContext +import org.jetbrains.jet.lang.resolve.scopes.JetScope +import org.jetbrains.jet.lang.descriptors.ModuleDescriptor +import org.jetbrains.jet.lang.resolve.name.FqName + +public class DocumentationContext(val bindingContext: BindingContext) { + val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>() + val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>() + + fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) { + descriptorToNode.put(descriptor, node) + nodeToDescriptor.put(node, descriptor) + } + + fun getResolutionScope(node: DocumentationNode): JetScope { + val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context") + return bindingContext.getResolutionScope(descriptor) + } + + fun parseDocumentation(descriptor: DeclarationDescriptor): Content { + val docText = bindingContext.getDocumentationElements(descriptor).map { it.extractText() }.join("\n") + val tree = MarkdownProcessor.parse(docText) + println(tree.toTestString()) + val content = tree.toContent() + return content + } +} + +fun BindingContext.createDocumentationModule(name: String, + module: ModuleDescriptor, + packages: Set<FqName>, + options: DocumentationOptions = DocumentationOptions()): DocumentationModule { + val documentationModule = DocumentationModule(name) + val context = DocumentationContext(this) + val visitor = DocumentationNodeBuilder(context) + for (packageName in packages) { + val pkg = module.getPackage(packageName) + pkg!!.accept(DocumentationBuildingVisitor(this, options, visitor), documentationModule) + } + + // TODO: Uncomment for resolve verification + // checkResolveChildren(documentationModule) + return documentationModule +} diff --git a/src/Model/DocumentationNodeBuilder.kt b/src/Kotlin/DocumentationNodeBuilder.kt index f724c444..535f037f 100644 --- a/src/Model/DocumentationNodeBuilder.kt +++ b/src/Kotlin/DocumentationNodeBuilder.kt @@ -1,12 +1,23 @@ package org.jetbrains.dokka -import org.jetbrains.jet.lang.resolve.* -import org.jetbrains.jet.lang.descriptors.* -import org.jetbrains.jet.lang.descriptors.impl.* -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies +import org.jetbrains.jet.lang.descriptors.MemberDescriptor +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.lang.descriptors.Named +import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor +import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor +import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor +import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor +import org.jetbrains.jet.lang.descriptors.ClassKind +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns -class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DocumentationNode>() { +class DocumentationNodeBuilder(val context: DocumentationContext) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DocumentationNode>() { fun reference(from: DocumentationNode, to: DocumentationNode, kind: DocumentationReference.Kind) { from.addReferenceTo(to, kind) @@ -18,13 +29,13 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip fun addModality(descriptor: MemberDescriptor, data: DocumentationNode) { val modifier = descriptor.getModality().name().toLowerCase() - val node = DocumentationNode(descriptor, modifier, DocumentationContent.Empty, DocumentationNode.Kind.Modifier) + val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier) reference(data, node, DocumentationReference.Kind.Detail) } fun addVisibility(descriptor: MemberDescriptor, data: DocumentationNode) { val modifier = descriptor.getVisibility().toString() - val node = DocumentationNode(descriptor, modifier, DocumentationContent.Empty, DocumentationNode.Kind.Modifier) + val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier) reference(data, node, DocumentationReference.Kind.Detail) } @@ -37,7 +48,7 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip is Named -> classifierDescriptor.getName().asString() else -> "<anonymous>" } - val node = DocumentationNode(descriptor, name, DocumentationContent.Empty, DocumentationNode.Kind.Type) + val node = DocumentationNode(name, Content.Empty, DocumentationNode.Kind.Type) reference(data, node, DocumentationReference.Kind.Detail) for (param in t.getArguments()) @@ -45,14 +56,17 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip } override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Unknown) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Unknown) reference(data!!, node, DocumentationReference.Kind.Link) + context.register(descriptor, node) return node } override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor?, data: DocumentationNode?): DocumentationNode? { - val node = DocumentationNode(descriptor!!, descriptor.getName().asString(), DocumentationContent.Empty, DocumentationNode.Kind.Receiver) + descriptor!! + val node = DocumentationNode(descriptor.getName().asString(), Content.Empty, DocumentationNode.Kind.Receiver) reference(data!!, node, DocumentationReference.Kind.Detail) addType(descriptor, descriptor.getType(), node) @@ -61,8 +75,9 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip } override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Parameter) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Parameter) reference(data!!, node, DocumentationReference.Kind.Detail) addType(descriptor, descriptor.getType(), node) @@ -71,83 +86,92 @@ class DocumentationNodeBuilder(val context: BindingContext) : DeclarationDescrip } override fun visitClassDescriptor(descriptor: ClassDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, when (descriptor.getKind()) { - ClassKind.OBJECT -> DocumentationNode.Kind.Object - ClassKind.CLASS_OBJECT -> DocumentationNode.Kind.Object - ClassKind.TRAIT -> DocumentationNode.Kind.Interface - ClassKind.ENUM_CLASS -> DocumentationNode.Kind.Enum - ClassKind.ENUM_ENTRY -> DocumentationNode.Kind.EnumItem + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, when (descriptor.getKind()) { + ClassKind.OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object + ClassKind.CLASS_OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object + ClassKind.TRAIT -> org.jetbrains.dokka.DocumentationNode.Kind.Interface + ClassKind.ENUM_CLASS -> org.jetbrains.dokka.DocumentationNode.Kind.Enum + ClassKind.ENUM_ENTRY -> org.jetbrains.dokka.DocumentationNode.Kind.EnumItem else -> DocumentationNode.Kind.Class }) reference(data!!, node, DocumentationReference.Kind.Member) addModality(descriptor, node) addVisibility(descriptor, node) + context.register(descriptor, node) return node } override fun visitFunctionDescriptor(descriptor: FunctionDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Function) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Function) reference(data!!, node, DocumentationReference.Kind.Member) addType(descriptor, descriptor.getReturnType(), node) addModality(descriptor, node) addVisibility(descriptor, node) - + context.register(descriptor, node) return node } override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.TypeParameter) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.TypeParameter) reference(data!!, node, DocumentationReference.Kind.Detail) val builtIns = KotlinBuiltIns.getInstance() for (constraint in descriptor.getUpperBounds()) { if (constraint == builtIns.getDefaultBound()) continue - val constraintNode = DocumentationNode(descriptor, constraint.toString(), DocumentationContent.Empty, DocumentationNode.Kind.UpperBound) + val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound) reference(node, constraintNode, DocumentationReference.Kind.Detail) } for (constraint in descriptor.getLowerBounds()) { if (builtIns.isNothing(constraint)) continue - val constraintNode = DocumentationNode(descriptor, constraint.toString(), DocumentationContent.Empty, DocumentationNode.Kind.LowerBound) + val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound) reference(node, constraintNode, DocumentationReference.Kind.Detail) } return node } override fun visitPropertyDescriptor(descriptor: PropertyDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Property) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Property) reference(data!!, node, DocumentationReference.Kind.Member) addType(descriptor, descriptor.getType(), node) addModality(descriptor, node) addVisibility(descriptor, node) + context.register(descriptor, node) return node } override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor?, data: DocumentationNode?): DocumentationNode? { - val doc = context.getDocumentation(descriptor!!) - val node = DocumentationNode(descriptor, descriptor.getName().asString(), doc, DocumentationNode.Kind.Constructor) + descriptor!! + val doc = context.parseDocumentation(descriptor) + val node = DocumentationNode(descriptor.getName().asString(), doc, DocumentationNode.Kind.Constructor) reference(data!!, node, DocumentationReference.Kind.Member) addVisibility(descriptor, node) - + context.register(descriptor, node) return node } override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor?, data: DocumentationNode?): DocumentationNode? { - val node = DocumentationNode(descriptor!!, descriptor.getFqName().asString(), DocumentationContent.Empty, DocumentationNode.Kind.Package) + descriptor!! + val node = DocumentationNode(descriptor.getFqName().asString(), Content.Empty, DocumentationNode.Kind.Package) reference(data!!, node, DocumentationReference.Kind.Member) return node } override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor?, data: DocumentationNode?): DocumentationNode? { - val node = DocumentationNode(descriptor!!, descriptor.fqName.asString(), DocumentationContent.Empty, DocumentationNode.Kind.Package) + descriptor!! + val node = DocumentationNode(descriptor.fqName.asString(), Content.Empty, DocumentationNode.Kind.Package) reference(data!!, node, DocumentationReference.Kind.Member) return node } -} +}
\ No newline at end of file diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt index fe6e8436..19d5a8fb 100644 --- a/src/Markdown/MarkdownProcessor.kt +++ b/src/Markdown/MarkdownProcessor.kt @@ -9,12 +9,11 @@ import com.intellij.lang.LighterASTNode import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.openapi.util.Ref import org.jetbrains.markdown.lexer.MarkdownLexer +import com.intellij.psi.tree.IElementType -public class MarkdownProcessor { - class object { - val EXPR_LANGUAGE = object : Language("MARKDOWN") {} - val DOCUMENT = IFileElementType("DOCUMENT", EXPR_LANGUAGE); - } +public object MarkdownProcessor { + val EXPR_LANGUAGE = object : Language("MARKDOWN") {} + val DOCUMENT = IFileElementType("DOCUMENT", EXPR_LANGUAGE); public fun parse(markdown: String): MarkdownTree { val parser = MarkdownParser() @@ -30,6 +29,28 @@ public class MarkdownTree(private val text: String, private val structure: Flywe visit(structure.getRoot(), action) } + fun findChildByType(node: LighterASTNode, findType: IElementType) : LighterASTNode? { + val ref = Ref.create<Array<LighterASTNode>?>() + val count = structure.getChildren(node, ref) + val children = ref.get() + if (children != null) { + for (index in 0..count - 1) { + val child = children[index] + val nodeType = child.getTokenType() + if (nodeType == findType) + return child + val nestedChild = findChildByType(child, findType) + if (nestedChild != null) + return nestedChild + } + } + return null + } + + fun getNodeText(node: LighterASTNode) : String { + return text.substring(node.getStartOffset(), node.getEndOffset()) + } + fun visit(node: LighterASTNode, action: (LighterASTNode, String, visitChildren: () -> Unit) -> Unit) { action(node, text) { val ref = Ref.create<Array<LighterASTNode>?>() @@ -46,7 +67,7 @@ public class MarkdownTree(private val text: String, private val structure: Flywe } -public fun MarkdownTree.dump(): String { +public fun MarkdownTree.toTestString(): String { val sb = StringBuilder() var level = 0 visit {(node, text, visitChildren) -> @@ -64,29 +85,25 @@ public fun MarkdownTree.dump(): String { public fun MarkdownTree.toHtml(): String { val sb = StringBuilder() - var level = 0 visit {(node, text, processChildren) -> val nodeType = node.getTokenType() val nodeText = text.substring(node.getStartOffset(), node.getEndOffset()) - val indent = " ".repeat(level * 2) when (nodeType) { MarkdownElementTypes.BULLET_LIST -> { - sb.appendln("$indent<ul>") - level++ + sb.appendln("<ul>") processChildren() - level-- - sb.appendln("$indent</ul>") + sb.appendln("</ul>") } MarkdownElementTypes.HORIZONTAL_RULE -> { - sb.appendln("$indent<hr/>") + sb.appendln("<hr/>") } MarkdownElementTypes.ORDERED_LIST -> { - sb.appendln("$indent<ol>") + sb.appendln("<ol>") processChildren() - sb.appendln("$indent</ol>") + sb.appendln("</ol>") } MarkdownElementTypes.LIST_BLOCK -> { - sb.append("$indent<li>") + sb.append("<li>") processChildren() sb.appendln("</li>") } @@ -110,14 +127,9 @@ public fun MarkdownTree.toHtml(): String { sb.appendln() } MarkdownElementTypes.PARA -> { - sb.appendln("$indent<p>") - processChildren() - sb.appendln("$indent</p>") - } - MarkdownElementTypes.VERBATIM -> { - sb.appendln("$indent<pre><code>") + sb.append("<p>") processChildren() - sb.appendln("$indent</code></pre>") + sb.appendln("</p>") } else -> { processChildren() @@ -129,8 +141,8 @@ public fun MarkdownTree.toHtml(): String { fun markdownToHtml(markdown: String): String { - val markdownTree = MarkdownProcessor().parse(markdown) - val ast = markdownTree.dump() + val markdownTree = MarkdownProcessor.parse(markdown) + val ast = markdownTree.toTestString() return markdownTree.toHtml() } diff --git a/src/Markdown/_MarkdownLexer.flex b/src/Markdown/_MarkdownLexer.flex deleted file mode 100644 index 0a15e41a..00000000 --- a/src/Markdown/_MarkdownLexer.flex +++ /dev/null @@ -1,40 +0,0 @@ -package org.jetbrains.markdown.lexer; - -import com.intellij.lexer.*; -import com.intellij.psi.tree.IElementType; -import static org.jetbrains.markdown.MarkdownElementTypes.*; - -%% - -%{ - public _MarkdownLexer() { - this((java.io.Reader)null); - } -%} - -%public -%class _MarkdownLexer -%implements FlexLexer -%function advance -%type IElementType -%unicode - -Newline="\r"|"\n"|"\r\n" -Spacechar=[\ \t\f] -Number=[0-9]+(\.[0-9]*)? -String=[^~\*_`&\[\]()<!#\\ \t\n\r]+ -AnyChar=. -Line=!'\r' !'\n' .* {Newline} - -%% -<YYINITIAL> { - {Spacechar} { return SPACECHAR; } - {Newline} { return NEWLINE; } - "\\357\\273\\277" { return BOM; } - - {Number} { return NUMBER; } - {String} { return STRING; } - {AnyChar} { return ANYCHAR; } - - [^] { return com.intellij.psi.TokenType.BAD_CHARACTER; } -} diff --git a/src/Markdown/markdown.bnf b/src/Markdown/markdown.bnf index ca254295..79e9e0b4 100644 --- a/src/Markdown/markdown.bnf +++ b/src/Markdown/markdown.bnf @@ -21,35 +21,35 @@ } -Document ::= BOM? ( Block )* +Document ::= BOM? Whitespace* AnonymousSection? (Whitespace* NamedSection)* -private OptionalSpace ::= Spacechar* -private RequiredSpace ::= Spacechar+ -private NonindentSpace ::= (" " | " " | " ")? +AnonymousSection ::= SectionBody +NamedSection ::= SectionHeader SectionBody +private SectionHeader ::= '$' SectionName OptionalSpace ':' OptionalSpace +SectionName ::= SectionNameStart | '{' OptionalSpace SectionNameStart (Spacechar+ String)* OptionalSpace '}' +private SectionNameStart ::= '$'? String +SectionBody::= Block* BlankLine ::= OptionalSpace Newline Whitespace ::= Spacechar | Newline +private OptionalSpace ::= Spacechar* +private NonindentSpace ::= (" " | " " | " ")? + EndLine ::= TerminalEndline | NormalEndline -private NormalEndline ::= OptionalSpace Newline !BlankLine -private TerminalEndline ::= (OptionalSpace Newline <<eof>>) | (OptionalSpace <<eof>>) +private NormalEndline ::= Newline !BlankLine +private TerminalEndline ::= OptionalSpace <<eof>> private Indent ::= "\t" | " " -NonblankIndentedLine ::= !BlankLine IndentedLine -IndentedLine ::= Indent PlainText // ---- BLOCKS ---- -private Block ::= BlankLine* ( - Para - | Verbatim - | OrderedList - | BulletList - | Inlines - ) - -Para ::= NonindentSpace Inlines (BlankLine | TerminalEndline) +Block ::= BlankLine* ( + OrderedList + | BulletList + | HorizontalRule + | Para + ) -Verbatim ::= VerbatimItem+ -VerbatimItem ::= BlankLine* NonblankIndentedLine +Para ::= Inlines (BlankLine | TerminalEndline)? HorizontalRule ::= NonindentSpace ( '*' OptionalSpace '*' OptionalSpace '*' (OptionalSpace '*')* @@ -74,8 +74,8 @@ ListContinuationBlock ::= BlankLine* (Indent ListBlock)+ // ---- INLINES ---- private Inlines ::= (!EndLine Inline | EndLine &Inline )+ EndLine? -private Inline ::= Strong | Emph | Link | PlainText -PlainText ::= (String | Number | Spacechar)+ +Inline ::= Strong | Emph | Link | PlainText +PlainText ::= (String | Number | Spacechar+)+ Emph ::= EmphStar | EmphUnderscore private EmphStar ::= '*' !Whitespace (!'*' Inline)+ '*' diff --git a/src/Model/Content.kt b/src/Model/Content.kt new file mode 100644 index 00000000..eb092cb2 --- /dev/null +++ b/src/Model/Content.kt @@ -0,0 +1,88 @@ +package org.jetbrains.dokka + +import kotlin.properties.Delegates + +public abstract class ContentNode { + val children = arrayListOf<ContentNode>() + + class object { + val empty = ContentEmpty + } + + fun isEmpty() = children.isEmpty() +} + +public object ContentEmpty : ContentNode( ) + +public class ContentText(val text : String) : ContentNode( ) +public class ContentBlock() : ContentNode( ) +public class ContentEmphasis() : ContentNode() +public class ContentStrong() : ContentNode() +public class ContentList() : ContentNode() +public class ContentSection(public val label: String) : ContentNode() + +public class Content() : ContentNode() { + public val sections: Map<String, ContentSection> by Delegates.lazy { + val map = hashMapOf<String, ContentSection>() + for (child in children) { + if (child is ContentSection) + map.put(child.label, child) + } + + if ("\$summary" !in map && "\$description" !in map) { + // no explicit summary and description, convert anonymous section + val anonymous = map[""] + if (anonymous != null) { + map.remove("") + val summary = ContentSection("\$summary") + val description = ContentSection("\$description") + + val summaryNodes = anonymous.children.take(1) + val descriptionNodes = anonymous.children.drop(1) + + if (summaryNodes.any()) { + summary.children.addAll(summaryNodes) + map.put("\$summary", summary) + } + + if (descriptionNodes.any()) { + description.children.addAll(descriptionNodes) + map.put("\$description", description) + } + } + } + map + } + + public val summary: ContentNode get() = sections["\$summary"] ?: ContentNode.empty + public val description: ContentNode get() = sections["\$description"] ?: ContentNode.empty + + override fun equals(other: Any?): Boolean { + if (other !is Content) + return false + if (sections.size != other.sections.size) + return false + for (keys in sections.keySet()) + if (sections[keys] != other.sections[keys]) + return false + + return true + } + + override fun hashCode(): Int { + return sections.map { it.hashCode() }.sum() + } + + override fun toString(): String { + if (sections.isEmpty()) + return "<empty>" + return sections.values().joinToString() + } + + val isEmpty: Boolean + get() = sections.none() + + class object { + val Empty = Content() + } +} diff --git a/src/Model/DocumentationContent.kt b/src/Model/DocumentationContent.kt deleted file mode 100644 index 77e8c764..00000000 --- a/src/Model/DocumentationContent.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.jet.lang.descriptors.* -import org.jetbrains.jet.lang.resolve.BindingContext - -public class DocumentationContentSection(public val label: String, public val text: RichString) { - override fun toString(): String { - return "$label = $text" - } -} - -public class DocumentationContent(public val sections: Map<String, DocumentationContentSection>) { - - public val summary: RichString get() = sections["\$summary"]?.text ?: RichString.empty - public val description: RichString get() = sections["\$description"]?.text ?: RichString.empty - - override fun equals(other: Any?): Boolean { - if (other !is DocumentationContent) - return false - if (sections.size != other.sections.size) - return false - for (keys in sections.keySet()) - if (sections[keys] != other.sections[keys]) - return false - - return true - } - - override fun hashCode(): Int { - return sections.map { it.hashCode() }.sum() - } - - override fun toString(): String { - if (sections.isEmpty()) - return "<empty>" - return sections.values().joinToString() - } - - val isEmpty: Boolean - get() = description.isEmpty() && sections.none() - - class object { - val Empty = DocumentationContent(mapOf()) - } -} - - -fun BindingContext.getDocumentation(descriptor: DeclarationDescriptor): DocumentationContent { - val docText = getDocumentationElements(descriptor).map { it.extractText() }.join("\n") - val sections = docText.parseSections() - sections.createSummaryAndDescription() - return DocumentationContent(sections) -} - -fun MutableMap<String, DocumentationContentSection>.createSummaryAndDescription() { - - val summary = get("\$summary") - val description = get("\$description") - if (summary != null && description == null) { - return - } - - if (summary == null && description != null) { - return - } - - val unnamed = get("") - if (unnamed == null) { - return - } - - val split = unnamed.text.splitBy("\n") - remove("") - if (!split.first.isEmpty()) - put("\$summary", DocumentationContentSection("\$summary", split.first)) - if (!split.second.isEmpty()) - put("\$description", DocumentationContentSection("\$description", split.second)) -} - -fun String.parseLabel(index: Int): Pair<String, Int> { - val c = get(index) - when { - Character.isJavaIdentifierStart(c) -> { - for (end in index + 1..length - 1) { - if (!Character.isJavaIdentifierPart(get(end))) { - return substring(index, end) to end - } - } - 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 + 1 - } - } - return "" to -1 -} - -fun String.parseSections(): MutableMap<String, DocumentationContentSection> { - val sections = hashMapOf<String, DocumentationContentSection>() - var currentLabel = "" - var currentSectionStart = 0 - var currentIndex = 0 - - while (currentIndex < length) { - if (get(currentIndex) == '$') { - val (label, index) = parseLabel(currentIndex + 1) - if (index != -1 && index < length() && get(index) == ':') { - // section starts, add previous section - val currentContent = substring(currentSectionStart, currentIndex).trim() - val section = DocumentationContentSection(currentLabel, currentContent.toRichString()) - sections.put(section.label, section) - - currentLabel = label - currentIndex = index + 1 - currentSectionStart = currentIndex - continue - } - } - currentIndex++ - - } - - val currentContent = substring(currentSectionStart, currentIndex).trim() - val section = DocumentationContentSection(currentLabel, currentContent.toRichString()) - sections.put(section.label, section) - return sections -} - -fun String.toRichString() : RichString { - val content = RichString() - for(index in indices) { - val ch = get(index) - when { - ch == '\\' -> continue - ch == '*' && index < length-1 && !get(index + 1).isWhitespace() -> ch - } - } - - content.addSlice(this, NormalStyle) - return content -}
\ No newline at end of file diff --git a/src/Model/DocumentationModule.kt b/src/Model/DocumentationModule.kt index 78ebda04..6084ea5e 100644 --- a/src/Model/DocumentationModule.kt +++ b/src/Model/DocumentationModule.kt @@ -1,30 +1,11 @@ package org.jetbrains.dokka -import org.jetbrains.jet.lang.resolve.BindingContext -import org.jetbrains.jet.lang.descriptors.* -import org.jetbrains.jet.lang.resolve.name.FqName - -public class DocumentationModule(name: String, val module: ModuleDescriptor) : DocumentationNode(module, name, DocumentationContent.Empty, DocumentationNode.Kind.Module) { +public class DocumentationModule(name: String) : DocumentationNode(name, Content.Empty, DocumentationNode.Kind.Module) { fun merge(other: DocumentationModule): DocumentationModule { - val model = DocumentationModule(name, module) + val model = DocumentationModule(name) model.addAllReferencesFrom(other) model.addAllReferencesFrom(this) return model } } -fun BindingContext.createDocumentationModule(name: String, - module: ModuleDescriptor, - packages: Set<FqName>, - options : DocumentationOptions = DocumentationOptions()): DocumentationModule { - val documentationModule = DocumentationModule(name, module) - val visitor = DocumentationNodeBuilder(this) - for (packageName in packages) { - val pkg = module.getPackage(packageName) - pkg!!.accept(DocumentationBuildingVisitor(this, options, visitor), documentationModule) - } - - // TODO: Uncomment for resolve verification - // checkResolveChildren(documentationModule) - return documentationModule -} diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index c96c383d..198e549b 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -1,12 +1,9 @@ package org.jetbrains.dokka -import org.jetbrains.jet.lang.descriptors.* import java.util.LinkedHashSet - -public open class DocumentationNode(val descriptor: DeclarationDescriptor, - val name: String, - val doc: DocumentationContent, +public open class DocumentationNode(val name: String, + val doc: Content, val kind: DocumentationNode.Kind) { private val references = LinkedHashSet<DocumentationReference>() |