fix AIOOBE in formatter

This commit is contained in:
Dmitry Jemerov
2008-06-29 15:00:58 +04:00
parent 60efec0f84
commit f0c2b75115
@@ -38,265 +38,255 @@ import java.util.List;
* @author yole
*/
public class PyBlock implements Block {
//TOLATER: fix formatter
private PythonLanguage _language;
private Alignment _alignment;
private Indent _indent;
private ASTNode _node;
private Wrap _wrap;
private CodeStyleSettings _settings;
private List<Block> _subBlocks = null;
private Alignment _childListAlignment;
private TokenSet _listElementTypes;
private static final boolean DUMP_FORMATTING_BLOCKS = false;
//TOLATER: fix formatter
private PythonLanguage _language;
private Alignment _alignment;
private Indent _indent;
private ASTNode _node;
private Wrap _wrap;
private CodeStyleSettings _settings;
private List<Block> _subBlocks = null;
private Alignment _childListAlignment;
private TokenSet _listElementTypes;
private static final boolean DUMP_FORMATTING_BLOCKS = false;
public PyBlock(PythonLanguage language, final ASTNode node, final Alignment alignment,
final Indent indent,
final Wrap wrap, final CodeStyleSettings settings) {
_language = language;
_alignment = alignment;
_indent = indent;
_node = node;
_wrap = wrap;
_settings = settings;
_childListAlignment = Alignment.createAlignment();
public PyBlock(PythonLanguage language,
final ASTNode node,
final Alignment alignment,
final Indent indent,
final Wrap wrap,
final CodeStyleSettings settings) {
_language = language;
_alignment = alignment;
_indent = indent;
_node = node;
_wrap = wrap;
_settings = settings;
_childListAlignment = Alignment.createAlignment();
_listElementTypes = TokenSet.create(PyElementTypes.LIST_LITERAL_EXPRESSION,
PyElementTypes.LIST_COMP_EXPRESSION,
PyElementTypes.DICT_LITERAL_EXPRESSION,
PyElementTypes.ARGUMENT_LIST,
PyElementTypes.PARAMETER_LIST);
_listElementTypes = TokenSet.create(PyElementTypes.LIST_LITERAL_EXPRESSION, PyElementTypes.LIST_COMP_EXPRESSION,
PyElementTypes.DICT_LITERAL_EXPRESSION, PyElementTypes.ARGUMENT_LIST,
PyElementTypes.PARAMETER_LIST);
}
@NotNull
public ASTNode getNode() {
return _node;
}
@NotNull
public TextRange getTextRange() {
return _node.getTextRange();
}
@NotNull
public List<Block> getSubBlocks() {
if (_subBlocks == null) {
_subBlocks = buildSubBlocks();
if (DUMP_FORMATTING_BLOCKS) {
dumpSubBlocks();
}
}
return new ArrayList<Block>(_subBlocks);
}
@NotNull
public ASTNode getNode() {
return _node;
}
private List<Block> buildSubBlocks() {
List<Block> blocks = new ArrayList<Block>();
for (ASTNode child = _node.getFirstChildNode(); child != null; child = child.getTreeNext()) {
@NotNull
public TextRange getTextRange() {
return _node.getTextRange();
}
IElementType childType = child.getElementType();
@NotNull
public List<Block> getSubBlocks() {
if (_subBlocks == null) {
_subBlocks = buildSubBlocks();
if (DUMP_FORMATTING_BLOCKS) {
dumpSubBlocks();
}
}
return new ArrayList<Block>(_subBlocks);
}
if (child.getTextRange().getLength() == 0) continue;
private List<Block> buildSubBlocks() {
List<Block> blocks = new ArrayList<Block>();
for (ASTNode child = _node.getFirstChildNode();
child != null; child = child.getTreeNext()) {
IElementType childType = child.getElementType();
if (child.getTextRange().getLength() == 0) continue;
if (childType == TokenType.WHITE_SPACE) {
// don't break this block into sub-blocks if some of the
// whitespace between sub-blocks would include \ characters,
// because the IDEA 5.0/5.1 formatter core requires that whitespace
// between blocks must include whitespace-only characters
if (child.getText().indexOf('\\') >= 0) {
return Collections.emptyList();
}
continue;
}
blocks.add(buildSubBlock(child));
}
return Collections.unmodifiableList(blocks);
}
private PyBlock buildSubBlock(ASTNode child) {
IElementType parentType = _node.getElementType();
IElementType childType = child.getElementType();
Wrap wrap = null;
Indent childIndent = Indent.getNoneIndent();
Alignment childAlignment = null;
if (childType == PyElementTypes.STATEMENT_LIST ||
childType == PyElementTypes.IMPORT_ELEMENT) {
if (hasLineBreakBefore(child)) {
childIndent = Indent.getNormalIndent();
}
}
if (_listElementTypes.contains(parentType)) {
wrap = Wrap.createWrap(WrapType.NORMAL, true);
if (!PyTokenTypes.OPEN_BRACES.contains(childType) &&
!PyTokenTypes.CLOSE_BRACES.contains(childType)) {
childAlignment = _childListAlignment;
}
}
if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION
|| parentType == PyElementTypes.ARGUMENT_LIST) {
childIndent = Indent.getContinuationIndent();
if (childType == TokenType.WHITE_SPACE) {
// don't break this block into sub-blocks if some of the
// whitespace between sub-blocks would include \ characters,
// because the IDEA 5.0/5.1 formatter core requires that whitespace
// between blocks must include whitespace-only characters
if (child.getText().indexOf('\\') >= 0) {
return Collections.emptyList();
}
return new PyBlock(_language, child, childAlignment, childIndent, wrap, _settings);
continue;
}
blocks.add(buildSubBlock(child));
}
return Collections.unmodifiableList(blocks);
}
private PyBlock buildSubBlock(ASTNode child) {
IElementType parentType = _node.getElementType();
IElementType childType = child.getElementType();
Wrap wrap = null;
Indent childIndent = Indent.getNoneIndent();
Alignment childAlignment = null;
if (childType == PyElementTypes.STATEMENT_LIST || childType == PyElementTypes.IMPORT_ELEMENT) {
if (hasLineBreakBefore(child)) {
childIndent = Indent.getNormalIndent();
}
}
if (_listElementTypes.contains(parentType)) {
wrap = Wrap.createWrap(WrapType.NORMAL, true);
if (!PyTokenTypes.OPEN_BRACES.contains(childType) && !PyTokenTypes.CLOSE_BRACES.contains(childType)) {
childAlignment = _childListAlignment;
}
}
if (parentType == PyElementTypes.LIST_LITERAL_EXPRESSION || parentType == PyElementTypes.ARGUMENT_LIST) {
childIndent = Indent.getContinuationIndent();
}
private boolean hasLineBreakBefore(ASTNode child) {
ASTNode prevNode = child.getTreePrev();
if (prevNode != null && prevNode.getElementType() == TokenType.WHITE_SPACE) {
String prevNodeText = prevNode.getText();
if (prevNodeText.indexOf('\n') >= 0) {
return true;
}
}
return false;
}
return new PyBlock(_language, child, childAlignment, childIndent, wrap, _settings);
}
private void dumpSubBlocks() {
System.out.println("Subblocks of " + _node.getPsi() + ":");
for (Block block : _subBlocks) {
if (block instanceof PyBlock) {
System.out.println(" "
+ ((PyBlock) block).getNode().getPsi().toString() + " "
+
block.getTextRange().getStartOffset() + ":" + block
.getTextRange().getLength());
} else {
System.out.println(" <unknown block>");
}
}
private boolean hasLineBreakBefore(ASTNode child) {
ASTNode prevNode = child.getTreePrev();
if (prevNode != null && prevNode.getElementType() == TokenType.WHITE_SPACE) {
String prevNodeText = prevNode.getText();
if (prevNodeText.indexOf('\n') >= 0) {
return true;
}
}
return false;
}
@Nullable
public Wrap getWrap() {
return _wrap;
private void dumpSubBlocks() {
System.out.println("Subblocks of " + _node.getPsi() + ":");
for (Block block : _subBlocks) {
if (block instanceof PyBlock) {
System.out.println(" " + ((PyBlock)block).getNode().getPsi().toString() + " " + block.getTextRange().getStartOffset() + ":" + block
.getTextRange().getLength());
}
else {
System.out.println(" <unknown block>");
}
}
}
@Nullable
public Indent getIndent() {
assert _indent != null;
return _indent;
@Nullable
public Wrap getWrap() {
return _wrap;
}
@Nullable
public Indent getIndent() {
assert _indent != null;
return _indent;
}
@Nullable
public Alignment getAlignment() {
return _alignment;
}
@Nullable
public Spacing getSpacing(Block child1, Block child2) {
ASTNode childNode1 = ((PyBlock)child1).getNode();
ASTNode childNode2 = ((PyBlock)child2).getNode();
IElementType parentType = _node.getElementType();
IElementType type1 = childNode1.getElementType();
IElementType type2 = childNode2.getElementType();
if (PyElementTypes.STATEMENTS.contains(type1) && PyElementTypes.STATEMENTS.contains(type2)) {
return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, false, 1);
}
@Nullable
public Alignment getAlignment() {
return _alignment;
}
@Nullable
public Spacing getSpacing(Block child1, Block child2) {
ASTNode childNode1 = ((PyBlock) child1).getNode();
ASTNode childNode2 = ((PyBlock) child2).getNode();
IElementType parentType = _node.getElementType();
IElementType type1 = childNode1.getElementType();
IElementType type2 = childNode2.getElementType();
if (PyElementTypes.STATEMENTS.contains(type1) && PyElementTypes.STATEMENTS.contains(type2)){
return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, false, 1);
}
/*
if (type1 == PyTokenTypes.COLON && type2 == PyElementTypes.STATEMENT_LIST) {
return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE);
}
*/
//if (parentType == PyElementTypes.ARGUMENT_LIST
// || parentType == PyElementTypes.LIST_LITERAL_EXPRESSION) {
// if (type1 == PyTokenTypes.COMMA && PyElementTypes.EXPRESSIONS.contains(type2)) {
// return Spacing.createSpacing(1, 1, 0, true, Integer.MAX_VALUE);
// }
//}
//if (PyElementTypes.STATEMENTS.contains(type1)
// && PyElementTypes.STATEMENTS.contains(type2)) {
// return Spacing.createSpacing(1, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE);
//}
//if (parentType == PyElementTypes.ARGUMENT_LIST
// || parentType == PyElementTypes.LIST_LITERAL_EXPRESSION) {
// if (type1 == PyTokenTypes.COMMA && PyElementTypes.EXPRESSIONS.contains(type2)) {
// return Spacing.createSpacing(1, 1, 0, true, Integer.MAX_VALUE);
// }
//}
//if (PyElementTypes.STATEMENTS.contains(type1)
// && PyElementTypes.STATEMENTS.contains(type2)) {
// return Spacing.createSpacing(1, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE);
//}
//return new PySpacingProcessor(getNode(), childNode1, childNode2,
// _settings).getResult();
//return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE);
//return new PySpacingProcessor(getNode(), childNode1, childNode2,
// _settings).getResult();
//return Spacing.createSpacing(0, Integer.MAX_VALUE, 1, true, Integer.MAX_VALUE);
return null;
return null;
}
@NotNull
public ChildAttributes getChildAttributes(int newChildIndex) {
int statementListsBelow = 0;
if (newChildIndex > 0) {
PyBlock insertAfterBlock = (PyBlock)_subBlocks.get(newChildIndex - 1);
ASTNode lastChild = insertAfterBlock.getNode();
// HACK? This code fragment is needed to make testClass2() pass,
// but I don't quite understand why it is necessary and why the formatter
// doesn't request childAttributes from the correct block
while (lastChild != null) {
if (lastChild.getElementType() == PyElementTypes.STATEMENT_LIST && hasLineBreakBefore(lastChild)) {
statementListsBelow++;
}
else if (statementListsBelow > 0 && lastChild.getPsi() instanceof PsiErrorElement) {
statementListsBelow++;
}
if (_node.getElementType() == PyElementTypes.STATEMENT_LIST && lastChild.getPsi() instanceof PsiErrorElement) {
return ChildAttributes.DELEGATE_TO_PREV_CHILD;
}
lastChild = getLastNonSpaceChild(lastChild, true);
}
}
public
@NotNull
ChildAttributes getChildAttributes(int newChildIndex) {
PyBlock insertAfterBlock = (PyBlock) _subBlocks.get(newChildIndex-1);
ASTNode lastChild = insertAfterBlock.getNode();
int statementListsBelow = 0;
// HACK? This code fragment is needed to make testClass2() pass,
// but I don't quite understand why it is necessary and why the formatter
// doesn't request childAttributes from the correct block
while(lastChild != null) {
if (lastChild.getElementType() == PyElementTypes.STATEMENT_LIST &&
hasLineBreakBefore(lastChild)) {
statementListsBelow++;
}
else if (statementListsBelow > 0 && lastChild.getPsi() instanceof PsiErrorElement) {
statementListsBelow++;
}
if (_node.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 > 1) {
int indent = _settings.getIndentSize(_language.getAssociatedFileType());
return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null);
}
return new ChildAttributes(getChildIndent(newChildIndex), getChildAlignment());
// 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 > 1) {
int indent = _settings.getIndentSize(_language.getAssociatedFileType());
return new ChildAttributes(Indent.getSpaceIndent(indent * statementListsBelow), null);
}
private Alignment getChildAlignment() {
if (_listElementTypes.contains(_node.getElementType())) {
return _childListAlignment;
}
return null;
return new ChildAttributes(getChildIndent(newChildIndex), getChildAlignment());
}
private Alignment getChildAlignment() {
if (_listElementTypes.contains(_node.getElementType())) {
return _childListAlignment;
}
return null;
}
private Indent getChildIndent(int newChildIndex) {
ASTNode lastChild = getLastNonSpaceChild(_node, false);
if (lastChild != null && lastChild.getElementType() == PyElementTypes.STATEMENT_LIST) {
PyBlock insertAfterBlock = (PyBlock)_subBlocks.get(newChildIndex - 1);
ASTNode afterNode = insertAfterBlock.getNode();
// handle pressing Enter after colon and before first statement in
// existing statement list
if (afterNode.getElementType() == PyElementTypes.STATEMENT_LIST || afterNode.getElementType() == PyTokenTypes.COLON) {
return Indent.getNormalIndent();
}
// handle pressing Enter after colon when there is nothing in the
// statement list
ASTNode lastFirstChild = lastChild.getFirstChildNode();
if (lastFirstChild != null && lastFirstChild == lastChild.getLastChildNode() && lastFirstChild.getPsi() instanceof PsiErrorElement) {
return Indent.getNormalIndent();
}
}
private Indent getChildIndent(int newChildIndex) {
ASTNode lastChild = getLastNonSpaceChild(_node, false);
if (lastChild != null &&
lastChild.getElementType() == PyElementTypes.STATEMENT_LIST) {
if (_listElementTypes.contains(_node.getElementType())) {
return Indent.getNormalIndent();
}
PyBlock insertAfterBlock = (PyBlock) _subBlocks.get(newChildIndex-1);
ASTNode afterNode = insertAfterBlock.getNode();
// handle pressing Enter after colon and before first statement in
// existing statement list
if (afterNode.getElementType() == PyElementTypes.STATEMENT_LIST ||
afterNode.getElementType() == PyTokenTypes.COLON) {
return Indent.getNormalIndent();
}
// handle pressing Enter after colon when there is nothing in the
// statement list
ASTNode lastFirstChild = lastChild.getFirstChildNode();
if (lastFirstChild != null &&
lastFirstChild == lastChild.getLastChildNode() &&
lastFirstChild.getPsi() instanceof PsiErrorElement) {
return Indent.getNormalIndent();
}
}
if (_listElementTypes.contains(_node.getElementType())) {
return Indent.getNormalIndent();
}
return Indent.getNoneIndent();
//return null;
return Indent.getNoneIndent();
//return null;
/*
Indent indent;
@@ -307,33 +297,31 @@ public class PyBlock implements Block {
}
return indent;
*/
}
private static ASTNode getLastNonSpaceChild(ASTNode node, boolean acceptError) {
ASTNode lastChild = node.getLastChildNode();
while (lastChild != null &&
(lastChild.getElementType() == TokenType.WHITE_SPACE || (!acceptError && lastChild.getPsi() instanceof PsiErrorElement))) {
lastChild = lastChild.getTreePrev();
}
return lastChild;
}
public boolean isIncomplete() {
ASTNode lastChild = getLastNonSpaceChild(_node, false);
if (lastChild != null && lastChild.getElementType() == PyElementTypes.STATEMENT_LIST) {
// only multiline statement lists are considered incomplete
ASTNode statementListPrev = lastChild.getTreePrev();
if (statementListPrev != null && statementListPrev.getText().indexOf('\n') >= 0) {
return true;
}
}
private static ASTNode getLastNonSpaceChild(ASTNode node, boolean acceptError) {
ASTNode lastChild = node.getLastChildNode();
while (lastChild != null &&
(lastChild.getElementType() == TokenType.WHITE_SPACE ||
(!acceptError && lastChild.getPsi() instanceof PsiErrorElement))) {
lastChild = lastChild.getTreePrev();
}
return lastChild;
}
return false;
}
public boolean isIncomplete() {
ASTNode lastChild = getLastNonSpaceChild(_node, false);
if (lastChild != null &&
lastChild.getElementType() == PyElementTypes.STATEMENT_LIST) {
// only multiline statement lists are considered incomplete
ASTNode statementListPrev = lastChild.getTreePrev();
if (statementListPrev != null && statementListPrev.getText().indexOf('\n') >= 0) {
return true;
}
}
return false;
}
public boolean isLeaf() {
return _node.getFirstChildNode() == null;
}
public boolean isLeaf() {
return _node.getFirstChildNode() == null;
}
}