Split KotlinBlock on two clases to share independent logic about

formatting  between Idea and Eclipse

 Also make 'formatter' module depend on fewer dependencies
This commit is contained in:
Mikhail Zarechenskiy
2016-03-20 23:24:48 +03:00
parent 4c006300ef
commit 37e0b71584
11 changed files with 586 additions and 439 deletions
+1
View File
@@ -48,6 +48,7 @@
<element id="module-output" name="idea-live-templates" />
<element id="module-output" name="resolution" />
<element id="module-output" name="idea-ultimate" />
<element id="module-output" name="formatter" />
</element>
<element id="library" level="project" name="javax.inject" />
<element id="directory" name="jps">
+12 -4
View File
@@ -5,11 +5,19 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="kotlin-runtime" level="project" />
<orderEntry type="library" name="idea-full" level="project" />
<orderEntry type="module" module-name="idea-core" />
<orderEntry type="module" module-name="frontend.java" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../ideaSDK/lib/openapi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -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?
}
@@ -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<Block>? = 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<Block>
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<Block> {
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<Block>(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<Block> {
val blocks = ArrayList<Block>()
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<NodeIndentStrategy>(
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<Block>, tokenSet: TokenSet): Int {
return blocks.indexOfFirst { block ->
if (block !is ASTBlock) return@indexOfFirst false
val node = block.node
node != null && node.elementType in tokenSet
}
}
@@ -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<Block>? = 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<Builder>()
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
}
@@ -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
}
}
}
@@ -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()
}
}
}
}
@@ -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<Block>? = 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<Block> {
if (mySubBlocks == null) {
var nodeSubBlocks = buildSubBlocks() as ArrayList<Block>
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<Block> {
val blocks = ArrayList<Block>()
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<Block> = 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<Block> = emptyList()
}
}
}
override fun getIndent(): Indent? = myIndent
override fun buildChildren(): List<Block> = 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<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()))
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<Block>, 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;
}
@@ -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
@@ -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) {
+2
View File
@@ -63,6 +63,7 @@
<root url="file://$PROJECT_DIR$/../out/production/util" />
<root url="file://$PROJECT_DIR$/../out/production/util.runtime" />
<root url="file://$PROJECT_DIR$/../out/production/Kotlin" />
<root url="file://$PROJECT_DIR$/../out/production/formatter" />
</CLASSES>
<JAVADOC />
<SOURCES>
@@ -94,6 +95,7 @@
<root url="file://$PROJECT_DIR$/../idea/idea-jps-common/src" />
<root url="file://$PROJECT_DIR$/../idea/idea-live-templates/src" />
<root url="file://$PROJECT_DIR$/../idea/idea-test-framework/src" />
<root url="file://$PROJECT_DIR$/../idea/formatter/src" />
</SOURCES>
</library>
</component>