Format functions with function literal arguments (KT-2934)

#KT-2934 Fixed
This commit is contained in:
Nikolay Krasko
2014-05-02 01:53:20 +04:00
parent eeea34ec9e
commit 109f7992b4
9 changed files with 317 additions and 5 deletions
@@ -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");