Move files related to formatting to module 'formatter' as is
This commit is contained in:
@@ -8,5 +8,8 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<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" />
|
||||
</component>
|
||||
</module>
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.core.formatter;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
|
||||
public final PackageEntryTable PACKAGES_TO_USE_STAR_IMPORTS = new PackageEntryTable();
|
||||
public boolean SPACE_AROUND_RANGE = false;
|
||||
public boolean SPACE_BEFORE_TYPE_COLON = false;
|
||||
public boolean SPACE_AFTER_TYPE_COLON = true;
|
||||
public boolean SPACE_BEFORE_EXTEND_COLON = true;
|
||||
public boolean SPACE_AFTER_EXTEND_COLON = true;
|
||||
public boolean INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = true;
|
||||
public boolean ALIGN_IN_COLUMNS_CASE_BRANCH = false;
|
||||
public boolean SPACE_AROUND_FUNCTION_TYPE_ARROW = true;
|
||||
public boolean SPACE_AROUND_WHEN_ARROW = true;
|
||||
public boolean SPACE_BEFORE_LAMBDA_ARROW = true;
|
||||
public boolean LBRACE_ON_NEXT_LINE = false;
|
||||
public int NAME_COUNT_TO_USE_STAR_IMPORT = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 5;
|
||||
public int NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 3;
|
||||
public boolean IMPORT_NESTED_CLASSES = false;
|
||||
|
||||
public KotlinCodeStyleSettings(CodeStyleSettings container) {
|
||||
super("JetCodeStyleSettings", container);
|
||||
|
||||
// defaults in IDE but not in tests
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new PackageEntry(false, "java.util", false));
|
||||
PACKAGES_TO_USE_STAR_IMPORTS.addEntry(new PackageEntry(false, "kotlinx.android.synthetic", true));
|
||||
}
|
||||
}
|
||||
|
||||
public static KotlinCodeStyleSettings getInstance(Project project) {
|
||||
return CodeStyleSettingsManager.getSettings(project).getCustomSettings(KotlinCodeStyleSettings.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
KotlinCodeStyleSettings clone = new KotlinCodeStyleSettings(getContainer());
|
||||
clone.copyFrom(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
private void copyFrom(@NotNull KotlinCodeStyleSettings from) {
|
||||
ReflectionUtil.copyFields(getClass().getFields(), from, this);
|
||||
|
||||
PACKAGES_TO_USE_STAR_IMPORTS.copyFrom(from.PACKAGES_TO_USE_STAR_IMPORTS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.formatting.DependentSpacingRule.Anchor
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
private val builders = ArrayList<Builder>()
|
||||
|
||||
private interface Builder {
|
||||
fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing?
|
||||
}
|
||||
|
||||
inner class BasicSpacingBuilder() : SpacingBuilder(codeStyleSettings, KotlinLanguage.INSTANCE), Builder {
|
||||
override fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing? {
|
||||
return super<SpacingBuilder>.getSpacing(parent, left, right)
|
||||
}
|
||||
}
|
||||
|
||||
inner class CustomSpacingBuilder() : Builder {
|
||||
private val rules = ArrayList<(ASTBlock, ASTBlock, ASTBlock) -> Spacing?>()
|
||||
private var conditions = ArrayList<(ASTBlock, ASTBlock, ASTBlock) -> Boolean>()
|
||||
|
||||
override fun getSpacing(parent: ASTBlock, left: ASTBlock, right: ASTBlock): Spacing? {
|
||||
for (rule in rules) {
|
||||
val spacing = rule(parent, left, right)
|
||||
if (spacing != null) {
|
||||
return spacing
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun inPosition(parent: IElementType? = null, left: IElementType? = null, right: IElementType? = null,
|
||||
parentSet: TokenSet? = null, leftSet: TokenSet? = null, rightSet: TokenSet? = null): CustomSpacingBuilder {
|
||||
conditions.add {
|
||||
p, l, r ->
|
||||
(parent == null || p.node!!.elementType == parent) &&
|
||||
(left == null || l.node!!.elementType == left) &&
|
||||
(right == null || r.node!!.elementType == right) &&
|
||||
(parentSet == null || parentSet.contains(p.node!!.elementType)) &&
|
||||
(leftSet == null || leftSet.contains(l.node!!.elementType)) &&
|
||||
(rightSet == null || rightSet.contains(r.node!!.elementType))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun lineBreakIfLineBreakInParent(numSpacesOtherwise: Int, allowBlankLines: Boolean = true) {
|
||||
newRule {
|
||||
p, l, r ->
|
||||
Spacing.createDependentLFSpacing(numSpacesOtherwise, numSpacesOtherwise, p.textRange,
|
||||
codeStyleSettings.KEEP_LINE_BREAKS,
|
||||
if (allowBlankLines) codeStyleSettings.KEEP_BLANK_LINES_IN_CODE else 0)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fun spacing(spacing: Spacing) {
|
||||
newRule { parent, left, right -> spacing }
|
||||
}
|
||||
|
||||
fun customRule(block: (parent: ASTBlock, left: ASTBlock, right: ASTBlock) -> Spacing?) {
|
||||
newRule(block)
|
||||
}
|
||||
|
||||
private fun newRule(rule: (ASTBlock, ASTBlock, ASTBlock) -> Spacing?) {
|
||||
val savedConditions = ArrayList(conditions)
|
||||
rules.add { p, l, r -> if (savedConditions.all { it(p, l, r) }) rule(p, l, r) else null }
|
||||
conditions.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpacing(parent: Block, child1: Block?, child2: Block): Spacing? {
|
||||
if (parent !is ASTBlock || child1 !is ASTBlock || child2 !is ASTBlock) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (builder in builders) {
|
||||
val spacing = builder.getSpacing(parent, child1, child2)
|
||||
|
||||
if (spacing != null) {
|
||||
// TODO: it's a severe hack but I don't know how to implement it in other way
|
||||
if (child1.node.elementType == KtTokens.EOL_COMMENT && spacing.toString().contains("minLineFeeds=0")) {
|
||||
val isBeforeBlock = child2.node.elementType == KtNodeTypes.BLOCK || child2.node.firstChildNode?.elementType == KtNodeTypes.BLOCK
|
||||
val keepBlankLines = if (isBeforeBlock) 0 else codeStyleSettings.KEEP_BLANK_LINES_IN_CODE
|
||||
return Spacing.createSpacing(0, 0, 1, true, keepBlankLines)
|
||||
}
|
||||
return spacing
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun simple(init: BasicSpacingBuilder.() -> Unit) {
|
||||
val builder = BasicSpacingBuilder()
|
||||
builder.init()
|
||||
builders.add(builder)
|
||||
}
|
||||
|
||||
fun custom(init: CustomSpacingBuilder.() -> Unit) {
|
||||
val builder = CustomSpacingBuilder()
|
||||
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)
|
||||
builder.init()
|
||||
return builder
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.Indent;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public abstract class NodeIndentStrategy {
|
||||
public static NodeIndentStrategy constIndent(Indent indent) {
|
||||
return new ConstIndentStrategy(indent);
|
||||
}
|
||||
|
||||
public static PositionStrategy strategy(@Nullable String debugInfo) {
|
||||
return new PositionStrategy(debugInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract Indent getIndent(@NotNull ASTNode node);
|
||||
|
||||
public static class ConstIndentStrategy extends NodeIndentStrategy {
|
||||
private final Indent indent;
|
||||
|
||||
public ConstIndentStrategy(Indent indent) {
|
||||
this.indent = indent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Indent getIndent(@NotNull ASTNode node) {
|
||||
return indent;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PositionStrategy extends NodeIndentStrategy {
|
||||
private Indent defaultIndent = Indent.getNoneIndent();
|
||||
|
||||
private final List<IElementType> in = new ArrayList<IElementType>();
|
||||
private final List<IElementType> notIn = new ArrayList<IElementType>();
|
||||
private final List<IElementType> forElement = new ArrayList<IElementType>();
|
||||
private final List<IElementType> notForElement = new ArrayList<IElementType>();
|
||||
|
||||
private final String debugInfo;
|
||||
|
||||
public PositionStrategy(@Nullable String debugInfo) {
|
||||
this.debugInfo = debugInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PositionStrategy " + (debugInfo != null ? debugInfo : "No debug info");
|
||||
}
|
||||
|
||||
public PositionStrategy set(Indent indent) {
|
||||
defaultIndent = indent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy in(@NotNull TokenSet parents) {
|
||||
IElementType[] types = parents.getTypes();
|
||||
if (types.length == 0) {
|
||||
throw new IllegalArgumentException("Empty token set is unexpected");
|
||||
}
|
||||
|
||||
fillTypes(in, types[0], Arrays.copyOfRange(types, 1, types.length));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy in(@NotNull IElementType parentType, @NotNull IElementType... orParentTypes) {
|
||||
fillTypes(in, parentType, orParentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy notIn(@NotNull IElementType parentType, @NotNull IElementType... orParentTypes) {
|
||||
fillTypes(notIn, parentType, orParentTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy inAny() {
|
||||
in.clear();
|
||||
notIn.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy forType(@NotNull IElementType elementType, @NotNull IElementType... otherTypes) {
|
||||
fillTypes(forElement, elementType, otherTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy notForType(@NotNull IElementType elementType, @NotNull IElementType... otherTypes) {
|
||||
fillTypes(notForElement, elementType, otherTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PositionStrategy forAny() {
|
||||
forElement.clear();
|
||||
notForElement.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Indent getIndent(@NotNull ASTNode node) {
|
||||
if (!forElement.isEmpty()) {
|
||||
if (!forElement.contains(node.getElementType())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (notForElement.contains(node.getElementType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ASTNode parent = node.getTreeParent();
|
||||
if (parent != null) {
|
||||
if (!in.isEmpty()) {
|
||||
if (!in.contains(parent.getElementType())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (notIn.contains(parent.getElementType())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!in.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultIndent;
|
||||
}
|
||||
|
||||
private static void fillTypes(List<IElementType> resultCollection, IElementType singleType, IElementType[] otherTypes) {
|
||||
resultCollection.clear();
|
||||
resultCollection.add(singleType);
|
||||
Collections.addAll(resultCollection, otherTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.Block
|
||||
import com.intellij.formatting.Alignment
|
||||
import com.intellij.formatting.Indent
|
||||
import com.intellij.formatting.Wrap
|
||||
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(
|
||||
private val node: ASTNode,
|
||||
private val subBlocks: MutableList<Block>,
|
||||
private val alignment: Alignment?,
|
||||
private val indent: Indent?,
|
||||
private val wrap: Wrap?,
|
||||
private val spacingBuilder: KotlinSpacingBuilder
|
||||
) : ASTBlock {
|
||||
|
||||
private val textRange = TextRange(
|
||||
subBlocks.first().textRange.startOffset,
|
||||
subBlocks.last().textRange.endOffset)
|
||||
|
||||
override fun getTextRange(): TextRange = textRange
|
||||
override fun getSubBlocks() = subBlocks
|
||||
override fun getWrap() = wrap
|
||||
override fun getIndent() = indent
|
||||
override fun getAlignment() = alignment
|
||||
override fun getChildAttributes(newChildIndex: Int) = ChildAttributes(getIndent(), null)
|
||||
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 toString(): String {
|
||||
var child = subBlocks.first()
|
||||
var treeNode: ASTNode? = null
|
||||
|
||||
loop@
|
||||
while (treeNode == null) when (child) {
|
||||
is AbstractBlock -> {
|
||||
treeNode = child.node
|
||||
}
|
||||
is SyntheticKotlinBlock -> {
|
||||
child = child.getSubBlocks().first()
|
||||
}
|
||||
else -> break@loop
|
||||
}
|
||||
|
||||
val textRange = getTextRange()
|
||||
if (treeNode != null) {
|
||||
val psi = treeNode.psi
|
||||
if (psi != null) {
|
||||
val file = psi.containingFile
|
||||
if (file != null) {
|
||||
return file.text!!.subSequence(textRange.startOffset, textRange.endOffset).toString() + " " + textRange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return javaClass.name + ": " + textRange
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.Wrap
|
||||
import com.intellij.psi.tree.IElementType
|
||||
|
||||
interface WrappingStrategy {
|
||||
fun getWrap(childElementType: IElementType): Wrap?
|
||||
|
||||
object NoWrapping: WrappingStrategy {
|
||||
override fun getWrap(childElementType: IElementType): Wrap? = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* 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.ASTBlock
|
||||
import com.intellij.formatting.Spacing
|
||||
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.codeStyle.CodeStyleSettings
|
||||
import com.intellij.psi.formatter.FormatterUtil
|
||||
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.idea.core.formatter.KotlinCodeStyleSettings
|
||||
import org.jetbrains.kotlin.idea.formatter.KotlinSpacingBuilder.CustomSpacingBuilder
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
|
||||
val MODIFIERS_LIST_ENTRIES = TokenSet.orSet(TokenSet.create(ANNOTATION_ENTRY, ANNOTATION), MODIFIER_KEYWORDS)
|
||||
|
||||
fun SpacingBuilder.beforeInside(element: IElementType, tokenSet: TokenSet, spacingFun: RuleBuilder.() -> Unit) {
|
||||
tokenSet.types.forEach { inType -> beforeInside(element, inType).spacingFun() }
|
||||
}
|
||||
|
||||
fun SpacingBuilder.afterInside(element: IElementType, tokenSet: TokenSet, spacingFun: RuleBuilder.() -> Unit) {
|
||||
tokenSet.types.forEach { inType -> afterInside(element, inType).spacingFun() }
|
||||
}
|
||||
|
||||
fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
|
||||
val kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java)!!
|
||||
val kotlinCommonSettings = settings.getCommonSettings(KotlinLanguage.INSTANCE)!!
|
||||
|
||||
return rules(settings) {
|
||||
val DECLARATIONS =
|
||||
TokenSet.create(PROPERTY, FUN, CLASS, OBJECT_DECLARATION, ENUM_ENTRY, SECONDARY_CONSTRUCTOR, CLASS_INITIALIZER)
|
||||
|
||||
custom {
|
||||
inPosition(left = CLASS, right = CLASS).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = CLASS, right = OBJECT_DECLARATION).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = OBJECT_DECLARATION, right = OBJECT_DECLARATION).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = OBJECT_DECLARATION, right = CLASS).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = FUN, right = FUN).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = PROPERTY, right = FUN).emptyLinesIfLineBreakInLeft(1)
|
||||
inPosition(left = FUN, right = PROPERTY).emptyLinesIfLineBreakInLeft(1)
|
||||
|
||||
// Case left for alternative constructors
|
||||
inPosition(left = FUN, right = CLASS).emptyLinesIfLineBreakInLeft(1)
|
||||
|
||||
inPosition(left = ENUM_ENTRY, right = ENUM_ENTRY).emptyLinesIfLineBreakInLeft(
|
||||
emptyLines = 0, numSpacesOtherwise = 1, numberOfLineFeedsOtherwise = 0)
|
||||
|
||||
inPosition(parent = CLASS_BODY, left = SEMICOLON).customRule { parent, left, right ->
|
||||
val klass = parent.node.treeParent.psi as? KtClass ?: return@customRule null
|
||||
if (klass.isEnum() && right.node.elementType in DECLARATIONS) {
|
||||
Spacing.createSpacing(0, 0, 2, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_DECLARATIONS)
|
||||
} else null
|
||||
}
|
||||
|
||||
val parameterWithDocCommentRule = {
|
||||
parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
|
||||
if (right.node.firstChildNode.elementType == KtTokens.DOC_COMMENT) {
|
||||
Spacing.createSpacing(0, 0, 1, true, settings.KEEP_BLANK_LINES_IN_DECLARATIONS)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
inPosition(parent = VALUE_PARAMETER_LIST, right = VALUE_PARAMETER).customRule(parameterWithDocCommentRule)
|
||||
}
|
||||
|
||||
simple {
|
||||
// ============ Line breaks ==============
|
||||
before(FILE_ANNOTATION_LIST).lineBreakInCode()
|
||||
after(FILE_ANNOTATION_LIST).blankLines(1)
|
||||
|
||||
after(PACKAGE_DIRECTIVE).blankLines(1)
|
||||
between(IMPORT_DIRECTIVE, IMPORT_DIRECTIVE).lineBreakInCode()
|
||||
after(IMPORT_LIST).blankLines(1)
|
||||
|
||||
before(DOC_COMMENT).lineBreakInCode()
|
||||
between(PROPERTY, PROPERTY).lineBreakInCode()
|
||||
|
||||
// CLASS - CLASS, CLASS - OBJECT_DECLARATION are exception
|
||||
between(CLASS, DECLARATIONS).blankLines(1)
|
||||
|
||||
// FUN - FUN, FUN - PROPERTY, FUN - CLASS are exceptions
|
||||
between(FUN, DECLARATIONS).blankLines(1)
|
||||
|
||||
// PROPERTY - PROPERTY, PROPERTY - FUN are exceptions
|
||||
between(PROPERTY, DECLARATIONS).blankLines(1)
|
||||
|
||||
// OBJECT_DECLARATION - OBJECT_DECLARATION, CLASS - OBJECT_DECLARATION are exception
|
||||
between(OBJECT_DECLARATION, DECLARATIONS).blankLines(1)
|
||||
between(SECONDARY_CONSTRUCTOR, DECLARATIONS).blankLines(1)
|
||||
between(CLASS_INITIALIZER, DECLARATIONS).blankLines(1)
|
||||
|
||||
// ENUM_ENTRY - ENUM_ENTRY is exception
|
||||
between(ENUM_ENTRY, DECLARATIONS).blankLines(1)
|
||||
|
||||
between(ENUM_ENTRY, SEMICOLON).spaces(0)
|
||||
|
||||
beforeInside(FUN, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(SECONDARY_CONSTRUCTOR, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(CLASS, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
beforeInside(OBJECT_DECLARATION, TokenSet.create(BODY, CLASS_BODY)).lineBreakInCode()
|
||||
before(PROPERTY).lineBreakInCode()
|
||||
|
||||
after(DOC_COMMENT).lineBreakInCode()
|
||||
|
||||
// =============== Spacing ================
|
||||
betweenInside(LBRACE, RBRACE, CLASS_BODY).spaces(0)
|
||||
|
||||
before(COMMA).spaceIf(kotlinCommonSettings.SPACE_BEFORE_COMMA)
|
||||
after(COMMA).spaceIf(kotlinCommonSettings.SPACE_AFTER_COMMA)
|
||||
|
||||
around(TokenSet.create(EQ, MULTEQ, DIVEQ, PLUSEQ, MINUSEQ, PERCEQ)).spaceIf(kotlinCommonSettings.SPACE_AROUND_ASSIGNMENT_OPERATORS)
|
||||
around(TokenSet.create(ANDAND, OROR)).spaceIf(kotlinCommonSettings.SPACE_AROUND_LOGICAL_OPERATORS)
|
||||
around(TokenSet.create(EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ)).spaceIf(kotlinCommonSettings.SPACE_AROUND_EQUALITY_OPERATORS)
|
||||
aroundInside(TokenSet.create(LT, GT, LTEQ, GTEQ), BINARY_EXPRESSION).spaceIf(kotlinCommonSettings.SPACE_AROUND_RELATIONAL_OPERATORS)
|
||||
aroundInside(TokenSet.create(PLUS, MINUS), BINARY_EXPRESSION).spaceIf(kotlinCommonSettings.SPACE_AROUND_ADDITIVE_OPERATORS)
|
||||
aroundInside(TokenSet.create(MUL, DIV, PERC), BINARY_EXPRESSION).spaceIf(kotlinCommonSettings.SPACE_AROUND_MULTIPLICATIVE_OPERATORS)
|
||||
around(TokenSet.create(PLUSPLUS, MINUSMINUS, EXCLEXCL, MINUS, PLUS, EXCL)).spaceIf(kotlinCommonSettings.SPACE_AROUND_UNARY_OPERATOR)
|
||||
around(ELVIS).spaces(1)
|
||||
around(RANGE).spaceIf(kotlinSettings.SPACE_AROUND_RANGE)
|
||||
|
||||
after(MODIFIER_LIST).spaces(1)
|
||||
|
||||
beforeInside(IDENTIFIER, CLASS).spaces(1)
|
||||
beforeInside(IDENTIFIER, OBJECT_DECLARATION).spaces(1)
|
||||
|
||||
betweenInside(VAL_KEYWORD, IDENTIFIER, PROPERTY).spaces(1)
|
||||
betweenInside(VAR_KEYWORD, IDENTIFIER, PROPERTY).spaces(1)
|
||||
betweenInside(VAL_KEYWORD, TYPE_REFERENCE, PROPERTY).spaces(1)
|
||||
betweenInside(VAR_KEYWORD, TYPE_REFERENCE, PROPERTY).spaces(1)
|
||||
beforeInside(PROPERTY_ACCESSOR, PROPERTY).spacing(1, 0, 0, true, 0)
|
||||
betweenInside(VAL_KEYWORD, IDENTIFIER, VALUE_PARAMETER).spaces(1)
|
||||
betweenInside(VAR_KEYWORD, IDENTIFIER, VALUE_PARAMETER).spaces(1)
|
||||
betweenInside(VAL_KEYWORD, TYPE_REFERENCE, VALUE_PARAMETER).spaces(1)
|
||||
betweenInside(VAR_KEYWORD, TYPE_REFERENCE, VALUE_PARAMETER).spaces(1)
|
||||
betweenInside(TYPE_PARAMETER_LIST, IDENTIFIER, PROPERTY).spaces(1)
|
||||
betweenInside(TYPE_REFERENCE, DOT, PROPERTY).spacing(0, 0, 0, false, 0)
|
||||
betweenInside(DOT, IDENTIFIER, PROPERTY).spacing(0, 0, 0, false, 0)
|
||||
|
||||
betweenInside(RETURN_KEYWORD, LABEL_QUALIFIER, RETURN).spaces(0)
|
||||
afterInside(RETURN_KEYWORD, RETURN).spaces(1)
|
||||
afterInside(LABEL_QUALIFIER, RETURN).spaces(1)
|
||||
|
||||
betweenInside(FUN_KEYWORD, IDENTIFIER, FUN).spaces(1)
|
||||
betweenInside(FUN_KEYWORD, TYPE_REFERENCE, FUN).spaces(1)
|
||||
betweenInside(TYPE_PARAMETER_LIST, TYPE_REFERENCE, FUN).spaces(1)
|
||||
betweenInside(TYPE_PARAMETER_LIST, IDENTIFIER, FUN).spaces(1)
|
||||
betweenInside(FUN_KEYWORD, VALUE_PARAMETER_LIST, FUN).spacing(0, 0, 0, false, 0)
|
||||
betweenInside(TYPE_REFERENCE, DOT, FUN).spacing(0, 0, 0, false, 0)
|
||||
betweenInside(DOT, IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
|
||||
afterInside(IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
|
||||
|
||||
// before LPAR in constructor(): this() {}
|
||||
after(CONSTRUCTOR_DELEGATION_REFERENCE).spacing(0, 0, 0, false, 0)
|
||||
|
||||
// class A() - no space before LPAR of PRIMARY_CONSTRUCTOR
|
||||
// class A private() - one space before modifier
|
||||
custom {
|
||||
inPosition(right = PRIMARY_CONSTRUCTOR).customRule { p, l, r ->
|
||||
val spacesCount = if (r.node.findLeafElementAt(0)?.elementType != LPAR) 1 else 0
|
||||
Spacing.createSpacing(spacesCount, spacesCount, 0, true, 0)
|
||||
}
|
||||
}
|
||||
|
||||
afterInside(CONSTRUCTOR_KEYWORD, PRIMARY_CONSTRUCTOR).spaces(0)
|
||||
|
||||
aroundInside(DOT, DOT_QUALIFIED_EXPRESSION).spaces(0)
|
||||
aroundInside(SAFE_ACCESS, SAFE_ACCESS_EXPRESSION).spaces(0)
|
||||
|
||||
between(MODIFIERS_LIST_ENTRIES, MODIFIERS_LIST_ENTRIES).spaces(1)
|
||||
|
||||
after(LBRACKET).spaces(0)
|
||||
before(RBRACKET).spaces(0)
|
||||
after(LPAR).spaces(0)
|
||||
before(RPAR).spaces(0)
|
||||
|
||||
afterInside(LPAR, VALUE_PARAMETER_LIST).spaces(0)
|
||||
beforeInside(RPAR, VALUE_PARAMETER_LIST).spaces(0)
|
||||
afterInside(LT, TYPE_PARAMETER_LIST).spaces(0)
|
||||
beforeInside(GT, TYPE_PARAMETER_LIST).spaces(0)
|
||||
afterInside(LPAR, VALUE_ARGUMENT_LIST).spaces(0)
|
||||
beforeInside(RPAR, VALUE_ARGUMENT_LIST).spaces(0)
|
||||
afterInside(LT, TYPE_ARGUMENT_LIST).spaces(0)
|
||||
beforeInside(GT, TYPE_ARGUMENT_LIST).spaces(0)
|
||||
|
||||
betweenInside(FOR_KEYWORD, LPAR, FOR).spacing(1, 1, 0, false, 0)
|
||||
betweenInside(IF_KEYWORD, LPAR, IF).spacing(1, 1, 0, false, 0)
|
||||
betweenInside(WHILE_KEYWORD, LPAR, WHILE).spacing(1, 1, 0, false, 0)
|
||||
betweenInside(WHILE_KEYWORD, LPAR, DO_WHILE).spacing(1, 1, 0, false, 0)
|
||||
betweenInside(WHEN_KEYWORD, LPAR, WHEN).spacing(1, 1, 0, false, 0)
|
||||
|
||||
betweenInside(LPAR, VALUE_PARAMETER, FOR).spaces(0)
|
||||
betweenInside(LPAR, DESTRUCTURING_DECLARATION, FOR).spaces(0)
|
||||
betweenInside(LOOP_RANGE, RPAR, FOR).spaces(0)
|
||||
|
||||
val TYPE_COLON_ELEMENTS = TokenSet.create(PROPERTY, FUN, VALUE_PARAMETER, DESTRUCTURING_DECLARATION_ENTRY, FUNCTION_LITERAL)
|
||||
beforeInside(COLON, TYPE_COLON_ELEMENTS) { spaceIf(kotlinSettings.SPACE_BEFORE_TYPE_COLON) }
|
||||
afterInside(COLON, TYPE_COLON_ELEMENTS) { spaceIf(kotlinSettings.SPACE_AFTER_TYPE_COLON) }
|
||||
|
||||
val EXTEND_COLON_ELEMENTS =
|
||||
TokenSet.create(TYPE_CONSTRAINT, CLASS, OBJECT_DECLARATION, TYPE_PARAMETER, ENUM_ENTRY, SECONDARY_CONSTRUCTOR)
|
||||
beforeInside(COLON, EXTEND_COLON_ELEMENTS) { spaceIf(kotlinSettings.SPACE_BEFORE_EXTEND_COLON) }
|
||||
afterInside(COLON, EXTEND_COLON_ELEMENTS) { spaceIf(kotlinSettings.SPACE_AFTER_EXTEND_COLON) }
|
||||
|
||||
beforeInside(ARROW, FUNCTION_LITERAL).spaceIf(kotlinSettings.SPACE_BEFORE_LAMBDA_ARROW)
|
||||
|
||||
aroundInside(ARROW, FUNCTION_TYPE).spaceIf(kotlinSettings.SPACE_AROUND_FUNCTION_TYPE_ARROW)
|
||||
|
||||
between(VALUE_ARGUMENT_LIST, LAMBDA_ARGUMENT).spaces(1)
|
||||
betweenInside(REFERENCE_EXPRESSION, LAMBDA_ARGUMENT, CALL_EXPRESSION).spaces(1)
|
||||
betweenInside(TYPE_ARGUMENT_LIST, LAMBDA_ARGUMENT, CALL_EXPRESSION).spaces(1)
|
||||
|
||||
between(WHEN_ENTRY, WHEN_ENTRY).lineBreakInCode()
|
||||
}
|
||||
custom {
|
||||
|
||||
fun CustomSpacingBuilder.ruleForKeywordOnNewLine(
|
||||
shouldBeOnNewLine: Boolean,
|
||||
keyword: IElementType,
|
||||
parent: IElementType,
|
||||
afterBlockFilter: (wordParent: ASTNode, block: ASTNode) -> Boolean = { keywordParent, block -> true }) {
|
||||
if (shouldBeOnNewLine) {
|
||||
inPosition(parent = parent, right = keyword)
|
||||
.lineBreakIfLineBreakInParent(numSpacesOtherwise = 1, allowBlankLines = false)
|
||||
}
|
||||
else {
|
||||
inPosition(parent = parent, right = keyword).customRule {
|
||||
parent, left, right ->
|
||||
|
||||
val previousLeaf = FormatterUtil.getPreviousNonWhitespaceLeaf(right.node)
|
||||
val leftBlock = if (
|
||||
previousLeaf != null &&
|
||||
previousLeaf.elementType == RBRACE &&
|
||||
previousLeaf.treeParent?.elementType == BLOCK) {
|
||||
previousLeaf.treeParent!!
|
||||
} else null
|
||||
|
||||
val removeLineBreaks = leftBlock != null && afterBlockFilter(right.node?.treeParent!!, leftBlock)
|
||||
Spacing.createSpacing(1, 1, 0, !removeLineBreaks, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ruleForKeywordOnNewLine(kotlinCommonSettings.ELSE_ON_NEW_LINE, keyword = ELSE_KEYWORD, parent = IF) { keywordParent, block ->
|
||||
block.treeParent?.elementType == THEN && block.treeParent?.treeParent == keywordParent
|
||||
}
|
||||
ruleForKeywordOnNewLine(kotlinCommonSettings.WHILE_ON_NEW_LINE, keyword = WHILE_KEYWORD, parent = DO_WHILE) { keywordParent, block ->
|
||||
block.treeParent?.elementType == BODY && block.treeParent?.treeParent == keywordParent
|
||||
}
|
||||
ruleForKeywordOnNewLine(kotlinCommonSettings.CATCH_ON_NEW_LINE, keyword = CATCH, parent = TRY)
|
||||
ruleForKeywordOnNewLine(kotlinCommonSettings.FINALLY_ON_NEW_LINE, keyword = FINALLY, parent = TRY)
|
||||
|
||||
|
||||
fun spacingForLeftBrace(block: ASTNode?, blockType: IElementType = BLOCK): Spacing? {
|
||||
if (block != null && block.elementType == blockType) {
|
||||
val leftBrace = block.findChildByType(LBRACE)
|
||||
if (leftBrace != null) {
|
||||
val previousLeaf = FormatterUtil.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
|
||||
return Spacing.createSpacing(1, 1, minimumLF, keepLineBreaks, 0)
|
||||
}
|
||||
}
|
||||
return Spacing.createSpacing(1, 1, 0, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE)
|
||||
}
|
||||
|
||||
fun leftBraceRule(blockType: IElementType = BLOCK) = {
|
||||
parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
|
||||
spacingForLeftBrace(right.node, blockType)
|
||||
}
|
||||
|
||||
val leftBraceRuleIfBlockIsWrapped = {
|
||||
parent: ASTBlock, left: ASTBlock, right: ASTBlock ->
|
||||
spacingForLeftBrace(right.node!!.firstChildNode)
|
||||
}
|
||||
|
||||
if (kotlinCommonSettings.KEEP_FIRST_COLUMN_COMMENT) {
|
||||
inPosition(parent = null, left = EOL_COMMENT, right = EOL_COMMENT).customRule { parent, left, right ->
|
||||
val nodeBeforeRight = right.node.treePrev
|
||||
if (nodeBeforeRight is PsiWhiteSpace && !nodeBeforeRight.textContains('\n')) {
|
||||
// Several line comments happened to be generated in one line
|
||||
Spacing.createSpacing(1, 0, 1, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
inPosition(rightSet = TokenSet.create(EOL_COMMENT, BLOCK_COMMENT)).spacing(
|
||||
Spacing.createKeepingFirstColumnSpacing(0, Integer.MAX_VALUE, settings.KEEP_LINE_BREAKS, kotlinCommonSettings.KEEP_BLANK_LINES_IN_CODE))
|
||||
}
|
||||
|
||||
inPosition(parent = IF, right = THEN).customRule(leftBraceRuleIfBlockIsWrapped)
|
||||
inPosition(parent = IF, right = ELSE).customRule(leftBraceRuleIfBlockIsWrapped)
|
||||
|
||||
inPosition(parent = FOR, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
|
||||
inPosition(parent = WHILE, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
|
||||
inPosition(parent = DO_WHILE, right = BODY).customRule(leftBraceRuleIfBlockIsWrapped)
|
||||
|
||||
inPosition(parent = TRY, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = CATCH, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = FINALLY, right = BLOCK).customRule(leftBraceRule())
|
||||
|
||||
inPosition(parent = FUN, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = SECONDARY_CONSTRUCTOR, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = CLASS_INITIALIZER, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = PROPERTY_ACCESSOR, right = BLOCK).customRule(leftBraceRule())
|
||||
|
||||
inPosition(right = CLASS_BODY).customRule(leftBraceRule(blockType = CLASS_BODY))
|
||||
|
||||
inPosition(parent = WHEN_ENTRY, right = BLOCK).customRule(leftBraceRule())
|
||||
inPosition(parent = WHEN, right = LBRACE).customRule {
|
||||
parent, left, right ->
|
||||
spacingForLeftBrace(block = parent.node, blockType = WHEN)
|
||||
}
|
||||
|
||||
val spacesInSimpleFunction = if (kotlinSettings.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD) 1 else 0
|
||||
inPosition(parent = FUNCTION_LITERAL,
|
||||
left = LBRACE,
|
||||
right = BLOCK)
|
||||
.lineBreakIfLineBreakInParent(numSpacesOtherwise = spacesInSimpleFunction)
|
||||
|
||||
inPosition(parent = FUNCTION_LITERAL,
|
||||
left = ARROW,
|
||||
right = BLOCK)
|
||||
.lineBreakIfLineBreakInParent(numSpacesOtherwise = 1)
|
||||
|
||||
inPosition(parent = FUNCTION_LITERAL,
|
||||
left = LBRACE,
|
||||
right = RBRACE)
|
||||
.spacing(Spacing.createSpacing(0, 1, 0, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE))
|
||||
|
||||
inPosition(parent = FUNCTION_LITERAL,
|
||||
right = RBRACE)
|
||||
.lineBreakIfLineBreakInParent(numSpacesOtherwise = spacesInSimpleFunction)
|
||||
|
||||
inPosition(parent = FUNCTION_LITERAL,
|
||||
left = LBRACE)
|
||||
.customRule {
|
||||
parent, left, right ->
|
||||
val rightNode = right.node!!
|
||||
val rightType = rightNode.elementType
|
||||
var numSpaces = spacesInSimpleFunction
|
||||
if (rightType == VALUE_PARAMETER_LIST) {
|
||||
val firstParamListNode = rightNode.firstChildNode
|
||||
if (firstParamListNode != null && firstParamListNode.elementType == LPAR) {
|
||||
// Don't put space for situation {<here>(a: Int) -> a }
|
||||
numSpaces = 0
|
||||
}
|
||||
}
|
||||
|
||||
Spacing.createSpacing(numSpaces, numSpaces, 0, settings.KEEP_LINE_BREAKS, settings.KEEP_BLANK_LINES_IN_CODE)
|
||||
}
|
||||
|
||||
inPosition(parent = CLASS_BODY, right = RBRACE).lineBreakIfLineBreakInParent(numSpacesOtherwise = 1)
|
||||
}
|
||||
|
||||
simple {
|
||||
afterInside(LBRACE, BLOCK).lineBreakInCode()
|
||||
beforeInside(RBRACE, BLOCK).lineBreakInCode()
|
||||
beforeInside(RBRACE, WHEN).lineBreakInCode()
|
||||
between(RPAR, BODY).spaces(1)
|
||||
|
||||
// if when entry has block, spacing after arrow should be set by lbrace rule
|
||||
aroundInside(ARROW, WHEN_ENTRY).spaceIf(kotlinSettings.SPACE_AROUND_WHEN_ARROW)
|
||||
|
||||
after(EOL_COMMENT).lineBreakInCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user