Support for showing rendered doc comments in editor
Fixed #KT-37361
This commit is contained in:
@@ -1,30 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kdoc.psi.api
|
||||
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiDocCommentBase
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
|
||||
|
||||
// Don't implement JetElement (or it will be treated as statement)
|
||||
interface KDoc : PsiComment, KDocElement {
|
||||
fun getOwner(): KtDeclaration?
|
||||
// Don't implement KtElement (or it will be treated as statement)
|
||||
interface KDoc : PsiDocCommentBase, KDocElement {
|
||||
override fun getOwner(): KtDeclaration?
|
||||
fun getDefaultSection(): KDocSection
|
||||
fun getAllSections(): List<KDocSection>
|
||||
fun findSectionByName(name: String): KDocSection?
|
||||
fun findSectionByTag(tag: KDocKnownTag): KDocSection?
|
||||
fun findSectionByTag(tag: KDocKnownTag, subjectName: String): KDocSection?
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kdoc.psi.impl
|
||||
@@ -37,9 +26,12 @@ class KDocImpl(buffer: CharSequence?) : LazyParseablePsiElement(KDocTokens.KDOC,
|
||||
|
||||
override fun getTokenType(): IElementType = KtTokens.DOC_COMMENT
|
||||
|
||||
override fun getOwner(): KtDeclaration? = getParentOfType<KtDeclaration>(true)
|
||||
override fun getOwner(): KtDeclaration? = getParentOfType(true)
|
||||
|
||||
override fun getDefaultSection(): KDocSection = getChildOfType<KDocSection>()!!
|
||||
override fun getDefaultSection(): KDocSection = getChildOfType()!!
|
||||
|
||||
override fun getAllSections(): List<KDocSection> =
|
||||
getChildrenOfType<KDocSection>().toList()
|
||||
|
||||
override fun findSectionByName(name: String): KDocSection? =
|
||||
getChildrenOfType<KDocSection>().firstOrNull { it.name == name }
|
||||
|
||||
@@ -62,7 +62,8 @@ class CodeConformanceTest : TestCase() {
|
||||
"compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt",
|
||||
"compiler/util/src/org/jetbrains/kotlin/config/MavenComparableVersion.java",
|
||||
"dependencies/protobuf/protobuf-relocated/build",
|
||||
"compiler/fir/lightTree/testData"
|
||||
"compiler/fir/lightTree/testData",
|
||||
"idea/testData/codeInsight/renderingKDoc"
|
||||
).map(::File)
|
||||
|
||||
private val COPYRIGHT_EXCLUDED_FILES_AND_DIRS = listOf(
|
||||
|
||||
@@ -476,6 +476,10 @@ fun main(args: Array<String>) {
|
||||
model("codeInsight/expressionType")
|
||||
}
|
||||
|
||||
testClass<AbstractRenderingKDocTest> {
|
||||
model("codeInsight/renderingKDoc")
|
||||
}
|
||||
|
||||
testClass<AbstractBackspaceHandlerTest> {
|
||||
model("editor/backspaceHandler")
|
||||
}
|
||||
|
||||
@@ -37,6 +37,6 @@ class KDocElementFactory(val project: Project) {
|
||||
val section = kdoc.getDefaultSection()
|
||||
val tag = section.findTagByName("param")
|
||||
val link = tag?.getSubjectLink() ?: throw IllegalArgumentException("Cannot find subject link in doc comment '$kdocText'")
|
||||
return link.getChildOfType<KDocName>()!!
|
||||
return link.getChildOfType()!!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +373,10 @@ kdoc.section.title.parameters=Params
|
||||
kdoc.section.title.returns=Returns
|
||||
kdoc.section.title.throws=Throws
|
||||
kdoc.section.title.author=Author
|
||||
kdoc.section.title.properties=Properties
|
||||
kdoc.section.title.constructor=Constructor
|
||||
kdoc.section.title.since=Since
|
||||
kdoc.section.title.suppress=Suppress
|
||||
kdoc.section.title.samples=Samples
|
||||
kdoc.section.title.see.also=See Also
|
||||
kdoc.comment.unresolved=Unresolved
|
||||
|
||||
@@ -13,12 +13,7 @@ import com.intellij.lang.documentation.DocumentationMarkup.*
|
||||
import com.intellij.lang.java.JavaDocumentationProvider
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.IndexNotReadyException
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
@@ -30,7 +25,7 @@ import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
|
||||
import org.jetbrains.kotlin.idea.kdoc.*
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer.appendKDocContent
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer.appendKDocSection
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer.appendKDocSections
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocTemplate.DescriptionBodyTemplate
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||
@@ -38,6 +33,7 @@ import org.jetbrains.kotlin.idea.util.isRunningInCidrIde
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
@@ -120,11 +116,9 @@ class WrapValueParameterHandler(val base: DescriptorRenderer.ValueParametersHand
|
||||
}
|
||||
base.appendAfterValueParameters(parameterCount, builder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
|
||||
open class KotlinQuickDocumentationProviderCompatBase : AbstractDocumentationProvider() {
|
||||
|
||||
override fun getCustomDocumentationElement(editor: Editor, fil: PsiFile, contextElement: PsiElement?): PsiElement? {
|
||||
return if (contextElement.isModifier()) contextElement else null
|
||||
@@ -173,6 +167,17 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
|
||||
boldOnlyForNamesInHtml = true
|
||||
}
|
||||
|
||||
fun StringBuilder.renderKDoc(contentTag: KDocTag, sections: List<KDocSection>) {
|
||||
insert(DescriptionBodyTemplate.Kotlin()) {
|
||||
content {
|
||||
appendKDocContent(contentTag)
|
||||
}
|
||||
sections {
|
||||
appendKDocSections(sections)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderEnumSpecialFunction(element: KtClass, functionDescriptor: FunctionDescriptor, quickNavigation: Boolean): String {
|
||||
val kdoc = run {
|
||||
val declarationDescriptor = element.resolveToDescriptorIfAny()
|
||||
@@ -188,23 +193,14 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
val section = kdoc?.getDefaultSection()
|
||||
|
||||
return buildString {
|
||||
insert(KDocTemplate()) {
|
||||
definition {
|
||||
renderDefinition(functionDescriptor, DESCRIPTOR_RENDERER)
|
||||
}
|
||||
if (!quickNavigation && section != null) {
|
||||
if (!quickNavigation && kdoc != null) {
|
||||
description {
|
||||
insert(DescriptionBodyTemplate.Kotlin()) {
|
||||
content {
|
||||
appendKDocContent(section)
|
||||
}
|
||||
sections {
|
||||
appendKDocSection(section)
|
||||
}
|
||||
}
|
||||
renderKDoc(kdoc.getDefaultSection(), listOf(kdoc.getDefaultSection()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +251,7 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
|
||||
// element is not an KtReferenceExpression, but KtClass of enum
|
||||
return renderEnum(element, originalElement, quickNavigation)
|
||||
} else if (element is KtEnumEntry && !quickNavigation) {
|
||||
val ordinal = element.containingClassOrObject?.getBody()?.run { getChildrenOfType<KtEnumEntry>().indexOf(element) }
|
||||
val ordinal = element.containingClassOrObject?.body?.run { getChildrenOfType<KtEnumEntry>().indexOf(element) }
|
||||
|
||||
return buildString {
|
||||
insert(buildKotlinDeclaration(element, quickNavigation)) {
|
||||
@@ -360,14 +356,8 @@ class KotlinQuickDocumentationProvider : AbstractDocumentationProvider() {
|
||||
description {
|
||||
val comment = declarationDescriptor.findKDoc { DescriptorToSourceUtilsIde.getAnyDeclaration(ktElement.project, it) }
|
||||
if (comment != null) {
|
||||
insert(DescriptionBodyTemplate.Kotlin()) {
|
||||
content {
|
||||
appendKDocContent(comment)
|
||||
}
|
||||
sections {
|
||||
if (comment is KDocSection) appendKDocSection(comment)
|
||||
}
|
||||
}
|
||||
val sectionList = if (comment is KDocSection) listOf(comment) else emptyList()
|
||||
renderKDoc(comment, sectionList)
|
||||
} else if (declarationDescriptor is CallableDescriptor) { // If we couldn't find KDoc, try to find javadoc in one of super's
|
||||
insert(DescriptionBodyTemplate.FromJava()) {
|
||||
body = extractJavaDescription(declarationDescriptor)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea
|
||||
|
||||
// BUNCH 201
|
||||
class KotlinQuickDocumentationProvider : KotlinQuickDocumentationProviderCompatBase()
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea
|
||||
|
||||
import com.intellij.codeInsight.javadoc.JavaDocExternalFilter
|
||||
import com.intellij.psi.PsiDocCommentBase
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.util.function.Consumer
|
||||
|
||||
// BUNCH 201
|
||||
class KotlinQuickDocumentationProvider : KotlinQuickDocumentationProviderCompatBase() {
|
||||
|
||||
override fun collectDocComments(file: PsiFile, sink: Consumer<PsiDocCommentBase>) {
|
||||
if (file !is KtFile) return
|
||||
|
||||
PsiTreeUtil.processElements(file) {
|
||||
val comment = (it as? KtDeclaration)?.docComment
|
||||
if (comment != null) sink.accept(comment)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateRenderedDoc(element: PsiElement): String? {
|
||||
val docComment = (element as? KtDeclaration)?.docComment ?: return null
|
||||
|
||||
val result = StringBuilder().also {
|
||||
it.renderKDoc(docComment.getDefaultSection(), docComment.getAllSections())
|
||||
}
|
||||
|
||||
return JavaDocExternalFilter.filterInternalDocInfo(result.toString())
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.kdoc
|
||||
@@ -37,44 +26,43 @@ import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
|
||||
object KDocRenderer {
|
||||
fun renderKDoc(docComment: KDocTag): String {
|
||||
return if (docComment is KDocSection) {
|
||||
renderKDocContent(docComment) + renderKDocSection(docComment)
|
||||
} else {
|
||||
renderKDocContent(docComment)
|
||||
}
|
||||
}
|
||||
|
||||
fun renderKDocContent(docComment: KDocTag): String {
|
||||
return markdownToHtml(docComment.getContent(), allowSingleParagraph = true)
|
||||
}
|
||||
private fun renderKDocContent(docComment: KDocTag) =
|
||||
markdownToHtml(docComment.getContent(), allowSingleParagraph = true)
|
||||
|
||||
fun StringBuilder.appendKDocContent(docComment: KDocTag) {
|
||||
fun StringBuilder.appendKDocContent(docComment: KDocTag): StringBuilder =
|
||||
append(renderKDocContent(docComment))
|
||||
}
|
||||
|
||||
fun StringBuilder.appendKDocSections(sections: List<KDocSection>) {
|
||||
fun findTagsByName(name: String) =
|
||||
sequence { sections.forEach { yieldAll(it.findTagsByName(name)) } }
|
||||
.filterNotNull()
|
||||
|
||||
fun renderKDocSection(section: KDocSection): String = buildString {
|
||||
appendKDocSection(section)
|
||||
}
|
||||
fun findTagByName(name: String) = findTagsByName(name).firstOrNull()
|
||||
|
||||
fun StringBuilder.appendKDocSection(section: KDocSection) {
|
||||
renderTag(section.findTagByName("receiver"), KotlinBundle.message("kdoc.section.title.receiver"), this)
|
||||
val paramTags = section.findTagsByName("param").filter { it.getSubjectName() != null }
|
||||
renderTag(findTagByName("receiver"), KotlinBundle.message("kdoc.section.title.receiver"), this)
|
||||
|
||||
val paramTags = findTagsByName("param").filter { it.getSubjectName() != null }
|
||||
renderTagList(paramTags, KotlinBundle.message("kdoc.section.title.parameters"), this)
|
||||
|
||||
renderTag(section.findTagByName("return"), KotlinBundle.message("kdoc.section.title.returns"), this)
|
||||
val propertyTags = findTagsByName("property").filter { it.getSubjectName() != null }
|
||||
renderTagList(propertyTags, KotlinBundle.message("kdoc.section.title.properties"), this)
|
||||
|
||||
val throwsTags = (section.findTagsByName("exception").union(section.findTagsByName("throws")))
|
||||
.filter { it.getSubjectName() != null }
|
||||
renderTagList(throwsTags, KotlinBundle.message("kdoc.section.title.throws"), this)
|
||||
renderTag(findTagByName("constructor"), KotlinBundle.message("kdoc.section.title.constructor"), this)
|
||||
|
||||
renderTag(section.findTagByName("author"), KotlinBundle.message("kdoc.section.title.author"), this)
|
||||
renderTag(section.findTagByName("since"), KotlinBundle.message("kdoc.section.title.since"), this)
|
||||
renderTag(findTagByName("return"), KotlinBundle.message("kdoc.section.title.returns"), this)
|
||||
|
||||
renderSeeAlso(section, this)
|
||||
val throwTags = findTagsByName("throws").filter { it.getSubjectName() != null }
|
||||
val exceptionTags = findTagsByName("exception").filter { it.getSubjectName() != null }
|
||||
renderThrows(throwTags, exceptionTags, this)
|
||||
|
||||
val sampleTags = section.findTagsByName("sample").filter { it.getSubjectLink() != null }
|
||||
renderTag(findTagByName("author"), KotlinBundle.message("kdoc.section.title.author"), this)
|
||||
renderTag(findTagByName("since"), KotlinBundle.message("kdoc.section.title.since"), this)
|
||||
renderTag(findTagByName("suppress"), KotlinBundle.message("kdoc.section.title.suppress"), this)
|
||||
|
||||
renderSeeAlso(findTagsByName("see"), this)
|
||||
|
||||
val sampleTags = findTagsByName("sample").filter { it.getSubjectLink() != null }
|
||||
renderSamplesList(sampleTags, this)
|
||||
}
|
||||
|
||||
@@ -88,8 +76,7 @@ object KDocRenderer {
|
||||
|
||||
private fun PsiElement.extractExampleText() = when (this) {
|
||||
is KtDeclarationWithBody -> {
|
||||
val bodyExpression = bodyExpression
|
||||
when (bodyExpression) {
|
||||
when (val bodyExpression = bodyExpression) {
|
||||
is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
|
||||
else -> bodyExpression!!.text
|
||||
}
|
||||
@@ -111,8 +98,8 @@ object KDocRenderer {
|
||||
append(SECTION_END)
|
||||
}
|
||||
|
||||
private fun renderSamplesList(sampleTags: List<KDocTag>, to: StringBuilder) {
|
||||
if (sampleTags.isEmpty()) return
|
||||
private fun renderSamplesList(sampleTags: Sequence<KDocTag>, to: StringBuilder) {
|
||||
if (!sampleTags.any()) return
|
||||
|
||||
to.renderSection(KotlinBundle.message("kdoc.section.title.samples")) {
|
||||
sampleTags.forEach {
|
||||
@@ -122,9 +109,9 @@ object KDocRenderer {
|
||||
val target = subjectLink.getTargetElement()
|
||||
wrapTag("pre") {
|
||||
wrapTag("code") {
|
||||
if (target == null)
|
||||
if (target == null) {
|
||||
to.append("// " + KotlinBundle.message("kdoc.comment.unresolved"))
|
||||
else {
|
||||
} else {
|
||||
to.append(trimCommonIndent(target.extractExampleText()).htmlEscape())
|
||||
}
|
||||
}
|
||||
@@ -134,32 +121,57 @@ object KDocRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSeeAlso(docComment: KDocSection, to: StringBuilder) {
|
||||
val seeTags = docComment.findTagsByName("see")
|
||||
if (seeTags.isEmpty()) return
|
||||
private fun renderSeeAlso(seeTags: Sequence<KDocTag>, to: StringBuilder) {
|
||||
if (!seeTags.any()) return
|
||||
|
||||
val iterator = seeTags.iterator()
|
||||
|
||||
to.renderSection(KotlinBundle.message("kdoc.section.title.see.also")) {
|
||||
seeTags.forEachIndexed { index, tag ->
|
||||
while (iterator.hasNext()) {
|
||||
val tag = iterator.next()
|
||||
val subjectName = tag.getSubjectName()
|
||||
if (subjectName != null) {
|
||||
DocumentationManagerUtil.createHyperlink(this, subjectName, subjectName, false)
|
||||
} else {
|
||||
append(tag.getContent())
|
||||
}
|
||||
if (index < seeTags.size - 1) {
|
||||
if (iterator.hasNext()) {
|
||||
append(", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTagList(tags: List<KDocTag>, title: String, to: StringBuilder) {
|
||||
if (tags.isEmpty()) {
|
||||
private fun renderThrows(throwsTags: Sequence<KDocTag>, exceptionsTags: Sequence<KDocTag>, to: StringBuilder) {
|
||||
if (!throwsTags.any() && !exceptionsTags.any()) return
|
||||
|
||||
to.renderSection(KotlinBundle.message("kdoc.section.title.throws")) {
|
||||
|
||||
fun KDocTag.append() {
|
||||
val subjectName = getSubjectName()
|
||||
if (subjectName != null) {
|
||||
append("<p><code>")
|
||||
DocumentationManagerUtil.createHyperlink(this@renderSection, subjectName, subjectName, false)
|
||||
append("</code> - ${markdownToHtml(getContent().trimStart())}")
|
||||
}
|
||||
}
|
||||
|
||||
throwsTags.forEach { it.append() }
|
||||
exceptionsTags.forEach { it.append() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun renderTagList(tags: Sequence<KDocTag>, title: String, to: StringBuilder) {
|
||||
if (!tags.any()) {
|
||||
return
|
||||
}
|
||||
to.renderSection(title) {
|
||||
tags.forEach {
|
||||
append("<p><code>${it.getSubjectName()}</code> - ${markdownToHtml(it.getContent().trimStart())}")
|
||||
val subjectName = it.getSubjectName()
|
||||
if (subjectName != null) {
|
||||
append("<p><code>$subjectName</code> - ${markdownToHtml(it.getContent().trimStart())}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +184,7 @@ object KDocRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
fun markdownToHtml(markdown: String, allowSingleParagraph: Boolean = false): String {
|
||||
private fun markdownToHtml(markdown: String, allowSingleParagraph: Boolean = false): String {
|
||||
val markdownTree = MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown)
|
||||
val markdownNode = MarkdownNode(markdownTree, null, markdown)
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
/**
|
||||
* Hello darling
|
||||
* @author DarkWing Duck
|
||||
*/
|
||||
class testClass
|
||||
|
||||
/**
|
||||
* Hello darling outher
|
||||
* @author Morgana Macawber
|
||||
*/
|
||||
class outherClass {
|
||||
/**
|
||||
* Hello darling inner
|
||||
* @author Launchpad McQuack
|
||||
*/
|
||||
inner class innerClass {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// RENDER: <div class='content'><p>Hello darling</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>DarkWing Duck</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling outher</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Morgana Macawber</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling inner</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Launchpad McQuack</td></table>
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* This class has no useful logic; it's just a documentation example.
|
||||
*
|
||||
* @param T the type of a member in this class.
|
||||
* @param X the type of a member in this class.
|
||||
* @property name is blablabla for name
|
||||
* @property name2 is blublublu for name2
|
||||
* @constructor primary ctor for it
|
||||
*/
|
||||
class klass<T, X>(val name: String, val name2: String) {
|
||||
/**
|
||||
* This method has no useful logic; it's just a documentation example.
|
||||
*
|
||||
* @param M the type of a member in this method.
|
||||
* @param K the type of a member in this group.
|
||||
* @property[name] is blablabla for name
|
||||
* @property name2 blublublu for name2
|
||||
* @throws IndexOutOfBoundsException if something wrong
|
||||
* @throws ArrayIndexOutOfBoundsException if else something wrong
|
||||
* @exception java.io.IOException if something else else wrong
|
||||
* @see method2
|
||||
* @see method3
|
||||
* @return [returnMethod] if Duck is Dark
|
||||
* @author DarkWing Duck
|
||||
* @suppress something to suppress
|
||||
* @sample sample
|
||||
* @sample sample2
|
||||
*/
|
||||
fun <M, K> method(name: String, name2: String) {
|
||||
|
||||
}
|
||||
|
||||
fun method2() {
|
||||
|
||||
}
|
||||
|
||||
fun method3() {
|
||||
|
||||
}
|
||||
|
||||
fun returnMethod() {
|
||||
|
||||
}
|
||||
|
||||
fun sample() {
|
||||
//this is the sample
|
||||
}
|
||||
|
||||
fun sample2() {
|
||||
//this is the sample2
|
||||
}
|
||||
}
|
||||
|
||||
//RENDER: <div class='content'><p>This class has no useful logic; it's just a documentation example.</p></div><table class='sections'><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><p><code>T</code> - the type of a member in this class.<p><code>X</code> - the type of a member in this class.</td><tr><td valign='top' class='section'><p>Properties:</td><td valign='top'><p><code>name</code> - is blablabla for name<p><code>name2</code> - is blublublu for name2</td><tr><td valign='top' class='section'><p>Constructor:</td><td valign='top'>primary ctor for it</td></table>
|
||||
//RENDER: <div class='content'><p>This method has no useful logic; it's just a documentation example.</p></div><table class='sections'><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><p><code>M</code> - the type of a member in this method.<p><code>K</code> - the type of a member in this group.</td><tr><td valign='top' class='section'><p>Properties:</td><td valign='top'><p><code>name</code> - is blablabla for name<p><code>name2</code> - blublublu for name2</td><tr><td valign='top' class='section'><p>Returns:</td><td valign='top'><a href="psi_element://returnMethod">returnMethod</a> if Duck is Dark</td><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><code><a href="psi_element://IndexOutOfBoundsException"><code>IndexOutOfBoundsException</code></a></code> - if something wrong<p><code><a href="psi_element://ArrayIndexOutOfBoundsException"><code>ArrayIndexOutOfBoundsException</code></a></code> - if else something wrong<p><code><a href="psi_element://java.io.IOException"><code>java.io.IOException</code></a></code> - if something else else wrong</td><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>DarkWing Duck</td><tr><td valign='top' class='section'><p>Suppress:</td><td valign='top'>something to suppress</td><tr><td valign='top' class='section'><p>See Also:</td><td valign='top'><a href="psi_element://method2"><code>method2</code></a>, <a href="psi_element://method3"><code>method3</code></a></td><tr><td valign='top' class='section'><p>Samples:</td><td valign='top'><p><a href="psi_element://sample"><code>sample</code></a><pre><code>//this is the sample</code></pre><p><a href="psi_element://sample2"><code>sample2</code></a><pre><code>//this is the sample2</code></pre></td></table>
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
/**
|
||||
* Hello darling
|
||||
* @author DarkWing Duck
|
||||
*/
|
||||
fun testFun() {}
|
||||
|
||||
class outherClass {
|
||||
/**
|
||||
* Hello darling instance
|
||||
* @author Morgana Macawber
|
||||
*/
|
||||
fun instanceFun() {
|
||||
/**
|
||||
* Hello darling local
|
||||
* @author Launchpad McQuack
|
||||
*/
|
||||
fun localFun() {
|
||||
if (true) {
|
||||
/**
|
||||
* Hello darling superLocal
|
||||
* @author Reginald Bushroot
|
||||
*/
|
||||
fun superLocalFun() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RENDER: <div class='content'><p>Hello darling</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>DarkWing Duck</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling instance</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Morgana Macawber</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling local</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Launchpad McQuack</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling superLocal</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Reginald Bushroot</td></table>
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
/**
|
||||
* Hello darling
|
||||
* @author DarkWing Duck
|
||||
*/
|
||||
val prop = 2
|
||||
|
||||
/**
|
||||
* Hello darling var
|
||||
* @author Megavolt
|
||||
*/
|
||||
var prop = 2
|
||||
|
||||
|
||||
class outherClass {
|
||||
/**
|
||||
* Hello darling instance
|
||||
* @author Morgana Macawber
|
||||
*/
|
||||
val instanceProp get() {
|
||||
/**
|
||||
* Hello darling local
|
||||
* @author Launchpad McQuack
|
||||
*/
|
||||
val localProp get() {
|
||||
if (true) {
|
||||
/**
|
||||
* Hello darling superLocal
|
||||
* @author Reginald Bushroot
|
||||
*/
|
||||
val superLocalProp = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RENDER: <div class='content'><p>Hello darling</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>DarkWing Duck</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling var</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Megavolt</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling instance</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Morgana Macawber</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling local</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Launchpad McQuack</td></table>
|
||||
// RENDER: <div class='content'><p>Hello darling superLocal</p></div><table class='sections'><tr><td valign='top' class='section'><p>Author:</td><td valign='top'>Reginald Bushroot</td></table>
|
||||
+1
-1
@@ -9,4 +9,4 @@ fun use() {
|
||||
|
||||
//INFO: <div class='definition'><pre><a href="psi_element://E"><code>E</code></a><br>public final fun <b>valueOf</b>(
|
||||
//INFO: value: String
|
||||
//INFO: ): <a href="psi_element://E">E</a></pre></div><div class='content'><p>Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)</p></div><table class='sections'><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><code>IllegalArgumentException</code> - if this enum type has no constant with the specified name</td></table>
|
||||
//INFO: ): <a href="psi_element://E">E</a></pre></div><div class='content'><p>Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)</p></div><table class='sections'><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><code><a href="psi_element://IllegalArgumentException"><code>IllegalArgumentException</code></a></code> - if this enum type has no constant with the specified name</td></table>
|
||||
|
||||
@@ -17,4 +17,4 @@ fun test() {
|
||||
//INFO: <div class='definition'><pre><font color="808080"><i>OnMethodUsageWithReturnAndThrows.kt</i></font><br>public fun <b>testMethod</b>(
|
||||
//INFO: a: Int,
|
||||
//INFO: b: String
|
||||
//INFO: ): Unit</pre></div><div class='content'><p>Some documentation</p></div><table class='sections'><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><p><code>a</code> - Some int<p><code>b</code> - String</td><tr><td valign='top' class='section'><p>Returns:</td><td valign='top'>Return value</td><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><code>IllegalArgumentException</code> - if the weather is bad</td></table>
|
||||
//INFO: ): Unit</pre></div><div class='content'><p>Some documentation</p></div><table class='sections'><tr><td valign='top' class='section'><p>Params:</td><td valign='top'><p><code>a</code> - Some int<p><code>b</code> - String</td><tr><td valign='top' class='section'><p>Returns:</td><td valign='top'>Return value</td><tr><td valign='top' class='section'><p>Throws:</td><td valign='top'><p><code><a href="psi_element://IllegalArgumentException"><code>IllegalArgumentException</code></a></code> - if the weather is bad</td></table>
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ package p
|
||||
<selection>/**
|
||||
* Doc comment <caret>here
|
||||
*/
|
||||
</selection>fun foo() {
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
</selection>
|
||||
fun bar(){}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package p
|
||||
|
||||
<selection>/**
|
||||
* Doc comment <caret>here
|
||||
*/
|
||||
fun foo() {
|
||||
}
|
||||
</selection>
|
||||
fun bar(){}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
|
||||
//BUNCH 201
|
||||
abstract class AbstractRenderingKDocTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
protected fun doTest(path: String) = Unit
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.idea.KotlinQuickDocumentationProvider
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
|
||||
//BUNCH 201
|
||||
abstract class AbstractRenderingKDocTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
myFixture.configureByFile(fileName())
|
||||
val file = myFixture.file
|
||||
|
||||
val kDocProvider = KotlinQuickDocumentationProvider()
|
||||
|
||||
val comments = mutableListOf<String>()
|
||||
kDocProvider.collectDocComments(file) {
|
||||
val rendered = it.owner?.let { owner -> kDocProvider.generateRenderedDoc(owner) }
|
||||
if (rendered != null) {
|
||||
comments.add(rendered.replace("\n", ""))
|
||||
}
|
||||
}
|
||||
|
||||
val expectedRenders = InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.text, "// RENDER: ")
|
||||
UsefulTestCase.assertOrderedEquals(comments, expectedRenders)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/codeInsight/renderingKDoc")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class RenderingKDocTestGenerated extends AbstractRenderingKDocTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInRenderingKDoc() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/codeInsight/renderingKDoc"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("classRendering.kt")
|
||||
public void testClassRendering() throws Exception {
|
||||
runTest("idea/testData/codeInsight/renderingKDoc/classRendering.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("difficultKDoc.kt")
|
||||
public void testDifficultKDoc() throws Exception {
|
||||
runTest("idea/testData/codeInsight/renderingKDoc/difficultKDoc.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionRendering.kt")
|
||||
public void testFunctionRendering() throws Exception {
|
||||
runTest("idea/testData/codeInsight/renderingKDoc/functionRendering.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyRendering.kt")
|
||||
public void testPropertyRendering() throws Exception {
|
||||
runTest("idea/testData/codeInsight/renderingKDoc/propertyRendering.kt");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user