From 3ba76e26e352ab4a1b9bfeee2943ccfac51c5e78 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Fri, 19 Aug 2022 15:18:55 +0200 Subject: [PATCH] [java] .new postfix template: support qualified references Fixes IDEA-300278 'new' postfix doesn't work well for nested classes GitOrigin-RevId: 3b0b7a2e378dcf386b1813d7ec8af247379d2736 --- .../NewExpressionPostfixTemplate.java | 81 ++++++++----------- .../postfix/templates/new/newFQN.java | 8 ++ .../postfix/templates/new/newFQNConflict.java | 10 +++ .../templates/new/newFQNConflict_after.java | 10 +++ .../postfix/templates/new/newFQNImport.java | 10 +++ .../templates/new/newFQNImport_after.java | 12 +++ .../postfix/templates/new/newFQN_after.java | 8 ++ .../postfix/templates/new/newNestedClass.java | 13 +++ .../templates/new/newNestedClass2.java | 13 +++ .../templates/new/newNestedClass2_after.java | 13 +++ .../templates/new/newNestedClass_after.java | 13 +++ .../NewExpressionPostfixTemplateTest.java | 20 +++++ 12 files changed, 165 insertions(+), 46 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict_after.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport_after.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN_after.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2_after.java create mode 100644 java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NewExpressionPostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NewExpressionPostfixTemplate.java index 6e992494bbfa..c5643181c51e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NewExpressionPostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NewExpressionPostfixTemplate.java @@ -19,36 +19,26 @@ import static com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplate public class NewExpressionPostfixTemplate extends StringBasedPostfixTemplate { private static final Condition CONSTRUCTOR = expression -> { - if (!(expression instanceof PsiReferenceExpression) && - !(expression instanceof PsiMethodCallExpression)) { - return false; - } + PsiReferenceExpression ref = expression instanceof PsiMethodCallExpression call ? call.getMethodExpression() : + expression instanceof PsiReferenceExpression r ? r : + null; + if (ref == null) return false; - PsiReferenceExpression ref = expression instanceof PsiMethodCallExpression ? - ((PsiMethodCallExpression)expression).getMethodExpression() : - (PsiReferenceExpression)expression; - - PsiExpression qualifierExpression = ref.getQualifierExpression(); - - //disabled for qualified elements - //todo implement proper support for Foo, Foo.new Bar() and java.util.Foo - if (qualifierExpression != null) return false; + PsiExpression qualifier = ref.getQualifierExpression(); JavaResolveResult result = ref.advancedResolve(true); - PsiElement element = result.getElement(); - if (element != null && !(element instanceof PsiClass)) return false; - if (element != null) { - PsiMethod[] constructors = ((PsiClass)element).getConstructors(); - if (constructors.length > 0) { - PsiResolveHelper helper = JavaPsiFacade.getInstance(element.getProject()).getResolveHelper(); - if (ContainerUtil.and(constructors, m -> !helper.isAccessible(m, ref, (PsiClass)element))) { - // All constructors aren't accessible - return false; - } - } - } - return true; + + //todo implement proper support for Foo, Foo.new Bar() + if (qualifier != null && (!(qualifier instanceof PsiReferenceExpression) || element == null)) return false; + + if (element == null) return true; + if (!(element instanceof PsiClass cls)) return false; + PsiMethod[] constructors = cls.getConstructors(); + if (constructors.length == 0) return true; + PsiResolveHelper helper = JavaPsiFacade.getInstance(element.getProject()).getResolveHelper(); + // Check whether there's at least one accessible constructor + return !ContainerUtil.and(constructors, m -> !helper.isAccessible(m, ref, cls)); }; protected NewExpressionPostfixTemplate() { @@ -63,20 +53,16 @@ public class NewExpressionPostfixTemplate extends StringBasedPostfixTemplate { @Override public void expandForChooseExpression(@NotNull PsiElement expression, @NotNull Editor editor) { - if (!(expression instanceof PsiReferenceExpression)) { - super.expandForChooseExpression(expression, editor); - return; - } - PsiReferenceExpression refExpression = (PsiReferenceExpression)expression; - JavaResolveResult result = refExpression.advancedResolve(true); - PsiElement element = result.getElement(); + if (expression instanceof PsiReferenceExpression ref) { + JavaResolveResult result = ref.advancedResolve(true); + PsiElement element = result.getElement(); - if (!(element instanceof PsiClass)) { - super.expandForChooseExpression(expression, editor); - return; + if (element instanceof PsiClass psiClass) { + WriteAction.run(() -> insertConstructorCallWithSmartBraces(expression, editor, psiClass)); + return; + } } - - WriteAction.run(() -> insertConstructorCallWithSmartBraces(expression, editor, (PsiClass)element)); + super.expandForChooseExpression(expression, editor); } public void insertConstructorCallWithSmartBraces(@NotNull PsiElement expression, @@ -87,7 +73,7 @@ public class NewExpressionPostfixTemplate extends StringBasedPostfixTemplate { Project project = expression.getProject(); SmartPsiElementPointer pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(javaClass); - replaceExpressionTextByNewExpressionInDocument(project, expression, document); + int startOffset = replaceExpressionTextByNewExpressionInDocument(project, expression, document); if (!javaClass.isValid()) { javaClass = pointer.getElement(); @@ -97,13 +83,14 @@ public class NewExpressionPostfixTemplate extends StringBasedPostfixTemplate { JavaPsiClassReferenceElement item = JavaClassNameCompletionContributor.createClassLookupItem(javaClass, true); - item.handleInsert(createInsertionContext(editor, file, item)); + item.handleInsert(createInsertionContext(editor, file, item, startOffset)); } @NotNull private static InsertionContext createInsertionContext(@NotNull Editor editor, @NotNull PsiFile file, - @NotNull JavaPsiClassReferenceElement item) { + @NotNull JavaPsiClassReferenceElement item, + int startOffset) { Document document = editor.getDocument(); final OffsetMap offsetMap = new OffsetMap(document); final InsertionContext insertionContext = new InsertionContext(offsetMap, @@ -112,15 +99,17 @@ public class NewExpressionPostfixTemplate extends StringBasedPostfixTemplate { file, editor, false); int offset = editor.getCaretModel().getOffset(); - return CompletionUtil.newContext(insertionContext, item, offset, offset); + return CompletionUtil.newContext(insertionContext, item, startOffset, offset); } - private static void replaceExpressionTextByNewExpressionInDocument(@NotNull Project project, - @NotNull PsiElement expression, - @NotNull Document document) { + private static int replaceExpressionTextByNewExpressionInDocument(@NotNull Project project, + @NotNull PsiElement expression, + @NotNull Document document) { TextRange range = expression.getTextRange(); - document.replaceString(range.getStartOffset(), range.getEndOffset(), "new " + expression.getText()); + String newPrefix = "new "; + document.replaceString(range.getStartOffset(), range.getEndOffset(), newPrefix + expression.getText()); PsiDocumentManager.getInstance(project).commitDocument(document); + return range.getStartOffset() + newPrefix.length(); } } diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN.java new file mode 100644 index 000000000000..d79a38e93729 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN.java @@ -0,0 +1,8 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + java.lang.String.new + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict.java new file mode 100644 index 000000000000..48aa2e99fbd9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict.java @@ -0,0 +1,10 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + java.lang.String.new + } + } + + class String {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict_after.java new file mode 100644 index 000000000000..7adf3898a7ac --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNConflict_after.java @@ -0,0 +1,10 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + new java.lang.String() + } + } + + class String {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport.java new file mode 100644 index 000000000000..332bd6f8043d --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport.java @@ -0,0 +1,10 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + java.util.ArrayList.new + } + } + + class String {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport_after.java new file mode 100644 index 000000000000..efa46e21a8cc --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQNImport_after.java @@ -0,0 +1,12 @@ +import java.util.ArrayList; + +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + new ArrayList<>() + } + } + + class String {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN_after.java new file mode 100644 index 000000000000..90744f82cf24 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newFQN_after.java @@ -0,0 +1,8 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + new String() + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass.java new file mode 100644 index 000000000000..8ceaeebca995 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass.java @@ -0,0 +1,13 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + Bar.new + } + } + + class Foo { + class Bar { + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2.java new file mode 100644 index 000000000000..04326a0e8a70 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2.java @@ -0,0 +1,13 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + Foo.Bar.new + } + } + + class Foo { + class Bar { + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2_after.java new file mode 100644 index 000000000000..52d1b3fa4c75 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass2_after.java @@ -0,0 +1,13 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + new Foo.Bar() + } + } + + class Foo { + class Bar { + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass_after.java b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass_after.java new file mode 100644 index 000000000000..6eea7f2a0985 --- /dev/null +++ b/java/java-tests/testData/codeInsight/template/postfix/templates/new/newNestedClass_after.java @@ -0,0 +1,13 @@ +public class ForStaticNestedNew { + + class Scratch { + public static void main(String[] args) { + new Bar() + } + } + + class Foo { + class Bar { + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/template/postfix/templates/NewExpressionPostfixTemplateTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/template/postfix/templates/NewExpressionPostfixTemplateTest.java index 8f9b57ce8f89..49f6f86bf4dc 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/template/postfix/templates/NewExpressionPostfixTemplateTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/template/postfix/templates/NewExpressionPostfixTemplateTest.java @@ -71,6 +71,26 @@ public class NewExpressionPostfixTemplateTest extends PostfixTemplateTestCase { doTest(); } + public void testNewNestedClass() { + doTest(); + } + + public void testNewNestedClass2() { + doTest(); + } + + public void testNewFQN() { + doTest(); + } + + public void testNewFQNConflict() { + doTest(); + } + + public void testNewFQNImport() { + doTest(); + } + public void testNewInaccessible() { myFixture.addClass("class Test {private Test() {}}"); doTest();