[java] module 'exports' package completion

This commit is contained in:
Roman Shevchenko
2016-08-25 20:51:25 +03:00
parent ddacc72c2d
commit ca0342f342
4 changed files with 60 additions and 19 deletions
@@ -34,6 +34,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.graph.Graph;
import org.jetbrains.annotations.NotNull;
@@ -161,7 +162,7 @@ public class ModuleHighlightUtil {
String message = JavaErrorMessages.message("package.not.found", packageName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
}
if (isEmpty(directories, packageName)) {
if (PsiUtil.isPackageEmpty(directories, packageName)) {
String message = JavaErrorMessages.message("package.is.empty", packageName);
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
}
@@ -227,18 +228,4 @@ public class ModuleHighlightUtil {
private static TextRange range(PsiJavaModule module) {
return new TextRange(module.getTextOffset(), module.getNameElement().getTextRange().getEndOffset());
}
private static boolean isEmpty(PsiDirectory[] directories, String packageName) {
for (PsiDirectory directory : directories) {
for (PsiFile file : directory.getFiles()) {
if (file instanceof PsiClassOwner &&
packageName.equals(((PsiClassOwner)file).getPackageName()) &&
((PsiClassOwner)file).getClasses().length > 0) {
return false;
}
}
}
return true;
}
}
@@ -19,8 +19,13 @@ import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.JavaKeywordCompletion.OverrideableSpace;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.module.impl.scopes.ModulesScope;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.file.impl.JavaFileManager;
import com.intellij.psi.impl.java.stubs.index.JavaModuleNameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
@@ -48,7 +53,10 @@ class JavaModuleCompletion {
addModuleStatementKeywords(position, result);
}
else if (context instanceof PsiJavaModuleReferenceElement) {
addModuleReferences(position, result);
addModuleReferences(context, result);
}
else if (context instanceof PsiJavaCodeReferenceElement) {
addCodeReferences(context, result);
}
}
}
@@ -66,11 +74,11 @@ class JavaModuleCompletion {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.PROVIDES), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
private static void addModuleReferences(PsiElement position, Consumer<LookupElement> result) {
PsiJavaModule host = PsiTreeUtil.getParentOfType(position, PsiJavaModule.class);
private static void addModuleReferences(PsiElement context, Consumer<LookupElement> result) {
PsiJavaModule host = PsiTreeUtil.getParentOfType(context, PsiJavaModule.class);
if (host != null) {
String hostName = host.getModuleName();
Project project = position.getProject();
Project project = context.getProject();
JavaModuleNameIndex index = JavaModuleNameIndex.getInstance();
GlobalSearchScope scope = ProjectScope.getAllScope(project);
index.processAllKeys(project, name -> {
@@ -81,4 +89,25 @@ class JavaModuleCompletion {
});
}
}
private static void addCodeReferences(PsiElement context, Consumer<LookupElement> result) {
PsiElement statement = PsiTreeUtil.skipParentsOfType(context, PsiJavaCodeReferenceElement.class);
if (statement instanceof PsiExportsStatement) {
Module module = ModuleUtilCore.findModuleForPsiElement(context);
PsiPackage topPackage = ServiceManager.getService(context.getProject(), JavaFileManager.class).findPackage("");
if (module != null && topPackage != null) {
processPackage(topPackage, new ModulesScope(module), result);
}
}
}
private static void processPackage(PsiPackage pkg, ModulesScope scope, Consumer<LookupElement> result) {
String packageName = pkg.getQualifiedName();
if (packageName.indexOf('.') > 0 && !PsiUtil.isPackageEmpty(pkg.getDirectories(scope), packageName)) {
result.consume(new OverrideableSpace(LookupElementBuilder.create(packageName), TailType.SEMICOLON));
}
for (PsiPackage subPackage : pkg.getSubPackages(scope)) {
processPackage(subPackage, scope, result);
}
}
}
@@ -1277,4 +1277,18 @@ public final class PsiUtil extends PsiUtilCore {
public static boolean isModuleFile(@NotNull PsiFile file) {
return file instanceof PsiJavaFile && ((PsiJavaFile)file).getModuleDeclaration() != null;
}
public static boolean isPackageEmpty(@NotNull PsiDirectory[] directories, @NotNull String packageName) {
for (PsiDirectory directory : directories) {
for (PsiFile file : directory.getFiles()) {
if (file instanceof PsiClassOwner &&
packageName.equals(((PsiClassOwner)file).getPackageName()) &&
((PsiClassOwner)file).getClasses().length > 0) {
return false;
}
}
}
return true;
}
}
@@ -15,8 +15,10 @@
*/
package com.intellij.codeInsight.completion
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.MultiModuleJava9ProjectDescriptor
import com.intellij.testFramework.VfsTestUtil
import org.assertj.core.api.Assertions.assertThat
class ModuleCompletionTest : LightFixtureCompletionTestCase() {
@@ -27,7 +29,16 @@ class ModuleCompletionTest : LightFixtureCompletionTestCase() {
fun testStatements2() = complete("module M { requires X; ex<caret> }", "module M { requires X; exports <caret> }")
fun testModuleRef() = complete("module M { requires M<caret> }", "module M { requires M2;<caret> }")
fun testExports() {
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 { }")
variants("module M { exports pkg.<caret> }", "pkg.main", "pkg.other")
}
//<editor-fold desc="Helpers.">
private fun addFile(path: String, text: String) = VfsTestUtil.createFile(LightPlatformTestCase.getSourceRoot(), path, text)
private fun complete(text: String, expected: String) {
myFixture.configureByText("module-info.java", text)
myFixture.completeBasic()