mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-65777 No alignment when pressing Enter in multiline implements list
1. Added ability to align class lbrace to the first reference of incomplete 'implements' list; 2. Corresponding test is added;
This commit is contained in:
@@ -106,7 +106,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
final CodeStyleSettings settings, final JavaWrapManager wrapManager,
|
||||
@NotNull final AlignmentStrategy alignmentStrategy, AlignmentInColumnsHelper alignmentInColumnsHelper)
|
||||
{
|
||||
super(node, wrap, alignmentStrategy.getAlignment(node.getElementType()));
|
||||
super(node, wrap, createBlockAlignment(alignmentStrategy, node));
|
||||
mySettings = settings;
|
||||
myIndentSettings = settings.getIndentOptions(StdFileTypes.JAVA);
|
||||
myIndent = indent;
|
||||
@@ -115,6 +115,16 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
myAlignmentInColumnsHelper = alignmentInColumnsHelper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Alignment createBlockAlignment(@NotNull AlignmentStrategy strategy, @NotNull ASTNode node) {
|
||||
// There is a possible case that 'implements' section is incomplete (e.g. ends with comma). We may want to align lbrace
|
||||
// to the first implemented interface reference then.
|
||||
if (node.getElementType() == JavaElementType.IMPLEMENTS_LIST) {
|
||||
return null;
|
||||
}
|
||||
return strategy.getAlignment(node.getElementType());
|
||||
}
|
||||
|
||||
public static Block createJavaBlock(final ASTNode child,
|
||||
final CodeStyleSettings settings,
|
||||
final Indent indent,
|
||||
@@ -168,7 +178,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
return block;
|
||||
}
|
||||
else if (isLikeExtendsList(elementType)) {
|
||||
return new ExtendsListBlock(child, wrap, alignment, settings);
|
||||
return new ExtendsListBlock(child, wrap, alignmentStrategy, settings);
|
||||
}
|
||||
else if (elementType == JavaElementType.CODE_BLOCK) {
|
||||
return new CodeBlockBlock(child, wrap, alignment, actualIndent, settings);
|
||||
@@ -330,10 +340,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
else if (nodeType == JavaElementType.FOR_STATEMENT) {
|
||||
return createAlignment(mySettings.ALIGN_MULTILINE_FOR, null);
|
||||
}
|
||||
else if (nodeType == JavaElementType.EXTENDS_LIST) {
|
||||
return createAlignment(mySettings.ALIGN_MULTILINE_EXTENDS_LIST, null);
|
||||
}
|
||||
else if (nodeType == JavaElementType.IMPLEMENTS_LIST) {
|
||||
else if (nodeType == JavaElementType.EXTENDS_LIST || nodeType == JavaElementType.IMPLEMENTS_LIST) {
|
||||
return createAlignment(mySettings.ALIGN_MULTILINE_EXTENDS_LIST, null);
|
||||
}
|
||||
else if (nodeType == JavaElementType.THROWS_LIST) {
|
||||
@@ -349,13 +356,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
}
|
||||
return createAlignment(mySettings.ALIGN_MULTILINE_BINARY_OPERATION, defaultAlignment);
|
||||
}
|
||||
else if (nodeType == JavaElementType.CLASS) {
|
||||
else if (nodeType == JavaElementType.CLASS || nodeType == JavaElementType.METHOD) {
|
||||
return Alignment.createAlignment();
|
||||
}
|
||||
else if (nodeType == JavaElementType.METHOD) {
|
||||
return Alignment.createAlignment();
|
||||
}
|
||||
|
||||
else if (nodeType == JavaElementType.MODIFIER_LIST) {
|
||||
return myAlignment;
|
||||
}
|
||||
@@ -514,9 +517,15 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
result.add(new SimpleJavaBlock(child, defaultWrap, alignmentStrategy, childIndent, mySettings));
|
||||
}
|
||||
else {
|
||||
final Block block =
|
||||
createJavaBlock(child, mySettings, childIndent, arrangeChildWrap(child, defaultWrap),
|
||||
AlignmentStrategy.wrap(arrangeChildAlignment(child, alignmentStrategy)), childOffset);
|
||||
AlignmentStrategy alignmentStrategyToUse = AlignmentStrategy.wrap(arrangeChildAlignment(child, alignmentStrategy));
|
||||
if (myAlignmentStrategy != null && myAlignmentStrategy.getAlignment(nodeType, childType) != null
|
||||
&& (nodeType == JavaElementType.IMPLEMENTS_LIST || nodeType == JavaElementType.CLASS))
|
||||
{
|
||||
alignmentStrategyToUse = myAlignmentStrategy;
|
||||
}
|
||||
final Block block = createJavaBlock(
|
||||
child, mySettings, childIndent, arrangeChildWrap(child, defaultWrap), alignmentStrategyToUse, childOffset
|
||||
);
|
||||
|
||||
if (childType == JavaElementType.MODIFIER_LIST && containsAnnotations(child)) {
|
||||
myAnnotationWrap = Wrap.createWrap(getWrapType(getAnnotationWrapType(child)), true);
|
||||
@@ -524,12 +533,9 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
|
||||
if (block instanceof AbstractJavaBlock) {
|
||||
final AbstractJavaBlock javaBlock = (AbstractJavaBlock)block;
|
||||
if (nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION) {
|
||||
javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType);
|
||||
javaBlock.setReservedWrap(getReservedWrap(childType), childType);
|
||||
}
|
||||
else if (nodeType == JavaElementType.REFERENCE_EXPRESSION &&
|
||||
childType == JavaElementType.METHOD_CALL_EXPRESSION) {
|
||||
if ((nodeType == JavaElementType.METHOD_CALL_EXPRESSION && childType == JavaElementType.REFERENCE_EXPRESSION)
|
||||
|| (nodeType == JavaElementType.REFERENCE_EXPRESSION && childType == JavaElementType.METHOD_CALL_EXPRESSION))
|
||||
{
|
||||
javaBlock.setReservedWrap(getReservedWrap(nodeType), nodeType);
|
||||
javaBlock.setReservedWrap(getReservedWrap(childType), childType);
|
||||
}
|
||||
@@ -596,7 +602,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
}
|
||||
|
||||
@NotNull private static ASTNode findLastFieldInGroup(final ASTNode child) {
|
||||
final PsiTypeElement typeElement = ((PsiVariable)child.getPsi()).getTypeElement();
|
||||
PsiElement psi = child.getPsi();
|
||||
if (psi == null) {
|
||||
return child;
|
||||
}
|
||||
final PsiTypeElement typeElement = ((PsiVariable)psi).getTypeElement();
|
||||
if (typeElement == null) return child;
|
||||
|
||||
ASTNode lastChildNode = child.getLastChildNode();
|
||||
@@ -613,7 +623,7 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
|| StdTokenSets.COMMENT_BIT_SET.contains(currentNode.getElementType())) {
|
||||
}
|
||||
else if (currentNode.getElementType() == JavaElementType.FIELD) {
|
||||
if (((PsiVariable)currentNode.getPsi()).getTypeElement() != typeElement) {
|
||||
if (((PsiVariable)psi).getTypeElement() != typeElement) {
|
||||
return currentResult;
|
||||
}
|
||||
else {
|
||||
@@ -786,7 +796,11 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
}
|
||||
|
||||
private static boolean containsAnnotations(final ASTNode child) {
|
||||
return ((PsiModifierList)child.getPsi()).getAnnotations().length > 0;
|
||||
PsiElement psi = child.getPsi();
|
||||
if (!(psi instanceof PsiModifierList)) {
|
||||
return false;
|
||||
}
|
||||
return ((PsiModifierList)psi).getAnnotations().length > 0;
|
||||
}
|
||||
|
||||
private int getAnnotationWrapType(@NotNull ASTNode child) {
|
||||
@@ -1094,18 +1108,15 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
if (psiNode instanceof PsiClass) {
|
||||
return mySettings.CLASS_BRACE_STYLE;
|
||||
}
|
||||
else if (psiNode instanceof PsiMethod) {
|
||||
return mySettings.METHOD_BRACE_STYLE;
|
||||
}
|
||||
|
||||
else if (psiNode instanceof PsiCodeBlock && psiNode.getParent() != null && psiNode.getParent() instanceof PsiMethod) {
|
||||
else if (psiNode instanceof PsiMethod
|
||||
|| (psiNode instanceof PsiCodeBlock && psiNode.getParent() != null && psiNode.getParent() instanceof PsiMethod))
|
||||
{
|
||||
return mySettings.METHOD_BRACE_STYLE;
|
||||
}
|
||||
|
||||
else {
|
||||
return mySettings.BRACE_STYLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected Indent getCodeBlockInternalIndent(final int baseChildrenIndent) {
|
||||
@@ -1269,14 +1280,16 @@ public abstract class AbstractJavaBlock extends AbstractBlock implements JavaBlo
|
||||
child = child.getTreeNext();
|
||||
|
||||
AlignmentStrategy varDeclarationAlignmentStrategy
|
||||
= AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, true);
|
||||
= AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, JavaElementType.FIELD, true);
|
||||
|
||||
while (child != null) {
|
||||
// We consider that subsequent fields shouldn't be aligned if they are separated by blank line(s).
|
||||
if (!FormatterUtil.containsWhiteSpacesOnly(child)) {
|
||||
if (!ElementType.JAVA_COMMENT_BIT_SET.contains(child.getElementType()) && !shouldUseVarDeclarationAlignment(child)) {
|
||||
// Reset var declaration alignment.
|
||||
varDeclarationAlignmentStrategy = AlignmentStrategy.createAlignmentPerTypeStrategy(VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, true);
|
||||
varDeclarationAlignmentStrategy = AlignmentStrategy.createAlignmentPerTypeStrategy(
|
||||
VAR_DECLARATION_ELEMENT_TYPES_TO_ALIGN, JavaElementType.FIELD, true
|
||||
);
|
||||
}
|
||||
final boolean rBrace = isRBrace(child);
|
||||
Indent childIndent = rBrace ? Indent.getNoneIndent() : getCodeBlockInternalIndent(childrenIndent);
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.psi.formatter.java;
|
||||
|
||||
import com.intellij.formatting.*;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.formatter.FormatterUtil;
|
||||
import com.intellij.psi.formatter.common.AbstractBlock;
|
||||
@@ -45,7 +46,7 @@ public class CodeBlockBlock extends AbstractJavaBlock {
|
||||
final Alignment alignment,
|
||||
final Indent indent,
|
||||
final CodeStyleSettings settings) {
|
||||
super(node, wrap, alignment, indent, settings);
|
||||
super(node, wrap, getAlignmentStrategy(alignment, node, settings), indent, settings);
|
||||
if (isSwitchCodeBlock() && !settings.INDENT_CASE_FROM_SWITCH) {
|
||||
myChildrenIndent = 0;
|
||||
}
|
||||
@@ -54,6 +55,37 @@ public class CodeBlockBlock extends AbstractJavaBlock {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a possible case that 'implements' section is incomplete (e.g. ends with comma). We may want to align lbrace
|
||||
* to the comma then.
|
||||
*
|
||||
* @param alignment block alignment
|
||||
* @param baseNode base AST node
|
||||
* @return alignment strategy to use for the given node
|
||||
*/
|
||||
private static AlignmentStrategy getAlignmentStrategy(Alignment alignment, ASTNode baseNode, @NotNull CodeStyleSettings settings) {
|
||||
if (baseNode.getElementType() != JavaElementType.CLASS || !settings.ALIGN_MULTILINE_EXTENDS_LIST) {
|
||||
return AlignmentStrategy.wrap(alignment);
|
||||
}
|
||||
for (ASTNode node = baseNode.getLastChildNode(); node != null; node = FormattingAstUtil.getPrevNonWhiteSpaceNode(node)) {
|
||||
if (node.getElementType() != JavaElementType.IMPLEMENTS_LIST) {
|
||||
continue;
|
||||
}
|
||||
ASTNode lastChildNode = node.getLastChildNode();
|
||||
if (lastChildNode != null && lastChildNode.getElementType() == JavaTokenType.ERROR_ELEMENT) {
|
||||
Alignment alignmentToUse = alignment;
|
||||
if (alignment == null) {
|
||||
alignmentToUse = Alignment.createAlignment();
|
||||
}
|
||||
return AlignmentStrategy.wrap(
|
||||
alignmentToUse, false, JavaTokenType.LBRACE, JavaElementType.JAVA_CODE_REFERENCE, node.getElementType()
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return AlignmentStrategy.wrap(alignment);
|
||||
}
|
||||
|
||||
private boolean isSwitchCodeBlock() {
|
||||
return myNode.getTreeParent().getElementType() == ElementType.SWITCH_STATEMENT;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.formatting.Alignment;
|
||||
import com.intellij.formatting.Block;
|
||||
import com.intellij.formatting.Indent;
|
||||
import com.intellij.formatting.Wrap;
|
||||
import com.intellij.formatting.alignment.AlignmentStrategy;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.formatter.FormatterUtil;
|
||||
@@ -31,6 +32,10 @@ public class ExtendsListBlock extends AbstractJavaBlock{
|
||||
public ExtendsListBlock(final ASTNode node, final Wrap wrap, final Alignment alignment, CodeStyleSettings settings) {
|
||||
super(node, wrap, alignment, Indent.getNoneIndent(), settings);
|
||||
}
|
||||
|
||||
public ExtendsListBlock(final ASTNode node, final Wrap wrap, final AlignmentStrategy alignmentStrategy, CodeStyleSettings settings) {
|
||||
super(node, wrap, alignmentStrategy, Indent.getNoneIndent(), settings);
|
||||
}
|
||||
|
||||
protected List<Block> buildChildren() {
|
||||
final ArrayList<Block> result = new ArrayList<Block>();
|
||||
@@ -52,8 +57,13 @@ public class ExtendsListBlock extends AbstractJavaBlock{
|
||||
}
|
||||
result.add(createJavaBlock(child, mySettings, myChildIndent, arrangeChildWrap(child, childWrap), alignment));
|
||||
} else {
|
||||
if (myAlignmentStrategy != null) {
|
||||
Alignment candidate = myAlignmentStrategy.getAlignment(child.getElementType());
|
||||
if (candidate != null) {
|
||||
alignment = myChildAlignment = candidate;
|
||||
}
|
||||
}
|
||||
processChild(elementsExceptKeyword, child, myChildAlignment, childWrap, myChildIndent);
|
||||
|
||||
}
|
||||
}
|
||||
child = child.getTreeNext();
|
||||
@@ -67,9 +77,7 @@ public class ExtendsListBlock extends AbstractJavaBlock{
|
||||
}
|
||||
|
||||
private boolean alignList() {
|
||||
if (myNode.getElementType() == ElementType.EXTENDS_LIST) {
|
||||
return mySettings.ALIGN_MULTILINE_EXTENDS_LIST;
|
||||
} else if (myNode.getElementType() == ElementType.IMPLEMENTS_LIST) {
|
||||
if (myNode.getElementType() == ElementType.EXTENDS_LIST || myNode.getElementType() == ElementType.IMPLEMENTS_LIST) {
|
||||
return mySettings.ALIGN_MULTILINE_EXTENDS_LIST;
|
||||
} else if (myNode.getElementType() == ElementType.THROWS_LIST) {
|
||||
return mySettings.ALIGN_MULTILINE_THROWS_LIST;
|
||||
|
||||
@@ -154,7 +154,7 @@ class InitialInfoBuilder {
|
||||
}
|
||||
|
||||
myCurrentWhiteSpace.append(blockStartOffset, myModel, myOptions);
|
||||
boolean isReadOnly = isReadOnly(textRange, rootBlockIsRightBlock);
|
||||
boolean isReadOnly = isReadOnly(rootBlock, rootBlockIsRightBlock);
|
||||
|
||||
ReadOnlyBlockInformationProvider previousProvider = myReadOnlyBlockInformationProvider;
|
||||
try {
|
||||
@@ -320,11 +320,49 @@ class InitialInfoBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReadOnly(final TextRange textRange, boolean rootIsRightBlock) {
|
||||
if (myAffectedRanges == null) return false;
|
||||
return myAffectedRanges.isReadOnly(textRange, rootIsRightBlock);
|
||||
private boolean isReadOnly(final Block block, boolean rootIsRightBlock) {
|
||||
if (myAffectedRanges == null || !myAffectedRanges.isReadOnly(block.getTextRange(), rootIsRightBlock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!block.isIncomplete()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is a possible case that particular sub-block has an alignment and we don't want to loose information about it by
|
||||
// excluding that sub-block from the processing.
|
||||
List<Block> blocks = block.getSubBlocks();
|
||||
for (Block subBlock : blocks) {
|
||||
if (hasAlignmentInTree(subBlock)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer if given block or any of its sub-blocks has defined alignment to use.
|
||||
*
|
||||
* @param block root block to check
|
||||
* @return <code>true</code> if given block or any of its sub-blocks has defined alignment to use;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private static boolean hasAlignmentInTree(@NotNull Block block) {
|
||||
Stack<Block> blocks = new Stack<Block>();
|
||||
blocks.push(block);
|
||||
|
||||
while (!blocks.isEmpty()) {
|
||||
Block blockToProcess = blocks.pop();
|
||||
if (blockToProcess.getAlignment() != null) {
|
||||
return true;
|
||||
}
|
||||
for (Block subBlock : blockToProcess.getSubBlocks()) {
|
||||
blocks.push(subBlock);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<AbstractBlockWrapper, Block> getBlockToInfoMap() {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
@@ -17,43 +17,68 @@ package com.intellij.formatting.alignment;
|
||||
|
||||
import com.intellij.formatting.Alignment;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* <code>GoF 'Strategy'</code> for {@link Alignment} retrieval.
|
||||
*/
|
||||
public abstract class AlignmentStrategy {
|
||||
/** <code>GoF 'Strategy'</code> for {@link Alignment} retrieval. */
|
||||
public abstract class AlignmentStrategy {
|
||||
|
||||
private static final AlignmentStrategy NULL_STRATEGY = new SharedAlignmentStrategy(null);
|
||||
private static final AlignmentStrategy NULL_STRATEGY = wrap(null);
|
||||
|
||||
/**
|
||||
* @return shared strategy instance that returns <code>null</code> all the time
|
||||
*/
|
||||
/** @return shared strategy instance that returns <code>null</code> all the time */
|
||||
public static AlignmentStrategy getNullStrategy() {
|
||||
return NULL_STRATEGY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs strategy that returns given alignment for all elements except those which types are delivered as a trailing argument.
|
||||
* Delegates the processing to {@link #wrap(Alignment, boolean, IElementType...)} with <code>'true'</code> as the second argument
|
||||
*
|
||||
* @param alignment target alignment to wrap
|
||||
* @param typesToIgnore types of the elements for which <code>null</code> should be returned on subsequent calls
|
||||
* to {@link #getAlignment(IElementType)}
|
||||
* @return strategy that returns given alignment all the time for elements which types are not defined
|
||||
* as <code>'types to ignore'</code>; <code>null</code> is returned for them
|
||||
* @param alignment
|
||||
* @param filterTypes
|
||||
* @return
|
||||
*/
|
||||
public static AlignmentStrategy wrap(Alignment alignment, IElementType ... typesToIgnore) {
|
||||
return new SharedAlignmentStrategy(alignment, typesToIgnore);
|
||||
public static AlignmentStrategy wrap(Alignment alignment, IElementType... filterTypes) {
|
||||
return new SharedAlignmentStrategy(alignment, true, filterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs strategy that returns given alignment for all elements which types pass through the target filter.
|
||||
*
|
||||
* @param alignment target alignment to wrap
|
||||
* @param ignoreFilterTypes flag that defines if given alignment should be returned for all elements with given types or
|
||||
* all elements except those with the given types
|
||||
* @param filterTypes element types that should be used for filtering on subsequent calls
|
||||
* to {@link #getAlignment(IElementType)}
|
||||
* @return strategy that returns given alignment all the time for elements which types pass through the target
|
||||
* filter; <code>null</code> otherwise
|
||||
*/
|
||||
public static AlignmentStrategy wrap(Alignment alignment, boolean ignoreFilterTypes, IElementType... filterTypes) {
|
||||
return new SharedAlignmentStrategy(alignment, ignoreFilterTypes, filterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link #createAlignmentPerTypeStrategy(Collection, IElementType, boolean)} with no parent type
|
||||
* check (<code>null</code> is delivered as a parent type).
|
||||
*
|
||||
* @param targetTypes target child types
|
||||
* @param allowBackwardShift flag that defines if backward alignment shift is allowed
|
||||
* @return alignment strategy for the given arguments
|
||||
*/
|
||||
public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(@NotNull Collection<IElementType> targetTypes,
|
||||
boolean allowBackwardShift)
|
||||
{
|
||||
return new AlignmentPerTypeStrategy(targetTypes, null, allowBackwardShift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates strategy that creates and caches one alignment per given type internally and returns it on subsequent calls
|
||||
* to {@link #getAlignment(IElementType)} for elements which type is listed at the given collection. <code>null</code>
|
||||
* is returned from {@link #getAlignment(IElementType)} for elements which types are not listed at the given collection.
|
||||
* to {@link #getAlignment(IElementType, IElementType)} for elements which type is listed at the given collection and parent type
|
||||
* (if defined) is the same as the given one; <code>null</code> is returned from {@link #getAlignment(IElementType, IElementType)} for all
|
||||
* other elements.
|
||||
* <p/>
|
||||
* This strategy is assumed to be used at following situations - suppose we want to align code blocks that doesn't belong
|
||||
* to the same parent but have similar structure, e.g. variable declaration assignments like the one below:
|
||||
@@ -64,19 +89,41 @@ public abstract class AlignmentStrategy {
|
||||
* We can provide parent blocks of that target blocks with the same instance of this alignment strategy and let them eventually
|
||||
* reuse the same alignment objects for target sub-blocks of the same type.
|
||||
*
|
||||
* @param targetTypes target types for which cached alignment should be returned
|
||||
* @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align
|
||||
* to subsequent element (e.g. <code>'='</code> block of <code>'int start = 1'</code> statement
|
||||
* below is shifted one symbol right in order to align to the <code>'='</code> block
|
||||
* of <code>'int finish = 1'</code> statement)
|
||||
* @return alignment retrieval strategy that follows the rules described above
|
||||
* @param targetTypes target types for which cached alignment should be returned
|
||||
* @param parentType target parent type
|
||||
* @param allowBackwardShift flag that specifies if former aligned element may be shifted to right in order to align
|
||||
* to subsequent element (e.g. <code>'='</code> block of <code>'int start = 1'</code> statement
|
||||
* below is shifted one symbol right in order to align to the <code>'='</code> block
|
||||
* of <code>'int finish = 1'</code> statement)
|
||||
* @return alignment retrieval strategy that follows the rules described above
|
||||
*/
|
||||
public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(Collection<IElementType> targetTypes, boolean allowBackwardShift) {
|
||||
return new AlignmentPerTypeStrategy(targetTypes, allowBackwardShift);
|
||||
public static AlignmentPerTypeStrategy createAlignmentPerTypeStrategy(
|
||||
@NotNull Collection<IElementType> targetTypes, @Nullable IElementType parentType, boolean allowBackwardShift)
|
||||
{
|
||||
return new AlignmentPerTypeStrategy(targetTypes, parentType, allowBackwardShift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates the processing to {@link #getAlignment(IElementType, IElementType)} without parent element type
|
||||
* filtering (<code>null</code> is used as parent element type).
|
||||
*
|
||||
* @param childType target child type
|
||||
* @return alignment to use
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Alignment getAlignment(IElementType elementType);
|
||||
public Alignment getAlignment(@Nullable IElementType childType) {
|
||||
return getAlignment(null, childType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests current strategy for alignment to use for the child of the given type assuming that parent node has the given type.
|
||||
*
|
||||
* @param parentType parent type to use for filtering (if not <code>null</code>)
|
||||
* @param childType child type to use for filtering (if not <code>null</code>)
|
||||
* @return alignment to use for the given arguments
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType);
|
||||
|
||||
/**
|
||||
* Stands for {@link AlignmentStrategy} implementation that is configured to return single pre-configured {@link Alignment} object
|
||||
@@ -84,17 +131,20 @@ public abstract class AlignmentStrategy {
|
||||
*/
|
||||
private static class SharedAlignmentStrategy extends AlignmentStrategy {
|
||||
|
||||
private final Set<IElementType> myDisableElementTypes = new HashSet<IElementType>();
|
||||
private final Alignment myAlignment;
|
||||
private final Set<IElementType> myFilterElementTypes = new HashSet<IElementType>();
|
||||
|
||||
SharedAlignmentStrategy(Alignment alignment, IElementType ... disabledElementTypes) {
|
||||
private final Alignment myAlignment;
|
||||
private final boolean myIgnoreFilterTypes;
|
||||
|
||||
private SharedAlignmentStrategy(Alignment alignment, boolean ignoreFilterTypes, IElementType... disabledElementTypes) {
|
||||
myAlignment = alignment;
|
||||
myDisableElementTypes.addAll(asList(disabledElementTypes));
|
||||
myIgnoreFilterTypes = ignoreFilterTypes;
|
||||
myFilterElementTypes.addAll(asList(disabledElementTypes));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Alignment getAlignment(IElementType elementType) {
|
||||
return myDisableElementTypes.contains(elementType) ? null : myAlignment;
|
||||
public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) {
|
||||
return (myFilterElementTypes.contains(childType) ^ myIgnoreFilterTypes) ? myAlignment : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +155,12 @@ public abstract class AlignmentStrategy {
|
||||
public static class AlignmentPerTypeStrategy extends AlignmentStrategy {
|
||||
|
||||
private final Map<IElementType, Alignment> myAlignments = new HashMap<IElementType, Alignment>();
|
||||
private final boolean myAllowBackwardShift;
|
||||
|
||||
private final IElementType myParentType;
|
||||
private final boolean myAllowBackwardShift;
|
||||
|
||||
AlignmentPerTypeStrategy(Collection<IElementType> targetElementTypes, boolean allowBackwardShift) {
|
||||
AlignmentPerTypeStrategy(Collection<IElementType> targetElementTypes, IElementType parentType, boolean allowBackwardShift) {
|
||||
myParentType = parentType;
|
||||
myAllowBackwardShift = allowBackwardShift;
|
||||
for (IElementType elementType : targetElementTypes) {
|
||||
myAlignments.put(elementType, Alignment.createAlignment(myAllowBackwardShift));
|
||||
@@ -115,8 +168,11 @@ public abstract class AlignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alignment getAlignment(IElementType elementType) {
|
||||
return myAlignments.get(elementType);
|
||||
public Alignment getAlignment(@Nullable IElementType parentType, @Nullable IElementType childType) {
|
||||
if (myParentType != null && parentType != null && myParentType != parentType) {
|
||||
return null;
|
||||
}
|
||||
return myAlignments.get(childType);
|
||||
}
|
||||
|
||||
public void renewAlignment(IElementType elementType) {
|
||||
|
||||
@@ -34,6 +34,7 @@ public abstract class AbstractBlock implements ASTBlock {
|
||||
protected final Alignment myAlignment;
|
||||
|
||||
private List<Block> mySubBlocks;
|
||||
private Boolean myIncomplete;
|
||||
|
||||
protected AbstractBlock(@NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment) {
|
||||
myNode = node;
|
||||
@@ -79,6 +80,7 @@ public abstract class AbstractBlock implements ASTBlock {
|
||||
return new ChildAttributes(getChildIndent(), getFirstChildAlignment());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Alignment getFirstChildAlignment() {
|
||||
List<Block> subBlocks = getSubBlocks();
|
||||
for (final Block subBlock : subBlocks) {
|
||||
@@ -96,7 +98,10 @@ public abstract class AbstractBlock implements ASTBlock {
|
||||
}
|
||||
|
||||
public boolean isIncomplete() {
|
||||
return FormatterUtil.isIncompleted(getNode());
|
||||
if (myIncomplete == null) {
|
||||
myIncomplete = FormatterUtil.isIncompleted(getNode());
|
||||
}
|
||||
return myIncomplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user