IDEA-64989 Regression: Smart Indent doesn't indent properly after hitting Enter on an indented line

1. Corrected smart indent processing for incomplete chained method call expression;
2. Corresponding test is added;
3. Green code policy is applied;
This commit is contained in:
Denis Zhdanov
2011-02-03 11:49:17 +03:00
parent bfa88b4aa0
commit 5f65739fa6
3 changed files with 79 additions and 10 deletions
@@ -24,11 +24,15 @@ import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.formatter.FormatterUtil;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.StdTokenSets;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SimpleJavaBlock extends AbstractJavaBlock {
@@ -102,7 +106,11 @@ public class SimpleJavaBlock extends AbstractJavaBlock {
} else {
return super.getChildAttributes(newChildIndex);
}
} else {
}
else if (myNode.getElementType() == JavaElementType.SWITCH_LABEL_STATEMENT) {
return new ChildAttributes(Indent.getNormalIndent(), null);
}
else {
return super.getChildAttributes(newChildIndex);
}
}
@@ -19,6 +19,8 @@ import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.formatter.common.AbstractBlock;
import org.jetbrains.annotations.NotNull;
@@ -84,13 +86,36 @@ public class SyntheticCodeBlock implements Block, JavaBlock{
}
public String toString() {
final ASTNode treeNode = ((AbstractBlock)mySubBlocks.get(0)).getNode();
ASTNode treeNode = null;
Block child = mySubBlocks.get(0);
while (treeNode == null) {
if (child instanceof AbstractBlock) {
treeNode = ((AbstractBlock)child).getNode();
}
else if (child instanceof SyntheticCodeBlock) {
child = ((SyntheticCodeBlock)child).mySubBlocks.get(0);
}
else {
break;
}
}
final TextRange textRange = getTextRange();
return treeNode.getPsi().getContainingFile().getText().subSequence(textRange.getStartOffset(), textRange.getEndOffset()).toString();
if (treeNode != null) {
PsiElement psi = treeNode.getPsi();
if (psi != null) {
PsiFile file = psi.getContainingFile();
if (file != null) {
return file.getText().subSequence(textRange.getStartOffset(), textRange.getEndOffset()) + " " + textRange;
}
}
}
return getClass().getName() + ": " + textRange;
}
public ASTNode getFirstTreeNode() {
return AbstractJavaBlock.getTreeNode(mySubBlocks.get(0));
ASTNode result = AbstractJavaBlock.getTreeNode(mySubBlocks.get(0));
assert result != null;
return result;
}
public void setChildAttributes(final ChildAttributes childAttributes) {
@@ -1128,8 +1128,9 @@ class FormatProcessor {
}
private static int getNewChildPosition(final AbstractBlockWrapper parent, final int offset) {
if (!(parent instanceof CompositeBlockWrapper)) return 0;
final List<AbstractBlockWrapper> subBlocks = ((CompositeBlockWrapper)parent).getChildren();
AbstractBlockWrapper parentBlockToUse = getLastNestedCompositeBlockForSameRange(parent);
if (!(parentBlockToUse instanceof CompositeBlockWrapper)) return 0;
final List<AbstractBlockWrapper> subBlocks = ((CompositeBlockWrapper)parentBlockToUse).getChildren();
//noinspection ConstantConditions
if (subBlocks != null) {
for (int i = 0; i < subBlocks.size(); i++) {
@@ -1159,7 +1160,7 @@ class FormatProcessor {
private AbstractBlockWrapper getParentFor(final int offset, LeafBlockWrapper block) {
AbstractBlockWrapper previous = getPreviousIncompleteBlock(block, offset);
if (previous != null) {
return previous;
return getLastNestedCompositeBlockForSameRange(previous);
}
else {
return getParentFor(offset, (AbstractBlockWrapper)block);
@@ -1215,12 +1216,47 @@ class FormatProcessor {
@Nullable
private static AbstractBlockWrapper getLastChildOf(final AbstractBlockWrapper currentResult) {
if (!(currentResult instanceof CompositeBlockWrapper)) return null;
final List<AbstractBlockWrapper> subBlocks = ((CompositeBlockWrapper)currentResult).getChildren();
AbstractBlockWrapper parentBlockToUse = getLastNestedCompositeBlockForSameRange(currentResult);
if (!(parentBlockToUse instanceof CompositeBlockWrapper)) return null;
final List<AbstractBlockWrapper> subBlocks = ((CompositeBlockWrapper)parentBlockToUse).getChildren();
if (subBlocks.isEmpty()) return null;
return subBlocks.get(subBlocks.size() - 1);
}
/**
* There is a possible case that particular block is a composite block that contains number of nested composite blocks
* that all target the same text range. This method allows to derive the most nested block that shares the same range (if any).
*
* @param block block to check
* @return the most nested block of the given one that shares the same text range if any; given block otherwise
*/
@NotNull
private static AbstractBlockWrapper getLastNestedCompositeBlockForSameRange(@NotNull final AbstractBlockWrapper block) {
if (!(block instanceof CompositeBlockWrapper)) {
return block;
}
AbstractBlockWrapper result = block;
AbstractBlockWrapper candidate = block;
while (true) {
List<AbstractBlockWrapper> subBlocks = ((CompositeBlockWrapper)candidate).getChildren();
if (subBlocks == null || subBlocks.size() != 1) {
break;
}
candidate = subBlocks.get(0);
if (candidate.getStartOffset() == block.getStartOffset() && candidate.getEndOffset() == block.getEndOffset()
&& candidate instanceof CompositeBlockWrapper)
{
result = candidate;
}
else {
break;
}
}
return result;
}
private void processBlocksBefore(final int offset) {
while (true) {
myAlignAgain.clear();