in gradle-dsl-kotlin/src/com/android/tools/idea/gradle/dsl/kotlin/KotlinDslWriter.kt [118:367]
override fun createDslElement(element: GradleDslElement): PsiElement? {
if (element is GradleDslInfixExpression) return createDslInfixExpression(element)
val psiElement = element.psiElement
if (psiElement != null) return psiElement
var anchorAfter = element.anchor
var isRealList = false // This is to keep track if we're creating a real list (listOf()).
var isNamedPropertyMap = false
var isVarOrProperty = false
if (element.isNewEmptyBlockElement) return null // Avoid creation of an empty block.
if (anchorAfter == null) return null // we don't have a parent? return null.
val dslParent = anchorAfter.parentDslElement ?: return null
var addBefore = false
if (needToCreateParent(dslParent)) {
addBefore = true
anchorAfter = GradleDslAnchor.Start(dslParent)
}
var parentPsiElement = getParentPsi(dslParent) ?: return null
val project = parentPsiElement.project
val psiFactory = KtPsiFactory(project)
val externalNameInfo = maybeTrimForParent(element, this)
val joinedName = externalNameInfo.externalNameParts.joinToString(".")
val quotedName = maybeQuoteBits(externalNameInfo.externalNameParts)
var statementText : String
val syntax = externalNameInfo.syntax.takeUnless { it == UNKNOWN } ?: element.externalSyntax
element.externalSyntax = syntax
// TODO(xof): this is a bit horrible, and if there are any other examples where we need to adjust the syntax (as opposed to name)
// of something depending on its context, try to figure out a useful generalization.
if (dslParent is DependenciesDslElement && (syntax == METHOD) && !KTS_KNOWN_CONFIGURATIONS.contains(joinedName)) {
statementText = "\"${joinedName}\""
}
else if (element is GradleDslNamedDomainElement) {
statementText = when {
// use an existing methodName if we have one
element.methodName != null -> "${element.methodName}(\"$joinedName\")"
// use getByName() if the element is implicitly provided, otherwise create()
dslParent is GradleDslNamedDomainContainer -> when {
dslParent.implicitlyExists(joinedName) -> "getByName(\"$joinedName\")"
else -> "create(\"$joinedName\")"
}
// should never happen (named domain element added to something that isn't a named domain container)
else -> {
val log = logger<KotlinDslWriter>()
log.warn("NamedDomainElement $element added to non-NamedDomainContainer $dslParent", Throwable())
"getByName(\"$joinedName\")"
}
}
}
else {
statementText = joinedName
}
if (element.isBlockElement) {
if (element is MavenRepositoryDslElement && element.getContainedElements(true).isEmpty()) {
statementText += "()"
}
else {
statementText += " {\n}" // Can't create expression with another new line after.
}
}
else if (syntax == ASSIGNMENT || syntax == AUGMENTED_ASSIGNMENT || syntax == SET_METHOD) {
if (element.elementType == PropertyType.REGULAR) {
if (element.parent is ExtDslElement) {
// This is about a regular extra property and should have a dedicated syntax.
// TODO(b/148769031): For now, we need to be careful about psi to dsl translation in both ways and reflect the dsl logic back to
// the psi elements.
if (element.fullName.startsWith("ext.")) statementText = "extra[\"${joinedName}\"] = \"abc\""
else {
statementText = "val $quotedName by extra(\"abc\")"
isVarOrProperty = true
}
}
else {
when (syntax) {
ASSIGNMENT -> statementText += " = \"abc\""
AUGMENTED_ASSIGNMENT -> statementText += " += \"abc\""
SET_METHOD -> statementText += ".set(\"abc\")" // Gradle pre-8.2 doesn't(?) provide Kotlin property setters
METHOD -> {}
UNKNOWN -> {}
}
}
}
else if (element.elementType == PropertyType.VARIABLE) {
statementText = "val ${quotedName} = \"abc\""
isVarOrProperty = true
}
}
else if (element is GradleDslExpressionList) {
if (dslParent is GradleDslMethodCall && element.elementType == PropertyType.DERIVED) {
// This is when we have not a proper list element (listOf()) but rather a methodCall arguments. In such case we need to skip
// creating the list and use the KtValueArgumentList of the parent.
return (dslParent.psiElement as? KtCallExpression)?.valueArgumentList // TODO add more tests to verify the code consistency.
}
else if (element.name.isEmpty()){
// This is the case where we are handling a list element
statementText += "listOf()"
isRealList = true
}
else {
statementText += "()"
}
}
else if (element is GradleDslExpressionMap) {
if (element.asNamedArgs) {
statementText += "()"
}
else if (element.name.isEmpty()) {
statementText += "mapOf()"
}
else if (element.elementType == PropertyType.DERIVED && element.isLiteralMap) {
// This is the case of maps within other maps
statementText = "\"${StringUtil.unquoteString(element.name)}\" to \"abc\""
}
else {
statementText += "(mapOf())"
isNamedPropertyMap = true
}
}
else {
statementText += "()"
}
val statement = if (isVarOrProperty) psiFactory.createProperty(statementText) else psiFactory.createExpression(statementText)
when (statement) {
is KtBinaryExpression -> {
statement.right?.delete()
}
is KtCallExpression -> {
if (element.isBlockElement) {
// Add new line to separate blocks statements.
statement.addAfter(psiFactory.createNewLine(), statement.lastChild)
}
}
is KtProperty -> {
// If we created a local variable, we need to delete the right value to allow adding the right one.
if (element.elementType == PropertyType.VARIABLE) {
statement.initializer?.delete()
}
else {
// This is the case os an extra property, and we will need to delete the value from the extra() callExpression.
val delegateExpression = statement.delegateExpression as? KtCallExpression ?: return null
delegateExpression.valueArgumentList?.removeArgument(0)
}
}
is KtDotQualifiedExpression -> {
val call = statement.getChildOfType<KtCallExpression>() ?: return null
call.valueArgumentList?.removeArgument(0)
}
}
val lineTerminator = psiFactory.createNewLine()
val addedElement : PsiElement
var anchor = getPsiElementForAnchor(parentPsiElement, anchorAfter)
when (parentPsiElement) {
is KtFile -> {
// If the anchor is null, we would add the new element to the beginning of the file which is correct, unless the file starts
// with a comment : in such case we need to add the element right after the comment and not before.
val fileBlock = parentPsiElement.script?.blockExpression
if (fileBlock != null) {
parentPsiElement = fileBlock
anchor = getPsiElementForAnchor(parentPsiElement, anchorAfter)
}
val firstRealChild = fileBlock?.firstChild
if (addBefore) {
addedElement = parentPsiElement.addBefore(statement, anchor)
if (!isWhiteSpaceOrNls(addedElement.prevSibling)) {
parentPsiElement.addBefore(lineTerminator, addedElement)
}
}
else if (fileBlock != null && anchor == null && firstRealChild?.node?.elementType == BLOCK_COMMENT) {
addedElement = fileBlock.addAfter(statement, firstRealChild)
}
else {
addedElement = parentPsiElement.addAfter(statement, anchor)
if (!isWhiteSpaceOrNls(addedElement.nextSibling)) {
parentPsiElement.addAfter(lineTerminator, addedElement)
}
}
if (element.isBlockElement && !isWhiteSpaceOrNls(addedElement.prevSibling)) {
parentPsiElement.addBefore(lineTerminator, addedElement)
}
if (addBefore) {
parentPsiElement.addAfter(lineTerminator, addedElement)
}
else {
parentPsiElement.addBefore(lineTerminator, addedElement)
}
}
is KtBlockExpression -> {
addedElement = parentPsiElement.addAfter(statement, anchor)
when (anchorAfter) {
is GradleDslAnchor.After -> parentPsiElement.addBefore(lineTerminator, addedElement)
is GradleDslAnchor.Start -> parentPsiElement.addAfter(lineTerminator, addedElement)
}
}
is KtValueArgumentList -> {
val argumentValue = psiFactory.createArgument(statement)
addedElement = parentPsiElement.addArgumentAfter(argumentValue, anchor as? KtValueArgument)
}
is KtCallExpression -> {
val argumentList = parentPsiElement.valueArgumentList ?: return null
val argumentValue = psiFactory.createArgument(statement)
addedElement = argumentList.addArgumentAfter(argumentValue, anchor as? KtValueArgument)?.getArgumentExpression() ?: return null
}
else -> {
addedElement = parentPsiElement.addAfter(statement, anchor)
parentPsiElement.addBefore(lineTerminator, addedElement)
}
}
if (element.isBlockElement) {
val blockExpression = getKtBlockExpression(addedElement)
if (blockExpression != null) {
element.psiElement = blockExpression
}
}
else if (addedElement is KtBinaryExpression) {
addedElement.addAfter(psiFactory.createWhiteSpace(), addedElement.lastChild)
element.psiElement = addedElement
}
else if (addedElement is KtCallExpression) {
if (element is GradleDslExpressionList && !isRealList) {
element.psiElement = addedElement.valueArgumentList
}
else if (element is GradleDslExpressionMap && isNamedPropertyMap) {
element.psiElement = addedElement.valueArguments[0].getArgumentExpression()
}
else {
element.psiElement = addedElement
}
}
else if (addedElement is KtValueArgument) {
element.psiElement = addedElement.getArgumentExpression()
}
else if (addedElement is KtProperty) {
element.psiElement = addedElement
}
else if (addedElement is KtDotQualifiedExpression) {
element.psiElement = addedElement.getChildOfType<KtCallExpression>() //?.valueArguments?.get(0)?.getArgumentExpression()
}
return element.psiElement
}