[java] module reference completion

This commit is contained in:
Roman Shevchenko
2016-08-25 12:00:24 +03:00
parent 0670fd3af0
commit c6d7c4eda0
4 changed files with 90 additions and 43 deletions
@@ -18,7 +18,12 @@ package com.intellij.codeInsight.completion;
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.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaModuleNameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
@@ -36,24 +41,44 @@ class JavaModuleCompletion {
PsiElement context = position.getParent();
if (context instanceof PsiErrorElement) context = context.getParent();
addFileHeaderKeywords(position, context, result);
addModuleStatementKeywords(position, context, result);
if (context instanceof PsiJavaFile) {
addFileHeaderKeywords(position, result);
}
else if (context instanceof PsiJavaModule) {
addModuleStatementKeywords(position, result);
}
else if (context instanceof PsiJavaModuleReferenceElement) {
addModuleReferences(position, result);
}
}
}
private static void addFileHeaderKeywords(PsiElement position, PsiElement context, Consumer<LookupElement> result) {
if (context instanceof PsiJavaFile && PsiTreeUtil.prevVisibleLeaf(position) == null) {
private static void addFileHeaderKeywords(PsiElement position, Consumer<LookupElement> result) {
if (PsiTreeUtil.prevVisibleLeaf(position) == null) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.MODULE), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
private static void addModuleStatementKeywords(PsiElement position, PsiElement context, Consumer<LookupElement> result) {
if (context instanceof PsiJavaModule) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.REQUIRES), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXPORTS), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.USES), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.PROVIDES), TailType.HUMBLE_SPACE_BEFORE_WORD));
private static void addModuleStatementKeywords(PsiElement position, Consumer<LookupElement> result) {
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.REQUIRES), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.EXPORTS), TailType.HUMBLE_SPACE_BEFORE_WORD));
result.consume(new OverrideableSpace(createKeyword(position, PsiKeyword.USES), TailType.HUMBLE_SPACE_BEFORE_WORD));
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);
if (host != null) {
String hostName = host.getModuleName();
Project project = position.getProject();
JavaModuleNameIndex index = JavaModuleNameIndex.getInstance();
GlobalSearchScope scope = ProjectScope.getAllScope(project);
index.processAllKeys(project, name -> {
if (!name.equals(hostName) && index.get(name, project, scope).size() == 1) {
result.consume(new OverrideableSpace(LookupElementBuilder.create(name), TailType.SEMICOLON));
}
return true;
});
}
}
}
@@ -16,15 +16,18 @@
package com.intellij.codeInsight.completion
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.MultiModuleJava9ProjectDescriptor
import org.assertj.core.api.Assertions.assertThat
class ModuleCompletionTest : LightFixtureCompletionTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_9
override fun getProjectDescriptor(): LightProjectDescriptor = MultiModuleJava9ProjectDescriptor
fun testFileHeader() = complete("<caret>", "module <caret>")
fun testStatements1() = variants("module M { <caret> }", "requires", "exports", "uses", "provides")
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> }")
//<editor-fold desc="Helpers.">
private fun complete(text: String, expected: String) {
myFixture.configureByText("module-info.java", text)
myFixture.completeBasic()
@@ -36,4 +39,5 @@ class ModuleCompletionTest : LightFixtureCompletionTestCase() {
val result = myFixture.completeBasic()?.map { it.lookupString }
assertThat(result).containsExactlyInAnyOrder(*variants)
}
//</editor-fold>
}
@@ -16,24 +16,16 @@
package com.intellij.codeInsight.daemon
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.LanguageLevelModuleExtension
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.PsiJavaModule
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
import com.intellij.testFramework.MultiModuleJava9ProjectDescriptor
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
class ModuleHighlightingTest : LightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = DESCRIPTOR
override fun getProjectDescriptor(): LightProjectDescriptor = MultiModuleJava9ProjectDescriptor
fun testWrongFileName() {
myFixture.configureByText("M.java", """/* ... */ <error descr="Module declaration should be in a file named 'module-info.java'">module M</error> { }""")
@@ -67,27 +59,6 @@ class ModuleHighlightingTest : LightCodeInsightFixtureTestCase() {
}
//<editor-fold desc="Helpers.">
companion object {
private val DESCRIPTOR = object : DefaultLightProjectDescriptor() {
override fun getSdk() = IdeaTestUtil.getMockJdk18()
override fun setUpProject(project: Project, handler: SetupHandler) {
super.setUpProject(project, handler)
runWriteAction {
val m2 = createModule(project, FileUtil.join(FileUtil.getTempDirectory(), "light_idea_test_case_m2.iml"))
val src2 = createSourceRoot(m2, "src2")
createContentEntry(m2, src2)
VfsUtil.saveText(src2.createChildData(this, "module-info.java"), "module M2 { requires M1; }")
}
}
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
model.getModuleExtension(LanguageLevelModuleExtension::class.java).languageLevel = LanguageLevel.JDK_1_9
}
}
}
private fun additionalFile(text: String) = myFixture.configureFromExistingVirtualFile(runWriteAction {
val file = LightPlatformTestCase.getSourceRoot().createChildDirectory(this, "pkg").createChildData(this, "module-info.java")
VfsUtil.saveText(file, text)
@@ -0,0 +1,47 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.testFramework
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.LanguageLevelModuleExtension
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() {
override fun getSdk(): Sdk = IdeaTestUtil.getMockJdk18()
override fun setUpProject(project: Project, handler: SetupHandler) {
super.setUpProject(project, handler)
runWriteAction {
val m2 = createModule(project, FileUtil.join(FileUtil.getTempDirectory(), "light_idea_test_case_m2.iml"))
val src2 = createSourceRoot(m2, "src2")
createContentEntry(m2, src2)
VfsUtil.saveText(src2.createChildData(this, "module-info.java"), "module M2 { requires M1; }")
}
}
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
model.getModuleExtension(LanguageLevelModuleExtension::class.java).languageLevel = LanguageLevel.JDK_1_9
}
}