From 41935bdd34bd2f6d20707444a9d88ed460ce6eeb Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Fri, 27 Sep 2019 22:45:39 +0300 Subject: [PATCH] New J2K: separate logic in code builder to different classes --- .../kotlin/nj2k/NewJavaToKotlinConverter.kt | 3 +- .../JKCodeBuilder.kt} | 309 ++++-------------- .../kotlin/nj2k/printing/JKCommentPrinter.kt | 59 ++++ .../kotlin/nj2k/printing/JKPrinter.kt | 141 ++++++++ .../nj2k/{ => printing}/JKSymbolRenderer.kt | 7 +- .../kotlin/nj2k/printing/printingUtils.kt | 13 + .../kotlin/nj2k/tree/declarations.kt | 9 +- 7 files changed, 286 insertions(+), 255 deletions(-) rename nj2k/src/org/jetbrains/kotlin/nj2k/{NewCodeBuilder.kt => printing/JKCodeBuilder.kt} (80%) create mode 100644 nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt create mode 100644 nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKPrinter.kt rename nj2k/src/org/jetbrains/kotlin/nj2k/{ => printing}/JKSymbolRenderer.kt (94%) create mode 100644 nj2k/src/org/jetbrains/kotlin/nj2k/printing/printingUtils.kt diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt index c903df390f9..7cc4f59c439 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverter.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.jetbrains.kotlin.idea.core.util.EDT import org.jetbrains.kotlin.j2k.* +import org.jetbrains.kotlin.nj2k.printing.JKCodeBuilder import org.jetbrains.kotlin.nj2k.types.JKTypeFactory import org.jetbrains.kotlin.psi.KtPsiFactory @@ -135,7 +136,7 @@ class NewJavaToKotlinConverter( processor.updateState(i, 1, phaseDescription) val (element, ast) = elementWithAst if (ast == null) return@mapIndexed null - val code = NewCodeBuilder(context).run { printCodeOut(ast) } + val code = JKCodeBuilder(context).run { printCodeOut(ast) } val parseContext = when (element) { is PsiStatement, is PsiExpression -> ParseContext.CODE_BLOCK else -> ParseContext.TOP_LEVEL diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt similarity index 80% rename from nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt rename to nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt index 93f6af5db83..7026fe3ed8d 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt @@ -3,76 +3,36 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.nj2k +package org.jetbrains.kotlin.nj2k.printing -import com.intellij.openapi.project.Project import org.jetbrains.kotlin.j2k.ast.Nullability -import org.jetbrains.kotlin.nj2k.symbols.JKSymbol +import org.jetbrains.kotlin.nj2k.* import org.jetbrains.kotlin.nj2k.symbols.getDisplayFqName import org.jetbrains.kotlin.nj2k.tree.* import org.jetbrains.kotlin.nj2k.tree.visitors.JKVisitorWithCommentsPrinting -import org.jetbrains.kotlin.nj2k.types.* +import org.jetbrains.kotlin.nj2k.types.JKContextType +import org.jetbrains.kotlin.nj2k.types.isInterface +import org.jetbrains.kotlin.nj2k.types.updateNullability import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class NewCodeBuilder(context: NewJ2kConverterContext) { +internal class JKCodeBuilder(context: NewJ2kConverterContext) { private val elementInfoStorage = context.elementsInfoStorage private val printer = JKPrinter(context.project, context.importStorage, elementInfoStorage) + private val commentPrinter = JKCommentPrinter(printer) - private fun classKindString(kind: JKClass.ClassKind): String = when (kind) { - JKClass.ClassKind.ANNOTATION -> "annotation class" - JKClass.ClassKind.CLASS -> "class" - JKClass.ClassKind.ENUM -> "enum class" - JKClass.ClassKind.INTERFACE -> "interface" - JKClass.ClassKind.OBJECT -> "object" - JKClass.ClassKind.COMPANION -> "companion object" + fun printCodeOut(root: JKTreeElement): String { + Visitor().also { root.accept(it) } + return printer.toString().replace("\r\n", "\n") } - inner class Visitor : JKVisitorWithCommentsPrinting() { - private val printedTokens = mutableSetOf() - - //TODO move to ast transformation phase - private fun JKComment.shouldBeDropped(): Boolean = - text.startsWith("//noinspection") - - private fun JKComment.createText() = - if (this !in printedTokens) { - printedTokens += this - text - } else null - - - private fun List.createText(): String = buildString { - var needNewLine = false - for (comment in this@createText) { - if (comment.shouldBeDropped()) continue - val text = comment.createText() ?: continue - if (needNewLine) appendln() else append(' ') - append(text) - needNewLine = text.startsWith("//") || '\n' in text - } - } - - private fun String.hasNoLineBreakAfterSingleLineComment() = lastIndexOf('\n') < lastIndexOf("//") - + private inner class Visitor : JKVisitorWithCommentsPrinting() { override fun printLeftNonCodeElements(element: JKFormattingOwner) { - val text = element.trailingComments.createText() - printer.print(text) - - val addNewLine = element.hasTrailingLineBreak - || element is JKDeclaration && element.trailingComments.isNotEmpty() // add new line between comment & declaration - || text.hasNoLineBreakAfterSingleLineComment() - - if (addNewLine) printer.println() + commentPrinter.printTrailingComments(element) } - override fun printRightNonCodeElements(element: JKFormattingOwner) { - val text = element.leadingComments.createText() - printer.print(text) - - val addNewLine = element.hasLeadingLineBreak || text.hasNoLineBreakAfterSingleLineComment() - if (addNewLine) printer.println() + commentPrinter.printLeadingComments(element) } private fun renderTokenElement(tokenElement: JKTokenElement) { @@ -81,24 +41,18 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { printRightNonCodeElements(tokenElement) } - override fun visitModifierElementRaw(modifierElement: JKModifierElement) { - if (modifierElement.modifier != Modality.FINAL) { - printer.print(modifierElement.modifier.text) - } - } - private fun renderExtraTypeParametersUpperBounds(typeParameterList: JKTypeParameterList) { val extraTypeBounds = typeParameterList.typeParameters .filter { it.upperBounds.size > 1 } if (extraTypeBounds.isNotEmpty()) { printer.print(" where ") - val typeParametersWithBoudnds = + val typeParametersWithBounds = extraTypeBounds.flatMap { typeParameter -> typeParameter.upperBounds.map { bound -> typeParameter.name to bound } } - printer.renderList(typeParametersWithBoudnds) { (name, bound) -> + printer.renderList(typeParametersWithBounds) { (name, bound) -> name.accept(this) printer.print(" : ") bound.accept(this) @@ -106,10 +60,35 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } + private fun renderModifiersList(modifiersList: JKModifiersListOwner) { + val hasOverrideModifier = modifiersList + .safeAs() + ?.hasOtherModifier(OtherModifier.OVERRIDE) == true + modifiersList.forEachModifier { modifierElement -> + if (modifierElement.modifier == Modality.FINAL || modifierElement.modifier == Visibility.PUBLIC) { + if (hasOverrideModifier) { + modifierElement.accept(this) + } else { + printLeftNonCodeElements(modifierElement) + printRightNonCodeElements(modifierElement) + } + } else { + modifierElement.accept(this) + } + printer.print(" ") + } + } + override fun visitTreeElementRaw(treeElement: JKTreeElement) { printer.print("/* !!! Hit visitElement for element type: ${treeElement::class} !!! */") } + override fun visitModifierElementRaw(modifierElement: JKModifierElement) { + if (modifierElement.modifier != Modality.FINAL) { + printer.print(modifierElement.modifier.text) + } + } + override fun visitTreeRootRaw(treeRoot: JKTreeRoot) { treeRoot.element.accept(this) } @@ -195,26 +174,6 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { breakStatement.label.accept(this) } - - private fun renderModifiersList(modifiersList: JKModifiersListOwner) { - val hasOverrideModifier = modifiersList.safeAs() - ?.otherModifierElements - ?.any { it.otherModifier == OtherModifier.OVERRIDE } == true - modifiersList.forEachModifier { modifierElement -> - if (modifierElement.modifier == Modality.FINAL || modifierElement.modifier == Visibility.PUBLIC) { - if (hasOverrideModifier) { - modifierElement.accept(this) - } else { - printLeftNonCodeElements(modifierElement) - printRightNonCodeElements(modifierElement) - } - } else { - modifierElement.accept(this) - } - printer.print(" ") - } - } - override fun visitClassRaw(klass: JKClass) { klass.annotationList.accept(this) if (klass.annotationList.annotations.isNotEmpty()) { @@ -222,7 +181,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } renderModifiersList(klass) printer.print(" ") - printer.print(classKindString(klass.classKind)) + printer.print(klass.classKind.text) printer.print(" ") klass.name.accept(this) klass.typeParameterList.accept(this) @@ -283,7 +242,6 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } - override fun visitFieldRaw(field: JKField) { field.annotationList.accept(this) if (field.annotationList.annotations.isNotEmpty()) { @@ -322,7 +280,6 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } - override fun visitIsExpressionRaw(isExpression: JKIsExpression) { isExpression.expression.accept(this) printer.print(" is ") @@ -353,7 +310,9 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } - override fun visitKtAnnotationArrayInitializerExpressionRaw(ktAnnotationArrayInitializerExpression: JKKtAnnotationArrayInitializerExpression) { + override fun visitKtAnnotationArrayInitializerExpressionRaw( + ktAnnotationArrayInitializerExpression: JKKtAnnotationArrayInitializerExpression + ) { printer.print("[") printer.renderList(ktAnnotationArrayInitializerExpression.initializers) { it.accept(this) @@ -431,7 +390,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { override fun visitTypeParameterListRaw(typeParameterList: JKTypeParameterList) { if (typeParameterList.typeParameters.isNotEmpty()) { - printer.par(ParenthesisKind.ANGLE) { + printer.par(JKPrinterBase.ParenthesisKind.ANGLE) { printer.renderList(typeParameterList.typeParameters) { it.accept(this) } @@ -472,9 +431,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { printer.print(" ") } - override fun visitLabelEmptyRaw(labelEmpty: JKLabelEmpty) { - - } + override fun visitLabelEmptyRaw(labelEmpty: JKLabelEmpty) {} override fun visitLabelTextRaw(labelText: JKLabelText) { printer.print("@") @@ -530,7 +487,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { override fun visitTypeArgumentListRaw(typeArgumentList: JKTypeArgumentList) { if (typeArgumentList.typeArguments.isNotEmpty()) { - printer.par(ParenthesisKind.ANGLE) { + printer.par(JKPrinterBase.ParenthesisKind.ANGLE) { printer.renderList(typeArgumentList.typeArguments) { it.accept(this) } @@ -584,11 +541,9 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } - override fun visitEmptyStatementRaw(emptyStatement: JKEmptyStatement) { - } + override fun visitEmptyStatementRaw(emptyStatement: JKEmptyStatement) {} - override fun visitStubExpressionRaw(stubExpression: JKStubExpression) { - } + override fun visitStubExpressionRaw(stubExpression: JKStubExpression) {} override fun visitKtConvertedFromForLoopSyntheticWhileStatementRaw( ktConvertedFromForLoopSyntheticWhileStatement: JKKtConvertedFromForLoopSyntheticWhileStatement @@ -605,7 +560,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { printer.renderSymbol(newExpression.classSymbol, newExpression) newExpression.typeArgumentList.accept(this) if (!newExpression.classSymbol.isInterface() || newExpression.arguments.arguments.isNotEmpty()) { - printer.par(ParenthesisKind.ROUND) { + printer.par(JKPrinterBase.ParenthesisKind.ROUND) { newExpression.arguments.accept(this) } } @@ -706,7 +661,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } private fun renderParameterList(parameters: List) { - printer.par(ParenthesisKind.ROUND) { + printer.par(JKPrinterBase.ParenthesisKind.ROUND) { printer.renderList(parameters) { it.accept(this) } @@ -740,14 +695,14 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } } - override fun visitLambdaExpressionRaw(lambdaExpression: JKLambdaExpression) { val printLambda = { - printer.par(ParenthesisKind.CURVED) { + printer.par(JKPrinterBase.ParenthesisKind.CURVED) { if (lambdaExpression.statement.statements.size > 1) printer.println() - lambdaExpression.parameters.firstOrNull()?.accept(this) - lambdaExpression.parameters.asSequence().drop(1).forEach { printer.print(", "); it.accept(this) } + printer.renderList(lambdaExpression.parameters) { + it.accept(this) + } if (lambdaExpression.parameters.isNotEmpty()) { printer.print(" -> ") } @@ -758,14 +713,15 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { } else { statement.accept(this) } - if (lambdaExpression.statement.statements.size > 1) + if (lambdaExpression.statement.statements.size > 1) { printer.println() + } } } if (lambdaExpression.functionalType.present()) { printer.renderType(lambdaExpression.functionalType.type, lambdaExpression) printer.print(" ") - printer.par(ParenthesisKind.ROUND, printLambda) + printer.par(JKPrinterBase.ParenthesisKind.ROUND, printLambda) } else { printLambda() } @@ -846,14 +802,13 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { JKClassLiteralExpression.ClassLiteralType.KOTLIN_CLASS -> printer.print("class") JKClassLiteralExpression.ClassLiteralType.JAVA_CLASS -> printer.print("class.java") JKClassLiteralExpression.ClassLiteralType.JAVA_PRIMITIVE_CLASS -> printer.print("class.javaPrimitiveType") - JKClassLiteralExpression.ClassLiteralType.JAVA_VOID_TYPE -> { - } + JKClassLiteralExpression.ClassLiteralType.JAVA_VOID_TYPE -> Unit } } } override fun visitKtWhenCaseRaw(ktWhenCase: JKKtWhenCase) { - printer.renderList(ktWhenCase.labels, ", ") { + printer.renderList(ktWhenCase.labels) { it.accept(this) } printer.print(" -> ") @@ -868,148 +823,4 @@ class NewCodeBuilder(context: NewJ2kConverterContext) { ktValueWhenLabel.expression.accept(this) } } - - - fun printCodeOut(root: JKTreeElement): String { - Visitor().also { root.accept(it) } - return printer.toString().replace("\r\n", "\n") - } -} - -enum class ParenthesisKind(val open: String, val close: String) { - ROUND("(", ")"), - CURVED("{", "}"), - ANGLE("<", ">") -} - - -private class JKPrinter( - project: Project, - importStorage: JKImportStorage, - private val elementInfoStorage: JKElementInfoStorage -) { - val symbolRenderer = JKSymbolRenderer(importStorage, project) - private val stringBuilder: StringBuilder = StringBuilder() - private var currentIndent = 0; - private val indentSymbol = " ".repeat(4) - - private var lastSymbolIsLineBreak = false - - override fun toString(): String = stringBuilder.toString() - - fun print(value: String) { - if (value.isNotEmpty()) { - lastSymbolIsLineBreak = false - } - stringBuilder.append(value) - } - - fun println() { - if (lastSymbolIsLineBreak) return - stringBuilder.append('\n') - repeat(currentIndent) { - stringBuilder.append(indentSymbol) - } - lastSymbolIsLineBreak = true - } - - - inline fun indented(block: () -> Unit) { - currentIndent++ - block() - currentIndent-- - } - - inline fun block(crossinline body: () -> Unit) { - par(ParenthesisKind.CURVED) { - indented(body) - } - } - - inline fun par(kind: ParenthesisKind = ParenthesisKind.ROUND, body: () -> Unit) { - print(kind.open) - body() - print(kind.close) - } - - - private fun JKType.renderTypeInfo() { - this@JKPrinter.print(elementInfoStorage.getOrCreateInfoForElement(this).render()) - } - - fun renderType(type: JKType, owner: JKTreeElement?) { - if (type is JKNoType) return - if (type is JKCapturedType) { - when (val wildcard = type.wildcardType) { - is JKVarianceTypeParameterType -> { - renderType(wildcard.boundType, owner) - } - is JKStarProjectionType -> { - type.renderTypeInfo() - this.print("Any?") - } - } - return - } - type.renderTypeInfo() - when (type) { - is JKClassType -> { - renderSymbol(type.classReference, owner) - } - is JKContextType -> return - is JKStarProjectionType -> - this.print("*") - is JKTypeParameterType -> - this.print(type.identifier.name) - is JKVarianceTypeParameterType -> { - when (type.variance) { - JKVarianceTypeParameterType.Variance.IN -> this.print("in ") - JKVarianceTypeParameterType.Variance.OUT -> this.print("out ") - } - renderType(type.boundType, null) - } - else -> this.print("Unit /* TODO: ${type::class} */") - } - if (type is JKParametrizedType && type.parameters.isNotEmpty()) { - par(ParenthesisKind.ANGLE) { - renderList(type.parameters, renderElement = { renderType(it, null) }) - } - } - // we print undefined types as nullable because we need smartcast to work in nullability inference in post-processing - if (type !is JKWildCardType - && (type.nullability == Nullability.Default - && owner?.safeAs()?.functionalType?.type != type - || type.nullability == Nullability.Nullable) - ) { - this.print("?") - } - } - - fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) { - print(symbolRenderer.renderSymbol(symbol, owner)) - } - - inline fun renderList(list: List, separator: String = ", ", renderElement: (T) -> Unit) = - renderList(list, { this.print(separator) }, renderElement) - - inline fun renderList(list: List, separator: () -> Unit, renderElement: (T) -> Unit) { - val (head, tail) = list.headTail() - head?.let(renderElement) ?: return - tail?.forEach { - separator() - renderElement(it) - } - } -} - - -fun String.escapedAsQualifiedName(): String = - split('.') - .map { it.escaped() } - .joinToString(".") { it } - -private fun List.headTail(): Pair?> { - val head = this.firstOrNull() - val tail = if (size <= 1) null else subList(1, size) - return head to tail } diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt new file mode 100644 index 00000000000..e513415e305 --- /dev/null +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCommentPrinter.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.nj2k.printing + +import org.jetbrains.kotlin.nj2k.tree.JKComment +import org.jetbrains.kotlin.nj2k.tree.JKDeclaration +import org.jetbrains.kotlin.nj2k.tree.JKFormattingOwner + +internal class JKCommentPrinter(val printer: JKPrinter) { + private val printedTokens = mutableSetOf() + + //TODO move to ast transformation phase + private fun JKComment.shouldBeDropped(): Boolean = + text.startsWith("//noinspection") + + private fun JKComment.createText() = + if (this !in printedTokens) { + printedTokens += this + text + } else null + + + private fun List.createText(): String = buildString { + var needNewLine = false + for (comment in this@createText) { + if (comment.shouldBeDropped()) continue + val text = comment.createText() ?: continue + if (needNewLine) appendln() else append(' ') + append(text) + needNewLine = text.startsWith("//") || '\n' in text + } + } + + private fun String.hasNoLineBreakAfterSingleLineComment() = lastIndexOf('\n') < lastIndexOf("//") + + + fun printLeadingComments(element: JKFormattingOwner) { + val text = element.leadingComments.createText() + printer.print(text) + + val addNewLine = element.hasLeadingLineBreak || text.hasNoLineBreakAfterSingleLineComment() + if (addNewLine) printer.println() + } + + + fun printTrailingComments(element: JKFormattingOwner) { + val text = element.trailingComments.createText() + printer.print(text) + + val addNewLine = element.hasTrailingLineBreak + || element is JKDeclaration && element.trailingComments.isNotEmpty() // add new line between comment & declaration + || text.hasNoLineBreakAfterSingleLineComment() + + if (addNewLine) printer.println() + } +} \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKPrinter.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKPrinter.kt new file mode 100644 index 00000000000..0bd2802955c --- /dev/null +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKPrinter.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.nj2k.printing + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.j2k.ast.Nullability +import org.jetbrains.kotlin.nj2k.JKElementInfoStorage +import org.jetbrains.kotlin.nj2k.JKImportStorage +import org.jetbrains.kotlin.nj2k.symbols.JKSymbol +import org.jetbrains.kotlin.nj2k.tree.* +import org.jetbrains.kotlin.nj2k.types.* +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +internal open class JKPrinterBase { + private val stringBuilder: StringBuilder = StringBuilder() + var currentIndent = 0; + private val indentSymbol = " ".repeat(4) + private var lastSymbolIsLineBreak = false + + override fun toString(): String = stringBuilder.toString() + + fun print(value: String) { + if (value.isNotEmpty()) { + lastSymbolIsLineBreak = false + } + stringBuilder.append(value) + } + + fun println() { + if (lastSymbolIsLineBreak) return + stringBuilder.append('\n') + repeat(currentIndent) { + stringBuilder.append(indentSymbol) + } + lastSymbolIsLineBreak = true + } + + + inline fun indented(block: () -> Unit) { + currentIndent++ + block() + currentIndent-- + } + + inline fun block(body: () -> Unit) { + par(ParenthesisKind.CURVED) { + indented(body) + } + } + + inline fun par(kind: ParenthesisKind = ParenthesisKind.ROUND, body: () -> Unit) { + print(kind.open) + body() + print(kind.close) + } + + inline fun renderList(list: List, separator: String = ", ", renderElement: (T) -> Unit) = + renderList(list, { this.print(separator) }, renderElement) + + inline fun renderList(list: List, separator: () -> Unit, renderElement: (T) -> Unit) { + if (list.isEmpty()) return + renderElement(list.first()) + for (element in list.subList(1, list.size)) { + separator() + renderElement(element) + } + } + + enum class ParenthesisKind(val open: String, val close: String) { + ROUND("(", ")"), + CURVED("{", "}"), + ANGLE("<", ">") + } +} + +internal class JKPrinter( + project: Project, + importStorage: JKImportStorage, + private val elementInfoStorage: JKElementInfoStorage +) : JKPrinterBase() { + val symbolRenderer = JKSymbolRenderer(importStorage, project) + + private fun JKType.renderTypeInfo() { + this@JKPrinter.print(elementInfoStorage.getOrCreateInfoForElement(this).render()) + } + + fun renderType(type: JKType, owner: JKTreeElement?) { + if (type is JKNoType) return + if (type is JKCapturedType) { + when (val wildcard = type.wildcardType) { + is JKVarianceTypeParameterType -> { + renderType(wildcard.boundType, owner) + } + is JKStarProjectionType -> { + type.renderTypeInfo() + this.print("Any?") + } + } + return + } + type.renderTypeInfo() + when (type) { + is JKClassType -> { + renderSymbol(type.classReference, owner) + } + is JKContextType -> return + is JKStarProjectionType -> + this.print("*") + is JKTypeParameterType -> + this.print(type.identifier.name) + is JKVarianceTypeParameterType -> { + when (type.variance) { + JKVarianceTypeParameterType.Variance.IN -> this.print("in ") + JKVarianceTypeParameterType.Variance.OUT -> this.print("out ") + } + renderType(type.boundType, null) + } + else -> this.print("Unit /* TODO: ${type::class} */") + } + if (type is JKParametrizedType && type.parameters.isNotEmpty()) { + par(ParenthesisKind.ANGLE) { + renderList(type.parameters, renderElement = { renderType(it, null) }) + } + } + // we print undefined types as nullable because we need smartcast to work in nullability inference in post-processing + if (type !is JKWildCardType + && (type.nullability == Nullability.Default + && owner?.safeAs()?.functionalType?.type != type + || type.nullability == Nullability.Nullable) + ) { + this.print("?") + } + } + + fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) { + print(symbolRenderer.renderSymbol(symbol, owner)) + } +} \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/JKSymbolRenderer.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKSymbolRenderer.kt similarity index 94% rename from nj2k/src/org/jetbrains/kotlin/nj2k/JKSymbolRenderer.kt rename to nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKSymbolRenderer.kt index e2fa5e899ed..a098de4dccf 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/JKSymbolRenderer.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKSymbolRenderer.kt @@ -3,21 +3,22 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.nj2k +package org.jetbrains.kotlin.nj2k.printing import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.PsiShortNamesCache import com.intellij.util.Processor -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.nj2k.JKImportStorage +import org.jetbrains.kotlin.nj2k.escaped import org.jetbrains.kotlin.nj2k.symbols.* import org.jetbrains.kotlin.nj2k.tree.JKClassAccessExpression import org.jetbrains.kotlin.nj2k.tree.JKQualifiedExpression import org.jetbrains.kotlin.nj2k.tree.JKTreeElement import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class JKSymbolRenderer(private val importStorage: JKImportStorage, project: Project) { +internal class JKSymbolRenderer(private val importStorage: JKImportStorage, project: Project) { private val canBeShortenedClassNameCache = CanBeShortenedCache(project) private fun JKSymbol.isFqNameExpected(owner: JKTreeElement?): Boolean { diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/printingUtils.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/printingUtils.kt new file mode 100644 index 00000000000..7a6cd83df49 --- /dev/null +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/printingUtils.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.nj2k.printing + +import org.jetbrains.kotlin.nj2k.escaped + +internal fun String.escapedAsQualifiedName(): String = + split('.') + .map { it.escaped() } + .joinToString(".") { it } \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/declarations.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/declarations.kt index 7a9547709d2..43ab61f593d 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/declarations.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/declarations.kt @@ -35,8 +35,13 @@ class JKClass( override var visibilityElement by child(visibilityElement) override var modalityElement by child(modalityElement) - enum class ClassKind { - ANNOTATION, CLASS, ENUM, INTERFACE, OBJECT, COMPANION + enum class ClassKind(val text: String) { + ANNOTATION("annotation class"), + CLASS("class"), + ENUM("enum class"), + INTERFACE("interface"), + OBJECT("object"), + COMPANION("companion object") } }