From 6b66e3b0e67ae1f9acdaaa09185fde4de5d78b57 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 22 May 2015 16:22:51 +0300 Subject: [PATCH] JetPsiFactory: refactored method for creating argument + introduced ability to create arbitrary constructs by pattern --- .../org/jetbrains/kotlin/psi/JetPsiFactory.kt | 36 ++++++++---- .../jetbrains/kotlin/psi/createByPattern.kt | 56 +++++++++++-------- .../idea/quickfix/AddNameToArgumentFix.java | 2 +- .../usages/JetFunctionCallUsage.java | 2 +- .../kotlin/idea/util/psiModificationUtil.kt | 2 +- .../moveLambda2.kt.after | 2 +- 6 files changed, 61 insertions(+), 39 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index 4cdf6cb7d15..3d8a46fa2f1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -23,13 +23,14 @@ import com.intellij.openapi.util.Key import com.intellij.psi.PsiComment import com.intellij.psi.PsiElement import com.intellij.psi.PsiFileFactory -import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.LocalTimeCounter import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.lexer.JetKeywordToken import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.renderName import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder.Target import org.jetbrains.kotlin.resolve.ImportPath import java.io.PrintWriter @@ -187,12 +188,12 @@ public class JetPsiFactory(private val project: Project) { return createDeclaration(text) } - public fun createDeclaration(text: String): T { + public fun createDeclaration(text: String): TDeclaration { val file = createFile(text) - val dcls = file.getDeclarations() - assert(dcls.size() == 1) { "${dcls.size()} declarations in $text" } - [suppress("UNCHECKED_CAST")] - val result = dcls.first() as T + val declarations = file.getDeclarations() + assert(declarations.size() == 1) { "${declarations.size()} declarations in $text" } + @suppress("UNCHECKED_CAST") + val result = declarations.first() as TDeclaration return result } @@ -361,13 +362,24 @@ public class JetPsiFactory(private val project: Project) { createExpressionByPattern("if ($0) $1", condition, thenExpr)) as JetIfExpression } - public fun createArgumentWithName(name: String?, argumentExpression: JetExpression): JetValueArgument { - val argumentText = (if (name != null) "$name = " else "") + argumentExpression.getText() - return createCallArguments("($argumentText)").getArguments().first() - } + public fun createArgument(expression: JetExpression, name: String? = null, isSpread: Boolean = false): JetValueArgument { + val argumentList = buildByPattern({ pattern, args -> createByPattern(pattern, *args) { createCallArguments(it) } }) { + appendFixedText("(") - public fun createArgument(argumentExpression: JetExpression): JetValueArgument { - return createArgumentWithName(null, argumentExpression) + if (name != null) { + appendFixedText(Name.identifier(name).renderName()) + appendFixedText("=") + } + + if (isSpread) { + appendFixedText("*") + } + + appendExpression(expression) + + appendFixedText(")") + } + return argumentList.getArguments().single() } public fun createDelegatorToSuperCall(text: String): JetDelegatorToSuperCall { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index d78bf8dbffe..97b335bbe9f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -30,13 +30,19 @@ import java.util.ArrayList import java.util.HashMap import java.util.LinkedHashMap -public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: Any): JetExpression { +public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: Any): JetExpression + = createByPattern(pattern, *args) { createExpression(it) } + +public fun JetPsiFactory.createDeclarationByPattern(pattern: String, vararg args: Any): TDeclaration + = createByPattern(pattern, *args) { createDeclaration(it) } + +public fun createByPattern(pattern: String, vararg args: Any, factory: (String) -> TElement): TElement { val (processedText, allPlaceholders) = processPattern(pattern, args) - var expression = createExpression(processedText.trim()) - val project = expression.getProject() + var resultElement = factory(processedText.trim()) + val project = resultElement.getProject() - val start = expression.startOffset + val start = resultElement.startOffset val pointerManager = SmartPointerManager.getInstance(project) @@ -52,7 +58,7 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: } for ((range, text) in placeholders) { - val token = expression.findElementAt(range.getStartOffset())!! + val token = resultElement.findElementAt(range.getStartOffset())!! for (element in token.parents()) { val elementRange = element.getTextRange().shiftRight(-start) if (elementRange == range && expectedElementType.isInstance(element)) { @@ -79,20 +85,20 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: // reformat whole text except for String arguments (as they can contain user's formatting to be preserved) if (stringPlaceholderRanges.none()) { - expression = codeStyleManager.reformat(expression, true) as JetExpression + resultElement = codeStyleManager.reformat(resultElement, true) as TElement } else { - var bound = expression.endOffset - 1 + var bound = resultElement.endOffset - 1 for (range in stringPlaceholderRanges) { // we extend reformatting range by 1 to the right because otherwise some of spaces are not reformatted - expression = codeStyleManager.reformatRange(expression, range.getEndOffset() + start, bound + 1, true) as JetExpression + resultElement = codeStyleManager.reformatRange(resultElement, range.getEndOffset() + start, bound + 1, true) as TElement bound = range.getStartOffset() + start } - expression = codeStyleManager.reformatRange(expression, start, bound + 1, true) as JetExpression + resultElement = codeStyleManager.reformatRange(resultElement, start, bound + 1, true) as TElement } // do not reformat the whole expression in PostprocessReformattingAspect - CodeEditUtil.setNodeGeneratedRecursively(expression.getNode(), false) + CodeEditUtil.setNodeGeneratedRecursively(resultElement.getNode(), false) for ((pointer, n) in pointers) { var element = pointer.getElement()!! @@ -102,9 +108,9 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: element.replace(args[n] as PsiElement) } - codeStyleManager.adjustLineIndent(expression.getContainingFile(), expression.getTextRange()) + codeStyleManager.adjustLineIndent(resultElement.getContainingFile(), resultElement.getTextRange()) - return expression + return resultElement } private data class Placeholder(val range: TextRange, val text: String) @@ -182,22 +188,22 @@ private fun processPattern(pattern: String, args: Array): PatternData { return PatternData(text, ranges) } -public class ExpressionBuilder { +public class BuilderByPattern { private val patternBuilder = StringBuilder() private val arguments = ArrayList() - public fun appendFixedText(text: String): ExpressionBuilder { + public fun appendFixedText(text: String): BuilderByPattern { patternBuilder.append(text) return this } - public fun appendNonFormattedText(text: String): ExpressionBuilder { + public fun appendNonFormattedText(text: String): BuilderByPattern { patternBuilder.append("$" + arguments.size()) arguments.add(text) return this } - public fun appendExpression(expression: JetExpression?): ExpressionBuilder { + public fun appendExpression(expression: JetExpression?): BuilderByPattern { if (expression != null) { patternBuilder.append("$" + arguments.size()) arguments.add(expression) @@ -205,7 +211,7 @@ public class ExpressionBuilder { return this } - public fun appendTypeReference(typeRef: JetTypeReference?): ExpressionBuilder { + public fun appendTypeReference(typeRef: JetTypeReference?): BuilderByPattern { if (typeRef != null) { patternBuilder.append("$" + arguments.size()) arguments.add(typeRef) @@ -213,13 +219,17 @@ public class ExpressionBuilder { return this } - public fun createExpression(factory: JetPsiFactory): JetExpression { - return factory.createExpressionByPattern(patternBuilder.toString(), *arguments.toArray()) + public fun create(factory: (String, Array) -> TElement): TElement { + return factory(patternBuilder.toString(), arguments.toArray()) } } -public fun JetPsiFactory.buildExpression(build: ExpressionBuilder.() -> Unit): JetExpression { - val builder = ExpressionBuilder() - builder.build() - return builder.createExpression(this) +public fun JetPsiFactory.buildExpression(build: BuilderByPattern.() -> Unit): JetExpression { + return buildByPattern({ pattern, args -> this.createExpressionByPattern(pattern, *args) }, build) +} + +public fun buildByPattern(factory: (String, Array) -> TElement, build: BuilderByPattern.() -> Unit): TElement { + val builder = BuilderByPattern() + builder.build() + return builder.create(factory) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java index a4aa744c1fa..46e7d3474fe 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddNameToArgumentFix.java @@ -147,7 +147,7 @@ public class AddNameToArgumentFix extends JetIntentionAction { private static JetValueArgument getParsedArgumentWithName(@NotNull String name, @NotNull JetValueArgument argument) { JetExpression argumentExpression = argument.getArgumentExpression(); assert argumentExpression != null : "Argument should be already parsed."; - return JetPsiFactory(argument).createArgumentWithName(name, argumentExpression); + return JetPsiFactory(argument).createArgument(argumentExpression, name, false); } @NotNull diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java index c441b9d62a7..a1c8206b9dc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java @@ -369,7 +369,7 @@ public class JetFunctionCallUsage extends JetUsageInfo { changeArgumentName(argumentNameExpression, parameterInfo); //noinspection ConstantConditions newArgument.replace(oldArgument instanceof JetFunctionLiteralArgument - ? psiFactory.createArgument(oldArgument.getArgumentExpression()) + ? psiFactory.createArgument(oldArgument.getArgumentExpression(), null, false) : oldArgument.asElement()); } // TODO: process default arguments in the middle diff --git a/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt b/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt index 19c3e7f115b..d06b3ec7c92 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt @@ -48,7 +48,7 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith( val psiFactory = JetPsiFactory(getProject()) val argument = if (newCallExpression.getValueArgumentsInParentheses().any { it.getArgumentName() != null }) { - psiFactory.createArgumentWithName(functionLiteralArgumentName, replacement) + psiFactory.createArgument(replacement, functionLiteralArgumentName) } else { psiFactory.createArgument(replacement) diff --git a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt.after b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt.after index 20c53729cf1..c39d9d9f0ec 100644 --- a/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt.after +++ b/idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt.after @@ -1,6 +1,6 @@ // IS_APPLICABLE: true fun foo() { - bar(2, { it * 3 }) + bar(2, {it * 3}) } fun bar(a: Int, b: (Int) -> Int) {