From 81fba80b5e7a15bd6998cfae87eddaf621f289f9 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 31 Jul 2020 15:41:45 +0700 Subject: [PATCH] [java-completion] IDEA-247109 Complete top-level pubic class name based on the file name GitOrigin-RevId: 867955fe94a6137a21ccd7a46d4a6bf615e75924 --- .../completion/JavaKeywordCompletion.java | 75 ++++++++++++++++++- .../completion/keywords/classScope1.java | 2 +- .../SwitchCaseWithEnumConstant_after.java | 2 +- .../normal/TopLevelPublicClass.java | 1 + .../TopLevelPublicClassBraceExists.java | 1 + .../TopLevelPublicClassBraceExists_after.java | 1 + .../TopLevelPublicClassIdentifierExists.java | 1 + ...evelPublicClassIdentifierExists_after.java | 1 + .../normal/TopLevelPublicClass_after.java | 2 + .../normal/TopLevelPublicRecord.java | 1 + .../TopLevelPublicRecordBraceExists.java | 1 + ...TopLevelPublicRecordBraceExists_after.java | 1 + ...TopLevelPublicRecordParenthesisExists.java | 2 + ...elPublicRecordParenthesisExists_after.java | 2 + .../normal/TopLevelPublicRecord_after.java | 2 + .../completion/NormalCompletionTest.groovy | 12 ++- 16 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java index 93a1fb81f881..a43fc5943713 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaKeywordCompletion.java @@ -7,8 +7,12 @@ import com.intellij.codeInsight.TailTypes; import com.intellij.codeInsight.completion.util.CompletionStyleUtil; import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler; import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature; +import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind; import com.intellij.codeInsight.lookup.*; +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.Conditions; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.ElementPattern; import com.intellij.patterns.PsiElementPattern; import com.intellij.pom.java.LanguageLevel; @@ -17,6 +21,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.filters.FilterPositionUtil; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.templateLanguages.OuterLanguageElement; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -24,9 +29,11 @@ import com.intellij.util.Consumer; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Set; import static com.intellij.openapi.util.Conditions.notInstanceOf; @@ -557,13 +564,22 @@ public class JavaKeywordCompletion { } if (PsiTreeUtil.getParentOfType(myPosition, PsiExpression.class, true, PsiMember.class) == null && PsiTreeUtil.getParentOfType(myPosition, PsiCodeBlock.class, true, PsiMember.class) == null) { - addKeyword(new OverridableSpace(createKeyword(PsiKeyword.CLASS), TailType.HUMBLE_SPACE_BEFORE_WORD)); - addKeyword(new OverridableSpace(createKeyword(PsiKeyword.INTERFACE), TailType.HUMBLE_SPACE_BEFORE_WORD)); + List keywords = new ArrayList<>(); + keywords.add(PsiKeyword.CLASS); + keywords.add(PsiKeyword.INTERFACE); if (HighlightingFeature.RECORDS.isAvailable(myPosition)) { - addKeyword(new OverridableSpace(createKeyword(PsiKeyword.RECORD), TailType.HUMBLE_SPACE_BEFORE_WORD)); + keywords.add(PsiKeyword.RECORD); } if (PsiUtil.isLanguageLevel5OrHigher(myPosition)) { - addKeyword(new OverridableSpace(createKeyword(PsiKeyword.ENUM), TailType.INSERT_SPACE)); + keywords.add(PsiKeyword.ENUM); + } + String className = recommendClassName(); + for (String keyword : keywords) { + if (className == null) { + addKeyword(new OverridableSpace(createKeyword(keyword), TailType.HUMBLE_SPACE_BEFORE_WORD)); + } else { + addKeyword(createTypeDeclaration(keyword, className)); + } } } } @@ -574,6 +590,57 @@ public class JavaKeywordCompletion { } } + @NotNull + private LookupElement createTypeDeclaration(String keyword, String className) { + LookupElement element; + PsiElement nextElement = PsiTreeUtil.skipWhitespacesAndCommentsForward(PsiTreeUtil.nextLeaf(myPosition)); + IElementType nextToken = nextElement instanceof PsiJavaToken ? ((PsiJavaToken)nextElement).getTokenType() : null; + element = LookupElementBuilder.create(keyword + " " + className).withPresentableText(keyword).bold() + .withTailText(" " + className, false) + .withIcon(CreateClassKind.valueOf(keyword.toUpperCase(Locale.ROOT)).getKindIcon()) + .withInsertHandler((context, item) -> { + Document document = context.getDocument(); + int offset = context.getTailOffset(); + String suffix = " "; + if (keyword.equals(PsiKeyword.RECORD)) { + if (JavaTokenType.LPARENTH.equals(nextToken)) { + suffix = ""; + } + else if (JavaTokenType.LBRACE.equals(nextToken)) { + suffix = "() "; + } + else { + suffix = "() {\n}"; + } + } + else if (!JavaTokenType.LBRACE.equals(nextToken)) { + suffix = " {\n}"; + } + if (offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == ' ') { + suffix = suffix.trim(); + } + document.insertString(offset, suffix); + context.getEditor().getCaretModel().moveToOffset(offset + 1); + }); + return element; + } + + @Nullable + private String recommendClassName() { + if (myPrevLeaf == null) return null; + if (!myPrevLeaf.textMatches(PsiKeyword.PUBLIC) || !(myPrevLeaf.getParent() instanceof PsiModifierList)) return null; + if (PsiTreeUtil.skipWhitespacesAndCommentsForward(PsiTreeUtil.nextLeaf(myPosition)) instanceof PsiIdentifier) return null; + PsiJavaFile file = ObjectUtils.tryCast(myPrevLeaf.getParent().getParent(), PsiJavaFile.class); + if (file == null) return null; + String name = file.getName(); + if (!StringUtil.endsWithIgnoreCase(name, JavaFileType.DOT_DEFAULT_EXTENSION)) return null; + String candidate = name.substring(0, name.length() - JavaFileType.DOT_DEFAULT_EXTENSION.length()); + if (StringUtil.isJavaIdentifier(candidate) && !ContainerUtil.exists(file.getClasses(), c -> candidate.equals(c.getName()))) { + return candidate; + } + return null; + } + private void addClassLiteral() { if (isAfterTypeDot(myPosition)) { addKeyword(createKeyword(PsiKeyword.CLASS)); diff --git a/java/java-tests/testData/codeInsight/completion/keywords/classScope1.java b/java/java-tests/testData/codeInsight/completion/keywords/classScope1.java index ed5717c06b9f..aec007f743c3 100644 --- a/java/java-tests/testData/codeInsight/completion/keywords/classScope1.java +++ b/java/java-tests/testData/codeInsight/completion/keywords/classScope1.java @@ -1 +1 @@ -public +public Cls diff --git a/java/java-tests/testData/codeInsight/completion/normal/SwitchCaseWithEnumConstant_after.java b/java/java-tests/testData/codeInsight/completion/normal/SwitchCaseWithEnumConstant_after.java index c6ed04eeda4e..43f8fcb29338 100644 --- a/java/java-tests/testData/codeInsight/completion/normal/SwitchCaseWithEnumConstant_after.java +++ b/java/java-tests/testData/codeInsight/completion/normal/SwitchCaseWithEnumConstant_after.java @@ -2,7 +2,7 @@ class A extends Base{ void foo(){ B x = null; switch(x){ - case GOO: + case GOO -> } } } diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass.java new file mode 100644 index 000000000000..404d09fbd960 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass.java @@ -0,0 +1 @@ +public cl \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists.java new file mode 100644 index 000000000000..e524613eca77 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists.java @@ -0,0 +1 @@ +public cl {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists_after.java new file mode 100644 index 000000000000..5876f1402811 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassBraceExists_after.java @@ -0,0 +1 @@ +public class TopLevelPublicClassBraceExists {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists.java new file mode 100644 index 000000000000..f2a1d4066bf2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists.java @@ -0,0 +1 @@ +public cl MyClass \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists_after.java new file mode 100644 index 000000000000..192a808e43a8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClassIdentifierExists_after.java @@ -0,0 +1 @@ +public class MyClass \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass_after.java new file mode 100644 index 000000000000..2fbe279f5333 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicClass_after.java @@ -0,0 +1,2 @@ +public class TopLevelPublicClass { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord.java new file mode 100644 index 000000000000..49065f7ae5e4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord.java @@ -0,0 +1 @@ +public re \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists.java new file mode 100644 index 000000000000..3cb13d15fe5c --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists.java @@ -0,0 +1 @@ +public re {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists_after.java new file mode 100644 index 000000000000..6b7db0669d94 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordBraceExists_after.java @@ -0,0 +1 @@ +public record TopLevelPublicRecordBraceExists() {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists.java new file mode 100644 index 000000000000..ad05e9cb94ac --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists.java @@ -0,0 +1,2 @@ +public re() { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists_after.java new file mode 100644 index 000000000000..f7660a96dfc3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecordParenthesisExists_after.java @@ -0,0 +1,2 @@ +public record TopLevelPublicRecordParenthesisExists() { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord_after.java b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord_after.java new file mode 100644 index 000000000000..2e535dc647d1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/TopLevelPublicRecord_after.java @@ -0,0 +1,2 @@ +public record TopLevelPublicRecord() { +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy index 4c2d8d630030..5b2c3154b6b9 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.groovy @@ -32,7 +32,7 @@ class NormalCompletionTest extends NormalCompletionTestCase { @NotNull @Override protected LightProjectDescriptor getProjectDescriptor() { - return JAVA_9 + return JAVA_15 } void testSimple() throws Exception { @@ -954,7 +954,7 @@ public class ListUtils { @NeedsIndex.ForStandardLibrary void testFinalInForLoop2() throws Throwable { configure() - myFixture.assertPreferredCompletionItems 0, 'finalize', 'final' + myFixture.assertPreferredCompletionItems 1, 'finalize', 'final' } void testOnlyClassesInExtends() throws Throwable { @@ -2161,4 +2161,12 @@ class Abc { selectItem(myItems[1]) checkResult() } + + void testTopLevelPublicClass() { doTest() } + void testTopLevelPublicClassIdentifierExists() { doTest() } + void testTopLevelPublicClassBraceExists() { doTest() } + + void testTopLevelPublicRecord() { doTest() } + void testTopLevelPublicRecordParenthesisExists() { doTest() } + void testTopLevelPublicRecordBraceExists() { doTest() } }