From c6d7c4eda0e6038461cc7b1024e9e3cebdb2883c Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 25 Aug 2016 12:00:24 +0300 Subject: [PATCH] [java] module reference completion --- .../completion/JavaModuleCompletion.java | 47 ++++++++++++++----- .../completion/ModuleCompletionTest.kt | 6 ++- .../daemon/ModuleHighlightingTest.kt | 33 +------------ .../MultiModuleJava9ProjectDescriptor.kt | 47 +++++++++++++++++++ 4 files changed, 90 insertions(+), 43 deletions(-) create mode 100644 java/testFramework/src/com/intellij/testFramework/MultiModuleJava9ProjectDescriptor.kt diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java index 414fd4fed51f..fe18d6ad4907 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaModuleCompletion.java @@ -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 result) { - if (context instanceof PsiJavaFile && PsiTreeUtil.prevVisibleLeaf(position) == null) { + private static void addFileHeaderKeywords(PsiElement position, Consumer 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 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 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 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; + }); } } } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/ModuleCompletionTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/completion/ModuleCompletionTest.kt index ddd3720613fc..f5289cf78fdd 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/ModuleCompletionTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/ModuleCompletionTest.kt @@ -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("", "module ") fun testStatements1() = variants("module M { }", "requires", "exports", "uses", "provides") fun testStatements2() = complete("module M { requires X; ex }", "module M { requires X; exports }") + fun testModuleRef() = complete("module M { requires M }", "module M { requires M2; }") + // 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) } + // } \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt index 4044be749da8..67378986466b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt @@ -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", """/* ... */ module M { }""") @@ -67,27 +59,6 @@ class ModuleHighlightingTest : LightCodeInsightFixtureTestCase() { } // - 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) diff --git a/java/testFramework/src/com/intellij/testFramework/MultiModuleJava9ProjectDescriptor.kt b/java/testFramework/src/com/intellij/testFramework/MultiModuleJava9ProjectDescriptor.kt new file mode 100644 index 000000000000..687a04ced041 --- /dev/null +++ b/java/testFramework/src/com/intellij/testFramework/MultiModuleJava9ProjectDescriptor.kt @@ -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 + } +} \ No newline at end of file