diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaTypedHandler.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaTypedHandler.java index 5f59dd8eb578..52aa2958ba1a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaTypedHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaTypedHandler.java @@ -42,8 +42,13 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharArrayUtil; +import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -130,6 +135,17 @@ public class JavaTypedHandler extends TypedHandlerDelegate { } } + if (c == '?') { + handleQuestionMark(project, editor, file, offsetBefore); + return Result.STOP; + } + + if (c == '=') { + if (handleEquality(project, editor, file, offsetBefore)) { + return Result.STOP; + } + } + if (c == ';') { if (handleSemicolon(project, editor, file, fileType)) return Result.STOP; } @@ -171,6 +187,67 @@ public class JavaTypedHandler extends TypedHandlerDelegate { return Result.CONTINUE; } + private static boolean handleEquality(Project project, Editor editor, PsiFile file, int offsetBefore) { + if (offsetBefore == 0) return false; + Document doc = editor.getDocument(); + char prevChar = doc.getCharsSequence().charAt(offsetBefore - 1); + if (prevChar != '=' && prevChar != '!') return false; + doc.insertString(offsetBefore, "="); + // a&b== => (a&b)== + editor.getCaretModel().moveToOffset(offsetBefore + 1); + PsiDocumentManager.getInstance(project).commitDocument(doc); + PsiJavaToken token = ObjectUtils.tryCast(file.findElementAt(offsetBefore), PsiJavaToken.class); + if (token == null) return true; + IElementType type = token.getTokenType(); + if (type != JavaTokenType.EQEQ && type != JavaTokenType.NE) return true; + PsiBinaryExpression comparison = ObjectUtils.tryCast(token.getParent(), PsiBinaryExpression.class); + if (comparison == null || comparison.getROperand() != null) return true; + PsiBinaryExpression bitwiseOp = ObjectUtils.tryCast(comparison.getParent(), PsiBinaryExpression.class); + if (bitwiseOp == null || bitwiseOp.getROperand() != comparison) return true; + IElementType bitwiseOpType = bitwiseOp.getOperationTokenType(); + if (bitwiseOpType != JavaTokenType.AND && bitwiseOpType != JavaTokenType.OR && bitwiseOpType != JavaTokenType.XOR) return true; + PsiExpression left = bitwiseOp.getLOperand(); + PsiExpression right = comparison.getLOperand(); + if (!TypeConversionUtil.isIntegralNumberType(left.getType()) || !TypeConversionUtil.isIntegralNumberType(right.getType())) { + return true; + } + doc.insertString(right.getTextRange().getEndOffset(), ")"); + doc.insertString(left.getTextRange().getStartOffset(), "("); + editor.getCaretModel().moveToOffset(offsetBefore + 3); + return true; + } + + private static void handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore) { + Document doc = editor.getDocument(); + doc.insertString(offsetBefore, "?"); + editor.getCaretModel().moveToOffset(offsetBefore + 1); + PsiDocumentManager.getInstance(project).commitDocument(doc); + PsiElement element = file.findElementAt(offsetBefore); + if (!(element instanceof PsiJavaToken) || !((PsiJavaToken)element).getTokenType().equals(JavaTokenType.QUEST)) return; + PsiConditionalExpression cond = ObjectUtils.tryCast(element.getParent(), PsiConditionalExpression.class); + if (cond == null || cond.getThenExpression() != null || cond.getElseExpression() != null) return; + PsiExpression condition = cond.getCondition(); + if (PsiUtilCore.hasErrorElementChild(condition)) return; + PsiExpression parenthesisStart = null; + // intVal+bool? => intVal+(bool?) + if (condition instanceof PsiPolyadicExpression && !PsiType.BOOLEAN.equals(condition.getType())) { + PsiExpression lastOperand = ArrayUtil.getLastElement(((PsiPolyadicExpression)condition).getOperands()); + if (lastOperand != null && PsiType.BOOLEAN.equals(lastOperand.getType())) { + parenthesisStart = lastOperand; + } + } + // bool? in void context => (bool?) + if (ExpressionUtils.isVoidContext(cond) && PsiType.BOOLEAN.equals(condition.getType())) { + parenthesisStart = cond; + } + if (parenthesisStart != null) { + int offset = parenthesisStart.getTextRange().getStartOffset(); + doc.insertString(offsetBefore + 1, ")"); + doc.insertString(offset, "("); + editor.getCaretModel().moveToOffset(offsetBefore + 2); + } + } + private static boolean shouldInsertPairedBrace(@NotNull PsiElement leaf) { PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(leaf); // lambda diff --git a/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_after.java b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_after.java new file mode 100644 index 000000000000..9e06b1dffa59 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_after.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + if((x|0x1F)!=0x1F && (x|0x38)!=) + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_before.java b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_before.java new file mode 100644 index 000000000000..9716e70530ed --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp2_before.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + if((x|0x1F)!=0x1F && x|0x38!) + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_after.java b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_after.java new file mode 100644 index 000000000000..b02b7d9bc3ff --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_after.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + if((x&1)==) + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_before.java b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_before.java new file mode 100644 index 000000000000..b0285241d68e --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/equalAfterBitwiseOp_before.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + if(x&1=) + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_after.java b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_after.java new file mode 100644 index 000000000000..fa9c2c8c7832 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_after.java @@ -0,0 +1,5 @@ +public class Foo { + void test(boolean x, boolean b) { + System.out.println(x==b?); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_before.java b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_before.java new file mode 100644 index 000000000000..66681ef33c85 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadicBoolean_before.java @@ -0,0 +1,5 @@ +public class Foo { + void test(boolean x, boolean b) { + System.out.println(x==b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_after.java b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_after.java new file mode 100644 index 000000000000..6ff23db04d5c --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_after.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x, boolean b) { + System.out.println(x+(b?)); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_before.java b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_before.java new file mode 100644 index 000000000000..60382e9d5371 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionAfterPolyadic_before.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x, boolean b) { + System.out.println(x+b); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionInVoidContext_after.java b/java/java-tests/testData/codeInsight/typing/questionInVoidContext_after.java new file mode 100644 index 000000000000..a631a5d21343 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionInVoidContext_after.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + (x > 0?) + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/typing/questionInVoidContext_before.java b/java/java-tests/testData/codeInsight/typing/questionInVoidContext_before.java new file mode 100644 index 000000000000..c8eb4d8d1725 --- /dev/null +++ b/java/java-tests/testData/codeInsight/typing/questionInVoidContext_before.java @@ -0,0 +1,5 @@ +public class Foo { + void test(int x) { + x > 0 + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/JavaTypingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/JavaTypingTest.java index 9bbf7ba51f3d..4df6c3fe8b12 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/JavaTypingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/JavaTypingTest.java @@ -141,6 +141,16 @@ public class JavaTypingTest extends BasePlatformTestCase { } public void testCommaInDefaultAnnotationStringArgumentWhenArrayIsExpected() { doTest(','); } + + public void testQuestionAfterPolyadic() { doTest('?'); } + + public void testQuestionAfterPolyadicBoolean() { doTest('?'); } + + public void testQuestionInVoidContext() { doTest('?'); } + + public void testEqualAfterBitwiseOp() { doTest('='); } + + public void testEqualAfterBitwiseOp2() { doTest('='); } private void doTest(char c) { myFixture.configureByFile(getTestName(true) + "_before.java");