Refactoring: rename android-idea-plugin to android-extensions-idea

This commit is contained in:
Natalia Ukhorskaya
2016-03-11 16:01:33 +03:00
parent 7e6fa8ef17
commit bf4c92d903
109 changed files with 53 additions and 53 deletions
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.module.ModuleServiceManager
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.xml.XmlAttribute
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class AndroidGotoDeclarationHandler : GotoDeclarationHandler {
override fun getGotoDeclarationTargets(sourceElement: PsiElement?, offset: Int, editor: Editor?): Array<PsiElement>? {
if (sourceElement is LeafPsiElement && sourceElement.parent is KtSimpleNameExpression) {
val simpleNameExpression = sourceElement.parent as? KtSimpleNameExpression ?: return null
val layoutManager = getLayoutManager(sourceElement) ?: return null
val propertyDescriptor = resolvePropertyDescriptor(simpleNameExpression) ?: return null
val psiElements = layoutManager.propertyToXmlAttributes(propertyDescriptor)
val valueElements = psiElements.mapNotNull { (it as? XmlAttribute)?.valueElement as? PsiElement }
if (valueElements.isNotEmpty()) return valueElements.toTypedArray()
}
return null
}
private fun resolvePropertyDescriptor(simpleNameExpression: KtSimpleNameExpression): PropertyDescriptor? {
val bindingContext = simpleNameExpression.analyze(BodyResolveMode.PARTIAL)
val call = bindingContext[BindingContext.CALL, simpleNameExpression]
val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, call]
return resolvedCall?.resultingDescriptor as? PropertyDescriptor
}
private fun getLayoutManager(sourceElement: PsiElement): AndroidLayoutXmlFileManager? {
val moduleInfo = sourceElement.getModuleInfo()
if (moduleInfo !is ModuleSourceInfo) return null
return ModuleServiceManager.getService(moduleInfo.module, AndroidLayoutXmlFileManager::class.java)
}
override fun getActionText(context: DataContext?): String? {
return null
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.descriptors.AndroidSyntheticPackageFragmentDescriptor
import org.jetbrains.kotlin.android.synthetic.descriptors.PredefinedPackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.core.extension.KotlinIndicesHelperExtension
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class AndroidIndicesHelperExtension : KotlinIndicesHelperExtension {
override fun appendExtensionCallables(
consumer: MutableList<in CallableDescriptor>,
moduleDescriptor: ModuleDescriptor,
receiverTypes: Collection<KotlinType>,
nameFilter: (String) -> Boolean
) {
for (packageFragment in moduleDescriptor.getPackage(FqName(AndroidConst.SYNTHETIC_PACKAGE)).fragments) {
if (packageFragment !is PredefinedPackageFragmentDescriptor) continue
fun handleScope(scope: MemberScope) {
val descriptors = scope.getContributedDescriptors(DescriptorKindFilter.CALLABLES) { nameFilter(it.asString()) }
for (descriptor in descriptors) {
val receiverType = (descriptor as CallableDescriptor).extensionReceiverParameter?.type ?: continue
if (receiverTypes.any { it.isSubtypeOf(receiverType) }) {
consumer += descriptor
}
}
}
handleScope(packageFragment.getMemberScope())
for (fragment in packageFragment.subpackages) {
if (fragment is AndroidSyntheticPackageFragmentDescriptor && fragment.packageData.isDeprecated) continue
handleScope(fragment.getMemberScope())
}
}
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.ide.highlighter.XmlFileType
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlToken
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
class AndroidPsiTreeChangePreprocessor : PsiTreeChangePreprocessor, SimpleModificationTracker() {
override fun treeChanged(event: PsiTreeChangeEventImpl) {
if (event.code in HANDLED_EVENTS) {
val child = event.child
// We should get more precise event notification (not just "that file was changed somehow")
if (child == null && event.code == PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED) {
return
}
if (child != null && checkIfLayoutFile(child)) {
incModificationCount()
return
}
val file = event.file ?: return
if (!checkIfLayoutFile(file)) return
val xmlAttribute = findXmlAttribute(child)
if (xmlAttribute != null) {
val name = xmlAttribute.name
if (name != "android:id" && name != "class") return
}
incModificationCount()
}
}
private companion object {
private val HANDLED_EVENTS = setOf(
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED,
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED)
private fun checkIfLayoutFile(element: PsiElement): Boolean {
val xmlFile = element as? XmlFile ?: return false
val projectFileIndex = ProjectRootManager.getInstance(xmlFile.project).fileIndex
val module = projectFileIndex.getModuleForFile(xmlFile.virtualFile)
if (module != null) {
val resourceManager = AndroidLayoutXmlFileManager.getInstance(module) ?: return false
val resDirectories = resourceManager.getAllModuleResDirectories()
val baseDirectory = xmlFile.parent?.parent?.virtualFile
if (baseDirectory != null && baseDirectory in resDirectories && xmlFile.isLayoutXmlFile()) {
return true
}
}
return false
}
private fun findXmlAttribute(element: PsiElement?): XmlAttribute? {
return when (element) {
is XmlToken, is XmlAttributeValue -> findXmlAttribute(element.parent)
is XmlAttribute -> element
else -> null
}
}
private fun AndroidLayoutXmlFileManager.getAllModuleResDirectories(): List<VirtualFile> {
val module = androidModule ?: return listOf()
val fileManager = VirtualFileManager.getInstance()
return module.variants.fold(arrayListOf<VirtualFile>()) { list, variant ->
for (dir in variant.resDirectories) {
fileManager.findFileByUrl("file://$dir")?.let { list += it }
}
list
}
}
private fun PsiFile.isLayoutXmlFile(): Boolean {
if (fileType != XmlFileType.INSTANCE) return false
return parent?.name?.startsWith("layout") ?: false
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.psi.PsiElement
import com.intellij.psi.xml.XmlAttributeValue
import org.jetbrains.android.dom.wrappers.ValueResourceElementWrapper
import org.jetbrains.android.util.AndroidResourceUtil
import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.KtPsiFactory
class AndroidSimpleNameReferenceExtension : SimpleNameReferenceExtension {
override fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement): Boolean {
return element is ValueResourceElementWrapper && AndroidResourceUtil.isIdDeclaration(element)
}
override fun handleElementRename(reference: KtSimpleNameReference, psiFactory: KtPsiFactory, newElementName: String): PsiElement? {
val resolvedElement = reference.resolve()
if (resolvedElement !is XmlAttributeValue || !AndroidResourceUtil.isIdDeclaration(resolvedElement)) return null
val newSyntheticPropertyName = androidIdToName(newElementName) ?: return null
return psiFactory.createNameIdentifier(newSyntheticPropertyName)
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea
import com.intellij.psi.PsiElement
import com.intellij.psi.XmlElementVisitor
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlElement
import com.intellij.psi.xml.XmlTag
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.androidIdToName
import org.jetbrains.kotlin.android.synthetic.isWidgetTypeIgnored
class AndroidXmlVisitor(val elementCallback: (String, String, XmlAttribute) -> Unit) : XmlElementVisitor() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitXmlElement(element: XmlElement?) {
element?.acceptChildren(this)
}
override fun visitXmlTag(tag: XmlTag?) {
val localName = tag?.localName ?: ""
if (isWidgetTypeIgnored(localName)) {
tag?.acceptChildren(this)
return
}
val idAttribute = tag?.getAttribute(AndroidConst.ID_ATTRIBUTE)
if (idAttribute != null) {
val idAttributeValue = idAttribute.value
if (idAttributeValue != null) {
val xmlType = tag?.getAttribute(AndroidConst.CLASS_ATTRIBUTE_NO_NAMESPACE)?.value ?: localName
val name = androidIdToName(idAttributeValue)
if (name != null) elementCallback(name, xmlType, idAttribute)
}
}
tag?.acceptChildren(this)
}
}
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea.res
import com.android.builder.model.SourceProvider
import com.intellij.openapi.module.Module
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.android.synthetic.AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH
import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.res.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) {
override val androidModule: AndroidModule?
get() = module.androidFacet?.toAndroidModuleInfo()
@Volatile
private var _moduleData: CachedValue<AndroidModuleData>? = null
override fun getModuleData(): AndroidModuleData {
if (androidModule == null) {
_moduleData = null
}
else {
if (_moduleData == null) {
_moduleData = cachedValue(project) {
CachedValueProvider.Result.create(super.getModuleData(), getPsiTreeChangePreprocessor())
}
}
}
return _moduleData?.value ?: AndroidModuleData.EMPTY
}
private fun getPsiTreeChangePreprocessor(): PsiTreeChangePreprocessor {
return project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor }
}
override fun doExtractResources(files: List<PsiFile>, module: ModuleDescriptor): List<AndroidResource> {
val widgets = arrayListOf<AndroidResource>()
val visitor = AndroidXmlVisitor { id, widgetType, attribute ->
widgets += parseAndroidResource(id, widgetType, attribute.valueElement)
}
files.forEach { it.accept(visitor) }
return widgets
}
override fun propertyToXmlAttributes(propertyDescriptor: PropertyDescriptor): List<PsiElement> {
val fqPath = propertyDescriptor.fqNameUnsafe.pathSegments()
if (fqPath.size <= SYNTHETIC_PACKAGE_PATH_LENGTH) return listOf()
fun handle(variantData: AndroidVariantData, defaultVariant: Boolean = false): List<PsiElement>? {
val layoutNamePosition = SYNTHETIC_PACKAGE_PATH_LENGTH + (if (defaultVariant) 0 else 1)
val layoutName = fqPath[layoutNamePosition].asString()
val layoutFiles = variantData[layoutName] ?: return null
if (layoutFiles.isEmpty()) return null
val propertyName = propertyDescriptor.name.asString()
val attributes = arrayListOf<PsiElement>()
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
if (retId == propertyName) attributes.add(valueElement)
}
layoutFiles.forEach { it.accept(visitor) }
return attributes
}
for (variantData in getModuleData()) {
if (variantData.variant.isMainVariant && fqPath.size == SYNTHETIC_PACKAGE_PATH_LENGTH + 2) {
handle(variantData, true)?.let { return it }
}
else if (fqPath[SYNTHETIC_PACKAGE_PATH_LENGTH].asString() == variantData.variant.name) {
handle(variantData)?.let { return it }
}
}
return listOf()
}
private val Module.androidFacet: AndroidFacet?
get() = AndroidFacet.getInstance(this)
private fun SourceProvider.toVariant() = AndroidVariant(name, resDirectories.map { it.absolutePath })
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModule? {
val applicationPackage = manifest?.`package`?.toString()
if (applicationPackage != null) {
// This code is needed for compatibility with AS 2.0 and IDEA 15.0, because of difference in android plugins
val modelClass = try {
Class.forName("com.android.tools.idea.gradle.AndroidGradleModel")
}
catch(e: ClassNotFoundException) {
null
}
val mainVariant = mainSourceProvider.toVariant()
if (modelClass == null) {
val flavorVariants = flavorSourceProviders?.map { it.toVariant() } ?: listOf()
return AndroidModule(applicationPackage, listOf(mainVariant) + flavorVariants)
}
else {
val model = modelClass.getDeclaredMethod("get", Module::class.java).invoke(null, module)
if (model != null) {
val sourceProviders = modelClass.getDeclaredMethod("getFlavorSourceProviders").invoke(model) as List<SourceProvider>
return AndroidModule(applicationPackage, listOf(mainVariant) + sourceProviders.map { it.toVariant() })
}
}
}
return null
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.android.synthetic.idea.res
import com.intellij.openapi.module.ModuleServiceManager
import com.intellij.openapi.project.Project
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidPackageFragmentProviderExtension
import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo
class IDEAndroidPackageFragmentProviderExtension : AndroidPackageFragmentProviderExtension() {
override fun getLayoutXmlFileManager(project: Project, moduleInfo: ModuleInfo?): AndroidLayoutXmlFileManager? {
val moduleSourceInfo = moduleInfo as? ModuleSourceInfo ?: return null
val module = moduleSourceInfo.module
if (AndroidFacet.getInstance(module) == null) return null
return ModuleServiceManager.getService(module, AndroidLayoutXmlFileManager::class.java)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.plugin.android
import com.intellij.diagnostic.ITNReporter
import com.intellij.openapi.diagnostic.IdeaLoggingEvent
class AndroidExtensionsReportSubmitter : ITNReporter() {
override fun showErrorInRelease(event: IdeaLoggingEvent?) = true
}