[java-completion] IDEA-246986 Fixes according to review IDEA-CR-65611

1. Honor SPACE_WITHIN_PARENTHESES setting
2. Some cheap lexer checks to avoid document commit when unnecessary

GitOrigin-RevId: 168e521a4f59d294d09098a05d7fa276501cf2ba
This commit is contained in:
Tagir Valeev
2020-07-31 11:34:14 +07:00
committed by intellij-monorepo-bot
parent 22bf433b63
commit ec21ef9436
6 changed files with 129 additions and 21 deletions

View File

@@ -15,6 +15,7 @@
*/
package com.intellij.codeInsight.editorActions;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionType;
@@ -136,8 +137,9 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
}
if (c == '?') {
handleQuestionMark(project, editor, file, offsetBefore);
return Result.STOP;
if (handleQuestionMark(project, editor, file, offsetBefore)) {
return Result.STOP;
}
}
if (c == '=') {
@@ -192,9 +194,29 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
Document doc = editor.getDocument();
char prevChar = doc.getCharsSequence().charAt(offsetBefore - 1);
if (prevChar != '=' && prevChar != '!') return false;
HighlighterIterator it = ((EditorEx)editor).getHighlighter().createIterator(offsetBefore - 1);
IElementType curToken = it.getTokenType();
if (curToken != JavaTokenType.EQ && curToken != JavaTokenType.EXCL) return false;
int lineStart = doc.getLineStartOffset(doc.getLineNumber(offsetBefore));
do {
it.retreat();
curToken = it.getTokenType();
}
while (curToken == TokenType.WHITE_SPACE || curToken == JavaTokenType.C_STYLE_COMMENT || curToken == JavaTokenType.END_OF_LINE_COMMENT);
// ) == or ) != : definitely no need to add parentheses
if (curToken == JavaTokenType.RPARENTH) return false;
while (true) {
if (it.getStart() < lineStart) return false;
it.retreat();
if (it.atEnd()) return false;
curToken = it.getTokenType();
if (curToken == JavaTokenType.AND || curToken == JavaTokenType.OR || curToken == JavaTokenType.XOR) break;
}
doc.insertString(offsetBefore, "=");
// a&b== => (a&b)==
editor.getCaretModel().moveToOffset(offsetBefore + 1);
// a&b== => (a&b)==
PsiDocumentManager.getInstance(project).commitDocument(doc);
PsiJavaToken token = ObjectUtils.tryCast(file.findElementAt(offsetBefore), PsiJavaToken.class);
if (token == null) return true;
@@ -211,23 +233,67 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
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);
int openingOffset = left.getTextRange().getStartOffset();
int closingOffset = right.getTextRange().getEndOffset();
wrapWithParentheses(file, doc, openingOffset, closingOffset);
return true;
}
private static void handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore) {
private static final TokenSet UNWANTED_TOKEN_AT_QUESTION =
TokenSet.create(JavaTokenType.C_STYLE_COMMENT, JavaTokenType.END_OF_LINE_COMMENT, JavaTokenType.CHARACTER_LITERAL,
JavaTokenType.STRING_LITERAL, JavaTokenType.TEXT_BLOCK_LITERAL);
private static final TokenSet UNWANTED_TOKEN_BEFORE_QUESTION =
TokenSet.create(
// inside assignment
JavaTokenType.EQ, JavaTokenType.ASTERISKEQ, JavaTokenType.DIVEQ, JavaTokenType.PERCEQ, JavaTokenType.PLUSEQ, JavaTokenType.MINUSEQ,
JavaTokenType.LTLTEQ, JavaTokenType.GTGTEQ, JavaTokenType.GTGTGTEQ, JavaTokenType.ANDEQ, JavaTokenType.OREQ, JavaTokenType.XOREQ,
// inside another ?:
JavaTokenType.QUEST, JavaTokenType.COLON);
private static final TokenSet WANTED_TOKEN_BEFORE_QUESTION =
TokenSet.create(
// Tokens that may appear before ?: in void context
JavaTokenType.ARROW, JavaTokenType.SEMICOLON, JavaTokenType.LBRACE, JavaTokenType.RBRACE,
// Tokens that may appear before ?: in polyadic expression that may have non-boolean result
JavaTokenType.OR, JavaTokenType.XOR, JavaTokenType.AND, JavaTokenType.LTLT, JavaTokenType.GTGT,
JavaTokenType.GTGTGT, JavaTokenType.PLUS, JavaTokenType.MINUS, JavaTokenType.ASTERISK, JavaTokenType.DIV,
JavaTokenType.PERC);
private static boolean handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore) {
if (offsetBefore == 0) return false;
HighlighterIterator it = ((EditorEx)editor).getHighlighter().createIterator(offsetBefore);
if (it.atEnd()) return false;
IElementType curToken = it.getTokenType();
if (UNWANTED_TOKEN_AT_QUESTION.contains(curToken)) return false;
int nesting = 0;
while (true) {
it.retreat();
if (it.atEnd()) return false;
curToken = it.getTokenType();
if (curToken == JavaTokenType.LPARENTH || curToken == JavaTokenType.LBRACKET) {
nesting--;
if (nesting < 0) return false;
}
else if (curToken == JavaTokenType.RPARENTH || curToken == JavaTokenType.RBRACKET) {
nesting++;
}
else if (nesting == 0) {
if (UNWANTED_TOKEN_BEFORE_QUESTION.contains(curToken)) return false;
if (WANTED_TOKEN_BEFORE_QUESTION.contains(curToken)) break;
}
}
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;
if (!(element instanceof PsiJavaToken) || !((PsiJavaToken)element).getTokenType().equals(JavaTokenType.QUEST)) return true;
PsiConditionalExpression cond = ObjectUtils.tryCast(element.getParent(), PsiConditionalExpression.class);
if (cond == null || cond.getThenExpression() != null || cond.getElseExpression() != null) return;
if (cond == null || cond.getThenExpression() != null || cond.getElseExpression() != null) return true;
PsiExpression condition = cond.getCondition();
if (PsiUtilCore.hasErrorElementChild(condition)) return;
if (PsiUtilCore.hasErrorElementChild(condition)) return true;
PsiExpression parenthesisStart = null;
// intVal+bool? => intVal+(bool?)
if (condition instanceof PsiPolyadicExpression && !PsiType.BOOLEAN.equals(condition.getType())) {
@@ -241,11 +307,17 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
parenthesisStart = cond;
}
if (parenthesisStart != null) {
int offset = parenthesisStart.getTextRange().getStartOffset();
doc.insertString(offsetBefore + 1, ")");
doc.insertString(offset, "(");
editor.getCaretModel().moveToOffset(offsetBefore + 2);
int openingOffset = parenthesisStart.getTextRange().getStartOffset();
int closingOffset = cond.getTextRange().getEndOffset();
wrapWithParentheses(file, doc, openingOffset, closingOffset);
}
return true;
}
private static void wrapWithParentheses(PsiFile file, Document doc, int openingOffset, int closingOffset) {
String space = CodeStyle.getLanguageSettings(file).SPACE_WITHIN_PARENTHESES ? " " : "";
doc.insertString(closingOffset, space + ")");
doc.insertString(openingOffset, "(" + space);
}
private static boolean shouldInsertPairedBrace(@NotNull PsiElement leaf) {

View File

@@ -1,5 +1,5 @@
public class Foo {
void test(int x) {
if((x|0x1F)!=0x1F && (x|0x38)!=<caret>)
if((x|0x1F)!=0x1F && ( x|0x38 )!=<caret>)
}
}

View File

@@ -0,0 +1,5 @@
public class Foo {
void test(int x, String s) {
System.out.println(x+(!s.trim().substring(new int[] {1,2,3}.length).isEmpty()?<caret>));
}
}

View File

@@ -0,0 +1,5 @@
public class Foo {
void test(int x, String s) {
System.out.println(x+!s.trim().substring(new int[] {1,2,3}.length).isEmpty()<caret>);
}
}

View File

@@ -1,12 +1,14 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight;
import com.intellij.application.options.CodeStyle;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
@@ -141,16 +143,28 @@ public class JavaTypingTest extends BasePlatformTestCase {
}
public void testCommaInDefaultAnnotationStringArgumentWhenArrayIsExpected() { doTest(','); }
public void testQuestionAfterPolyadic() { doTest('?'); }
public void testQuestionAfterPolyadic2() { doTest('?'); }
public void testQuestionAfterPolyadicBoolean() { doTest('?'); }
public void testQuestionInVoidContext() { doTest('?'); }
public void testEqualAfterBitwiseOp() { doTest('='); }
public void testEqualAfterBitwiseOp2() { doTest('='); }
public void testEqualAfterBitwiseOp2() {
myFixture.configureByFile(getTestName(true) + "_before.java");
CommonCodeStyleSettings settings = CodeStyle.getLanguageSettings(myFixture.getFile());
settings.SPACE_WITHIN_PARENTHESES = true;
try {
myFixture.type('=');
myFixture.checkResultByFile(getTestName(true) + "_after.java");
}
finally {
settings.SPACE_WITHIN_PARENTHESES = false;
}
}
private void doTest(char c) {
myFixture.configureByFile(getTestName(true) + "_before.java");

View File

@@ -25,8 +25,20 @@ public interface HighlighterIterator {
TextAttributes getTextAttributes();
int getStart();
int getEnd();
/**
* @return type if the current token
*/
IElementType getTokenType();
/**
* Move iterator to the next segment
*/
void advance();
/**
* Move iterator to the previous segment
*/
void retreat();
boolean atEnd();
Document getDocument();