[gradle][groovy] IDEA-343916 enable completion for a property with version catalog name

- fixes completion for "libs" `GradleVersionCatalogsCompletionTest#testCompletionForVersionCatalogProperty`
- also enables completion for all other version catalog names of a build

I realized that `GradleExtensionsContributor#processPropertiesFromCatalog` is called not only while resolving but also while completion: when only a part of catalog name was written. In this case, `name` argument is `null` and accessors should be created for all version catalogs of the corresponding build. To achieve that, I added `GradleVersionCatalogHandler#getAccessorsForAllCatalogs`.

I decided to add a separate method for getting all accessors because if I use the existing `getAccessorClass`, it would recreate `ProjectBuildModel` for each version catalog.

Code review: IJ-CR-153925
(cherry picked from commit 1250f5b3bb22b6ed1968683b0b6144418d0ad70f)

GitOrigin-RevId: 3f270387510983fbd51ba630c54a04db7d5a5025
This commit is contained in:
Nikita Biriukov
2024-12-12 13:03:22 +01:00
committed by intellij-monorepo-bot
parent 5ad7bbe193
commit 7110c90cca
3 changed files with 39 additions and 9 deletions

View File

@@ -38,6 +38,18 @@ class GradleDslVersionCatalogHandler : GradleVersionCatalogHandler {
return SyntheticVersionCatalogAccessor(project, scope, versionCatalogModel, catalogName)
}
override fun getAccessorsForAllCatalogs(context: PsiElement): Map<String, PsiClass> {
val project = context.project
val scope = context.resolveScope
val module = ModuleUtilCore.findModuleForPsiElement(context) ?: return emptyMap()
val catalogsModel = getBuildModel(module)?.versionCatalogsModel ?: return emptyMap()
val result = mutableMapOf<String, PsiClass>()
catalogsModel.catalogNames().forEach { catalogName ->
result.putIfAbsent(catalogName, SyntheticVersionCatalogAccessor(project, scope, catalogsModel, catalogName))
}
return result
}
private fun getBuildModel(module: Module): ProjectBuildModel? {
val buildPath = ExternalSystemModulePropertyManager.getInstance(module)
.getLinkedProjectPath() ?: return null

View File

@@ -1,10 +1,8 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gradle.service.resolve
//import org.jetbrains.plugins.gradle.service.resolve.static.getStaticallyHandledExtensions
import com.intellij.icons.AllIcons
import com.intellij.lang.properties.IProperty
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.psi.*
import com.intellij.psi.scope.PsiScopeProcessor
@@ -102,9 +100,13 @@ class GradleExtensionsContributor : NonCodeMembersContributor() {
}
fun processPropertiesFromCatalog(name: String?, place: PsiElement, processor: PsiScopeProcessor, state: ResolveState) : Set<String>? {
if (!isNameOfVersionCatalog(name, place)) return emptySet()
val accessor = getVersionCatalogAccessor(place, name!!) ?: return emptySet()
if (!processor.execute(StaticVersionCatalogProperty(place, name, accessor), state)) {
if (name == null) {
// this case is possible when only a part of a catalog name is written and autocomplete is triggered
return processAllCatalogsOfBuild(place, processor, state)
}
val accessor = getVersionCatalogAccessor(place, name) ?: return emptySet()
val element = StaticVersionCatalogProperty(place, name, accessor)
if (!processor.execute(element, state)) {
return null // to stop processing
}
return setOf(name)
@@ -119,10 +121,14 @@ class GradleExtensionsContributor : NonCodeMembersContributor() {
internal const val PROPERTIES_FILE_ORIGINAL_INFO : String = "by gradle.properties"
private fun isNameOfVersionCatalog(catalogName: String?, place: PsiElement): Boolean {
catalogName ?: return false
val module = ModuleUtilCore.findModuleForPsiElement(place) ?: return false
return getVersionCatalogFiles(module).contains(catalogName)
private fun processAllCatalogsOfBuild(place: PsiElement, processor: PsiScopeProcessor, state: ResolveState): Set<String>? {
val catalogNameToAccessor: Map<String, PsiClass> = getAccessorsForAllCatalogs(place)
catalogNameToAccessor.forEach { (catalogName, accessor) ->
if (!processor.execute(StaticVersionCatalogProperty(place, catalogName, accessor), state)) {
return null // to stop processing
}
}
return catalogNameToAccessor.keys
}
}
}

View File

@@ -22,6 +22,7 @@ interface GradleVersionCatalogHandler {
fun getVersionCatalogFiles(module: Module) : Map</*catalog name*/ String, /*catalog file*/ VirtualFile>
fun getAccessorClass(context: PsiElement, catalogName: String) : PsiClass?
fun getAccessorsForAllCatalogs(context: PsiElement) : Map</*catalog name*/ String, /*accessor*/ PsiClass>
}
@Deprecated("Doesn't work for included builds of a composite build", ReplaceWith("getVersionCatalogFiles(module)"))
@@ -62,4 +63,15 @@ fun getVersionCatalogAccessor(context: PsiElement, name: String) : PsiClass? {
return null
}
/**
* Provides accessors for all version catalogs of a build the context element belongs to (maps catalog name to accessor)
*/
fun getAccessorsForAllCatalogs(context: PsiElement) : Map<String, PsiClass> {
val container = mutableMapOf<String, PsiClass>()
for (extension in EP_NAME.extensionList) {
container.putAll(extension.getAccessorsForAllCatalogs(context))
}
return container
}
private val EP_NAME : ExtensionPointName<GradleVersionCatalogHandler> = ExtensionPointName.create("org.jetbrains.plugins.gradle.externallyHandledExtensions")