Fix for KT-11032 Samples not rendered in Quick Doc
Now samples included as code blocks into quick doc
This commit is contained in:
@@ -17,14 +17,22 @@
|
|||||||
package org.jetbrains.kotlin.idea.kdoc
|
package org.jetbrains.kotlin.idea.kdoc
|
||||||
|
|
||||||
import com.intellij.codeInsight.documentation.DocumentationManagerUtil
|
import com.intellij.codeInsight.documentation.DocumentationManagerUtil
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.intellij.markdown.IElementType
|
import org.intellij.markdown.IElementType
|
||||||
import org.intellij.markdown.MarkdownElementTypes
|
import org.intellij.markdown.MarkdownElementTypes
|
||||||
import org.intellij.markdown.MarkdownTokenTypes
|
import org.intellij.markdown.MarkdownTokenTypes
|
||||||
import org.intellij.markdown.ast.ASTNode
|
import org.intellij.markdown.ast.ASTNode
|
||||||
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
||||||
import org.intellij.markdown.parser.MarkdownParser
|
import org.intellij.markdown.parser.MarkdownParser
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.util.wrapTag
|
||||||
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
|
||||||
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
||||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||||
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||||
|
|
||||||
object KDocRenderer {
|
object KDocRenderer {
|
||||||
fun renderKDoc(docComment: KDocTag): String {
|
fun renderKDoc(docComment: KDocTag): String {
|
||||||
@@ -47,10 +55,67 @@ object KDocRenderer {
|
|||||||
renderTag(docComment.findTagByName("since"), "Since", result)
|
renderTag(docComment.findTagByName("since"), "Since", result)
|
||||||
|
|
||||||
renderSeeAlso(docComment, result)
|
renderSeeAlso(docComment, result)
|
||||||
|
|
||||||
|
val sampleTags = docComment.findTagsByName("sample").filter { it.getSubjectLink() != null }
|
||||||
|
renderSamplesList(sampleTags, result)
|
||||||
}
|
}
|
||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KDocLink.createHyperlink(to: StringBuilder) {
|
||||||
|
DocumentationManagerUtil.createHyperlink(to, getLinkText(), getLinkText(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KDocLink.getTargetElement(): PsiElement? {
|
||||||
|
return this.getChildrenOfType<KDocName>().last().mainReference.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiElement.extractExampleText() = when (this) {
|
||||||
|
is KtDeclarationWithBody -> {
|
||||||
|
val bodyExpression = bodyExpression
|
||||||
|
when (bodyExpression) {
|
||||||
|
is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
|
||||||
|
else -> bodyExpression!!.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> text
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun trimCommonIndent(text: String): String {
|
||||||
|
fun String.leadingIndent() = indexOfFirst { !it.isWhitespace() }
|
||||||
|
|
||||||
|
val lines = text.split('\n')
|
||||||
|
val minIndent = lines.filter { it.trim().isNotEmpty() }.map(String::leadingIndent).min() ?: 0
|
||||||
|
return lines.map { it.drop(minIndent) }.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun renderSamplesList(sampleTags: List<KDocTag>, to: StringBuilder) {
|
||||||
|
if (sampleTags.isEmpty()) return
|
||||||
|
to.apply {
|
||||||
|
wrapTag("dl") {
|
||||||
|
append("<dt><b>Samples:</b></dt>")
|
||||||
|
sampleTags.forEach {
|
||||||
|
wrapTag("dd") {
|
||||||
|
it.getSubjectLink()?.let { subjectLink ->
|
||||||
|
subjectLink.createHyperlink(to)
|
||||||
|
val target = subjectLink.getTargetElement()
|
||||||
|
wrapTag("pre") {
|
||||||
|
wrapTag("code") {
|
||||||
|
if (target == null)
|
||||||
|
to.append("// Unresolved")
|
||||||
|
else {
|
||||||
|
to.append(trimCommonIndent(target.extractExampleText()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderSeeAlso(docComment: KDocSection, to: StringBuilder) {
|
private fun renderSeeAlso(docComment: KDocSection, to: StringBuilder) {
|
||||||
val seeTags = docComment.findTagsByName("see")
|
val seeTags = docComment.findTagsByName("see")
|
||||||
if (seeTags.isEmpty()) return
|
if (seeTags.isEmpty()) return
|
||||||
@@ -99,7 +164,8 @@ object KDocRenderer {
|
|||||||
val maybeSingleParagraph = markdownNode.children.filter { it.type != MarkdownTokenTypes.EOL }.singleOrNull()
|
val maybeSingleParagraph = markdownNode.children.filter { it.type != MarkdownTokenTypes.EOL }.singleOrNull()
|
||||||
if (maybeSingleParagraph != null && !allowSingleParagraph) {
|
if (maybeSingleParagraph != null && !allowSingleParagraph) {
|
||||||
return maybeSingleParagraph.children.joinToString("") { it.toHtml() }
|
return maybeSingleParagraph.children.joinToString("") { it.toHtml() }
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return markdownNode.toHtml()
|
return markdownNode.toHtml()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.util
|
||||||
|
|
||||||
|
|
||||||
|
inline fun StringBuilder.wrap(prefix: String, postfix: String, crossinline body: () -> Unit) {
|
||||||
|
append(prefix)
|
||||||
|
body()
|
||||||
|
append(postfix)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun StringBuilder.wrapTag(tag: String, crossinline body: () -> Unit) {
|
||||||
|
wrap("<$tag>", "</$tag>", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun StringBuilder.wrapTag(tag: String, params: String, crossinline body: () -> Unit) {
|
||||||
|
wrap("<$tag $params>", "</$tag>", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package magic
|
||||||
|
|
||||||
|
object Samples {
|
||||||
|
fun sampleMagic() {
|
||||||
|
castTextSpell("[asd] [dse] [asz]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sampleScroll() {
|
||||||
|
val reader = Scroll("[asd] [dse] [asz]").reader()
|
||||||
|
castTextSpell(reader.readAll())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @sample Samples.sampleMagic
|
||||||
|
* @sample sampleScroll
|
||||||
|
*/
|
||||||
|
fun <caret>castTextSpell(spell: String) {
|
||||||
|
throw SecurityException("Magic prohibited outside Hogwarts")
|
||||||
|
}
|
||||||
|
|
||||||
|
//INFO: <b>public</b> <b>fun</b> castTextSpell(spell: String): Unit <i>defined in</i> magic<br/>
|
||||||
|
//INFO: <dl><dt><b>Samples:</b></dt><dd><a href="psi_element://Samples.sampleMagic"><code>Samples.sampleMagic</code></a><pre><code>
|
||||||
|
//INFO: castTextSpell("[asd] [dse] [asz]")
|
||||||
|
//INFO: </code></pre></dd><dd><a href="psi_element://sampleScroll"><code>sampleScroll</code></a><pre><code>
|
||||||
|
//INFO: val reader = Scroll("[asd] [dse] [asz]").reader()
|
||||||
|
//INFO: castTextSpell(reader.readAll())
|
||||||
|
//INFO: </code></pre></dd></dl>
|
||||||
+6
@@ -204,6 +204,12 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("Samples.kt")
|
||||||
|
public void testSamples() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/Samples.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("TopLevelMethodFromJava.java")
|
@TestMetadata("TopLevelMethodFromJava.java")
|
||||||
public void testTopLevelMethodFromJava() throws Exception {
|
public void testTopLevelMethodFromJava() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/TopLevelMethodFromJava.java");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/editor/quickDoc/TopLevelMethodFromJava.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user