Merge pull request #595 from JetBrains/rr/yole/render-markdown

render Markdown and support link navigation in KDoc comments
This commit is contained in:
Dmitry Jemerov
2015-03-18 08:39:35 +01:00
23 changed files with 387 additions and 47 deletions
+1
View File
@@ -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" />
+9
View File
@@ -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
View File
@@ -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("&gt;")
MarkdownTokenTypes.LT -> sb.append("&lt;")
else -> {
processChildren()
}
}
}
return sb.toString()
}
fun String.htmlEscape(): String = replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
}
@@ -2,4 +2,4 @@ fun ktTest() {
Test.<caret>foo("SomeTest")
}
// INFO: <b>public</b> <b>open</b> <b>fun</b> foo(param: String!): Array&lt;(out) Any!&gt;!<br/>Java declaration:<br/>Test...
//INFO: <b>public</b> <b>open</b> <b>fun</b> foo(param: String!): Array&lt;(out) Any!&gt;!<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> &lt;T&gt; Iterable&lt;T&gt;.filter(predicate: (T) &rarr; Boolean): List&lt;T&gt;<br/><p>Returns a list containing all elements matching the given [predicate]</p>
//INFO: inline <b>public</b> <b>fun</b> &lt;T&gt; Iterable&lt;T&gt;.filter(predicate: (T) &rarr; Boolean): List&lt;T&gt;<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>
+5
View File
@@ -0,0 +1,5 @@
class C {
}
fun fo<caret>o() {
}
@@ -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());
}
@@ -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())
}
}
+4
View File
@@ -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">