From ac1ca890fda1e36f13475fb2022ad248378a100d Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Thu, 10 Mar 2016 10:55:00 +0300 Subject: [PATCH] J2K KotlinBlock: convert and prettify Also get rid of ContainerUtil --- .../kotlin/idea/formatter/KotlinBlock.kt | 767 ++++++++---------- 1 file changed, 351 insertions(+), 416 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt index 9dcad285982..bc24e090d4e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt @@ -14,498 +14,433 @@ * limitations under the License. */ -package org.jetbrains.kotlin.idea.formatter; +package org.jetbrains.kotlin.idea.formatter -import com.intellij.formatting.*; -import com.intellij.formatting.alignment.AlignmentStrategy; -import com.intellij.lang.ASTNode; -import com.intellij.openapi.util.Condition; -import com.intellij.psi.TokenType; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CommonCodeStyleSettings; -import com.intellij.psi.formatter.common.AbstractBlock; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.TokenSet; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.idea.KotlinLanguage; -import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings; -import org.jetbrains.kotlin.kdoc.lexer.KDocTokens; -import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes; -import org.jetbrains.kotlin.lexer.KtTokens; -import org.jetbrains.kotlin.psi.KtDeclaration; +import com.intellij.formatting.* +import com.intellij.formatting.alignment.AlignmentStrategy +import com.intellij.lang.ASTNode +import com.intellij.psi.TokenType +import com.intellij.psi.codeStyle.CodeStyleSettings +import com.intellij.psi.formatter.common.AbstractBlock +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.kotlin.KtNodeTypes.* +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings +import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.strategy +import org.jetbrains.kotlin.kdoc.lexer.KDocTokens +import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.lexer.KtTokens.* +import org.jetbrains.kotlin.psi.KtDeclaration +import java.util.* -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +private val KDOC_COMMENT_INDENT = 1 -import static org.jetbrains.kotlin.KtNodeTypes.*; -import static org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.strategy; -import static org.jetbrains.kotlin.lexer.KtTokens.*; +private val BINARY_EXPRESSIONS = TokenSet.create(BINARY_EXPRESSION, BINARY_WITH_TYPE, IS_EXPRESSION) +private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS) +private val ALIGN_FOR_BINARY_OPERATIONS = TokenSet.create(MUL, DIV, PERC, PLUS, MINUS, ELVIS, LT, GT, LTEQ, GTEQ, ANDAND, OROR) + +private val CODE_BLOCKS = TokenSet.create(BLOCK, CLASS_BODY, FUNCTION_LITERAL) + +private val KDOC_CONTENT = TokenSet.create(KDocTokens.KDOC, KDocElementTypes.KDOC_SECTION, KDocElementTypes.KDOC_TAG) /** * @see Block for good JavaDoc documentation */ -public class KotlinBlock extends AbstractBlock { - private static final int KDOC_COMMENT_INDENT = 1; - private final NodeAlignmentStrategy myAlignmentStrategy; - private final Indent myIndent; - private final CodeStyleSettings mySettings; - private final KotlinSpacingBuilder mySpacingBuilder; +class KotlinBlock( + node: ASTNode, + private val myAlignmentStrategy: NodeAlignmentStrategy, + private val myIndent: Indent?, + wrap: Wrap?, + private val mySettings: CodeStyleSettings, + private val mySpacingBuilder: KotlinSpacingBuilder) : AbstractBlock(node, wrap, myAlignmentStrategy.getAlignment(node)) { - private List mySubBlocks; + private var mySubBlocks: List? = null - private static final TokenSet BINARY_EXPRESSIONS = TokenSet.create(BINARY_EXPRESSION, BINARY_WITH_TYPE, IS_EXPRESSION); - private static final TokenSet QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS); - private static final TokenSet ALIGN_FOR_BINARY_OPERATIONS = - TokenSet.create(MUL, DIV, PERC, PLUS, MINUS, ELVIS, LT, GT, LTEQ, GTEQ, ANDAND, OROR); - - private static final TokenSet CODE_BLOCKS = TokenSet.create( - BLOCK, - CLASS_BODY, - FUNCTION_LITERAL); - - private static final TokenSet KDOC_CONTENT = TokenSet.create(KDocTokens.KDOC, - KDocElementTypes.KDOC_SECTION, - KDocElementTypes.KDOC_TAG); - - // private static final List - - public KotlinBlock( - @NotNull ASTNode node, - @NotNull NodeAlignmentStrategy alignmentStrategy, - Indent indent, - Wrap wrap, - CodeStyleSettings settings, - KotlinSpacingBuilder spacingBuilder - ) { - super(node, wrap, alignmentStrategy.getAlignment(node)); - myAlignmentStrategy = alignmentStrategy; - myIndent = indent; - mySettings = settings; - mySpacingBuilder = spacingBuilder; + override fun getIndent(): Indent? { + return myIndent } - @Override - public Indent getIndent() { - return myIndent; - } - - @Override - protected List buildChildren() { + override fun buildChildren(): List { if (mySubBlocks == null) { - List nodeSubBlocks = buildSubBlocks(); + var nodeSubBlocks = buildSubBlocks() as ArrayList - if (getNode().getElementType() == DOT_QUALIFIED_EXPRESSION || getNode().getElementType() == SAFE_ACCESS_EXPRESSION) { - int operationBlockIndex = findNodeBlockIndex(nodeSubBlocks, QUALIFIED_OPERATION); + if (node.elementType === DOT_QUALIFIED_EXPRESSION || node.elementType === SAFE_ACCESS_EXPRESSION) { + val operationBlockIndex = findNodeBlockIndex(nodeSubBlocks, QUALIFIED_OPERATION) if (operationBlockIndex != -1) { // Create fake ".something" or "?.something" block here, so child indentation will be // relative to it when it starts from new line (see Indent javadoc). - Block operationBlock = nodeSubBlocks.get(operationBlockIndex); - SyntheticKotlinBlock operationSynteticBlock = - new SyntheticKotlinBlock( - ((ASTBlock) operationBlock).getNode(), - nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size()), - null, operationBlock.getIndent(), null, mySpacingBuilder); + val operationBlock = nodeSubBlocks[operationBlockIndex] + val operationSyntheticBlock = SyntheticKotlinBlock( + (operationBlock as ASTBlock).node, + nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size), + null, operationBlock.getIndent(), null, mySpacingBuilder) nodeSubBlocks = ContainerUtil.addAll( ContainerUtil.newArrayList(nodeSubBlocks.subList(0, operationBlockIndex)), - operationSynteticBlock); + operationSyntheticBlock) } } - mySubBlocks = nodeSubBlocks; + mySubBlocks = nodeSubBlocks } - return mySubBlocks; + return mySubBlocks!! } - private List buildSubBlocks() { - List blocks = new ArrayList(); + private fun buildSubBlocks(): List { + val blocks = ArrayList() - NodeAlignmentStrategy childrenAlignmentStrategy = getChildrenAlignmentStrategy(); - WrappingStrategy wrappingStrategy = getWrappingStrategy(); + val childrenAlignmentStrategy = getChildrenAlignmentStrategy() + val wrappingStrategy = getWrappingStrategy() - for (ASTNode child = myNode.getFirstChildNode(); child != null; child = child.getTreeNext()) { - IElementType childType = child.getElementType(); + var child: ASTNode? = myNode.firstChildNode + while (child != null) { + val childType = child.elementType - if (child.getTextRange().getLength() == 0) continue; - - if (childType == TokenType.WHITE_SPACE) { - continue; + if (child.textRange.length == 0) { + child = child.treeNext + continue } - blocks.add(buildSubBlock(child, childrenAlignmentStrategy, wrappingStrategy)); + if (childType === TokenType.WHITE_SPACE) { + child = child.treeNext + continue + } + + blocks.add(buildSubBlock(child, childrenAlignmentStrategy, wrappingStrategy)) + child = child.treeNext } - return Collections.unmodifiableList(blocks); + return blocks } - @NotNull - private Block buildSubBlock( - @NotNull ASTNode child, - NodeAlignmentStrategy alignmentStrategy, - @NotNull WrappingStrategy wrappingStrategy) { - Wrap wrap = wrappingStrategy.getWrap(child.getElementType()); + private fun buildSubBlock( + child: ASTNode, + alignmentStrategy: NodeAlignmentStrategy, + wrappingStrategy: WrappingStrategy): Block { + val wrap = wrappingStrategy.getWrap(child.elementType) // Skip one sub-level for operators, so type of block node is an element type of operator - if (child.getElementType() == OPERATION_REFERENCE) { - ASTNode operationNode = child.getFirstChildNode(); + if (child.elementType === OPERATION_REFERENCE) { + val operationNode = child.firstChildNode if (operationNode != null) { - return new KotlinBlock(operationNode, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder); + return KotlinBlock(operationNode, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder) } } - return new KotlinBlock(child, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder); + return KotlinBlock(child, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder) } - private static ASTNode getPrevWithoutWhitespace(ASTNode node) { - node = node.getTreePrev(); - while (node != null && node.getElementType() == TokenType.WHITE_SPACE) { - node = node.getTreePrev(); - } + override fun getSpacing(child1: Block?, child2: Block): Spacing? = mySpacingBuilder.getSpacing(this, child1, child2) - return node; - } + override fun getChildAttributes(newChildIndex: Int): ChildAttributes { + val type = node.elementType + return when (type) { + in CODE_BLOCKS, WHEN, IF, FOR, WHILE, DO_WHILE -> ChildAttributes(Indent.getNormalIndent(), null) - private static ASTNode getPrevWithoutWhitespaceAndComments(ASTNode node) { - node = node.getTreePrev(); - while (node != null && (node.getElementType() == TokenType.WHITE_SPACE || KtTokens.COMMENTS.contains(node.getElementType()))) { - node = node.getTreePrev(); - } + TRY -> ChildAttributes(Indent.getNoneIndent(), null) - return node; - } + DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION -> ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null) - @Override - public Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { - return mySpacingBuilder.getSpacing(this, child1, child2); - } - - @NotNull - @Override - public ChildAttributes getChildAttributes(int newChildIndex) { - IElementType type = getNode().getElementType(); - if (CODE_BLOCKS.contains(type) || - type == WHEN || - type == IF || - type == FOR || - type == WHILE || - type == DO_WHILE) { - - return new ChildAttributes(Indent.getNormalIndent(), null); - } - else if (type == TRY) { - // In try - try BLOCK catch BLOCK finally BLOCK - return new ChildAttributes(Indent.getNoneIndent(), null); - } - else if (type == DOT_QUALIFIED_EXPRESSION || type == SAFE_ACCESS_EXPRESSION) { - return new ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null); - } - else if (type == VALUE_PARAMETER_LIST || type == VALUE_ARGUMENT_LIST) { - // Child index 1 - cursor is after ( - parameter alignment should be recreated - // Child index 0 - before expression - know nothing about it - if (newChildIndex != 1 && newChildIndex != 0 && newChildIndex < getSubBlocks().size()) { - Block block = getSubBlocks().get(newChildIndex); - return new ChildAttributes(block.getIndent(), block.getAlignment()); + VALUE_PARAMETER_LIST, VALUE_ARGUMENT_LIST -> { + if (newChildIndex != 1 && newChildIndex != 0 && newChildIndex < subBlocks.size) { + val block = subBlocks[newChildIndex] + ChildAttributes(block.indent, block.alignment) + } + else { + ChildAttributes(Indent.getContinuationIndent(), null) + } } - return new ChildAttributes(Indent.getContinuationIndent(), null); - } - else if (type == DOC_COMMENT) { - return new ChildAttributes(Indent.getSpaceIndent(KDOC_COMMENT_INDENT), null); - } - if (type == PARENTHESIZED) { - return super.getChildAttributes(newChildIndex); - } + DOC_COMMENT -> ChildAttributes(Indent.getSpaceIndent(KDOC_COMMENT_INDENT), null) - List blocks = getSubBlocks(); - if (newChildIndex != 0) { - boolean isIncomplete = newChildIndex < blocks.size() ? blocks.get(newChildIndex - 1).isIncomplete() : isIncomplete(); - if (isIncomplete) { - return super.getChildAttributes(newChildIndex); + PARENTHESIZED -> super.getChildAttributes(newChildIndex) + + else -> { + val blocks = subBlocks + if (newChildIndex != 0) { + val isIncomplete = if (newChildIndex < blocks.size) blocks[newChildIndex - 1].isIncomplete else isIncomplete + if (isIncomplete) { + return super.getChildAttributes(newChildIndex) + } + } + + ChildAttributes(Indent.getNoneIndent(), null) + } + } + } + + override fun isLeaf(): Boolean = myNode.firstChildNode == null + + private fun getWrappingStrategy(): WrappingStrategy { + val commonSettings = mySettings.getCommonSettings(KotlinLanguage.INSTANCE) + val elementType = myNode.elementType + + if (elementType === VALUE_ARGUMENT_LIST) { + return getWrappingStrategyForItemList(commonSettings.CALL_PARAMETERS_WRAP, VALUE_ARGUMENT) + } + if (elementType === VALUE_PARAMETER_LIST) { + val parentElementType = myNode.treeParent.elementType + if (parentElementType === FUN || parentElementType === CLASS) { + return getWrappingStrategyForItemList(commonSettings.METHOD_PARAMETERS_WRAP, VALUE_PARAMETER) } } - return new ChildAttributes(Indent.getNoneIndent(), null); + return WrappingStrategy.NoWrapping } - @Override - public boolean isLeaf() { - return myNode.getFirstChildNode() == null; - } - - @NotNull - private static WrappingStrategy getWrappingStrategyForItemList(int wrapType, @NotNull final IElementType itemType) { - final Wrap itemWrap = Wrap.createWrap(wrapType, false); - return new WrappingStrategy() { - @Nullable - @Override - public Wrap getWrap(@NotNull IElementType childElementType) { - return childElementType == itemType ? itemWrap : null; - } - }; - } - - @NotNull - private WrappingStrategy getWrappingStrategy() { - CommonCodeStyleSettings commonSettings = mySettings.getCommonSettings(KotlinLanguage.INSTANCE); - IElementType elementType = myNode.getElementType(); - - if (elementType == VALUE_ARGUMENT_LIST) { - return getWrappingStrategyForItemList(commonSettings.CALL_PARAMETERS_WRAP, VALUE_ARGUMENT); - } - if (elementType == VALUE_PARAMETER_LIST) { - IElementType parentElementType = myNode.getTreeParent().getElementType(); - if (parentElementType == FUN || parentElementType == CLASS) { - return getWrappingStrategyForItemList(commonSettings.METHOD_PARAMETERS_WRAP, VALUE_PARAMETER); - } - } - - return WrappingStrategy.NoWrapping.INSTANCE; - } - - private NodeAlignmentStrategy getChildrenAlignmentStrategy() { - final CommonCodeStyleSettings jetCommonSettings = mySettings.getCommonSettings(KotlinLanguage.INSTANCE); - KotlinCodeStyleSettings jetSettings = mySettings.getCustomSettings(KotlinCodeStyleSettings.class); - - // Redefine list of strategies for some special elements - IElementType parentType = myNode.getElementType(); - if (parentType == VALUE_PARAMETER_LIST) { + // Redefine list of strategies for some special elements + // Propagate when alignment for -> + private fun getChildrenAlignmentStrategy(): NodeAlignmentStrategy { + val jetCommonSettings = mySettings.getCommonSettings(KotlinLanguage.INSTANCE) + val jetSettings = mySettings.getCustomSettings(KotlinCodeStyleSettings::class.java) + val parentType = myNode.elementType + if (parentType === VALUE_PARAMETER_LIST) { return getAlignmentForChildInParenthesis( jetCommonSettings.ALIGN_MULTILINE_PARAMETERS, VALUE_PARAMETER, COMMA, - jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR); + jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR) } - else if (parentType == VALUE_ARGUMENT_LIST) { + else if (parentType === VALUE_ARGUMENT_LIST) { return getAlignmentForChildInParenthesis( jetCommonSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS, VALUE_ARGUMENT, COMMA, - jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR); + jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR) } - else if (parentType == WHEN) { - return getAlignmentForCaseBranch(jetSettings.ALIGN_IN_COLUMNS_CASE_BRANCH); + else if (parentType === WHEN) { + return getAlignmentForCaseBranch(jetSettings.ALIGN_IN_COLUMNS_CASE_BRANCH) } - else if (parentType == WHEN_ENTRY) { - // Propagate when alignment for -> - return myAlignmentStrategy; + else if (parentType === WHEN_ENTRY) { + return myAlignmentStrategy } - else if (BINARY_EXPRESSIONS.contains(parentType) && ALIGN_FOR_BINARY_OPERATIONS.contains(getOperationType(getNode()))) { + else if (parentType in BINARY_EXPRESSIONS && getOperationType(node) in ALIGN_FOR_BINARY_OPERATIONS) { return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap( - createAlignment(jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION, getAlignment()))); + createAlignment(jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION, alignment))) } - else if (parentType == SUPER_TYPE_LIST || parentType == INITIALIZER_LIST) { + else if (parentType === SUPER_TYPE_LIST || parentType === INITIALIZER_LIST) { return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap( - createAlignment(jetCommonSettings.ALIGN_MULTILINE_EXTENDS_LIST, getAlignment()))); + createAlignment(jetCommonSettings.ALIGN_MULTILINE_EXTENDS_LIST, alignment))) } - else if (parentType == PARENTHESIZED) { - return new NodeAlignmentStrategy() { - Alignment bracketsAlignment = jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION ? Alignment.createAlignment() : null; + else if (parentType === PARENTHESIZED) { + return object : NodeAlignmentStrategy() { + private var bracketsAlignment: Alignment? = if (jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION) Alignment.createAlignment() else null - @Nullable - @Override - public Alignment getAlignment(@NotNull ASTNode childNode) { - IElementType childNodeType = childNode.getElementType(); - ASTNode prev = getPrevWithoutWhitespace(childNode); + override fun getAlignment(childNode: ASTNode): Alignment? { + val childNodeType = childNode.elementType + val prev = getPrevWithoutWhitespace(childNode) - if ((prev != null && prev.getElementType() == TokenType.ERROR_ELEMENT) || childNodeType == TokenType.ERROR_ELEMENT) { - return bracketsAlignment; + if (prev != null && prev.elementType === TokenType.ERROR_ELEMENT || childNodeType === TokenType.ERROR_ELEMENT) { + return bracketsAlignment } - if (childNodeType == LPAR || childNodeType == RPAR) { - return bracketsAlignment; + if (childNodeType === LPAR || childNodeType === RPAR) { + return bracketsAlignment } - return null; + return null } - }; - } - - return NodeAlignmentStrategy.getNullStrategy(); - } - - private static NodeAlignmentStrategy getAlignmentForChildInParenthesis( - boolean shouldAlignChild, final IElementType parameter, final IElementType delimiter, - boolean shouldAlignParenthesis, final IElementType openBracket, final IElementType closeBracket - ) { - final Alignment parameterAlignment = shouldAlignChild ? Alignment.createAlignment() : null; - final Alignment bracketsAlignment = shouldAlignParenthesis ? Alignment.createAlignment() : null; - - return new NodeAlignmentStrategy() { - @Override - public Alignment getAlignment(@NotNull ASTNode node) { - IElementType childNodeType = node.getElementType(); - - ASTNode prev = getPrevWithoutWhitespace(node); - if ((prev != null && prev.getElementType() == TokenType.ERROR_ELEMENT) || childNodeType == TokenType.ERROR_ELEMENT) { - // Prefer align to parameters on incomplete code (case of line break after comma, when next parameters is absent) - return parameterAlignment; - } - - if (childNodeType == openBracket || childNodeType == closeBracket) { - return bracketsAlignment; - } - - if (childNodeType == parameter || childNodeType == delimiter) { - return parameterAlignment; - } - - return null; - } - }; - } - - private static NodeAlignmentStrategy getAlignmentForCaseBranch(boolean shouldAlignInColumns) { - if (shouldAlignInColumns) { - return NodeAlignmentStrategy.fromTypes( - AlignmentStrategy.createAlignmentPerTypeStrategy(Arrays.asList((IElementType) ARROW), WHEN_ENTRY, true)); - } - else { - return NodeAlignmentStrategy.getNullStrategy(); - } - } - - private static final NodeIndentStrategy[] INDENT_RULES = new NodeIndentStrategy[] { - strategy("No indent for braces in blocks") - .in(BLOCK, CLASS_BODY, FUNCTION_LITERAL) - .forType(RBRACE, LBRACE) - .set(Indent.getNoneIndent()), - - strategy("Indent for block content") - .in(BLOCK, CLASS_BODY, FUNCTION_LITERAL) - .notForType(RBRACE, LBRACE, BLOCK) - .set(Indent.getNormalIndent(false)), - - strategy("Indent for property accessors") - .in(PROPERTY) - .forType(PROPERTY_ACCESSOR) - .set(Indent.getNormalIndent()), - - strategy("For a single statement in 'for'") - .in(BODY) - .notForType(BLOCK) - .set(Indent.getNormalIndent()), - - strategy("For the entry in when") - .forType(WHEN_ENTRY) - .set(Indent.getNormalIndent()), - - strategy("For single statement in THEN and ELSE") - .in(THEN, ELSE) - .notForType(BLOCK) - .set(Indent.getNormalIndent()), - - strategy("Indent for parts") - .in(PROPERTY, FUN, DESTRUCTURING_DECLARATION) - .notForType(BLOCK, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD) - .set(Indent.getContinuationWithoutFirstIndent()), - - strategy("Chained calls") - .in(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) - .set(Indent.getContinuationWithoutFirstIndent(false)), - - strategy("Delegation list") - .in(SUPER_TYPE_LIST, INITIALIZER_LIST) - .set(Indent.getContinuationIndent(false)), - - strategy("Indices") - .in(INDICES) - .set(Indent.getContinuationIndent(false)), - - strategy("Binary expressions") - .in(BINARY_EXPRESSIONS) - .set(Indent.getContinuationWithoutFirstIndent(false)), - - strategy("Parenthesized expression") - .in(PARENTHESIZED) - .set(Indent.getContinuationWithoutFirstIndent(false)), - - strategy("KDoc comment indent") - .in(KDOC_CONTENT) - .forType(KDocTokens.LEADING_ASTERISK, KDocTokens.END) - .set(Indent.getSpaceIndent(KDOC_COMMENT_INDENT)), - - strategy("Block in when entry") - .in(WHEN_ENTRY) - .notForType(BLOCK, WHEN_CONDITION_EXPRESSION, WHEN_CONDITION_IN_RANGE, WHEN_CONDITION_IS_PATTERN, ELSE_KEYWORD, ARROW) - .set(Indent.getNormalIndent()), - }; - - @Nullable - protected static Indent createChildIndent(@NotNull ASTNode child) { - ASTNode childParent = child.getTreeParent(); - IElementType childType = child.getElementType(); - - if (childParent != null && childParent.getTreeParent() != null) { - if (childParent.getElementType() == BLOCK && childParent.getTreeParent().getElementType() == SCRIPT) { - return Indent.getNoneIndent(); } } - // do not indent child after heading comments inside declaration - if (childParent != null && childParent.getPsi() instanceof KtDeclaration) { - ASTNode prev = getPrevWithoutWhitespace(child); - if (prev != null && KtTokens.COMMENTS.contains(prev.getElementType()) && getPrevWithoutWhitespaceAndComments(prev) == null) { - return Indent.getNoneIndent(); - } - } - - for (NodeIndentStrategy strategy : INDENT_RULES) { - Indent indent = strategy.getIndent(child); - if (indent != null) { - return indent; - } - } - - // TODO: Try to rewrite other rules to declarative style - if (childParent != null) { - IElementType parentType = childParent.getElementType(); - - if (parentType == VALUE_PARAMETER_LIST || parentType == VALUE_ARGUMENT_LIST) { - ASTNode prev = getPrevWithoutWhitespace(child); - if (childType == RPAR && (prev == null || prev.getElementType() != TokenType.ERROR_ELEMENT)) { - return Indent.getNoneIndent(); - } - - return Indent.getContinuationWithoutFirstIndent(); - } - - if (parentType == TYPE_PARAMETER_LIST || parentType == TYPE_ARGUMENT_LIST) { - return Indent.getContinuationWithoutFirstIndent(); - } - } - - return Indent.getNoneIndent(); - } - - @Nullable - private static Alignment createAlignment(boolean alignOption, @Nullable Alignment defaultAlignment) { - return alignOption ? createAlignmentOrDefault(null, defaultAlignment) : defaultAlignment; - } - - @Nullable - private static Alignment createAlignmentOrDefault(@Nullable Alignment base, @Nullable Alignment defaultAlignment) { - if (defaultAlignment == null) { - return base == null ? Alignment.createAlignment() : Alignment.createChildAlignment(base); - } - return defaultAlignment; - } - - private static int findNodeBlockIndex(List blocks, final TokenSet tokenSet) { - return ContainerUtil.indexOf(blocks, new Condition() { - @Override - public boolean value(Block block) { - if (!(block instanceof ASTBlock)) return false; - - ASTNode node = ((ASTBlock) block).getNode(); - return node != null && tokenSet.contains(node.getElementType()); - } - }); - } - - @Nullable - private static IElementType getOperationType(ASTNode node) { - ASTNode operationNode = node.findChildByType(OPERATION_REFERENCE); - return operationNode != null ? operationNode.getFirstChildNode().getElementType() : null; + return NodeAlignmentStrategy.getNullStrategy() } } + +private val INDENT_RULES = arrayOf( + strategy("No indent for braces in blocks") + .`in`(BLOCK, CLASS_BODY, FUNCTION_LITERAL) + .forType(RBRACE, LBRACE) + .set(Indent.getNoneIndent()), + + strategy("Indent for block content") + .`in`(BLOCK, CLASS_BODY, FUNCTION_LITERAL) + .notForType(RBRACE, LBRACE, BLOCK) + .set(Indent.getNormalIndent(false)), + + strategy("Indent for property accessors") + .`in`(PROPERTY).forType(PROPERTY_ACCESSOR) + .set(Indent.getNormalIndent()), + + strategy("For a single statement in 'for'") + .`in`(BODY).notForType(BLOCK) + .set(Indent.getNormalIndent()), + + strategy("For the entry in when") + .forType(WHEN_ENTRY) + .set(Indent.getNormalIndent()), + + strategy("For single statement in THEN and ELSE") + .`in`(THEN, ELSE).notForType(BLOCK) + .set(Indent.getNormalIndent()), + + strategy("Indent for parts") + .`in`(PROPERTY, FUN, DESTRUCTURING_DECLARATION) + .notForType(BLOCK, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD) + .set(Indent.getContinuationWithoutFirstIndent()), + + strategy("Chained calls") + .`in`(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) + .set(Indent.getContinuationWithoutFirstIndent(false)), + + strategy("Delegation list") + .`in`(SUPER_TYPE_LIST, INITIALIZER_LIST) + .set(Indent.getContinuationIndent(false)), + + strategy("Indices") + .`in`(INDICES) + .set(Indent.getContinuationIndent(false)), + + strategy("Binary expressions") + .`in`(BINARY_EXPRESSIONS) + .set(Indent.getContinuationWithoutFirstIndent(false)), + + strategy("Parenthesized expression") + .`in`(PARENTHESIZED) + .set(Indent.getContinuationWithoutFirstIndent(false)), + + strategy("KDoc comment indent") + .`in`(KDOC_CONTENT) + .forType(KDocTokens.LEADING_ASTERISK, KDocTokens.END) + .set(Indent.getSpaceIndent(KDOC_COMMENT_INDENT)), + + strategy("Block in when entry") + .`in`(WHEN_ENTRY) + .notForType(BLOCK, WHEN_CONDITION_EXPRESSION, WHEN_CONDITION_IN_RANGE, WHEN_CONDITION_IS_PATTERN, ELSE_KEYWORD, ARROW) + .set(Indent.getNormalIndent())) + +private fun getPrevWithoutWhitespace(pNode: ASTNode?): ASTNode? { + var node = pNode + node = node!!.treePrev + while (node != null && node.elementType === TokenType.WHITE_SPACE) { + node = node.treePrev + } + + return node +} + +private fun getPrevWithoutWhitespaceAndComments(pNode: ASTNode?): ASTNode? { + var node = pNode + node = node!!.treePrev + while (node != null && (node.elementType === TokenType.WHITE_SPACE || KtTokens.COMMENTS.contains(node.elementType))) { + node = node.treePrev + } + + return node +} + +private fun getWrappingStrategyForItemList(wrapType: Int, itemType: IElementType): WrappingStrategy { + val itemWrap = Wrap.createWrap(wrapType, false) + return object : WrappingStrategy { + override fun getWrap(childElementType: IElementType): Wrap? { + return if (childElementType === itemType) itemWrap else null + } + } +} + +private fun getAlignmentForChildInParenthesis( + shouldAlignChild: Boolean, parameter: IElementType, delimiter: IElementType, + shouldAlignParenthesis: Boolean, openBracket: IElementType, closeBracket: IElementType): NodeAlignmentStrategy { + val parameterAlignment = if (shouldAlignChild) Alignment.createAlignment() else null + val bracketsAlignment = if (shouldAlignParenthesis) Alignment.createAlignment() else null + + return object : NodeAlignmentStrategy() { + override fun getAlignment(node: ASTNode): Alignment? { + val childNodeType = node.elementType + + val prev = getPrevWithoutWhitespace(node) + if (prev != null && prev.elementType === TokenType.ERROR_ELEMENT || childNodeType === TokenType.ERROR_ELEMENT) { + // Prefer align to parameters on incomplete code (case of line break after comma, when next parameters is absent) + return parameterAlignment + } + + if (childNodeType === openBracket || childNodeType === closeBracket) { + return bracketsAlignment + } + + if (childNodeType === parameter || childNodeType === delimiter) { + return parameterAlignment + } + + return null + } + } +} + +private fun getAlignmentForCaseBranch(shouldAlignInColumns: Boolean): NodeAlignmentStrategy { + return if (shouldAlignInColumns) { + NodeAlignmentStrategy.fromTypes( + AlignmentStrategy.createAlignmentPerTypeStrategy(listOf(ARROW as IElementType), WHEN_ENTRY, true)) + } + else { + NodeAlignmentStrategy.getNullStrategy() + } +} + +private fun createChildIndent(child: ASTNode): Indent? { + val childParent = child.treeParent + val childType = child.elementType + + if (childParent != null && childParent.treeParent != null) { + if (childParent.elementType === BLOCK && childParent.treeParent.elementType === SCRIPT) { + return Indent.getNoneIndent() + } + } + + // do not indent child after heading comments inside declaration + if (childParent != null && childParent.psi is KtDeclaration) { + val prev = getPrevWithoutWhitespace(child) + if (prev != null && prev.elementType in KtTokens.COMMENTS && getPrevWithoutWhitespaceAndComments(prev) == null) { + return Indent.getNoneIndent() + } + } + + for (strategy in INDENT_RULES) { + val indent = strategy.getIndent(child) + if (indent != null) { + return indent + } + } + + // TODO: Try to rewrite other rules to declarative style + if (childParent != null) { + val parentType = childParent.elementType + + if (parentType === VALUE_PARAMETER_LIST || parentType === VALUE_ARGUMENT_LIST) { + val prev = getPrevWithoutWhitespace(child) + if (childType === RPAR && (prev == null || prev.elementType !== TokenType.ERROR_ELEMENT)) { + return Indent.getNoneIndent() + } + + return Indent.getContinuationWithoutFirstIndent() + } + + if (parentType === TYPE_PARAMETER_LIST || parentType === TYPE_ARGUMENT_LIST) { + return Indent.getContinuationWithoutFirstIndent() + } + } + + return Indent.getNoneIndent() +} + +private fun createAlignment(alignOption: Boolean, defaultAlignment: Alignment?): Alignment? { + return if (alignOption) createAlignmentOrDefault(null, defaultAlignment) else defaultAlignment +} + +private fun createAlignmentOrDefault(base: Alignment?, defaultAlignment: Alignment?): Alignment? { + return defaultAlignment ?: if (base == null) Alignment.createAlignment() else Alignment.createChildAlignment(base) +} + +private fun findNodeBlockIndex(blocks: List, tokenSet: TokenSet): Int { + return blocks.indexOfFirst { block -> + if (block !is ASTBlock) return@indexOfFirst false + + val node = block.node + node != null && node.elementType in tokenSet + } +} + +private fun getOperationType(node: ASTNode): IElementType? { + val operationNode = node.findChildByType(OPERATION_REFERENCE) + return operationNode?.firstChildNode?.elementType; +} \ No newline at end of file