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.*;
|
||||||
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.openapi.util.Condition;
|
||||||
import com.intellij.psi.TokenType;
|
import com.intellij.psi.TokenType;
|
||||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||||
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.psi.tree.TokenSet;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.kdoc.lexer.KDocTokens;
|
import org.jetbrains.jet.kdoc.lexer.KDocTokens;
|
||||||
@@ -51,6 +53,7 @@ public class JetBlock extends AbstractBlock {
|
|||||||
|
|
||||||
private List<Block> mySubBlocks;
|
private List<Block> mySubBlocks;
|
||||||
|
|
||||||
|
private static final TokenSet QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS);
|
||||||
private static final TokenSet CODE_BLOCKS = TokenSet.create(
|
private static final TokenSet CODE_BLOCKS = TokenSet.create(
|
||||||
BLOCK,
|
BLOCK,
|
||||||
CLASS_BODY,
|
CLASS_BODY,
|
||||||
@@ -81,9 +84,30 @@ public class JetBlock extends AbstractBlock {
|
|||||||
@Override
|
@Override
|
||||||
protected List<Block> buildChildren() {
|
protected List<Block> buildChildren() {
|
||||||
if (mySubBlocks == null) {
|
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() {
|
private List<Block> buildSubBlocks() {
|
||||||
@@ -102,6 +126,7 @@ public class JetBlock extends AbstractBlock {
|
|||||||
|
|
||||||
blocks.add(buildSubBlock(child, childrenAlignmentStrategy));
|
blocks.add(buildSubBlock(child, childrenAlignmentStrategy));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableList(blocks);
|
return Collections.unmodifiableList(blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +313,7 @@ public class JetBlock extends AbstractBlock {
|
|||||||
strategy("Indent for block content")
|
strategy("Indent for block content")
|
||||||
.in(BLOCK, CLASS_BODY, FUNCTION_LITERAL)
|
.in(BLOCK, CLASS_BODY, FUNCTION_LITERAL)
|
||||||
.notForType(RBRACE, LBRACE, BLOCK)
|
.notForType(RBRACE, LBRACE, BLOCK)
|
||||||
.set(Indent.getNormalIndent()),
|
.set(Indent.getNormalIndent(false)),
|
||||||
|
|
||||||
strategy("Indent for property accessors")
|
strategy("Indent for property accessors")
|
||||||
.in(PROPERTY)
|
.in(PROPERTY)
|
||||||
@@ -316,7 +341,7 @@ public class JetBlock extends AbstractBlock {
|
|||||||
|
|
||||||
strategy("Chained calls")
|
strategy("Chained calls")
|
||||||
.in(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
|
.in(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
|
||||||
.set(Indent.getContinuationWithoutFirstIndent(true)),
|
.set(Indent.getContinuationWithoutFirstIndent(false)),
|
||||||
|
|
||||||
strategy("Delegation list")
|
strategy("Delegation list")
|
||||||
.in(DELEGATION_SPECIFIER_LIST, INITIALIZER_LIST)
|
.in(DELEGATION_SPECIFIER_LIST, INITIALIZER_LIST)
|
||||||
@@ -397,4 +422,16 @@ public class JetBlock extends AbstractBlock {
|
|||||||
}
|
}
|
||||||
return defaultAlignment;
|
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 java.util.ArrayList
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.jet.plugin.JetLanguage
|
import org.jetbrains.jet.plugin.JetLanguage
|
||||||
|
import com.intellij.psi.formatter.common.AbstractBlock
|
||||||
|
import com.intellij.lang.ASTNode
|
||||||
|
|
||||||
class KotlinSpacingBuilder(val codeStyleSettings: CodeStyleSettings) {
|
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 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)
|
betweenInside(DOT, IDENTIFIER, FUN).spacing(0, 0, 0, false, 0)
|
||||||
afterInside(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)
|
between(MODIFIERS_LIST_ENTRIES, MODIFIERS_LIST_ENTRIES).spaces(1)
|
||||||
|
|
||||||
after(LBRACKET).spaces(0)
|
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");
|
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")
|
@TestMetadata("FunctionWithInference.after.kt")
|
||||||
public void testFunctionWithInference() throws Exception {
|
public void testFunctionWithInference() throws Exception {
|
||||||
doTest("idea/testData/formatter/FunctionWithInference.after.kt");
|
doTest("idea/testData/formatter/FunctionWithInference.after.kt");
|
||||||
@@ -294,6 +299,11 @@ public class JetFormatterTestGenerated extends AbstractJetFormatterTest {
|
|||||||
doTest("idea/testData/formatter/SpacesInDeclarations.after.kt");
|
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")
|
@TestMetadata("TryCatchLineBreak.after.kt")
|
||||||
public void testTryCatchLineBreak() throws Exception {
|
public void testTryCatchLineBreak() throws Exception {
|
||||||
doTest("idea/testData/formatter/TryCatchLineBreak.after.kt");
|
doTest("idea/testData/formatter/TryCatchLineBreak.after.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user