join declaration and assignment (IDEA-92226; IDEA-123689)

This commit is contained in:
Anna Kozlova
2014-04-14 21:38:56 +02:00
parent b708f2006e
commit 9d3b4638ce
17 changed files with 263 additions and 40 deletions
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.editorActions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.LocalSearchScope;
@@ -64,13 +65,51 @@ public class DeclarationJoinLinesHandler implements JoinLinesHandlerDelegate {
}
final PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
final PsiExpression initializerExpression = getInitializerExpression(var, assignment);
if (initializerExpression == null) return -1;
PsiExpressionStatement statement = (PsiExpressionStatement)assignment.getParent();
int startOffset = decl.getTextRange().getStartOffset();
try {
PsiDeclarationStatement newDecl = factory.createVariableDeclarationStatement(var.getName(), var.getType(), initializerExpression);
PsiVariable newVar = (PsiVariable)newDecl.getDeclaredElements()[0];
if (var.getModifierList().getText().length() > 0) {
PsiUtil.setModifierProperty(newVar, PsiModifier.FINAL, true);
}
newVar.getModifierList().replace(var.getModifierList());
PsiVariable variable = (PsiVariable)newDecl.getDeclaredElements()[0];
final int offsetBeforeEQ = variable.getNameIdentifier().getTextRange().getEndOffset();
final int offsetAfterEQ = variable.getInitializer().getTextRange().getStartOffset() + 1;
newDecl = (PsiDeclarationStatement)CodeStyleManager.getInstance(psiManager).reformatRange(newDecl, offsetBeforeEQ, offsetAfterEQ);
PsiElement child = statement.getLastChild();
while (child instanceof PsiComment || child instanceof PsiWhiteSpace) {
child = child.getPrevSibling();
}
if (child != null && child.getNextSibling() != null) {
newDecl.addRangeBefore(child.getNextSibling(), statement.getLastChild(), null);
}
decl.replace(newDecl);
statement.delete();
return startOffset + newDecl.getTextRange().getEndOffset() - newDecl.getTextRange().getStartOffset();
}
catch (IncorrectOperationException e) {
LOG.error(e);
return -1;
}
}
public static PsiExpression getInitializerExpression(PsiLocalVariable var,
PsiAssignmentExpression assignment) {
PsiExpression initializerExpression;
final IElementType originalOpSign = assignment.getOperationTokenType();
if (originalOpSign == JavaTokenType.EQ) {
initializerExpression = assignment.getRExpression();
}
else {
if (var.getInitializer() == null) return -1;
if (var.getInitializer() == null) return null;
String opSign = null;
if (originalOpSign == JavaTokenType.ANDEQ) {
opSign = "&";
@@ -107,46 +146,16 @@ public class DeclarationJoinLinesHandler implements JoinLinesHandlerDelegate {
}
try {
initializerExpression =
factory.createExpressionFromText(var.getInitializer().getText() + opSign + assignment.getRExpression().getText(), var);
initializerExpression = (PsiExpression)CodeStyleManager.getInstance(psiManager).reformat(initializerExpression);
final Project project = var.getProject();
final String initializerText = var.getInitializer().getText() + opSign + assignment.getRExpression().getText();
initializerExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(initializerText, var);
initializerExpression = (PsiExpression)CodeStyleManager.getInstance(project).reformat(initializerExpression);
}
catch (IncorrectOperationException e) {
LOG.error(e);
return -1;
return null;
}
}
PsiExpressionStatement statement = (PsiExpressionStatement)assignment.getParent();
int startOffset = decl.getTextRange().getStartOffset();
try {
PsiDeclarationStatement newDecl = factory.createVariableDeclarationStatement(var.getName(), var.getType(), initializerExpression);
PsiVariable newVar = (PsiVariable)newDecl.getDeclaredElements()[0];
if (var.getModifierList().getText().length() > 0) {
PsiUtil.setModifierProperty(newVar, PsiModifier.FINAL, true);
}
newVar.getModifierList().replace(var.getModifierList());
PsiVariable variable = (PsiVariable)newDecl.getDeclaredElements()[0];
final int offsetBeforeEQ = variable.getNameIdentifier().getTextRange().getEndOffset();
final int offsetAfterEQ = variable.getInitializer().getTextRange().getStartOffset() + 1;
newDecl = (PsiDeclarationStatement)CodeStyleManager.getInstance(psiManager).reformatRange(newDecl, offsetBeforeEQ, offsetAfterEQ);
PsiElement child = statement.getLastChild();
while (child instanceof PsiComment || child instanceof PsiWhiteSpace) {
child = child.getPrevSibling();
}
if (child != null && child.getNextSibling() != null) {
newDecl.addRangeBefore(child.getNextSibling(), statement.getLastChild(), null);
}
decl.replace(newDecl);
statement.delete();
return startOffset + newDecl.getTextRange().getEndOffset() - newDecl.getTextRange().getStartOffset();
}
catch (IncorrectOperationException e) {
LOG.error(e);
return -1;
}
return initializerExpression;
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2000-2014 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.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.editorActions.DeclarationJoinLinesHandler;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public class JoinDeclarationAndAssignmentAction extends PsiElementBaseIntentionAction {
@Override
@NotNull
public String getFamilyName() {
return CodeInsightBundle.message("intention.join.declaration.family");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
if (element instanceof PsiCompiledElement) return false;
if (!element.getManager().isInProject(element)) return false;
if (!element.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false;
if (getPair(element) != null) {
setText(CodeInsightBundle.message("intention.join.declaration.text"));
return true;
}
return false;
}
private static Pair<PsiLocalVariable, PsiAssignmentExpression> getPair(PsiElement element) {
PsiAssignmentExpression assignmentExpression = PsiTreeUtil.getParentOfType(element, PsiAssignmentExpression.class);
PsiDeclarationStatement declarationStatement = PsiTreeUtil.getParentOfType(element, PsiDeclarationStatement.class);
if (declarationStatement != null) {
assignmentExpression = getAssignmentStatement(declarationStatement);
} else if (assignmentExpression != null) {
declarationStatement = getDeclarationStatement(assignmentExpression);
}
if (declarationStatement != null && assignmentExpression != null) {
final PsiExpression lExpression = assignmentExpression.getLExpression();
final PsiExpression rExpression = assignmentExpression.getRExpression();
if (lExpression instanceof PsiReferenceExpression && rExpression != null) {
final PsiElement resolve = ((PsiReferenceExpression)lExpression).resolve();
if (resolve instanceof PsiLocalVariable && resolve.getParent() == declarationStatement) {
final PsiLocalVariable variable = (PsiLocalVariable)resolve;
if (ReferencesSearch.search(variable, new LocalSearchScope(rExpression), false).findFirst() != null) {
return null;
}
return Pair.create(variable, assignmentExpression);
}
}
}
return null;
}
private static PsiAssignmentExpression getAssignmentStatement(PsiDeclarationStatement statement) {
final PsiElement element = PsiTreeUtil.skipSiblingsForward(statement, PsiWhiteSpace.class);
if (element instanceof PsiExpressionStatement) {
final PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
if (expression instanceof PsiAssignmentExpression) {
return (PsiAssignmentExpression)expression;
}
}
return null;
}
private static PsiDeclarationStatement getDeclarationStatement(PsiAssignmentExpression assignmentExpression) {
final PsiElement parent = assignmentExpression.getParent();
if (parent instanceof PsiExpressionStatement) {
final PsiElement element = PsiTreeUtil.skipSiblingsBackward(parent, PsiWhiteSpace.class);
if (element instanceof PsiDeclarationStatement) {
return (PsiDeclarationStatement)element;
}
}
return null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
final Pair<PsiLocalVariable, PsiAssignmentExpression> pair = getPair(element);
final PsiLocalVariable variable = pair.getFirst();
final PsiAssignmentExpression assignmentExpression = pair.getSecond();
final PsiExpression initializerExpression = DeclarationJoinLinesHandler.getInitializerExpression(variable, assignmentExpression);
variable.setInitializer(initializerExpression);
assignmentExpression.delete();
}
}
@@ -0,0 +1,6 @@
// "Join declaration and assignment" "true"
class Test {
{
String ss = "hello";
}
}
@@ -0,0 +1,6 @@
// "Join declaration and assignment" "true"
class Test {
{
String ss = "hello", a = "";
}
}
@@ -0,0 +1,6 @@
// "Join declaration and assignment" "true"
class Test {
{
String ss = "hello" + "world";
}
}
@@ -0,0 +1,7 @@
// "Join declaration and assignment" "true"
class Test {
{
String s<caret>s = "";
ss = "hello";
}
}
@@ -0,0 +1,8 @@
// "Join declaration and assignment" "false"
class Test {
{
String s<caret>s = "";
System.out.println(ss);
ss = "hello";
}
}
@@ -0,0 +1,7 @@
// "Join declaration and assignment" "true"
class Test {
{
String s<caret>s = "", a = "";
ss = "hello";
}
}
@@ -0,0 +1,7 @@
// "Join declaration and assignment" "true"
class Test {
{
String ss = "hello";
ss +<caret>= "world";
}
}
@@ -0,0 +1,7 @@
// "Join declaration and assignment" "false"
class Test {
{
String ss = "hello";
ss +<caret>= ss;
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2014 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.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
public class JoinDeclarationTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/joinDeclaration";
}
}
@@ -16,9 +16,6 @@
package com.intellij.codeInsight.intention;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInsight.intention.impl.SplitIfAction;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.testFramework.LightCodeInsightTestCase;
public class SplitDeclarationTest extends LightQuickFixParameterizedTestCase {
@@ -151,7 +151,9 @@ i18nize.dialog.template.link.label=Edit I18n template
i18nize.dialog.error.property.already.defined.message=Property ''{0}'' already exists in the file ''{1}''
i18nize.dialog.error.property.already.defined.title=Property Already Exists
intention.split.declaration.family=Split Declaration
intention.join.declaration.family=Join Declaration and Assignment
intention.split.declaration.text=Split into separate declarations
intention.join.declaration.text=Join declaration and assignment
intention.split.declaration.assignment.text=Split into declaration and assignment
intention.add.override.annotation=Add '@Override' Annotation
intention.add.override.annotation.family=Add Override Annotation
@@ -0,0 +1,5 @@
public class X {
void f() {
int i = 0;
}
}
@@ -0,0 +1,6 @@
public class X {
void f() {
<spot>int i;
i = 0;</spot>
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention converts a local variable declaration and consequence assignment to the same variable into one declaration
</body>
</html>
+4
View File
@@ -744,6 +744,10 @@
<className>com.intellij.codeInsight.intention.impl.SplitDeclarationAction</className>
<category>Declaration</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.JoinDeclarationAndAssignmentAction</className>
<category>Declaration</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.PushConditionInCallAction</className>
<category>Declaration</category>