Format functions with function literal arguments (KT-2934)
#KT-2934 Fixed
This commit is contained in:
@@ -19,12 +19,14 @@ package org.jetbrains.jet.plugin.formatter;
|
||||
import com.intellij.formatting.*;
|
||||
import com.intellij.formatting.alignment.AlignmentStrategy;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.formatter.common.AbstractBlock;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.kdoc.lexer.KDocTokens;
|
||||
@@ -51,6 +53,7 @@ public class JetBlock extends AbstractBlock {
|
||||
|
||||
private List<Block> mySubBlocks;
|
||||
|
||||
private static final TokenSet QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS);
|
||||
private static final TokenSet CODE_BLOCKS = TokenSet.create(
|
||||
BLOCK,
|
||||
CLASS_BODY,
|
||||
@@ -81,9 +84,30 @@ public class JetBlock extends AbstractBlock {
|
||||
@Override
|
||||
protected List<Block> buildChildren() {
|
||||
if (mySubBlocks == null) {
|
||||
mySubBlocks = buildSubBlocks();
|
||||
List<Block> nodeSubBlocks = buildSubBlocks();
|
||||
|
||||
if (getNode().getElementType() == DOT_QUALIFIED_EXPRESSION || getNode().getElementType() == SAFE_ACCESS_EXPRESSION) {
|
||||
int operationBlockIndex = findNodeBlockIndex(nodeSubBlocks, QUALIFIED_OPERATION);
|
||||
if (operationBlockIndex != -1) {
|
||||
// Create fake ".something" or "?.something" block here, so child indentation will be
|
||||
// relative to it when it starts from new line (see Indent javadoc).
|
||||
|
||||
Block operationBlock = nodeSubBlocks.get(operationBlockIndex);
|
||||
SyntheticKotlinBlock operationSynteticBlock =
|
||||
new SyntheticKotlinBlock(
|
||||
((ASTBlock) operationBlock).getNode(),
|
||||
nodeSubBlocks.subList(operationBlockIndex, nodeSubBlocks.size()),
|
||||
null, operationBlock.getIndent(), null, mySpacingBuilder);
|
||||
|
||||
nodeSubBlocks = ContainerUtil.addAll(
|
||||
ContainerUtil.newArrayList(nodeSubBlocks.subList(0, operationBlockIndex)),
|
||||
operationSynteticBlock);
|
||||
}
|
||||
}
|
||||
|
||||
mySubBlocks = nodeSubBlocks;
|
||||
}
|
||||
return new ArrayList<Block>(mySubBlocks);
|
||||
return mySubBlocks;
|
||||
}
|
||||
|
||||
private List<Block> buildSubBlocks() {
|
||||
@@ -102,6 +126,7 @@ public class JetBlock extends AbstractBlock {
|
||||
|
||||
blocks.add(buildSubBlock(child, childrenAlignmentStrategy));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(blocks);
|
||||
}
|
||||
|
||||
@@ -288,7 +313,7 @@ public class JetBlock extends AbstractBlock {
|
||||
strategy("Indent for block content")
|
||||
.in(BLOCK, CLASS_BODY, FUNCTION_LITERAL)
|
||||
.notForType(RBRACE, LBRACE, BLOCK)
|
||||
.set(Indent.getNormalIndent()),
|
||||
.set(Indent.getNormalIndent(false)),
|
||||
|
||||
strategy("Indent for property accessors")
|
||||
.in(PROPERTY)
|
||||
@@ -316,7 +341,7 @@ public class JetBlock extends AbstractBlock {
|
||||
|
||||
strategy("Chained calls")
|
||||
.in(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
|
||||
.set(Indent.getContinuationWithoutFirstIndent(true)),
|
||||
.set(Indent.getContinuationWithoutFirstIndent(false)),
|
||||
|
||||
strategy("Delegation list")
|
||||
.in(DELEGATION_SPECIFIER_LIST, INITIALIZER_LIST)
|
||||
@@ -397,4 +422,16 @@ public class JetBlock extends AbstractBlock {
|
||||
}
|
||||
return defaultAlignment;
|
||||
}
|
||||
|
||||
private static int findNodeBlockIndex(List<Block> blocks, final TokenSet tokenSet) {
|
||||
return ContainerUtil.indexOf(blocks, new Condition<Block>() {
|
||||
@Override
|
||||
public boolean value(Block block) {
|
||||
if (!(block instanceof ASTBlock)) return false;
|
||||
|
||||
ASTNode node = ((ASTBlock) block).getNode();
|
||||
return node != null && tokenSet.contains(node.getElementType());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,15 @@ import com.intellij.psi.codeStyle.CodeStyleSettings
|
||||
import java.util.ArrayList
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.jet.plugin.JetLanguage
|
||||
|
||||
import com.intellij.psi.formatter.common.AbstractBlock
|
||||
import com.intellij.lang.ASTNode
|
||||
|
||||
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>()
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.jet.plugin.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
|
||||
import java.util.ArrayList
|
||||
|
||||
public 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().getTextRange().getStartOffset(),
|
||||
_subBlocks.last().getTextRange().getEndOffset())
|
||||
|
||||
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.getTreeParent()!!), child1, child2)
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
var child = _subBlocks.first()
|
||||
var treeNode: ASTNode? = null
|
||||
while (treeNode == null) when (child) {
|
||||
is AbstractBlock -> {
|
||||
treeNode = (child as AbstractBlock).getNode()
|
||||
}
|
||||
is SyntheticKotlinBlock -> {
|
||||
child = (child as SyntheticKotlinBlock).getSubBlocks().first()
|
||||
}
|
||||
else -> break
|
||||
}
|
||||
|
||||
val textRange = getTextRange()
|
||||
if (treeNode != null) {
|
||||
val psi = treeNode!!.getPsi()
|
||||
if (psi != null) {
|
||||
val file = psi.getContainingFile()
|
||||
if (file != null) {
|
||||
return file.getText()!!.subSequence(textRange.getStartOffset(), textRange.getEndOffset()).toString() + " " + textRange
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getClass().getName() + ": " + textRange
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,9 @@ fun createSpacingBuilder(settings: CodeStyleSettings): KotlinSpacingBuilder {
|
||||
betweenInside(DOT, IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
|
||||
afterInside(IDENTIFIER, FUN).spacing(0, 0, 0, false, 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)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun test() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map {
|
||||
it * 2
|
||||
}
|
||||
.filter {
|
||||
it > 4
|
||||
}
|
||||
}
|
||||
|
||||
fun test1() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map({
|
||||
it * 2
|
||||
})
|
||||
.filter({
|
||||
it > 4
|
||||
})
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map {
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
val abc = ArrayList<Int>().map {
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
|
||||
fun testWithComments() {
|
||||
val abc = ArrayList<Int>()
|
||||
// .map {
|
||||
// it * 2
|
||||
// }
|
||||
.filter {
|
||||
it > 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import java.util.ArrayList
|
||||
|
||||
fun test() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map {
|
||||
it * 2
|
||||
}
|
||||
.filter {
|
||||
it > 4
|
||||
}
|
||||
}
|
||||
|
||||
fun test1() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map({
|
||||
it * 2
|
||||
})
|
||||
.filter({
|
||||
it > 4
|
||||
})
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
val abc = ArrayList<Int>()
|
||||
.map {
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
val abc = ArrayList<Int>().map {
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
|
||||
fun testWithComments() {
|
||||
val abc = ArrayList<Int>()
|
||||
// .map {
|
||||
// it * 2
|
||||
// }
|
||||
.filter {
|
||||
it > 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
trait Test {
|
||||
fun foo(): Test
|
||||
}
|
||||
|
||||
fun test(t: Test) {
|
||||
t.foo()
|
||||
|
||||
t.
|
||||
foo()
|
||||
|
||||
t.
|
||||
|
||||
|
||||
foo()
|
||||
|
||||
t
|
||||
|
||||
|
||||
.foo()
|
||||
|
||||
|
||||
t.foo().foo()
|
||||
|
||||
|
||||
t?.foo()
|
||||
|
||||
t?.
|
||||
foo()
|
||||
|
||||
t?.
|
||||
|
||||
|
||||
foo()
|
||||
|
||||
t
|
||||
|
||||
|
||||
?.foo()
|
||||
|
||||
t?.foo()?.foo()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
trait Test {
|
||||
fun foo(): Test
|
||||
}
|
||||
|
||||
fun test(t: Test) {
|
||||
t . foo()
|
||||
|
||||
t.
|
||||
foo()
|
||||
|
||||
t.
|
||||
|
||||
|
||||
foo()
|
||||
|
||||
t
|
||||
|
||||
|
||||
|
||||
.foo()
|
||||
|
||||
|
||||
t.foo() . foo()
|
||||
|
||||
|
||||
t ?. foo()
|
||||
|
||||
t?.
|
||||
foo()
|
||||
|
||||
t?.
|
||||
|
||||
|
||||
foo()
|
||||
|
||||
t
|
||||
|
||||
|
||||
|
||||
?.foo()
|
||||
|
||||
t?.foo() ?. foo()
|
||||
}
|
||||
@@ -154,6 +154,11 @@ public class JetFormatterTestGenerated extends AbstractJetFormatterTest {
|
||||
doTest("idea/testData/formatter/FunctionLineBreak.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionLiteralsInChainCalls.after.kt")
|
||||
public void testFunctionLiteralsInChainCalls() throws Exception {
|
||||
doTest("idea/testData/formatter/FunctionLiteralsInChainCalls.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionWithInference.after.kt")
|
||||
public void testFunctionWithInference() throws Exception {
|
||||
doTest("idea/testData/formatter/FunctionWithInference.after.kt");
|
||||
@@ -294,6 +299,11 @@ public class JetFormatterTestGenerated extends AbstractJetFormatterTest {
|
||||
doTest("idea/testData/formatter/SpacesInDeclarations.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SpacesInQualifiedExpressions.after.kt")
|
||||
public void testSpacesInQualifiedExpressions() throws Exception {
|
||||
doTest("idea/testData/formatter/SpacesInQualifiedExpressions.after.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TryCatchLineBreak.after.kt")
|
||||
public void testTryCatchLineBreak() throws Exception {
|
||||
doTest("idea/testData/formatter/TryCatchLineBreak.after.kt");
|
||||
|
||||
Reference in New Issue
Block a user