diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index fdcd46b6bbdd..4a76a487de6d 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -336,7 +336,16 @@ public class PyBlock implements ASTBlock { else if (elem instanceof PyFunction) { return hasHangingIndent(((PyFunction)elem).getParameterList()); } - else if (elem instanceof PySequenceExpression) { + + final PsiElement firstChild = elem.getFirstChild(); + if (firstChild == null) { + return false; + } + if (PyTokenTypes.OPEN_BRACES.contains(firstChild.getNode().getElementType()) && hasLineBreaksAfter(firstChild.getNode(), 1)) { + return true; + } + + if (elem instanceof PySequenceExpression) { items = ((PySequenceExpression)elem).getElements(); } else if (elem instanceof PyParameterList) { @@ -345,14 +354,19 @@ public class PyBlock implements ASTBlock { else if (elem instanceof PyArgumentList) { items = ((PyArgumentList)elem).getArguments(); } + else if (elem instanceof PyParenthesizedExpression) { + final PyParenthesizedExpression parenthesizedExpr = (PyParenthesizedExpression)elem; + if (parenthesizedExpr.getContainedExpression() instanceof PyTupleExpression) { + items = (((PyTupleExpression)parenthesizedExpr.getContainedExpression()).getElements()); + } + else { + items = new PsiElement[]{parenthesizedExpr.getContainedExpression()}; + } + } else { return false; } - if (items.length == 0) { - return true; - } - final PsiElement firstItem = items[0]; - return hasLineBreaksBefore(firstItem.getNode(), 1) || hasHangingIndent(firstItem); + return items.length == 0 || hasHangingIndent(items[0]); } private static boolean breaksAlignment(IElementType type) { @@ -439,7 +453,7 @@ public class PyBlock implements ASTBlock { myContext.getMode() == FormattingMode.ADJUST_INDENT) { return true; } - return false; + return !hasHangingIndent(_node.getPsi()); } if (_node.getElementType() == PyElementTypes.ARGUMENT_LIST) { if (!myContext.getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS || hasHangingIndent(_node.getPsi())) { @@ -477,12 +491,18 @@ public class PyBlock implements ASTBlock { return node; } - private static boolean hasLineBreaksBefore(ASTNode child, int minCount) { + private static boolean hasLineBreaksBefore(@NotNull ASTNode child, int minCount) { final ASTNode treePrev = child.getTreePrev(); return (treePrev != null && isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(treePrev), minCount)) || isWhitespaceWithLineBreaks(child.getFirstChildNode(), minCount); } + private static boolean hasLineBreaksAfter(@NotNull ASTNode child, int minCount) { + final ASTNode treeNext = child.getTreeNext(); + return (treeNext != null && isWhitespaceWithLineBreaks(TreeUtil.findLastLeaf(treeNext), minCount)) || + isWhitespaceWithLineBreaks(child.getLastChildNode(), minCount); + } + private static boolean isWhitespaceWithLineBreaks(ASTNode node, int minCount) { if (node != null && node.getElementType() == TokenType.WHITE_SPACE) { String prevNodeText = node.getText(); diff --git a/python/testData/formatter/alignInParenthesizedExpression_after.py b/python/testData/formatter/alignInParenthesizedExpression_after.py index 868557ca748f..9e619c4e8013 100644 --- a/python/testData/formatter/alignInParenthesizedExpression_after.py +++ b/python/testData/formatter/alignInParenthesizedExpression_after.py @@ -5,4 +5,4 @@ print(1 + (1 + (1 + x. calc() -)) \ No newline at end of file + )) \ No newline at end of file diff --git a/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent.py b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent.py new file mode 100644 index 000000000000..af427d022905 --- /dev/null +++ b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent.py @@ -0,0 +1,8 @@ +class Checkpoints(webapp2.RequestHandler): + def get(self): + self.response.write(json.dumps({"meta": {"code": 400, + "errorType": "paramError", + "errorDetail": "Parameter 'api_key' is missing" + }, + "response": {} + })) \ No newline at end of file diff --git a/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py new file mode 100644 index 000000000000..d7df54a76338 --- /dev/null +++ b/python/testData/formatter/alignmentOfClosingBraceInDictLiteralWhenNoHangingIndent_after.py @@ -0,0 +1,8 @@ +class Checkpoints(webapp2.RequestHandler): + def get(self): + self.response.write(json.dumps({"meta": {"code": 400, + "errorType": "paramError", + "errorDetail": "Parameter 'api_key' is missing" + }, + "response": {} + })) \ No newline at end of file diff --git a/python/testData/formatter/tupleInArgList_after.py b/python/testData/formatter/tupleInArgList_after.py index ff31f6bd51cc..72dce723faab 100644 --- a/python/testData/formatter/tupleInArgList_after.py +++ b/python/testData/formatter/tupleInArgList_after.py @@ -1,4 +1,4 @@ urlpatterns = patterns('', (r'^admin/', include(admin.site.urls)), url(r'^', include('cms.urls')), -) + ) diff --git a/python/testSrc/com/jetbrains/python/PyFormatterTest.java b/python/testSrc/com/jetbrains/python/PyFormatterTest.java index a1f5cc860347..96e2c7e86ee7 100644 --- a/python/testSrc/com/jetbrains/python/PyFormatterTest.java +++ b/python/testSrc/com/jetbrains/python/PyFormatterTest.java @@ -466,6 +466,11 @@ public class PyFormatterTest extends PyTestCase { } } + // PY-12145 + public void testAlignmentOfClosingBraceInDictLiteralWhenNoHangingIndent() { + doTest(); + } + /** * This test merely checks that call to {@link com.intellij.psi.codeStyle.CodeStyleManager#reformat(com.intellij.psi.PsiElement)} * is possible for Python sources.