mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
IDEA-141739 Devkit: resolve languages in 'plugin.xml' defined in Kotlin classes, objects
GitOrigin-RevId: 0d9a7fb42a01e44c77cfaaf316191ea1d001dd56
This commit is contained in:
committed by
intellij-monorepo-bot
parent
31e3bdfe89
commit
3abfa769c4
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.idea.devkit.dom.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionConfidenceEP;
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.dom.Extension;
|
||||
import org.jetbrains.idea.devkit.dom.ExtensionPoint;
|
||||
import org.jetbrains.uast.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
@@ -109,7 +110,7 @@ class LanguageResolvingUtil {
|
||||
languageId = computeConstantReturnValue(language, "getID");
|
||||
}
|
||||
if (StringUtil.isEmpty(languageId)) {
|
||||
return Result.create((LanguageDefinition)null, language);
|
||||
return Result.create(null, language);
|
||||
}
|
||||
|
||||
String displayName = computeConstantReturnValue(language, "getDisplayName");
|
||||
@@ -131,14 +132,16 @@ class LanguageResolvingUtil {
|
||||
}
|
||||
|
||||
private static String computeConstantSuperCtorCallParameter(PsiClass languagePsiClass, int index) {
|
||||
if (languagePsiClass instanceof PsiAnonymousClass) {
|
||||
return getStringConstantExpression(((PsiAnonymousClass)languagePsiClass).getArgumentList(), index);
|
||||
UClass languageClass = UastContextKt.toUElement(languagePsiClass, UClass.class);
|
||||
if (languageClass == null) return null;
|
||||
if (languageClass instanceof UAnonymousClass) {
|
||||
return getStringConstantExpression(UastUtils.findContaining(languageClass.getSourcePsi(), UObjectLiteralExpression.class), index);
|
||||
}
|
||||
|
||||
PsiMethod defaultConstructor = null;
|
||||
for (PsiMethod constructor : languagePsiClass.getConstructors()) {
|
||||
if (constructor.getParameterList().isEmpty()) {
|
||||
defaultConstructor = constructor;
|
||||
UMethod defaultConstructor = null;
|
||||
for (UMethod method : languageClass.getMethods()) {
|
||||
if (method.isConstructor() && method.getUastParameters().isEmpty()) {
|
||||
defaultConstructor = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -146,41 +149,46 @@ class LanguageResolvingUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiCodeBlock body = defaultConstructor.getBody();
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiStatement[] statements = body.getStatements();
|
||||
if (statements.length < 1) {
|
||||
final UExpression body = defaultConstructor.getUastBody();
|
||||
if (!(body instanceof UBlockExpression)) {
|
||||
return null;
|
||||
}
|
||||
final List<UExpression> expressions = ((UBlockExpression)body).getExpressions();
|
||||
|
||||
// super() must be first
|
||||
PsiStatement statement = statements[0];
|
||||
if (!(statement instanceof PsiExpressionStatement)) {
|
||||
UExpression expression = ContainerUtil.getFirstItem(expressions);
|
||||
|
||||
if (!(expression instanceof UCallExpression)) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression expression = ((PsiExpressionStatement)statement).getExpression();
|
||||
if (!(expression instanceof PsiMethodCallExpression)) {
|
||||
UCallExpression methodCallExpression = (UCallExpression)expression;
|
||||
|
||||
if (!isSuperConstructorCall(methodCallExpression)) {
|
||||
return null;
|
||||
}
|
||||
PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
|
||||
PsiExpressionList expressionList = methodCallExpression.getArgumentList();
|
||||
return getStringConstantExpression(expressionList, index);
|
||||
return getStringConstantExpression(methodCallExpression, index);
|
||||
}
|
||||
|
||||
private static boolean isSuperConstructorCall(@Nullable UCallExpression callExpression) {
|
||||
if (callExpression == null) return false;
|
||||
UastCallKind kind = callExpression.getKind();
|
||||
String name = callExpression.getMethodName();
|
||||
|
||||
// TODO: Simplify once IDEA-229756 fixed
|
||||
return kind == UastCallKind.CONSTRUCTOR_CALL && "<init>".equals(name) // Kotlin way
|
||||
|| kind == UastCallKind.METHOD_CALL && "super".equals(name); // Java way
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getStringConstantExpression(@Nullable PsiExpressionList expressionList, int index) {
|
||||
if (expressionList == null) {
|
||||
private static String getStringConstantExpression(@Nullable UCallExpression callExpression, int index) {
|
||||
if (callExpression == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiExpression[] argumentExpressions = expressionList.getExpressions();
|
||||
if (argumentExpressions.length < index + 1) {
|
||||
UExpression argument = callExpression.getArgumentForParameter(index);
|
||||
if (argument == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getStringConstantExpression(argumentExpressions[index]);
|
||||
return UastUtils.evaluateString(argument);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
public class MyLanguage extends com.intellij.lang.Language {
|
||||
|
||||
public static final String PREFIX = "My"
|
||||
public static final String ID = "LanguageID"
|
||||
public static final String ANONYMOUS_ID = "AnonymousLanguageID"
|
||||
|
||||
public static final com.intellij.lang.Language ANONYMOUS_LANGUAGE =
|
||||
new MySubLanguage("MyAnonymousLanguageID", "MyDisplayName") {};
|
||||
new MySubLanguage(PREFIX + ANONYMOUS_ID, "MyDisplayName") {};
|
||||
|
||||
public MyLanguage() {
|
||||
super("MyLanguageID");
|
||||
super(PREFIX + ID);
|
||||
}
|
||||
|
||||
private static class MySubLanguage extends com.intellij.lang.Language {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
class MyLanguage : com.intellij.lang.Language {
|
||||
companion object {
|
||||
const val PREFIX = "My"
|
||||
const val ID = "LanguageID"
|
||||
const val ANONYMOUS_ID = "AnonymousLanguageID"
|
||||
|
||||
val ANONYMOUS_LANGUAGE: com.intellij.lang.Language = object : MySubLanguage(PREFIX + ANONYMOUS_ID, "MyDisplayName") {}
|
||||
}
|
||||
|
||||
@Suppress("ConvertSecondaryConstructorToPrimary")
|
||||
constructor() : super(PREFIX + ID)
|
||||
|
||||
private open class MySubLanguage(id: String?, private val myName: String) : com.intellij.lang.Language(id!!) {
|
||||
override fun getDisplayName(): String = myName
|
||||
}
|
||||
|
||||
abstract class AbstractLanguage protected constructor() : com.intellij.lang.Language("AbstractLanguageIDMustNotBeVisible")
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class MyLanguageAttributeEPBean {
|
||||
@com.intellij.util.xmlb.annotations.Attribute("language")
|
||||
lateinit var language: String
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<idea-plugin>
|
||||
<id>com.intellij.myPlugin</id>
|
||||
<vendor>JetBrains</vendor>
|
||||
<version>1.0</version>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint name="myLanguageEP" beanClass="MyLanguageAttributeEPBean"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij.myPlugin">
|
||||
|
||||
<myLanguageEP language="My<caret>LanguageID"/>
|
||||
<myLanguageEP language="MyAnonymousLanguageID"/>
|
||||
|
||||
<myLanguageEP language="<error descr="Cannot resolve language with id ''INVALID_VALUE''">INVALID_VALUE</error>"/>
|
||||
<myLanguageEP language="<error descr="Cannot resolve language with id ''AbstractLanguageIDMustNotBeVisible''">AbstractLanguageIDMustNotBeVisible</error>"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.idea.devkit.kotlin.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionContributorEP
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.xmlb.annotations.XCollection
|
||||
import groovy.transform.CompileStatic
|
||||
import org.jetbrains.idea.devkit.inspections.PluginXmlDomInspection
|
||||
import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil
|
||||
|
||||
@TestDataPath("\$CONTENT_ROOT/testData/codeInsight")
|
||||
@CompileStatic
|
||||
class KtPluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(PluginXmlDomInspection.class)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return DevkitKtTestsUtil.TESTDATA_PATH + "codeInsight"
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception {
|
||||
String pathForClass = PathUtil.getJarPathForClass(XCollection.class)
|
||||
moduleBuilder.addLibrary("util", pathForClass)
|
||||
String langApiJar = PathUtil.getJarPathForClass(CompletionContributorEP.class)
|
||||
moduleBuilder.addLibrary("lang-api", langApiJar)
|
||||
}
|
||||
|
||||
private void doHighlightingTest(String... filePaths) {
|
||||
myFixture.testHighlighting(true, false, false, filePaths)
|
||||
}
|
||||
|
||||
void testLanguageAttributeHighlighting() {
|
||||
configureLanguageAttributeTest()
|
||||
doHighlightingTest("languageAttribute.xml", "MyLanguageAttributeEPBean.kt")
|
||||
}
|
||||
|
||||
void testLanguageAttributeCompletion() {
|
||||
configureLanguageAttributeTest()
|
||||
myFixture.allowTreeAccessForFile(myFixture.copyFileToProject("MyLanguageAttributeEPBean.kt"))
|
||||
myFixture.configureByFile("languageAttribute.xml")
|
||||
|
||||
|
||||
def lookupElements = myFixture.complete(CompletionType.BASIC).sort { it.lookupString }
|
||||
assertEquals(2, lookupElements.length)
|
||||
assertLookupElement(lookupElements[0], "MyAnonymousLanguageID", "MyLanguage.MySubLanguage")
|
||||
assertLookupElement(lookupElements[1], "MyLanguageID", "MyLanguage")
|
||||
}
|
||||
|
||||
private static void assertLookupElement(LookupElement element, String lookupString, String typeText) {
|
||||
def presentation = new LookupElementPresentation()
|
||||
element.renderElement(presentation)
|
||||
assertEquals(lookupString, presentation.itemText)
|
||||
assertEquals(typeText, presentation.typeText)
|
||||
}
|
||||
|
||||
private void configureLanguageAttributeTest() {
|
||||
myFixture.addClass("package com.intellij.lang; " +
|
||||
"public class Language { " +
|
||||
" protected Language(String id) {}" +
|
||||
"}")
|
||||
myFixture.allowTreeAccessForFile(myFixture.copyFileToProject("MyLanguage.kt"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user