diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml
index 1eb3897786f..8c817a1fa8f 100644
--- a/.idea/artifacts/KotlinPlugin.xml
+++ b/.idea/artifacts/KotlinPlugin.xml
@@ -48,6 +48,7 @@
+
diff --git a/idea/formatter/formatter.iml b/idea/formatter/formatter.iml
index 8c779a87e85..efaee3d60da 100644
--- a/idea/formatter/formatter.iml
+++ b/idea/formatter/formatter.iml
@@ -5,11 +5,19 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/CommonAlignmentStrategy.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/CommonAlignmentStrategy.kt
new file mode 100644
index 00000000000..639150972fb
--- /dev/null
+++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/CommonAlignmentStrategy.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.formatter
+
+import com.intellij.formatting.Alignment
+import com.intellij.lang.ASTNode
+
+abstract class CommonAlignmentStrategy {
+ abstract fun getAlignment(node: ASTNode): Alignment?
+}
\ No newline at end of file
diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt
new file mode 100644
index 00000000000..94cba885d95
--- /dev/null
+++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2010-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.formatter
+
+import com.intellij.formatting.*
+import com.intellij.lang.ASTNode
+import com.intellij.psi.TokenType
+import com.intellij.psi.codeStyle.CodeStyleSettings
+import com.intellij.psi.tree.IElementType
+import com.intellij.psi.tree.TokenSet
+import org.jetbrains.kotlin.KtNodeTypes
+import org.jetbrains.kotlin.idea.KotlinLanguage
+import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
+import org.jetbrains.kotlin.psi.KtDeclaration
+import java.util.*
+import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.strategy
+import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
+import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes
+import org.jetbrains.kotlin.lexer.KtTokens.*
+
+private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS)
+private val KDOC_COMMENT_INDENT = 1
+
+private val BINARY_EXPRESSIONS = TokenSet.create(KtNodeTypes.BINARY_EXPRESSION, KtNodeTypes.BINARY_WITH_TYPE, KtNodeTypes.IS_EXPRESSION)
+private val KDOC_CONTENT = TokenSet.create(KDocTokens.KDOC, KDocElementTypes.KDOC_SECTION, KDocElementTypes.KDOC_TAG)
+
+private val CODE_BLOCKS = TokenSet.create(KtNodeTypes.BLOCK, KtNodeTypes.CLASS_BODY, KtNodeTypes.FUNCTION_LITERAL)
+
+private val ALIGN_FOR_BINARY_OPERATIONS = TokenSet.create(MUL, DIV, PERC, PLUS, MINUS, ELVIS, LT, GT, LTEQ, GTEQ, ANDAND, OROR)
+
+abstract class KotlinCommonBlock(
+ private val node: ASTNode,
+ private val settings: CodeStyleSettings,
+ private val spacingBuilder: KotlinSpacingBuilder,
+ private val alignmentStrategy: CommonAlignmentStrategy) {
+ private @Volatile var mySubBlocks: List? = null
+
+ abstract protected fun createBlock(node: ASTNode,
+ alignmentStrategy: CommonAlignmentStrategy,
+ indent: Indent?,
+ wrap: Wrap?,
+ settings: CodeStyleSettings,
+ spacingBuilder: KotlinSpacingBuilder): Block
+
+ abstract protected fun createSyntheticSpacingNodeBlock(node: ASTNode): ASTBlock
+
+ abstract protected fun getSubBlocks(): List
+
+ abstract protected fun getSuperChildAttributes(newChildIndex: Int): ChildAttributes
+
+ abstract protected fun isIncompleteInSuper(): Boolean
+
+ abstract protected fun getAlignmentForCaseBranch(shouldAlignInColumns: Boolean): CommonAlignmentStrategy
+
+ abstract protected fun getAlignment(): Alignment?
+
+ abstract protected fun createAlignmentStrategy(alignOption: Boolean, defaultAlignment: Alignment?): CommonAlignmentStrategy
+
+ abstract protected fun getNullAlignmentStrategy(): CommonAlignmentStrategy
+
+ fun isLeaf(): Boolean = node.firstChildNode == null
+
+ fun buildChildren(): List {
+ if (mySubBlocks != null) {
+ return mySubBlocks!!
+ }
+
+ var nodeSubBlocks = buildSubBlocks()
+
+ if (node.elementType === KtNodeTypes.DOT_QUALIFIED_EXPRESSION || node.elementType === KtNodeTypes.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).
+
+ val operationBlock = nodeSubBlocks[operationBlockIndex]
+ val operationSyntheticBlock = SyntheticKotlinBlock(
+ (operationBlock as ASTBlock).node,
+ nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size),
+ null, operationBlock.getIndent(), null, spacingBuilder) { createSyntheticSpacingNodeBlock(it) }
+
+ nodeSubBlocks = ArrayList(nodeSubBlocks.subList(0, operationBlockIndex))
+ nodeSubBlocks.add(operationSyntheticBlock)
+ }
+ }
+
+ mySubBlocks = nodeSubBlocks
+
+ return nodeSubBlocks
+ }
+
+ fun createChildIndent(child: ASTNode): Indent? {
+ val childParent = child.treeParent
+ val childType = child.elementType
+
+ if (childParent != null && childParent.treeParent != null) {
+ if (childParent.elementType === KtNodeTypes.BLOCK && childParent.treeParent.elementType === KtNodeTypes.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 && COMMENTS.contains(prev.elementType) && 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 === KtNodeTypes.VALUE_PARAMETER_LIST || parentType === KtNodeTypes.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 === KtNodeTypes.TYPE_PARAMETER_LIST || parentType === KtNodeTypes.TYPE_ARGUMENT_LIST) {
+ return Indent.getContinuationWithoutFirstIndent()
+ }
+ }
+
+ return Indent.getNoneIndent()
+ }
+
+ fun getChildAttributes(newChildIndex: Int): ChildAttributes {
+ val type = node.elementType
+ return when (type) {
+ in CODE_BLOCKS, KtNodeTypes.WHEN, KtNodeTypes.IF, KtNodeTypes.FOR, KtNodeTypes.WHILE, KtNodeTypes.DO_WHILE -> ChildAttributes(Indent.getNormalIndent(), null)
+
+ KtNodeTypes.TRY -> ChildAttributes(Indent.getNoneIndent(), null)
+
+ KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION -> ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null)
+
+ KtNodeTypes.VALUE_PARAMETER_LIST, KtNodeTypes.VALUE_ARGUMENT_LIST -> {
+ val subBlocks = getSubBlocks()
+ if (newChildIndex != 1 && newChildIndex != 0 && newChildIndex < subBlocks.size) {
+ val block = subBlocks[newChildIndex]
+ ChildAttributes(block.indent, block.alignment)
+ }
+ else {
+ ChildAttributes(Indent.getContinuationIndent(), null)
+ }
+ }
+
+ DOC_COMMENT -> ChildAttributes(Indent.getSpaceIndent(KDOC_COMMENT_INDENT), null)
+
+ KtNodeTypes.PARENTHESIZED -> getSuperChildAttributes(newChildIndex)
+
+ else -> {
+ val blocks = getSubBlocks()
+ if (newChildIndex != 0) {
+ val isIncomplete = if (newChildIndex < blocks.size) blocks[newChildIndex - 1].isIncomplete else isIncompleteInSuper()
+ if (isIncomplete) {
+ return getSuperChildAttributes(newChildIndex)
+ }
+ }
+
+ ChildAttributes(Indent.getNoneIndent(), null)
+ }
+ }
+ }
+
+ private fun getChildrenAlignmentStrategy(): CommonAlignmentStrategy {
+ val jetCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)
+ val jetSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java)
+ val parentType = node.elementType
+ if (parentType === KtNodeTypes.VALUE_PARAMETER_LIST) {
+ return getAlignmentForChildInParenthesis(
+ jetCommonSettings.ALIGN_MULTILINE_PARAMETERS, KtNodeTypes.VALUE_PARAMETER, COMMA,
+ jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR)
+ }
+ else if (parentType === KtNodeTypes.VALUE_ARGUMENT_LIST) {
+ return getAlignmentForChildInParenthesis(
+ jetCommonSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS, KtNodeTypes.VALUE_ARGUMENT, COMMA,
+ jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR)
+ }
+ else if (parentType === KtNodeTypes.WHEN) {
+ return getAlignmentForCaseBranch(jetSettings.ALIGN_IN_COLUMNS_CASE_BRANCH)
+ }
+ else if (parentType === KtNodeTypes.WHEN_ENTRY) {
+ return alignmentStrategy
+ }
+ else if (parentType in BINARY_EXPRESSIONS && getOperationType(node) in ALIGN_FOR_BINARY_OPERATIONS) {
+ return createAlignmentStrategy(jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION, getAlignment())
+ }
+ else if (parentType === KtNodeTypes.SUPER_TYPE_LIST || parentType === KtNodeTypes.INITIALIZER_LIST) {
+ return createAlignmentStrategy(jetCommonSettings.ALIGN_MULTILINE_EXTENDS_LIST, getAlignment())
+ }
+ else if (parentType === KtNodeTypes.PARENTHESIZED) {
+ return object : CommonAlignmentStrategy() {
+ private var bracketsAlignment: Alignment? = if (jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION) Alignment.createAlignment() else null
+
+ 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) {
+ return bracketsAlignment
+ }
+
+ if (childNodeType === LPAR || childNodeType === RPAR) {
+ return bracketsAlignment
+ }
+
+ return null
+ }
+ }
+ }
+
+ return getNullAlignmentStrategy()
+ }
+
+
+ private fun buildSubBlock(child: ASTNode, alignmentStrategy: CommonAlignmentStrategy, wrappingStrategy: WrappingStrategy): Block {
+ val childWrap = wrappingStrategy.getWrap(child.elementType)
+
+ // Skip one sub-level for operators, so type of block node is an element type of operator
+ if (child.elementType === KtNodeTypes.OPERATION_REFERENCE) {
+ val operationNode = child.firstChildNode
+ if (operationNode != null) {
+ return createBlock(
+ operationNode,
+ alignmentStrategy,
+ createChildIndent(child),
+ childWrap,
+ settings,
+ spacingBuilder)
+ }
+ }
+
+ return createBlock(child, alignmentStrategy, createChildIndent(child), childWrap, settings, spacingBuilder)
+ }
+
+ private fun buildSubBlocks(): ArrayList {
+ val blocks = ArrayList()
+
+ val childrenAlignmentStrategy = getChildrenAlignmentStrategy()
+ val wrappingStrategy = getWrappingStrategy()
+
+ var child: ASTNode? = node.firstChildNode
+ while (child != null) {
+ val childType = child.elementType
+
+ if (child.textRange.length == 0) {
+ child = child.treeNext
+ continue
+ }
+
+ if (childType === TokenType.WHITE_SPACE) {
+ child = child.treeNext
+ continue
+ }
+
+ blocks.add(buildSubBlock(child, childrenAlignmentStrategy, wrappingStrategy))
+ child = child.treeNext
+ }
+
+ return blocks
+ }
+
+ private fun getWrappingStrategy(): WrappingStrategy {
+ val commonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)
+ val elementType = node.elementType
+
+ if (elementType === KtNodeTypes.VALUE_ARGUMENT_LIST) {
+ return getWrappingStrategyForItemList(commonSettings.CALL_PARAMETERS_WRAP, KtNodeTypes.VALUE_ARGUMENT)
+ }
+ if (elementType === KtNodeTypes.VALUE_PARAMETER_LIST) {
+ val parentElementType = node.treeParent.elementType
+ if (parentElementType === KtNodeTypes.FUN || parentElementType === KtNodeTypes.CLASS) {
+ return getWrappingStrategyForItemList(commonSettings.METHOD_PARAMETERS_WRAP, KtNodeTypes.VALUE_PARAMETER)
+ }
+ }
+
+ return WrappingStrategy.NoWrapping
+ }
+}
+
+private val INDENT_RULES = arrayOf(
+ strategy("No indent for braces in blocks")
+ .`in`(KtNodeTypes.BLOCK, KtNodeTypes.CLASS_BODY, KtNodeTypes.FUNCTION_LITERAL)
+ .forType(RBRACE, LBRACE)
+ .set(Indent.getNoneIndent()),
+
+ strategy("Indent for block content")
+ .`in`(KtNodeTypes.BLOCK, KtNodeTypes.CLASS_BODY, KtNodeTypes.FUNCTION_LITERAL)
+ .notForType(RBRACE, LBRACE, KtNodeTypes.BLOCK)
+ .set(Indent.getNormalIndent(false)),
+
+ strategy("Indent for property accessors")
+ .`in`(KtNodeTypes.PROPERTY).forType(KtNodeTypes.PROPERTY_ACCESSOR)
+ .set(Indent.getNormalIndent()),
+
+ strategy("For a single statement in 'for'")
+ .`in`(KtNodeTypes.BODY).notForType(KtNodeTypes.BLOCK)
+ .set(Indent.getNormalIndent()),
+
+ strategy("For the entry in when")
+ .forType(KtNodeTypes.WHEN_ENTRY)
+ .set(Indent.getNormalIndent()),
+
+ strategy("For single statement in THEN and ELSE")
+ .`in`(KtNodeTypes.THEN, KtNodeTypes.ELSE).notForType(KtNodeTypes.BLOCK)
+ .set(Indent.getNormalIndent()),
+
+ strategy("Indent for parts")
+ .`in`(KtNodeTypes.PROPERTY, KtNodeTypes.FUN, KtNodeTypes.DESTRUCTURING_DECLARATION)
+ .notForType(KtNodeTypes.BLOCK, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD)
+ .set(Indent.getContinuationWithoutFirstIndent()),
+
+ strategy("Chained calls")
+ .`in`(KtNodeTypes.DOT_QUALIFIED_EXPRESSION, KtNodeTypes.SAFE_ACCESS_EXPRESSION)
+ .set(Indent.getContinuationWithoutFirstIndent(false)),
+
+ strategy("Delegation list")
+ .`in`(KtNodeTypes.SUPER_TYPE_LIST, KtNodeTypes.INITIALIZER_LIST)
+ .set(Indent.getContinuationIndent(false)),
+
+ strategy("Indices")
+ .`in`(KtNodeTypes.INDICES)
+ .set(Indent.getContinuationIndent(false)),
+
+ strategy("Binary expressions")
+ .`in`(BINARY_EXPRESSIONS)
+ .set(Indent.getContinuationWithoutFirstIndent(false)),
+
+ strategy("Parenthesized expression")
+ .`in`(KtNodeTypes.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`(KtNodeTypes.WHEN_ENTRY)
+ .notForType(KtNodeTypes.BLOCK, KtNodeTypes.WHEN_CONDITION_EXPRESSION, KtNodeTypes.WHEN_CONDITION_IN_RANGE, KtNodeTypes.WHEN_CONDITION_IS_PATTERN, ELSE_KEYWORD, ARROW)
+ .set(Indent.getNormalIndent()))
+
+private fun getOperationType(node: ASTNode): IElementType? = node.findChildByType(KtNodeTypes.OPERATION_REFERENCE)?.firstChildNode?.elementType
+
+private fun getAlignmentForChildInParenthesis(
+ shouldAlignChild: Boolean, parameter: IElementType, delimiter: IElementType,
+ shouldAlignParenthesis: Boolean, openBracket: IElementType, closeBracket: IElementType): CommonAlignmentStrategy {
+ val parameterAlignment = if (shouldAlignChild) Alignment.createAlignment() else null
+ val bracketsAlignment = if (shouldAlignParenthesis) Alignment.createAlignment() else null
+
+ return object : CommonAlignmentStrategy() {
+ 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 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 || 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 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
+ }
+}
\ No newline at end of file
diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinSpacingBuilder.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinSpacingBuilder.kt
index 9f99052894b..fff92839c7b 100644
--- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinSpacingBuilder.kt
+++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinSpacingBuilder.kt
@@ -22,7 +22,6 @@ import com.intellij.formatting.DependentSpacingRule.Trigger
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
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 org.jetbrains.kotlin.KtNodeTypes
@@ -30,13 +29,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.lexer.KtTokens
import java.util.ArrayList
-class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
- class SpacingNodeBlock(node: ASTNode): AbstractBlock(node, null, null) {
- override fun buildChildren(): MutableList? = ArrayList()
- override fun getSpacing(child1: Block?, child2: Block): Spacing? = null
- override fun isLeaf(): Boolean = false
- }
-
+class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings, val spacingBuilderUtil: KotlinSpacingBuilderUtil) {
private val builders = ArrayList()
private interface Builder {
@@ -89,12 +82,13 @@ class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
fun emptyLinesIfLineBreakInLeft(emptyLines: Int, numberOfLineFeedsOtherwise: Int = 1, numSpacesOtherwise: Int = 0) {
newRule { parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
val dependentSpacingRule = DependentSpacingRule(Trigger.HAS_LINE_FEEDS).registerData(Anchor.MIN_LINE_FEEDS, emptyLines + 1)
- LineFeedDependantSpacing(
- numSpacesOtherwise, numSpacesOtherwise,
- minimumLineFeeds = numberOfLineFeedsOtherwise,
- keepLineBreaks = codeStyleSettings.KEEP_LINE_BREAKS,
- keepBlankLines = codeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
- dependency = left.textRange, rule = dependentSpacingRule)
+ spacingBuilderUtil.createLineFeedDependentSpacing(numSpacesOtherwise,
+ numSpacesOtherwise,
+ numberOfLineFeedsOtherwise,
+ codeStyleSettings.KEEP_LINE_BREAKS,
+ codeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
+ left.textRange,
+ dependentSpacingRule)
}
}
@@ -145,24 +139,24 @@ class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
builder.init()
builders.add(builder)
}
-
- private class LineFeedDependantSpacing(
- minSpaces: Int,
- maxSpaces: Int,
- val minimumLineFeeds: Int,
- keepLineBreaks: Boolean,
- keepBlankLines: Int,
- dependency: TextRange,
- rule: DependentSpacingRule) : DependantSpacingImpl(minSpaces, maxSpaces, dependency, keepLineBreaks, keepBlankLines, rule) {
- override fun getMinLineFeeds(): Int {
- val superMin = super.getMinLineFeeds()
- return if (superMin == 0) minimumLineFeeds else superMin
- }
- }
}
-fun rules(codeStyleSettings: CodeStyleSettings, init: KotlinSpacingBuilder.() -> Unit): KotlinSpacingBuilder {
- val builder = KotlinSpacingBuilder(codeStyleSettings)
+interface KotlinSpacingBuilderUtil {
+ fun createLineFeedDependentSpacing(minSpaces: Int,
+ maxSpaces: Int,
+ minimumLineFeeds: Int,
+ keepLineBreaks: Boolean,
+ keepBlankLines: Int,
+ dependency: TextRange,
+ rule: DependentSpacingRule): Spacing
+
+ fun getPreviousNonWhitespaceLeaf(node: ASTNode?): ASTNode?
+
+ fun isWhitespaceOrEmpty(node: ASTNode?): Boolean
+}
+
+fun rules(codeStyleSettings: CodeStyleSettings, builderUtil: KotlinSpacingBuilderUtil, init: KotlinSpacingBuilder.() -> Unit): KotlinSpacingBuilder {
+ val builder = KotlinSpacingBuilder(codeStyleSettings, builderUtil)
builder.init()
return builder
}
diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/SyntheticKotlinBlock.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/SyntheticKotlinBlock.kt
index 2e3161a069a..960b7fbd0d9 100644
--- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/SyntheticKotlinBlock.kt
+++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/SyntheticKotlinBlock.kt
@@ -24,7 +24,6 @@ import com.intellij.openapi.util.TextRange
import com.intellij.formatting.ChildAttributes
import com.intellij.formatting.Spacing
import com.intellij.lang.ASTNode
-import com.intellij.psi.formatter.common.AbstractBlock
import com.intellij.formatting.ASTBlock
class SyntheticKotlinBlock(
@@ -33,7 +32,8 @@ class SyntheticKotlinBlock(
private val alignment: Alignment?,
private val indent: Indent?,
private val wrap: Wrap?,
- private val spacingBuilder: KotlinSpacingBuilder
+ private val spacingBuilder: KotlinSpacingBuilder,
+ private val createSyntheticSpacingNodeBlock: (ASTNode) -> ASTBlock
) : ASTBlock {
private val textRange = TextRange(
@@ -49,8 +49,9 @@ class SyntheticKotlinBlock(
override fun isIncomplete() = getSubBlocks().last().isIncomplete
override fun isLeaf() = false
override fun getNode() = node
- override fun getSpacing(child1: Block?, child2: Block): Spacing? =
- spacingBuilder.getSpacing(KotlinSpacingBuilder.SpacingNodeBlock(node.treeParent!!), child1, child2)
+ override fun getSpacing(child1: Block?, child2: Block): Spacing? {
+ return spacingBuilder.getSpacing(createSyntheticSpacingNodeBlock(node.treeParent!!), child1, child2)
+ }
override fun toString(): String {
@@ -59,12 +60,10 @@ class SyntheticKotlinBlock(
loop@
while (treeNode == null) when (child) {
- is AbstractBlock -> {
- treeNode = child.node
- }
- is SyntheticKotlinBlock -> {
- child = child.getSubBlocks().first()
- }
+ is SyntheticKotlinBlock -> child = child.getSubBlocks().first()
+
+ is ASTBlock -> treeNode = child.node
+
else -> break@loop
}
@@ -81,4 +80,4 @@ class SyntheticKotlinBlock(
return javaClass.name + ": " + textRange
}
-}
+}
\ No newline at end of file
diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt
index fb36b1f803d..b761988c64d 100644
--- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt
+++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/kotlinSpacingRules.kt
@@ -22,8 +22,9 @@ import com.intellij.formatting.SpacingBuilder
import com.intellij.formatting.SpacingBuilder.RuleBuilder
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiWhiteSpace
+import com.intellij.psi.TokenType
import com.intellij.psi.codeStyle.CodeStyleSettings
-import com.intellij.psi.formatter.FormatterUtil
+import com.intellij.psi.impl.source.tree.TreeUtil
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes.*
@@ -44,11 +45,11 @@ fun SpacingBuilder.afterInside(element: IElementType, tokenSet: TokenSet, spacin
tokenSet.types.forEach { inType -> afterInside(element, inType).spacingFun() }
}
-fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
+fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacingBuilderUtil): KotlinSpacingBuilder {
val kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java)!!
val kotlinCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)!!
- return rules(settings) {
+ return rules(settings, builderUtil) {
val DECLARATIONS =
TokenSet.create(PROPERTY, FUN, CLASS, OBJECT_DECLARATION, ENUM_ENTRY, SECONDARY_CONSTRUCTOR, CLASS_INITIALIZER)
@@ -65,7 +66,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
inPosition(left = FUN, right = CLASS).emptyLinesIfLineBreakInLeft(1)
inPosition(left = ENUM_ENTRY, right = ENUM_ENTRY).emptyLinesIfLineBreakInLeft(
- emptyLines = 0, numSpacesOtherwise = 1, numberOfLineFeedsOtherwise = 0)
+ emptyLines = 0, numberOfLineFeedsOtherwise = 0, numSpacesOtherwise = 1)
inPosition(parent = CLASS_BODY, left = SEMICOLON).customRule { parent, left, right ->
val klass = parent.node.treeParent.psi as? KtClass ?: return@customRule null
@@ -249,7 +250,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
inPosition(parent = parent, right = keyword).customRule {
parent, left, right ->
- val previousLeaf = FormatterUtil.getPreviousNonWhitespaceLeaf(right.node)
+ val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(right.node)
val leftBlock = if (
previousLeaf != null &&
previousLeaf.elementType == RBRACE &&
@@ -277,7 +278,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
if (block != null && block.elementType == blockType) {
val leftBrace = block.findChildByType(LBRACE)
if (leftBrace != null) {
- val previousLeaf = FormatterUtil.getPreviousNonWhitespaceLeaf(leftBrace)
+ val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(leftBrace)
val isAfterEolComment = previousLeaf != null && (previousLeaf.elementType == EOL_COMMENT)
val keepLineBreaks = kotlinSettings.LBRACE_ON_NEXT_LINE || isAfterEolComment
val minimumLF = if (kotlinSettings.LBRACE_ON_NEXT_LINE) 1 else 0
@@ -390,4 +391,4 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
after(EOL_COMMENT).lineBreakInCode()
}
}
-}
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt
index bc24e090d4e..063119979a5 100644
--- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinBlock.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2015 JetBrains s.r.o.
+ * Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,428 +19,110 @@ package org.jetbrains.kotlin.idea.formatter
import com.intellij.formatting.*
import com.intellij.formatting.alignment.AlignmentStrategy
import com.intellij.lang.ASTNode
-import com.intellij.psi.TokenType
+import com.intellij.openapi.util.TextRange
import com.intellij.psi.codeStyle.CodeStyleSettings
+import com.intellij.psi.formatter.FormatterUtil
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.*
-
-private val KDOC_COMMENT_INDENT = 1
-
-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
*/
class KotlinBlock(
node: ASTNode,
- private val myAlignmentStrategy: NodeAlignmentStrategy,
+ private val myAlignmentStrategy: CommonAlignmentStrategy,
private val myIndent: Indent?,
wrap: Wrap?,
- private val mySettings: CodeStyleSettings,
+ mySettings: CodeStyleSettings,
private val mySpacingBuilder: KotlinSpacingBuilder) : AbstractBlock(node, wrap, myAlignmentStrategy.getAlignment(node)) {
- private var mySubBlocks: List? = null
+ private val kotlinDelegationBlock = object : KotlinCommonBlock(node, mySettings, mySpacingBuilder, myAlignmentStrategy) {
+ override fun getNullAlignmentStrategy(): CommonAlignmentStrategy = NodeAlignmentStrategy.getNullStrategy()
- override fun getIndent(): Indent? {
- return myIndent
- }
-
- override fun buildChildren(): List {
- if (mySubBlocks == null) {
- var nodeSubBlocks = buildSubBlocks() as ArrayList
-
- 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).
-
- 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)),
- operationSyntheticBlock)
- }
- }
-
- mySubBlocks = nodeSubBlocks
- }
- return mySubBlocks!!
- }
-
- private fun buildSubBlocks(): List {
- val blocks = ArrayList()
-
- val childrenAlignmentStrategy = getChildrenAlignmentStrategy()
- val wrappingStrategy = getWrappingStrategy()
-
- var child: ASTNode? = myNode.firstChildNode
- while (child != null) {
- val childType = child.elementType
-
- if (child.textRange.length == 0) {
- child = child.treeNext
- continue
- }
-
- if (childType === TokenType.WHITE_SPACE) {
- child = child.treeNext
- continue
- }
-
- blocks.add(buildSubBlock(child, childrenAlignmentStrategy, wrappingStrategy))
- child = child.treeNext
+ override fun createAlignmentStrategy(alignOption: Boolean, defaultAlignment: Alignment?): CommonAlignmentStrategy {
+ return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap(createAlignment(alignOption, defaultAlignment)))
}
- return blocks
- }
-
- 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.elementType === OPERATION_REFERENCE) {
- val operationNode = child.firstChildNode
- if (operationNode != null) {
- return KotlinBlock(operationNode, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder)
+ override fun getAlignmentForCaseBranch(shouldAlignInColumns: Boolean): CommonAlignmentStrategy {
+ return if (shouldAlignInColumns) {
+ NodeAlignmentStrategy.fromTypes(
+ AlignmentStrategy.createAlignmentPerTypeStrategy(listOf(ARROW as IElementType), WHEN_ENTRY, true))
+ }
+ else {
+ NodeAlignmentStrategy.getNullStrategy()
}
}
- return KotlinBlock(child, alignmentStrategy, createChildIndent(child), wrap, mySettings, mySpacingBuilder)
+ override fun getAlignment(): Alignment? = alignment
+
+ override fun isIncompleteInSuper(): Boolean = this@KotlinBlock.isIncomplete
+
+ override fun getSuperChildAttributes(newChildIndex: Int): ChildAttributes = super@KotlinBlock.getChildAttributes(newChildIndex)
+
+ override fun getSubBlocks(): List = subBlocks
+
+ override fun createBlock(node: ASTNode, alignmentStrategy: CommonAlignmentStrategy, indent: Indent?, wrap: Wrap?, settings: CodeStyleSettings, spacingBuilder: KotlinSpacingBuilder): Block {
+ return KotlinBlock(
+ node,
+ alignmentStrategy,
+ indent,
+ wrap,
+ mySettings,
+ mySpacingBuilder)
+ }
+
+ override fun createSyntheticSpacingNodeBlock(node: ASTNode): ASTBlock {
+ return object : AbstractBlock(node, null, null) {
+ override fun isLeaf(): Boolean = false
+ override fun getSpacing(child1: Block?, child2: Block): Spacing? = null
+ override fun buildChildren(): List = emptyList()
+ }
+ }
}
+ override fun getIndent(): Indent? = myIndent
+
+ override fun buildChildren(): List = kotlinDelegationBlock.buildChildren()
+
override fun getSpacing(child1: Block?, child2: Block): Spacing? = mySpacingBuilder.getSpacing(this, child1, child2)
- 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)
+ override fun getChildAttributes(newChildIndex: Int): ChildAttributes = kotlinDelegationBlock.getChildAttributes(newChildIndex)
- TRY -> ChildAttributes(Indent.getNoneIndent(), null)
-
- DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION -> ChildAttributes(Indent.getContinuationWithoutFirstIndent(), null)
-
- 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)
- }
- }
-
- DOC_COMMENT -> ChildAttributes(Indent.getSpaceIndent(KDOC_COMMENT_INDENT), null)
-
- 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 WrappingStrategy.NoWrapping
- }
-
- // 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)
- }
- else if (parentType === VALUE_ARGUMENT_LIST) {
- return getAlignmentForChildInParenthesis(
- jetCommonSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS, VALUE_ARGUMENT, COMMA,
- jetCommonSettings.ALIGN_MULTILINE_METHOD_BRACKETS, LPAR, RPAR)
- }
- else if (parentType === WHEN) {
- return getAlignmentForCaseBranch(jetSettings.ALIGN_IN_COLUMNS_CASE_BRANCH)
- }
- else if (parentType === WHEN_ENTRY) {
- return myAlignmentStrategy
- }
- else if (parentType in BINARY_EXPRESSIONS && getOperationType(node) in ALIGN_FOR_BINARY_OPERATIONS) {
- return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap(
- createAlignment(jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION, alignment)))
- }
- else if (parentType === SUPER_TYPE_LIST || parentType === INITIALIZER_LIST) {
- return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap(
- createAlignment(jetCommonSettings.ALIGN_MULTILINE_EXTENDS_LIST, alignment)))
- }
- else if (parentType === PARENTHESIZED) {
- return object : NodeAlignmentStrategy() {
- private var bracketsAlignment: Alignment? = if (jetCommonSettings.ALIGN_MULTILINE_BINARY_OPERATION) Alignment.createAlignment() else null
-
- override fun getAlignment(childNode: ASTNode): Alignment? {
- val childNodeType = childNode.elementType
- val prev = getPrevWithoutWhitespace(childNode)
-
- if (prev != null && prev.elementType === TokenType.ERROR_ELEMENT || childNodeType === TokenType.ERROR_ELEMENT) {
- return bracketsAlignment
- }
-
- if (childNodeType === LPAR || childNodeType === RPAR) {
- return bracketsAlignment
- }
-
- return null
- }
- }
- }
-
- return NodeAlignmentStrategy.getNullStrategy()
- }
+ override fun isLeaf(): Boolean = kotlinDelegationBlock.isLeaf()
}
-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
+object KotlinSpacingBuilderUtilImpl : KotlinSpacingBuilderUtil {
+ override fun getPreviousNonWhitespaceLeaf(node: ASTNode?): ASTNode? {
+ return FormatterUtil.getPreviousNonWhitespaceLeaf(node)
}
- 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
+ override fun isWhitespaceOrEmpty(node: ASTNode?): Boolean {
+ return FormatterUtil.isWhitespaceOrEmpty(node)
}
- 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
+ override fun createLineFeedDependentSpacing(
+ minSpaces: Int,
+ maxSpaces: Int,
+ minimumLineFeeds: Int,
+ keepLineBreaks: Boolean,
+ keepBlankLines: Int,
+ dependency: TextRange,
+ rule: DependentSpacingRule): Spacing {
+ return object : DependantSpacingImpl(minSpaces, maxSpaces, dependency, keepLineBreaks, keepBlankLines, rule) {
+ override fun getMinLineFeeds(): Int {
+ val superMin = super.getMinLineFeeds()
+ return if (superMin == 0) minimumLineFeeds else superMin
}
-
- 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
diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java
index 2444504ea11..67dc25bea63 100644
--- a/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java
+++ b/idea/src/org/jetbrains/kotlin/idea/formatter/KotlinFormattingModelBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2015 JetBrains s.r.o.
+ * Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public class KotlinFormattingModelBuilder implements FormattingModelBuilder {
PsiFile containingFile = element.getContainingFile().getViewProvider().getPsi(KotlinLanguage.INSTANCE);
KotlinBlock block = new KotlinBlock(
containingFile.getNode(), NodeAlignmentStrategy.getNullStrategy(), Indent.getNoneIndent(), null, settings,
- KotlinSpacingRulesKt.createSpacingBuilder(settings));
+ KotlinSpacingRulesKt.createSpacingBuilder(settings, KotlinSpacingBuilderUtilImpl.INSTANCE));
//TODO: this is temporary code to allow formatting non-physical files in non-UI thread (used by conversion from Java to Kotlin)
// it's needed until IDEA's issue with this document being created with wrong threading policy is fixed
diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/NodeAlignmentStrategy.java b/idea/src/org/jetbrains/kotlin/idea/formatter/NodeAlignmentStrategy.java
index 1135929520c..20c96bdd86f 100644
--- a/idea/src/org/jetbrains/kotlin/idea/formatter/NodeAlignmentStrategy.java
+++ b/idea/src/org/jetbrains/kotlin/idea/formatter/NodeAlignmentStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2015 JetBrains s.r.o.
+ * Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-public abstract class NodeAlignmentStrategy {
+public abstract class NodeAlignmentStrategy extends CommonAlignmentStrategy {
private static final NodeAlignmentStrategy NULL_STRATEGY = fromTypes(AlignmentStrategy.wrap(null));
@@ -35,10 +35,11 @@ public abstract class NodeAlignmentStrategy {
return new AlignmentStrategyWrapper(strategy);
}
+ @Override
@Nullable
public abstract Alignment getAlignment(@NotNull ASTNode node);
- public static class AlignmentStrategyWrapper extends NodeAlignmentStrategy {
+ private static class AlignmentStrategyWrapper extends NodeAlignmentStrategy {
private final AlignmentStrategy internalStrategy;
public AlignmentStrategyWrapper(@NotNull AlignmentStrategy internalStrategy) {
diff --git a/ultimate/.idea/libraries/main_project_production.xml b/ultimate/.idea/libraries/main_project_production.xml
index 4effdebc0dc..5a4296b76f2 100644
--- a/ultimate/.idea/libraries/main_project_production.xml
+++ b/ultimate/.idea/libraries/main_project_production.xml
@@ -63,6 +63,7 @@
+
@@ -94,6 +95,7 @@
+
\ No newline at end of file