New J2K: separate logic in code builder to different classes
This commit is contained in:
@@ -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
|
||||
|
||||
+60
-249
@@ -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<JKComment>()
|
||||
|
||||
//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<JKComment>.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<JKOtherModifiersOwner>()
|
||||
?.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<JKOtherModifiersOwner>()
|
||||
?.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<JKParameter>) {
|
||||
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<JKLambdaExpression>()?.functionalType?.type != type
|
||||
|| type.nullability == Nullability.Nullable)
|
||||
) {
|
||||
this.print("?")
|
||||
}
|
||||
}
|
||||
|
||||
fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) {
|
||||
print(symbolRenderer.renderSymbol(symbol, owner))
|
||||
}
|
||||
|
||||
inline fun <T> renderList(list: List<T>, separator: String = ", ", renderElement: (T) -> Unit) =
|
||||
renderList(list, { this.print(separator) }, renderElement)
|
||||
|
||||
inline fun <T> renderList(list: List<T>, 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 <T> List<T>.headTail(): Pair<T?, List<T>?> {
|
||||
val head = this.firstOrNull()
|
||||
val tail = if (size <= 1) null else subList(1, size)
|
||||
return head to tail
|
||||
}
|
||||
@@ -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<JKComment>()
|
||||
|
||||
//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<JKComment>.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()
|
||||
}
|
||||
}
|
||||
@@ -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 <T> renderList(list: List<T>, separator: String = ", ", renderElement: (T) -> Unit) =
|
||||
renderList(list, { this.print(separator) }, renderElement)
|
||||
|
||||
inline fun <T> renderList(list: List<T>, 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<JKLambdaExpression>()?.functionalType?.type != type
|
||||
|| type.nullability == Nullability.Nullable)
|
||||
) {
|
||||
this.print("?")
|
||||
}
|
||||
}
|
||||
|
||||
fun renderSymbol(symbol: JKSymbol, owner: JKTreeElement?) {
|
||||
print(symbolRenderer.renderSymbol(symbol, owner))
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -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 {
|
||||
@@ -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 }
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user