IDEA-259784 Type hints: show implicit types of lambda parameters

GitOrigin-RevId: 586b24be8f16e61de4e02c588fa5b3546d34ff41
This commit is contained in:
Ivan Fomenko
2024-08-06 17:23:52 +03:00
committed by intellij-monorepo-bot
parent 86726e53e0
commit ec3b7d382c
6 changed files with 151 additions and 6 deletions

View File

@@ -0,0 +1,6 @@
void hintsDemo() {
List<Integer> list = Arrays.asList(1, 2, 3);
List<Integer> filteredList = list.stream()
.filter(/*<# List<Integer> #>*/el -> el >= 3)
.toList();
}

View File

@@ -1496,8 +1496,16 @@
group="TYPES_GROUP"
providerId="java.implicit.types"
bundle="messages.JavaBundle"
nameKey="java.implicit.types.inlay.provider.name"
descriptionKey="settings.inlay.java.implicit.types.description"/>
nameKey="java.implicit.types.local.inlay.provider.name"
descriptionKey="settings.inlay.java.implicit.types.local.description"/>
<codeInsight.declarativeInlayProvider language="JAVA"
implementationClass="com.intellij.codeInsight.hints.JavaLambdaParameterTypeHintsProvider"
isEnabledByDefault="true"
group="TYPES_GROUP"
providerId="java.implicit.lambdaParameter"
bundle="messages.JavaBundle"
nameKey="java.implicit.types.lambda.inlay.provider.name"
descriptionKey="settings.inlay.java.implicit.types.lambda.description"/>
<codeInsight.declarativeInlayProvider language="JAVA"
implementationClass="com.intellij.codeInsight.hints.JavaMethodChainsDeclarativeInlayProvider"
isEnabledByDefault="true"

View File

@@ -0,0 +1,33 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.hints
import com.intellij.codeInsight.hints.declarative.*
import com.intellij.codeInsight.hints.declarative.InlayHintsCollector
import com.intellij.codeInsight.hints.declarative.InlayHintsProvider
import com.intellij.openapi.editor.Editor
import com.intellij.psi.*
class JavaLambdaParameterTypeHintsProvider : InlayHintsProvider {
companion object {
const val PROVIDER_ID : String = "java.implicit.types.lambda"
}
override fun createCollector(file: PsiFile, editor: Editor): InlayHintsCollector {
return Collector()
}
private class Collector : SharedBypassCollector {
override fun collectFromElement(element: PsiElement, sink: InlayTreeSink) {
if (element !is PsiParameter) return
if (element.parent !is PsiParameterList) return
if (element.parent.parent !is PsiLambdaExpression) return
if (element.typeElement != null) return
val identifier = element.nameIdentifier ?: return
val type = element.type
if (type == PsiTypes.nullType() || type is PsiLambdaParameterType) return
sink.addPresentation(InlineInlayPosition(identifier.textRange.startOffset, false), hasBackground = true) {
JavaTypeHintsFactory.typeHint(type, this)
}
}
}
}

View File

@@ -880,7 +880,8 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() {
val available = availableIntentions
.map { (it.asModCommandAction() ?: IntentionActionDelegate.unwrap(it))::class.java }
.filter { it.name.startsWith("com.intellij.codeInsight.") &&
!(it.name.startsWith("com.intellij.codeInsight.intention.impl.") && it.name.endsWith("Action"))}
!(it.name.startsWith("com.intellij.codeInsight.intention.impl.") && it.name.endsWith("Action"))
&& !it.name.endsWith("DeclarativeHintsTogglingIntention")}
.map { it.simpleName }
assertThat(available).describedAs(availableIntentions.toString()).containsExactlyInAnyOrder(*fixes)
}

View File

@@ -0,0 +1,94 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon.inlays
import com.intellij.codeInsight.hints.JavaLambdaParameterTypeHintsProvider
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
import com.intellij.testFramework.utils.inlays.declarative.DeclarativeInlayHintsProviderTestCase
import org.intellij.lang.annotations.Language
class JavaLambdaParameterTypeHintsProviderTest : DeclarativeInlayHintsProviderTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = LightJavaCodeInsightFixtureTestCase.JAVA_17
private val functionInterface = """
public interface BiFunction<T, U> {
U apply(T t);
}
class FooClass {
<T, U> U foo(BiFunction<T, U> func, T a){
return func.apply(a);
}
}"""
fun `test parameter not shown`() {
val text = """
class Demo {
private static void pure(int x, int y) {
var items = Arrays.asList(1, 2, 3, 4, 5);
var product = items.stream().reduce(1, (a, b) -> a * b);
}
}"""
testAnnotations(text)
}
fun `test basic parameters`() {
val text = """
$functionInterface
class Demo {
private static void pure(int x, String s) {
FooClass obj = new FooClass();
obj.foo(/*<# [java.lang.String:java.fqn.class]String #>*/a -> {}, s);
obj.foo(/*<# [java.lang.Integer:java.fqn.class]Integer #>*/a -> {}, x);
}
}"""
testAnnotations(text)
}
fun `test integer list parameter`() {
val listClass = """
class ListClass {
List<Integer> foo(BiFunction<List<Integer>, List<Integer>> func, List<Integer> a){
return func.apply(a);
}
}"""
val text = """
$functionInterface
$listClass
class Demo {
private static void pure(List<Integer> list) {
ListClass obj = new ListClass();
obj.foo(/*<# List|<|[java.lang.Integer:java.fqn.class]Integer|> #>*/a -> {}, list);
}
}"""
testAnnotations(text)
}
fun `test generic class parameter`() {
val genericClass = """
class GenericLongClass<T, U> {
GenericLongClass(T first, U second) {}
}"""
val text = """
$functionInterface
$genericClass
class Demo {
private static void pure(GenericLongClass<String, Integer> firstPerson, GenericLongClass<Integer, GenericLongClass<String,String>> secondPerson) {
FooClass obj = new FooClass();
obj.foo(/*<# [GenericLongClass:java.fqn.class]GenericLongClass|<|[java.lang.String:java.fqn.class]String|, |[java.lang.Integer:java.fqn.class]Integer|> #>*/a -> {}, firstPerson);
obj.foo(/*<# [GenericLongClass:java.fqn.class]GenericLongClass|<|[java.lang.Integer:java.fqn.class]Integer|, |[GenericLongClass:java.fqn.class]GenericLongClass|<...>|> #>*/a -> {}, secondPerson);
}
}"""
testAnnotations(text)
}
private fun testAnnotations(@Language("Java") text: String) {
doTestProvider("A.java", text, JavaLambdaParameterTypeHintsProvider(), )
}
}

View File

@@ -1258,8 +1258,10 @@ settings.inlay.java.external.annotations=External annotations
settings.inlay.java.inferred.annotations=Inferred annotations
settings.inlay.java.inheritors=Inheritors
settings.inlay.java.insert.annotation=Insert annotation
settings.inlay.java.implicit.types=Implicit types
settings.inlay.java.implicit.types.description=Local variables declared with the var keyword when the inferred type may not be clear from the right part of the assignment, for example, when using a factory method.
settings.inlay.java.implicit.types.local=Implicit types
settings.inlay.java.implicit.types.local.description=Local variables declared with the var keyword when the inferred type may not be clear from the right part of the assignment, for example, when using a factory method.
settings.inlay.java.implicit.types.lambda=Lambda types
settings.inlay.java.implicit.types.lambda.description=Show inlay hints for lambda parameter types.
settings.inlay.java.methods.with.same.named.numbered.parameters=Methods with same-named numbered parameters
settings.inlay.java.new.expressions='New' expressions
settings.inlay.java.non.literals.in.case.of.multiple.parameters.with.the.same.type=Non-literals in case of multiple parameters with the same type
@@ -1868,7 +1870,8 @@ adds.library.preview=Adds {0, choice, 1#library ''''{1}''''|2#one of {2}} to the
adds.library.preview.no.import=Adds {0, choice, 1#library ''''{1}''''|2#one of {2}} to the dependencies of module ''{3}''
notification.content.added.annotations=Added {0} {0, choice, 1#annotation|2#annotations}
java.method.chains.inlay.provider.name=Method chains
java.implicit.types.inlay.provider.name=Implicit types
java.implicit.types.local.inlay.provider.name=Implicit types
java.implicit.types.lambda.inlay.provider.name=Lambda parameter types
intention.make.final.fixer.stream=Make ''{0}'' effectively final using stream API
intention.make.final.fixer.if=Make ''{0}'' effectively final by moving initializer to the ''if'' statement
package.classes=Package classes: