diff --git a/python/python-common-tests/com/jetbrains/python/PythonCommonFormatterTest.java b/python/python-common-tests/com/jetbrains/python/PythonCommonFormatterTest.java index a197d700deaa..85fe8c516be0 100644 --- a/python/python-common-tests/com/jetbrains/python/PythonCommonFormatterTest.java +++ b/python/python-common-tests/com/jetbrains/python/PythonCommonFormatterTest.java @@ -7,6 +7,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.fixture.PythonCommonTestCase; import com.jetbrains.python.formatter.PyCodeStyleSettings; import com.jetbrains.python.psi.LanguageLevel; @@ -1058,19 +1059,37 @@ public abstract class PythonCommonFormatterTest extends PythonCommonTestCase { } // PY-27615 - public void testFStringFragmentWrappingSplitInsideExpression() { + public void testFStringFragmentWrappingSplitInsideExpressionWithBackslash() { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = false; + getCodeStyleSettings().setRightMargin(PythonLanguage.getInstance(), 20); + getCommonCodeStyleSettings().WRAP_LONG_LINES = true; + doTest(); + } + + + public void testFStringFragmentWrappingSplitInsideExpressionWithParentheses() { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = true; getCodeStyleSettings().setRightMargin(PythonLanguage.getInstance(), 20); getCommonCodeStyleSettings().WRAP_LONG_LINES = true; doTest(); } // PY-27615 - public void testFStringFragmentWrappingSplitInsideNestedExpression() { + public void testFStringFragmentWrappingSplitInsideNestedExpressionWithBackslash() { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = false; getCodeStyleSettings().setRightMargin(PythonLanguage.getInstance(), 20); getCommonCodeStyleSettings().WRAP_LONG_LINES = true; doTest(); } + // TODO enable after PY-61453 fixed + //public void testFStringFragmentWrappingSplitInsideNestedExpressionWithParentheses() { + // PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = true; + // getCodeStyleSettings().setRightMargin(PythonLanguage.getInstance(), 20); + // getCommonCodeStyleSettings().WRAP_LONG_LINES = true; + // doTest(); + //} + // PY-40778 public void testFStringSpacesBetweenFragmentAndExpressionBracesPreserved() { doTest(); diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java index d63a3c8fa67d..2a22f3b9ba14 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/PyCodeInsightSettings.java @@ -36,7 +36,7 @@ public class PyCodeInsightSettings implements PersistentStateComponentbar': - pass \ No newline at end of file diff --git a/python/testData/editing/backslashOnEnterInTopLevelStringLiteralPattern.py b/python/testData/editing/backslashOnEnterInTopLevelStringLiteralPattern.py deleted file mode 100644 index 6a13036d0158..000000000000 --- a/python/testData/editing/backslashOnEnterInTopLevelStringLiteralPattern.py +++ /dev/null @@ -1,3 +0,0 @@ -match x: - case 'foobar': - pass \ No newline at end of file diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideExpression.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithBackslash.py similarity index 100% rename from python/testData/formatter/fStringFragmentWrappingSplitInsideExpression.py rename to python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithBackslash.py diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideExpression_after.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithBackslash_after.py similarity index 100% rename from python/testData/formatter/fStringFragmentWrappingSplitInsideExpression_after.py rename to python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithBackslash_after.py diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses.py new file mode 100644 index 000000000000..85bdb670e026 --- /dev/null +++ b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses.py @@ -0,0 +1 @@ +s = f'aaaaaa{oct(42)}' \ No newline at end of file diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses_after.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses_after.py new file mode 100644 index 000000000000..c9b720699fbd --- /dev/null +++ b/python/testData/formatter/fStringFragmentWrappingSplitInsideExpressionWithParentheses_after.py @@ -0,0 +1,2 @@ +s = (f'aaaaaa' + f'{oct(42)}') diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpression.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpressionWithBackslash.py similarity index 100% rename from python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpression.py rename to python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpressionWithBackslash.py diff --git a/python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpression_after.py b/python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpressionWithBackslash_after.py similarity index 100% rename from python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpression_after.py rename to python/testData/formatter/fStringFragmentWrappingSplitInsideNestedExpressionWithBackslash_after.py diff --git a/python/testData/refactoring/inlinelocal/resultExceedsRightMargin.after.py b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithBackslash.after.py similarity index 100% rename from python/testData/refactoring/inlinelocal/resultExceedsRightMargin.after.py rename to python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithBackslash.after.py diff --git a/python/testData/refactoring/inlinelocal/resultExceedsRightMargin.before.py b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithBackslash.before.py similarity index 100% rename from python/testData/refactoring/inlinelocal/resultExceedsRightMargin.before.py rename to python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithBackslash.before.py diff --git a/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.after.py b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.after.py new file mode 100644 index 000000000000..d372b69af98a --- /dev/null +++ b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.after.py @@ -0,0 +1,3 @@ +result = ('123456789|123456789|123456789|123456789|123456789|' + + '123456789|123456789|123456789|123456789|123456789|' + + '123456789|123456789|123456789|123456789|123456789|') diff --git a/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.before.py b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.before.py new file mode 100644 index 000000000000..8910b0d40270 --- /dev/null +++ b/python/testData/refactoring/inlinelocal/resultExceedsRightMarginWithParenthesizeOnEnter.before.py @@ -0,0 +1,3 @@ +s = '123456789|123456789|123456789|123456789|123456789|' + +result = s + s + s \ No newline at end of file diff --git a/python/testData/wrap/ParenthesesOnWrap.after.py b/python/testData/wrap/ParenthesesOnWrap.after.py new file mode 100644 index 000000000000..57ae4f6ad600 --- /dev/null +++ b/python/testData/wrap/ParenthesesOnWrap.after.py @@ -0,0 +1,4 @@ +def copy_location(new_node, old_node): + for attr in 'lineno', 'col_offset': + if (attr in old_node._attributes and attr in new_node._attributes and + hasattr(old_node, attr)) \ No newline at end of file diff --git a/python/testData/wrap/ParenthesesOnWrap.py b/python/testData/wrap/ParenthesesOnWrap.py new file mode 100644 index 000000000000..4361dc9e8247 --- /dev/null +++ b/python/testData/wrap/ParenthesesOnWrap.py @@ -0,0 +1,3 @@ +def copy_location(new_node, old_node): + for attr in 'lineno', 'col_offset': + if attr in old_node._attributes and attr in new_node._attributes \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index e8cb666ece60..278f28118044 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -545,12 +545,25 @@ public class PyEditingTest extends PyTestCase { } private void doTestEnter(String before, final String after) { - int pos = before.indexOf(""); - before = before.replace("", ""); - doTestTyping(before, pos, '\n'); - myFixture.checkResult(after); + doTestEnter(before, after, false); } + private void doTestEnter(String before, final String after, boolean parenthesiseOnEnter) { + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = parenthesiseOnEnter; + int pos = before.indexOf(""); + before = before.replace("", ""); + doTestTyping(before, pos, '\n'); + myFixture.checkResult(after); + } + finally { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; + } + } + + + // PY-21478 public void testContinuationIndentForFunctionArguments() { getPythonCodeStyleSettings().USE_CONTINUATION_INDENT_FOR_ARGUMENTS = true; @@ -915,7 +928,18 @@ public class PyEditingTest extends PyTestCase { // PY-49080 public void testBackslashOnEnterInTopLevelStringLiteralPattern() { - doTypingTest('\n'); + doTestEnter(""" + match x: + case 'foobar': + pass + """, + """ + match x: + case 'foo' \\ + 'bar': + pass + """ + ); } // PY-42200 @@ -1053,51 +1077,51 @@ public class PyEditingTest extends PyTestCase { } public void testParenthesiseBinaryExpression() { - testWithParenthesiseOnEnter( + doTestEnter( """ x = a + b + c + d """, """ x = (a + b +\s c + d) - """); + """, true); } public void testParenthesiseImportStatement() { - testWithParenthesiseOnEnter( + doTestEnter( """ from collections import Hashable, Iterable, KeysView, Mapping, MutableMapping """, """ from collections import (Hashable, Iterable,\s KeysView, Mapping, MutableMapping) - """); + """, true); } public void testParenthesiseCallChain() { - testWithParenthesiseOnEnter( + doTestEnter( """ result = str.capitalize().foo().bar().baz() """, """ result = (str.capitalize() .foo().bar().baz()) - """); + """, true); } public void testParenthesiseConditionalExpression() { - testWithParenthesiseOnEnter( + doTestEnter( """ x = 3 if a == 10 or b == 13 or c < 4 else 3 """, """ x = 3 if (a == 10 or b == 13 or c < 4) else 3 - """); + """, true); } public void testParenthesiseTupleExpression() { - testWithParenthesiseOnEnter( + doTestEnter( """ for x in 'a', 'b', 'c': pass @@ -1106,55 +1130,55 @@ public class PyEditingTest extends PyTestCase { for x in ('a', 'b', 'c'): pass - """); + """, true); } public void testParenthesiseString() { - testWithParenthesiseOnEnter( + doTestEnter( """ s = "string" """, """ s = ("str" "ing") - """); + """, true); } public void testStringNotParenthesisedRepeatedly() { - testWithParenthesiseOnEnter( + doTestEnter( """ s = ("string") """, """ s = ("str" "ing") - """); + """, true); } public void testParenthesiseUnicodeString() { - testWithParenthesiseOnEnter( + doTestEnter( """ s = u"unicode" """, """ s = (u"uni" u"code") - """); + """, true); } public void testParenthesiseEscapedQuote() { - testWithParenthesiseOnEnter( + doTestEnter( """ a = 'some \\' string' """, """ a = ('some \\'' ' string') - """); + """, true); } public void testParenthesiseSequencePattern() { - testWithParenthesiseOnEnter( + doTestEnter( """ match (1, 2): case int(), int(): @@ -1165,22 +1189,22 @@ public class PyEditingTest extends PyTestCase { case (int(), int()): pass - """); + """, true); } public void testParenthesiseAttributeAccessInCallChain() { - testWithParenthesiseOnEnter( + doTestEnter( """ C().m().m().m().attr """, """ (C().m().m().m() .attr) - """); + """, true); } public void testParenthesiseWithStatement() { - testWithParenthesiseOnEnter( + doTestEnter( """ with open('foo.txt') as foo, open('bar.txt') as bar: pass @@ -1189,18 +1213,7 @@ public class PyEditingTest extends PyTestCase { with (open('foo.txt') as foo,\s open('bar.txt') as bar): pass - """); - } - - private void testWithParenthesiseOnEnter(String before, String after) { - boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; - try { - PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = true; - doTestEnter(before, after); - } - finally { - PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; - } + """, true); } @NotNull diff --git a/python/testSrc/com/jetbrains/python/PyFillParagraphTest.java b/python/testSrc/com/jetbrains/python/PyFillParagraphTest.java index e2022ad48e9d..781605f0b5b2 100644 --- a/python/testSrc/com/jetbrains/python/PyFillParagraphTest.java +++ b/python/testSrc/com/jetbrains/python/PyFillParagraphTest.java @@ -16,6 +16,7 @@ package com.jetbrains.python; import com.intellij.codeInsight.editorActions.fillParagraph.FillParagraphAction; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.fixtures.PyTestCase; /** @@ -36,7 +37,7 @@ public class PyFillParagraphTest extends PyTestCase { } public void testString() { - doTest(); + doTestWithParenthesizeOnEnter(false, 120); } public void testComment() { @@ -65,7 +66,7 @@ public class PyFillParagraphTest extends PyTestCase { // PY-26422 public void testFString() { - doTestWithMargin(20); + doTestWithParenthesizeOnEnter(false, 20); } private void doTest() { @@ -79,4 +80,14 @@ public class PyFillParagraphTest extends PyTestCase { myFixture.testAction(new FillParagraphAction()); myFixture.checkResultByFile(baseName + "_after.py", true); } + + private void doTestWithParenthesizeOnEnter(boolean enabled, int margin) { + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = enabled; + doTestWithMargin(margin); + } finally { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; + } + } } diff --git a/python/testSrc/com/jetbrains/python/PyWrapTest.java b/python/testSrc/com/jetbrains/python/PyWrapTest.java index c5c4d77c8116..2807423264d1 100644 --- a/python/testSrc/com/jetbrains/python/PyWrapTest.java +++ b/python/testSrc/com/jetbrains/python/PyWrapTest.java @@ -6,6 +6,7 @@ import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.fixtures.PyTestCase; @@ -41,7 +42,23 @@ public class PyWrapTest extends PyTestCase { } public void testBackslashOnWrap() { - doTest("and hasattr(old_node, attr):"); + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = false; + doTest("and hasattr(old_node, attr):"); + } finally { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; + } + } + + public void testParenthesesOnWrap() { + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = true; + doTest("and hasattr(old_node, attr)"); + } finally { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; + } } public void testWrapInComment() { diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java index a8e6ac67c1d4..c63c632e2e63 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java @@ -10,6 +10,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.jetbrains.python.PyPsiBundle; import com.jetbrains.python.PythonLanguage; +import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.refactoring.inline.PyInlineLocalHandler; import org.jetbrains.annotations.NotNull; @@ -98,21 +99,47 @@ public class PyInlineLocalTest extends PyTestCase { } // PY-12409 - public void testResultExceedsRightMargin() { + public void testResultExceedsRightMarginWithBackslash() { final CodeStyleSettings settings = getCodeStyleSettings(); final CommonCodeStyleSettings commonSettings = settings.getCommonSettings(PythonLanguage.getInstance()); final int oldRightMargin = settings.getRightMargin(PythonLanguage.getInstance()); final boolean oldWrapLongLines = commonSettings.WRAP_LONG_LINES; + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + settings.setRightMargin(PythonLanguage.getInstance(), 80); commonSettings.WRAP_LONG_LINES = true; try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = false; doTest(); } finally { commonSettings.WRAP_LONG_LINES = oldWrapLongLines; settings.setRightMargin(PythonLanguage.getInstance(), oldRightMargin); + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; + } + } + + public void testResultExceedsRightMarginWithParenthesizeOnEnter() { + final CodeStyleSettings settings = getCodeStyleSettings(); + final CommonCodeStyleSettings commonSettings = settings.getCommonSettings(PythonLanguage.getInstance()); + + final int oldRightMargin = settings.getRightMargin(PythonLanguage.getInstance()); + final boolean oldWrapLongLines = commonSettings.WRAP_LONG_LINES; + + boolean initialValue = PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER; + + settings.setRightMargin(PythonLanguage.getInstance(), 80); + commonSettings.WRAP_LONG_LINES = true; + try { + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = true; + doTest(); + } + finally { + commonSettings.WRAP_LONG_LINES = oldWrapLongLines; + settings.setRightMargin(PythonLanguage.getInstance(), oldRightMargin); + PyCodeInsightSettings.getInstance().PARENTHESISE_ON_ENTER = initialValue; } }