IDEA-140452 Complete-statement insert closing parenthesis at wrong place

This commit is contained in:
peter
2015-07-09 20:49:01 +02:00
parent 26f25b917d
commit 7194336a1f
4 changed files with 64 additions and 7 deletions
@@ -16,10 +16,14 @@
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbService;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.jsp.jspJava.JspMethodCall;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
@@ -38,17 +42,20 @@ public class MethodCallFixer implements Fixer {
args = ((PsiNewExpression) psiElement).getArgumentList();
}
if (args == null) return;
if (args != null && !hasRParenth(args)) {
int caret = editor.getCaretModel().getOffset();
PsiCallExpression innermostCall = PsiTreeUtil.findElementOfClassAtOffset(psiElement.getContainingFile(), caret, PsiCallExpression.class, false);
if (innermostCall == null) return;
PsiElement parenth = args.getLastChild();
args = innermostCall.getArgumentList();
if (args == null) return;
if (parenth == null || !")".equals(parenth.getText())) {
int endOffset = -1;
PsiElement child = args.getFirstChild();
while (child != null) {
if (child instanceof PsiErrorElement) {
final PsiErrorElement errorElement = (PsiErrorElement)child;
if (errorElement.getErrorDescription().indexOf("')'") >= 0) {
if (errorElement.getErrorDescription().contains("')'")) {
endOffset = errorElement.getTextRange().getStartOffset();
break;
}
@@ -62,17 +69,48 @@ public class MethodCallFixer implements Fixer {
final PsiExpression[] params = args.getExpressions();
if (params.length > 0 &&
startLine(editor, args) != startLine(editor, params[0]) &&
editor.getCaretModel().getOffset() < params[0].getTextRange().getStartOffset()) {
startLine(editor, args) != startLine(editor, params[0]) &&
caret < params[0].getTextRange().getStartOffset()) {
endOffset = args.getTextRange().getStartOffset() + 1;
}
if (!DumbService.isDumb(args.getProject())) {
Integer argCount = getUnambiguousParameterCount(innermostCall);
if (argCount != null && argCount > 0 && argCount < params.length) {
endOffset = Math.min(endOffset, params[argCount - 1].getTextRange().getEndOffset());
}
}
endOffset = CharArrayUtil.shiftBackward(editor.getDocument().getCharsSequence(), endOffset - 1, " \t\n") + 1;
editor.getDocument().insertString(endOffset, ")");
editor.getCaretModel().moveToOffset(endOffset + 1);
}
}
private int startLine(Editor editor, PsiElement psiElement) {
private static boolean hasRParenth(PsiExpressionList args) {
PsiElement parenth = args.getLastChild();
return parenth != null && ")".equals(parenth.getText());
}
@Nullable
private static Integer getUnambiguousParameterCount(PsiCallExpression call) {
int argCount = -1;
for (CandidateInfo candidate : PsiResolveHelper.SERVICE.getInstance(call.getProject()).getReferencedMethodCandidates(call, false)) {
PsiElement element = candidate.getElement();
if (!(element instanceof PsiMethod)) return null;
if (((PsiMethod)element).isVarArgs()) return null;
int count = ((PsiMethod)element).getParameterList().getParametersCount();
if (argCount == -1) {
argCount = count;
} else if (argCount != count) {
return null;
}
}
return argCount;
}
private static int startLine(Editor editor, PsiElement psiElement) {
return editor.getDocument().getLineNumber(psiElement.getTextRange().getStartOffset());
}
}
@@ -0,0 +1,9 @@
class Main {
public void test() {
Path path = Paths.get("/tmp");
foo(getf<caret>path, 5);
}
private String getFileName(Path path) { return path.toString(); }
private void foo(String text, int count) {}
}
@@ -0,0 +1,9 @@
class Main {
public void test() {
Path path = Paths.get("/tmp");
foo(getFileName(path), 5);<caret>
}
private String getFileName(Path path) { return path.toString(); }
private void foo(String text, int count) {}
}
@@ -934,6 +934,7 @@ public class ListUtils {
public void testSmartEnterWrapsConstructorCall() throws Throwable { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) }
public void testSmartEnterNoNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) }
public void testSmartEnterWithNewLine() { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) }
public void testSmartEnterGuessArgumentCount() throws Throwable { doTest(Lookup.COMPLETE_STATEMENT_SELECT_CHAR as String) }
public void testTabReplacesMethodNameWithLocalVariableName() throws Throwable { doTest('\t'); }
public void testMethodParameterAnnotationClass() throws Throwable { doTest(); }