mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-269636 Devkit: improve <override-text> 'place' code insight
GitOrigin-RevId: 376870875d7bfc74076fe69249b9c8f96dc47514
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0790ff26fc
commit
801e239dbf
@@ -1,59 +1,105 @@
|
||||
// Copyright 2000-2021 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;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.codeInspection.dataFlow.StringExpressionHelper;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.AllClassesSearch;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.xml.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public interface OverrideText extends DomElement {
|
||||
|
||||
@NotNull
|
||||
@Required
|
||||
@Convert(PlaceConverter.class)
|
||||
GenericAttributeValue<String> getPlace();
|
||||
@Convert(value = PlaceConverter.class, soft = true)
|
||||
GenericAttributeValue<PsiField> getPlace();
|
||||
|
||||
@NotNull
|
||||
@Required(false)
|
||||
@Convert(PlaceConverter.class)
|
||||
GenericAttributeValue<String> getUseTextOfPlace();
|
||||
@Convert(value = PlaceConverter.class, soft = true)
|
||||
GenericAttributeValue<PsiField> getUseTextOfPlace();
|
||||
|
||||
@NotNull
|
||||
@Required(false)
|
||||
GenericAttributeValue<String> getText();
|
||||
|
||||
class PlaceConverter extends ResolvingConverter.StringConverter {
|
||||
|
||||
class PlaceConverter extends ResolvingConverter<PsiField> {
|
||||
|
||||
@NonNls private static final String PLACES_CLASSNAME_SUFFIX = "Places";
|
||||
|
||||
@Override
|
||||
public @Nullable PsiField fromString(@Nullable String s,
|
||||
ConvertContext context) {
|
||||
if (s == null) return null;
|
||||
CommonProcessors.FindProcessor<PsiField> resolve = new CommonProcessors.FindProcessor<>() {
|
||||
@Override
|
||||
protected boolean accept(PsiField field) {
|
||||
return s.equals(getPlaceName(field));
|
||||
}
|
||||
};
|
||||
|
||||
processAllActionPlaces(context, resolve);
|
||||
return resolve.getFoundValue();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @Nullable String toString(@Nullable PsiField field,
|
||||
ConvertContext context) {
|
||||
return field != null ? field.getName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable
|
||||
LookupElement createLookupElement(PsiField field) {
|
||||
return LookupElementBuilder.create(field, Objects.requireNonNull(getPlaceName(field)))
|
||||
.withStrikeoutness(field.isDeprecated())
|
||||
.withTailText(" (" + field.getName() + ")")
|
||||
.withTypeText(Objects.requireNonNull(field.getContainingClass()).getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<String> getVariants(ConvertContext context) {
|
||||
return ContainerUtil.concat(getPlaceNames(context, "com.intellij.openapi.actionSystem.ActionPlaces"),
|
||||
getPlaceNames(context, "com.intellij.vcs.log.ui.VcsLogActionPlaces"));
|
||||
public Collection<PsiField> getVariants(ConvertContext context) {
|
||||
CommonProcessors.CollectProcessor<PsiField> collectProcessor = new CommonProcessors.CollectProcessor<>();
|
||||
processAllActionPlaces(context, collectProcessor);
|
||||
return collectProcessor.getResults();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> getPlaceNames(ConvertContext context, String className) {
|
||||
final PsiClass actionPlacesClass = DomJavaUtil.findClass(className, context.getInvocationElement());
|
||||
if (actionPlacesClass == null) return Collections.emptyList();
|
||||
private static void processAllActionPlaces(ConvertContext context, Processor<PsiField> fieldProcessor) {
|
||||
final GlobalSearchScope scope = context.getSearchScope();
|
||||
if (scope == null) return;
|
||||
|
||||
return ContainerUtil.mapNotNull(actionPlacesClass.getFields(), field -> {
|
||||
if (!field.hasModifierProperty(PsiModifier.PUBLIC)) return null;
|
||||
AllClassesSearch.search(context.getSearchScope(), context.getProject(), s -> s.endsWith(PLACES_CLASSNAME_SUFFIX))
|
||||
.forEach(psiClass -> {
|
||||
return ContainerUtil.process(psiClass.getFields(), psiField -> {
|
||||
if (!psiField.hasModifierProperty(PsiModifier.PUBLIC)) return true;
|
||||
if (getPlaceName(psiField) == null) return true;
|
||||
|
||||
final PsiExpression initializer = field.getInitializer();
|
||||
if (initializer != null) {
|
||||
return Pair.getSecond(StringExpressionHelper.evaluateExpression(initializer));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return fieldProcessor.process(psiField);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getPlaceName(PsiField field) {
|
||||
final PsiExpression initializer = field.getInitializer();
|
||||
return initializer != null ? Pair.getSecond(StringExpressionHelper.evaluateExpression(initializer)) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<idea-plugin>
|
||||
<actions>
|
||||
<action>
|
||||
<override-text place="<caret>"/>
|
||||
</action>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<idea-plugin>
|
||||
<actions>
|
||||
<action>
|
||||
<override-text place="Favorites<caret>Popup"/>
|
||||
</action>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
+42
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2000-2021 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.codeInsight
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.psi.ElementDescriptionUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder
|
||||
@@ -406,16 +408,9 @@ class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
|
||||
myFixture.configureByFile("languageAttribute.xml")
|
||||
|
||||
|
||||
def lookupElements = myFixture.complete(CompletionType.BASIC).sort { it.lookupString }
|
||||
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)
|
||||
def lookupElements = myFixture.complete(CompletionType.BASIC)
|
||||
assertLookupElement(lookupElements, "MyAnonymousLanguageID", null, "MyLanguage.MySubLanguage")
|
||||
assertLookupElement(lookupElements, "MyLanguageID", null, "MyLanguage")
|
||||
}
|
||||
|
||||
@SuppressWarnings("ComponentNotRegistered")
|
||||
@@ -431,7 +426,7 @@ class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
|
||||
void testIconAttributeCompletion() {
|
||||
addIconClasses()
|
||||
myFixture.configureByFile("iconAttributeCompletion.xml")
|
||||
Registry.get("ide.completion.variant.limit").setValue("5000", getTestRootDisposable());
|
||||
Registry.get("ide.completion.variant.limit").setValue("5000", getTestRootDisposable())
|
||||
myFixture.completeBasic()
|
||||
|
||||
List<String> lookupElementStrings = myFixture.getLookupElementStrings()
|
||||
@@ -664,17 +659,27 @@ public class MyErrorHandler extends ErrorReportSubmitter {}
|
||||
|
||||
myFixture.completeBasic()
|
||||
LookupElement[] lookupElements = myFixture.getLookupElements()
|
||||
assertActionLookupElement(lookupElements[0], "actionId", " \"ActionId Text\"", "ActionId description")
|
||||
assertActionLookupElement(lookupElements[1], "actionId.localized", " \"Action Localized Text\"", "Action localized description")
|
||||
assertActionLookupElement(lookupElements[2], "actionId.missing.localized", null, null)
|
||||
assertLookupElement(lookupElements, "actionId", " \"ActionId Text\"", "ActionId description")
|
||||
assertLookupElement(lookupElements, "actionId.localized", " \"Action Localized Text\"", "Action localized description")
|
||||
assertLookupElement(lookupElements, "actionId.missing.localized", null, null)
|
||||
}
|
||||
|
||||
private static void assertActionLookupElement(LookupElement element, String lookupText, String tailText, String typeText) {
|
||||
def presentation = new LookupElementPresentation()
|
||||
element.renderElement(presentation)
|
||||
assertEquals(lookupText, presentation.itemText)
|
||||
assertEquals(tailText, presentation.tailText)
|
||||
assertEquals(typeText, presentation.typeText)
|
||||
void testActionOverrideTextPlaceCompletion() {
|
||||
configureByFile()
|
||||
myFixture.addClass("public interface CustomPlaces { String CUSTOM=\"custom\"; }")
|
||||
|
||||
myFixture.completeBasic()
|
||||
LookupElement[] lookupElements = myFixture.getLookupElements()
|
||||
assertLookupElement(lookupElements, "FavoritesPopup", " (FAVORITES_VIEW_POPUP)", "ActionPlaces")
|
||||
assertLookupElement(lookupElements, "custom", " (CUSTOM)", "CustomPlaces")
|
||||
}
|
||||
|
||||
void testActionOverrideTextPlaceResolve() {
|
||||
configureByFile()
|
||||
|
||||
PsiReference reference = myFixture.getReferenceAtCaretPositionWithAssertion()
|
||||
PsiField resolvedField = assertInstanceOf(reference.resolve(), PsiField.class)
|
||||
assertEquals("FAVORITES_VIEW_POPUP", resolvedField.getName())
|
||||
}
|
||||
|
||||
void testExtensionPointNameValidity() {
|
||||
@@ -777,10 +782,6 @@ public class MyErrorHandler extends ErrorReportSubmitter {}
|
||||
doHighlightingTest("pluginWithSinceBuildGreaterThanUntilBuild.xml")
|
||||
}
|
||||
|
||||
private void doHighlightingTest(String... filePaths) {
|
||||
myFixture.testHighlighting(true, false, false, filePaths)
|
||||
}
|
||||
|
||||
void testProductDescriptor() {
|
||||
doHighlightingTest("productDescriptor.xml")
|
||||
}
|
||||
@@ -813,4 +814,20 @@ public class MyErrorHandler extends ErrorReportSubmitter {}
|
||||
void testRedundantServiceInterfaceClass() {
|
||||
doHighlightingTest("redundantServiceInterfaceClass.xml")
|
||||
}
|
||||
|
||||
private void doHighlightingTest(String... filePaths) {
|
||||
myFixture.testHighlighting(true, false, false, filePaths)
|
||||
}
|
||||
|
||||
private static void assertLookupElement(LookupElement[] variants, String lookupText, String tailText, String typeText) {
|
||||
LookupElement lookupElement = variants.find { it.lookupString == lookupText }
|
||||
assertNotNull(toString(variants, "\n"), lookupElement)
|
||||
|
||||
def presentation = new LookupElementPresentation()
|
||||
lookupElement.renderElement(presentation)
|
||||
|
||||
assertEquals(lookupText, presentation.itemText)
|
||||
assertEquals(tailText, presentation.tailText)
|
||||
assertEquals(typeText, presentation.typeText)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user