From e21a93097772ab527d8f152e6c2a0de24f3ec7d9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 28 Dec 2012 21:38:42 +0400 Subject: [PATCH] Introduce refactoring for substrings of %-formatted strings with single value (PY-3654) --- .../refactoring/PyReplaceExpressionUtil.java | 39 +++++++++++++++++++ .../substringFromFormatSingleValue.after.py | 2 + .../substringFromFormatSingleValue.py | 1 + .../refactoring/PyIntroduceVariableTest.java | 5 +++ 4 files changed, 47 insertions(+) create mode 100644 python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.after.py create mode 100644 python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.py diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index b3fb0836cf3b..9974d3899f7a 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -12,7 +12,12 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PythonStringUtil; import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.PyTypeChecker; +import com.jetbrains.python.psi.types.PyTypeParser; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -144,6 +149,40 @@ public class PyReplaceExpressionUtil implements PyElementTypes { final PsiElement newElement = valueExpression.replace(newDictLiteral); return newElement.findElementAt(pos); } + else { + final TypeEvalContext context = TypeEvalContext.slow(); + final PyType valueType = valueExpression.getType(context); + final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(oldExpression); + final PyType tupleType = builtinCache.getTupleType(); + final PyType mappingType = PyTypeParser.getTypeByName(null, "collections.Mapping"); + if (!PyTypeChecker.match(tupleType, valueType, context) || + (mappingType != null && !PyTypeChecker.match(mappingType, valueType, context))) { + // 'foo%s' % value if value is not tuple or mapping -> '%s%s' % (s, value) + final String newLiteralText = prefix + "%s" + suffix; + final PyStringLiteralExpression newLiteralExpression = generator.createStringLiteralAlreadyEscaped(newLiteralText); + oldExpression.replace(newLiteralExpression); + final StringBuilder builder = new StringBuilder(); + builder.append("("); + final List positional = PyStringFormatParser.getPositionalSubstitutions(substitutions); + final int i = getPositionInRanges(PyStringFormatParser.substitutionsToRanges(positional), textRange); + final int pos; + if (i == 0) { + pos = builder.toString().length(); + builder.append(newText); + builder.append(","); + builder.append(valueExpression.getText()); + } + else { + builder.append(valueExpression.getText()); + builder.append(","); + pos = builder.toString().length(); + builder.append(newText); + } + builder.append(")"); + final PsiElement newElement = valueExpression.replace(generator.createExpressionFromText(languageLevel, builder.toString())); + return newElement.findElementAt(pos); + } + } } if (isConcatFormatting(oldExpression) || substitutions.size() > 0) { diff --git a/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.after.py b/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.after.py new file mode 100644 index 000000000000..273d13f6bc84 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.after.py @@ -0,0 +1,2 @@ +a = "Hello" +print("%s %s" % (a, "World")) diff --git a/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.py b/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.py new file mode 100644 index 000000000000..55ef4171f531 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringFromFormatSingleValue.py @@ -0,0 +1 @@ +print("Hello %s" % "World") diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java index eb631714e6c7..7b6f14c60821 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -183,6 +183,11 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase { doTest(); } + // PY-3654 + public void testSubstringFromFormatSingleValue() { + doTest(); + } + private void doTestCannotPerform() { boolean thrownExpectedException = false; try {