mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[devkit] Resolve action IDs for Activate$ToolwindowID$ToolWindow (IJPL-165055)
GitOrigin-RevId: 44ad796d26ce253134675264e169af55bbc6dc1b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
aa4640209b
commit
518f9c1879
@@ -36,6 +36,8 @@ import org.jetbrains.idea.devkit.util.PluginRelatedLocatorsUtils;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.openapi.util.NullableLazyValue.lazyNullable;
|
||||
import static org.jetbrains.idea.devkit.references.ActionOrGroupIdResolveUtil.ACTIVATE_TOOLWINDOW_ACTION_PREFIX;
|
||||
import static org.jetbrains.idea.devkit.references.ActionOrGroupIdResolveUtil.ACTIVATE_TOOLWINDOW_ACTION_SUFFIX;
|
||||
|
||||
public class ActionOrGroupResolveConverter extends ResolvingConverter<ActionOrGroup> {
|
||||
|
||||
@@ -48,13 +50,25 @@ public class ActionOrGroupResolveConverter extends ResolvingConverter<ActionOrGr
|
||||
public @NotNull Set<String> getAdditionalVariants(@NotNull ConvertContext context) {
|
||||
if (!isActionsAllowed()) return Collections.emptySet();
|
||||
|
||||
// add executor IDs here as valid results
|
||||
Set<String> executorIds = new HashSet<>();
|
||||
ActionOrGroupIdResolveUtil.processExecutors(context.getProject(), (id, psiClass) -> {
|
||||
executorIds.add(id);
|
||||
// add executor/activate toolwindow IDs here as valid results
|
||||
Project project = context.getProject();
|
||||
Set<String> additionalIds = new HashSet<>();
|
||||
ActionOrGroupIdResolveUtil.processExecutors(project, (id, psiClass) -> {
|
||||
additionalIds.add(id);
|
||||
return true;
|
||||
});
|
||||
return executorIds;
|
||||
ActionOrGroupIdResolveUtil.processActivateToolWindowActions(project, extension -> {
|
||||
additionalIds.add(ACTIVATE_TOOLWINDOW_ACTION_PREFIX +
|
||||
ActionOrGroupIdResolveUtil.getToolWindowIdValue(extension) +
|
||||
ACTIVATE_TOOLWINDOW_ACTION_SUFFIX);
|
||||
return true;
|
||||
});
|
||||
ActionOrGroupIdResolveUtil.processToolWindowId(project, (id, field) -> {
|
||||
additionalIds.add(ACTIVATE_TOOLWINDOW_ACTION_PREFIX + id + ACTIVATE_TOOLWINDOW_ACTION_SUFFIX);
|
||||
return true;
|
||||
});
|
||||
|
||||
return additionalIds;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
@@ -4,8 +4,10 @@ package org.jetbrains.idea.devkit.references;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.execution.Executor;
|
||||
import com.intellij.ide.actions.QualifiedNameProviderUtil;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -19,15 +21,18 @@ import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import com.intellij.util.xml.DomElement;
|
||||
import com.intellij.util.xml.DomTarget;
|
||||
import com.intellij.util.xml.GenericAttributeValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.DevKitBundle;
|
||||
import org.jetbrains.idea.devkit.dom.ActionOrGroup;
|
||||
import org.jetbrains.idea.devkit.dom.Extension;
|
||||
import org.jetbrains.idea.devkit.dom.OverrideText;
|
||||
import org.jetbrains.idea.devkit.dom.impl.ActionOrGroupResolveConverter;
|
||||
import org.jetbrains.idea.devkit.dom.index.IdeaPluginRegistrationIndex;
|
||||
import org.jetbrains.idea.devkit.util.DevKitDomUtil;
|
||||
import org.jetbrains.idea.devkit.util.PluginRelatedLocatorsUtils;
|
||||
import org.jetbrains.uast.UExpression;
|
||||
|
||||
@@ -35,6 +40,9 @@ import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.jetbrains.idea.devkit.references.ActionOrGroupIdResolveUtil.ACTIVATE_TOOLWINDOW_ACTION_PREFIX;
|
||||
import static org.jetbrains.idea.devkit.references.ActionOrGroupIdResolveUtil.ACTIVATE_TOOLWINDOW_ACTION_SUFFIX;
|
||||
|
||||
final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElement> implements PluginConfigReference {
|
||||
private final String myId;
|
||||
|
||||
@@ -64,10 +72,44 @@ final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElem
|
||||
}
|
||||
return true;
|
||||
};
|
||||
ActionOrGroupIdResolveUtil.processExecutors(getElement().getProject(), pairProcessor);
|
||||
ActionOrGroupIdResolveUtil.processExecutors(project, pairProcessor);
|
||||
if (!executor.isNull()) {
|
||||
return PsiElementResolveResult.createResults(executor.get());
|
||||
}
|
||||
|
||||
if (myId.startsWith(ACTIVATE_TOOLWINDOW_ACTION_PREFIX) && myId.endsWith(ACTIVATE_TOOLWINDOW_ACTION_SUFFIX)) {
|
||||
String toolwindowId = StringUtil.substringBeforeLast(
|
||||
Objects.requireNonNull(StringUtil.substringAfter(myId, ACTIVATE_TOOLWINDOW_ACTION_PREFIX)),
|
||||
ACTIVATE_TOOLWINDOW_ACTION_SUFFIX);
|
||||
|
||||
// ToolWindow EPs - process all as we need to remove spaces from ID
|
||||
Ref<Extension> toolwindowExtension = Ref.create();
|
||||
ActionOrGroupIdResolveUtil.processActivateToolWindowActions(project, extension -> {
|
||||
String idValue = ActionOrGroupIdResolveUtil.getToolWindowIdValue(extension);
|
||||
if (Comparing.strEqual(toolwindowId, idValue)) {
|
||||
toolwindowExtension.set(extension);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!toolwindowExtension.isNull()) {
|
||||
return PsiElementResolveResult.createResults(getDomTargetPsi(toolwindowExtension.get()));
|
||||
}
|
||||
|
||||
// known (programmatic) ToolWindow IDs
|
||||
Ref<PsiField> knownToolWindowIdField = Ref.create();
|
||||
ActionOrGroupIdResolveUtil.processToolWindowId(project, (s, field) -> {
|
||||
if (Comparing.strEqual(s, toolwindowId)) {
|
||||
knownToolWindowIdField.set(field);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!knownToolWindowIdField.isNull()) {
|
||||
return PsiElementResolveResult.createResults(knownToolWindowIdField.get());
|
||||
}
|
||||
return ResolveResult.EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
// action|group.ActionId.<override-text@place>.text
|
||||
@@ -92,11 +134,7 @@ final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElem
|
||||
}
|
||||
|
||||
final List<PsiElement> psiElements =
|
||||
JBIterable.from(processor.getResults())
|
||||
.map(actionOrGroup -> {
|
||||
final DomTarget target = DomTarget.getTarget(actionOrGroup);
|
||||
return target == null ? null : PomService.convertToPsi(project, target);
|
||||
}).filter(Objects::nonNull).toList();
|
||||
ContainerUtil.mapNotNull(processor.getResults(), actionOrGroup -> getDomTargetPsi(actionOrGroup));
|
||||
return PsiElementResolveResult.createResults(psiElements);
|
||||
}
|
||||
|
||||
@@ -113,24 +151,52 @@ final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElem
|
||||
converter = new ActionOrGroupResolveConverter();
|
||||
}
|
||||
|
||||
List<ActionOrGroup> domVariants =
|
||||
converter.getVariants(getElement().getProject(), ModuleUtilCore.findModuleForPsiElement(getElement()));
|
||||
Project project = getElement().getProject();
|
||||
List<ActionOrGroup> domVariants = converter.getVariants(project, ModuleUtilCore.findModuleForPsiElement(getElement()));
|
||||
List<LookupElement> domLookupElements = ContainerUtil.map(domVariants, actionOrGroup -> converter.createLookupElement(actionOrGroup));
|
||||
if (myIsAction == ThreeState.NO) {
|
||||
return domLookupElements.toArray();
|
||||
}
|
||||
|
||||
List<LookupElement> executorLookupElements = new SmartList<>();
|
||||
ActionOrGroupIdResolveUtil.processExecutors(getElement().getProject(), (id, psiClass) -> {
|
||||
ActionOrGroupIdResolveUtil.processExecutors(project, (id, psiClass) -> {
|
||||
LookupElementBuilder builder = LookupElementBuilder.create(psiClass, id)
|
||||
.bold()
|
||||
.withIcon(computeIcon(id, psiClass))
|
||||
.withTailText(computeTailText(id), true)
|
||||
.withIcon(computeExecutorIcon(id, psiClass))
|
||||
.withTailText(computeExecutorTailText(id), true)
|
||||
.withTypeText(psiClass.getQualifiedName(), true);
|
||||
executorLookupElements.add(builder);
|
||||
return true;
|
||||
});
|
||||
return ContainerUtil.concat(domLookupElements, executorLookupElements).toArray();
|
||||
|
||||
|
||||
List<LookupElement> activateToolWindowElements = new SmartList<>();
|
||||
ActionOrGroupIdResolveUtil.processActivateToolWindowActions(project, extension -> {
|
||||
GenericAttributeValue<?> factoryClass = DevKitDomUtil.getAttribute(extension, "factoryClass");
|
||||
|
||||
LookupElementBuilder builder =
|
||||
LookupElementBuilder.create(getDomTargetPsi(extension),
|
||||
ACTIVATE_TOOLWINDOW_ACTION_PREFIX +
|
||||
ActionOrGroupIdResolveUtil.getToolWindowIdValue(extension) +
|
||||
ACTIVATE_TOOLWINDOW_ACTION_SUFFIX)
|
||||
.bold()
|
||||
.withTypeText(factoryClass != null ? factoryClass.getStringValue() : "", true);
|
||||
activateToolWindowElements.add(builder);
|
||||
return true;
|
||||
});
|
||||
|
||||
ActionOrGroupIdResolveUtil.processToolWindowId(project, (value, field) -> {
|
||||
LookupElementBuilder builder =
|
||||
LookupElementBuilder.create(field,
|
||||
ACTIVATE_TOOLWINDOW_ACTION_PREFIX + value + ACTIVATE_TOOLWINDOW_ACTION_SUFFIX)
|
||||
.bold()
|
||||
.withStrikeoutness(field.isDeprecated())
|
||||
.withTypeText(QualifiedNameProviderUtil.getQualifiedName(field), true);
|
||||
activateToolWindowElements.add(builder);
|
||||
return true;
|
||||
});
|
||||
|
||||
return ContainerUtil.concat(domLookupElements, executorLookupElements, activateToolWindowElements).toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,7 +217,13 @@ final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElem
|
||||
}
|
||||
}
|
||||
|
||||
private static Icon computeIcon(String id, PsiClass executor) {
|
||||
private static PsiElement getDomTargetPsi(DomElement domElement) {
|
||||
DomTarget target = DomTarget.getTarget(domElement);
|
||||
assert target != null;
|
||||
return PomService.convertToPsi(target);
|
||||
}
|
||||
|
||||
private static Icon computeExecutorIcon(@NotNull String id, PsiClass executor) {
|
||||
final ProjectIconsAccessor iconsAccessor = ProjectIconsAccessor.getInstance(executor.getProject());
|
||||
final UExpression icon = ActionOrGroupIdResolveUtil.getReturnExpression(executor, "getIcon");
|
||||
if (icon == null) {
|
||||
@@ -162,7 +234,7 @@ final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<PsiElem
|
||||
return iconFile == null ? null : iconsAccessor.getIcon(iconFile);
|
||||
}
|
||||
|
||||
private static String computeTailText(@NotNull String id) {
|
||||
private static String computeExecutorTailText(@NotNull String id) {
|
||||
Executor executorById = findExecutor(id);
|
||||
return executorById != null ? " \"" + executorById.getActionName() + "\"" : "";
|
||||
}
|
||||
|
||||
@@ -5,16 +5,28 @@ import com.intellij.execution.Executor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScopesCore.projectProductionScope
|
||||
import com.intellij.psi.search.ProjectScope.getLibrariesScope
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.util.PairProcessor
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import org.jetbrains.idea.devkit.dom.Extension
|
||||
import org.jetbrains.idea.devkit.util.PsiUtil
|
||||
import org.jetbrains.idea.devkit.util.processExtensionCandidates
|
||||
import org.jetbrains.uast.UExpression
|
||||
import org.jetbrains.uast.UField
|
||||
import org.jetbrains.uast.evaluateString
|
||||
import org.jetbrains.uast.toUElement
|
||||
|
||||
internal object ActionOrGroupIdResolveUtil {
|
||||
|
||||
const val ACTIVATE_TOOLWINDOW_ACTION_PREFIX: @NonNls String = "Activate"
|
||||
|
||||
const val ACTIVATE_TOOLWINDOW_ACTION_SUFFIX: @NonNls String = "ToolWindow"
|
||||
|
||||
@JvmStatic
|
||||
fun processExecutors(project: Project, processor: PairProcessor<String, PsiClass>) {
|
||||
// not necessarily in the element's resolve scope
|
||||
@@ -40,6 +52,29 @@ internal object ActionOrGroupIdResolveUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun processActivateToolWindowActions(project: Project, processor: Processor<Extension>) {
|
||||
processExtensionCandidates(project, "com.intellij.toolWindow", processor, null, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getToolWindowIdValue(extension : Extension) : String? {
|
||||
return extension.id.stringValue?.replace(" ", "")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun processToolWindowId(project: Project, processor: PairProcessor<String, PsiField>) {
|
||||
val toolWindowIdClass =
|
||||
JavaPsiFacade.getInstance(project).findClass("com.intellij.openapi.wm.ToolWindowId", GlobalSearchScope.allScope(project))
|
||||
if (toolWindowIdClass == null) return
|
||||
|
||||
for (field in toolWindowIdClass.getFields()) {
|
||||
val initializer = field.toUElement<UField>(UField::class.java)?.uastInitializer ?: continue
|
||||
val value = initializer.evaluateString() ?: continue
|
||||
if (!processor.process(value, field)) return
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeConstantReturnValue(
|
||||
psiClass: PsiClass,
|
||||
methodName: String,
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.references;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.references.PomService;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReferenceBase;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.xml.DomElement;
|
||||
import com.intellij.util.xml.DomManager;
|
||||
import com.intellij.util.xml.DomTarget;
|
||||
import com.intellij.util.xml.GenericAttributeValue;
|
||||
import com.intellij.util.xml.reflect.DomAttributeChildDescription;
|
||||
@@ -20,12 +16,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.idea.devkit.dom.index.ExtensionPointIndex;
|
||||
import org.jetbrains.idea.devkit.util.ExtensionCandidate;
|
||||
import org.jetbrains.idea.devkit.util.ExtensionLocatorKt;
|
||||
import org.jetbrains.idea.devkit.util.PluginRelatedLocatorsUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
abstract class ExtensionReferenceBase extends PsiReferenceBase<PsiElement> implements PluginConfigReference {
|
||||
|
||||
@@ -57,7 +48,8 @@ abstract class ExtensionReferenceBase extends PsiReferenceBase<PsiElement> imple
|
||||
if (StringUtil.isEmptyOrSpaces(resolveId)) return null;
|
||||
|
||||
final CommonProcessors.FindProcessor<Extension> resolveProcessor = new CommonProcessors.FindFirstProcessor<>();
|
||||
processCandidates(resolveProcessor, resolveId);
|
||||
ExtensionLocatorKt.processExtensionCandidates(myElement.getProject(), getExtensionPointFqn(), resolveProcessor,
|
||||
resolveId, extension -> getNameElement(extension));
|
||||
|
||||
final Extension value = resolveProcessor.getFoundValue();
|
||||
if (value == null) return null;
|
||||
@@ -85,40 +77,7 @@ abstract class ExtensionReferenceBase extends PsiReferenceBase<PsiElement> imple
|
||||
}
|
||||
|
||||
protected void processCandidates(Processor<? super Extension> processor) {
|
||||
processCandidates(processor, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extensionPointId To locate a specific extension instance by ID or {@code null} to process all.
|
||||
*/
|
||||
private void processCandidates(Processor<? super Extension> processor,
|
||||
@Nullable String extensionPointId) {
|
||||
final Project project = myElement.getProject();
|
||||
|
||||
final ExtensionPoint extensionPointDomElement =
|
||||
ExtensionPointIndex.findExtensionPoint(project, PluginRelatedLocatorsUtils.getCandidatesScope(project), getExtensionPointFqn());
|
||||
if (extensionPointDomElement == null) return;
|
||||
|
||||
final List<ExtensionCandidate> candidates;
|
||||
if (extensionPointId == null) {
|
||||
candidates = ExtensionLocatorKt.locateExtensionsByExtensionPoint(extensionPointDomElement);
|
||||
}
|
||||
else {
|
||||
candidates = ExtensionLocatorKt.
|
||||
locateExtensionsByExtensionPointAndId(extensionPointDomElement, extensionPointId,
|
||||
extension -> {
|
||||
final GenericAttributeValue<String> nameElement = getNameElement(extension);
|
||||
return nameElement != null ? nameElement.getStringValue() : null;
|
||||
}).findCandidates();
|
||||
}
|
||||
|
||||
final DomManager manager = DomManager.getDomManager(project);
|
||||
for (ExtensionCandidate candidate : candidates) {
|
||||
final XmlTag element = candidate.pointer.getElement();
|
||||
final DomElement domElement = manager.getDomElement(element);
|
||||
if (domElement instanceof Extension) {
|
||||
if (!processor.process((Extension)domElement)) return;
|
||||
}
|
||||
}
|
||||
ExtensionLocatorKt.processExtensionCandidates(myElement.getProject(), getExtensionPointFqn(), processor,
|
||||
null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,21 +3,23 @@ package org.jetbrains.idea.devkit.util
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.platform.ide.progress.ModalTaskOwner.project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.SmartPointerManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.PsiSearchHelper
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.psi.util.ClassUtil
|
||||
import com.intellij.psi.xml.XmlTag
|
||||
import com.intellij.util.PerformanceAssertions
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.xml.DomManager
|
||||
import com.intellij.util.xml.GenericAttributeValue
|
||||
import org.jetbrains.idea.devkit.dom.Extension
|
||||
import org.jetbrains.idea.devkit.dom.ExtensionPoint
|
||||
import org.jetbrains.idea.devkit.dom.index.ExtensionPointIndex
|
||||
import java.util.*
|
||||
import java.util.function.Function
|
||||
|
||||
fun locateExtensionsByPsiClass(psiClass: PsiClass): List<ExtensionCandidate> {
|
||||
return findExtensionsByClassName(psiClass.project, ClassUtil.getJVMClassName(psiClass) ?: return emptyList())
|
||||
@@ -34,22 +36,62 @@ fun locateExtensionsByExtensionPointAndId(extensionPoint: ExtensionPoint, extens
|
||||
/**
|
||||
* @param extensionIdFunction in case EP is located via custom attribute instead of [Extension.getId].
|
||||
*/
|
||||
fun locateExtensionsByExtensionPointAndId(extensionPoint: ExtensionPoint,
|
||||
extensionId: String,
|
||||
extensionIdFunction: (Extension) -> String?): ExtensionLocator {
|
||||
fun locateExtensionsByExtensionPointAndId(
|
||||
extensionPoint: ExtensionPoint,
|
||||
extensionId: String,
|
||||
extensionIdFunction: (Extension) -> String?,
|
||||
): ExtensionLocator {
|
||||
return ExtensionByExtensionPointLocator(extensionPoint.xmlTag.project, extensionPoint, extensionId, extensionIdFunction)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extensionPointId To locate a specific extension instance by ID or `null` to process all.
|
||||
* @param nameElementFunction Returns the DOM `id` element when [extensionPointId] is passed.
|
||||
*/
|
||||
fun processExtensionCandidates(
|
||||
project: Project,
|
||||
extensionPointFqn: String,
|
||||
processor: Processor<in Extension>,
|
||||
extensionPointId: String?,
|
||||
nameElementFunction: Function<Extension, GenericAttributeValue<String>?>?,
|
||||
) {
|
||||
val extensionPointDomElement =
|
||||
ExtensionPointIndex.findExtensionPoint(project, PluginRelatedLocatorsUtils.getCandidatesScope(project), extensionPointFqn)
|
||||
if (extensionPointDomElement == null) return
|
||||
|
||||
val candidates: List<ExtensionCandidate>
|
||||
if (extensionPointId == null) {
|
||||
candidates = locateExtensionsByExtensionPoint(extensionPointDomElement)
|
||||
}
|
||||
else {
|
||||
candidates = locateExtensionsByExtensionPointAndId(extensionPointDomElement, extensionPointId) { extension: Extension ->
|
||||
val nameElement = nameElementFunction!!.apply(extension)
|
||||
nameElement?.getStringValue()
|
||||
}.findCandidates()
|
||||
}
|
||||
|
||||
val manager = DomManager.getDomManager(project)
|
||||
for (candidate in candidates) {
|
||||
val element = candidate.pointer.getElement()
|
||||
val domElement = manager.getDomElement(element)
|
||||
if (domElement is Extension) {
|
||||
if (!processor.process(domElement)) return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO consider converting to a stream-like entity to avoid IDEA-277738, EA-139648, etc.
|
||||
/**
|
||||
* A synchronized collection should be used as an accumulator in callbacks.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun processExtensionDeclarations(name: String,
|
||||
project: Project,
|
||||
strictMatch: Boolean = true,
|
||||
scope : SearchScope = PluginRelatedLocatorsUtils.getCandidatesScope(project),
|
||||
callback: (Extension, XmlTag) -> Boolean) {
|
||||
fun processExtensionDeclarations(
|
||||
name: String,
|
||||
project: Project,
|
||||
strictMatch: Boolean = true,
|
||||
scope: SearchScope = PluginRelatedLocatorsUtils.getCandidatesScope(project),
|
||||
callback: (Extension, XmlTag) -> Boolean,
|
||||
) {
|
||||
PerformanceAssertions.assertDoesNotAffectHighlighting()
|
||||
|
||||
val searchWord = name.substringBeforeLast('$')
|
||||
@@ -82,18 +124,22 @@ private fun findExtensionsByClassName(project: Project, className: String): List
|
||||
return result
|
||||
}
|
||||
|
||||
internal inline fun processExtensionsByClassName(project: Project,
|
||||
className: String,
|
||||
crossinline processor: (XmlTag, ExtensionPoint) -> Boolean) {
|
||||
internal inline fun processExtensionsByClassName(
|
||||
project: Project,
|
||||
className: String,
|
||||
crossinline processor: (XmlTag, ExtensionPoint) -> Boolean,
|
||||
) {
|
||||
processExtensionDeclarations(className, project) { extension, tag ->
|
||||
extension.extensionPoint?.let { processor(tag, it) } ?: true
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExtensionByExtensionPointLocator(private val project: Project,
|
||||
extensionPoint: ExtensionPoint,
|
||||
private val extensionId: String?,
|
||||
private val extensionIdFunction: (Extension) -> String? = { extension -> extension.id.stringValue }) : ExtensionLocator() {
|
||||
private class ExtensionByExtensionPointLocator(
|
||||
private val project: Project,
|
||||
extensionPoint: ExtensionPoint,
|
||||
private val extensionId: String?,
|
||||
private val extensionIdFunction: (Extension) -> String? = { extension -> extension.id.stringValue },
|
||||
) : ExtensionLocator() {
|
||||
private val pointQualifiedName = extensionPoint.effectiveQualifiedName
|
||||
|
||||
private fun processCandidates(processor: (XmlTag) -> Boolean) {
|
||||
|
||||
+16
@@ -114,4 +114,20 @@
|
||||
<add-to-group group-id="<error descr="Cannot resolve group 'MyExecutorId'">MyExecutorId</error>"/>
|
||||
</action>
|
||||
</actions>
|
||||
|
||||
<!-- ToolWindow -->
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.toolWindow" beanClass="java.lang.String"/>
|
||||
</extensionPoints>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<toolWindow id="ToolWindowId"/>
|
||||
</extensions>
|
||||
<actions>
|
||||
<action class="foo.bar.BarAction" use-shortcut-of="ActivateToolWindowIdToolWindow"/>
|
||||
<action class="foo.bar.BarAction" use-shortcut-of="ActivateToolWindowIdFromConstantsToolWindow"/>
|
||||
|
||||
<action class="foo.bar.BarAction">
|
||||
<add-to-group group-id="<error descr="Cannot resolve group 'ActivateToolWindowIdToolWindow'">ActivateToolWindowIdToolWindow</error>"/>
|
||||
</action>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
+11
-4
@@ -58,10 +58,10 @@ public class PluginXmlDomInspectionActionHighlightingTest extends PluginXmlDomIn
|
||||
// fake Executor
|
||||
myFixture.addClass("""
|
||||
package com.intellij.execution;
|
||||
public abstract class Executor {
|
||||
public abstract String getId();
|
||||
public abstract String getContextActionId();
|
||||
}
|
||||
public abstract class Executor {
|
||||
public abstract String getId();
|
||||
public abstract String getContextActionId();
|
||||
}
|
||||
""");
|
||||
myFixture.addClass("""
|
||||
public class MyExecutor extends com.intellij.execution.Executor {
|
||||
@@ -76,6 +76,13 @@ public class PluginXmlDomInspectionActionHighlightingTest extends PluginXmlDomIn
|
||||
}
|
||||
}
|
||||
""");
|
||||
// toolwindow ID
|
||||
myFixture.addClass("""
|
||||
package com.intellij.openapi.wm;
|
||||
interface ToolWindowId {
|
||||
String FAVORITES = "ToolWindowIdFromConstants";
|
||||
}
|
||||
""");
|
||||
|
||||
myFixture.addFileToProject("keymaps/MyKeymap.xml", "<keymap/>");
|
||||
myFixture.testHighlighting("ActionComplexHighlighting.xml");
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
|
||||
class ActionReferenceToolWindowHighlighting {
|
||||
|
||||
fun testToolWindowExtensionHighlighting() {
|
||||
ActionUtil.wrap("ActivateToolWindowIdToolWindow")
|
||||
ActionUtil.wrap("ActivateToolWindowIdWithSpacesToolWindow")
|
||||
|
||||
ActionUtil.wrap("<error descr="Cannot resolve action id 'ActivateINVALID_VALUEToolWindow'">ActivateINVALID_VALUEToolWindow</error>")
|
||||
|
||||
ActionUtil.getActionGroup("<error descr="Cannot resolve group id 'ActivateToolWindowIdToolWindow'">ActivateToolWindowIdToolWindow</error>")
|
||||
}
|
||||
|
||||
fun testToolWindowToolWindowIdHighlighting() {
|
||||
ActionUtil.wrap("ActivateToolWindowIdFromConstantsToolWindow")
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<idea-plugin>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<toolWindow factoryClass="FactoryClass" id="ToolWindowId"/>
|
||||
<toolWindow factoryClass="FactoryClassForSpaces" id="Tool Window Id With Spaces"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
+48
-2
@@ -1,6 +1,8 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.kotlin.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.codeInspection.LocalInspectionEP
|
||||
import com.intellij.execution.executors.DefaultRunExecutor
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
@@ -13,7 +15,6 @@ import com.intellij.ui.components.JBList
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.idea.devkit.inspections.UnresolvedPluginConfigReferenceInspection
|
||||
import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil
|
||||
import kotlin.jvm.java
|
||||
|
||||
@TestDataPath("\$CONTENT_ROOT/testData/codeInsight/actionReference")
|
||||
class KtActionReferenceTest : JavaCodeInsightFixtureTestCase() {
|
||||
@@ -128,6 +129,46 @@ class KtActionReferenceTest : JavaCodeInsightFixtureTestCase() {
|
||||
myFixture.testHighlighting("ActionReferenceHighlighting.kt")
|
||||
}
|
||||
|
||||
fun testActionReferenceToolWindowHighlighting() {
|
||||
myFixture.enableInspections(UnresolvedPluginConfigReferenceInspection::class.java)
|
||||
configureToolWindowTest()
|
||||
|
||||
myFixture.testHighlighting("ActionReferenceToolWindowHighlighting.kt")
|
||||
}
|
||||
|
||||
fun testActionReferenceToolWindowCompletion() {
|
||||
configureToolWindowTest()
|
||||
myFixture.configureByText("Caller.kt", """
|
||||
fun usage(actionManager: com.intellij.openapi.actionSystem.ActionManager){
|
||||
actionManager.getAction("ActivateT<caret>")
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
assertContainsElements(myFixture.getCompletionVariants("Caller.kt").orEmpty(),
|
||||
"ActivateToolWindowIdToolWindow", "ActivateToolWindowIdWithSpacesToolWindow",
|
||||
"ActivateToolWindowIdFromConstantsToolWindow", "ActivateToolWindowIdFromConstants_DeprecatedToolWindow")
|
||||
|
||||
val extension = getLookupElementPresentation("ActivateToolWindowIdToolWindow")
|
||||
assertTrue(extension.isItemTextBold)
|
||||
assertEquals("FactoryClass", extension.typeText)
|
||||
|
||||
val deprecated = getLookupElementPresentation("ActivateToolWindowIdFromConstants_DeprecatedToolWindow")
|
||||
assertTrue(deprecated.isStrikeout)
|
||||
assertEquals("com.intellij.openapi.wm.ToolWindowId#DEPRECATED", deprecated.typeText)
|
||||
}
|
||||
|
||||
private fun configureToolWindowTest() {
|
||||
myFixture.copyFileToProject("actionReferenceToolWindowHighlighting.xml")
|
||||
myFixture.addClass("""
|
||||
package com.intellij.openapi.wm;
|
||||
interface ToolWindowId {
|
||||
String FAVORITES = "ToolWindowIdFromConstants";
|
||||
@Deprecated
|
||||
String DEPRECATED = "ToolWindowIdFromConstants_Deprecated";
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun testRenameGroup() {
|
||||
myFixture.createFile("plugin.xml", pluginXmlActions("""
|
||||
<group id="myGroup"></group>
|
||||
@@ -151,4 +192,9 @@ class KtActionReferenceTest : JavaCodeInsightFixtureTestCase() {
|
||||
|
||||
private val DLR = '$'.toString()
|
||||
|
||||
}
|
||||
private fun getLookupElementPresentation(lookupString: String): LookupElementPresentation {
|
||||
val lookupElement: LookupElement? = myFixture.getLookupElements()!!.find({ element: LookupElement -> element.getLookupString() == lookupString })
|
||||
assertNotNull(lookupString, lookupElement)
|
||||
return LookupElementPresentation.renderElement(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user