Refactor Android plugin

This commit is contained in:
Yan Zhulanow
2014-12-30 19:18:13 +03:00
parent 4f9aba2cdc
commit 346dbe3f7c
23 changed files with 311 additions and 292 deletions
@@ -28,13 +28,13 @@ import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.psi.JetProperty
import com.intellij.psi.impl.light.LightElement
import com.intellij.psi.xml.XmlAttribute
import org.jetbrains.jet.lang.resolve.android.isRClassField
import com.intellij.psi.PsiField
import com.intellij.openapi.module.ModuleServiceManager
import org.jetbrains.jet.plugin.caches.resolve.getModuleInfo
import org.jetbrains.jet.plugin.caches.resolve.ModuleSourceInfo
public class AndroidGotoDeclarationHandler : GotoDeclarationHandler {
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
if (sourceElement is LeafPsiElement && sourceElement.getParent() is JetSimpleNameExpression) {
val resolved = JetSimpleNameReference(sourceElement.getParent() as JetSimpleNameExpression).resolve()
@@ -60,4 +60,5 @@ public class AndroidGotoDeclarationHandler : GotoDeclarationHandler {
override fun getActionText(context: DataContext?): String? {
return null
}
}
@@ -29,22 +29,38 @@ import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper
import org.jetbrains.android.util.AndroidResourceUtil
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.impl.light.LightElement
import org.jetbrains.jet.lang.resolve.android.isRClassField
import com.intellij.openapi.module.ModuleServiceManager
import org.jetbrains.jet.plugin.caches.resolve.getModuleInfo
import org.jetbrains.jet.plugin.caches.resolve.ModuleSourceInfo
import com.intellij.openapi.module.Module
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.moduleInfo
import org.jetbrains.jet.lang.resolve.android.nameToIdDeclaration
import org.jetbrains.jet.lang.resolve.android.idToName
import com.intellij.psi.PsiField
import com.intellij.psi.PsiClass
public class AndroidRenameProcessor : RenamePsiElementProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
// either renaming synthetic property, or value in ui xml, or R class field
// Either renaming synthetic property, or value in layout xml, or R class field
return (element.namedUnwrappedElement is JetProperty &&
isAndroidSyntheticElement(element.namedUnwrappedElement)) || element is XmlAttributeValue ||
isRClassField(element)
}
private fun isRClassField(element: PsiElement): Boolean {
return if (element is PsiField) {
val outerClass = element.getParent()?.getParent()
if (outerClass !is PsiClass) return false
if (outerClass.getQualifiedName()?.startsWith(AndroidConst.SYNTHETIC_PACKAGE) ?: false)
true else false
}
else false
}
private fun PsiElement.getModule(): Module? {
val moduleInfo = getModuleInfo()
if (moduleInfo is ModuleSourceInfo) return moduleInfo.module
@@ -58,7 +74,12 @@ public class AndroidRenameProcessor : RenamePsiElementProcessor() {
return null
}
override fun prepareRenaming(element: PsiElement?, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
override fun prepareRenaming(
element: PsiElement?,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope
) {
if (element != null && element.namedUnwrappedElement is JetProperty) {
renameSyntheticProperty(element.namedUnwrappedElement as JetProperty, newName, allRenames, scope)
}
@@ -70,45 +91,66 @@ public class AndroidRenameProcessor : RenamePsiElementProcessor() {
}
}
private fun renameSyntheticProperty(jetProperty: JetProperty, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
private fun renameSyntheticProperty(
jetProperty: JetProperty,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope
) {
val oldName = jetProperty.getName()
val module = jetProperty.getModule()
if (module == null) return
val processor = ModuleServiceManager.getService(module, javaClass<AndroidUIXmlProcessor>())
val resourceManager = processor!!.resourceManager
val resourceManager = processor.resourceManager
val attr = resourceManager.idToXmlAttribute(oldName) as XmlAttribute
allRenames[XmlAttributeValueWrapper(attr.getValueElement()!!)] = resourceManager.nameToIdDeclaration(newName!!)
allRenames[XmlAttributeValueWrapper(attr.getValueElement())] = nameToIdDeclaration(newName)
val name = AndroidResourceUtil.getResourceNameByReferenceText(newName)
for (resField in AndroidResourceUtil.findIdFields(attr)) {
allRenames.put(resField, AndroidResourceUtil.getFieldNameByResourceName(name!!))
allRenames.put(resField, AndroidResourceUtil.getFieldNameByResourceName(name))
}
}
private fun renameAttributeValue(attribute: XmlAttributeValue, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
val element1 = LazyValueResourceElementWrapper.computeLazyElement(attribute)
val module = attribute.getModule()
private fun renameAttributeValue(
attribute: XmlAttributeValue,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope
) {
val element = LazyValueResourceElementWrapper.computeLazyElement(attribute)
val module = attribute.getModule() ?: ModuleUtilCore.findModuleForFile(
attribute.getContainingFile().getVirtualFile(), attribute.getProject())
if (module == null) return
val processor = ModuleServiceManager.getService(module, javaClass<AndroidUIXmlProcessor>())
if (element1 == null) return
val oldPropName = AndroidResourceUtil.getResourceNameByReferenceText(attribute.getValue()!!)
val newPropName = processor!!.resourceManager.idToName(newName!!)
if (element == null) return
val oldPropName = AndroidResourceUtil.getResourceNameByReferenceText(attribute.getValue())
val newPropName = idToName(newName)
renameSyntheticProperties(allRenames, newPropName, oldPropName, processor)
}
private fun renameSyntheticProperties(allRenames: MutableMap<PsiElement, String>, newPropName: String, oldPropName: String?, processor: AndroidUIXmlProcessor) {
val props = processor.lastCachedPsi?.findChildrenByClass(javaClass<JetProperty>())
val matchedProps = props?.filter { it.getName() == oldPropName } ?: arrayListOf()
private fun renameSyntheticProperties(
allRenames: MutableMap<PsiElement, String>,
newPropName: String,
oldPropName: String,
processor: AndroidUIXmlProcessor
) {
val props = processor.parseToPsi()?.flatMap { it.findChildrenByClass(javaClass<JetProperty>()).toList() }
val matchedProps = props?.filter { it.getName() == oldPropName } ?: listOf()
for (prop in matchedProps) {
allRenames[prop] = newPropName
}
}
private fun renameLightClassField(field: LightElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
private fun renameLightClassField(
field: LightElement,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope
) {
val oldName = field.getName()
val processor = ServiceManager.getService(field.getProject(), javaClass<AndroidUIXmlProcessor>())
renameSyntheticProperties(allRenames, newName!!, oldName, processor!!)
renameSyntheticProperties(allRenames, newName, oldName, processor)
}
}
@@ -33,7 +33,7 @@ public class AndroidSimpleNameReferenceExtension : SimpleNameReferenceExtension
}
if (isAndroidSyntheticElement(resolvedElement)) {
if (element is ValueResourceElementWrapper) {
val resource = element.getValue()!!
val resource = element.getValue()
return (resolvedElement as JetProperty).getName() == resource.substring(resource.indexOf('/') + 1)
}
}
@@ -22,8 +22,13 @@ import com.intellij.psi.xml.XmlElement
import com.intellij.psi.xml.XmlTag
import com.intellij.psi.xml.XmlAttribute
import org.jetbrains.jet.lang.resolve.android.AndroidResourceManager
import org.jetbrains.jet.lang.resolve.android.idToName
import org.jetbrains.jet.lang.resolve.android.AndroidConst
class AndroidXmlVisitor(val resourceManager: AndroidResourceManager, val elementCallback: (String, String, XmlAttribute) -> Unit) : XmlElementVisitor() {
class AndroidXmlVisitor(
val resourceManager: AndroidResourceManager,
val elementCallback: (String, String, XmlAttribute) -> Unit
) : XmlElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
@@ -34,10 +39,13 @@ class AndroidXmlVisitor(val resourceManager: AndroidResourceManager, val element
}
override fun visitXmlTag(tag: XmlTag?) {
val attribute = tag?.getAttribute(resourceManager.idAttribute)
if (attribute != null && attribute.getValue() != null) {
val classNameAttr = tag?.getAttribute(resourceManager.classAttributeNoNamespace)?.getValue() ?: tag?.getLocalName()
elementCallback(resourceManager.idToName(attribute.getValue()), classNameAttr!!, attribute)
val attribute = tag?.getAttribute(AndroidConst.ID_ATTRIBUTE)
if (attribute != null) {
val attributeValue = attribute.getValue()
if (attributeValue != null) {
val classNameAttr = tag?.getAttribute(AndroidConst.CLASS_ATTRIBUTE_NO_NAMESPACE)?.getValue() ?: tag?.getLocalName()
elementCallback(idToName(attributeValue), classNameAttr!!, attribute)
}
}
tag?.acceptChildren(this)
}
@@ -32,7 +32,7 @@ public class IDEAndroidExternalDeclarationsProvider(private val project: Project
val module = moduleInfo.module
val parser = ModuleServiceManager.getService<AndroidUIXmlProcessor>(module, javaClass<AndroidUIXmlProcessor>())
val syntheticFile = parser.parseToPsi(project)
val syntheticFile = parser.parseToPsi()
syntheticFile?.moduleInfo = moduleInfo
return if (syntheticFile != null) listOf(syntheticFile) else listOf()
@@ -17,36 +17,23 @@
package org.jetbrains.jet.plugin.android
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.resolve.android.AndroidResourceManagerBase
import com.intellij.psi.PsiElement
import java.util.HashMap
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.PsiFile
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.jet.lang.resolve.android.AndroidManifest
import com.intellij.openapi.module.ModuleManager
import java.util.ArrayList
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.module.Module
import com.intellij.psi.PsiManager
import org.jetbrains.jet.lang.resolve.android.AndroidResourceManager
import org.jetbrains.jet.lang.resolve.android.AndroidModuleInfo
import kotlin.properties.Delegates
public class IDEAndroidResourceManager(val module: Module, searchPath: String?) : AndroidResourceManagerBase(module.getProject(), searchPath) {
public class IDEAndroidResourceManager(val module: Module) : AndroidResourceManager(module.getProject()) {
override fun getLayoutXmlFiles(): Collection<PsiFile> {
val directories = getAndroidFacet()?.getAllResourceDirectories() ?: listOf()
return directories.flatMap {
(it.findChild("layout")?.getChildren() ?: array<VirtualFile>()).map { virtualFileToPsi(it)!! }
}
}
private fun getAndroidFacet(): AndroidFacet? {
return AndroidFacet.getInstance(module)
}
override fun readManifest(): AndroidManifest {
val facet = getAndroidFacet()
val attributeValue = facet?.getManifest()!!.getPackage()
return AndroidManifest(attributeValue.getRawText())
}
override val androidModuleInfo: AndroidModuleInfo? by Delegates.lazy { module.androidFacet?.toAndroidModuleInfo() }
override fun idToXmlAttribute(id: String): PsiElement? {
var ret: PsiElement? = null
@@ -58,4 +45,13 @@ public class IDEAndroidResourceManager(val module: Module, searchPath: String?)
return ret
}
private val Module.androidFacet: AndroidFacet?
get() = AndroidFacet.getInstance(this)
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo {
val applicationPackage = getManifest().getPackage().toString()
val mainResDirectory = getAllResourceDirectories().firstOrNull()?.getPath()
return AndroidModuleInfo(applicationPackage, mainResDirectory)
}
}
@@ -28,18 +28,16 @@ import org.jetbrains.jet.lang.resolve.android.AndroidResourceManager
import com.intellij.openapi.module.Module
class IDEAndroidUIXmlProcessor(val module: Module) : AndroidUIXmlProcessor(module.getProject()) {
override val searchPath: String? = project.getBasePath() + "/res/layout/"
override var androidAppPackage: String = ""
get() = resourceManager.readManifest()._package
override val resourceManager: IDEAndroidResourceManager = IDEAndroidResourceManager(module, searchPath)
override val resourceManager: IDEAndroidResourceManager = IDEAndroidResourceManager(module)
override fun parseSingleFileImpl(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList()
override fun parseSingleFile(file: PsiFile): List<AndroidWidget> {
val widgets = arrayListOf<AndroidWidget>()
file.accept(AndroidXmlVisitor(resourceManager, { id, wClass, valueElement ->
ids.add(AndroidWidget(id, wClass))
widgets.add(AndroidWidget(id, wClass))
}))
return produceKotlinProperties(KotlinStringWriter(), ids).toString()
}
}
return widgets
}
}