Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add += completion items to properties that have augmentations #13

Merged
merged 1 commit into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format

[versions]
gradle-tooling = "8.14-milestone-5"
declarative-dsl = "8.14-milestone-5"
gradle-tooling = "8.14-milestone-7"
declarative-dsl = "8.14-milestone-7"
detekt = "1.23.6"
lsp4j = "0.23.1"
logback = "1.5.6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either
import org.eclipse.lsp4j.services.LanguageClient
import org.eclipse.lsp4j.services.TextDocumentService
import org.gradle.declarative.dsl.schema.AnalysisSchema
import org.gradle.declarative.dsl.schema.AssignmentAugmentationKind
import org.gradle.declarative.dsl.schema.DataClass
import org.gradle.declarative.dsl.schema.DataProperty
import org.gradle.declarative.dsl.schema.DataType
import org.gradle.declarative.dsl.schema.EnumClass
import org.gradle.declarative.dsl.schema.SchemaFunction
import org.gradle.declarative.lsp.build.model.DeclarativeResourcesModel
import org.gradle.declarative.lsp.extension.indexBasedOverlayResultFromDocuments
import org.gradle.declarative.lsp.extension.toLspRange
Expand Down Expand Up @@ -188,8 +188,10 @@ class DeclarativeTextDocumentService : TextDocumentService {
.let { it ?: schema.topLevelReceiverType }
.let { dataClass ->
computePropertyCompletions(dataClass, typeRefContext) +
computePropertyByValueFactoryCompletions(dataClass, typeRefContext, valueFactoryIndex) +
computeFunctionCompletions(dataClass, typeRefContext)
computePropertyByValueFactoryCompletions(
dataClass, schema, typeRefContext, valueFactoryIndex
) +
computeFunctionCompletions(dataClass, typeRefContext)
}
}
}.orEmpty().toMutableList()
Expand Down Expand Up @@ -380,6 +382,7 @@ private fun completionItem(property: DataProperty, resolvedType: DataType) =

private fun computePropertyByValueFactoryCompletions(
dataClass: DataClass,
schema: AnalysisSchema,
typeRefContext: TypeRefContext,
valueFactoryIndex: ValueFactoryIndex,
): List<CompletionItem> {
Expand All @@ -388,18 +391,34 @@ private fun computePropertyByValueFactoryCompletions(
if (propertiesType is DataType.ClassDataType) {
val factoriesForProperty = valueFactoryIndex.factoriesForProperty(propertiesType.name)
factoriesForProperty
?.mapNotNull { indexEntry ->
?.flatMap { indexEntry ->
val functionReturnType = typeRefContext.resolveRef(indexEntry.function.returnValueType)
val genericTypeSubstitution = typeRefContext.computeGenericTypeSubstitutionIfAssignable(propertiesType, functionReturnType)
genericTypeSubstitution?.let { typeSubstitution ->
val completionLabel = indexEntry.function.computeCompletionLabel(typeRefContext, typeSubstitution)
val insertionText = indexEntry.function.computeCompletionInsertText(typeRefContext)
CompletionItem("${property.name} = ${indexEntry.namePrefix}$completionLabel").apply {
kind = CompletionItemKind.Field
insertTextFormat = InsertTextFormat.Snippet
insertText = "${property.name} = ${indexEntry.namePrefix}$insertionText"

fun propertyCompletionItem(withPlus: Boolean): CompletionItem {
val operator = if (withPlus) "+=" else "="

return CompletionItem(
"${property.name} $operator ${indexEntry.namePrefix}$completionLabel"
).apply {
kind = if (withPlus) CompletionItemKind.Method else CompletionItemKind.Field
insertTextFormat = InsertTextFormat.Snippet
insertText = "${property.name} $operator ${indexEntry.namePrefix}$insertionText"
}
}
}

val hasPlusEqualsAugmentation = schema.assignmentAugmentationsByTypeName[propertiesType.name]
.orEmpty()
.any { it.kind is AssignmentAugmentationKind.Plus }

listOfNotNull(
propertyCompletionItem(false),
if (hasPlusEqualsAugmentation) { propertyCompletionItem(true) } else null
)
}.orEmpty()
} ?: emptyList()
} else {
emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.gradle.internal.declarativedsl.analysis.DefaultAnalysisSchema
import org.gradle.internal.declarativedsl.analysis.DefaultDataClass
import org.gradle.internal.declarativedsl.analysis.DefaultEnumClass
import org.gradle.internal.declarativedsl.analysis.DefaultFqName
import kotlin.collections.flatten

/*
* Copyright 2024 the original author or authors.
Expand Down Expand Up @@ -108,7 +109,8 @@ else {
}

val genericInstantiationsByFqName = run {
val mergedResult = mutableMapOf<FqName, Map<List<DataType.ParameterizedTypeInstance.TypeArgument>, DataType.ClassDataType>>()
val mergedResult =
mutableMapOf<FqName, Map<List<DataType.ParameterizedTypeInstance.TypeArgument>, DataType.ClassDataType>>()
schemas.forEach { schema ->
schema.genericInstantiationsByFqName.forEach { instantiation ->
mergedResult.merge(instantiation.key, instantiation.value) { oldVal, newVal -> oldVal + newVal }
Expand All @@ -117,14 +119,20 @@ else {
mergedResult
}

val augmentations =
schemas.flatMap { it.assignmentAugmentationsByTypeName.entries }
.groupBy({ it.key }, { it.value })
.mapValues { (_, value) -> value.flatten() }

DefaultAnalysisSchema(
newTopLevelReceiver,
dataClassesByFqName,
emptyMap(),
genericInstantiationsByFqName,
newExternalFunctionsByFqName,
emptyMap(),
emptySet()
schemas.flatMapTo(mutableSetOf()) { it.defaultImports },
augmentations
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.gradle.internal.declarativedsl.dom.BlockMismatch
import org.gradle.internal.declarativedsl.dom.CrossScopeAccess
import org.gradle.internal.declarativedsl.dom.DeclarativeDocument
import org.gradle.internal.declarativedsl.dom.DocumentResolution
import org.gradle.internal.declarativedsl.dom.IllegalAugmentedAssignment
import org.gradle.internal.declarativedsl.dom.IsError
import org.gradle.internal.declarativedsl.dom.NonEnumValueNamedReference
import org.gradle.internal.declarativedsl.dom.NotAssignable
Expand Down Expand Up @@ -67,6 +68,7 @@ class SemanticErrorToDiagnosticVisitor(private val documentOverlayResult: Docume
ValueTypeMismatch -> "Value type mismatch"
NonEnumValueNamedReference -> "Non-enum value named reference"
OpaqueValueInIdentityKey -> "Non-literal value used as identity key"
IllegalAugmentedAssignment -> "Illegal augmented assignment"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ class DeclarativeTextDocumentServiceForBuildScriptsTest : AbstractDeclarativeTex
assertCompletion(
script(), 6, 21, """
|baselineProfile { this: BaselineProfile } --> baselineProfile {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|defaultProguardFiles += listOf(vararg elements: ProguardFile) --> defaultProguardFiles += listOf(${'$'}1)${'$'}0
|defaultProguardFiles = listOf(vararg elements: ProguardFile) --> defaultProguardFiles = listOf(${'$'}1)${'$'}0
|dependencies { this: AndroidLibraryDependencies } --> dependencies {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|minify { this: Minify } --> minify {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|proguardFile(name: String) --> proguardFile("${'$'}{1}")${'$'}0
|proguardFiles += listOf(vararg elements: ProguardFile) --> proguardFiles += listOf(${'$'}1)${'$'}0
|proguardFiles = listOf(vararg elements: ProguardFile) --> proguardFiles = listOf(${'$'}1)${'$'}0
""".trimMargin()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ class DeclarativeTextDocumentServiceForSettingsScriptsTest: AbstractDeclarativeT
assertCompletion(
script(), 60, 21, """
|baselineProfile { this: BaselineProfile } --> baselineProfile {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|defaultProguardFiles += listOf(vararg elements: ProguardFile) --> defaultProguardFiles += listOf(${'$'}1)${'$'}0
|defaultProguardFiles = listOf(vararg elements: ProguardFile) --> defaultProguardFiles = listOf(${'$'}1)${'$'}0
|dependencies { this: AndroidLibraryDependencies } --> dependencies {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|minify { this: Minify } --> minify {${'\n'}|${'\t'}${'$'}0${'\n'}|}
|proguardFile(name: String) --> proguardFile("${'$'}{1}")${'$'}0
|proguardFiles += listOf(vararg elements: ProguardFile) --> proguardFiles += listOf(${'$'}1)${'$'}0
|proguardFiles = listOf(vararg elements: ProguardFile) --> proguardFiles = listOf(${'$'}1)${'$'}0
""".trimMargin()
)
Expand Down