From 000f447107c3f39b2dc52579a18c1d6d5a0772fd Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 27 Oct 2023 15:15:16 +0200 Subject: [PATCH] [java-completion] Insert comma automatically when completing a new annotation attribute Fixes IDEA-336370 Autocompletion should be smarter for annotations GitOrigin-RevId: aa6efeb2b35f6ea242ed43cc1cb05aeceefe6d5c --- .../completion/JavaCompletionContributor.java | 27 +++++++++++++++++-- .../normal/AnnotationAttrBeforeExisting.java | 9 +++++++ .../AnnotationAttrBeforeExistingBool.java | 9 +++++++ ...nnotationAttrBeforeExistingBool_after.java | 9 +++++++ .../AnnotationAttrBeforeExisting_after.java | 9 +++++++ .../completion/NormalCompletionTest.java | 8 ++++++ 6 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool_after.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index e9062b17c9ad..c08444f5dfc8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -20,6 +20,7 @@ import com.intellij.lang.jvm.types.JvmPrimitiveTypeKind; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; @@ -39,6 +40,7 @@ import com.intellij.patterns.PsiJavaElementPattern; import com.intellij.patterns.PsiNameValuePairPattern; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.filters.*; import com.intellij.psi.filters.classes.AnnotationTypeFilter; import com.intellij.psi.filters.classes.AssignableFromContextFilter; @@ -1014,7 +1016,8 @@ public final class JavaCompletionContributor extends CompletionContributor imple private static LookupElementBuilder createAnnotationAttributeElement(@NotNull PsiMethod annoMethod, @Nullable String value, @NotNull PsiElement position) { - String space = ReferenceExpressionCompletionContributor.getSpace(CodeStyle.getLanguageSettings(annoMethod.getContainingFile()).SPACE_AROUND_ASSIGNMENT_OPERATORS); + CommonCodeStyleSettings styleSettings = CodeStyle.getLanguageSettings(annoMethod.getContainingFile()); + String space = ReferenceExpressionCompletionContributor.getSpace(styleSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS); String lookupString = annoMethod.getName() + (value == null ? "" : space + "=" + space + value); return LookupElementBuilder.create(annoMethod, lookupString).withIcon(annoMethod.getIcon(0)) .withStrikeoutness(JavaDeprecationUtils.isDeprecated(annoMethod, position)) @@ -1028,14 +1031,34 @@ public final class JavaCompletionContributor extends CompletionContributor imple context.commitDocument(); PsiAnnotationParameterList paramList = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiAnnotationParameterList.class, false); + Document document = context.getDocument(); if (paramList != null && paramList.getAttributes().length > 0 && paramList.getAttributes()[0].getName() == null) { int valueOffset = paramList.getAttributes()[0].getTextRange().getStartOffset(); - context.getDocument().insertString(valueOffset, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); + document.insertString(valueOffset, PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); EqTailType.INSTANCE.processTail(editor, valueOffset + PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.length()); } + int offset = editor.getCaretModel().getOffset(); + CharSequence sequence = document.getCharsSequence(); + if (hasAttributeNameAt(sequence, offset)) { + document.insertString(offset, styleSettings.SPACE_AFTER_COMMA ? ", " : ","); + } }); } + private static boolean hasAttributeNameAt(@NotNull CharSequence sequence, int offset) { + int length = sequence.length(); + if (length <= offset) return false; + char nextChar = sequence.charAt(offset); + if (!StringUtil.isJavaIdentifierStart(nextChar)) return false; + while (offset < length - 1 && StringUtil.isJavaIdentifierPart(sequence.charAt(offset + 1))) { + offset++; + } + while (offset < length - 1 && StringUtil.isWhiteSpace(sequence.charAt(offset + 1))) { + offset++; + } + return offset < length - 1 && sequence.charAt(offset + 1) == '='; + } + @Override public String advertise(@NotNull CompletionParameters parameters) { if (!(parameters.getOriginalFile() instanceof PsiJavaFile)) return null; diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting.java new file mode 100644 index 000000000000..3d01322eb4c6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting.java @@ -0,0 +1,9 @@ +@interface Anno { + String attr(); + int existing(); +} + +@Anno(attexisting = 2) +class Cls { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool.java new file mode 100644 index 000000000000..c9bc90887698 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool.java @@ -0,0 +1,9 @@ +@interface Anno { + boolean attr() default true; + int existing(); +} + +@Anno(attexisting = 2) +class Cls { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool_after.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool_after.java new file mode 100644 index 000000000000..c6f050f05227 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExistingBool_after.java @@ -0,0 +1,9 @@ +@interface Anno { + boolean attr() default true; + int existing(); +} + +@Anno(attr = false, existing = 2) +class Cls { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting_after.java b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting_after.java new file mode 100644 index 000000000000..075b166ff71e --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/AnnotationAttrBeforeExisting_after.java @@ -0,0 +1,9 @@ +@interface Anno { + String attr(); + int existing(); +} + +@Anno(attr = , existing = 2) +class Cls { + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.java index 965297613e80..830fbcb11e4d 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/NormalCompletionTest.java @@ -342,6 +342,14 @@ public class NormalCompletionTest extends NormalCompletionTestCase { selectItem(myItems[0]); checkResultByFile("Annotation7_after.java"); } + + public void testAnnotationAttrBeforeExisting() { + doTest("\n"); + } + + public void testAnnotationAttrBeforeExistingBool() { + doTest("\n"); + } public void testEnumInAnnotation() { configureByFile("Annotation4.java");