Merge pull request #595 from JetBrains/rr/yole/render-markdown
render Markdown and support link navigation in KDoc comments
This commit is contained in:
Generated
+1
@@ -45,6 +45,7 @@
|
||||
<element id="directory" name="jps">
|
||||
<element id="artifact" artifact-name="KotlinJpsPlugin" />
|
||||
</element>
|
||||
<element id="library" level="project" name="markdown" />
|
||||
</element>
|
||||
<element id="directory" name="kotlinc">
|
||||
<element id="dir-copy" path="$PROJECT_DIR$/dist/kotlinc" />
|
||||
|
||||
Generated
+9
@@ -0,0 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="markdown">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/dependencies/markdown.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
+2
-1
@@ -45,5 +45,6 @@
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="android-compiler-plugin" scope="TEST" />
|
||||
<orderEntry type="module" module-name="android-idea-plugin" scope="TEST" />
|
||||
<orderEntry type="library" name="markdown" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
@@ -16,39 +16,42 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.intellij.lang.documentation.AbstractDocumentationProvider;
|
||||
import com.intellij.lang.java.JavaDocumentationProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightMethod;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource;
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocFinder;
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocRenderer;
|
||||
import org.jetbrains.kotlin.idea.kdoc.KdocPackage;
|
||||
import org.jetbrains.kotlin.idea.project.ResolveSessionForBodies;
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag;
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration;
|
||||
import org.jetbrains.kotlin.psi.JetPackageDirective;
|
||||
import org.jetbrains.kotlin.psi.JetElement;
|
||||
import org.jetbrains.kotlin.psi.JetPsiUtil;
|
||||
import org.jetbrains.kotlin.psi.JetReferenceExpression;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public class JetQuickDocumentationProvider extends AbstractDocumentationProvider {
|
||||
private static final Logger LOG = Logger.getInstance(JetQuickDocumentationProvider.class);
|
||||
|
||||
private static final Predicate<PsiElement> SKIP_WHITESPACE_AND_EMPTY_PACKAGE = new Predicate<PsiElement>() {
|
||||
@Override
|
||||
public boolean apply(PsiElement input) {
|
||||
// Skip empty package because there can be comments before it
|
||||
// Skip whitespaces
|
||||
return (input instanceof JetPackageDirective && input.getChildren().length == 0) || input instanceof PsiWhiteSpace;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
|
||||
return getText(element, originalElement, true);
|
||||
@@ -118,4 +121,33 @@ public class JetQuickDocumentationProvider extends AbstractDocumentationProvider
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
|
||||
if (!(context instanceof JetElement)) {
|
||||
return null;
|
||||
}
|
||||
JetElement jetElement = (JetElement) context;
|
||||
Project project = psiManager.getProject();
|
||||
KotlinCacheService cacheService = KotlinCacheService.getInstance(project);
|
||||
ResolveSessionForBodies session = cacheService.getLazyResolveSession(jetElement);
|
||||
ResolutionFacade facade = cacheService.getResolutionFacade(Collections.singletonList(jetElement));
|
||||
BindingContext bindingContext = facade.analyze(jetElement, BodyResolveMode.FULL);
|
||||
DeclarationDescriptor contextDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, context);
|
||||
if (contextDescriptor == null) {
|
||||
return null;
|
||||
}
|
||||
Collection<DeclarationDescriptor> descriptors =
|
||||
KdocPackage.resolveKDocLink(session, contextDescriptor, null, StringUtil.split(link, ","));
|
||||
if (!descriptors.isEmpty()) {
|
||||
DeclarationDescriptor target = descriptors.iterator().next();
|
||||
if (target instanceof DeclarationDescriptorWithSource) {
|
||||
SourceElement source = ((DeclarationDescriptorWithSource) target).getSource();
|
||||
if (source instanceof PsiSourceElement) {
|
||||
return ((PsiSourceElement) source).getPsi();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.kdoc
|
||||
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerUtil
|
||||
import org.intellij.markdown.IElementType
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import org.intellij.markdown.MarkdownTokenTypes
|
||||
import org.intellij.markdown.ast.ASTNode
|
||||
import org.intellij.markdown.parser.MarkdownParser
|
||||
import org.intellij.markdown.parser.dialects.commonmark.CommonMarkMarkerProcessor
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
|
||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
|
||||
|
||||
object KDocRenderer {
|
||||
fun renderKDoc(docComment: KDocTag): String {
|
||||
@@ -26,6 +32,7 @@ object KDocRenderer {
|
||||
val result = StringBuilder("<p>")
|
||||
result.append(markdownToHtml(content))
|
||||
if (docComment is KDocSection) {
|
||||
result.append("\n")
|
||||
val paramTags = docComment.findTagsByName("param").filter { it.getSubjectName() != null }
|
||||
renderTagList(paramTags, "Parameters", result)
|
||||
|
||||
@@ -37,11 +44,28 @@ object KDocRenderer {
|
||||
|
||||
renderTag(docComment.findTagByName("author"), "Author", result)
|
||||
renderTag(docComment.findTagByName("since"), "Since", result)
|
||||
|
||||
renderSeeAlso(docComment, result)
|
||||
}
|
||||
result.append("</p>")
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
private fun renderSeeAlso(docComment: KDocSection, to: StringBuilder) {
|
||||
val seeTags = docComment.findTagsByName("see")
|
||||
if (seeTags.isEmpty()) return
|
||||
to.append("<DD><DL>")
|
||||
to.append("<DT><b>").append("See Also:").append("</b>")
|
||||
to.append("<DD>")
|
||||
seeTags.forEachIndexed { index, tag ->
|
||||
DocumentationManagerUtil.createHyperlink(to, tag.getSubjectName(), tag.getSubjectName(), false)
|
||||
if (index < seeTags.size() - 1) {
|
||||
to.append(", ")
|
||||
}
|
||||
}
|
||||
to.append("</DD></DL></DD>");
|
||||
}
|
||||
|
||||
private fun renderTagList(tags: List<KDocTag>, title: String, to: StringBuilder) {
|
||||
if (tags.isEmpty()) {
|
||||
return
|
||||
@@ -50,19 +74,166 @@ object KDocRenderer {
|
||||
tags.forEach {
|
||||
to.append("<dd><code>${it.getSubjectName()}</code> - ${markdownToHtml(it.getContent().trimLeading())}</dd>")
|
||||
}
|
||||
to.append("</dl>")
|
||||
to.append("</dl>\n")
|
||||
}
|
||||
|
||||
private fun renderTag(tag: KDocTag?, title: String, to: StringBuilder) {
|
||||
if (tag != null) {
|
||||
to.append("<dl><dt><b>${title}:</b></dt>")
|
||||
to.append("<dd>${markdownToHtml(tag.getContent())}</dd>")
|
||||
to.append("</dl>")
|
||||
to.append("</dl>\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun markdownToHtml(markdown: String): String {
|
||||
// TODO Integrate a real Markdown parser
|
||||
return StringUtil.replace(markdown, "\n", "<br/>");
|
||||
val markdownTree = MarkdownParser(CommonMarkMarkerProcessor.Factory).buildMarkdownTreeFromString(markdown)
|
||||
val markdownNode = MarkdownNode(markdownTree, null, markdown)
|
||||
|
||||
// Avoid wrapping the entire converted contents in a <p> tag if it's just a single paragraph
|
||||
val maybeSingleParagraph = markdownNode.children.filter { it.type == MarkdownElementTypes.PARAGRAPH }.singleOrNull()
|
||||
if (maybeSingleParagraph != null) {
|
||||
return maybeSingleParagraph.children.map { it.toHtml() }.join("")
|
||||
} else {
|
||||
return markdownNode.toHtml()
|
||||
}
|
||||
}
|
||||
|
||||
class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) {
|
||||
val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) }
|
||||
val endOffset: Int get() = node.endOffset
|
||||
val startOffset: Int get() = node.startOffset
|
||||
val type: IElementType get() = node.type
|
||||
val text: String get() = markdown.substring(startOffset, endOffset)
|
||||
fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type }
|
||||
}
|
||||
|
||||
fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) {
|
||||
action(this) {
|
||||
for (child in children) {
|
||||
child.visit(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun MarkdownNode.toHtml(): String {
|
||||
val sb = StringBuilder()
|
||||
visit {(node, processChildren) ->
|
||||
val nodeType = node.type
|
||||
val nodeText = node.text
|
||||
when (nodeType) {
|
||||
MarkdownElementTypes.UNORDERED_LIST -> {
|
||||
sb.appendln("<ul>")
|
||||
processChildren()
|
||||
sb.appendln("</ul>")
|
||||
}
|
||||
MarkdownElementTypes.ORDERED_LIST -> {
|
||||
sb.appendln("<ol>")
|
||||
processChildren()
|
||||
sb.appendln("</ol>")
|
||||
}
|
||||
MarkdownElementTypes.LIST_ITEM -> {
|
||||
sb.append("<li>")
|
||||
processChildren()
|
||||
sb.appendln("</li>")
|
||||
}
|
||||
MarkdownElementTypes.EMPH -> {
|
||||
sb.append("<em>")
|
||||
processChildren()
|
||||
sb.append("</em>")
|
||||
}
|
||||
MarkdownElementTypes.STRONG -> {
|
||||
sb.append("<strong>")
|
||||
processChildren()
|
||||
sb.append("</strong>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_1 -> {
|
||||
sb.append("<h1>")
|
||||
processChildren()
|
||||
sb.append("</h1>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_2 -> {
|
||||
sb.append("<h2>")
|
||||
processChildren()
|
||||
sb.append("</h2>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_3 -> {
|
||||
sb.append("<h3>")
|
||||
processChildren()
|
||||
sb.append("</h3>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_4 -> {
|
||||
sb.append("<h4>")
|
||||
processChildren()
|
||||
sb.append("</h4>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_5 -> {
|
||||
sb.append("<h5>")
|
||||
processChildren()
|
||||
sb.append("</h5>")
|
||||
}
|
||||
MarkdownElementTypes.ATX_6 -> {
|
||||
sb.append("<h6>")
|
||||
processChildren()
|
||||
sb.append("</h6>")
|
||||
}
|
||||
MarkdownElementTypes.BLOCK_QUOTE -> {
|
||||
sb.append("<blockquote>")
|
||||
processChildren()
|
||||
sb.append("</blockquote>")
|
||||
}
|
||||
MarkdownElementTypes.PARAGRAPH -> {
|
||||
sb.append("<p>")
|
||||
processChildren()
|
||||
sb.appendln("</p>")
|
||||
}
|
||||
MarkdownElementTypes.CODE_SPAN -> {
|
||||
sb.append("<code>")
|
||||
processChildren()
|
||||
sb.append("</code>")
|
||||
}
|
||||
MarkdownElementTypes.CODE_BLOCK -> {
|
||||
sb.append("<pre><code>")
|
||||
processChildren()
|
||||
sb.append("</code><pre>")
|
||||
}
|
||||
MarkdownElementTypes.SHORT_REFERENCE_LINK,
|
||||
MarkdownElementTypes.FULL_REFERENCE_LINK -> {
|
||||
val label = node.child(MarkdownElementTypes.LINK_LABEL)?.child(MarkdownTokenTypes.TEXT)?.text
|
||||
if (label != null) {
|
||||
val linkText = node.child(MarkdownElementTypes.LINK_TEXT)?.child(MarkdownTokenTypes.TEXT)?.text ?: label
|
||||
DocumentationManagerUtil.createHyperlink(sb, label, linkText, true)
|
||||
} else {
|
||||
sb.append(node.text)
|
||||
}
|
||||
}
|
||||
MarkdownElementTypes.INLINE_LINK -> {
|
||||
val label = node.child(MarkdownElementTypes.LINK_TEXT)?.child(MarkdownTokenTypes.TEXT)?.text
|
||||
val destination = node.child(MarkdownElementTypes.LINK_DESTINATION)?.text
|
||||
if (label != null && destination != null) {
|
||||
sb.append("a href=\"${destination}\">${label.htmlEscape()}</a>")
|
||||
} else {
|
||||
sb.append(node.text)
|
||||
}
|
||||
}
|
||||
MarkdownTokenTypes.TEXT,
|
||||
MarkdownTokenTypes.WHITE_SPACE,
|
||||
MarkdownTokenTypes.COLON,
|
||||
MarkdownTokenTypes.DOUBLE_QUOTE,
|
||||
MarkdownTokenTypes.LPAREN,
|
||||
MarkdownTokenTypes.RPAREN,
|
||||
MarkdownTokenTypes.LBRACKET,
|
||||
MarkdownTokenTypes.RBRACKET -> {
|
||||
sb.append(nodeText)
|
||||
}
|
||||
MarkdownTokenTypes.GT -> sb.append(">")
|
||||
MarkdownTokenTypes.LT -> sb.append("<")
|
||||
else -> {
|
||||
processChildren()
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun String.htmlEscape(): String = replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ fun ktTest() {
|
||||
Test.<caret>foo("SomeTest")
|
||||
}
|
||||
|
||||
// INFO: <b>public</b> <b>open</b> <b>fun</b> foo(param: String!): Array<(out) Any!>!<br/>Java declaration:<br/>Test...
|
||||
//INFO: <b>public</b> <b>open</b> <b>fun</b> foo(param: String!): Array<(out) Any!>!<br/>Java declaration:<br/>Test...
|
||||
|
||||
@@ -6,4 +6,4 @@ class KotlinClassUsedFromJava {
|
||||
}
|
||||
}
|
||||
|
||||
// INFO: [light_idea_test_case] testing...
|
||||
//INFO: [light_idea_test_case] testing.TestingPackage...
|
||||
|
||||
@@ -2,4 +2,5 @@ fun test() {
|
||||
listOf(1, 2, 4).<caret>filter { it > 0 }
|
||||
}
|
||||
|
||||
// INFO: inline <b>public</b> <b>fun</b> <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T><br/><p>Returns a list containing all elements matching the given [predicate]</p>
|
||||
//INFO: inline <b>public</b> <b>fun</b> <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T><br/><p>Returns a list containing all elements matching the given <a href="psi_element://predicate">predicate</a>
|
||||
//INFO: </p>
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
*/
|
||||
class <caret>Some
|
||||
|
||||
// INFO: <b>internal</b> <b>final</b> <b>class</b> Some<br/><p>Usefull comment</p>
|
||||
//INFO: <b>internal</b> <b>final</b> <b>class</b> Some<br/><p>Usefull comment
|
||||
//INFO: </p>
|
||||
|
||||
@@ -12,4 +12,6 @@ package test
|
||||
*/
|
||||
fun <caret>testFun(first: String, second: Int) = 12
|
||||
|
||||
// INFO: <b>internal</b> <b>fun</b> testFun(first: String, second: Int): Int<br/><p>Test function<br/><br/><br/><dl><dt><b>Parameters:</b></dt><dd><code>first</code> - Some</dd><dd><code>second</code> - Other</dd></dl></p>
|
||||
//INFO: <b>internal</b> <b>fun</b> testFun(first: String, second: Int): Int<br/><p>Test function
|
||||
//INFO: <dl><dt><b>Parameters:</b></dt><dd><code>first</code> - Some</dd><dd><code>second</code> - Other</dd></dl>
|
||||
//INFO: </p>
|
||||
|
||||
@@ -14,4 +14,5 @@ fun test() {
|
||||
D().f<caret>oo()
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>open</b> <b>fun</b> foo(): Int<br/><p>This method returns zero.</p>
|
||||
//INFO: <b>internal</b> <b>open</b> <b>fun</b> foo(): Int<br/><p>This method returns zero.
|
||||
//INFO: </p>
|
||||
|
||||
@@ -14,4 +14,5 @@ fun test() {
|
||||
D().f<caret>oo
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>open</b> <b>val</b> foo: Int<br/><p>This property returns zero.</p>
|
||||
//INFO: <b>internal</b> <b>open</b> <b>val</b> foo: Int<br/><p>This property returns zero.
|
||||
//INFO: </p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
Some documentation
|
||||
Some documentation
|
||||
|
||||
* @param a Some int
|
||||
* @param b String
|
||||
@@ -12,4 +12,6 @@ fun test() {
|
||||
<caret>testMethod(1, "value")
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation<br/><br/><dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl></p>
|
||||
//INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation
|
||||
//INFO: <dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl>
|
||||
//INFO: </p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
Some documentation
|
||||
Some documentation
|
||||
|
||||
* @param[a] Some int
|
||||
* @param[b] String
|
||||
@@ -12,4 +12,6 @@ fun test() {
|
||||
<caret>testMethod(1, "value")
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation<br/><br/><dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl></p>
|
||||
//INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation
|
||||
//INFO: <dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl>
|
||||
//INFO: </p>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Some documentation. **Bold** *underline* `code` foo: bar (baz) [quux] <xyzzy>
|
||||
*
|
||||
* [Kotlin](http://www.kotlinlang.org)
|
||||
*
|
||||
* [C]
|
||||
*
|
||||
* [See this class][C]
|
||||
*/
|
||||
fun testMethod() {
|
||||
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
fun test() {
|
||||
<caret>testMethod(1, "value")
|
||||
}
|
||||
|
||||
//INFO: <b>internal</b> <b>fun</b> testMethod(): Unit<br/><p><p>Some documentation. <strong>Bold</strong> <em>underline</em> <code>code</code> foo: bar (baz) <a href="psi_element://quux">quux</a> </p>
|
||||
//INFO: <p>a href="http://www.kotlinlang.org">Kotlin</a></p>
|
||||
//INFO: <p><a href="psi_element://C">C</a></p>
|
||||
//INFO: <p><a href="psi_element://C">See this class</a></p>
|
||||
//INFO:
|
||||
//INFO: </p>
|
||||
@@ -14,4 +14,8 @@ fun test() {
|
||||
<caret>testMethod(1, "value")
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation<br/><br/><dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl><dl><dt><b>Returns:</b></dt><dd>Return value</dd></dl><dl><dt><b>Throws:</b></dt><dd><code>IllegalArgumentException</code> - if the weather is bad</dd></dl></p>
|
||||
//INFO: <b>internal</b> <b>fun</b> testMethod(a: Int, b: String): Unit<br/><p>Some documentation
|
||||
//INFO: <dl><dt><b>Parameters:</b></dt><dd><code>a</code> - Some int</dd><dd><code>b</code> - String</dd></dl>
|
||||
//INFO: <dl><dt><b>Returns:</b></dt><dd>Return value</dd></dl>
|
||||
//INFO: <dl><dt><b>Throws:</b></dt><dd><code>IllegalArgumentException</code> - if the weather is bad</dd></dl>
|
||||
//INFO: </p>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @see C
|
||||
* @see D
|
||||
*/
|
||||
fun testMethod() {
|
||||
|
||||
}
|
||||
|
||||
class C {
|
||||
}
|
||||
|
||||
class D {
|
||||
}
|
||||
|
||||
fun test() {
|
||||
<caret>testMethod(1, "value")
|
||||
}
|
||||
|
||||
//INFO: <b>internal</b> <b>fun</b> testMethod(): Unit<br/><p>
|
||||
//INFO: <DD><DL><DT><b>See Also:</b><DD><a href="psi_element://C"><code>C</code></a>, <a href="psi_element://D"><code>D</code></a></DD></DL></DD></p>
|
||||
@@ -8,4 +8,5 @@ class Testing {
|
||||
}
|
||||
}
|
||||
|
||||
// INFO: <b>internal</b> <b>fun</b> foo(bar: Int): Unit<br/><p>KDoc foo</p>
|
||||
//INFO: <b>internal</b> <b>fun</b> foo(bar: Int): Unit<br/><p>KDoc foo
|
||||
//INFO: </p>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class C {
|
||||
}
|
||||
|
||||
fun fo<caret>o() {
|
||||
}
|
||||
+19
-15
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.idea.JetLightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.kotlin.idea.ProjectDescriptorWithStdlibSources;
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||
import org.jetbrains.kotlin.test.util.UtilPackage;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
@@ -44,6 +43,12 @@ public abstract class AbstractJetQuickDocProviderTest extends JetLightCodeInsigh
|
||||
PsiElement targetElement = documentationManager.findTargetElement(myFixture.getEditor(), myFixture.getFile());
|
||||
|
||||
String info = CtrlMouseHandler.getInfo(targetElement, element);
|
||||
if (info != null) {
|
||||
info = StringUtil.convertLineSeparators(info);
|
||||
}
|
||||
if (info != null && !info.endsWith("\n")) {
|
||||
info += "\n";
|
||||
}
|
||||
|
||||
File testDataFile = new File(path);
|
||||
String textData = FileUtil.loadFile(testDataFile, true);
|
||||
@@ -56,14 +61,15 @@ public abstract class AbstractJetQuickDocProviderTest extends JetLightCodeInsigh
|
||||
textData + "\n\n//INFO: " + info,
|
||||
testDataFile.getAbsolutePath());
|
||||
}
|
||||
else if (directives.size() == 1) {
|
||||
assertNotNull(info);
|
||||
else {
|
||||
StringBuilder expectedInfoBuilder = new StringBuilder();
|
||||
for (String directive : directives) {
|
||||
expectedInfoBuilder.append(directive).append("\n");
|
||||
}
|
||||
String expectedInfo = expectedInfoBuilder.toString();
|
||||
|
||||
String expectedInfo = directives.get(0);
|
||||
|
||||
// We can avoid testing for too long comments with \n character by placing '...' in test data
|
||||
if (expectedInfo.endsWith("...")) {
|
||||
if (!info.startsWith(StringUtil.trimEnd(expectedInfo, "..."))) {
|
||||
if (expectedInfo.endsWith("...\n")) {
|
||||
if (!info.startsWith(StringUtil.trimEnd(expectedInfo, "...\n"))) {
|
||||
wrapToFileComparisonFailure(info, path, textData);
|
||||
}
|
||||
}
|
||||
@@ -71,18 +77,16 @@ public abstract class AbstractJetQuickDocProviderTest extends JetLightCodeInsigh
|
||||
wrapToFileComparisonFailure(info, path, textData);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Assert.fail("Too many '// INFO:' directives in file " + path);
|
||||
}
|
||||
}
|
||||
|
||||
private static void wrapToFileComparisonFailure(String info, String filePath, String fileData) {
|
||||
int newLineIndex = info.indexOf('\n');
|
||||
if (newLineIndex != -1) {
|
||||
info = info.substring(0, newLineIndex) + "...";
|
||||
List<String> infoLines = StringUtil.split(info, "\n");
|
||||
StringBuilder infoBuilder = new StringBuilder();
|
||||
for (String line : infoLines) {
|
||||
infoBuilder.append("//INFO: ").append(line).append("\n");
|
||||
}
|
||||
|
||||
String correctedFileText = fileData.replaceFirst("//\\s?INFO: .*", "// INFO: " + info);
|
||||
String correctedFileText = fileData.replaceAll("//\\s?INFO: .*\n?", "") + infoBuilder.toString();
|
||||
throw new FileComparisonFailure("Unexpected info", fileData, correctedFileText, new File(filePath).getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -120,12 +120,24 @@ public class JetQuickDocProviderTestGenerated extends AbstractJetQuickDocProvide
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OnMethodUsageWithMarkdown.kt")
|
||||
public void testOnMethodUsageWithMarkdown() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnMethodUsageWithMarkdown.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OnMethodUsageWithReturnAndThrows.kt")
|
||||
public void testOnMethodUsageWithReturnAndThrows() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndThrows.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OnMethodUsageWithSee.kt")
|
||||
public void testOnMethodUsageWithSee() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/quickDoc/OnMethodUsageWithSee.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelMethodFromJava.java")
|
||||
public void testTopLevelMethodFromJava() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/editor/quickDoc/TopLevelMethodFromJava.java");
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.editor.quickDoc
|
||||
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.JetQuickDocumentationProvider
|
||||
import org.jetbrains.kotlin.idea.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.junit.Assert
|
||||
|
||||
public class QuickDocNavigationTest() : LightPlatformCodeInsightFixtureTestCase() {
|
||||
override fun getTestDataPath(): String {
|
||||
return PluginTestCaseBase.getTestDataPathBase() + "/kdoc/navigate/"
|
||||
}
|
||||
|
||||
public fun testSimple() {
|
||||
myFixture.configureByFile(getTestName(true) + ".kt")
|
||||
val source = myFixture.getElementAtCaret().getParentOfType<JetFunction>(false)
|
||||
val target = JetQuickDocumentationProvider().getDocumentationElementForLink(
|
||||
myFixture.getPsiManager(), "C", source);
|
||||
Assert.assertTrue(target is JetClass)
|
||||
Assert.assertEquals("C", (target as JetClass).getName())
|
||||
}
|
||||
}
|
||||
@@ -220,6 +220,10 @@
|
||||
</exec>
|
||||
</then>
|
||||
</if>
|
||||
|
||||
<!-- Markdown parser -->
|
||||
<get src="https://teamcity.jetbrains.com/guestAuth/repository/download/IntelliJMarkdownParser_Build/.lastFinished/markdown_jar/markdown.jar"
|
||||
dest="dependencies/markdown.jar" usetimestamp="true"/>
|
||||
</target>
|
||||
|
||||
<macrodef name="get_android_sdk">
|
||||
|
||||
Reference in New Issue
Block a user