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:
Generated
+1
@@ -48,6 +48,7 @@
|
|||||||
<element id="module-output" name="idea-live-templates" />
|
<element id="module-output" name="idea-live-templates" />
|
||||||
<element id="module-output" name="resolution" />
|
<element id="module-output" name="resolution" />
|
||||||
<element id="module-output" name="idea-ultimate" />
|
<element id="module-output" name="idea-ultimate" />
|
||||||
|
<element id="module-output" name="formatter" />
|
||||||
</element>
|
</element>
|
||||||
<element id="library" level="project" name="javax.inject" />
|
<element id="library" level="project" name="javax.inject" />
|
||||||
<element id="directory" name="jps">
|
<element id="directory" name="jps">
|
||||||
|
|||||||
@@ -5,11 +5,19 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="kotlin-runtime" level="project" />
|
<orderEntry type="library" name="kotlin-runtime" level="project" />
|
||||||
<orderEntry type="library" name="idea-full" level="project" />
|
<orderEntry type="module" module-name="frontend" />
|
||||||
<orderEntry type="module" module-name="idea-core" />
|
<orderEntry type="module" module-name="util" />
|
||||||
<orderEntry type="module" module-name="frontend.java" />
|
<orderEntry type="module-library">
|
||||||
|
<library>
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$MODULE_DIR$/../../ideaSDK/lib/openapi.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</orderEntry>
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</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.lang.ASTNode
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings
|
import com.intellij.psi.codeStyle.CodeStyleSettings
|
||||||
import com.intellij.psi.formatter.common.AbstractBlock
|
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
@@ -30,13 +29,7 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
|
class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings, val spacingBuilderUtil: KotlinSpacingBuilderUtil) {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private val builders = ArrayList<Builder>()
|
private val builders = ArrayList<Builder>()
|
||||||
|
|
||||||
private interface Builder {
|
private interface Builder {
|
||||||
@@ -89,12 +82,13 @@ class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
|
|||||||
fun emptyLinesIfLineBreakInLeft(emptyLines: Int, numberOfLineFeedsOtherwise: Int = 1, numSpacesOtherwise: Int = 0) {
|
fun emptyLinesIfLineBreakInLeft(emptyLines: Int, numberOfLineFeedsOtherwise: Int = 1, numSpacesOtherwise: Int = 0) {
|
||||||
newRule { parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
|
newRule { parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
|
||||||
val dependentSpacingRule = DependentSpacingRule(Trigger.HAS_LINE_FEEDS).registerData(Anchor.MIN_LINE_FEEDS, emptyLines + 1)
|
val dependentSpacingRule = DependentSpacingRule(Trigger.HAS_LINE_FEEDS).registerData(Anchor.MIN_LINE_FEEDS, emptyLines + 1)
|
||||||
LineFeedDependantSpacing(
|
spacingBuilderUtil.createLineFeedDependentSpacing(numSpacesOtherwise,
|
||||||
numSpacesOtherwise, numSpacesOtherwise,
|
numSpacesOtherwise,
|
||||||
minimumLineFeeds = numberOfLineFeedsOtherwise,
|
numberOfLineFeedsOtherwise,
|
||||||
keepLineBreaks = codeStyleSettings.KEEP_LINE_BREAKS,
|
codeStyleSettings.KEEP_LINE_BREAKS,
|
||||||
keepBlankLines = codeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
|
codeStyleSettings.KEEP_BLANK_LINES_IN_DECLARATIONS,
|
||||||
dependency = left.textRange, rule = dependentSpacingRule)
|
left.textRange,
|
||||||
|
dependentSpacingRule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,24 +139,24 @@ class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
|
|||||||
builder.init()
|
builder.init()
|
||||||
builders.add(builder)
|
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 {
|
interface KotlinSpacingBuilderUtil {
|
||||||
val builder = KotlinSpacingBuilder(codeStyleSettings)
|
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()
|
builder.init()
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.intellij.openapi.util.TextRange
|
|||||||
import com.intellij.formatting.ChildAttributes
|
import com.intellij.formatting.ChildAttributes
|
||||||
import com.intellij.formatting.Spacing
|
import com.intellij.formatting.Spacing
|
||||||
import com.intellij.lang.ASTNode
|
import com.intellij.lang.ASTNode
|
||||||
import com.intellij.psi.formatter.common.AbstractBlock
|
|
||||||
import com.intellij.formatting.ASTBlock
|
import com.intellij.formatting.ASTBlock
|
||||||
|
|
||||||
class SyntheticKotlinBlock(
|
class SyntheticKotlinBlock(
|
||||||
@@ -33,7 +32,8 @@ class SyntheticKotlinBlock(
|
|||||||
private val alignment: Alignment?,
|
private val alignment: Alignment?,
|
||||||
private val indent: Indent?,
|
private val indent: Indent?,
|
||||||
private val wrap: Wrap?,
|
private val wrap: Wrap?,
|
||||||
private val spacingBuilder: KotlinSpacingBuilder
|
private val spacingBuilder: KotlinSpacingBuilder,
|
||||||
|
private val createSyntheticSpacingNodeBlock: (ASTNode) -> ASTBlock
|
||||||
) : ASTBlock {
|
) : ASTBlock {
|
||||||
|
|
||||||
private val textRange = TextRange(
|
private val textRange = TextRange(
|
||||||
@@ -49,8 +49,9 @@ class SyntheticKotlinBlock(
|
|||||||
override fun isIncomplete() = getSubBlocks().last().isIncomplete
|
override fun isIncomplete() = getSubBlocks().last().isIncomplete
|
||||||
override fun isLeaf() = false
|
override fun isLeaf() = false
|
||||||
override fun getNode() = node
|
override fun getNode() = node
|
||||||
override fun getSpacing(child1: Block?, child2: Block): Spacing? =
|
override fun getSpacing(child1: Block?, child2: Block): Spacing? {
|
||||||
spacingBuilder.getSpacing(KotlinSpacingBuilder.SpacingNodeBlock(node.treeParent!!), child1, child2)
|
return spacingBuilder.getSpacing(createSyntheticSpacingNodeBlock(node.treeParent!!), child1, child2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@@ -59,12 +60,10 @@ class SyntheticKotlinBlock(
|
|||||||
|
|
||||||
loop@
|
loop@
|
||||||
while (treeNode == null) when (child) {
|
while (treeNode == null) when (child) {
|
||||||
is AbstractBlock -> {
|
is SyntheticKotlinBlock -> child = child.getSubBlocks().first()
|
||||||
treeNode = child.node
|
|
||||||
}
|
is ASTBlock -> treeNode = child.node
|
||||||
is SyntheticKotlinBlock -> {
|
|
||||||
child = child.getSubBlocks().first()
|
|
||||||
}
|
|
||||||
else -> break@loop
|
else -> break@loop
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,4 +80,4 @@ class SyntheticKotlinBlock(
|
|||||||
|
|
||||||
return javaClass.name + ": " + textRange
|
return javaClass.name + ": " + textRange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,9 @@ import com.intellij.formatting.SpacingBuilder
|
|||||||
import com.intellij.formatting.SpacingBuilder.RuleBuilder
|
import com.intellij.formatting.SpacingBuilder.RuleBuilder
|
||||||
import com.intellij.lang.ASTNode
|
import com.intellij.lang.ASTNode
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import com.intellij.psi.TokenType
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings
|
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.IElementType
|
||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
import org.jetbrains.kotlin.KtNodeTypes.*
|
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() }
|
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 kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java)!!
|
||||||
val kotlinCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)!!
|
val kotlinCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)!!
|
||||||
|
|
||||||
return rules(settings) {
|
return rules(settings, builderUtil) {
|
||||||
val DECLARATIONS =
|
val DECLARATIONS =
|
||||||
TokenSet.create(PROPERTY, FUN, CLASS, OBJECT_DECLARATION, ENUM_ENTRY, SECONDARY_CONSTRUCTOR, CLASS_INITIALIZER)
|
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 = FUN, right = CLASS).emptyLinesIfLineBreakInLeft(1)
|
||||||
|
|
||||||
inPosition(left = ENUM_ENTRY, right = ENUM_ENTRY).emptyLinesIfLineBreakInLeft(
|
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 ->
|
inPosition(parent = CLASS_BODY, left = SEMICOLON).customRule { parent, left, right ->
|
||||||
val klass = parent.node.treeParent.psi as? KtClass ?: return@customRule null
|
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 {
|
inPosition(parent = parent, right = keyword).customRule {
|
||||||
parent, left, right ->
|
parent, left, right ->
|
||||||
|
|
||||||
val previousLeaf = FormatterUtil.getPreviousNonWhitespaceLeaf(right.node)
|
val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(right.node)
|
||||||
val leftBlock = if (
|
val leftBlock = if (
|
||||||
previousLeaf != null &&
|
previousLeaf != null &&
|
||||||
previousLeaf.elementType == RBRACE &&
|
previousLeaf.elementType == RBRACE &&
|
||||||
@@ -277,7 +278,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
|
|||||||
if (block != null && block.elementType == blockType) {
|
if (block != null && block.elementType == blockType) {
|
||||||
val leftBrace = block.findChildByType(LBRACE)
|
val leftBrace = block.findChildByType(LBRACE)
|
||||||
if (leftBrace != null) {
|
if (leftBrace != null) {
|
||||||
val previousLeaf = FormatterUtil.getPreviousNonWhitespaceLeaf(leftBrace)
|
val previousLeaf = builderUtil.getPreviousNonWhitespaceLeaf(leftBrace)
|
||||||
val isAfterEolComment = previousLeaf != null && (previousLeaf.elementType == EOL_COMMENT)
|
val isAfterEolComment = previousLeaf != null && (previousLeaf.elementType == EOL_COMMENT)
|
||||||
val keepLineBreaks = kotlinSettings.LBRACE_ON_NEXT_LINE || isAfterEolComment
|
val keepLineBreaks = kotlinSettings.LBRACE_ON_NEXT_LINE || isAfterEolComment
|
||||||
val minimumLF = if (kotlinSettings.LBRACE_ON_NEXT_LINE) 1 else 0
|
val minimumLF = if (kotlinSettings.LBRACE_ON_NEXT_LINE) 1 else 0
|
||||||
@@ -390,4 +391,4 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
|
|||||||
after(EOL_COMMENT).lineBreakInCode()
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.*
|
||||||
import com.intellij.formatting.alignment.AlignmentStrategy
|
import com.intellij.formatting.alignment.AlignmentStrategy
|
||||||
import com.intellij.lang.ASTNode
|
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.codeStyle.CodeStyleSettings
|
||||||
|
import com.intellij.psi.formatter.FormatterUtil
|
||||||
import com.intellij.psi.formatter.common.AbstractBlock
|
import com.intellij.psi.formatter.common.AbstractBlock
|
||||||
import com.intellij.psi.tree.IElementType
|
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.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.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
|
* @see Block for good JavaDoc documentation
|
||||||
*/
|
*/
|
||||||
class KotlinBlock(
|
class KotlinBlock(
|
||||||
node: ASTNode,
|
node: ASTNode,
|
||||||
private val myAlignmentStrategy: NodeAlignmentStrategy,
|
private val myAlignmentStrategy: CommonAlignmentStrategy,
|
||||||
private val myIndent: Indent?,
|
private val myIndent: Indent?,
|
||||||
wrap: Wrap?,
|
wrap: Wrap?,
|
||||||
private val mySettings: CodeStyleSettings,
|
mySettings: CodeStyleSettings,
|
||||||
private val mySpacingBuilder: KotlinSpacingBuilder) : AbstractBlock(node, wrap, myAlignmentStrategy.getAlignment(node)) {
|
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? {
|
override fun createAlignmentStrategy(alignOption: Boolean, defaultAlignment: Alignment?): CommonAlignmentStrategy {
|
||||||
return myIndent
|
return NodeAlignmentStrategy.fromTypes(AlignmentStrategy.wrap(createAlignment(alignOption, defaultAlignment)))
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks
|
override fun getAlignmentForCaseBranch(shouldAlignInColumns: Boolean): CommonAlignmentStrategy {
|
||||||
}
|
return if (shouldAlignInColumns) {
|
||||||
|
NodeAlignmentStrategy.fromTypes(
|
||||||
private fun buildSubBlock(
|
AlignmentStrategy.createAlignmentPerTypeStrategy(listOf(ARROW as IElementType), WHEN_ENTRY, true))
|
||||||
child: ASTNode,
|
}
|
||||||
alignmentStrategy: NodeAlignmentStrategy,
|
else {
|
||||||
wrappingStrategy: WrappingStrategy): Block {
|
NodeAlignmentStrategy.getNullStrategy()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 getSpacing(child1: Block?, child2: Block): Spacing? = mySpacingBuilder.getSpacing(this, child1, child2)
|
||||||
|
|
||||||
override fun getChildAttributes(newChildIndex: Int): ChildAttributes {
|
override fun getChildAttributes(newChildIndex: Int): ChildAttributes = kotlinDelegationBlock.getChildAttributes(newChildIndex)
|
||||||
val type = node.elementType
|
|
||||||
return when (type) {
|
|
||||||
in CODE_BLOCKS, WHEN, IF, FOR, WHILE, DO_WHILE -> ChildAttributes(Indent.getNormalIndent(), null)
|
|
||||||
|
|
||||||
TRY -> ChildAttributes(Indent.getNoneIndent(), null)
|
override fun isLeaf(): Boolean = kotlinDelegationBlock.isLeaf()
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val INDENT_RULES = arrayOf<NodeIndentStrategy>(
|
object KotlinSpacingBuilderUtilImpl : KotlinSpacingBuilderUtil {
|
||||||
strategy("No indent for braces in blocks")
|
override fun getPreviousNonWhitespaceLeaf(node: ASTNode?): ASTNode? {
|
||||||
.`in`(BLOCK, CLASS_BODY, FUNCTION_LITERAL)
|
return FormatterUtil.getPreviousNonWhitespaceLeaf(node)
|
||||||
.forType(RBRACE, LBRACE)
|
|
||||||
.set(Indent.getNoneIndent()),
|
|
||||||
|
|
||||||
strategy("Indent for block content")
|
|
||||||
.`in`(BLOCK, CLASS_BODY, FUNCTION_LITERAL)
|
|
||||||
.notForType(RBRACE, LBRACE, BLOCK)
|
|
||||||
.set(Indent.getNormalIndent(false)),
|
|
||||||
|
|
||||||
strategy("Indent for property accessors")
|
|
||||||
.`in`(PROPERTY).forType(PROPERTY_ACCESSOR)
|
|
||||||
.set(Indent.getNormalIndent()),
|
|
||||||
|
|
||||||
strategy("For a single statement in 'for'")
|
|
||||||
.`in`(BODY).notForType(BLOCK)
|
|
||||||
.set(Indent.getNormalIndent()),
|
|
||||||
|
|
||||||
strategy("For the entry in when")
|
|
||||||
.forType(WHEN_ENTRY)
|
|
||||||
.set(Indent.getNormalIndent()),
|
|
||||||
|
|
||||||
strategy("For single statement in THEN and ELSE")
|
|
||||||
.`in`(THEN, ELSE).notForType(BLOCK)
|
|
||||||
.set(Indent.getNormalIndent()),
|
|
||||||
|
|
||||||
strategy("Indent for parts")
|
|
||||||
.`in`(PROPERTY, FUN, DESTRUCTURING_DECLARATION)
|
|
||||||
.notForType(BLOCK, FUN_KEYWORD, VAL_KEYWORD, VAR_KEYWORD)
|
|
||||||
.set(Indent.getContinuationWithoutFirstIndent()),
|
|
||||||
|
|
||||||
strategy("Chained calls")
|
|
||||||
.`in`(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
|
|
||||||
.set(Indent.getContinuationWithoutFirstIndent(false)),
|
|
||||||
|
|
||||||
strategy("Delegation list")
|
|
||||||
.`in`(SUPER_TYPE_LIST, INITIALIZER_LIST)
|
|
||||||
.set(Indent.getContinuationIndent(false)),
|
|
||||||
|
|
||||||
strategy("Indices")
|
|
||||||
.`in`(INDICES)
|
|
||||||
.set(Indent.getContinuationIndent(false)),
|
|
||||||
|
|
||||||
strategy("Binary expressions")
|
|
||||||
.`in`(BINARY_EXPRESSIONS)
|
|
||||||
.set(Indent.getContinuationWithoutFirstIndent(false)),
|
|
||||||
|
|
||||||
strategy("Parenthesized expression")
|
|
||||||
.`in`(PARENTHESIZED)
|
|
||||||
.set(Indent.getContinuationWithoutFirstIndent(false)),
|
|
||||||
|
|
||||||
strategy("KDoc comment indent")
|
|
||||||
.`in`(KDOC_CONTENT)
|
|
||||||
.forType(KDocTokens.LEADING_ASTERISK, KDocTokens.END)
|
|
||||||
.set(Indent.getSpaceIndent(KDOC_COMMENT_INDENT)),
|
|
||||||
|
|
||||||
strategy("Block in when entry")
|
|
||||||
.`in`(WHEN_ENTRY)
|
|
||||||
.notForType(BLOCK, WHEN_CONDITION_EXPRESSION, WHEN_CONDITION_IN_RANGE, WHEN_CONDITION_IS_PATTERN, ELSE_KEYWORD, ARROW)
|
|
||||||
.set(Indent.getNormalIndent()))
|
|
||||||
|
|
||||||
private fun getPrevWithoutWhitespace(pNode: ASTNode?): ASTNode? {
|
|
||||||
var node = pNode
|
|
||||||
node = node!!.treePrev
|
|
||||||
while (node != null && node.elementType === TokenType.WHITE_SPACE) {
|
|
||||||
node = node.treePrev
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
override fun isWhitespaceOrEmpty(node: ASTNode?): Boolean {
|
||||||
}
|
return FormatterUtil.isWhitespaceOrEmpty(node)
|
||||||
|
|
||||||
private fun getPrevWithoutWhitespaceAndComments(pNode: ASTNode?): ASTNode? {
|
|
||||||
var node = pNode
|
|
||||||
node = node!!.treePrev
|
|
||||||
while (node != null && (node.elementType === TokenType.WHITE_SPACE || KtTokens.COMMENTS.contains(node.elementType))) {
|
|
||||||
node = node.treePrev
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return node
|
override fun createLineFeedDependentSpacing(
|
||||||
}
|
minSpaces: Int,
|
||||||
|
maxSpaces: Int,
|
||||||
private fun getWrappingStrategyForItemList(wrapType: Int, itemType: IElementType): WrappingStrategy {
|
minimumLineFeeds: Int,
|
||||||
val itemWrap = Wrap.createWrap(wrapType, false)
|
keepLineBreaks: Boolean,
|
||||||
return object : WrappingStrategy {
|
keepBlankLines: Int,
|
||||||
override fun getWrap(childElementType: IElementType): Wrap? {
|
dependency: TextRange,
|
||||||
return if (childElementType === itemType) itemWrap else null
|
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
|
||||||
private fun getAlignmentForChildInParenthesis(
|
|
||||||
shouldAlignChild: Boolean, parameter: IElementType, delimiter: IElementType,
|
|
||||||
shouldAlignParenthesis: Boolean, openBracket: IElementType, closeBracket: IElementType): NodeAlignmentStrategy {
|
|
||||||
val parameterAlignment = if (shouldAlignChild) Alignment.createAlignment() else null
|
|
||||||
val bracketsAlignment = if (shouldAlignParenthesis) Alignment.createAlignment() else null
|
|
||||||
|
|
||||||
return object : NodeAlignmentStrategy() {
|
|
||||||
override fun getAlignment(node: ASTNode): Alignment? {
|
|
||||||
val childNodeType = node.elementType
|
|
||||||
|
|
||||||
val prev = getPrevWithoutWhitespace(node)
|
|
||||||
if (prev != null && prev.elementType === TokenType.ERROR_ELEMENT || childNodeType === TokenType.ERROR_ELEMENT) {
|
|
||||||
// Prefer align to parameters on incomplete code (case of line break after comma, when next parameters is absent)
|
|
||||||
return parameterAlignment
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childNodeType === openBracket || childNodeType === closeBracket) {
|
|
||||||
return bracketsAlignment
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childNodeType === parameter || childNodeType === delimiter) {
|
|
||||||
return parameterAlignment
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAlignmentForCaseBranch(shouldAlignInColumns: Boolean): NodeAlignmentStrategy {
|
|
||||||
return if (shouldAlignInColumns) {
|
|
||||||
NodeAlignmentStrategy.fromTypes(
|
|
||||||
AlignmentStrategy.createAlignmentPerTypeStrategy(listOf(ARROW as IElementType), WHEN_ENTRY, true))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
NodeAlignmentStrategy.getNullStrategy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createChildIndent(child: ASTNode): Indent? {
|
|
||||||
val childParent = child.treeParent
|
|
||||||
val childType = child.elementType
|
|
||||||
|
|
||||||
if (childParent != null && childParent.treeParent != null) {
|
|
||||||
if (childParent.elementType === BLOCK && childParent.treeParent.elementType === SCRIPT) {
|
|
||||||
return Indent.getNoneIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do not indent child after heading comments inside declaration
|
|
||||||
if (childParent != null && childParent.psi is KtDeclaration) {
|
|
||||||
val prev = getPrevWithoutWhitespace(child)
|
|
||||||
if (prev != null && prev.elementType in KtTokens.COMMENTS && getPrevWithoutWhitespaceAndComments(prev) == null) {
|
|
||||||
return Indent.getNoneIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (strategy in INDENT_RULES) {
|
|
||||||
val indent = strategy.getIndent(child)
|
|
||||||
if (indent != null) {
|
|
||||||
return indent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Try to rewrite other rules to declarative style
|
|
||||||
if (childParent != null) {
|
|
||||||
val parentType = childParent.elementType
|
|
||||||
|
|
||||||
if (parentType === VALUE_PARAMETER_LIST || parentType === VALUE_ARGUMENT_LIST) {
|
|
||||||
val prev = getPrevWithoutWhitespace(child)
|
|
||||||
if (childType === RPAR && (prev == null || prev.elementType !== TokenType.ERROR_ELEMENT)) {
|
|
||||||
return Indent.getNoneIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
return Indent.getContinuationWithoutFirstIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentType === TYPE_PARAMETER_LIST || parentType === TYPE_ARGUMENT_LIST) {
|
|
||||||
return Indent.getContinuationWithoutFirstIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Indent.getNoneIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createAlignment(alignOption: Boolean, defaultAlignment: Alignment?): Alignment? {
|
private fun createAlignment(alignOption: Boolean, defaultAlignment: Alignment?): Alignment? {
|
||||||
return if (alignOption) createAlignmentOrDefault(null, defaultAlignment) else defaultAlignment
|
return if (alignOption) createAlignmentOrDefault(null, defaultAlignment) else defaultAlignment
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAlignmentOrDefault(base: Alignment?, defaultAlignment: Alignment?): Alignment? {
|
private fun createAlignmentOrDefault(base: Alignment?, defaultAlignment: Alignment?): Alignment? {
|
||||||
return defaultAlignment ?: if (base == null) Alignment.createAlignment() else Alignment.createChildAlignment(base)
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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);
|
PsiFile containingFile = element.getContainingFile().getViewProvider().getPsi(KotlinLanguage.INSTANCE);
|
||||||
KotlinBlock block = new KotlinBlock(
|
KotlinBlock block = new KotlinBlock(
|
||||||
containingFile.getNode(), NodeAlignmentStrategy.getNullStrategy(), Indent.getNoneIndent(), null, settings,
|
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)
|
//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
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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));
|
private static final NodeAlignmentStrategy NULL_STRATEGY = fromTypes(AlignmentStrategy.wrap(null));
|
||||||
|
|
||||||
@@ -35,10 +35,11 @@ public abstract class NodeAlignmentStrategy {
|
|||||||
return new AlignmentStrategyWrapper(strategy);
|
return new AlignmentStrategyWrapper(strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public abstract Alignment getAlignment(@NotNull ASTNode node);
|
public abstract Alignment getAlignment(@NotNull ASTNode node);
|
||||||
|
|
||||||
public static class AlignmentStrategyWrapper extends NodeAlignmentStrategy {
|
private static class AlignmentStrategyWrapper extends NodeAlignmentStrategy {
|
||||||
private final AlignmentStrategy internalStrategy;
|
private final AlignmentStrategy internalStrategy;
|
||||||
|
|
||||||
public AlignmentStrategyWrapper(@NotNull AlignmentStrategy internalStrategy) {
|
public AlignmentStrategyWrapper(@NotNull AlignmentStrategy internalStrategy) {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
<root url="file://$PROJECT_DIR$/../out/production/util" />
|
<root url="file://$PROJECT_DIR$/../out/production/util" />
|
||||||
<root url="file://$PROJECT_DIR$/../out/production/util.runtime" />
|
<root url="file://$PROJECT_DIR$/../out/production/util.runtime" />
|
||||||
<root url="file://$PROJECT_DIR$/../out/production/Kotlin" />
|
<root url="file://$PROJECT_DIR$/../out/production/Kotlin" />
|
||||||
|
<root url="file://$PROJECT_DIR$/../out/production/formatter" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
@@ -94,6 +95,7 @@
|
|||||||
<root url="file://$PROJECT_DIR$/../idea/idea-jps-common/src" />
|
<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-live-templates/src" />
|
||||||
<root url="file://$PROJECT_DIR$/../idea/idea-test-framework/src" />
|
<root url="file://$PROJECT_DIR$/../idea/idea-test-framework/src" />
|
||||||
|
<root url="file://$PROJECT_DIR$/../idea/formatter/src" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
||||||
Reference in New Issue
Block a user