summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorSergey Prigogin <sprigogin@google.com>2022-03-16 17:13:41 -0700
committerSergey Prigogin <sprigogin@google.com>2022-03-17 17:19:15 +0000
commit01574bec7be75ca0ab88c542dd5ec267f424876b (patch)
tree661b5c04e8a537f5164dda0eb2dc1867bedacfdb /android
parentb2f6ad771e5d74a5dddbe514cd2411659c0810e5 (diff)
downloadidea-01574bec7be75ca0ab88c542dd5ec267f424876b.tar.gz
Support Navigate > Implementation for fields of R classes
Test: ResourceDefinitionSearchTest Fixes: 152802800 Change-Id: I15f6936faa11991adcae730e3e306fe7a48f7f89
Diffstat (limited to 'android')
-rw-r--r--android/src/META-INF/android-plugin.xml1
-rw-r--r--android/src/com/android/tools/idea/res/psi/ResourceDefinitionSearch.kt43
-rw-r--r--android/src/com/android/tools/idea/res/psi/ResourceRepositoryToPsiResolver.kt28
-rw-r--r--android/testSrc/com/android/tools/idea/res/psi/ResourceDefinitionSearchTest.kt57
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