if (!(target instanceof PsiJavaModule)) {
boolean missing = ref.multiResolve(true).length == 0;
String message = JavaErrorMessages.message(missing ? "module.not.found" : "module.not.on.path", refElement.getReferenceText());
- return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).description(message).create();
+ HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).description(message).create();
+ if (!missing) {
+ factory().registerOrderEntryFixes(new QuickFixActionRegistrarImpl(info), ref);
+ }
+ return info;
}
else if (target == container) {
String message = JavaErrorMessages.message("module.cyclic.dependence", container.getModuleName());
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
@Override
- public void invoke(@NotNull final Project project, @Nullable final Editor editor, PsiFile file) {
+ public void invoke(@NotNull Project project, @Nullable Editor editor, PsiFile file) {
DependencyScope scope = suggestScopeByLocation(myCurrentModule, myReference.getElement());
JavaProjectModelModificationService.getInstance(project).addDependency(myCurrentModule, myLibrary, scope);
- if (editor != null) {
+ if (myQualifiedClassName != null && editor != null) {
importClass(myCurrentModule, editor, myReference, myQualifiedClassName);
}
}
-}
+}
\ No newline at end of file
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
import com.intellij.compiler.ModuleCompilerUtil;
-import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
+import java.util.*;
/**
- * User: anna
- * Date: 11/20/12
+ * @author anna
+ * @since 20.11.2012
*/
class AddModuleDependencyFix extends OrderEntryFix {
- private final LinkedHashSet<Module> myModules = new LinkedHashSet<>();
+ private static final Logger LOG = Logger.getInstance(AddModuleDependencyFix.class);
+
private final Module myCurrentModule;
- private final VirtualFile myClassVFile;
- private final PsiClass[] myClasses;
+ private final VirtualFile myRefVFile;
+ private final List<PsiClass> myClasses;
private final PsiReference myReference;
- private static final Logger LOG = Logger.getInstance("#" + AddModuleDependencyFix.class.getName());
+ private final Set<Module> myModules;
+
+ public AddModuleDependencyFix(Module currentModule, VirtualFile refVFile, List<PsiClass> classes, PsiReference reference) {
+ myCurrentModule = currentModule;
+ myRefVFile = refVFile;
+ myClasses = classes;
+ myReference = reference;
+ myModules = new LinkedHashSet<>();
- public AddModuleDependencyFix(Module currentModule,
- VirtualFile classVFile,
- PsiClass[] classes,
- PsiReference reference) {
final PsiElement psiElement = reference.getElement();
final Project project = psiElement.getProject();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
-
+ final ModuleRootManager rootManager = ModuleRootManager.getInstance(currentModule);
for (PsiClass aClass : classes) {
if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue;
PsiFile psiFile = aClass.getContainingFile();
if (psiFile == null) continue;
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) continue;
- final Module classModule = fileIndex.getModuleForFile(virtualFile);
- if (classModule != null && classModule != currentModule && !ModuleRootManager.getInstance(currentModule).isDependsOn(classModule)) {
+ Module classModule = fileIndex.getModuleForFile(virtualFile);
+ if (classModule != null && classModule != currentModule && !rootManager.isDependsOn(classModule)) {
myModules.add(classModule);
}
}
+ }
+
+ public AddModuleDependencyFix(Module currentModule, VirtualFile refVFile, Set<Module> modules, PsiReference reference) {
myCurrentModule = currentModule;
- myClassVFile = classVFile;
- myClasses = classes;
+ myRefVFile = refVFile;
+ myClasses = Collections.emptyList();
myReference = reference;
+ myModules = modules;
}
@Override
return QuickFixBundle.message("orderEntry.fix.add.dependency.on.module", module.getName());
}
else {
- return "Add dependency on module...";
+ return QuickFixBundle.message("orderEntry.fix.add.dependency.on.module.choose");
}
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
- for (Module module : myModules) {
- if (module.isDisposed()) return false;
- }
- return !project.isDisposed() && !myModules.isEmpty() && !myCurrentModule.isDisposed();
+ return !project.isDisposed() && !myCurrentModule.isDisposed() && myModules.stream().noneMatch(Module::isDisposed);
}
@Override
- public void invoke(@NotNull final Project project, @Nullable final Editor editor, PsiFile file) {
+ public void invoke(@NotNull Project project, @Nullable Editor editor, PsiFile file) {
if (myModules.size() == 1) {
addDependencyOnModule(project, editor, ContainerUtil.getFirstItem(myModules));
}
else {
- final JBList list = new JBList(myModules);
+ JBList<Module> list = new JBList<>(myModules);
list.setCellRenderer(new ModuleListCellRenderer());
- final JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list)
- .setTitle("Choose Module to Add Dependency on")
+ JBPopup popup = JBPopupFactory.getInstance().createListPopupBuilder(list)
+ .setTitle(QuickFixBundle.message("orderEntry.fix.choose.module.to.add.dependency.on"))
.setMovable(false)
.setResizable(false)
.setRequestFocus(true)
- .setItemChoosenCallback(() -> {
- final Object value = list.getSelectedValue();
- if (value instanceof Module) {
- addDependencyOnModule(project, editor, (Module)value);
- }
- }).createPopup();
+ .setItemChoosenCallback(() -> addDependencyOnModule(project, editor, list.getSelectedValue()))
+ .createPopup();
if (editor != null) {
popup.showInBestPositionFor(editor);
- } else {
+ }
+ else {
popup.showCenteredInCurrentWindow(project);
}
}
}
- private void addDependencyOnModule(final Project project, final Editor editor, final Module module) {
- final Runnable doit = () -> {
- final boolean test = ModuleRootManager.getInstance(myCurrentModule).getFileIndex().isInTestSourceContent(myClassVFile);
- JavaProjectModelModificationService.getInstance(project).addDependency(myCurrentModule, module,
- test ? DependencyScope.TEST : DependencyScope.COMPILE);
- if (editor != null) {
- final List<PsiClass> targetClasses = new ArrayList<>();
- for (PsiClass psiClass : myClasses) {
- if (ModuleUtilCore.findModuleForPsiElement(psiClass) == module) {
- targetClasses.add(psiClass);
- }
- }
- if (!DumbService.isDumb(project)) {
- new AddImportAction(project, myReference, editor, targetClasses.toArray(new PsiClass[targetClasses.size()])).execute();
- }
- }
- };
+ private void addDependencyOnModule(Project project, Editor editor, @Nullable Module module) {
+ if (module == null) return;
Couple<Module> circularModules = ModuleCompilerUtil.addingDependencyFormsCircularity(myCurrentModule, module);
if (circularModules == null || showCircularWarning(project, circularModules, module)) {
- WriteAction.run(doit::run);
+ WriteAction.run(() -> {
+ boolean test = ModuleRootManager.getInstance(myCurrentModule).getFileIndex().isInTestSourceContent(myRefVFile);
+ DependencyScope scope = test ? DependencyScope.TEST : DependencyScope.COMPILE;
+ JavaProjectModelModificationService.getInstance(project).addDependency(myCurrentModule, module, scope);
+
+ if (editor != null && !myClasses.isEmpty()) {
+ PsiClass[] targetClasses = myClasses.stream()
+ .filter(c -> ModuleUtilCore.findModuleForPsiElement(c) == module)
+ .toArray(PsiClass[]::new);
+ if (targetClasses.length > 0 && !DumbService.isDumb(project)) {
+ new AddImportAction(project, myReference, editor, targetClasses).execute();
+ }
+ }
+ });
}
}
- private static boolean showCircularWarning(Project project, Couple<Module> circularModules, Module classModule) {
- final String message = QuickFixBundle.message("orderEntry.fix.circular.dependency.warning", classModule.getName(),
- circularModules.getFirst().getName(), circularModules.getSecond().getName());
- if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
-
- return Messages.showOkCancelDialog(project, message,
- QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning"),
- Messages.getWarningIcon()) == Messages.OK;
+ private static boolean showCircularWarning(Project project, Couple<Module> circle, Module classModule) {
+ String message = QuickFixBundle.message("orderEntry.fix.circular.dependency.warning",
+ classModule.getName(), circle.getFirst().getName(), circle.getSecond().getName());
+ String title = QuickFixBundle.message("orderEntry.fix.title.circular.dependency.warning");
+ return Messages.showOkCancelDialog(project, message, title, Messages.getWarningIcon()) == Messages.OK;
}
@Override
public boolean startInWriteAction() {
return false;
}
-}
+}
\ No newline at end of file
public class DefaultQuickFixProvider extends UnresolvedReferenceQuickFixProvider<PsiJavaCodeReferenceElement> {
@Override
public void registerFixes(@NotNull PsiJavaCodeReferenceElement ref, @NotNull QuickFixActionRegistrar registrar) {
+ if (PsiUtil.isModuleFile(ref.getContainingFile())) {
+ OrderEntryFix.registerFixes(registrar, ref);
+ return;
+ }
+
registrar.register(new ImportClassFix(ref));
registrar.register(new StaticImportConstantFix(ref));
registrar.register(QuickFixFactory.getInstance().createSetupJDKFix());
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packageDependencies.DependencyValidationManager;
import com.intellij.psi.*;
+import com.intellij.psi.impl.source.PsiJavaModuleReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.Nullable;
import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* @author cdr
Project project = psiElement.getProject();
PsiFile containingFile = psiElement.getContainingFile();
if (containingFile == null) return null;
- VirtualFile classVFile = containingFile.getVirtualFile();
- if (classVFile == null) return null;
+ VirtualFile refVFile = containingFile.getVirtualFile();
+ if (refVFile == null) return null;
- final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
- final Module currentModule = fileIndex.getModuleForFile(classVFile);
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+ Module currentModule = fileIndex.getModuleForFile(refVFile);
if (currentModule == null) return null;
+ if (reference instanceof PsiJavaModuleReference) {
+ List<LocalQuickFix> result = ContainerUtil.newSmartList();
+ createModuleFixes((PsiJavaModuleReference)reference, currentModule, refVFile, result);
+ result.forEach(fix -> registrar.register((IntentionAction)fix));
+ return result;
+ }
+
List<LocalQuickFix> result = ContainerUtil.newSmartList();
JavaPsiFacade facade = JavaPsiFacade.getInstance(psiElement.getProject());
return result;
}
- Set<Object> librariesToAdd = new THashSet<>();
PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(shortReferenceName, GlobalSearchScope.allScope(project));
List<PsiClass> allowedDependencies = filterAllowedDependencies(psiElement, classes);
if (allowedDependencies.isEmpty()) {
return result;
}
- classes = allowedDependencies.toArray(new PsiClass[allowedDependencies.size()]);
- OrderEntryFix moduleDependencyFix = new AddModuleDependencyFix(currentModule, classVFile, classes, reference);
+ OrderEntryFix moduleDependencyFix = new AddModuleDependencyFix(currentModule, refVFile, allowedDependencies, reference);
registrar.register(moduleDependencyFix);
result.add(moduleDependencyFix);
- for (final PsiClass aClass : classes) {
+ Set<Object> librariesToAdd = new THashSet<>();
+ ModuleFileIndex moduleFileIndex = ModuleRootManager.getInstance(currentModule).getFileIndex();
+ for (PsiClass aClass : allowedDependencies) {
if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) continue;
PsiFile psiFile = aClass.getContainingFile();
if (psiFile == null) continue;
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile == null) continue;
- ModuleFileIndex moduleFileIndex = ModuleRootManager.getInstance(currentModule).getFileIndex();
for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) {
if (orderEntry instanceof LibraryOrderEntry) {
final LibraryOrderEntry libraryEntry = (LibraryOrderEntry)orderEntry;
if (entryForFile != null &&
!(entryForFile instanceof ExportableOrderEntry &&
((ExportableOrderEntry)entryForFile).getScope() == DependencyScope.TEST &&
- !ModuleRootManager.getInstance(currentModule).getFileIndex().isInTestSourceContent(classVFile))) {
+ !moduleFileIndex.isInTestSourceContent(refVFile))) {
continue;
}
- final OrderEntryFix platformFix = new AddLibraryToDependenciesFix(currentModule, library, reference, aClass.getQualifiedName());
+
+ OrderEntryFix platformFix = new AddLibraryToDependenciesFix(currentModule, library, reference, aClass.getQualifiedName());
registrar.register(platformFix);
result.add(platformFix);
}
return result;
}
+ private static void createModuleFixes(PsiJavaModuleReference reference,
+ Module currentModule,
+ VirtualFile refVFile,
+ List<LocalQuickFix> result) {
+ ProjectFileIndex index = ProjectRootManager.getInstance(currentModule.getProject()).getFileIndex();
+ List<PsiElement> targets = Stream.of(reference.multiResolve(true))
+ .map(ResolveResult::getElement)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ Set<Module> modules = targets.stream()
+ .map(e -> !(e instanceof PsiCompiledElement) ? e.getContainingFile().getVirtualFile() : null)
+ .filter(vf -> vf != null && index.isInSource(vf))
+ .map(vf -> index.getModuleForFile(vf))
+ .filter(m -> m != null && m != currentModule)
+ .collect(Collectors.toSet());
+ if (!modules.isEmpty()) {
+ result.add(0, new AddModuleDependencyFix(currentModule, refVFile, modules, reference));
+ }
+
+ targets.stream()
+ .map(e -> e instanceof PsiCompiledElement ? e.getContainingFile().getVirtualFile() : null)
+ .flatMap(vf -> vf != null ? index.getOrderEntriesForFile(vf).stream() : Stream.empty())
+ .map(e -> e instanceof LibraryOrderEntry ? ((LibraryOrderEntry)e).getLibrary() : null)
+ .filter(Objects::nonNull)
+ .distinct()
+ .forEach(l -> result.add(new AddLibraryToDependenciesFix(currentModule, l, reference, null)));
+ }
+
private static void registerExternalFixes(@NotNull QuickFixActionRegistrar registrar,
@NotNull PsiReference reference,
PsiElement psiElement,
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
+import org.assertj.core.api.Assertions.assertThat
class ModuleHighlightingTest : LightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = MultiModuleJava9ProjectDescriptor
fun testFileDuplicate() {
addFile("pkg/module-info.java", """module M.bis { }""")
- doTest("""<error descr="'module-info.java' already exists in the module">module M</error> { }""")
+ highlight("""<error descr="'module-info.java' already exists in the module">module M</error> { }""")
}
fun testWrongFileLocation() {
fun testDuplicateStatements() {
addFile("pkg/main/C.java", "package pkg.main;\npublic class C { }")
addFile("pkg/main/Impl.java", "package pkg.main;\npublic class Impl extends C { }")
- doTest("""
+ highlight("""
module M {
requires M2;
<error descr="Duplicate requires: M2">requires M2;</error>
fun testUnusedStatements() {
addFile("pkg/main/C.java", "package pkg.main;\npublic class C { }")
addFile("pkg/main/Impl.java", "package pkg.main;\npublic class Impl extends C { }")
- doTest("""
+ highlight("""
module M {
provides pkg.main.<warning descr="Service interface provided but not exported or used">C</warning> with pkg.main.Impl;
}""".trimIndent(), true)
fun testRequires() {
addFile("module-info.java", "module M2 { requires M1 }", M2)
- doTest("""
+ highlight("""
module M1 {
requires <error descr="Module not found: M.missing">M.missing</error>;
requires <error descr="Cyclic dependence: M1">M1</error>;
addFile("pkg/empty/package-info.java", "package pkg.empty;")
addFile("pkg/main/C.java", "package pkg.main;\nclass C { }")
addFile("pkg/other/C.groovy", "package pkg.other\nclass C { }")
- doTest("""
+ highlight("""
module M {
exports pkg.<error descr="Cannot resolve symbol 'missing'">missing</error>;
exports <error descr="Package is empty: pkg.empty">pkg.empty</error>;
fun testUses() {
addFile("pkg/main/C.java", "package pkg.main;\nclass C { }")
addFile("pkg/main/E.java", "package pkg.main;\npublic enum E { }")
- doTest("""
+ highlight("""
module M {
uses pkg.<error descr="Cannot resolve symbol 'main'">main</error>;
uses pkg.main.<error descr="Cannot resolve symbol 'X'">X</error>;
addFile("pkg/main/Impl3.java", "package pkg.main;\npublic abstract class Impl3 implements C { }")
addFile("pkg/main/Impl4.java", "package pkg.main;\npublic class Impl4 implements C {\n public Impl4(String s) { }\n}")
addFile("pkg/main/Impl5.java", "package pkg.main;\npublic class Impl5 implements C {\n protected Impl5() { }\n}")
- doTest("""
+ highlight("""
module M {
provides pkg.main.C with pkg.main.<error descr="'pkg.main.Impl1' is not public in 'pkg.main'. Cannot be accessed from outside package">Impl1</error>;
provides pkg.main.C with pkg.main.<error descr="The service implementation type must be a subtype of the service interface type">Impl2</error>;
}""".trimIndent())
}
+ fun testModuleRefFixes() {
+ fixes("module M { requires <caret>M.missing; }")
+ fixes("module M { requires <caret>M3; }", "AddModuleDependencyFix")
+ }
+
+ fun testPackageRefFixes() {
+ fixes("module M { exports <caret>pkg.missing; }")
+ addFile("pkg/m3/C3.java", "package pkg.m3;\npublic class C3 { }", M3)
+ fixes("module M { exports <caret>pkg.m3; }")
+ }
+
+ fun testClassRefFixes() {
+ addFile("pkg/m3/C3.java", "package pkg.m3;\npublic class C3 { }", M3)
+ fixes("module M { uses <caret>pkg.m3.C3; }", "AddModuleDependencyFix")
+ }
+
//<editor-fold desc="Helpers.">
private fun addFile(path: String, text: String, module: ModuleDescriptor = MAIN) = VfsTestUtil.createFile(module.root(), path, text)
- private fun doTest(text: String, filter: Boolean = false) {
+ private fun highlight(text: String, filter: Boolean = false) {
myFixture.configureByText("module-info.java", text)
if (filter) {
(myFixture as CodeInsightTestFixtureImpl).setVirtualFileFilter { it.name != PsiJavaModule.MODULE_INFO_FILE }
}
myFixture.checkHighlighting()
}
+
+ private fun fixes(text: String, vararg fixes: String) {
+ myFixture.configureByText("module-info.java", text)
+ assertThat(myFixture.getAllQuickFixes().map { it.javaClass.simpleName }).containsExactlyInAnyOrder(*fixes)
+ }
//</editor-fold>
}
\ No newline at end of file
fix.add.special.annotation.text=Add ''{0}'' to special annotations list
orderEntry.fix.add.dependency.on.module=Add dependency on module ''{0}''
+orderEntry.fix.add.dependency.on.module.choose=Add dependency on module...
+orderEntry.fix.choose.module.to.add.dependency.on=Choose Module to Add Dependency on
orderEntry.fix.family.add.module.dependency=Add module dependency
orderEntry.fix.add.library.to.classpath=Add library ''{0}'' to classpath
orderEntry.fix.family.add.library.to.classpath=Add library to classpath