ChildAttributes (int newChildIndex) { int statementListsBelow = 0; if (newChildIndex > 0) { // always pass decision to a sane block from top level from file or definition if (myNode.getPsi() instanceof PyFile || myNode.getElementType() == PyTokenTypes.COLON) { return ChildAttributes.DELEGATE_TO_PREV_CHILD; } final PyBlock insertAfterBlock = getSubBlockByIndex(newChildIndex - 1); final ASTNode prevNode = insertAfterBlock.getNode(); final PsiElement prevElt = prevNode.getPsi(); // TODO Use the same approach for other list-like constructs if (myNode.getElementType() == PyElementTypes.WITH_STATEMENT && isInsideWithStatementParentheses(myNode, prevNode)) { ASTNode openingParenthesis = myNode.findChildByType(PyTokenTypes.LPAR); for (int i = newChildIndex - 1; i >= 0 ; i--) { PyBlock prevBlock = mySubBlocks.get(i); if (prevBlock.myNode == openingParenthesis) { break; } if (prevBlock.getAlignment() != null) { return new ChildAttributes(Indent.getNormalIndent(), prevBlock.getAlignment()); } } return new ChildAttributes(Indent.getNormalIndent(), null); } // stmt lists, parts and definitions should also think for themselves if (prevElt instanceof PyStatementList) { if (dedentAfterLastStatement((PyStatementList)prevElt)) { return new ChildAttributes(Indent.getNoneIndent(), getChildAlignment()); } return ChildAttributes.DELEGATE_TO_PREV_CHILD; } else if (prevElt instanceof PyStatementPart) { return ChildAttributes.DELEGATE_TO_PREV_CHILD; } ASTNode lastChild = insertAfterBlock.getNode(); //In case of a dedent(or multiple dedents) the cursor doesn't belong to the correct block, instead //formatter chooses the biggest enclosing incomplete block as a parent (see FormatProcessor.getParentFor). //To get the correct indent, we have to descend into the inner incomplete block: //each time we see an error element inside a statement we delegate to the previous child. while (lastChild != null) { final IElementType lastType = lastChild.getElementType(); if (lastType == PyElementTypes.STATEMENT_LIST && hasLineBreaksBeforeInSameParent(lastChild, 1)) { if (dedentAfterLastStatement((PyStatementList)lastChild.getPsi())) { break; } statementListsBelow++; } else if (statementListsBelow > 0 && lastChild.getPsi() instanceof PsiErrorElement) { statementListsBelow++; } if (myNode.getElementType() == PyElementTypes.STATEMENT_LIST && lastChild.getPsi() instanceof PsiErrorElement) { return ChildAttributes.DELEGATE_TO_PREV_CHILD; } lastChild = getLastNonSpaceChild(lastChild, true); } } // HACKETY-HACK // If a multi-step dedent follows the cursor position (see testMultiDedent()), // the whitespace (which must be a single Py:LINE_BREAK token) gets attached // to the outermost indented block (because we may not consume the DEDENT // tokens while parsing inner blocks). The policy is to put the indent to // the innermost block, so we need to resolve the situation here. Nested // delegation sometimes causes NPEs in formatter core, so we calculate the // correct indent manually. if (statementListsBelow > 0) { // was 1... strange @SuppressWarnings("ConstantConditions") final int indent = myContext.getSettings().getIndentOptions().INDENT_SIZE; return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null); } /* // it might be something like "def foo(): # comment" or "[1, # comment"; jump up to the real thing if (_node instanceof PsiComment || _node instanceof PsiWhiteSpace) { get } */ final Indent childIndent = getChildIndent(newChildIndex); final Alignment childAlignment = getChildAlignment(); return new ChildAttributes(childIndent, childAlignment); }