From 4e3322a0eb645f78764de460b750d44fbac4e430 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Wed, 11 Jan 2017 19:29:21 +0300 Subject: [PATCH] PY-10182 Initial implementation of pycodestyle's option "--hang-closing" in Python formatter --- .../com/jetbrains/python/PyBundle.properties | 1 + .../jetbrains/python/formatter/PyBlock.java | 22 ++++++++-------- .../python/formatter/PyCodeStyleSettings.java | 7 ++++++ .../PyLanguageCodeStyleSettingsProvider.java | 1 + .../hangClosingParenthesisInFromImport.py | 5 ++++ ...angClosingParenthesisInFromImport_after.py | 5 ++++ .../hangClosingParenthesisInFunctionCall.py | 5 ++++ ...gClosingParenthesisInFunctionCall_after.py | 5 ++++ ...gClosingParenthesisInFunctionDefinition.py | 6 +++++ ...ngParenthesisInFunctionDefinition_after.py | 6 +++++ .../hangClosingParenthesisInListLiteral.py | 5 ++++ ...ngClosingParenthesisInListLiteral_after.py | 5 ++++ .../com/jetbrains/python/PyFormatterTest.java | 25 +++++++++++++++++++ 13 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 python/testData/formatter/hangClosingParenthesisInFromImport.py create mode 100644 python/testData/formatter/hangClosingParenthesisInFromImport_after.py create mode 100644 python/testData/formatter/hangClosingParenthesisInFunctionCall.py create mode 100644 python/testData/formatter/hangClosingParenthesisInFunctionCall_after.py create mode 100644 python/testData/formatter/hangClosingParenthesisInFunctionDefinition.py create mode 100644 python/testData/formatter/hangClosingParenthesisInFunctionDefinition_after.py create mode 100644 python/testData/formatter/hangClosingParenthesisInListLiteral.py create mode 100644 python/testData/formatter/hangClosingParenthesisInListLiteral_after.py diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index d0c6d1807ec8..2faa0aa3aa90 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -1035,6 +1035,7 @@ formatter.align.when.multiline=Align when multiline formatter.collections.and.comprehensions=Collections and Comprehensions formatter.import.statements=Import Statements formatter.dictionary.literals=Dictionary literals +formatter.hang.closing.brackets=Hang closing brackets smartKeys.insert.backslash.in.statement.on.enter=Insert backslash when pressing Enter inside a statement smartKeys.insert.self.in.method=Insert 'self' when defining a method diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index c52010280675..4898bca87aca 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -250,7 +250,7 @@ public class PyBlock implements ASTBlock { final PyCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(child.getPsi().getProject()).getCustomSettings(PyCodeStyleSettings.class); if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION || parentType == PyElementTypes.LIST_COMP_EXPRESSION) { - if (childType == PyTokenTypes.RBRACKET || childType == PyTokenTypes.LBRACKET) { + if ((childType == PyTokenTypes.RBRACKET && !settings.HANG_CLOSING_BRACKETS) || childType == PyTokenTypes.LBRACKET) { childIndent = Indent.getNoneIndent(); } else { @@ -259,7 +259,7 @@ public class PyBlock implements ASTBlock { } else if (parentType == PyElementTypes.DICT_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_LITERAL_EXPRESSION || parentType == PyElementTypes.SET_COMP_EXPRESSION || parentType == PyElementTypes.DICT_COMP_EXPRESSION) { - if (childType == PyTokenTypes.RBRACE || !hasLineBreaksBeforeInSameParent(child, 1)) { + if ((childType == PyTokenTypes.RBRACE && !settings.HANG_CLOSING_BRACKETS) || !hasLineBreaksBeforeInSameParent(child, 1)) { childIndent = Indent.getNoneIndent(); } else { @@ -283,7 +283,7 @@ public class PyBlock implements ASTBlock { } if (childType == PyTokenTypes.RPAR) { childIndent = Indent.getNoneIndent(); - if (!hasHangingIndent(myNode.getPsi())) { + if (!hasHangingIndent(myNode.getPsi()) || settings.HANG_CLOSING_BRACKETS) { childAlignment = getAlignmentForChildren(); } } @@ -320,18 +320,16 @@ public class PyBlock implements ASTBlock { } } else if (parentType == PyElementTypes.ARGUMENT_LIST || parentType == PyElementTypes.PARAMETER_LIST) { - if (childType == PyTokenTypes.RPAR) { + if (childType == PyTokenTypes.RPAR && !settings.HANG_CLOSING_BRACKETS) { childIndent = Indent.getNoneIndent(); } + else if (parentType == PyElementTypes.PARAMETER_LIST || + settings.USE_CONTINUATION_INDENT_FOR_ARGUMENTS || + argumentMayHaveSameIndentAsFollowingStatementList()) { + childIndent = Indent.getContinuationIndent(); + } else { - if (parentType == PyElementTypes.PARAMETER_LIST || - settings.USE_CONTINUATION_INDENT_FOR_ARGUMENTS || - argumentMayHaveSameIndentAsFollowingStatementList()) { - childIndent = Indent.getContinuationIndent(); - } - else { - childIndent = Indent.getNormalIndent(); - } + childIndent = Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.SUBSCRIPTION_EXPRESSION) { diff --git a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java index 1d1ee8dc6dac..26133f0842ce 100644 --- a/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java +++ b/python/src/com/jetbrains/python/formatter/PyCodeStyleSettings.java @@ -93,6 +93,13 @@ public class PyCodeStyleSettings extends CustomCodeStyleSettings { public boolean OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST = true; public boolean OPTIMIZE_IMPORTS_JOIN_FROM_IMPORTS_WITH_SAME_SOURCE = false; + /** + * Corresponds to the option of pycodestyle.py "--hang-closing". Basically, it means that the closing brace of a collection literal, + * a comprehension, an argument list, a parameter list or parentheses in "from" import statement should have the same indent as the items + * inside even if there is so called hanging indent (nothing follows the opening bracket on its line). + */ + public boolean HANG_CLOSING_BRACKETS = false; + public PyCodeStyleSettings(CodeStyleSettings container) { super("Python", container); } diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java index 7a7d2181c8c9..9aee738589eb 100644 --- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java +++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java @@ -129,6 +129,7 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin consumer.showCustomOption(PyCodeStyleSettings.class, "DICT_NEW_LINE_BEFORE_RIGHT_BRACE", ApplicationBundle.message("wrapping.rbrace.on.new.line"), PyBundle.message("formatter.dictionary.literals")); + consumer.showCustomOption(PyCodeStyleSettings.class, "HANG_CLOSING_BRACKETS", PyBundle.message("formatter.hang.closing.brackets"), null); } } diff --git a/python/testData/formatter/hangClosingParenthesisInFromImport.py b/python/testData/formatter/hangClosingParenthesisInFromImport.py new file mode 100644 index 000000000000..1d61e72bfed8 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFromImport.py @@ -0,0 +1,5 @@ +from module import ( + A, + B, + C, +) diff --git a/python/testData/formatter/hangClosingParenthesisInFromImport_after.py b/python/testData/formatter/hangClosingParenthesisInFromImport_after.py new file mode 100644 index 000000000000..419e49e22bc3 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFromImport_after.py @@ -0,0 +1,5 @@ +from module import ( + A, + B, + C, + ) diff --git a/python/testData/formatter/hangClosingParenthesisInFunctionCall.py b/python/testData/formatter/hangClosingParenthesisInFunctionCall.py new file mode 100644 index 000000000000..9bbb606d2412 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFunctionCall.py @@ -0,0 +1,5 @@ +func( + 1, + 2, + 3 +) diff --git a/python/testData/formatter/hangClosingParenthesisInFunctionCall_after.py b/python/testData/formatter/hangClosingParenthesisInFunctionCall_after.py new file mode 100644 index 000000000000..e693e6fe93cc --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFunctionCall_after.py @@ -0,0 +1,5 @@ +func( + 1, + 2, + 3 + ) diff --git a/python/testData/formatter/hangClosingParenthesisInFunctionDefinition.py b/python/testData/formatter/hangClosingParenthesisInFunctionDefinition.py new file mode 100644 index 000000000000..b0db6da04317 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFunctionDefinition.py @@ -0,0 +1,6 @@ +def func( + x, + y, + z +): + pass diff --git a/python/testData/formatter/hangClosingParenthesisInFunctionDefinition_after.py b/python/testData/formatter/hangClosingParenthesisInFunctionDefinition_after.py new file mode 100644 index 000000000000..99ade773a618 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInFunctionDefinition_after.py @@ -0,0 +1,6 @@ +def func( + x, + y, + z + ): + pass diff --git a/python/testData/formatter/hangClosingParenthesisInListLiteral.py b/python/testData/formatter/hangClosingParenthesisInListLiteral.py new file mode 100644 index 000000000000..fb437dcfe0aa --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInListLiteral.py @@ -0,0 +1,5 @@ +xs = [ + 1, + 2, + 3 +] diff --git a/python/testData/formatter/hangClosingParenthesisInListLiteral_after.py b/python/testData/formatter/hangClosingParenthesisInListLiteral_after.py new file mode 100644 index 000000000000..cbb3e460e528 --- /dev/null +++ b/python/testData/formatter/hangClosingParenthesisInListLiteral_after.py @@ -0,0 +1,5 @@ +xs = [ + 1, + 2, + 3 + ] diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index 5da55236a3a8..f6a5359472d4 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -674,6 +674,31 @@ public class PyFormatterTest extends PyTestCase { doTest(); } + // PY-10182 + public void testHangClosingParenthesisInFromImport() { + getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true; + doTest(); + } + + // PY-10182 + public void testHangClosingParenthesisInFunctionCall() { + getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true; + doTest(); + } + + // PY-10182 + public void testHangClosingParenthesisInFunctionDefinition() { + getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true; + doTest(); + } + + // PY-10182 + public void testHangClosingParenthesisInListLiteral() { + getPythonCodeStyleSettings().HANG_CLOSING_BRACKETS = true; + doTest(); + } + + public void testVariableAnnotations() { runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); }