diff options
author | Sergey Prigogin <sprigogin@google.com> | 2022-03-16 17:13:41 -0700 |
---|---|---|
committer | Sergey Prigogin <sprigogin@google.com> | 2022-03-17 17:19:15 +0000 |
commit | 01574bec7be75ca0ab88c542dd5ec267f424876b (patch) | |
tree | 661b5c04e8a537f5164dda0eb2dc1867bedacfdb /android | |
parent | b2f6ad771e5d74a5dddbe514cd2411659c0810e5 (diff) | |
download | idea-01574bec7be75ca0ab88c542dd5ec267f424876b.tar.gz |
Support Navigate > Implementation for fields of R classes
Test: ResourceDefinitionSearchTest
Fixes: 152802800
Change-Id: I15f6936faa11991adcae730e3e306fe7a48f7f89
Diffstat (limited to 'android')
4 files changed, 115 insertions, 14 deletions
diff --git a/android/src/META-INF/android-plugin.xml b/android/src/META-INF/android-plugin.xml index 71dba4cfa8f..81e02d5b71c 100644 --- a/android/src/META-INF/android-plugin.xml +++ b/android/src/META-INF/android-plugin.xml @@ -570,6 +570,7 @@ <sdkType implementation="org.jetbrains.android.sdk.AndroidSdkType"/> <gotoDeclarationHandler implementation="org.jetbrains.android.AndroidGotoDeclarationHandler"/> <gotoSymbolContributor implementation="com.android.tools.idea.res.psi.GoToAndroidResourceContributor"/> + <definitionsScopedSearch implementation="com.android.tools.idea.res.psi.ResourceDefinitionSearch" order="first"/> <moduleRendererFactory implementation="com.android.tools.idea.res.psi.ResourceModuleRendererFactory" order="first"/> <importFilter implementation="com.android.tools.idea.editors.AndroidImportFilter" /> <overrideImplementsAnnotationsHandler implementation="com.android.tools.idea.editors.AndroidOverrideAnnotationsHandler" /> diff --git a/android/src/com/android/tools/idea/res/psi/ResourceDefinitionSearch.kt b/android/src/com/android/tools/idea/res/psi/ResourceDefinitionSearch.kt new file mode 100644 index 00000000000..873dcff098a --- /dev/null +++ b/android/src/com/android/tools/idea/res/psi/ResourceDefinitionSearch.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.res.psi + +import com.android.tools.idea.res.ResourceRepositoryManager +import com.intellij.openapi.application.QueryExecutorBase +import com.intellij.psi.PsiElement +import com.intellij.psi.search.searches.DefinitionsScopedSearch +import com.intellij.util.Processor + +/** + * Looks up definitions of an Android resource. + */ +class ResourceDefinitionSearch : QueryExecutorBase<PsiElement, DefinitionsScopedSearch.SearchParameters>(/* requireReadAction = */ true) { + + override fun processQuery(queryParameters: DefinitionsScopedSearch.SearchParameters, consumer: Processor<in PsiElement>) { + val project = queryParameters.project + val element = queryParameters.element + val refElement = ResourceReferencePsiElement.create(element) ?: return + val resourceRepositoryManager = ResourceRepositoryManager.getInstance(element) ?: return + val resourceReference = refElement.resourceReference + val repository = resourceRepositoryManager.getResourcesForNamespace(resourceReference.namespace) ?: return + for (resource in repository.getResources(resourceReference).filterDefinitions(project)) { + val declaration = ResourceRepositoryToPsiResolver.resolveToDeclaration(resource, project) + if (declaration != null && !consumer.process(declaration)) { + return + } + } + } +}
\ No newline at end of file diff --git a/android/src/com/android/tools/idea/res/psi/ResourceRepositoryToPsiResolver.kt b/android/src/com/android/tools/idea/res/psi/ResourceRepositoryToPsiResolver.kt index 9d81b3609a3..f720785ac09 100644 --- a/android/src/com/android/tools/idea/res/psi/ResourceRepositoryToPsiResolver.kt +++ b/android/src/com/android/tools/idea/res/psi/ResourceRepositoryToPsiResolver.kt @@ -183,20 +183,6 @@ object ResourceRepositoryToPsiResolver : AndroidResourceToPsiResolver { .mapNotNull { resolveToDeclaration(it, context.project) } } - /** - * Filters a collection of resources of the same type. If the collection contains non-ID - * resources, returns the whole collection. If the collection contains ID resources, returns - * a subset corresponding to ID definitions, unless this subset is empty, in which case - * returns all resources. - */ - private fun Collection<ResourceItem>.filterDefinitions(project: Project): Collection<ResourceItem> { - if (size <= 1 || first().type != ResourceType.ID) { - return this // Nothing to filter out. - } - val definitions = filter { it.isIdDefinition(project) } - return definitions.ifEmpty { this } - } - private fun getGotoDeclarationElementsFromDynamicFeatureModules( resourceReference: ResourceReference, context: PsiElement @@ -261,3 +247,17 @@ object ResourceRepositoryToPsiResolver : AndroidResourceToPsiResolver { return resourceRepositoryManager.getResourcesForNamespace(resourceReference.namespace) } } + +/** + * Filters a collection of resources of the same type. If the collection contains non-ID + * resources, returns the whole collection. If the collection contains ID resources, returns + * a subset corresponding to ID definitions, unless this subset is empty, in which case + * returns all resources. + */ +internal fun Collection<ResourceItem>.filterDefinitions(project: Project): Collection<ResourceItem> { + if (size <= 1 || first().type != ResourceType.ID) { + return this // Nothing to filter out. + } + val definitions = filter { it.isIdDefinition(project) } + return definitions.ifEmpty { this } +} diff --git a/android/testSrc/com/android/tools/idea/res/psi/ResourceDefinitionSearchTest.kt b/android/testSrc/com/android/tools/idea/res/psi/ResourceDefinitionSearchTest.kt new file mode 100644 index 00000000000..7ef0a3a203a --- /dev/null +++ b/android/testSrc/com/android/tools/idea/res/psi/ResourceDefinitionSearchTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.tools.idea.res.psi + +import com.android.tools.idea.res.ResourceRepositoryManager +import com.google.common.truth.Truth.assertThat +import com.intellij.psi.search.searches.DefinitionsScopedSearch +import org.jetbrains.android.AndroidTestCase + +/** + * Tests for [ResourceDefinitionSearch]. + */ +class ResourceDefinitionSearchTest : AndroidTestCase() { + + fun testResourceReference() { + myFixture.addFileToProject("res/values/strings.xml", + //language=XML + """ + <resources> + <string name="app_name">My Application</string> + </resources> + """.trimIndent()) + + val file = myFixture.addFileToProject( + "/src/p1/p2/Foo.java", + //language=JAVA + """ + package p1.p2; + class Foo { + public static void foo() { + int n = R.string.app_<caret>name; + } + } + """.trimIndent()).virtualFile + + ResourceRepositoryManager.getAppResources(myFacet) + myFixture.configureFromExistingVirtualFile(file) + + val element = myFixture.elementAtCaret + val psiElements = DefinitionsScopedSearch.search(element).findAll() + assertThat(psiElements).hasSize(1) + assertThat(psiElements.first().parent.parent.text).isEqualTo("<string name=\"app_name\">My Application</string>") + } +}
\ No newline at end of file |