From 8f6eed481f70a87653bcee9a13c1dddbda1b4dc1 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 29 Feb 2012 13:53:48 +0100 Subject: [PATCH 1/3] don't select current item when typing : in groovy map or method call --- .../completion/GroovyReferenceCharFilter.java | 24 ++++++++++++------- .../MapArgumentCompletionProvider.java | 15 ++++++------ .../completion/GroovyAutoPopupTest.groovy | 12 ++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java index 185e62caa2c5..6df03c3d5f32 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java @@ -20,14 +20,16 @@ import com.intellij.codeInsight.lookup.CharFilter; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.impl.LookupImpl; +import com.intellij.patterns.StandardPatterns; import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseLabel; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrConditionalExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; + +import static com.intellij.patterns.PsiJavaPatterns.psiElement; /** * @author ilyas @@ -58,12 +60,16 @@ public class GroovyReferenceCharFilter extends CharFilter { if (c == ':') { PsiFile file = lookup.getPsiFile(); PsiDocumentManager.getInstance(file.getProject()).commitDocument(lookup.getEditor().getDocument()); - PsiElement element = lookup.getPsiElement(); - if (PsiTreeUtil.getParentOfType(element, GrCaseLabel.class) != null || - PsiTreeUtil.getParentOfType(element, GrConditionalExpression.class) != null || - PsiTreeUtil.getParentOfType(element, GrArgumentList.class) != null || - PsiTreeUtil.getParentOfType(element, GrListOrMap.class) != null - ) { + PsiElement element = file.findElementAt(Math.max(caret - 1, 0)); + if (psiElement().withParent( + psiElement(GrReferenceExpression.class).withParent( + StandardPatterns.or(psiElement(GrCaseLabel.class), + psiElement(GrConditionalExpression.class)))).accepts(element)) { + return Result.SELECT_ITEM_AND_FINISH_LOOKUP; + } + if (item.getObject() instanceof NamedArgumentDescriptor && + (MapArgumentCompletionProvider.IN_ARGUMENT_LIST_OF_CALL.accepts(element) || + MapArgumentCompletionProvider.IN_LABEL.accepts(element))) { return Result.SELECT_ITEM_AND_FINISH_LOOKUP; } return Result.HIDE_LOOKUP; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/MapArgumentCompletionProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/MapArgumentCompletionProvider.java index 84b6b2808589..e69a65085b31 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/MapArgumentCompletionProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/MapArgumentCompletionProvider.java @@ -51,20 +51,19 @@ import static org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor.Pr */ class MapArgumentCompletionProvider extends CompletionProvider { + public static final ElementPattern IN_ARGUMENT_LIST_OF_CALL = psiElement().withParent(psiElement(GrReferenceExpression.class).withParent( + StandardPatterns.or(psiElement(GrArgumentList.class), psiElement(GrListOrMap.class))) + ); + public static final ElementPattern IN_LABEL = psiElement(GroovyTokenTypes.mIDENT).withParent(GrArgumentLabel.class); + private MapArgumentCompletionProvider() { } public static void register(CompletionContributor contributor) { MapArgumentCompletionProvider instance = new MapArgumentCompletionProvider(); - ElementPattern inArgumentListOfCall = psiElement().withParent(psiElement(GrReferenceExpression.class).withParent( - StandardPatterns.or(psiElement(GrArgumentList.class), psiElement(GrListOrMap.class))) - ); - - ElementPattern inLabel = psiElement(GroovyTokenTypes.mIDENT).withParent(GrArgumentLabel.class); - - contributor.extend(CompletionType.BASIC, inArgumentListOfCall, instance); - contributor.extend(CompletionType.BASIC, inLabel, instance); + contributor.extend(CompletionType.BASIC, IN_ARGUMENT_LIST_OF_CALL, instance); + contributor.extend(CompletionType.BASIC, IN_LABEL, instance); } @Override diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy index e47e50a569e7..e37f354abbc9 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy @@ -232,4 +232,16 @@ class GroovyAutoPopupTest extends CompletionAutoPopupTestCase { assert myFixture.file.text == 'FIS:' } + public void testEnteringNamedArg() { + myFixture.configureByText 'a.groovy', 'foo()' + type 'has:' + myFixture.checkResult 'foo(has:)' + } + + public void testEnteringMapKey() { + myFixture.configureByText 'a.groovy', '[]' + type 'has:' + myFixture.checkResult '[has:]' + } + } From 2044c1d46adca8053e89a4f25560a9272b6ea4bd Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 29 Feb 2012 14:03:46 +0100 Subject: [PATCH 2/3] autopopup shouldn't prevent from entering qualified names --- .../intellij/codeInsight/completion/JavaCharFilter.java | 9 +++++++-- .../lang/completion/GroovyReferenceCharFilter.java | 3 ++- .../plugins/groovy/completion/GroovyAutoPopupTest.groovy | 7 +++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCharFilter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCharFilter.java index d513b80f588b..9b45d139d5ff 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCharFilter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCharFilter.java @@ -42,7 +42,7 @@ public class JavaCharFilter extends CharFilter { return psiElement != null && psiElement.getParent() instanceof PsiLiteralExpression; } - public static boolean isNonImportedClassEntered(LookupImpl lookup) { + public static boolean isNonImportedClassEntered(LookupImpl lookup, boolean orPackage) { if (lookup.isSelectionTouched() || !lookup.isCompletion()) return false; CompletionProcess process = CompletionService.getCompletionService().getCurrentCompletion(); @@ -66,6 +66,10 @@ public class JavaCharFilter extends CharFilter { } } + if (orPackage && prefix.length() > 1 && JavaPsiFacade.getInstance(file.getProject()).findPackage(prefix) != null) { + return true; + } + return false; } @@ -117,7 +121,8 @@ public class JavaCharFilter extends CharFilter { return Result.HIDE_LOOKUP; } - if ((c == '[' || c == '<' || c == '.' || c == ' ' || c == '(') && isNonImportedClassEntered((LookupImpl)lookup)) { + if ((c == '[' || c == '<' || c == '.' || c == ' ' || c == '(') && + isNonImportedClassEntered((LookupImpl)lookup, c == '.')) { return Result.HIDE_LOOKUP; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java index 6df03c3d5f32..f7d604d8fd67 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyReferenceCharFilter.java @@ -47,7 +47,8 @@ public class GroovyReferenceCharFilter extends CharFilter { return Result.ADD_TO_PREFIX; } - if ((c == '[' || c == '<' || c == '.' || c == ' ' || c == '(') && JavaCharFilter.isNonImportedClassEntered((LookupImpl)lookup)) { + if ((c == '[' || c == '<' || c == '.' || c == ' ' || c == '(') && + JavaCharFilter.isNonImportedClassEntered((LookupImpl)lookup, c == '.')) { return Result.HIDE_LOOKUP; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy index e37f354abbc9..03f99f1c3f9c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyAutoPopupTest.groovy @@ -244,4 +244,11 @@ class GroovyAutoPopupTest extends CompletionAutoPopupTestCase { myFixture.checkResult '[has:]' } + public void testTypingFqn() { + CodeInsightSettings.instance.COMPLETION_CASE_SENSITIVE = CodeInsightSettings.NONE + myFixture.configureByText 'a.groovy', '' + type 'java.' + myFixture.checkResult 'java.' + } + } From 6265e3c860dad98816a12c03783c7b1af05eb0a7 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 29 Feb 2012 14:40:56 +0100 Subject: [PATCH 3/3] infer instanceofed variable types in groovy logical binary operations --- .../controlFlow/impl/ControlFlowBuilder.java | 22 +++++++++++-------- .../completion/GroovyCompletionTest.groovy | 3 +++ ...anceofHelpsDetermineTypeInBinaryAnd.groovy | 7 ++++++ ...HelpsDetermineTypeInBinaryAnd_after.groovy | 7 ++++++ ...tanceofHelpsDetermineTypeInBinaryOr.groovy | 7 ++++++ ...fHelpsDetermineTypeInBinaryOr_after.groovy | 7 ++++++ 6 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd.groovy create mode 100644 plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd_after.groovy create mode 100644 plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr.groovy create mode 100644 plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr_after.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java index cba609eeb069..2e4f31e52201 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java @@ -463,19 +463,23 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final GrExpression right = expression.getRightOperand(); final IElementType opType = expression.getOperationTokenType(); - final FakeInstruction binaryEnd = - opType == GroovyTokenTypes.mLAND || opType == GroovyTokenTypes.mLOR ? new FakeInstruction(myInstructionNumber++) : null; - + InstructionImpl start = myHead; left.accept(this); - if (binaryEnd != null && myHead != null) { - addEdge(myHead, binaryEnd); + + if (right != null) { + if (opType == GroovyTokenTypes.mLOR) { + addPendingEdge(expression, myHead); + myHead = start; + + myNegate = !myNegate; + left.accept(this); + myNegate = !myNegate; + } + + right.accept(this); } - if (right != null) right.accept(this); - visitCall(expression); - - if (binaryEnd != null) addNode(binaryEnd); } /** diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy index 058d28cc9305..b610ce6fc158 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy @@ -111,6 +111,9 @@ public class GroovyCompletionTest extends GroovyCompletionTestBase { doBasicTest(); } + public void testInstanceofHelpsDetermineTypeInBinaryAnd() throws Throwable { doBasicTest() } + public void testInstanceofHelpsDetermineTypeInBinaryOr() throws Throwable { doBasicTest() } + public void testNotInstanceofDoesntHelpDetermineType() throws Throwable { myFixture.testCompletion(getTestName(false) + ".groovy", getTestName(false) + ".groovy"); } diff --git a/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd.groovy b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd.groovy new file mode 100644 index 000000000000..54162e5aa5b1 --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd.groovy @@ -0,0 +1,7 @@ +public class Parent { + + def foo(o) { + return o instanceof String && o.subst + } + +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd_after.groovy b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd_after.groovy new file mode 100644 index 000000000000..55e3f521edb1 --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryAnd_after.groovy @@ -0,0 +1,7 @@ +public class Parent { + + def foo(o) { + return o instanceof String && o.substring() + } + +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr.groovy b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr.groovy new file mode 100644 index 000000000000..1e8ef025bd1d --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr.groovy @@ -0,0 +1,7 @@ +public class Parent { + + def foo(o) { + return !(o instanceof String) || o.subst + } + +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr_after.groovy b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr_after.groovy new file mode 100644 index 000000000000..f46c3fbbf144 --- /dev/null +++ b/plugins/groovy/testdata/groovy/completion/InstanceofHelpsDetermineTypeInBinaryOr_after.groovy @@ -0,0 +1,7 @@ +public class Parent { + + def foo(o) { + return !(o instanceof String) || o.substring() + } + +} \ No newline at end of file