Support Android variants in IDE

This commit is contained in:
Yan Zhulanow
2015-10-23 15:19:15 +03:00
parent 161d8b105f
commit 9112dd632a
40 changed files with 93 additions and 65 deletions
@@ -76,10 +76,10 @@ public class AndroidPsiTreeChangePreprocessor : PsiTreeChangePreprocessor, Simpl
if (module != null) {
val resourceManager = AndroidLayoutXmlFileManager.getInstance(module) ?: return false
val resDirectories = resourceManager.getModuleResDirectories()
val resDirectories = resourceManager.getAllModuleResDirectories()
val baseDirectory = xmlFile.parent?.parent?.virtualFile
if (baseDirectory in resDirectories && xmlFile.isLayoutXmlFile()) {
if (baseDirectory != null && baseDirectory in resDirectories && xmlFile.isLayoutXmlFile()) {
return true
}
}
@@ -95,11 +95,16 @@ public class AndroidPsiTreeChangePreprocessor : PsiTreeChangePreprocessor, Simpl
}
}
private fun AndroidLayoutXmlFileManager.getModuleResDirectories(): List<VirtualFile> {
val info = androidModuleInfo ?: return listOf()
private fun AndroidLayoutXmlFileManager.getAllModuleResDirectories(): List<VirtualFile> {
val module = androidModule ?: return listOf()
val fileManager = VirtualFileManager.getInstance()
return info.resDirectories.map { fileManager.findFileByUrl("file://$it") }.filterNotNull()
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 {
@@ -16,47 +16,69 @@
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 org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.AndroidModuleInfo
import org.jetbrains.kotlin.android.synthetic.res.AndroidModule
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariantData
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.android.synthetic.AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH
public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) {
override val androidModuleInfo: AndroidModuleInfo? by lazy { module.androidFacet?.toAndroidModuleInfo() }
override val androidModule: AndroidModule? by lazy { module.androidFacet?.toAndroidModuleInfo() }
override fun propertyToXmlAttributes(property: KtProperty): List<PsiElement> {
val fqPath = property.fqName?.pathSegments() ?: return listOf()
if (fqPath.size() <= AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH) return listOf()
if (fqPath.size <= SYNTHETIC_PACKAGE_PATH_LENGTH) return listOf()
val layoutPackageName = fqPath[AndroidConst.SYNTHETIC_PACKAGE_PATH_LENGTH].asString()
val layoutFiles = getLayoutXmlFiles()[layoutPackageName]
if (layoutFiles == null || layoutFiles.isEmpty()) 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 propertyName = property.name
val layoutFiles = variantData[layoutName] ?: return null
if (layoutFiles.isEmpty()) return null
val attributes = arrayListOf<PsiElement>()
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
if (retId == propertyName) attributes.add(valueElement)
val propertyName = property.name
val attributes = arrayListOf<PsiElement>()
val visitor = AndroidXmlVisitor { retId, wClass, valueElement ->
if (retId == propertyName) attributes.add(valueElement)
}
layoutFiles.forEach { it.accept(visitor) }
return attributes
}
layoutFiles.forEach { it.accept(visitor) }
return attributes
for (variantData in getLayoutXmlFiles()) {
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 AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? {
val applicationPackage = manifest?.getPackage()?.toString()
val mainResDirectories = allResourceDirectories.map { it.path }
private fun SourceProvider.toVariant() = AndroidVariant(name, resDirectories.map { it.absolutePath })
private fun AndroidFacet.toAndroidModuleInfo(): AndroidModule? {
val applicationPackage = manifest?.`package`?.toString()
return if (applicationPackage != null) {
AndroidModuleInfo(applicationPackage, mainResDirectories)
val mainVariant = mainSourceProvider.toVariant()
val flavorVariants = flavorSourceProviders?.map { it.toVariant() } ?: listOf()
AndroidModule(applicationPackage, listOf(mainVariant) + flavorVariants)
}
else null
}
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyBu<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyBu<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyBu<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyBu<caret>
@@ -1,8 +1,8 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.layout1.*
import kotlinx.android.synthetic.main.layout.*
import kotlinx.android.synthetic.main.layout1.*
class MyActivity: Activity() {
val button = log<caret>
@@ -1,8 +1,8 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.layout1.*
import kotlinx.android.synthetic.main.layout.*
import kotlinx.android.synthetic.main.layout1.*
class MyFragment: Fragment() {
val button = log<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.layout.view.*
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
@@ -3,7 +3,7 @@ package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
@@ -2,7 +2,7 @@ package com.myapp
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyFragemnt : Fragment() {
override fun onResume() {}
@@ -1,7 +1,7 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.layout.view.*
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = login<caret>
@@ -3,7 +3,7 @@ package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
@@ -3,7 +3,7 @@ package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
@@ -2,7 +2,7 @@ package com.myapp
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyFragment : Fragment() {
override fun onResume() {}
@@ -1,7 +1,7 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.layout.view.*
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.MyButton<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Activity
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyActivity: Activity() {
val button = this.login<caret>
@@ -1,7 +1,7 @@
package com.myapp
import android.app.Fragment
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
class MyFragment: Fragment() {
val button = this.login<caret>
@@ -3,7 +3,7 @@ package com.myapp
import android.app.Activity
import android.os.Bundle
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {}
@@ -2,7 +2,7 @@ package com.myapp
import android.app.Fragment
import java.io.File
import kotlinx.android.synthetic.layout.*
import kotlinx.android.synthetic.main.layout.*
public class MyFragment : Fragment() {
override fun onResume() {}
@@ -1,7 +1,7 @@
package com.myapp
import android.view.View
import kotlinx.android.synthetic.layout.view.*
import kotlinx.android.synthetic.main.layout.view.*
fun View.a() {
val button = login<caret>
@@ -19,20 +19,21 @@ package org.jetbrains.kotlin.android
import com.intellij.openapi.module.ModuleManager
import org.jetbrains.kotlin.android.synthetic.idea.TestConst
import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator
import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
import kotlin.test.assertEquals
public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() {
public fun doTest(path: String) {
val project = myFixture.getProject()
val project = myFixture.project
project.putUserData(TestConst.TESTDATA_PATH, path)
val resDirs = getResourceDirs(path).map {
myFixture.copyDirectoryToProject(it.name, it.name)
"$path${it.name}/"
}
val cliParser = CliSyntheticFileGenerator(project, "$path../AndroidManifest.xml", resDirs)
val ideParser = IDESyntheticFileGenerator(ModuleManager.getInstance(project).getModules()[0])
val variants = listOf(AndroidVariant.createMainVariant(resDirs))
val cliParser = CliSyntheticFileGenerator(project, "$path../AndroidManifest.xml", variants)
val ideParser = IDESyntheticFileGenerator(ModuleManager.getInstance(project).modules[0])
val cliResult = cliParser.getSyntheticFiles().joinToString("\n\n")
val ideResult = ideParser.getSyntheticFiles().joinToString("\n\n")