mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java, complete, import-module] enhance code completion ordering: support transitive dependencies and implicit imports
GitOrigin-RevId: 737e01da30f0619ecae83693062e8b22adb5f6f3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
24ca282a62
commit
ab45ebd22f
+9
-29
@@ -231,12 +231,12 @@ public final class JavaModuleGraphUtil {
|
||||
return getRequiresGraph(source).reads(source, destination);
|
||||
}
|
||||
|
||||
public static boolean reads(@NotNull PsiJavaModule source, @NotNull String destination) {
|
||||
return getRequiresGraph(source).reads(source, source, destination);
|
||||
public static @NotNull Set<PsiJavaModule> getAllDependencies(PsiJavaModule source) {
|
||||
return getRequiresGraph(source).getAllDependencies(source, false);
|
||||
}
|
||||
|
||||
public static @NotNull Set<PsiJavaModule> getAllDependencies(PsiJavaModule source) {
|
||||
return getRequiresGraph(source).getAllDependencies(source);
|
||||
public static @NotNull Set<PsiJavaModule> getAllTransitiveDependencies(PsiJavaModule source) {
|
||||
return getRequiresGraph(source).getAllDependencies(source, true);
|
||||
}
|
||||
|
||||
public static @Nullable Trinity<String, PsiJavaModule, PsiJavaModule> findConflict(@NotNull PsiJavaModule module) {
|
||||
@@ -510,26 +510,6 @@ public final class JavaModuleGraphUtil {
|
||||
myTransitiveEdges = transitiveEdges;
|
||||
}
|
||||
|
||||
public boolean reads(@NotNull PsiJavaModule source, @NotNull String destination) {
|
||||
return reads(source, source, destination);
|
||||
}
|
||||
|
||||
private boolean reads(@NotNull PsiJavaModule top, @NotNull PsiJavaModule source, @NotNull String destination) {
|
||||
Collection<PsiJavaModule> nodes = myGraph.getNodes();
|
||||
if (ContainerUtil.exists(nodes, m -> m.getName().equals(destination)) && nodes.contains(source)) {
|
||||
Iterator<PsiJavaModule> directReaders = myGraph.getIn(source);
|
||||
while (directReaders.hasNext()) {
|
||||
PsiJavaModule next = directReaders.next();
|
||||
if (top.equals(source)) {
|
||||
if (next.getName().equals(destination) || reads(top, next, destination)) return true;
|
||||
} else if(myTransitiveEdges.contains(key(next, source))) {
|
||||
if (next.getName().equals(destination) || reads(top, next, destination)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean reads(PsiJavaModule source, PsiJavaModule destination) {
|
||||
Collection<PsiJavaModule> nodes = myGraph.getNodes();
|
||||
if (nodes.contains(destination) && nodes.contains(source)) {
|
||||
@@ -595,18 +575,18 @@ public final class JavaModuleGraphUtil {
|
||||
return module.getName() + '/' + exporter.getName();
|
||||
}
|
||||
|
||||
public @NotNull Set<PsiJavaModule> getAllDependencies(@NotNull PsiJavaModule module) {
|
||||
public @NotNull Set<PsiJavaModule> getAllDependencies(@NotNull PsiJavaModule module, boolean transitive) {
|
||||
Set<PsiJavaModule> requires = new HashSet<>();
|
||||
collectDependencies(module, requires);
|
||||
collectDependencies(module, requires, transitive);
|
||||
return requires;
|
||||
}
|
||||
|
||||
private void collectDependencies(@NotNull PsiJavaModule module, @NotNull Set<PsiJavaModule> dependencies) {
|
||||
private void collectDependencies(@NotNull PsiJavaModule module, @NotNull Set<PsiJavaModule> dependencies, boolean transitive) {
|
||||
for (Iterator<PsiJavaModule> iterator = myGraph.getIn(module); iterator.hasNext();) {
|
||||
PsiJavaModule dependency = iterator.next();
|
||||
if (!dependencies.contains(dependency)) {
|
||||
if (!dependencies.contains(dependency) && (!transitive || myTransitiveEdges.contains(key(dependency, module)))) {
|
||||
dependencies.add(dependency);
|
||||
collectDependencies(dependency, dependencies);
|
||||
collectDependencies(dependency, dependencies, transitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.util.proximity;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.util.NotNullLazyKey;
|
||||
import com.intellij.openapi.util.NullableLazyKey;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.ProximityLocation;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -18,23 +16,37 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class ExplicitlyImportedWeigher extends ProximityWeigher {
|
||||
private static final NullableLazyKey<PsiPackage, ProximityLocation> PLACE_PACKAGE = NullableLazyKey.create("placePackage", location -> {
|
||||
PsiElement position = location.getPosition();
|
||||
return position == null ? null : getContextPackage(position);
|
||||
});
|
||||
private static final NullableLazyKey<List<String>, ProximityLocation> PLACE_IMPORTED_MODULE_NAMES = NullableLazyKey.create("importedModuleNames", location -> {
|
||||
final PsiJavaFile psiJavaFile = PsiTreeUtil.getContextOfType(location.getPosition(), PsiJavaFile.class, false);
|
||||
final PsiImportList importList = psiJavaFile == null ? null : psiJavaFile.getImportList();
|
||||
if (importList == null) return Collections.emptyList();
|
||||
private static final NullableLazyKey<List<PsiJavaModule>, ProximityLocation> PLACE_IMPORTED_MODULES =
|
||||
NullableLazyKey.create("importedModuleNames", location -> {
|
||||
final PsiJavaFile psiJavaFile = PsiTreeUtil.getContextOfType(location.getPosition(), PsiJavaFile.class, false);
|
||||
final PsiImportList importList = psiJavaFile == null ? null : psiJavaFile.getImportList();
|
||||
if (importList == null) return Collections.emptyList();
|
||||
|
||||
List<String> importedModuleNames = new ArrayList<>();
|
||||
for (PsiImportModuleStatement statement : importList.getImportModuleStatements()) {
|
||||
ContainerUtil.addIfNotNull(importedModuleNames, statement.getReferenceName());
|
||||
}
|
||||
return importedModuleNames;
|
||||
});
|
||||
BiConsumer<List<PsiJavaModule>, PsiJavaModule> append = (list, module) -> {
|
||||
if (module != null) {
|
||||
list.add(module);
|
||||
list.addAll(JavaModuleGraphUtil.getAllTransitiveDependencies(module));
|
||||
}
|
||||
};
|
||||
|
||||
List<PsiJavaModule> importedModules = new ArrayList<>();
|
||||
for (PsiImportModuleStatement statement : importList.getImportModuleStatements()) {
|
||||
append.accept(importedModules, statement.resolveTargetModule());
|
||||
}
|
||||
for (PsiImportStatementBase statement : ImportsUtil.getAllImplicitImports(psiJavaFile)) {
|
||||
if (statement instanceof PsiImportModuleStatement moduleStatement) {
|
||||
append.accept(importedModules, moduleStatement.resolveTargetModule());
|
||||
}
|
||||
}
|
||||
return importedModules;
|
||||
});
|
||||
private static final NotNullLazyKey<List<String>, ProximityLocation> PLACE_IMPORTED_NAMES =
|
||||
NotNullLazyKey.createLazyKey("importedNames", location -> {
|
||||
final PsiJavaFile psiJavaFile = PsiTreeUtil.getContextOfType(location.getPosition(), PsiJavaFile.class, false);
|
||||
@@ -106,10 +118,10 @@ public final class ExplicitlyImportedWeigher extends ProximityWeigher {
|
||||
return ImportWeight.CLASS_ON_DEMAND_NESTED;
|
||||
}
|
||||
|
||||
List<String> moduleNames = PLACE_IMPORTED_MODULE_NAMES.getValue(location);
|
||||
if (moduleNames != null && !moduleNames.isEmpty()) {
|
||||
PsiJavaModule psiJavaModule = JavaModuleGraphHelper.getInstance().findDescriptorByElement(element);
|
||||
if (psiJavaModule != null && moduleNames.contains(psiJavaModule.getName())) return ImportWeight.MODULE_IMPORTED;
|
||||
List<PsiJavaModule> importedModules = PLACE_IMPORTED_MODULES.getValue(location);
|
||||
if (importedModules != null && !importedModules.isEmpty()) {
|
||||
PsiJavaModule suggestedModule = JavaModuleGraphHelper.getInstance().findDescriptorByElement(element);
|
||||
if (suggestedModule != null && importedModules.contains(suggestedModule)) return ImportWeight.MODULE_IMPORTED;
|
||||
}
|
||||
|
||||
final PsiPackage placePackage = PLACE_PACKAGE.getValue(location);
|
||||
|
||||
+51
@@ -252,6 +252,57 @@ class ModuleCompletionTest : LightJava9ModulesCodeInsightFixtureTestCase() {
|
||||
myFixture.assertPreferredCompletionItems(0, "MyClassC", "MyClassB", "MyClassA")
|
||||
}
|
||||
|
||||
@NeedsIndex.Full
|
||||
fun testTransitiveModuleImportDeclarationsOrder() {
|
||||
addFile("module-info.java", """
|
||||
module first.module.name {
|
||||
requires transitive second.module.name;
|
||||
}
|
||||
""".trimIndent(), M2)
|
||||
|
||||
addFile("module-info.java", """
|
||||
module second.module.name {
|
||||
exports second.module.name;
|
||||
}
|
||||
""".trimIndent(), M4)
|
||||
addFile("MyClassB.java", """
|
||||
package second.module.name;
|
||||
public class MyClassB { }
|
||||
""".trimIndent(), M4)
|
||||
|
||||
addFile("module-info.java", """
|
||||
module third.module.name {
|
||||
exports third.module.name;
|
||||
}
|
||||
""".trimIndent(), M5)
|
||||
addFile("MyClassC.java", """
|
||||
package third.module.name;
|
||||
public class MyClassC { }
|
||||
""".trimIndent(), M4)
|
||||
|
||||
|
||||
addFile("module-info.java", """
|
||||
module current.module.name {
|
||||
requires first.module.name;
|
||||
}
|
||||
""".trimIndent(), M4)
|
||||
|
||||
myFixture.configureByText("Main.java", """
|
||||
import module second.module.name;
|
||||
import current.pkg.name.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
MyCla<caret>
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.complete(CompletionType.BASIC)
|
||||
myFixture.getLookup()
|
||||
myFixture.assertPreferredCompletionItems(0, "MyClassB", "MyClassC")
|
||||
}
|
||||
|
||||
@NeedsIndex.Full
|
||||
fun testReadableCompletion1() {
|
||||
addFile("module-info.java", "module current.module.name { requires first.module.name; }")
|
||||
|
||||
Reference in New Issue
Block a user