PyBlock (@NotNull ASTNode child) { final IElementType parentType = myNode.getElementType(); final ASTNode grandParentNode = myNode.getTreeParent(); final IElementType grandparentType = grandParentNode == null ? null : grandParentNode.getElementType(); final IElementType childType = child.getElementType(); Wrap childWrap = null; Indent childIndent = Indent.getNoneIndent(); Alignment childAlignment = null; final PyCodeStyleSettings settings = myContext.getPySettings(); if (childType == PyElementTypes.STATEMENT_LIST) { if (hasLineBreaksBeforeInSameParent(child, 1) || needLineBreakInStatement()) { childIndent = Indent.getNormalIndent(); } } else if (childType == PyElementTypes.CASE_CLAUSE) { childIndent = Indent.getNormalIndent(); } else if (childType == PyElementTypes.IMPORT_ELEMENT) { if (parentType == PyElementTypes.FROM_IMPORT_STATEMENT) { childWrap = myFromImportWrapping; } else { childWrap = Wrap.createWrap(WrapType.NORMAL, true); } childIndent = Indent.getNormalIndent(); } if (childType == PyTokenTypes.END_OF_LINE_COMMENT && (parentType == PyElementTypes.FROM_IMPORT_STATEMENT || parentType == PyElementTypes.MATCH_STATEMENT)) { childIndent = Indent.getNormalIndent(); } // TODO merge it with the following check of needListAlignment() if (ourListElementTypes.contains(parentType)) { // wrapping in non-parenthesized tuple expression is not allowed (PY-1792) if ((parentType != PyElementTypes.TUPLE_EXPRESSION || grandparentType == PyElementTypes.PARENTHESIZED_EXPRESSION) && !PyTokenTypes.ALL_BRACES.contains(childType) && childType != PyTokenTypes.COMMA && !isSliceOperand(child) /*&& !isSubscriptionOperand(child)*/) { childWrap = Wrap.createWrap(WrapType.NORMAL, true); } if (needListAlignment(child) && !myEmptySequence) { childAlignment = getAlignmentForChildren(); } if (childType == PyTokenTypes.END_OF_LINE_COMMENT) { childIndent = Indent.getNormalIndent(); } } if (parentType == PyElementTypes.BINARY_EXPRESSION) { if (childType != PyElementTypes.BINARY_EXPRESSION) { final PyBlock topmostBinary = findTopmostBinaryExpressionBlock(child); assert topmostBinary != null; final PyBlock binaryParentBlock = topmostBinary.myParent; final ASTNode binaryParentNode = binaryParentBlock.myNode; final IElementType binaryParentType = binaryParentNode.getElementType(); // TODO check for comprehensions explicitly here if (ourListElementTypes.contains(binaryParentType) && needListAlignment(child) && !myEmptySequence) { childAlignment = binaryParentBlock.getChildAlignment(); } final boolean parenthesised = binaryParentType == PyElementTypes.PARENTHESIZED_EXPRESSION; if (binaryParentType != PyElementTypes.RETURN_STATEMENT && binaryParentType != PyElementTypes.YIELD_EXPRESSION) { if (childAlignment == null && topmostBinary != null && !(parenthesised && isIfCondition(binaryParentNode)) && !(isCondition(topmostBinary.myNode) && !ALIGN_CONDITIONS_WITHOUT_PARENTHESES)) { childAlignment = topmostBinary.getAlignmentForChildren(); } // We omit indentation for the binary expression itself in this case (similarly to PyTupleExpression inside // PyParenthesisedExpression) because we indent individual operands and operators inside rather than // the whole contained expression. childIndent = parenthesised ? Indent.getContinuationIndent() : Indent.getContinuationWithoutFirstIndent(); } else { childIndent = Indent.getNormalIndent(); } } } else if (parentType == PyElementTypes.OR_PATTERN) { childAlignment = getAlignmentForChildren(); } else if (parentType == PyElementTypes.SEQUENCE_PATTERN || parentType == PyElementTypes.MAPPING_PATTERN) { if (PyTokenTypes.CLOSE_BRACES.contains(childType) && !settings.HANG_CLOSING_BRACKETS || PyTokenTypes.OPEN_BRACES.contains(childType)) { childIndent = Indent.getNoneIndent(); } else { childIndent = Indent.getNormalIndent(); } } else if (ourCollectionLiteralTypes.contains(parentType)) { if ((PyTokenTypes.CLOSE_BRACES.contains(childType) && !settings.HANG_CLOSING_BRACKETS) || PyTokenTypes.OPEN_BRACES.contains(childType)) { childIndent = Indent.getNoneIndent(); } else if (settings.USE_CONTINUATION_INDENT_FOR_COLLECTION_AND_COMPREHENSIONS) { childIndent = Indent.getContinuationIndent(); } else { childIndent = Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.STRING_LITERAL_EXPRESSION) { if (PyTokenTypes.STRING_NODES.contains(childType) || childType == PyElementTypes.FSTRING_NODE) { childAlignment = getAlignmentForChildren(); } } else if (parentType == PyElementTypes.FROM_IMPORT_STATEMENT) { if (myNode.findChildByType(PyTokenTypes.LPAR) != null) { if (childType == PyElementTypes.IMPORT_ELEMENT) { if (settings.ALIGN_MULTILINE_IMPORTS) { childAlignment = getAlignmentForChildren(); } else { childIndent = Indent.getNormalIndent(); } } if (childType == PyTokenTypes.RPAR) { childIndent = Indent.getNoneIndent(); // Don't have hanging indent and is not going to have it due to the setting about opening parenthesis if (!hasHangingIndent(myNode.getPsi()) && !settings.FROM_IMPORT_NEW_LINE_AFTER_LEFT_PARENTHESIS) { childAlignment = getAlignmentForChildren(); } else if (settings.HANG_CLOSING_BRACKETS) { childIndent = Indent.getNormalIndent(); } } } } else if (isValueOfKeyValuePair(child)) { childIndent = Indent.getNormalIndent(); } // TODO: merge with needChildAlignment() //Align elements vertically if there is an argument in the first line of parenthesized expression else if (!hasHangingIndent(myNode.getPsi()) && (parentType == PyElementTypes.PARENTHESIZED_EXPRESSION || (parentType == PyElementTypes.ARGUMENT_LIST && myContext.getSettings().ALIGN_MULTILINE_PARAMETERS_IN_CALLS) || (parentType == PyElementTypes.PARAMETER_LIST && myContext.getSettings().ALIGN_MULTILINE_PARAMETERS)) && !isIndentNext(child) && !hasLineBreaksBeforeInSameParent(myNode.getFirstChildNode(), 1) && !ourListElementTypes.contains(childType)) { if (!PyTokenTypes.ALL_BRACES.contains(childType)) { childAlignment = getAlignmentForChildren(); childIndent = Indent.getNormalIndent(); } else if (childType == PyTokenTypes.RPAR) { childIndent = Indent.getNoneIndent(); } } // Note that colons are aligned together with bounds and stride else if (myNode.getElementType() == PyElementTypes.SLICE_ITEM) { childAlignment = getChildAlignment(); } else if (parentType == PyElementTypes.GENERATOR_EXPRESSION || parentType == PyElementTypes.PARENTHESIZED_EXPRESSION) { final boolean tupleOrGenerator = parentType == PyElementTypes.GENERATOR_EXPRESSION || myNode.getPsi(PyParenthesizedExpression.class).getContainedExpression() instanceof PyTupleExpression; if ((childType == PyTokenTypes.RPAR && !(tupleOrGenerator && settings.HANG_CLOSING_BRACKETS)) || !hasLineBreaksBeforeInSameParent(child, 1)) { childIndent = Indent.getNoneIndent(); } // Operands and operators of a binary expression have their own indent, no need to increase it by indenting the expression itself else if (childType == PyElementTypes.BINARY_EXPRESSION && parentType == PyElementTypes.PARENTHESIZED_EXPRESSION) { childIndent = Indent.getNoneIndent(); } else { final boolean useWiderIndent = isIndentNext(child) || settings.USE_CONTINUATION_INDENT_FOR_COLLECTION_AND_COMPREHENSIONS; childIndent = useWiderIndent ? Indent.getContinuationIndent() : Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.ARGUMENT_LIST || parentType == PyElementTypes.PATTERN_ARGUMENT_LIST || parentType == PyElementTypes.PARAMETER_LIST) { if (childType == PyTokenTypes.RPAR && !settings.HANG_CLOSING_BRACKETS) { childIndent = Indent.getNoneIndent(); } else if ((parentType == PyElementTypes.PARAMETER_LIST && settings.USE_CONTINUATION_INDENT_FOR_PARAMETERS) || (parentType == PyElementTypes.ARGUMENT_LIST && settings.USE_CONTINUATION_INDENT_FOR_ARGUMENTS) || argumentMayHaveSameIndentAsFollowingStatementList()) { childIndent = Indent.getContinuationIndent(); } else { childIndent = Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.SUBSCRIPTION_EXPRESSION) { final PyExpression indexExpression = ((PySubscriptionExpression)myNode.getPsi()).getIndexExpression(); if (indexExpression != null && child == indexExpression.getNode()) { childIndent = Indent.getNormalIndent(); } } else if (parentType == PyElementTypes.REFERENCE_EXPRESSION) { if (child != myNode.getFirstChildNode()) { childIndent = Indent.getNoneIndent(); if (hasLineBreaksBeforeInSameParent(child, 1)) { if (isInControlStatement()) { childIndent = Indent.getContinuationIndent(); } else { PyBlock b = myParent; while (b != null) { if (b.getNode().getPsi() instanceof PyParenthesizedExpression || b.getNode().getPsi() instanceof PyArgumentList || b.getNode().getPsi() instanceof PyParameterList) { childAlignment = getAlignmentOfChild(b, 1); break; } b = b.myParent; } } } } } if (childType == PyElementTypes.KEY_VALUE_EXPRESSION && isChildOfDictLiteral(child)) { childWrap = myDictWrapping; } if (parentType == PyElementTypes.PARAMETER_LIST && childType != PyTokenTypes.COMMA && childType != PyTokenTypes.LPAR && childType != PyTokenTypes.RPAR) { childWrap = myParameterListWrapping; } if (parentType == PyElementTypes.ARGUMENT_LIST && childType != PyTokenTypes.COMMA && childType != PyTokenTypes.LPAR && childType != PyTokenTypes.RPAR) { childWrap = myArgumentListWrapping; } if (isAfterStatementList(child) && !hasLineBreaksBeforeInSameParent(child, 2) && child.getElementType() != PyTokenTypes.END_OF_LINE_COMMENT) { // maybe enter was pressed and cut us from a previous (nested) statement list childIndent = Indent.getNormalIndent(); } if (settings.DICT_ALIGNMENT == DICT_ALIGNMENT_ON_VALUE) { // Align not the whole value but its left bracket if it starts with it if (isValueOfKeyValuePairOfDictLiteral(child) && !isOpeningBracket(child.getFirstChildNode())) { childAlignment = myParent.myDictAlignment; } else if (isValueOfKeyValuePairOfDictLiteral(myNode) && isOpeningBracket(child)) { childAlignment = myParent.myParent.myDictAlignment; } } else if (myContext.getPySettings().DICT_ALIGNMENT == DICT_ALIGNMENT_ON_COLON) { if (isChildOfKeyValuePairOfDictLiteral(child) && childType == PyTokenTypes.COLON) { childAlignment = myParent.myDictAlignment; } } if (parentType == PyElementTypes.WITH_STATEMENT && isInsideWithStatementParentheses(myNode, child)) { if (needListAlignment(child)) { childAlignment = getAlignmentForChildren(); } else { childIndent = Indent.getNormalIndent(); } if (childType == PyTokenTypes.RPAR && !settings.HANG_CLOSING_BRACKETS) { childIndent = Indent.getNoneIndent(); } } ASTNode prev = child.getTreePrev(); while (prev != null && prev.getElementType() == TokenType.WHITE_SPACE) { if (prev.textContains('\\') && !childIndent.equals(Indent.getContinuationIndent(false)) && !childIndent.equals(Indent.getContinuationIndent(true))) { childIndent = isIndentNext(child) ? Indent.getContinuationIndent() : Indent.getNormalIndent(); break; } prev = prev.getTreePrev(); } // Don't wrap anything inside f-string fragments if (TreeUtil.findParent(child, PyElementTypes.FSTRING_FRAGMENT) != null) { childWrap = Wrap.createWrap(WrapType.NONE, false); } return new PyBlock(this, child, childAlignment, childIndent, childWrap, myContext); }