This is an automated email from the ASF dual-hosted git repository.
jdaugherty pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/grails-intellij-plugin.git
The following commit(s) were added to refs/heads/main by this push:
new 2aa08f1 Improve toolbar artefact navigation for multi-project builds
2aa08f1 is described below
commit 2aa08f1c87e61eb56bf2e747db4ad86a14356fe9
Author: James Daugherty <[email protected]>
AuthorDate: Mon Jul 20 14:38:01 2026 -0500
Improve toolbar artefact navigation for multi-project builds
- Deduplicate navigate targets: collectFromPluginXmls ran once per
dependency
module, adding each descriptor-declared artefact N times; run it once and
dedupe files.
- Navigate to artefacts in downstream (dependent) modules, not just
dependencies, so a shared upstream artefact can reach its consumers.
- Fall back to name-only matching when no same-package artefact exists, for
shared artefacts living in a different package than the referencing class.
---
.../editor/toolbar/GrailsGoToArtefactActionBase.kt | 10 +++++--
.../plugins/grails/util/GrailsArtifact.java | 35 ++++++++++++++++++++--
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git
a/src/org/jetbrains/plugins/grails/editor/toolbar/GrailsGoToArtefactActionBase.kt
b/src/org/jetbrains/plugins/grails/editor/toolbar/GrailsGoToArtefactActionBase.kt
index f043215..092d4d0 100644
---
a/src/org/jetbrains/plugins/grails/editor/toolbar/GrailsGoToArtefactActionBase.kt
+++
b/src/org/jetbrains/plugins/grails/editor/toolbar/GrailsGoToArtefactActionBase.kt
@@ -31,9 +31,13 @@ abstract class GrailsGoToArtefactActionBase(private val
artefactType: GrailsArti
return artefactData.artefactName.replaceFirstChar { it.uppercaseChar() } +
artefactType.suffix
}
- override fun getNavigateTargets(artefactData: ArtefactData):
MutableCollection<GrClassDefinition> = artefactType.getInstances(
- artefactData.module, artefactData.packageName, artefactData.artefactName
- )
+ override fun getNavigateTargets(artefactData: ArtefactData):
Collection<GrClassDefinition> {
+ // Prefer an artefact in the same package as the current one, but fall
back to matching by name
+ // alone: in multi-project builds a shared artefact (e.g. a domain in an
upstream project) often
+ // lives in a different package than the controller/service that uses it.
+ val samePackage = artefactType.getInstances(artefactData.module,
artefactData.packageName, artefactData.artefactName)
+ return samePackage.ifEmpty {
artefactType.getInstances(artefactData.module, artefactData.artefactName) }
+ }
@ActionText override fun getNavigateTitle(target: PsiClass): String =
diff --git a/src/org/jetbrains/plugins/grails/util/GrailsArtifact.java
b/src/org/jetbrains/plugins/grails/util/GrailsArtifact.java
index d666bba..06f2123 100644
--- a/src/org/jetbrains/plugins/grails/util/GrailsArtifact.java
+++ b/src/org/jetbrains/plugins/grails/util/GrailsArtifact.java
@@ -18,6 +18,7 @@ package org.jetbrains.plugins.grails.util;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
@@ -63,7 +64,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -310,7 +314,8 @@ public enum GrailsArtifact {
ProjectFileIndex fileIndex =
ProjectRootManager.getInstance(module.getProject()).getFileIndex();
Set<Module> modules = new HashSet<>();
- ModuleUtilCore.getDependencies(module, modules);
+ ModuleUtilCore.getDependencies(module, modules); // module + its
(transitive) dependencies
+ collectDependentModules(module, modules); // + modules that
(transitively) depend on it
for (Module m : modules) {
if (GrailsFramework.isCommonPluginsModule(m)) {
@@ -329,13 +334,35 @@ public enum GrailsArtifact {
if (directory != null) {
calculateInstancesInDirectory(directory, res, fileIndex);
}
- collectFromPluginXmls(module, res);
}
}
+ // Grails plugin descriptors are resolved against the module's whole
dependency+libraries scope,
+ // so this must run once for the module - not once per dependency, which
would add every declared
+ // artefact as many times as there are modules in the dependency graph
(duplicate navigate targets).
+ collectFromPluginXmls(module, res);
+
return res;
}
+ /**
+ * Adds every module that (transitively) depends on {@code module} to {@code
result}. This lets a
+ * "Go To ..." action on an artefact in an upstream project (e.g. a shared
plugin) resolve artefacts
+ * declared in downstream projects that depend on it - the reverse of {@link
ModuleUtilCore#getDependencies}.
+ */
+ private static void collectDependentModules(@NotNull Module module, @NotNull
Set<Module> result) {
+ ModuleManager moduleManager =
ModuleManager.getInstance(module.getProject());
+ Deque<Module> queue = new ArrayDeque<>();
+ queue.add(module);
+ while (!queue.isEmpty()) {
+ for (Module dependent :
moduleManager.getModuleDependentModules(queue.poll())) {
+ if (result.add(dependent)) {
+ queue.add(dependent);
+ }
+ }
+ }
+ }
+
private void collectFromPluginXmls(Module module, MultiMap<String,
VirtualFile> result) {
Project project = module.getProject();
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
@@ -446,9 +473,11 @@ class GrailsArtifactCache {
return Collections.singletonList(classDefinition);
}
+ // The same artefact can be discovered through more than one path (e.g. a
source directory and a
+ // grails-plugin.xml descriptor), so dedupe files to avoid duplicate
navigate targets.
List<GrClassDefinition> list = new ArrayList<>(size);
- for (VirtualFile virtualFile : vfList) {
+ for (VirtualFile virtualFile : new LinkedHashSet<>(vfList)) {
ContainerUtil.addIfNotNull(list, getClassDefinition(virtualFile));
}