From 0a96c2c1480a3f03a5cb11c367e297e18988855b Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 20 Mar 2015 22:20:28 +0300 Subject: [PATCH] Lookup XML attribute using the property (for a particular layout) --- .../lang/resolve/android/AndroidConst.kt | 3 +- .../resolve/android/AndroidResourceManager.kt | 3 +- .../resolve/android/AndroidUIXmlProcessor.kt | 4 +-- .../android/AndroidFindMemberUsagesHandler.kt | 18 +++++++---- .../android/AndroidGotoDeclarationHandler.kt | 22 ++++++------- .../plugin/android/AndroidRenameProcessor.kt | 8 ++--- .../android/IDEAndroidResourceManager.kt | 31 ++++++++++++++----- 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidConst.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidConst.kt index 6abedf73d55..1cfa552c0f9 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidConst.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidConst.kt @@ -21,7 +21,8 @@ import com.intellij.openapi.util.Key public object AndroidConst { val ANDROID_USER_PACKAGE: Key = Key.create("ANDROID_USER_PACKAGE") val SYNTHETIC_FILENAME: String = "ANDROIDXML" - val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic." + val SYNTHETIC_PACKAGE: String = "kotlinx.android.synthetic" + val SYNTHETIC_PACKAGE_PATH_LENGTH = SYNTHETIC_PACKAGE.count { it == '.' } + 1 val ANDROID_NAMESPACE: String = "android" val ID_ATTRIBUTE_NO_NAMESPACE: String = "id" diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt index 937a32c6891..a0e1fa27b22 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidResourceManager.kt @@ -26,12 +26,13 @@ import com.intellij.openapi.vfs.* import com.intellij.openapi.components.* import com.intellij.openapi.extensions.* import com.intellij.openapi.module.* +import org.jetbrains.kotlin.psi.JetProperty public abstract class AndroidResourceManager(val project: Project) { public abstract val androidModuleInfo: AndroidModuleInfo? - public open fun idToXmlAttribute(id: String): PsiElement? = null + public open fun propertyToXmlAttribute(property: JetProperty): PsiElement? = null public fun getLayoutXmlFiles(): Map> { val info = androidModuleInfo diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt index 5cb83191883..488975ad3ef 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/lang/resolve/android/AndroidUIXmlProcessor.kt @@ -84,7 +84,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { public fun parse(generateCommonFiles: Boolean = true): List { val commonFiles = if (generateCommonFiles) { - val clearCacheFile = renderLayoutFile("kotlinx.android.synthetic") {} + + val clearCacheFile = renderLayoutFile(AndroidConst.SYNTHETIC_PACKAGE) {} + renderClearCacheFunction("Activity") + renderClearCacheFunction("Fragment") listOf(clearCacheFile, FLEXIBLE_TYPE_FILE) } else listOf() @@ -132,7 +132,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) { } private fun PsiFile.genSyntheticPackageName(): String { - return AndroidConst.SYNTHETIC_PACKAGE + getName().substringBefore('.') + return AndroidConst.SYNTHETIC_PACKAGE + "." + getName().substringBefore('.') } private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, widget: AndroidWidget, stubCall: String) { diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidFindMemberUsagesHandler.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidFindMemberUsagesHandler.kt index 3ef5628f46f..6dead75f56c 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidFindMemberUsagesHandler.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidFindMemberUsagesHandler.kt @@ -27,6 +27,9 @@ import com.intellij.psi.xml.XmlAttribute import org.jetbrains.android.util.AndroidResourceUtil import java.util.ArrayList import com.intellij.find.findUsages.JavaVariableFindUsagesOptions +import com.intellij.openapi.module.ModuleServiceManager +import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo +import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo import org.jetbrains.kotlin.plugin.findUsages.handlers.KotlinFindUsagesHandlerDecorator import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.psi.JetProperty @@ -52,9 +55,10 @@ class AndroidFindMemberUsagesHandler( assert(isAndroidSyntheticElement(declaration)) val property = declaration as JetProperty - val name = property.getName()!! - val parser = ServiceManager.getService(declaration.getProject(), javaClass()) - val psiElement = parser?.resourceManager?.idToXmlAttribute(name) as? XmlAttribute + val moduleInfo = declaration.getModuleInfo() as? ModuleSourceInfo ?: return super.getPrimaryElements() + val parser = ModuleServiceManager.getService(moduleInfo.module, javaClass()) + + val psiElement = parser?.resourceManager?.propertyToXmlAttribute(property) as? XmlAttribute if (psiElement != null && psiElement.getValueElement() != null) { return array(psiElement.getValueElement()!!) } @@ -69,9 +73,11 @@ class AndroidFindMemberUsagesHandler( override fun getSecondaryElements(): Array { assert(isAndroidSyntheticElement(declaration)) - val name = (declaration as JetProperty).getName()!! - val parser = ServiceManager.getService(declaration.getProject(), javaClass()) - val psiElement = parser?.resourceManager?.idToXmlAttribute(name) as? XmlAttribute + val property = declaration as JetProperty + val moduleInfo = declaration.getModuleInfo() as? ModuleSourceInfo ?: return super.getPrimaryElements() + val parser = ModuleServiceManager.getService(moduleInfo.module, javaClass()) + + val psiElement = parser?.resourceManager?.propertyToXmlAttribute(property) as? XmlAttribute if (psiElement != null) { val res = ArrayList() val fields = AndroidResourceUtil.findIdFields(psiElement) diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidGotoDeclarationHandler.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidGotoDeclarationHandler.kt index 665dda1f20e..fb33da1fe10 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidGotoDeclarationHandler.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidGotoDeclarationHandler.kt @@ -35,20 +35,16 @@ public class AndroidGotoDeclarationHandler : GotoDeclarationHandler { override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array? { if (sourceElement is LeafPsiElement && sourceElement.getParent() is JetSimpleNameExpression) { val resolved = JetSimpleNameReference(sourceElement.getParent() as JetSimpleNameExpression).resolve() - if (resolved == null) return null - val name = if (resolved is JetProperty) { - resolved.getName() - } - else null - if (name != null) { - val moduleInfo = sourceElement.getModuleInfo() - if (moduleInfo !is ModuleSourceInfo) return null + val property = resolved as? JetProperty ?: return null - val parser = ModuleServiceManager.getService(moduleInfo.module, javaClass()) - val psiElement = parser.resourceManager.idToXmlAttribute(name) as? XmlAttribute - if (psiElement != null) { - return array(psiElement.getValueElement()) - } + val moduleInfo = sourceElement.getModuleInfo() + if (moduleInfo !is ModuleSourceInfo) return null + + val parser = ModuleServiceManager.getService(moduleInfo.module, javaClass()) + val psiElement = parser.resourceManager.propertyToXmlAttribute(property) as? XmlAttribute + val attributeValue = psiElement?.getValueElement() + if (attributeValue != null) { + return array(attributeValue) } } return null diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidRenameProcessor.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidRenameProcessor.kt index 1594259e84b..71df43a6303 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidRenameProcessor.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/AndroidRenameProcessor.kt @@ -84,7 +84,7 @@ public class AndroidRenameProcessor : RenamePsiElementProcessor() { scope: SearchScope ) { if (element != null && element.namedUnwrappedElement is JetProperty) { - renameSyntheticProperty(element.namedUnwrappedElement as JetProperty, newName, allRenames, scope) + renameSyntheticProperty(element.namedUnwrappedElement as JetProperty, newName, allRenames) } else if (element is XmlAttributeValue) { renameAttributeValue(element, newName, allRenames, scope) @@ -97,16 +97,14 @@ public class AndroidRenameProcessor : RenamePsiElementProcessor() { private fun renameSyntheticProperty( jetProperty: JetProperty, newName: String, - allRenames: MutableMap, - scope: SearchScope + allRenames: MutableMap ) { - val oldName = jetProperty.getName() val module = jetProperty.getModule() if (module == null) return val processor = ModuleServiceManager.getService(module, javaClass()) val resourceManager = processor.resourceManager - val attr = resourceManager.idToXmlAttribute(oldName) as XmlAttribute + val attr = resourceManager.propertyToXmlAttribute(jetProperty) as XmlAttribute allRenames[XmlAttributeValueWrapper(attr.getValueElement())] = nameToIdDeclaration(newName) val name = AndroidResourceUtil.getResourceNameByReferenceText(newName) for (resField in AndroidResourceUtil.findIdFields(attr)) { diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/IDEAndroidResourceManager.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/IDEAndroidResourceManager.kt index 2f9d6f13f46..b314ef4df14 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/IDEAndroidResourceManager.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/plugin/android/IDEAndroidResourceManager.kt @@ -19,32 +19,47 @@ package org.jetbrains.kotlin.plugin.android import com.intellij.openapi.module.Module import com.intellij.psi.PsiElement import org.jetbrains.android.facet.AndroidFacet +import org.jetbrains.kotlin.lang.resolve.android.AndroidConst import kotlin.properties.Delegates import org.jetbrains.kotlin.plugin.android.AndroidXmlVisitor import org.jetbrains.kotlin.lang.resolve.android.AndroidResourceManager import org.jetbrains.kotlin.lang.resolve.android.AndroidModuleInfo +import org.jetbrains.kotlin.psi.JetProperty public class IDEAndroidResourceManager(val module: Module) : AndroidResourceManager(module.getProject()) { override val androidModuleInfo: AndroidModuleInfo? by Delegates.lazy { module.androidFacet?.toAndroidModuleInfo() } - override fun idToXmlAttribute(id: String): PsiElement? { + override fun propertyToXmlAttribute(property: JetProperty): PsiElement? { + val fqPath = property.getFqName()?.pathSegments() ?: return null + if (fqPath.size() <= AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH) return null + + val layoutPackageName = fqPath[AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH].asString() + val layoutFiles = getLayoutXmlFiles()[layoutPackageName] + if (layoutFiles == null || layoutFiles.isEmpty()) return null + + val propertyName = property.getName() + var ret: PsiElement? = null - for (file in getLayoutXmlFiles().values().flatMap { it }) { - file.accept(AndroidXmlVisitor({ retId, wClass, valueElement -> - if (retId == id) ret = valueElement - })) + val visitor = AndroidXmlVisitor { retId, wClass, valueElement -> + if (retId == propertyName) ret = valueElement } + + layoutFiles.first().accept(visitor) return ret } private val Module.androidFacet: AndroidFacet? get() = AndroidFacet.getInstance(this) - private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo { - val applicationPackage = getManifest().getPackage().toString() + private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? { + val applicationPackage = getManifest()?.getPackage()?.toString() val mainResDirectory = getAllResourceDirectories().firstOrNull()?.getPath() - return AndroidModuleInfo(applicationPackage, mainResDirectory) + + return if (applicationPackage != null) { + AndroidModuleInfo(applicationPackage, mainResDirectory) + } + else null } } \ No newline at end of file