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