From d6a56f86c33d3766fa1fdb3d700586b5802fbc32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yann=20C=C3=A9bron?=
Date: Wed, 12 Feb 2025 16:49:13 +0100
Subject: [PATCH] [devkit] Handle without 'id' correctly (IJPL-177709)
GitOrigin-RevId: 012c879eb66e914667517359390dd884ac056705
---
.../resources/intellij.devkit.core.xml | 2 ++
.../devkit/devkit-core/src/dom/Action.java | 21 ++++++++++++
.../devkit-core/src/dom/ActionOrGroup.java | 24 ++++++++++++--
.../index/IdeaPluginRegistrationIndex.java | 6 ++--
.../src/dom/index/RegistrationIndexer.java | 15 ++++-----
.../src/navigation/LineMarkerInfoHelper.java | 8 ++---
.../PluginDescriptorStructureUtil.java | 8 ++---
.../references/ActionOrGroupIdReference.java | 17 ++++++----
.../ActionWithoutIdReferencesQueryExecutor.kt | 33 +++++++++++++++++++
.../DevKitRelatedPropertiesProvider.java | 9 ++---
.../testData/codeInsight/ActionCompletion.xml | 1 +
.../ActionCompletionBundle.properties | 2 ++
.../testData/navigation/structure/plugin.xml | 2 +-
.../codeInsight/PluginXmlFunctionalTest.java | 3 +-
.../PluginDescriptorStructureTest.java | 4 +--
.../codeInsight/KtActionReferenceTest.kt | 3 +-
.../src/PluginXmlI18nInspection.java | 16 +++++----
.../MyBundle.properties | 3 ++
...uginXmlCapitalization_ActionPluginName.xml | 2 ++
.../pluginXmlI18n/PluginXmlI18nInspection.xml | 5 ++-
20 files changed, 137 insertions(+), 47 deletions(-)
create mode 100644 plugins/devkit/devkit-core/src/references/ActionWithoutIdReferencesQueryExecutor.kt
diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
index b2feb2adca54..6c164f19ad2f 100644
--- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
+++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
@@ -510,6 +510,8 @@
+
+
getEffectiveIdAttribute() {
+ if (DomUtil.hasXml(getId())) {
+ return getId();
+ }
+
+ return getClazz();
+ }
+
@NotNull
@Attribute("class")
@Required
diff --git a/plugins/devkit/devkit-core/src/dom/ActionOrGroup.java b/plugins/devkit/devkit-core/src/dom/ActionOrGroup.java
index 0fb8c41c4a26..6f497512af97 100644
--- a/plugins/devkit/devkit-core/src/dom/ActionOrGroup.java
+++ b/plugins/devkit/devkit-core/src/dom/ActionOrGroup.java
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.util.xml.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.dom.impl.ActionOrGroupReferencingConverter;
import java.util.List;
@@ -12,6 +13,25 @@ import java.util.function.Function;
public interface ActionOrGroup extends DomElement {
+ /**
+ * @return possibly fallback ID if {@link #getId()} is not specified
+ * @see #getEffectiveIdAttribute()
+ */
+ @Nullable
+ default String getEffectiveId() {
+ return getId().getStringValue();
+ }
+
+ /**
+ * @return underlying attribute for {@link #getEffectiveId()}, used for navigation purposes
+ */
+ default GenericAttributeValue> getEffectiveIdAttribute() {
+ return getId();
+ }
+
+ /**
+ * @see #getEffectiveId()
+ */
@NotNull
@NameValue
@Stubbed
@@ -96,12 +116,12 @@ public interface ActionOrGroup extends DomElement {
}
public String getMessageKey(ActionOrGroup actionOrGroup) {
- return getMessageKeyPrefix(actionOrGroup) + actionOrGroup.getId().getStringValue() +
+ return getMessageKeyPrefix(actionOrGroup) + actionOrGroup.getEffectiveId() +
myPropertyKeySuffix;
}
public String getMessageKey(ActionOrGroup actionOrGroup, @NotNull OverrideText overrideText) {
- return getMessageKeyPrefix(actionOrGroup) + actionOrGroup.getId().getStringValue() +
+ return getMessageKeyPrefix(actionOrGroup) + actionOrGroup.getEffectiveId() +
"." + overrideText.getPlace().getStringValue() +
myPropertyKeySuffix;
}
diff --git a/plugins/devkit/devkit-core/src/dom/index/IdeaPluginRegistrationIndex.java b/plugins/devkit/devkit-core/src/dom/index/IdeaPluginRegistrationIndex.java
index cc68b1345860..660cb855c88d 100644
--- a/plugins/devkit/devkit-core/src/dom/index/IdeaPluginRegistrationIndex.java
+++ b/plugins/devkit/devkit-core/src/dom/index/IdeaPluginRegistrationIndex.java
@@ -1,4 +1,4 @@
-// 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.dom.index;
import com.intellij.openapi.project.Project;
@@ -40,7 +40,7 @@ import java.util.*;
*
* - Application/Project/Module-component class - {@link Component#getInterfaceClass()} / {@link Component#getImplementationClass()} / {@link Component#getHeadlessImplementationClass()}
* - Action/ActionGroup class - {@link Action#getClazz()}/{@link Group#getClazz()}
- * - Action/ActionGroup ID - {@link ActionOrGroup#getId()}
+ * - Action/ActionGroup ID - {@link ActionOrGroup#getEffectiveId()}
* - Application/Project Listener class - {@link Listeners.Listener#getListenerClassName()}
* - Listener topic class - {@link Listeners.Listener#getTopicClassName()}
*
@@ -48,7 +48,7 @@ import java.util.*;
@SuppressWarnings("UnusedReturnValue")
public final class IdeaPluginRegistrationIndex extends PluginXmlIndexBase> {
- private static final int INDEX_VERSION = 8;
+ private static final int INDEX_VERSION = 9;
private static final ID> NAME = ID.create("IdeaPluginRegistrationIndex");
diff --git a/plugins/devkit/devkit-core/src/dom/index/RegistrationIndexer.java b/plugins/devkit/devkit-core/src/dom/index/RegistrationIndexer.java
index e8141d835e9a..573f97f1771c 100644
--- a/plugins/devkit/devkit-core/src/dom/index/RegistrationIndexer.java
+++ b/plugins/devkit/devkit-core/src/dom/index/RegistrationIndexer.java
@@ -1,4 +1,4 @@
-// Copyright 2000-2022 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.dom.index;
import com.intellij.openapi.util.text.StringUtil;
@@ -9,9 +9,9 @@ import com.intellij.util.SmartList;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomUtil;
-import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.GenericDomValue;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.dom.*;
import java.util.List;
@@ -106,25 +106,24 @@ class RegistrationIndexer {
private void processActionContainer(ActionContainer actionContainer) {
for (Action action : actionContainer.getActions()) {
addEntry(action, action.getClazz(), RegistrationEntry.RegistrationType.ACTION);
- addIdEntry(action, action.getId(), RegistrationEntry.RegistrationType.ACTION_ID);
+ addIdEntry(action, action.getEffectiveId(), RegistrationEntry.RegistrationType.ACTION_ID);
}
for (Group group : actionContainer.getGroups()) {
addEntry(group, group.getClazz(), RegistrationEntry.RegistrationType.ACTION);
- addIdEntry(group, group.getId(), RegistrationEntry.RegistrationType.ACTION_GROUP_ID);
+ addIdEntry(group, group.getEffectiveId(), RegistrationEntry.RegistrationType.ACTION_GROUP_ID);
processActionContainer(group);
}
}
private void addIdEntry(DomElement domElement,
- GenericAttributeValue idValue,
+ @Nullable String idValue,
RegistrationEntry.RegistrationType type) {
+ if (StringUtil.isEmptyOrSpaces(idValue)) return;
if (!DomUtil.hasXml(domElement)) return;
- String id = idValue.getStringValue();
- if (StringUtil.isEmptyOrSpaces(id)) return;
- storeEntry(id, domElement, type);
+ storeEntry(idValue, domElement, type);
}
private void addEntry(DomElement domElement,
diff --git a/plugins/devkit/devkit-core/src/navigation/LineMarkerInfoHelper.java b/plugins/devkit/devkit-core/src/navigation/LineMarkerInfoHelper.java
index ccfbd903bc05..8d664aaed4cb 100644
--- a/plugins/devkit/devkit-core/src/navigation/LineMarkerInfoHelper.java
+++ b/plugins/devkit/devkit-core/src/navigation/LineMarkerInfoHelper.java
@@ -1,4 +1,4 @@
-// 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.navigation;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
@@ -80,15 +80,13 @@ final class LineMarkerInfoHelper {
static RelatedItemLineMarkerInfo> createActionLineMarkerInfo(List extends ActionCandidate> targets, PsiElement element) {
return createPluginLineMarkerInfo(targets, element,
DevKitBundle.message("gutter.related.navigation.choose.action"),
- (NullableFunction)action ->
- ObjectUtils.chooseNotNull(action.getId().getStringValue(), action.getClazz().getStringValue()));
+ (NullableFunction)action -> action.getEffectiveId());
}
static RelatedItemLineMarkerInfo> createActionGroupLineMarkerInfo(List extends ActionCandidate> targets, PsiElement element) {
return createPluginLineMarkerInfo(targets, element,
DevKitBundle.message("gutter.related.navigation.choose.action.group"),
- (NullableFunction)group ->
- ObjectUtils.chooseNotNull(group.getId().getStringValue(), group.getClazz().getStringValue()));
+ (NullableFunction)group -> group.getEffectiveId());
}
static RelatedItemLineMarkerInfo> createComponentLineMarkerInfo(List extends ComponentCandidate> targets, PsiElement element) {
diff --git a/plugins/devkit/devkit-core/src/navigation/structure/PluginDescriptorStructureUtil.java b/plugins/devkit/devkit-core/src/navigation/structure/PluginDescriptorStructureUtil.java
index 7142200eff29..995f87250a92 100644
--- a/plugins/devkit/devkit-core/src/navigation/structure/PluginDescriptorStructureUtil.java
+++ b/plugins/devkit/devkit-core/src/navigation/structure/PluginDescriptorStructureUtil.java
@@ -1,4 +1,4 @@
-// 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.navigation.structure;
import com.intellij.icons.AllIcons;
@@ -63,8 +63,8 @@ public final class PluginDescriptorStructureUtil {
return safeGetTagDisplayText(tag);
}
- if (element instanceof Action) {
- String actionId = ((Action)element).getId().getStringValue();
+ if (element instanceof Action action) {
+ String actionId = action.getEffectiveId();
if (StringUtil.isNotEmpty(actionId)) {
return actionId;
}
@@ -196,7 +196,7 @@ public final class PluginDescriptorStructureUtil {
}
private static @Nullable String getGroupLocation(ActionOrGroup element) {
- return element.getId().getStringValue();
+ return element.getEffectiveId();
}
private static @Nullable String getAddToGroupLocation(AddToGroup element) {
diff --git a/plugins/devkit/devkit-core/src/references/ActionOrGroupIdReference.java b/plugins/devkit/devkit-core/src/references/ActionOrGroupIdReference.java
index db19d2245f35..a6bb16072b47 100644
--- a/plugins/devkit/devkit-core/src/references/ActionOrGroupIdReference.java
+++ b/plugins/devkit/devkit-core/src/references/ActionOrGroupIdReference.java
@@ -19,10 +19,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.ProjectIconsAccessor;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.xml.DomElement;
-import com.intellij.util.xml.DomTarget;
-import com.intellij.util.xml.ElementPresentationManager;
-import com.intellij.util.xml.GenericAttributeValue;
+import com.intellij.util.xml.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
@@ -101,7 +98,7 @@ public final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<
}
final List psiElements =
- ContainerUtil.mapNotNull(processor.getResults(), actionOrGroup -> getDomTargetPsi(actionOrGroup));
+ ContainerUtil.mapNotNull(processor.getResults(), actionOrGroup -> getActionOrGroupDomTargetPsi(actionOrGroup));
return PsiElementResolveResult.createResults(psiElements);
}
@@ -172,8 +169,8 @@ public final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<
final GlobalSearchScope domSearchScope = PluginRelatedLocatorsUtils.getCandidatesScope(project);
IdeaPluginRegistrationIndex.processAllActionOrGroup(project, domSearchScope, actionOrGroup -> {
if (isRelevantForVariant(actionOrGroup)) {
- PsiElement psiElement = getDomTargetPsi(actionOrGroup);
- String name = StringUtil.notNullize(actionOrGroup.getId().getStringValue(),
+ PsiElement psiElement = getActionOrGroupDomTargetPsi(actionOrGroup);
+ String name = StringUtil.notNullize(actionOrGroup.getEffectiveId(),
DevKitBundle.message("plugin.xml.convert.action.or.group.invalid.name"));
LookupElementBuilder builder = LookupElementBuilder.create(psiElement, name)
.withRenderer(ActionOrGroupLookupRenderer.INSTANCE);
@@ -245,6 +242,12 @@ public final class ActionOrGroupIdReference extends PsiPolyVariantReferenceBase<
};
}
+ private static PsiElement getActionOrGroupDomTargetPsi(ActionOrGroup actionOrGroup) {
+ DomTarget target = DomTarget.getTarget(actionOrGroup, actionOrGroup.getEffectiveIdAttribute());
+ assert target != null;
+ return PomService.convertToPsi(target);
+ }
+
private static PsiElement getDomTargetPsi(DomElement domElement) {
DomTarget target = DomTarget.getTarget(domElement);
assert target != null;
diff --git a/plugins/devkit/devkit-core/src/references/ActionWithoutIdReferencesQueryExecutor.kt b/plugins/devkit/devkit-core/src/references/ActionWithoutIdReferencesQueryExecutor.kt
new file mode 100644
index 000000000000..fe0728475056
--- /dev/null
+++ b/plugins/devkit/devkit-core/src/references/ActionWithoutIdReferencesQueryExecutor.kt
@@ -0,0 +1,33 @@
+// 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.application.QueryExecutorBase
+import com.intellij.psi.PsiReference
+import com.intellij.psi.search.searches.ReferencesSearch
+import com.intellij.psi.xml.XmlAttributeValue
+import com.intellij.util.Processor
+import com.intellij.util.xml.DomUtil
+import org.jetbrains.idea.devkit.dom.Action
+
+/**
+ * Search for implicit [Action] `id` if not specified.
+ * Reference for [Action.getEffectiveId] to [Action.getClazz] (short class name).
+ *
+ * @see DevKitRelatedPropertiesProvider
+ */
+internal class ActionWithoutIdReferencesQueryExecutor : QueryExecutorBase() {
+
+ override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor) {
+ val elementToSearch = queryParameters.elementToSearch
+
+ if (elementToSearch !is XmlAttributeValue) return
+ if (elementToSearch.hostName != "class") return
+
+ val domElement = DomUtil.getDomElement(elementToSearch) ?: return
+ val action = DomUtil.getParentOfType(domElement, Action::class.java, true) ?: return
+ if (DomUtil.hasXml(action.id)) return
+
+ val effectiveId = action.effectiveId ?: return
+ queryParameters.optimizer.searchWord(effectiveId, queryParameters.effectiveSearchScope, true, elementToSearch)
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-core/src/references/DevKitRelatedPropertiesProvider.java b/plugins/devkit/devkit-core/src/references/DevKitRelatedPropertiesProvider.java
index 602680d86d60..5ab49d51cf5f 100644
--- a/plugins/devkit/devkit-core/src/references/DevKitRelatedPropertiesProvider.java
+++ b/plugins/devkit/devkit-core/src/references/DevKitRelatedPropertiesProvider.java
@@ -45,6 +45,7 @@ import java.util.Collections;
*
*
* @see MessageBundleReferenceContributor
+ * @see ActionWithoutIdReferencesQueryExecutor
*/
final class DevKitRelatedPropertiesProvider extends DevkitRelatedLineMarkerProviderBase {
@@ -72,7 +73,7 @@ final class DevKitRelatedPropertiesProvider extends DevkitRelatedLineMarkerProvi
DomElement domElement = DomUtil.getDomElement(leaf);
if (domElement instanceof ActionOrGroup actionOrGroup) {
- createLineMarker(leaf, result, domElement, actionOrGroup.getId());
+ createLineMarker(leaf, result, domElement, actionOrGroup.getEffectiveIdAttribute());
}
else if (domElement instanceof OverrideText overrideText) {
createLineMarker(leaf, result, domElement, overrideText.getPlace());
@@ -90,10 +91,10 @@ final class DevKitRelatedPropertiesProvider extends DevkitRelatedLineMarkerProvi
final XmlElement valueXmlElement = DomUtil.getValueElement(referenceElement);
if (valueXmlElement == null) return;
- PropertiesFile file = DescriptorI18nUtil.findBundlePropertiesFile(domElement);
- if (file == null) return;
+ PropertiesFile propertiesFile = DescriptorI18nUtil.findBundlePropertiesFile(domElement);
+ if (propertiesFile == null) return;
- final Query query = ReferencesSearch.search(valueXmlElement, new LocalSearchScope(file.getContainingFile()));
+ final Query query = ReferencesSearch.search(valueXmlElement, new LocalSearchScope(propertiesFile.getContainingFile()));
if (query.findFirst() == null) return;
result.add(
diff --git a/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletion.xml b/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletion.xml
index e3266777605b..51cab0c56de2 100644
--- a/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletion.xml
+++ b/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletion.xml
@@ -5,6 +5,7 @@
+
diff --git a/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletionBundle.properties b/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletionBundle.properties
index 1f958ba4e8a0..1a092002ac7e 100644
--- a/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletionBundle.properties
+++ b/plugins/devkit/devkit-java-tests/testData/codeInsight/ActionCompletionBundle.properties
@@ -1,2 +1,4 @@
action.actionId.localized.text=_Action Localized Text
action.actionId.localized.description=Action localized description
+action.BarAction.text=Action without ID Localized Text
+action.BarAction.description=Action without ID localized description
diff --git a/plugins/devkit/devkit-java-tests/testData/navigation/structure/plugin.xml b/plugins/devkit/devkit-java-tests/testData/navigation/structure/plugin.xml
index bd397b1ec8d1..58a4f124fbe3 100644
--- a/plugins/devkit/devkit-java-tests/testData/navigation/structure/plugin.xml
+++ b/plugins/devkit/devkit-java-tests/testData/navigation/structure/plugin.xml
@@ -65,6 +65,6 @@
-
+
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.java
index 7dc3d2d9fb45..c1718bb3ed46 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.java
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.java
@@ -1,4 +1,4 @@
-// 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.codeInsight;
import com.intellij.codeInsight.TargetElementUtil;
@@ -748,6 +748,7 @@ public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase {
LookupElement[] lookupElements = myFixture.getLookupElements();
assertLookupElement(lookupElements, "actionId", " \"ActionId Text\"", "ActionId description");
assertLookupElement(lookupElements, "actionId.localized", " \"Action Localized Text\"", "Action localized description");
+ assertLookupElement(lookupElements, "BarAction", " \"Action without ID Localized Text\"", "Action without ID localized description");
assertLookupElement(lookupElements, "actionId.missing.localized", null, null);
}
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/structure/PluginDescriptorStructureTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/structure/PluginDescriptorStructureTest.java
index 77f97f5d5d2b..7b40a35702c3 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/structure/PluginDescriptorStructureTest.java
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/structure/PluginDescriptorStructureTest.java
@@ -1,4 +1,4 @@
-// Copyright 2000-2023 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.navigation.structure;
import com.intellij.icons.AllIcons;
@@ -117,7 +117,7 @@ public class PluginDescriptorStructureTest extends JavaCodeInsightFixtureTestCas
TreeElement groupNode = actionNodes[0];
assertEquals("Group", groupNode.getPresentation().getPresentableText());
assertEquals("MyPlugin.MyGroup", groupNode.getPresentation().getLocationString());
- assertEquals("SomeAction2", actionNodes[1].getPresentation().getPresentableText());
+ assertEquals("SomeAction2Class", actionNodes[1].getPresentation().getPresentableText());
assertEquals("SomeAction2Class", actionNodes[1].getPresentation().getLocationString());
TreeElement[] inGroupNodes = groupNode.getChildren();
diff --git a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/codeInsight/KtActionReferenceTest.kt b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/codeInsight/KtActionReferenceTest.kt
index 325eba4eb054..730e87bc2412 100644
--- a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/codeInsight/KtActionReferenceTest.kt
+++ b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/codeInsight/KtActionReferenceTest.kt
@@ -106,6 +106,7 @@ class KtActionReferenceTest : JavaCodeInsightFixtureTestCase() {
myFixture.createFile("plugin.xml", pluginXmlActions("""
+
"""
));
myFixture.configureByText("Caller.kt", """
@@ -115,7 +116,7 @@ class KtActionReferenceTest : JavaCodeInsightFixtureTestCase() {
}
""".trimIndent())
- assertSameElements(myFixture.getCompletionVariants("Caller.kt").orEmpty(), "myAction", "myGroup")
+ assertSameElements(myFixture.getCompletionVariants("Caller.kt").orEmpty(), "myAction", "myGroup", "myActionWithoutExplicitId")
}
fun testActionReferenceHighlighting() {
diff --git a/plugins/devkit/intellij.devkit.i18n/src/PluginXmlI18nInspection.java b/plugins/devkit/intellij.devkit.i18n/src/PluginXmlI18nInspection.java
index d730bd727cf6..19d08b8570ae 100644
--- a/plugins/devkit/intellij.devkit.i18n/src/PluginXmlI18nInspection.java
+++ b/plugins/devkit/intellij.devkit.i18n/src/PluginXmlI18nInspection.java
@@ -1,4 +1,4 @@
-// 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.i18n;
import com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl;
@@ -180,9 +180,6 @@ final class PluginXmlI18nInspection extends DevKitPluginXmlInspectionBase {
}
private static void highlightActionOrGroup(@NotNull DomElementAnnotationHolder holder, @NotNull ActionOrGroup actionOrGroup) {
- String id = actionOrGroup.getId().getStringValue();
- if (id == null) return;
-
String text = actionOrGroup.getText().getStringValue();
String desc = actionOrGroup.getDescription().getStringValue();
if (text == null && desc == null) return;
@@ -540,6 +537,13 @@ final class PluginXmlI18nInspection extends DevKitPluginXmlInspectionBase {
}
}
+ @Nullable
+ private static String getEffectiveActionOrGroupId(@Nullable XmlTag tag) {
+ ActionOrGroup actionOrGroup = DomUtil.findDomElement(tag, ActionOrGroup.class);
+ assert actionOrGroup != null;
+ return actionOrGroup.getEffectiveId();
+ }
+
private static void extractTextAndDescription(@NotNull Project project,
Collection tags,
PropertiesFile propertiesFile,
@@ -552,10 +556,10 @@ final class PluginXmlI18nInspection extends DevKitPluginXmlInspectionBase {
String id;
if (tag.getName().equals("override-text")) {
- id = Objects.requireNonNull(tag.getParentTag()).getAttributeValue("id") + "." + tag.getAttributeValue("place");
+ id = getEffectiveActionOrGroupId(tag.getParentTag()) + "." + tag.getAttributeValue("place");
}
else {
- id = tag.getAttributeValue("id");
+ id = getEffectiveActionOrGroupId(tag);
}
List propertiesFiles = Collections.singletonList(propertiesFile);
diff --git a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/MyBundle.properties b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/MyBundle.properties
index 9c215ee30267..6a9a1925b518 100644
--- a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/MyBundle.properties
+++ b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/MyBundle.properties
@@ -6,6 +6,9 @@ action.BundleActionWithoutDescription.text=My Text
action.BundleActionWrongCasing.text=lower case text
action.BundleActionWrongCasing.description=lower case description
+action.MyActionWithoutId.text=action without id lower case text
+action.MyActionWithoutId.description=action without id lower case description
+
group.BundleGroup.text=Bundle Group
group.BundleGroup.description=Bundle group description
diff --git a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/pluginXmlCapitalization_ActionPluginName.xml b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/pluginXmlCapitalization_ActionPluginName.xml
index f839184b9e9a..d2cb1f971a33 100644
--- a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/pluginXmlCapitalization_ActionPluginName.xml
+++ b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlCapitalization/pluginXmlCapitalization_ActionPluginName.xml
@@ -32,6 +32,8 @@
<action id="BundleActionWrongCasing"/>
+ <action class="MyActionWithoutId"/>
+
diff --git a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlI18n/PluginXmlI18nInspection.xml b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlI18n/PluginXmlI18nInspection.xml
index b53d57b6e931..0ac4e2dad05d 100644
--- a/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlI18n/PluginXmlI18nInspection.xml
+++ b/plugins/devkit/intellij.devkit.i18n/testData/inspections/pluginXmlI18n/PluginXmlI18nInspection.xml
@@ -3,8 +3,7 @@
-
- action description="Not localized"
text="Not Localized"
class="foo.bar.BarAction"/>
@@ -23,7 +22,7 @@
- group description="Not localized"
text="Not Localized"/>