[java-completion] Complete statement: process loops uniformly; wrap loop body

Fixes IDEA-73881 "Complete Current Statement" incorrectly handles for with non-block loop statement

GitOrigin-RevId: 0e92756e7536f5d939db3f2f0ed42c8e716076c3
This commit is contained in:
Tagir Valeev
2022-04-20 14:37:10 +02:00
committed by intellij-monorepo-bot
parent c62e2b7740
commit b5838faa84
11 changed files with 155 additions and 187 deletions

View File

@@ -72,7 +72,10 @@ public class ForStatementFixer implements Fixer {
final PsiExpression condition = forStatement.getCondition();
if (condition == null) {
registerErrorOffset(editor, processor, initialization, forStatement);
boolean endlessLoop = initialization instanceof PsiEmptyStatement && forStatement.getUpdate() == null;
if (!endlessLoop) {
registerErrorOffset(editor, processor, initialization, forStatement);
}
return;
}

View File

@@ -56,13 +56,11 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor {
new DoWhileConditionFixer(),
new BlockBraceFixer(),
new MissingIfBranchesFixer(),
new MissingWhileBodyFixer(),
new MissingTryBodyFixer(),
new MissingSwitchBodyFixer(),
new MissingCatchBodyFixer(),
new MissingSynchronizedBodyFixer(),
new MissingForBodyFixer(),
new MissingForeachBodyFixer(),
new MissingLoopBodyFixer(),
new ParameterListFixer(),
new MissingCommaFixer(),
new MissingMethodBodyFixer(),

View File

@@ -1,75 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
public class MissingForBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
PsiForStatement forStatement = getForStatementParent(psiElement);
if (forStatement == null) return;
final Document doc = editor.getDocument();
PsiElement body = forStatement.getBody();
if (body instanceof PsiBlockStatement) return;
if (body != null && startLine(doc, body) == startLine(doc, forStatement)) return;
PsiElement eltToInsertAfter = forStatement.getRParenth();
String braces = "{\n}";
String text = braces;
if (eltToInsertAfter == null) {
eltToInsertAfter = forStatement;
text = ")" + text;
}
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, text);
editor.getCaretModel().moveToOffset(offset + text.length() - braces.length());
}
@Nullable
private static PsiForStatement getForStatementParent(PsiElement psiElement) {
PsiForStatement statement = PsiTreeUtil.getParentOfType(psiElement, PsiForStatement.class);
if (statement == null) return null;
PsiStatement init = statement.getInitialization();
PsiStatement update = statement.getUpdate();
PsiExpression check = statement.getCondition();
return isValidChild(init, psiElement) || isValidChild(update, psiElement) || isValidChild(check, psiElement) ? statement : null;
}
private static boolean isValidChild(PsiElement ancestor, PsiElement psiElement) {
if (ancestor != null) {
if (PsiTreeUtil.isAncestor(ancestor, psiElement, false)) {
if (PsiTreeUtil.hasErrorElements(ancestor)) return false;
return true;
}
}
return false;
}
private static int startLine(Document doc, PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
/**
* @author Maxim.Mossienko
*/
public class MissingForeachBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
PsiForeachStatement forStatement = getForeachStatementParent(psiElement);
if (forStatement == null) return;
final Document doc = editor.getDocument();
PsiElement body = forStatement.getBody();
if (body instanceof PsiBlockStatement) return;
if (body != null && startLine(doc, body) == startLine(doc, forStatement)) return;
PsiElement eltToInsertAfter = forStatement.getRParenth();
String text = "{}";
if (eltToInsertAfter == null) {
eltToInsertAfter = forStatement;
text = "){}";
}
doc.insertString(eltToInsertAfter.getTextRange().getEndOffset(), text);
}
private static PsiForeachStatement getForeachStatementParent(PsiElement psiElement) {
PsiForeachStatement statement = PsiTreeUtil.getParentOfType(psiElement, PsiForeachStatement.class);
if (statement == null) return null;
PsiExpression iterated = statement.getIteratedValue();
PsiParameter parameter = statement.getIterationParameter();
return PsiTreeUtil.isAncestor(iterated, psiElement, false) || PsiTreeUtil.isAncestor(parameter, psiElement, false) ? statement : null;
}
private static int startLine(Document doc, PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
public class MissingLoopBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
PsiLoopStatement loopStatement = getLoopParent(psiElement);
if (loopStatement == null) return;
final Document doc = editor.getDocument();
PsiElement body = loopStatement.getBody();
if (body instanceof PsiBlockStatement) return;
if (body != null && startLine(doc, body) == startLine(doc, loopStatement)) return;
PsiElement eltToInsertAfter;
if (loopStatement instanceof PsiWhileStatement) {
eltToInsertAfter = ((PsiWhileStatement)loopStatement).getRParenth();
}
else if (loopStatement instanceof PsiForStatement) {
eltToInsertAfter = ((PsiForStatement)loopStatement).getRParenth();
}
else if (loopStatement instanceof PsiForeachStatement) {
eltToInsertAfter = ((PsiForeachStatement)loopStatement).getRParenth();
}
else {
return;
}
fixLoopBody(editor, processor, loopStatement, doc, body, eltToInsertAfter);
}
private static PsiLoopStatement getLoopParent(PsiElement element) {
PsiLoopStatement statement = PsiTreeUtil.getParentOfType(element, PsiLoopStatement.class);
if (statement == null) return null;
if (statement instanceof PsiForeachStatement) {
return isForEachApplicable((PsiForeachStatement)statement, element) ? statement : null;
}
if (statement instanceof PsiForStatement) {
return isForApplicable((PsiForStatement)statement, element) ? statement : null;
}
if (statement instanceof PsiWhileStatement) {
return statement;
}
return null;
}
private static boolean isForApplicable(PsiForStatement statement, PsiElement psiElement) {
PsiStatement init = statement.getInitialization();
PsiStatement update = statement.getUpdate();
PsiExpression check = statement.getCondition();
return isValidChild(init, psiElement) || isValidChild(update, psiElement) || isValidChild(check, psiElement);
}
private static boolean isValidChild(PsiElement ancestor, PsiElement psiElement) {
if (ancestor != null) {
if (PsiTreeUtil.isAncestor(ancestor, psiElement, false)) {
if (PsiTreeUtil.hasErrorElements(ancestor)) return false;
return true;
}
}
return false;
}
private static boolean isForEachApplicable(PsiForeachStatement statement, PsiElement psiElement) {
PsiExpression iterated = statement.getIteratedValue();
PsiParameter parameter = statement.getIterationParameter();
return PsiTreeUtil.isAncestor(iterated, psiElement, false) || PsiTreeUtil.isAncestor(parameter, psiElement, false);
}
private static int startLine(Document doc, PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
static void fixLoopBody(Editor editor,
JavaSmartEnterProcessor processor,
PsiLoopStatement loop,
Document doc,
PsiElement body,
PsiElement eltToInsertAfter) {
if (body != null && eltToInsertAfter != null) {
int endOffset = body.getTextRange().getEndOffset();
doc.insertString(endOffset, "\n}");
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, "{");
editor.getCaretModel().moveToOffset(endOffset + "{".length());
processor.setSkipEnter(true);
processor.reformat(loop);
}
else {
String prefix = "{}";
String text = prefix;
if (eltToInsertAfter == null) {
eltToInsertAfter = loop;
text = ")" + text;
}
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, text);
editor.getCaretModel().moveToOffset(offset + text.length() - prefix.length());
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiBlockStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.PsiWhileStatement;
import com.intellij.util.IncorrectOperationException;
public class MissingWhileBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiWhileStatement)) return;
PsiWhileStatement whileStatement = (PsiWhileStatement) psiElement;
final Document doc = editor.getDocument();
PsiElement body = whileStatement.getBody();
if (body instanceof PsiBlockStatement) return;
if (body != null && startLine(doc, body) == startLine(doc, whileStatement) && whileStatement.getCondition() != null) return;
final PsiJavaToken rParenth = whileStatement.getRParenth();
assert rParenth != null;
doc.insertString(rParenth.getTextRange().getEndOffset(), "{}");
}
private static int startLine(Document doc, PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
}

View File

@@ -0,0 +1,6 @@
class X {{
int[] a = new int[3];
long res = 0;
for (int val : a)<caret>
res+=val
}}

View File

@@ -0,0 +1,7 @@
class X {{
int[] a = new int[3];
long res = 0;
for (int val : a) {
res += val
}
}}

View File

@@ -0,0 +1,5 @@
class X {{
int[] a = new int[3];
for (int i = 0; i < a.length; ++i)
a[i] = 1<caret>
}}

View File

@@ -0,0 +1,6 @@
class X {{
int[] a = new int[3];
for (int i = 0; i < a.length; ++i) {
a[i] = 1;
}
}}

View File

@@ -75,6 +75,8 @@ public class CompleteStatementTest extends EditorActionTestCase {
public void testBlock1() { doTest(); }
public void testAfterFor() { doTest(); }
public void testBeforeFor() { doTest(); }
public void testForSingleStatementInBody() { doTest(); }
public void testForEachSingleStatementInBody() { doTest(); }
public void testAtBlockEnd() { doTest(); }
public void testForceBlock() { doTest(); }
public void testElseIf() { doTest(); }