From f60d2d018d206e55f3b5174bfd82fc42036e5df1 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 27 Dec 2012 18:10:12 +0400 Subject: [PATCH] Moved replace expression to PyReplaceExpressionUtil --- .../jetbrains/python/psi/impl/PyPsiUtils.java | 52 --------------- .../python/refactoring/PyRefactoringUtil.java | 7 +- .../refactoring/PyReplaceExpressionUtil.java | 65 +++++++++++++++++++ .../extractmethod/PyExtractMethodUtil.java | 5 +- .../introduce/IntroduceHandler.java | 14 ++-- .../introduce/IntroduceValidator.java | 6 +- .../constant/PyIntroduceConstantHandler.java | 4 +- .../field/PyIntroduceFieldHandler.java | 4 +- .../PyIntroduceParameterHandler.java | 4 +- 9 files changed, 87 insertions(+), 74 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java index 4b398df5e41a..34981829d251 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyPsiUtils.java @@ -4,9 +4,6 @@ import com.google.common.collect.Lists; import com.intellij.extapi.psi.ASTDelegatePsiElement; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.stubs.StubElement; @@ -15,7 +12,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.PythonStringUtil; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,8 +25,6 @@ import java.util.List; * @author max */ public class PyPsiUtils { - public static final Key> SELECTION_BREAKS_AST_NODE = - new Key>("python.selection.breaks.ast.node"); private static final Logger LOG = Logger.getInstance(PyPsiUtils.class.getName()); private PyPsiUtils() { @@ -68,52 +62,6 @@ public class PyPsiUtils { return node; } - public static PsiElement replaceExpression(@NotNull final PsiElement oldExpression, - @NotNull final PsiElement newExpression) { - final Pair data = oldExpression.getUserData(SELECTION_BREAKS_AST_NODE); - if (data != null) { - final PsiElement element = data.first; - final TextRange textRange = data.second; - final String parentText = element.getText(); - final String prefix = parentText.substring(0, textRange.getStartOffset()); - final String suffix = parentText.substring(textRange.getEndOffset(), element.getTextLength()); - final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject()); - final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression); - if (element instanceof PyStringLiteralExpression) { - final Pair quotes = PythonStringUtil.getQuotes(parentText); - final PsiElement parent = element.getParent(); - final boolean parensNeeded = parent instanceof PyExpression && !(parent instanceof PyParenthesizedExpression); - if (quotes != null) { - final String leftQuote = quotes.getFirst(); - final String rightQuote = quotes.getSecond(); - final StringBuilder builder = new StringBuilder(); - if (parensNeeded) { - builder.append("("); - } - if (!leftQuote.endsWith(prefix)) { - builder.append(prefix + rightQuote + " + "); - } - final int pos = builder.toString().length(); - builder.append(newExpression.getText()); - if (!rightQuote.startsWith(suffix)) { - builder.append(" + " + leftQuote + suffix); - } - if (parensNeeded) { - builder.append(")"); - } - final PsiElement expression = generator.createExpressionFromText(languageLevel, builder.toString()); - final PsiElement newElement = element.replace(expression); - return newElement.findElementAt(pos); - } - } - final PsiElement expression = generator.createFromText(languageLevel, element.getClass(), prefix + newExpression.getText() + suffix); - return element.replace(expression); - } - else { - return oldExpression.replace(newExpression); - } - } - public static void addBeforeInParent(@NotNull final PsiElement anchor, @NotNull final PsiElement... newElements) { final ASTNode anchorNode = anchor.getNode(); LOG.assertTrue(anchorNode != null); diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java index 03a61fa3817f..e3c9bba6c4dc 100644 --- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java @@ -14,7 +14,6 @@ import com.intellij.util.Processor; import com.intellij.util.containers.HashSet; import com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.impl.PyPsiUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,7 +42,7 @@ public class PyRefactoringUtil { return; } if (element instanceof PyStringLiteralExpression) { - final Pair selection = pattern.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + final Pair selection = pattern.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (selection != null) { final String substring = selection.getSecond().substring(pattern.getText()); final PyStringLiteralExpression expr = (PyStringLiteralExpression)element; @@ -51,7 +50,7 @@ public class PyRefactoringUtil { if (text != null && expr.getStringNodes().size() == 1) { final int start = text.indexOf(substring); if (start >= 0) { - element.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(element, TextRange.from(start, substring.length()))); + element.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element, TextRange.from(start, substring.length()))); occurrences.add(element); return; } @@ -113,7 +112,7 @@ public class PyRefactoringUtil { return null; } - expression.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(parent, textRange)); + expression.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(parent, textRange)); return expression; } return null; diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index b4575c196ec1..e0e1a0b06809 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -1,9 +1,15 @@ package com.jetbrains.python.refactoring; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.jetbrains.python.PyElementTypes; +import com.jetbrains.python.PythonStringUtil; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static com.jetbrains.python.PyTokenTypes.*; @@ -11,6 +17,8 @@ import static com.jetbrains.python.PyTokenTypes.*; * @author Dennis.Ushakov */ public class PyReplaceExpressionUtil implements PyElementTypes { + public static final Key> SELECTION_BREAKS_AST_NODE = + new Key>("python.selection.breaks.ast.node"); private PyReplaceExpressionUtil() {} @@ -37,6 +45,63 @@ public class PyReplaceExpressionUtil implements PyElementTypes { return false; } + public static PsiElement replaceExpression(@NotNull final PsiElement oldExpression, + @NotNull final PsiElement newExpression) { + final Pair data = oldExpression.getUserData(SELECTION_BREAKS_AST_NODE); + if (data != null) { + final PsiElement element = data.first; + final TextRange textRange = data.second; + final String parentText = element.getText(); + final String prefix = parentText.substring(0, textRange.getStartOffset()); + final String suffix = parentText.substring(textRange.getEndOffset(), element.getTextLength()); + final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject()); + final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression); + if (element instanceof PyStringLiteralExpression) { + return replaceSubstringInStringLiteral((PyStringLiteralExpression)element, newExpression, textRange); + } + final PsiElement expression = generator.createFromText(languageLevel, element.getClass(), prefix + newExpression.getText() + suffix); + return element.replace(expression); + } + else { + return oldExpression.replace(newExpression); + } + } + + @Nullable + private static PsiElement replaceSubstringInStringLiteral(@NotNull PyStringLiteralExpression oldExpression, + @NotNull PsiElement newExpression, + @NotNull TextRange textRange) { + final String fullText = oldExpression.getText(); + final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject()); + final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression); + final String prefix = fullText.substring(0, textRange.getStartOffset()); + final String suffix = fullText.substring(textRange.getEndOffset(), oldExpression.getTextLength()); + final Pair detectedQuotes = PythonStringUtil.getQuotes(fullText); + final Pair quotes = detectedQuotes != null ? detectedQuotes : Pair.create("'", "'"); + final PsiElement parent = oldExpression.getParent(); + final boolean parensNeeded = parent instanceof PyExpression && !(parent instanceof PyParenthesizedExpression); + final String leftQuote = quotes.getFirst(); + final String rightQuote = quotes.getSecond(); + final StringBuilder builder = new StringBuilder(); + if (parensNeeded) { + builder.append("("); + } + if (!leftQuote.endsWith(prefix)) { + builder.append(prefix + rightQuote + " + "); + } + final int pos = builder.toString().length(); + builder.append(newExpression.getText()); + if (!rightQuote.startsWith(suffix)) { + builder.append(" + " + leftQuote + suffix); + } + if (parensNeeded) { + builder.append(")"); + } + final PsiElement expression = generator.createExpressionFromText(languageLevel, builder.toString()); + final PsiElement newElement = oldExpression.replace(expression); + return newElement.findElementAt(pos); + } + private static boolean isNotAssociative(@NotNull final PyBinaryExpression binaryExpression) { final IElementType opType = getOperationType(binaryExpression); return COMPARISON_OPERATIONS.contains(opType) || binaryExpression instanceof PySliceExpression || diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index c72833ce298b..b71249e7e40a 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -41,6 +41,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyFunctionBuilder; import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -308,7 +309,7 @@ public class PyExtractMethodUtil { // replace statements with call if (callElement != null) { - callElement = PyPsiUtils.replaceExpression(expression, callElement); + callElement = PyReplaceExpressionUtil.replaceExpression(expression, callElement); } // Set editor @@ -397,7 +398,7 @@ public class PyExtractMethodUtil { } private static PyFunction insertGeneratedMethod(PsiElement anchor, final PyFunction generatedMethod) { - final Pair data = anchor.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + final Pair data = anchor.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (data != null) { anchor = data.first; } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 13bedbc0d2b7..20f093f3f85a 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -33,13 +33,13 @@ import com.jetbrains.python.PythonStringUtil; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.types.PyNoneType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.refactoring.NameSuggesterUtil; import com.jetbrains.python.refactoring.PyRefactoringUtil; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -116,12 +116,12 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) { PyExpressionStatement statement = PsiTreeUtil.getParentOfType(expression, PyExpressionStatement.class); if (statement != null) { - if (statement.getExpression() == expression && expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) == null) { + if (statement.getExpression() == expression && expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE) == null) { statement.delete(); return null; } } - return PyPsiUtils.replaceExpression(expression, newExpression); + return PyReplaceExpressionUtil.replaceExpression(expression, newExpression); } private final IntroduceValidator myValidator; @@ -173,7 +173,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } }; String text = expression.getText(); - final Pair selection = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + final Pair selection = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (selection != null) { text = selection.getSecond().substring(text); } @@ -293,7 +293,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { showCannotPerformError(project, editor); return; } - element1.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange)); + element1.putUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange)); } } @@ -505,7 +505,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { @Override public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { - final Pair data = node.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + final Pair data = node.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (data != null) { final PsiElement parent = data.getFirst(); final String text = parent.getText(); @@ -598,7 +598,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { @Nullable public PsiElement addDeclaration(IntroduceOperation operation, PsiElement declaration) { final PsiElement expression = operation.getInitializer(); - final Pair data = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + final Pair data = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (data == null) { return addDeclaration(expression, declaration, operation); } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceValidator.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceValidator.java index 83b5c339d616..3d57ea6ed8bc 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceValidator.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceValidator.java @@ -12,8 +12,8 @@ import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.refactoring.PyRefactoringUtil; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,8 +40,8 @@ public abstract class IntroduceValidator { public abstract String check(String name, PsiElement psiElement); public static boolean isDefinedInScope(String name, PsiElement psiElement) { - if (psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) != null) { - final Pair data = psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + if (psiElement.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE) != null) { + final Pair data = psiElement.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); psiElement = data.first; } PsiElement context = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class); diff --git a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java index 54358b4f87c8..2302f6d17659 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java @@ -9,7 +9,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.imports.AddImportHelper; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyFile; -import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import org.jetbrains.annotations.NotNull; @@ -29,7 +29,7 @@ public class PyIntroduceConstantHandler extends IntroduceHandler { if (PsiTreeUtil.getParentOfType(expression, ScopeOwner.class) instanceof PyFile) { return super.replaceExpression(expression, newExpression, operation); } - return PyPsiUtils.replaceExpression(expression, newExpression); + return PyReplaceExpressionUtil.replaceExpression(expression, newExpression); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java index b15679205eb4..d1d717e048ae 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/field/PyIntroduceFieldHandler.java @@ -25,7 +25,7 @@ import com.jetbrains.python.inspections.quickfix.AddFieldQuickFix; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyFunctionBuilder; -import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import com.jetbrains.python.refactoring.introduce.variable.PyIntroduceVariableHandler; @@ -78,7 +78,7 @@ public class PyIntroduceFieldHandler extends IntroduceHandler { @Override protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) { if (operation.getInitPlace() != InitPlace.SAME_METHOD) { - return PyPsiUtils.replaceExpression(expression, newExpression); + return PyReplaceExpressionUtil.replaceExpression(expression, newExpression); } return super.replaceExpression(expression, newExpression, operation); } diff --git a/python/src/com/jetbrains/python/refactoring/introduce/parameter/PyIntroduceParameterHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/parameter/PyIntroduceParameterHandler.java index 5fb57aea831d..a72dce5be7dd 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/parameter/PyIntroduceParameterHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/parameter/PyIntroduceParameterHandler.java @@ -10,7 +10,7 @@ import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; import com.jetbrains.python.refactoring.introduce.IntroduceOperation; import com.jetbrains.python.refactoring.introduce.variable.VariableValidator; @@ -56,7 +56,7 @@ public class PyIntroduceParameterHandler extends IntroduceHandler { @Nullable @Override protected PsiElement replaceExpression(PsiElement expression, PyExpression newExpression, IntroduceOperation operation) { - return PyPsiUtils.replaceExpression(expression, newExpression); + return PyReplaceExpressionUtil.replaceExpression(expression, newExpression); } protected boolean isValidIntroduceContext(PsiElement element) {