Support for showing rendered doc comments in editor

Fixed #KT-37361
This commit is contained in:
Igor Yakovlev
2020-03-20 23:20:15 +03:00
parent d620802a18
commit a71fd0e6d9
21 changed files with 419 additions and 131 deletions
@@ -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
View File
@@ -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
View File
@@ -3,7 +3,7 @@ package p
<selection>/**
* Doc comment <caret>here
*/
</selection>fun foo() {
fun foo() {
}
</selection>
fun bar(){}
-9
View File
@@ -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)
}
}
@@ -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");
}
}