KT-32163: Open Quick Documentation when cursor inside function / constructor brackets (#2502)

* Show function docs when cursor is in args of the function call

* Show constructor docs when cursor is in args of the constructor call

* Show function docs when cursor is in args of the kt function call

* Add some tests

* Use JavaDocumentationProvider to fetch java method document

* Support caret in params

#KT-32163 fixed
This commit is contained in:
Yuku Kotani
2020-04-28 00:15:24 +09:00
committed by GitHub
parent 6b5b23f37b
commit de28a41caa
8 changed files with 107 additions and 1 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.idea
import com.google.common.html.HtmlEscapers
import com.intellij.codeInsight.documentation.DocumentationManager
import com.intellij.codeInsight.documentation.DocumentationManagerUtil
import com.intellij.codeInsight.javadoc.JavaDocInfoGeneratorFactory
import com.intellij.lang.documentation.AbstractDocumentationProvider
@@ -13,7 +14,12 @@ 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.psi.*
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.compiled.ClsMethodImpl
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.descriptors.*
@@ -156,6 +162,7 @@ open class KotlinDocumentationProviderCompatBase : AbstractDocumentationProvider
companion object {
private val LOG = Logger.getInstance(KotlinDocumentationProvider::class.java)
private val javaDocumentProvider = JavaDocumentationProvider()
private val DESCRIPTOR_RENDERER = DescriptorRenderer.HTML.withOptions {
classifierNamePolicy = HtmlClassifierNamePolicy(ClassifierNamePolicy.SHORT)
@@ -270,6 +277,17 @@ open class KotlinDocumentationProviderCompatBase : AbstractDocumentationProvider
} else if (element is KtLightDeclaration<*, *>) {
val origin = element.kotlinOrigin ?: return null
return renderKotlinDeclaration(origin, quickNavigation)
} else if (element is KtValueArgumentList) {
val referenceExpression = element.prevSibling as? KtSimpleNameExpression ?: return null
val calledElement = referenceExpression.mainReference.resolve()
if (calledElement is KtNamedFunction || calledElement is KtConstructor<*>) { // In case of Kotlin function or constructor
return renderKotlinDeclaration(calledElement as KtExpression, quickNavigation)
} else if (calledElement is ClsMethodImpl || calledElement is PsiMethod) { // In case of java function or constructor
return javaDocumentProvider.generateDoc(calledElement, referenceExpression)
}
} else if (element is KtCallExpression) {
val calledElement = element.referenceExpression()?.mainReference?.resolve()
return calledElement?.let { getTextImpl(it, originalElement, quickNavigation) }
} else if (element.isModifier()) {
when (element.text) {
KtTokens.LATEINIT_KEYWORD.value -> return KotlinBundle.message("quick.doc.text.lateinit")
@@ -0,0 +1,7 @@
fun testing() {
SomeClassWithParen("param", 1<caret>)
}
//INFO: <div class='definition'><pre><a href="psi_element://SomeClassWithParen"><code>SomeClassWithParen</code></a><br><i>@Contract(pure = true)</i>&nbsp;
//INFO: public&nbsp;<b>SomeClassWithParen</b>(<a href="psi_element://java.lang.String"><code>String</code></a>&nbsp;str,
//INFO: int&nbsp;num)</pre></div><table class='sections'><p><tr><td valign='top' class='section'><p><i>Inferred</i> annotations:</td><td valign='top'><p><i>@org.jetbrains.annotations.Contract(pure = true)</i></td></table>
@@ -0,0 +1,11 @@
/**
* Some Java Class
*/
public class SomeClassWithParen {
String str;
int num;
public SomeClassWithParen(String str, int num) {
this.str = str;
this.num = num;
}
}
@@ -0,0 +1,10 @@
fun ktTestWithParen() {
TestWithParen.foo("SomeTest", 1<caret>)
}
//INFO: <div class='definition'><pre><a href="psi_element://TestWithParen"><code>TestWithParen</code></a><br><i>@Contract(value = &quot;_, _ -&gt; new&quot;,&nbsp;pure = true)</i>&nbsp;
//INFO: <i>@<a href="psi_element://org.jetbrains.annotations.NotNull"><code>NotNull</code></a></i>&nbsp;
//INFO: public static&nbsp;<a href="psi_element://java.lang.Object"><code>Object</code></a>[]&nbsp;<b>foo</b>(<a href="psi_element://java.lang.String"><code>String</code></a>&nbsp;str,
//INFO: int&nbsp;num)</pre></div><div class='content'>
//INFO: Java Method
//INFO: <p></div><table class='sections'><p><tr><td valign='top' class='section'><p><i>Inferred</i> annotations:</td><td valign='top'><p><i>@org.jetbrains.annotations.Contract(value = &quot;_, _ -&gt; new&quot;,&nbsp;pure = true)</i> <i>@<a href="psi_element://org.jetbrains.annotations.NotNull">org.jetbrains.annotations.NotNull</a></i></td></table>
@@ -0,0 +1,8 @@
class TestWithParen {
/**
* Java Method
*/
public static Object[] foo(String str, int num) {
return new Object[0];
}
}
+18
View File
@@ -0,0 +1,18 @@
/**
Some documentation
* @param a Some int
* @param b String
*/
fun testMethod(a: Int, b: String) {
}
fun test() {
testMethod(<caret>1, "value")
}
//INFO: <div class='definition'><pre><font color="808080"><i>OnMethodUsageInParen.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></table>
@@ -6,6 +6,9 @@
package org.jetbrains.kotlin.idea.editor.quickDoc;
import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.codeInsight.hint.ParameterInfoController;
import com.intellij.codeInsight.lookup.LookupEx;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
@@ -32,6 +35,22 @@ public abstract class AbstractQuickDocProviderTest extends KotlinLightCodeInsigh
PsiElement targetElement = documentationManager.findTargetElement(myFixture.getEditor(), myFixture.getFile());
PsiElement originalElement = DocumentationManager.getOriginalElement(targetElement);
PsiElement list = ParameterInfoController.findArgumentList(myFixture.getFile(), myFixture.getEditor().getCaretModel().getOffset(), -1);
PsiElement expressionList = null;
if (list != null) {
LookupEx lookup = LookupManager.getInstance(myFixture.getProject()).getActiveLookup();
if (lookup != null) {
expressionList = null; // take completion variants for documentation then
}
else {
expressionList = list;
}
}
if (targetElement == null && expressionList != null) {
targetElement = expressionList;
}
String info = DocumentationManager.getProviderFromElement(targetElement).generateDoc(targetElement, originalElement);
if (info != null) {
info = StringUtil.convertLineSeparators(info);
@@ -98,6 +98,11 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest
runTest("idea/testData/editor/quickDoc/IndentedCodeBlock.kt");
}
@TestMetadata("JavaClassConstructorUsedInKotlin.kt")
public void testJavaClassConstructorUsedInKotlin() throws Exception {
runTest("idea/testData/editor/quickDoc/JavaClassConstructorUsedInKotlin.kt");
}
@TestMetadata("JavaClassUsedInKotlin.kt")
public void testJavaClassUsedInKotlin() throws Exception {
runTest("idea/testData/editor/quickDoc/JavaClassUsedInKotlin.kt");
@@ -118,6 +123,11 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest
runTest("idea/testData/editor/quickDoc/JavaMethodUsedInKotlin.kt");
}
@TestMetadata("JavaMethodUsedInKotlinInParen.kt")
public void testJavaMethodUsedInKotlinInParen() throws Exception {
runTest("idea/testData/editor/quickDoc/JavaMethodUsedInKotlinInParen.kt");
}
@TestMetadata("KotlinClassUsedFromJava.java")
public void testKotlinClassUsedFromJava() throws Exception {
runTest("idea/testData/editor/quickDoc/KotlinClassUsedFromJava.java");
@@ -203,6 +213,11 @@ public class QuickDocProviderTestGenerated extends AbstractQuickDocProviderTest
runTest("idea/testData/editor/quickDoc/OnMethodUsage.kt");
}
@TestMetadata("OnMethodUsageInParen.kt")
public void testOnMethodUsageInParen() throws Exception {
runTest("idea/testData/editor/quickDoc/OnMethodUsageInParen.kt");
}
@TestMetadata("OnMethodUsageMultiline.kt")
public void testOnMethodUsageMultiline() throws Exception {
runTest("idea/testData/editor/quickDoc/OnMethodUsageMultiline.kt");