diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml index 3d1bf9b325a..719f52019c5 100644 --- a/.idea/artifacts/KotlinPlugin.xml +++ b/.idea/artifacts/KotlinPlugin.xml @@ -45,6 +45,7 @@ + diff --git a/.idea/libraries/markdown.xml b/.idea/libraries/markdown.xml new file mode 100644 index 00000000000..5fbeaedaec3 --- /dev/null +++ b/.idea/libraries/markdown.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/idea/idea.iml b/idea/idea.iml index a3c6acca7e9..8f40f4b0dc8 100644 --- a/idea/idea.iml +++ b/idea/idea.iml @@ -45,5 +45,6 @@ + - + \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java b/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java index 1dc5375bd54..ce6f81df26b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java +++ b/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java @@ -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 SKIP_WHITESPACE_AND_EMPTY_PACKAGE = new Predicate() { - @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 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; + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt index d24e8ad4163..26c0f4f6dd9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/kdoc/KDocRenderer.kt @@ -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("

") 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("

") return result.toString() } + private fun renderSeeAlso(docComment: KDocSection, to: StringBuilder) { + val seeTags = docComment.findTagsByName("see") + if (seeTags.isEmpty()) return + to.append("
") + to.append("
").append("See Also:").append("") + to.append("
") + seeTags.forEachIndexed { index, tag -> + DocumentationManagerUtil.createHyperlink(to, tag.getSubjectName(), tag.getSubjectName(), false) + if (index < seeTags.size() - 1) { + to.append(", ") + } + } + to.append("
"); + } + private fun renderTagList(tags: List, title: String, to: StringBuilder) { if (tags.isEmpty()) { return @@ -50,19 +74,166 @@ object KDocRenderer { tags.forEach { to.append("
${it.getSubjectName()} - ${markdownToHtml(it.getContent().trimLeading())}
") } - to.append("") + to.append("\n") } private fun renderTag(tag: KDocTag?, title: String, to: StringBuilder) { if (tag != null) { to.append("
${title}:
") to.append("
${markdownToHtml(tag.getContent())}
") - to.append("
") + to.append("\n") } } fun markdownToHtml(markdown: String): String { - // TODO Integrate a real Markdown parser - return StringUtil.replace(markdown, "\n", "
"); + val markdownTree = MarkdownParser(CommonMarkMarkerProcessor.Factory).buildMarkdownTreeFromString(markdown) + val markdownNode = MarkdownNode(markdownTree, null, markdown) + + // Avoid wrapping the entire converted contents in a

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 = 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("

    ") + processChildren() + sb.appendln("
") + } + MarkdownElementTypes.ORDERED_LIST -> { + sb.appendln("
    ") + processChildren() + sb.appendln("
") + } + MarkdownElementTypes.LIST_ITEM -> { + sb.append("
  • ") + processChildren() + sb.appendln("
  • ") + } + MarkdownElementTypes.EMPH -> { + sb.append("") + processChildren() + sb.append("") + } + MarkdownElementTypes.STRONG -> { + sb.append("") + processChildren() + sb.append("") + } + MarkdownElementTypes.ATX_1 -> { + sb.append("

    ") + processChildren() + sb.append("

    ") + } + MarkdownElementTypes.ATX_2 -> { + sb.append("

    ") + processChildren() + sb.append("

    ") + } + MarkdownElementTypes.ATX_3 -> { + sb.append("

    ") + processChildren() + sb.append("

    ") + } + MarkdownElementTypes.ATX_4 -> { + sb.append("

    ") + processChildren() + sb.append("

    ") + } + MarkdownElementTypes.ATX_5 -> { + sb.append("
    ") + processChildren() + sb.append("
    ") + } + MarkdownElementTypes.ATX_6 -> { + sb.append("
    ") + processChildren() + sb.append("
    ") + } + MarkdownElementTypes.BLOCK_QUOTE -> { + sb.append("
    ") + processChildren() + sb.append("
    ") + } + MarkdownElementTypes.PARAGRAPH -> { + sb.append("

    ") + processChildren() + sb.appendln("

    ") + } + MarkdownElementTypes.CODE_SPAN -> { + sb.append("") + processChildren() + sb.append("") + } + MarkdownElementTypes.CODE_BLOCK -> { + sb.append("
    ")
    +                    processChildren()
    +                    sb.append("
    ")
    +                }
    +                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()}")
    +                    } 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(">", ">")
     }
    diff --git a/idea/testData/editor/quickDoc/JavaMethodUsedInKotlin.kt b/idea/testData/editor/quickDoc/JavaMethodUsedInKotlin.kt
    index 130341fb6f1..a98e6526025 100644
    --- a/idea/testData/editor/quickDoc/JavaMethodUsedInKotlin.kt
    +++ b/idea/testData/editor/quickDoc/JavaMethodUsedInKotlin.kt
    @@ -2,4 +2,4 @@ fun ktTest() {
         Test.foo("SomeTest")
     }
     
    -// INFO: public open fun foo(param: String!): Array<(out) Any!>!
    Java declaration:
    Test... \ No newline at end of file +//INFO: public open fun foo(param: String!): Array<(out) Any!>!
    Java declaration:
    Test... diff --git a/idea/testData/editor/quickDoc/KotlinPackageClassUsedFromJava.java b/idea/testData/editor/quickDoc/KotlinPackageClassUsedFromJava.java index 9d64e69288e..969e5b498e2 100644 --- a/idea/testData/editor/quickDoc/KotlinPackageClassUsedFromJava.java +++ b/idea/testData/editor/quickDoc/KotlinPackageClassUsedFromJava.java @@ -6,4 +6,4 @@ class KotlinClassUsedFromJava { } } -// INFO: [light_idea_test_case] testing... \ No newline at end of file +//INFO: [light_idea_test_case] testing.TestingPackage... diff --git a/idea/testData/editor/quickDoc/MethodFromStdLib.kt b/idea/testData/editor/quickDoc/MethodFromStdLib.kt index 57fb7c02a15..72cc73da994 100644 --- a/idea/testData/editor/quickDoc/MethodFromStdLib.kt +++ b/idea/testData/editor/quickDoc/MethodFromStdLib.kt @@ -2,4 +2,5 @@ fun test() { listOf(1, 2, 4).filter { it > 0 } } -// INFO: inline public fun <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T>

    Returns a list containing all elements matching the given [predicate]

    +//INFO: inline public fun <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T>

    Returns a list containing all elements matching the given predicate +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt b/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt index e85de36f4aa..b329cc907b0 100644 --- a/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt +++ b/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt @@ -3,4 +3,5 @@ */ class Some -// INFO: internal final class Some

    Usefull comment

    \ No newline at end of file +//INFO: internal final class Some

    Usefull comment +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnFunctionDeclarationWithPackage.kt b/idea/testData/editor/quickDoc/OnFunctionDeclarationWithPackage.kt index 7f1a2cd4f31..758db8e473b 100644 --- a/idea/testData/editor/quickDoc/OnFunctionDeclarationWithPackage.kt +++ b/idea/testData/editor/quickDoc/OnFunctionDeclarationWithPackage.kt @@ -12,4 +12,6 @@ package test */ fun testFun(first: String, second: Int) = 12 -// INFO: internal fun testFun(first: String, second: Int): Int

    Test function


    Parameters:
    first - Some
    second - Other

    \ No newline at end of file +//INFO: internal fun testFun(first: String, second: Int): Int

    Test function +//INFO:

    Parameters:
    first - Some
    second - Other
    +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnInheritedMethodUsage.kt b/idea/testData/editor/quickDoc/OnInheritedMethodUsage.kt index be307e58bf7..337d5fa80c9 100644 --- a/idea/testData/editor/quickDoc/OnInheritedMethodUsage.kt +++ b/idea/testData/editor/quickDoc/OnInheritedMethodUsage.kt @@ -14,4 +14,5 @@ fun test() { D().foo() } -// INFO: internal open fun foo(): Int

    This method returns zero.

    \ No newline at end of file +//INFO: internal open fun foo(): Int

    This method returns zero. +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnInheritedPropertyUsage.kt b/idea/testData/editor/quickDoc/OnInheritedPropertyUsage.kt index 66ab6538482..902c241713a 100644 --- a/idea/testData/editor/quickDoc/OnInheritedPropertyUsage.kt +++ b/idea/testData/editor/quickDoc/OnInheritedPropertyUsage.kt @@ -14,4 +14,5 @@ fun test() { D().foo } -// INFO: internal open val foo: Int

    This property returns zero.

    \ No newline at end of file +//INFO: internal open val foo: Int

    This property returns zero. +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnMethodUsage.kt b/idea/testData/editor/quickDoc/OnMethodUsage.kt index 9f1ab9a90f4..fa3c76d7675 100644 --- a/idea/testData/editor/quickDoc/OnMethodUsage.kt +++ b/idea/testData/editor/quickDoc/OnMethodUsage.kt @@ -1,5 +1,5 @@ /** - Some documentation +Some documentation * @param a Some int * @param b String @@ -12,4 +12,6 @@ fun test() { testMethod(1, "value") } -// INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation

    Parameters:
    a - Some int
    b - String

    \ No newline at end of file +//INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation +//INFO:

    Parameters:
    a - Some int
    b - String
    +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnMethodUsageWithBracketsInParam.kt b/idea/testData/editor/quickDoc/OnMethodUsageWithBracketsInParam.kt index 59df5c27d7e..acd0dde376e 100644 --- a/idea/testData/editor/quickDoc/OnMethodUsageWithBracketsInParam.kt +++ b/idea/testData/editor/quickDoc/OnMethodUsageWithBracketsInParam.kt @@ -1,5 +1,5 @@ /** - Some documentation +Some documentation * @param[a] Some int * @param[b] String @@ -12,4 +12,6 @@ fun test() { testMethod(1, "value") } -// INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation

    Parameters:
    a - Some int
    b - String

    \ No newline at end of file +//INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation +//INFO:

    Parameters:
    a - Some int
    b - String
    +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnMethodUsageWithMarkdown.kt b/idea/testData/editor/quickDoc/OnMethodUsageWithMarkdown.kt new file mode 100644 index 00000000000..5039d12cdf4 --- /dev/null +++ b/idea/testData/editor/quickDoc/OnMethodUsageWithMarkdown.kt @@ -0,0 +1,26 @@ +/** + * Some documentation. **Bold** *underline* `code` foo: bar (baz) [quux] + * + * [Kotlin](http://www.kotlinlang.org) + * + * [C] + * + * [See this class][C] + */ +fun testMethod() { + +} + +class C { +} + +fun test() { + testMethod(1, "value") +} + +//INFO: internal fun testMethod(): Unit

    Some documentation. Bold underline code foo: bar (baz) quux

    +//INFO:

    a href="http://www.kotlinlang.org">Kotlin

    +//INFO:

    C

    +//INFO:

    See this class

    +//INFO: +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndThrows.kt b/idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndThrows.kt index caedc42a566..3bb5eec0621 100644 --- a/idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndThrows.kt +++ b/idea/testData/editor/quickDoc/OnMethodUsageWithReturnAndThrows.kt @@ -14,4 +14,8 @@ fun test() { testMethod(1, "value") } -// INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation

    Parameters:
    a - Some int
    b - String
    Returns:
    Return value
    Throws:
    IllegalArgumentException - if the weather is bad

    \ No newline at end of file +//INFO: internal fun testMethod(a: Int, b: String): Unit

    Some documentation +//INFO:

    Parameters:
    a - Some int
    b - String
    +//INFO:
    Returns:
    Return value
    +//INFO:
    Throws:
    IllegalArgumentException - if the weather is bad
    +//INFO:

    diff --git a/idea/testData/editor/quickDoc/OnMethodUsageWithSee.kt b/idea/testData/editor/quickDoc/OnMethodUsageWithSee.kt new file mode 100644 index 00000000000..cb4d1a9d12b --- /dev/null +++ b/idea/testData/editor/quickDoc/OnMethodUsageWithSee.kt @@ -0,0 +1,20 @@ +/** + * @see C + * @see D + */ +fun testMethod() { + +} + +class C { +} + +class D { +} + +fun test() { + testMethod(1, "value") +} + +//INFO: internal fun testMethod(): Unit

    +//INFO:

    See Also:
    C, D

    diff --git a/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java b/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java index fde04a1cd48..a58793107d9 100644 --- a/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java +++ b/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java @@ -8,4 +8,5 @@ class Testing { } } -// INFO: internal fun foo(bar: Int): Unit

    KDoc foo

    \ No newline at end of file +//INFO: internal fun foo(bar: Int): Unit

    KDoc foo +//INFO:

    diff --git a/idea/testData/kdoc/navigate/simple.kt b/idea/testData/kdoc/navigate/simple.kt new file mode 100644 index 00000000000..3d922364e73 --- /dev/null +++ b/idea/testData/kdoc/navigate/simple.kt @@ -0,0 +1,5 @@ +class C { +} + +fun foo() { +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/AbstractJetQuickDocProviderTest.java b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/AbstractJetQuickDocProviderTest.java index 8cf596c1243..928ae15245e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/AbstractJetQuickDocProviderTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/AbstractJetQuickDocProviderTest.java @@ -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 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()); } diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/JetQuickDocProviderTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/JetQuickDocProviderTestGenerated.java index 4df205fb321..c984eb02e15 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/JetQuickDocProviderTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/JetQuickDocProviderTestGenerated.java @@ -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"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocNavigationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocNavigationTest.kt new file mode 100644 index 00000000000..123cc74b56d --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/quickDoc/QuickDocNavigationTest.kt @@ -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(false) + val target = JetQuickDocumentationProvider().getDocumentationElementForLink( + myFixture.getPsiManager(), "C", source); + Assert.assertTrue(target is JetClass) + Assert.assertEquals("C", (target as JetClass).getName()) + } +} diff --git a/update_dependencies.xml b/update_dependencies.xml index f8ce6d184c9..c3fc70056f2 100644 --- a/update_dependencies.xml +++ b/update_dependencies.xml @@ -220,6 +220,10 @@ + + +