From 4163e6241acf2a602b4ec04041b66624c432fa22 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 19 Dec 2012 13:21:40 +0100 Subject: [PATCH 1/3] rename run configuration group (PY-8310) --- python/src/com/jetbrains/python/PyBundle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 8289d9646d05..cfd37a7f6c02 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -611,8 +611,8 @@ runcfg.attest.display_name=Attests runcfg.attest.description=Python's attests run configuration ### test run configuration -runcfg.test.display_name=Python's test -runcfg.test.description=Python's test run configuration +runcfg.test.display_name=Python tests +runcfg.test.description=Python frameworks supported by PyCharm ### unittest run configuration runcfg.unittest.display_name=Unittests From c9e423dcaccaa78d3c97a781f8afec4be50e4f2a Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 19 Dec 2012 19:42:41 +0400 Subject: [PATCH 2/3] Fixed Python string utils for triple-quoted and triple-double quoted string and bytes literals --- .../jetbrains/python/PythonStringUtil.java | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/PythonStringUtil.java b/python/psi-api/src/com/jetbrains/python/PythonStringUtil.java index 1702ed7a2d0f..215362bcc22e 100644 --- a/python/psi-api/src/com/jetbrains/python/PythonStringUtil.java +++ b/python/psi-api/src/com/jetbrains/python/PythonStringUtil.java @@ -1,5 +1,6 @@ package com.jetbrains.python; +import com.google.common.collect.ImmutableList; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.TextRange; @@ -23,9 +24,11 @@ import static com.intellij.openapi.util.text.StringUtil.startsWith; /** * @author Alexei Orischenko - * Date: Nov 26, 2009 + * @author vlan */ public class PythonStringUtil { + private static final ImmutableList QUOTES = ImmutableList.of("'''", "\"\"\"", "'", "\""); + private PythonStringUtil() { } @@ -44,12 +47,11 @@ public class PythonStringUtil { public static TextRange getStringValueTextRange(@NotNull String s) { - if ((s.charAt(0) == '\'' || s.charAt(0) == '"') && (s.charAt(0) == s.charAt(s.length() - 1)) && s.length() > 1) { - return TextRange.create(1, s.length() - 1); - } - else { - return TextRange.create(0, s.length()); + final Pair quotes = getQuotes(s); + if (quotes != null) { + return TextRange.create(quotes.getFirst().length(), s.length() - quotes.getSecond().length()); } + return TextRange.allOf(s); } @NotNull @@ -169,23 +171,7 @@ public class PythonStringUtil { */ public static boolean isQuoted(@Nullable String text) { - if (text == null) { - return false; - } - if (text.toLowerCase().startsWith("u")) { - text = text.substring(1); - } - - assert text != null; - if (text.toLowerCase().startsWith("r")) { - text = text.substring(1); - } - - assert text != null; - if (text.length() > 1 && text.charAt(0) == text.charAt(text.length() - 1) && (text.charAt(0) == '\'' || text.charAt(0) == '"')) { - return true; - } - return false; + return text != null && getQuotes(text) != null; } /** @@ -197,27 +183,42 @@ public class PythonStringUtil { * UR"unicode raw string" -> (UR", ") */ @Nullable - public static Pair getQuotes(@NotNull String text) { - String first = ""; - - if (text.toLowerCase().startsWith("u")) { - first += text.substring(0, 1); - text = text.substring(1); + public static Pair getQuotes(@NotNull final String text) { + boolean start = true; + int pos = 0; + for (int i = 0; i < text.length(); i++) { + final char c = Character.toLowerCase(text.charAt(i)); + if (start) { + if (c == 'u' || c == 'r' || c == 'b') { + pos = i + 1; + } + else { + start = false; + } + } + else { + break; + } } - - if (text.toLowerCase().startsWith("r")) { - first += text.substring(0, 1); - text = text.substring(1); + final String prefix = text.substring(0, pos); + final String mainText = text.substring(pos); + for (String quote : QUOTES) { + final Pair quotes = getQuotes(mainText, prefix, quote); + if (quotes != null) { + return quotes; + } } + return null; + } - int last = text.length() - 1; - - if (text.length() > 2 && (text.charAt(0) == '\'' || text.charAt(0) == '"') && (text.charAt(last) == '\'' || text.charAt(last) == '"')) { - return Pair.create(first + text.substring(0, 1), text.substring(last)); - } - else { - return null; + @Nullable + private static Pair getQuotes(@NotNull String text, @NotNull String prefix, @NotNull String quote) { + final int length = text.length(); + final int n = quote.length(); + if (length >= 2 * n && text.startsWith(quote) && text.endsWith(quote)) { + return Pair.create(prefix + text.substring(0, n), text.substring(length - n)); } + return null; } @Nullable From 9d80d6f98c4c1732fc28c1d6cf18f28fb924a765 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 19 Dec 2012 19:44:10 +0400 Subject: [PATCH 3/3] Initial implementation of introduce refactoring for substrings (PY-3654) --- .../jetbrains/python/psi/impl/PyPsiUtils.java | 38 ++++++- .../python/refactoring/PyRefactoringUtil.java | 16 +++ .../introduce/IntroduceHandler.java | 99 ++++++++++++------- .../introduceVariable/bytesSubstring.after.py | 2 + .../introduceVariable/bytesSubstring.py | 1 + .../leftQuoteSubstring.after.py | 2 + .../introduceVariable/leftQuoteSubstring.py | 1 + .../introduceVariable/leftSubstring.after.py | 2 + .../introduceVariable/leftSubstring.py | 1 + .../middleSubstring.after.py | 2 + .../introduceVariable/middleSubstring.py | 1 + .../introduceVariable/rightSubstring.after.py | 2 + .../introduceVariable/rightSubstring.py | 1 + .../substringInExpression.after.py | 2 + .../substringInExpression.py | 1 + .../substringInExpressionStatement.after.py | 2 + .../substringInExpressionStatement.py | 1 + .../substringInStatement.after.py | 2 + .../introduceVariable/substringInStatement.py | 1 + .../tripleQuotedSubstring.after.py | 6 ++ .../tripleQuotedSubstring.py | 5 + .../refactoring/PyIntroduceVariableTest.java | 45 +++++++++ 22 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 python/testData/refactoring/introduceVariable/bytesSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/bytesSubstring.py create mode 100644 python/testData/refactoring/introduceVariable/leftQuoteSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/leftQuoteSubstring.py create mode 100644 python/testData/refactoring/introduceVariable/leftSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/leftSubstring.py create mode 100644 python/testData/refactoring/introduceVariable/middleSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/middleSubstring.py create mode 100644 python/testData/refactoring/introduceVariable/rightSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/rightSubstring.py create mode 100644 python/testData/refactoring/introduceVariable/substringInExpression.after.py create mode 100644 python/testData/refactoring/introduceVariable/substringInExpression.py create mode 100644 python/testData/refactoring/introduceVariable/substringInExpressionStatement.after.py create mode 100644 python/testData/refactoring/introduceVariable/substringInExpressionStatement.py create mode 100644 python/testData/refactoring/introduceVariable/substringInStatement.after.py create mode 100644 python/testData/refactoring/introduceVariable/substringInStatement.py create mode 100644 python/testData/refactoring/introduceVariable/tripleQuotedSubstring.after.py create mode 100644 python/testData/refactoring/introduceVariable/tripleQuotedSubstring.py 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 598501ef7617..4b398df5e41a 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 @@ -15,6 +15,7 @@ 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; @@ -71,15 +72,42 @@ public class PyPsiUtils { @NotNull final PsiElement newExpression) { final Pair data = oldExpression.getUserData(SELECTION_BREAKS_AST_NODE); if (data != null) { - final PsiElement parent = data.first; + final PsiElement element = data.first; final TextRange textRange = data.second; - final String parentText = parent.getText(); + final String parentText = element.getText(); final String prefix = parentText.substring(0, textRange.getStartOffset()); - final String suffix = parentText.substring(textRange.getEndOffset(), parent.getTextLength()); + final String suffix = parentText.substring(textRange.getEndOffset(), element.getTextLength()); final PyElementGenerator generator = PyElementGenerator.getInstance(oldExpression.getProject()); final LanguageLevel languageLevel = LanguageLevel.forElement(oldExpression); - final PsiElement expression = generator.createFromText(languageLevel, parent.getClass(), prefix + newExpression.getText() + suffix); - return parent.replace(expression); + 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); diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java index ff946a310991..03a61fa3817f 100644 --- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java @@ -42,6 +42,22 @@ public class PyRefactoringUtil { occurrences.add(element); return; } + if (element instanceof PyStringLiteralExpression) { + final Pair selection = pattern.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + if (selection != null) { + final String substring = selection.getSecond().substring(pattern.getText()); + final PyStringLiteralExpression expr = (PyStringLiteralExpression)element; + final String text = element.getText(); + 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()))); + occurrences.add(element); + return; + } + } + } + } element.acceptChildren(this); } }; diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index a330bb006be8..0e5a53ee7f27 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -29,6 +29,8 @@ import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.Function; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.PythonStringUtil; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.resolve.PyResolveContext; @@ -44,22 +46,23 @@ import java.util.*; /** * @author Alexey.Ivanov + * @author vlan */ abstract public class IntroduceHandler implements RefactoringActionHandler { protected static PsiElement findAnchor(List occurrences) { PsiElement anchor = occurrences.get(0); next: do { - PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class); - - final PsiElement parent = statement.getParent(); - for (PsiElement element : occurrences) { - if (!PsiTreeUtil.isAncestor(parent, element, true)) { - anchor = statement; - continue next; + final PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class); + if (statement != null) { + final PsiElement parent = statement.getParent(); + for (PsiElement element : occurrences) { + if (!PsiTreeUtil.isAncestor(parent, element, true)) { + anchor = statement; + continue next; + } } } - return statement; } while (true); @@ -112,7 +115,7 @@ 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) { + if (statement.getExpression() == expression && expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) == null) { statement.delete(); return null; } @@ -169,6 +172,10 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } }; String text = expression.getText(); + final Pair selection = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + if (selection != null) { + text = selection.getSecond().substring(text); + } if (expression instanceof PyCallExpression) { final PyExpression callee = ((PyCallExpression)expression).getCallee(); if (callee != null) { @@ -263,13 +270,26 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { return; } - // Introduce refactoring for substrings is not supported yet - TextRange r = element1.getTextRange(); - if (singleElementSelection && element1 instanceof PyStringLiteralExpression && - (r.getStartOffset() < selectionModel.getSelectionStart() || r.getEndOffset() > selectionModel.getSelectionEnd())) { - showCannotPerformError(project, editor); - return; + if (singleElementSelection && element1 instanceof PyStringLiteralExpression) { + // TODO: Protect against escapes + // TODO: Protect against substrings with format characters + // TODO: Handle extracting substring from a string with formatting + final PyStringLiteralExpression literal = (PyStringLiteralExpression)element1; + if (literal.getStringNodes().size() > 1) { + showCannotPerformError(project, editor); + return; + } + final int offset = element1.getTextOffset(); + final TextRange selectionRange = TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + final TextRange elementRange = element1.getTextRange(); + if (!elementRange.equals(selectionRange) && elementRange.contains(selectionRange)) { + final TextRange innerRange = literal.getStringValueTextRange(); + final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange); + final TextRange finalRange = intersection != null ? intersection : selectionRange; + element1.putUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange)); + } } + if (!checkIntroduceContext(file, editor, element1)) { return; } @@ -357,10 +377,10 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { (PyExpression)element; operation.setInitializer(initializer); - if (initializer.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) == null) { + if (initializer != null) { operation.setOccurrences(getOccurrences(element, initializer)); + operation.setSuggestedNames(getSuggestedNames(initializer)); } - operation.setSuggestedNames(getSuggestedNames(initializer)); if (operation.getOccurrences().size() == 0) { operation.setReplaceAll(false); } @@ -453,21 +473,33 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { @Override public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { - ASTNode child = node.getNode().getFirstChildNode(); - while (child != null) { - String text = child.getText(); - if (child.getElementType() == TokenType.WHITE_SPACE) { - if (text.contains("\n")) { - if (!text.contains("\\")) { - myResult.append("\\"); + final Pair data = node.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE); + if (data != null) { + final PsiElement parent = data.getFirst(); + final String text = parent.getText(); + final Pair detectedQuotes = PythonStringUtil.getQuotes(text); + final Pair quotes = detectedQuotes != null ? detectedQuotes : Pair.create("'", "'"); + final TextRange range = data.getSecond(); + final String substring = range.substring(text); + myResult.append(quotes.getFirst() + substring + quotes.getSecond()); + } + else { + ASTNode child = node.getNode().getFirstChildNode(); + while (child != null) { + String text = child.getText(); + if (child.getElementType() == TokenType.WHITE_SPACE) { + if (text.contains("\n")) { + if (!text.contains("\\")) { + myResult.append("\\"); + } + myResult.append(text); } + } + else { myResult.append(text); } + child = child.getTreeNext(); } - else { - myResult.append(text); - } - child = child.getTreeNext(); } } @@ -498,14 +530,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } protected List getOccurrences(PsiElement element, @NotNull final PyExpression expression) { - PsiElement context = PsiTreeUtil.getParentOfType(expression, PyFunction.class); - if (context == null) { - context = PsiTreeUtil.getParentOfType(expression, PyClass.class); - } - if (context == null) { - context = expression.getContainingFile(); - } - return PyRefactoringUtil.getOccurrences(expression, context); + return PyRefactoringUtil.getOccurrences(expression, ScopeUtil.getScopeOwner(expression)); } private PsiElement performReplace(@NotNull final PsiElement declaration, @@ -551,7 +576,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } protected PyExpression createExpression(Project project, String name, PsiElement declaration) { - return PyElementGenerator.getInstance(project).createExpressionFromText(name); + return PyElementGenerator.getInstance(project).createExpressionFromText(LanguageLevel.forElement(declaration), name); } @Nullable diff --git a/python/testData/refactoring/introduceVariable/bytesSubstring.after.py b/python/testData/refactoring/introduceVariable/bytesSubstring.after.py new file mode 100644 index 000000000000..67a86c8749cf --- /dev/null +++ b/python/testData/refactoring/introduceVariable/bytesSubstring.after.py @@ -0,0 +1,2 @@ +a = b'bar' +b'foo' + a + b'baz' \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/bytesSubstring.py b/python/testData/refactoring/introduceVariable/bytesSubstring.py new file mode 100644 index 000000000000..6f98337f4121 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/bytesSubstring.py @@ -0,0 +1 @@ +b'foobarbaz' \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/leftQuoteSubstring.after.py b/python/testData/refactoring/introduceVariable/leftQuoteSubstring.after.py new file mode 100644 index 000000000000..66921e9c1aa1 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/leftQuoteSubstring.after.py @@ -0,0 +1,2 @@ +a = "hello" +print(a + " world") diff --git a/python/testData/refactoring/introduceVariable/leftQuoteSubstring.py b/python/testData/refactoring/introduceVariable/leftQuoteSubstring.py new file mode 100644 index 000000000000..4c4486bf7ccd --- /dev/null +++ b/python/testData/refactoring/introduceVariable/leftQuoteSubstring.py @@ -0,0 +1 @@ +print("hello world") diff --git a/python/testData/refactoring/introduceVariable/leftSubstring.after.py b/python/testData/refactoring/introduceVariable/leftSubstring.after.py new file mode 100644 index 000000000000..d21668605427 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/leftSubstring.after.py @@ -0,0 +1,2 @@ +a = "hello" +print(a + " world") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/leftSubstring.py b/python/testData/refactoring/introduceVariable/leftSubstring.py new file mode 100644 index 000000000000..e981bd05c293 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/leftSubstring.py @@ -0,0 +1 @@ +print("hello world") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/middleSubstring.after.py b/python/testData/refactoring/introduceVariable/middleSubstring.after.py new file mode 100644 index 000000000000..148c9c774e8e --- /dev/null +++ b/python/testData/refactoring/introduceVariable/middleSubstring.after.py @@ -0,0 +1,2 @@ +a = "lo wor" +print("hel" + a + "ld") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/middleSubstring.py b/python/testData/refactoring/introduceVariable/middleSubstring.py new file mode 100644 index 000000000000..938db7bd1c1e --- /dev/null +++ b/python/testData/refactoring/introduceVariable/middleSubstring.py @@ -0,0 +1 @@ +print("hello world") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/rightSubstring.after.py b/python/testData/refactoring/introduceVariable/rightSubstring.after.py new file mode 100644 index 000000000000..fa468b6341c7 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/rightSubstring.after.py @@ -0,0 +1,2 @@ +a = "world" +print("hello " + a) \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/rightSubstring.py b/python/testData/refactoring/introduceVariable/rightSubstring.py new file mode 100644 index 000000000000..faaede5b7ddb --- /dev/null +++ b/python/testData/refactoring/introduceVariable/rightSubstring.py @@ -0,0 +1 @@ +print("hello world") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInExpression.after.py b/python/testData/refactoring/introduceVariable/substringInExpression.after.py new file mode 100644 index 000000000000..3420cf9bf534 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInExpression.after.py @@ -0,0 +1,2 @@ +a = 'foo' +print((a + 'bar').upper()) \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInExpression.py b/python/testData/refactoring/introduceVariable/substringInExpression.py new file mode 100644 index 000000000000..ef56fc02a263 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInExpression.py @@ -0,0 +1 @@ +print('foobar'.upper()) \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInExpressionStatement.after.py b/python/testData/refactoring/introduceVariable/substringInExpressionStatement.after.py new file mode 100644 index 000000000000..c4b048f0ab3d --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInExpressionStatement.after.py @@ -0,0 +1,2 @@ +a = "two" +"one " + a + " three" \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInExpressionStatement.py b/python/testData/refactoring/introduceVariable/substringInExpressionStatement.py new file mode 100644 index 000000000000..e81c165f4255 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInExpressionStatement.py @@ -0,0 +1 @@ +"one two three" \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInStatement.after.py b/python/testData/refactoring/introduceVariable/substringInStatement.after.py new file mode 100644 index 000000000000..4027559cab28 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInStatement.after.py @@ -0,0 +1,2 @@ +a = 'foo' +x = a + 'bar' \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/substringInStatement.py b/python/testData/refactoring/introduceVariable/substringInStatement.py new file mode 100644 index 000000000000..526ad2c55ad5 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringInStatement.py @@ -0,0 +1 @@ +x = 'foobar' \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.after.py b/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.after.py new file mode 100644 index 000000000000..5389de87a910 --- /dev/null +++ b/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.after.py @@ -0,0 +1,6 @@ +a = """Three""" +print(""""One two + +* """ + a + """ +* Four +* Five""") \ No newline at end of file diff --git a/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.py b/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.py new file mode 100644 index 000000000000..5972d20c474c --- /dev/null +++ b/python/testData/refactoring/introduceVariable/tripleQuotedSubstring.py @@ -0,0 +1,5 @@ +print(""""One two + +* Three +* Four +* Five""") \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java index 1ce1af50e248..5a28cd82f9f5 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -88,6 +88,51 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase { doTest(); } + // PY-3654 + public void testLeftSubstring() { + doTest(); + } + + // PY-3654 + public void testRightSubstring() { + doTest(); + } + + // PY-3654 + public void testMiddleSubstring() { + doTest(); + } + + // PY-3654 + public void testLeftQuoteSubstring() { + doTest(); + } + + // PY-3654 + public void testSubstringInExpression() { + doTest(); + } + + // PY-3654 + public void testSubstringInStatement() { + doTest(); + } + + // PY-3654 + public void testTripleQuotedSubstring() { + doTest(); + } + + // PY-3654 + public void testSubstringInExpressionStatement() { + doTest(); + } + + // PY-6354 + public void testBytesSubstring() { + doTest(); + } + private void doTestCannotPerform() { boolean thrownExpectedException = false; try {