From 855197a45f7b1dac2f90224fc85d7bbe5c0adf0e Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Fri, 30 Jun 2023 13:47:46 -0700 Subject: [PATCH] Add K2 KotlinCallHighlighterExtension This commit adds KotlinCallHighlighterExtension for K2 call highlighter. If we register an implementation of KotlinCallHighlighterExtension interface in XML, FunctionCallHighlighter will use it instead of the default call highlighter (the default call highlighter function is FunctionCallHighlighter::getTextAttributesForCall(call: KtCall) function). GitOrigin-RevId: ba69aff2c819887bd0d3b5cac761def9301d28ab --- .../KotlinCallHighlighterExtension.kt | 33 ++++++++++++ .../highlighters/FunctionCallHighlighter.kt | 30 ++++++----- ...K2HighlightingMetaInfoWithExtensionTest.kt | 48 +++++++++++++++++ ...ingMetaInfoWithExtensionTestGenerated.java | 30 +++++++++++ .../Functions.kt | 52 +++++++++++++++++++ .../Functions.kt.highlighting | 52 +++++++++++++++++++ .../k2/resources/META-INF/highlighting-k2.xml | 7 +++ .../GenerateK2HighlighterTests.kt | 5 ++ 8 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/KotlinCallHighlighterExtension.kt create mode 100644 plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/AbstractK2HighlightingMetaInfoWithExtensionTest.kt create mode 100644 plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightingMetaInfoWithExtensionTestGenerated.java create mode 100644 plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt create mode 100644 plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt.highlighting diff --git a/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/KotlinCallHighlighterExtension.kt b/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/KotlinCallHighlighterExtension.kt new file mode 100644 index 000000000000..743631097780 --- /dev/null +++ b/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/KotlinCallHighlighterExtension.kt @@ -0,0 +1,33 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.highlighting + +import com.intellij.codeInsight.daemon.impl.HighlightInfoType +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.calls.KtCall +import org.jetbrains.kotlin.idea.highlighting.highlighters.FunctionCallHighlighter + +/** + * An interface for an extension point that enables a user-defined call highlighter. + * + * To add a new custom call highlighter, you have to add + * + * + * + * in XML. The "implementation" must be a class that implements this interface. See highlighting-k2.xml. + */ +interface KotlinCallHighlighterExtension { + /** + * A function determines how to highlight [call] for PSI [elementToHighlight]. + * + * Note that this function is used by [FunctionCallHighlighter]. When this function returns a non-null [HighlightInfoType], + * [FunctionCallHighlighter] uses it instead of the default one to highlight [call]. + */ + context(KtAnalysisSession) + fun highlightCall(elementToHighlight: PsiElement, call: KtCall): HighlightInfoType? + + companion object { + val EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.callHighlighterExtension") + } +} \ No newline at end of file diff --git a/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/highlighters/FunctionCallHighlighter.kt b/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/highlighters/FunctionCallHighlighter.kt index 21ee5222c9a5..4d9872d522ca 100644 --- a/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/highlighters/FunctionCallHighlighter.kt +++ b/plugins/kotlin/highlighting/src/org/jetbrains/kotlin/idea/highlighting/highlighters/FunctionCallHighlighter.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.idea.base.highlighting.HighlightingFactory import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightInfoTypeSemanticNames +import org.jetbrains.kotlin.idea.highlighting.KotlinCallHighlighterExtension import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.Name @@ -38,8 +39,8 @@ internal class FunctionCallHighlighter( if (operationReference.isAssignment()) return emptyList() val call = expression.resolveCall()?.successfulCallOrNull() ?: return emptyList() if (call is KtSimpleFunctionCall && (call.symbol as? KtFunctionSymbol)?.isOperator == true) return emptyList() - val h = getTextAttributesForCall(call)?.let { attributes -> - HighlightingFactory.highlightName(operationReference, attributes) + val h = getDefaultHighlightInfoTypeForCall(call)?.let { highlightInfoType -> + HighlightingFactory.highlightName(operationReference, highlightInfoType) } return listOfNotNull(h) } @@ -49,20 +50,21 @@ internal class FunctionCallHighlighter( context(KtAnalysisSession) private fun highlightCallExpression(expression: KtCallExpression): List { - return listOfNotNull(expression.calleeExpression - ?.takeUnless { it is KtLambdaExpression } - ?.takeUnless { it is KtCallExpression /* KT-16159 */ } - ?.let { callee -> - expression.resolveCall()?.singleCallOrNull()?.let { call -> - getTextAttributesForCall(call)?.let { attributes -> - HighlightingFactory.highlightName (callee, attributes) - } - } - }) + val callee = expression.calleeExpression ?: return emptyList() + val call = expression.resolveCall()?.singleCallOrNull() ?: return emptyList() + if (callee is KtLambdaExpression || callee is KtCallExpression /* KT-16159 */) return emptyList() + val highlightInfoType = getHighlightInfoTypeForCallFromExtension(callee, call) + ?: getDefaultHighlightInfoTypeForCall(call) + ?: return emptyList() + return listOfNotNull(HighlightingFactory.highlightName(callee, highlightInfoType)) } context(KtAnalysisSession) - private fun getTextAttributesForCall(call: KtCall): HighlightInfoType? { + private fun getHighlightInfoTypeForCallFromExtension(callee: KtExpression, call: KtCall): HighlightInfoType? = + KotlinCallHighlighterExtension.EP_NAME.getExtensions(project).firstNotNullOfOrNull { it.highlightCall(callee, call) } + + context(KtAnalysisSession) + private fun getDefaultHighlightInfoTypeForCall(call: KtCall): HighlightInfoType? { if (call !is KtSimpleFunctionCall) return null return when (val function = call.symbol) { is KtConstructorSymbol -> KotlinHighlightInfoTypeSemanticNames.CONSTRUCTOR_CALL @@ -89,4 +91,4 @@ internal class FunctionCallHighlighter( private val KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME_CALLABLE_ID = CallableId(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, Name.identifier("suspend")) } -} \ No newline at end of file +} diff --git a/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/AbstractK2HighlightingMetaInfoWithExtensionTest.kt b/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/AbstractK2HighlightingMetaInfoWithExtensionTest.kt new file mode 100644 index 000000000000..193d468f58d4 --- /dev/null +++ b/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/AbstractK2HighlightingMetaInfoWithExtensionTest.kt @@ -0,0 +1,48 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.k2.highlighting + +import com.intellij.codeInsight.daemon.impl.HighlightInfoType +import com.intellij.openapi.extensions.ExtensionPoint +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.annotations.annotations +import org.jetbrains.kotlin.analysis.api.calls.KtCall +import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall +import org.jetbrains.kotlin.analysis.api.calls.symbol +import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightInfoTypeSemanticNames +import org.jetbrains.kotlin.idea.highlighting.KotlinCallHighlighterExtension + +object KotlinCallHighlighterExtensionForTest : KotlinCallHighlighterExtension { + private enum class HighlightType(val annotationName: String) { + SUSPEND("MySuspend"), + DYNAMIC("MyDynamic"), + EXTENSION("MyExtension"), + } + + context(KtAnalysisSession) + override fun highlightCall(elementToHighlight: PsiElement, call: KtCall): HighlightInfoType? { + if (call !is KtCallableMemberCall<*, *>) return null + val highlightType = call.partiallyAppliedSymbol.symbol.annotations.firstNotNullOfOrNull { annotation -> + HighlightType.values().singleOrNull { it.annotationName == annotation.classId?.shortClassName?.asString() } + } ?: return null + return when (highlightType) { + HighlightType.SUSPEND -> KotlinHighlightInfoTypeSemanticNames.SUSPEND_FUNCTION_CALL + HighlightType.DYNAMIC -> KotlinHighlightInfoTypeSemanticNames.DYNAMIC_FUNCTION_CALL + HighlightType.EXTENSION -> KotlinHighlightInfoTypeSemanticNames.EXTENSION_FUNCTION_CALL + } + } +} + +abstract class AbstractK2HighlightingMetaInfoWithExtensionTest : AbstractK2HighlightingMetaInfoTest() { + override fun setUp() { + super.setUp() + project.extensionArea.registerExtensionPoint( + KotlinCallHighlighterExtension.EP_NAME.name, + KotlinCallHighlighterExtension::class.java.name, + ExtensionPoint.Kind.INTERFACE, + true + ) + project.extensionArea.getExtensionPoint(KotlinCallHighlighterExtension.EP_NAME) + .registerExtension(KotlinCallHighlighterExtensionForTest, testRootDisposable) + } +} \ No newline at end of file diff --git a/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightingMetaInfoWithExtensionTestGenerated.java b/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightingMetaInfoWithExtensionTestGenerated.java new file mode 100644 index 000000000000..5a3b88a8f7ee --- /dev/null +++ b/plugins/kotlin/highlighting/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightingMetaInfoWithExtensionTestGenerated.java @@ -0,0 +1,30 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. + +package org.jetbrains.kotlin.idea.k2.highlighting; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.idea.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.jetbrains.kotlin.idea.base.test.TestRoot; +import org.junit.runner.RunWith; + +/** + * This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}. + * DO NOT MODIFY MANUALLY. + */ +@SuppressWarnings("all") +@TestRoot("highlighting") +@TestDataPath("$CONTENT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +@TestMetadata("../idea/tests/testData/highlighterMetaInfoWithExtension") +public class K2HighlightingMetaInfoWithExtensionTestGenerated extends AbstractK2HighlightingMetaInfoWithExtensionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("Functions.kt") + public void testFunctions() throws Exception { + runTest("../idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt"); + } +} diff --git a/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt b/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt new file mode 100644 index 000000000000..2f3cbf2648df --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt @@ -0,0 +1,52 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// CHECK_SYMBOL_NAMES +// HIGHLIGHTER_ATTRIBUTES_KEY +fun global() { + fun inner() { + + } + inner() +} + +fun Int.ext() { +} + +infix fun Int.fif(y: Int) { + this * y +} + +open class Container { + open fun member() { + global() + 5.ext() + member() + 5 fif 6 + } +} + +fun foo() { + suspend { + + } +} + +annotation class MySuspend +annotation class MyDynamic +annotation class MyExtension + +@MySuspend +fun suspendFunction() { +} +@MyDynamic +fun dynamicFunction() { +} +@MyExtension +fun extensionFunction() { +} + +fun testFunctionsWithSpecialAnnotations() { + suspendFunction() + dynamicFunction() + extensionFunction() +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt.highlighting b/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt.highlighting new file mode 100644 index 000000000000..21ce24c7f2ff --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/highlighterMetaInfoWithExtension/Functions.kt.highlighting @@ -0,0 +1,52 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// CHECK_SYMBOL_NAMES +// HIGHLIGHTER_ATTRIBUTES_KEY +fun global() { + fun inner() { + + } + inner() +} + +fun Int.ext() { +} + +infix fun Int.fif(y: Int) { + this * y +} + +open class Container { + open fun member() { + global() + 5.ext() + member() + 5 fif 6 + } +} + +fun foo() { + suspend { + + } +} + +annotation class MySuspend +annotation class MyDynamic +annotation class MyExtension + +@MySuspend +fun suspendFunction() { +} +@MyDynamic +fun dynamicFunction() { +} +@MyExtension +fun extensionFunction() { +} + +fun testFunctionsWithSpecialAnnotations() { + suspendFunction() + dynamicFunction() + extensionFunction() +} \ No newline at end of file diff --git a/plugins/kotlin/plugin/k2/resources/META-INF/highlighting-k2.xml b/plugins/kotlin/plugin/k2/resources/META-INF/highlighting-k2.xml index 4a5585e42e40..9a1d4998c94b 100644 --- a/plugins/kotlin/plugin/k2/resources/META-INF/highlighting-k2.xml +++ b/plugins/kotlin/plugin/k2/resources/META-INF/highlighting-k2.xml @@ -1,4 +1,11 @@ + + + + diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt index c95814e62249..5d68ce8f1449 100644 --- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt +++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.fir.testGenerator import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2BundledCompilerPluginsHighlightingMetaInfoTest import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2HighlightingMetaInfoTest +import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2HighlightingMetaInfoWithExtensionTest import org.jetbrains.kotlin.idea.k2.highlighting.AbstractOutsiderHighlightingTest import org.jetbrains.kotlin.testGenerator.model.* @@ -15,6 +16,10 @@ internal fun MutableTWorkspace.generateK2HighlighterTests() { testClass { model("highlighterMetaInfoWithBundledCompilerPlugins") } + + testClass { + model("highlighterMetaInfoWithExtension") + } } testGroup("highlighting") {