IDEA-153614 (Flip ',' (comma) generates invalid code)

This commit is contained in:
Bas Leijdekkers
2016-03-30 11:37:35 +02:00
parent 4d0e044c2f
commit 5210f8dbe5
7 changed files with 145 additions and 25 deletions

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2000-2016 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.intention.impl;
import com.intellij.openapi.editor.actions.FlipCommaIntention;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
/**
* @author Bas Leijdekkers
*/
public class JavaFlipper implements FlipCommaIntention.Flipper {
@Override
public boolean flip(PsiElement left, PsiElement right) {
if (left instanceof PsiVariable && right instanceof PsiVariable) {
final PsiElement first = left.getFirstChild();
if (!(first instanceof PsiModifierList)) {
return false;
}
final PsiElement child = PsiTreeUtil.skipSiblingsForward(first, PsiWhiteSpace.class);
if (!(child instanceof PsiTypeElement)) {
return false;
}
final PsiElement last = child.getNextSibling();
if (!(last instanceof PsiWhiteSpace)) {
return false;
}
final PsiElement anchor = right.getFirstChild();
if (!(anchor instanceof PsiIdentifier)) {
return false;
}
final PsiElement semiColon = right.getLastChild();
if (!(semiColon instanceof PsiJavaToken)) {
return false;
}
right.addRangeBefore(first, last, anchor);
left.deleteChildRange(first, last);
left.add(semiColon);
semiColon.delete();
final PsiElement copy = left.copy();
left.replace(right);
right.replace(copy);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,5 @@
// "Flip ','" "true"
class MultiVar {
private int b,<caret> a;
}

View File

@@ -0,0 +1,8 @@
// "Flip ','" "true"
class MultiVar {
void m() {
int a,<caret> b;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2000-2016 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.intention;
import com.siyeh.ipp.IPPTestCase;
/**
* @author Bas Leijdekkers
*/
public class FlipCommaIntentionTest extends IPPTestCase {
public void test() throws Exception {
doTest("class C {\n" +
" int a,/*_Flip ','*/ b;" +
"}",
"class C {\n" +
" int b, a;\n" +
"}");
}
}

View File

@@ -16,18 +16,15 @@
package com.intellij.openapi.editor.actions;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageExtension;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,23 +44,14 @@ public class FlipCommaIntention implements IntentionAction {
@Override
public boolean isAvailable(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
PsiElement comma = currentCommaElement(editor, file);
return comma != null && smartAdvanceAsExpr(comma, true) != null && smartAdvance(comma, false) != null;
return comma != null && smartAdvance(comma, true) != null && smartAdvance(comma, false) != null;
}
@Override
public void invoke(@NotNull Project project, @NotNull final Editor editor, @NotNull PsiFile file) throws IncorrectOperationException {
public void invoke(@NotNull Project project, @NotNull final Editor editor, @NotNull PsiFile file) {
final PsiElement element = currentCommaElement(editor, file);
if (element != null) {
new WriteCommandAction(project, file) {
protected void run(@NotNull Result result) throws Throwable {
PostprocessReformattingAspect.getInstance(getProject()).disablePostprocessFormattingInside(new Runnable() {
@Override
public void run() {
swapAtComma(element);
}
});
}
}.execute();
swapAtComma(element);
}
}
@@ -73,15 +61,37 @@ public class FlipCommaIntention implements IntentionAction {
}
private static void swapAtComma(@NotNull PsiElement comma) {
PsiElement prev = smartAdvanceAsExpr(comma, false);
PsiElement next = smartAdvanceAsExpr(comma, true);
PsiElement prev = smartAdvance(comma, false);
PsiElement next = smartAdvance(comma, true);
if (prev != null && next != null) {
if (Flipper.tryFlip(prev, next)) {
return;
}
PsiElement copy = prev.copy();
prev.replace(next);
next.replace(copy);
}
}
public interface Flipper {
LanguageExtension<Flipper> EXTENSION = new LanguageExtension<>("com.intellij.flipCommaIntention.flipper");
/**
* @return true, if elements were flipped; false, if default flip implementation should be used.
*/
boolean flip(PsiElement left, PsiElement right);
static boolean tryFlip(PsiElement left, PsiElement right) {
final Language language = left.getLanguage();
for (Flipper handler : EXTENSION.allForLanguage(language)) {
if (handler.flip(left, right)) {
return true;
}
}
return false;
}
}
private static PsiElement currentCommaElement(@NotNull Editor editor, @NotNull PsiFile file) {
PsiElement element;
if (!isComma(element = leftElement(editor, file)) && !isComma(element = rightElement(editor, file))) {
@@ -110,9 +120,4 @@ public class FlipCommaIntention implements IntentionAction {
return fwd ? PsiTreeUtil.skipSiblingsForward(element, skipTypes)
: PsiTreeUtil.skipSiblingsBackward(element, skipTypes);
}
@Nullable
static private PsiElement smartAdvanceAsExpr(PsiElement element, boolean fwd) {
return ObjectUtils.tryCast(smartAdvance(element, fwd), PsiElement.class);
}
}

View File

@@ -751,6 +751,11 @@
implements="com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementLeftRightHandler"/>
</extensionPoint>
<extensionPoint name="flipCommaIntention.flipper" beanClass="com.intellij.lang.LanguageExtensionPoint">
<with attribute="implementationClass"
implements="com.intellij.openapi.editor.actions.FlipCommaIntention.Flipper"/>
</extensionPoint>
<extensionPoint name="fileLookupInfoProvider" interface="com.intellij.psi.file.FileLookupInfoProvider"/>
<extensionPoint name="idIndexer" beanClass="com.intellij.openapi.fileTypes.FileTypeExtensionPoint">

View File

@@ -1114,7 +1114,8 @@
<statementUpDownMover implementation="com.intellij.codeInsight.editorActions.moveUpDown.CaseBlockMover" id="caseBlock"
order="before statement"/>
<moveLeftRightHandler language="JAVA" implementationClass="com.intellij.codeInsight.editorActions.moveLeftRight.JavaMoveLeftRightHandler"/>
<flipCommaIntention.flipper language="JAVA" implementationClass="com.intellij.codeInsight.intention.impl.JavaFlipper"/>
<fileType.fileViewProviderFactory filetype="CLASS" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
<editorFileSwapper implementation="com.intellij.codeEditor.JavaEditorFileSwapper"/>