Some formatting preserving for special methods too

This commit is contained in:
Valentin Kipyatkov
2016-10-01 02:36:15 +03:00
parent f4d9909f6f
commit 5a6f068130
10 changed files with 144 additions and 67 deletions
@@ -64,12 +64,10 @@ class CodeConverter(
= expressions.map { convertExpression(it) }
fun convertArgumentList(list: PsiExpressionList): ArgumentList {
val lPar = list.node.findChildByType(JavaTokenType.LPARENTH)?.psi
val rPar = list.node.findChildByType(JavaTokenType.RPARENTH)?.psi
return ArgumentList(
convertExpressions(list.expressions),
LPar().assignPrototype(lPar, CommentsAndSpacesInheritance.LINE_BREAKS),
RPar().assignPrototype(rPar, CommentsAndSpacesInheritance.LINE_BREAKS)
LPar.withPrototype(list.lPar()),
RPar.withPrototype(list.rPar())
).assignPrototype(list)
}
@@ -324,6 +324,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val methodExpr = expression.methodExpression
val arguments = expression.argumentList.expressions
val qualifier = methodExpr.qualifierExpression
val dot = methodExpr.dot()
val target = methodExpr.resolve()
val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(codeConverter.settings) else false
val typeArguments = convertTypeArguments(expression)
@@ -347,7 +348,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
propertyName
}
else if (qualifier != null) {
QualifiedExpression(codeConverter.convertExpression(qualifier), propertyName, methodExpr.dot()).assignNoPrototype()
QualifiedExpression(codeConverter.convertExpression(qualifier), propertyName, dot).assignNoPrototype()
}
else {
propertyName
@@ -375,7 +376,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
origin.name!!,
convertArguments(expression, isExtension = true),
typeArguments,
isNullable)
isNullable,
dot)
}
else {
MethodCallExpression.build(null,
@@ -396,7 +398,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
methodExpr.referenceName!!,
convertArguments(expression, isExtension = true),
typeArguments,
isNullable)
isNullable,
dot)
}
else {
MethodCallExpression.build(null,
@@ -413,7 +416,9 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (target is PsiMethod) {
val specialMethod = SpecialMethod.match(target, arguments.size, converter.services)
if (specialMethod != null) {
val data = SpecialMethod.ConvertCallData(qualifier, arguments, typeArguments, codeConverter)
val lPar = expression.argumentList.lPar()
val rPar = expression.argumentList.rPar()
val data = SpecialMethod.ConvertCallData(qualifier, arguments, typeArguments, dot, lPar, rPar, codeConverter)
val converted = specialMethod.convertCall(data)
if (converted != null) {
result = converted
@@ -705,14 +710,9 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
arguments.map { codeConverter.convertExpression(it).assignPrototype(it, commentsAndSpacesInheritance) }
}
val lPar = argumentList?.node?.findChildByType(JavaTokenType.LPARENTH)?.psi
val rPar = argumentList?.node?.findChildByType(JavaTokenType.RPARENTH)?.psi
return ArgumentList(
convertedArguments,
LPar().assignPrototype(lPar, CommentsAndSpacesInheritance.LINE_BREAKS),
RPar().assignPrototype(rPar, CommentsAndSpacesInheritance.LINE_BREAKS)
).assignPrototype(argumentList)
val lPar = argumentList?.lPar()
val rPar = argumentList?.rPar()
return ArgumentList(convertedArguments, LPar.withPrototype(lPar), RPar.withPrototype(rPar)).assignPrototype(argumentList)
}
override fun visitLambdaExpression(expression: PsiLambdaExpression) {
@@ -826,7 +826,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
})
val callData = SpecialMethod.ConvertCallData(fakeReceiver, fakeParams.toTypedArray(), emptyList(), patchedConverter)
val callData = SpecialMethod.ConvertCallData(fakeReceiver, fakeParams.toTypedArray(), emptyList(), null, null, null, patchedConverter)
statement = specialMethod.convertCall(callData)!!
}
else {
@@ -193,7 +193,7 @@ fun Converter.convertParameterList(
null
convertParameter(parameter, defaultValueConverted)
},
LPar().assignPrototype(lParen, CommentsAndSpacesInheritance.LINE_BREAKS),
RPar().assignPrototype(rParen, CommentsAndSpacesInheritance.LINE_BREAKS)
LPar.withPrototype(lParen),
RPar.withPrototype(rParen)
).assignPrototype(parameterList)
}
@@ -211,7 +211,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
override fun ConvertCallData.convertCall(): Expression? {
val comparison = BinaryExpression(Identifier.withNoPrototype("it", isNullable = false), LiteralExpression("' '").assignNoPrototype(), Operator(JavaTokenType.LE).assignNoPrototype()).assignNoPrototype()
val argumentList = ArgumentList.withNoPrototype(LambdaExpression(null, Block.of(comparison).assignNoPrototype()))
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "trim", argumentList)
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "trim", argumentList, dotPrototype = dot)
}
},
@@ -221,7 +221,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
codeConverter.convertToRegex(arguments[0]),
codeConverter.convertExpression(arguments[1])
)
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "replace", argumentList)
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "replace", argumentList, dotPrototype = dot)
}
},
@@ -232,7 +232,8 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
ArgumentList.withNoPrototype(
codeConverter.convertToRegex(arguments[0]),
codeConverter.convertExpression(arguments[1])
)
),
dotPrototype = dot
)
}
},
@@ -240,19 +241,25 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
STRING_MATCHES(JAVA_LANG_STRING, "matches", 1) {
override fun ConvertCallData.convertCall(): MethodCallExpression {
val argumentList = ArgumentList.withNoPrototype(codeConverter.convertToRegex(arguments.single()))
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "matches", argumentList)
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "matches", argumentList, dotPrototype = dot)
}
},
STRING_SPLIT(JAVA_LANG_STRING, "split", 1) {
override fun ConvertCallData.convertCall(): Expression? {
val splitCall = MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "split",
ArgumentList.withNoPrototype(codeConverter.convertToRegex(arguments.single()))
val splitCall = MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
"split",
ArgumentList.withNoPrototype(codeConverter.convertToRegex(arguments.single())),
dotPrototype = dot
).assignNoPrototype()
val isEmptyCall = MethodCallExpression.buildNonNull(Identifier.withNoPrototype("it", isNullable = false), "isEmpty").assignNoPrototype()
val isEmptyCallBlock = Block.of(isEmptyCall).assignNoPrototype()
val dropLastCall = MethodCallExpression.buildNonNull(splitCall, "dropLastWhile",
ArgumentList.withNoPrototype(LambdaExpression(null, isEmptyCallBlock).assignNoPrototype())).assignNoPrototype()
val dropLastCall = MethodCallExpression.buildNonNull(
splitCall,
"dropLastWhile",
ArgumentList.withNoPrototype(LambdaExpression(null, isEmptyCallBlock).assignNoPrototype())
).assignNoPrototype()
return MethodCallExpression.buildNonNull(dropLastCall, "toTypedArray")
}
},
@@ -276,7 +283,12 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
listOf(patternArgument, limitArgument)
}
val splitCall = MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "split", ArgumentList.withNoPrototype(splitArguments)).assignNoPrototype()
val splitCall = MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
"split",
ArgumentList.withNoPrototype(splitArguments),
dotPrototype = dot
).assignNoPrototype()
return MethodCallExpression.buildNonNull(splitCall, "toTypedArray")
}
},
@@ -341,7 +353,12 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
else
it
}
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "toByteArray", ArgumentList.withNoPrototype(convertedArguments))
return MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
"toByteArray",
ArgumentList.withNoPrototype(convertedArguments),
dotPrototype = dot
)
}
},
@@ -349,7 +366,12 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
override fun ConvertCallData.convertCall(): MethodCallExpression {
// reorder parameters: srcBegin(0), srcEnd(1), dst(2), dstOffset(3) -> destination(2), destinationOffset(3), startIndex(0), endIndex(1)
val argumentList = ArgumentList.withNoPrototype(codeConverter.convertExpressions(arguments.slice(listOf(2, 3, 0, 1))))
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), "toCharArray", argumentList)
return MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
"toCharArray",
argumentList,
dotPrototype = dot
)
}
},
@@ -406,6 +428,9 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
val qualifier: PsiExpression?,
@Suppress("ArrayInDataClass") val arguments: Array<PsiExpression>,
val typeArgumentsConverted: List<Type>,
val dot: PsiElement?,
val lPar: PsiElement?,
val rPar: PsiElement?,
val codeConverter: CodeConverter
)
@@ -417,7 +442,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
protected fun ConvertCallData.convertMethodCallToPropertyUse(propertyName: String = methodName): Expression {
val identifier = Identifier.withNoPrototype(propertyName, isNullable = false)
return if (qualifier != null)
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, null)
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier, dot)
else
identifier
}
@@ -426,19 +451,27 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
protected fun ConvertCallData.convertWithChangedName(name: String, argumentNullabilities: List<Nullability>): MethodCallExpression {
assert(argumentNullabilities.size == arguments.size)
val argumentList = ArgumentList.withNoPrototype(
arguments.zip(argumentNullabilities).map { codeConverter.convertExpression(it.first, null, it.second) }
)
return MethodCallExpression.buildNonNull(codeConverter.convertExpression(qualifier), name, argumentList, typeArgumentsConverted)
val argumentsConverted = arguments.zip(argumentNullabilities).map {
codeConverter.convertExpression(it.first, null, it.second).assignPrototype(it.first, CommentsAndSpacesInheritance.LINE_BREAKS)
}
val argumentList = ArgumentList(argumentsConverted, LPar.withPrototype(lPar), RPar.withPrototype(rPar)).assignNoPrototype()
return MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
name,
argumentList,
typeArgumentsConverted,
dot)
}
protected fun ConvertCallData.convertWithReceiverCast(): MethodCallExpression? {
val convertedArguments = arguments.map { codeConverter.convertExpression(it) }
val qualifierWithCast = castQualifierToType(codeConverter, qualifier!!, qualifiedClassName!!)
if (qualifierWithCast != null) {
return MethodCallExpression.buildNonNull(qualifierWithCast, methodName, ArgumentList.withNoPrototype(convertedArguments), typeArgumentsConverted)
}
return null
val qualifierWithCast = castQualifierToType(codeConverter, qualifier!!, qualifiedClassName!!) ?: return null
return MethodCallExpression.buildNonNull(
qualifierWithCast,
methodName,
ArgumentList.withNoPrototype(convertedArguments),
typeArgumentsConverted,
dot)
}
private fun castQualifierToType(codeConverter: CodeConverter, qualifier: PsiExpression, type: String): TypeCastExpression? {
@@ -454,12 +487,16 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
val ignoreCaseExpression = ignoreCaseArgument?.let { codeConverter.convertExpression(it) }
?: LiteralExpression("true").assignNoPrototype()
val ignoreCaseArgumentExpression = AssignmentExpression(Identifier.withNoPrototype("ignoreCase"), ignoreCaseExpression, Operator.EQ).assignNoPrototype()
val argumentList = ArgumentList.withNoPrototype(arguments.map { codeConverter.convertExpression(it, null, Nullability.NotNull) } + ignoreCaseArgumentExpression)
val convertedArguments = arguments.map {
codeConverter.convertExpression(it, null, Nullability.NotNull).assignPrototype(it, CommentsAndSpacesInheritance.LINE_BREAKS)
} + ignoreCaseArgumentExpression
val argumentList = ArgumentList(convertedArguments, LPar.withPrototype(lPar), RPar.withPrototype(rPar)).assignNoPrototype()
return MethodCallExpression.buildNonNull(
codeConverter.convertExpression(qualifier),
methodName,
argumentList,
typeArgumentsConverted)
typeArgumentsConverted,
dot)
}
protected fun ConvertCallData.convertSystemOutMethodCall(methodName: String): Expression? {
@@ -468,7 +505,11 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
if (qqualifier.canonicalText != "java.lang.System") return null
if (qualifier.referenceName != "out") return null
if (typeArgumentsConverted.isNotEmpty()) return null
val argumentList = ArgumentList.withNoPrototype(arguments.map { codeConverter.convertExpression(it) })
val argumentList = ArgumentList(
arguments.map { codeConverter.convertExpression(it).assignPrototype(it, CommentsAndSpacesInheritance.LINE_BREAKS) },
LPar.withPrototype(lPar),
RPar.withPrototype(rPar)
).assignNoPrototype()
return MethodCallExpression.buildNonNull(null, methodName, argumentList)
}
+3 -2
View File
@@ -21,7 +21,6 @@ import com.intellij.psi.util.PsiMethodUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.types.expressions.OperatorConventions
fun quoteKeywords(packageName: String): String = packageName.split('.').map { Identifier.toKotlin(it) }.joinToString(".")
@@ -111,7 +110,9 @@ fun PsiModifierListOwner.accessModifier(): String = when {
fun PsiMethod.isMainMethod(): Boolean = PsiMethodUtil.isMainMethod(this)
fun PsiReferenceExpression.dot(): PsiElement? = allChildren.firstOrNull { it.node.elementType == JavaTokenType.DOT }
fun PsiReferenceExpression.dot(): PsiElement? = node.findChildByType(JavaTokenType.DOT)?.psi
fun PsiExpressionList.lPar(): PsiElement? = node.findChildByType(JavaTokenType.LPARENTH)?.psi
fun PsiExpressionList.rPar(): PsiElement? = node.findChildByType(JavaTokenType.RPARENTH)?.psi
fun PsiMember.isImported(file: PsiJavaFile): Boolean {
if (this is PsiClass) {
@@ -32,7 +32,7 @@ class ArgumentList(
companion object {
fun withNoPrototype(arguments: List<Expression>): ArgumentList {
return ArgumentList(arguments, LPar().assignNoPrototype(), RPar().assignNoPrototype()).assignNoPrototype()
return ArgumentList(arguments, LPar.withPrototype(null), RPar.withPrototype(null)).assignNoPrototype()
}
fun withNoPrototype(vararg arguments: Expression): ArgumentList = withNoPrototype(arguments.asList())
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.append
@@ -36,28 +37,32 @@ class MethodCallExpression(
receiver: Expression?,
methodName: String,
argumentList: ArgumentList = ArgumentList.withNoPrototype(),
typeArguments: List<Type> = emptyList()
): MethodCallExpression = build(receiver, methodName, argumentList, typeArguments, false)
typeArguments: List<Type> = emptyList(),
dotPrototype: PsiElement? = null
): MethodCallExpression = build(receiver, methodName, argumentList, typeArguments, false, dotPrototype)
fun buildNullable(
receiver: Expression?,
methodName: String,
argumentList: ArgumentList = ArgumentList.withNoPrototype(),
typeArguments: List<Type> = emptyList()
): MethodCallExpression = build(receiver, methodName, argumentList, typeArguments, true)
typeArguments: List<Type> = emptyList(),
dotPrototype: PsiElement? = null
): MethodCallExpression = build(receiver, methodName, argumentList, typeArguments, true, dotPrototype)
fun build(
receiver: Expression?,
methodName: String,
argumentList: ArgumentList,
typeArguments: List<Type>,
isNullable: Boolean
isNullable: Boolean,
dotPrototype: PsiElement? = null
): MethodCallExpression {
val identifier = Identifier.withNoPrototype(methodName, isNullable = false)
return MethodCallExpression(if (receiver != null) QualifiedExpression(receiver, identifier, null).assignNoPrototype() else identifier,
argumentList,
typeArguments,
isNullable)
val methodExpression = if (receiver != null)
QualifiedExpression(receiver, identifier, dotPrototype).assignNoPrototype()
else
identifier
return MethodCallExpression(methodExpression, argumentList, typeArguments, isNullable)
}
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.j2k.ast
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.j2k.CodeBuilder
import org.jetbrains.kotlin.j2k.append
@@ -34,20 +35,28 @@ class ParameterList(
companion object {
fun withNoPrototype(parameters: List<Parameter>): ParameterList {
return ParameterList(parameters, LPar().assignNoPrototype(), RPar().assignNoPrototype()).assignNoPrototype()
return ParameterList(parameters, LPar.withPrototype(null), RPar.withPrototype(null)).assignNoPrototype()
}
}
}
// we use LPar and RPar elements to better handle comments and line breaks around them
class LPar : Element() {
class LPar private constructor() : Element() {
override fun generateCode(builder: CodeBuilder) {
builder.append("(")
}
companion object {
fun withPrototype(element: PsiElement?) = LPar().assignPrototype(element, CommentsAndSpacesInheritance.LINE_BREAKS)
}
}
class RPar : Element() {
class RPar private constructor() : Element() {
override fun generateCode(builder: CodeBuilder) {
builder.append(")")
}
companion object {
fun withPrototype(element: PsiElement?) = RPar().assignPrototype(element, CommentsAndSpacesInheritance.LINE_BREAKS)
}
}
@@ -64,13 +64,21 @@ class A {
void specialMethods() throws Exception {
String s = "test string";
s.equals("test");
s.equalsIgnoreCase("tesT");
s.equalsIgnoreCase(
"tesT"
);
s.compareToIgnoreCase("Test");
s.regionMatches(true, 0, "TE", 0, 2);
s.regionMatches(
true,
0,
"TE",
0,
2
);
s.regionMatches(0, "st", 1, 2);
s.matches("\\w+");
s.replaceAll("\\w+", "---");
s.replaceFirst("([s-t])", "A$1");
s.replaceAll("\\w+", "---")
.replaceFirst("([s-t])", "A$1");
useSplit(s.split("\\s+"));
useSplit(s.split("\\s+", 0));
useSplit(s.split("\\s+", -1));
@@ -97,7 +105,11 @@ class A {
String.valueOf(3.14);
String.valueOf(new Object());
String.format(Locale.FRENCH, "Je ne mange pas %d jours", 6);
String.format(
Locale.FRENCH,
"Je ne mange pas %d jours",
6
);
String.format("Operation completed with %s", "success");
char[] chars = {'a', 'b', 'c'};
@@ -72,13 +72,20 @@ internal class A {
fun specialMethods() {
val s = "test string"
s == "test"
s.equals("tesT", ignoreCase = true)
s.equals(
"tesT", ignoreCase = true
)
s.compareTo("Test", ignoreCase = true)
s.regionMatches(0, "TE", 0, 2, ignoreCase = true)
s.regionMatches(
0,
"TE",
0,
2, ignoreCase = true
)
s.regionMatches(0, "st", 1, 2)
s.matches("\\w+".toRegex())
s.replace("\\w+".toRegex(), "---")
s.replaceFirst("([s-t])".toRegex(), "A$1")
.replaceFirst("([s-t])".toRegex(), "A$1")
useSplit(s.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
useSplit(s.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
useSplit(s.split("\\s+".toRegex()).toTypedArray())
@@ -105,7 +112,11 @@ internal class A {
3.14.toString()
Any().toString()
String.format(Locale.FRENCH, "Je ne mange pas %d jours", 6)
String.format(
Locale.FRENCH,
"Je ne mange pas %d jours",
6
)
String.format("Operation completed with %s", "success")
val chars = charArrayOf('a', 'b', 'c')