[java-completion] IDEA-247109 Complete top-level pubic class name based on the file name

GitOrigin-RevId: 867955fe94a6137a21ccd7a46d4a6bf615e75924
This commit is contained in:
Tagir Valeev
2020-07-31 09:34:30 +00:00
committed by intellij-monorepo-bot
parent 44b38beea9
commit 81fba80b5e
16 changed files with 99 additions and 8 deletions
@@ -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<String> 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));
@@ -1 +1 @@
public <caret>
public <caret> Cls
@@ -2,7 +2,7 @@ class A extends Base{
void foo(){
B x = null;
switch(x){
case GOO:<caret>
case GOO -> <caret>
}
}
}
@@ -0,0 +1 @@
public cl<caret>
@@ -0,0 +1 @@
public cl<caret> {}
@@ -0,0 +1 @@
public class TopLevelPublicClassBraceExists <caret>{}
@@ -0,0 +1 @@
public cl<caret> MyClass
@@ -0,0 +1 @@
public class<caret> MyClass
@@ -0,0 +1,2 @@
public class TopLevelPublicClass <caret>{
}
@@ -0,0 +1 @@
public re<caret>
@@ -0,0 +1 @@
public re<caret> {}
@@ -0,0 +1 @@
public record TopLevelPublicRecordBraceExists(<caret>) {}
@@ -0,0 +1,2 @@
public re<caret>() {
}
@@ -0,0 +1,2 @@
public record TopLevelPublicRecordParenthesisExists(<caret>) {
}
@@ -0,0 +1,2 @@
public record TopLevelPublicRecord(<caret>) {
}
@@ -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() }
}