diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt index 9a2b4dd1682..bb73f75be9a 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidComponentRegistrar.kt @@ -21,6 +21,10 @@ import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension +import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager +import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager +import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator +import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.compiler.plugin.CliOption import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException @@ -29,10 +33,6 @@ import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider -import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager -import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator -import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager -import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator import org.jetbrains.kotlin.psi.JetFile public object AndroidConfigurationKeys { @@ -70,8 +70,8 @@ public class AndroidCommandLineProcessor : CommandLineProcessor { public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider { override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection { - val parser = ServiceManager.getService(project, javaClass()) - return parser.getSyntheticFiles() ?: listOf() + val parser = ServiceManager.getService(project, javaClass()) as? CliSyntheticFileGenerator + return parser?.getSyntheticFiles() ?: listOf() } } diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt index e6928660ec5..81d7204478d 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/AndroidConst.kt @@ -55,6 +55,8 @@ public object AndroidConst { val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes()) .map { it as? JetKeywordToken }.filterNotNull().map { it.getValue() }.toSet() + + val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view") } public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name @@ -74,10 +76,13 @@ fun escapeAndroidIdentifier(id: String): String { return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id } -public fun parseAndroidResource(id: String, type: String): AndroidResource { - return when (type) { +public fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource { + return when (tag) { "fragment" -> AndroidFragment(id) - "include" -> AndroidWidget(id, "View") - else -> AndroidWidget(id, type) + "include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME) + else -> { + val fqName = fqNameResolver(tag) ?: AndroidConst.VIEW_FQNAME + AndroidWidget(id, fqName) + } } } \ No newline at end of file diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt index 90d041dbcab..bdc7b026abf 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/codegen/AndroidExpressionCodegenExtension.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.android.synthetic.codegen +import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.FunctionCodegen import org.jetbrains.kotlin.codegen.StackValue @@ -23,7 +24,6 @@ import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.JetTypeMapper import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt index b9580590167..8e6f6531124 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/CliSyntheticFileGenerator.kt @@ -16,23 +16,31 @@ package org.jetbrains.kotlin.android.synthetic.res +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project -import com.intellij.openapi.util.ModificationTracker +import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiFile -import com.intellij.psi.util.CachedValue -import com.intellij.psi.util.CachedValueProvider.Result import java.io.ByteArrayInputStream import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler import org.jetbrains.kotlin.android.synthetic.parseAndroidResource +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.psi.JetFile import kotlin.properties.Delegates -public class CliSyntheticFileGenerator( +public open class CliSyntheticFileGenerator( project: Project, private val manifestPath: String, private val resDirectories: List, private val supportV4: Boolean ) : SyntheticFileGenerator(project) { + private val javaPsiFacade: JavaPsiFacade by lazy { JavaPsiFacade.getInstance(project) } + private val projectScope: GlobalSearchScope by lazy { GlobalSearchScope.allScope(project) } + + private val cachedJetFiles by lazy { + generateSyntheticJetFiles(generateSyntheticFiles(true, projectScope)) + } + override fun supportV4(): Boolean { return supportV4 } @@ -41,27 +49,31 @@ public class CliSyntheticFileGenerator( CliAndroidLayoutXmlFileManager(project, manifestPath, resDirectories) } - override val cachedSources: CachedValue> by lazy { - cachedValue { - Result.create(generateSyntheticFiles(), ModificationTracker.NEVER_CHANGED) - } - } + public override fun getSyntheticFiles(): List = cachedJetFiles - override fun extractLayoutResources(files: List): List { + override fun extractLayoutResources(files: List, scope: GlobalSearchScope): List { val resources = arrayListOf() - val handler = AndroidXmlHandler { id, widgetType -> resources.add(parseAndroidResource(id, widgetType)) } - try { - for (file in files) { + val handler = AndroidXmlHandler { id, tag -> + resources += parseAndroidResource(id, tag) { tag -> + resolveFqClassNameForView(javaPsiFacade, scope, tag) + } + } + + for (file in files) { + try { val inputStream = ByteArrayInputStream(file.getVirtualFile().contentsToByteArray()) layoutXmlFileManager.saxParser.parse(inputStream, handler) + } catch (e: Throwable) { + LOG.error(e) } - return filterDuplicates(resources) - } - catch (e: Throwable) { - LOG.error(e) - return listOf() } + + return filterDuplicates(resources) + } + + private companion object { + private val LOG: Logger = Logger.getInstance(javaClass) } } diff --git a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt index 5f6a3e9052c..c57f629eea4 100644 --- a/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt +++ b/plugins/android-compiler-plugin/src/org/jetbrains/kotlin/android/synthetic/res/SyntheticFileGenerator.kt @@ -16,10 +16,11 @@ package org.jetbrains.kotlin.android.synthetic.res -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider.Result import com.intellij.psi.util.CachedValuesManager @@ -36,56 +37,20 @@ public abstract class SyntheticFileGenerator(protected val project: Project) { public class NoAndroidManifestFound : Exception("No android manifest file found in project root") - protected val LOG: Logger = Logger.getInstance(javaClass) - public abstract val layoutXmlFileManager: AndroidLayoutXmlFileManager - protected abstract val cachedSources: CachedValue> - - private val cachedJetFiles: CachedValue> by lazy { - cachedValue { - val psiManager = PsiManager.getInstance(project) - val applicationPackage = layoutXmlFileManager.androidModuleInfo?.applicationPackage - - val jetFiles = cachedSources.value.mapIndexed { index, syntheticFile -> - val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt" - val virtualFile = LightVirtualFile(fileName, syntheticFile.contents) - val jetFile = psiManager.findFile(virtualFile) as JetFile - if (applicationPackage != null) { - jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage) - } - jetFile - } - - Result.create(jetFiles, cachedSources) - } - } - protected abstract fun supportV4(): Boolean - public fun generateSyntheticFiles(generateCommonFiles: Boolean = true): List { - val commonFiles = if (generateCommonFiles) { - val renderSyntheticFile = renderSyntheticFile("clearCache") { - writePackage(AndroidConst.SYNTHETIC_PACKAGE) - writeAndroidImports() - writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME) - writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME) - if (supportV4()) writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME) - } - val clearCacheFile = renderSyntheticFile + public abstract fun getSyntheticFiles(): List - listOf(clearCacheFile, - FLEXIBLE_TYPE_FILE, - FAKE_SUPPORT_V4_APP_FILE, - FAKE_SUPPORT_V4_VIEW_FILE, - FAKE_SUPPORT_V4_WIDGET_FILE) - } else listOf() + protected fun generateSyntheticFiles(generateCommonFiles: Boolean = true, scope: GlobalSearchScope): List { + val commonFiles = if (generateCommonFiles) generateCommonFiles() else listOf() return layoutXmlFileManager.getLayoutXmlFiles().flatMap { entry -> val files = entry.getValue() - val resources = extractLayoutResources(files) + val resources = extractLayoutResources(files, scope) - val layoutName = files[0].name.substringBefore('.') + val layoutName = entry.getKey() val mainLayoutFile = renderMainLayoutFile(layoutName, resources) val viewLayoutFile = renderViewLayoutFile(layoutName, resources) @@ -94,9 +59,25 @@ public abstract class SyntheticFileGenerator(protected val project: Project) { }.filterNotNull() + commonFiles } - public fun getSyntheticFiles(): List? = cachedJetFiles.value + private fun generateCommonFiles(): List { + val renderSyntheticFile = renderSyntheticFile("clearCache") { + writePackage(AndroidConst.SYNTHETIC_PACKAGE) + writeAndroidImports() + writeClearCacheFunction(AndroidConst.ACTIVITY_FQNAME) + writeClearCacheFunction(AndroidConst.FRAGMENT_FQNAME) + if (supportV4()) writeClearCacheFunction(AndroidConst.SUPPORT_FRAGMENT_FQNAME) + } + val clearCacheFile = renderSyntheticFile - protected abstract fun extractLayoutResources(files: List): List + return listOf( + clearCacheFile, + FLEXIBLE_TYPE_FILE, + FAKE_SUPPORT_V4_APP_FILE, + FAKE_SUPPORT_V4_VIEW_FILE, + FAKE_SUPPORT_V4_WIDGET_FILE) + } + + protected abstract fun extractLayoutResources(files: List, scope: GlobalSearchScope): List private fun renderMainLayoutFile(layoutName: String, resources: List): AndroidSyntheticFile { return renderLayoutFile(layoutName + AndroidConst.LAYOUT_POSTFIX, @@ -140,13 +121,13 @@ public abstract class SyntheticFileGenerator(protected val project: Project) { writeEmptyLine() } - private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, widget: AndroidResource, stubCall: String) { + private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) { // extract startsWith() to fun - val className = if (isFromSupportV4Package(receiver)) widget.supportClassName else widget.className - val cast = if (widget.className != "View") " as? $className" else "" + val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className + val cast = if (resource.className != "View") " as? $className" else "" val body = arrayListOf("return $stubCall$cast") writeImmutableExtensionProperty(receiver, - name = widget.id, + name = resource.id, retType = "$EXPLICIT_FLEXIBLE_CLASS_NAME<$className, $className?>", getterBody = body) } @@ -163,6 +144,22 @@ public abstract class SyntheticFileGenerator(protected val project: Project) { return fqName.startsWith(AndroidConst.SUPPORT_V4_PACKAGE) } + protected fun resolveFqClassNameForView(javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope, tag: String): String? { + if (tag.contains('.')) { + if (javaPsiFacade.findClass(tag, scope) == null) { + return null + } + return tag + } + for (pkg in AndroidConst.FQNAME_RESOLVE_PACKAGES) { + val fqName = "$pkg.$tag" + if (javaPsiFacade.findClass(fqName, scope) != null) { + return fqName + } + } + return null + } + protected fun filterDuplicates(resources: List): List { val resourceMap = linkedMapOf() val resourcesToExclude = hashSetOf() @@ -184,6 +181,21 @@ public abstract class SyntheticFileGenerator(protected val project: Project) { return resourceMap.values().toList() } + protected fun generateSyntheticJetFiles(files: List): List { + val psiManager = PsiManager.getInstance(project) + val applicationPackage = layoutXmlFileManager.androidModuleInfo?.applicationPackage + + return files.mapIndexed { index, syntheticFile -> + val fileName = AndroidConst.SYNTHETIC_FILENAME_PREFIX + syntheticFile.name + ".kt" + val virtualFile = LightVirtualFile(fileName, syntheticFile.contents) + val jetFile = psiManager.findFile(virtualFile) as JetFile + if (applicationPackage != null) { + jetFile.putUserData(AndroidConst.ANDROID_USER_PACKAGE, applicationPackage) + } + jetFile + } + } + companion object { private val EXPLICIT_FLEXIBLE_PACKAGE = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.packageFqName.asString() private val EXPLICIT_FLEXIBLE_CLASS_NAME = Flexibility.FLEXIBLE_TYPE_CLASSIFIER.relativeClassName.asString() diff --git a/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/AbstractAndroidXml2KConversionTest.kt b/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/AbstractAndroidXml2KConversionTest.kt index eb9bbcbb36c..252cb36ec91 100644 --- a/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/AbstractAndroidXml2KConversionTest.kt +++ b/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/AbstractAndroidXml2KConversionTest.kt @@ -16,17 +16,18 @@ package org.jetbrains.kotlin.lang.resolve.android.test +import com.intellij.openapi.module.ModuleManager import com.intellij.testFramework.UsefulTestCase -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import java.io.File -import org.jetbrains.kotlin.test.JetTestUtils -import org.jetbrains.kotlin.test.ConfigurationKind -import org.jetbrains.kotlin.test.TestJdkKind -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.android.synthetic.AndroidConst -import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator -import kotlin.test.* +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.JetTestUtils +import org.jetbrains.kotlin.test.TestJdkKind +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.fail public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() { @@ -35,10 +36,14 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() { val jetCoreEnvironment = getEnvironment() val layoutPaths = getResPaths(path) - val parser = CliSyntheticFileGenerator(jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths) - parser.supportV4 = testDirectory.name.startsWith("support") + val supportV4 = testDirectory.name.startsWith("support") + val parser = CliSyntheticFileGeneratorForConversionTest( + jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths, supportV4) - val actual = parser.generateSyntheticFiles(false).toMap { it.name } + val module = ModuleManager.getInstance(jetCoreEnvironment.project).modules.first() + val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(false) + + val actual = parser.gen(moduleScope).toMap { it.name } val expectedLayoutFiles = testDirectory.listFiles { it.isFile() && it.name.endsWith(".kt") diff --git a/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/CompilerTestUtils.kt b/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/CompilerTestUtils.kt index 9e62dbef34d..8469347fcae 100644 --- a/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/CompilerTestUtils.kt +++ b/plugins/android-compiler-plugin/tests/org/jetbrains/kotlin/lang/resolve/android/test/CompilerTestUtils.kt @@ -16,20 +16,19 @@ package org.jetbrains.kotlin.lang.resolve.android.test -import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.project.Project -import com.intellij.psi.impl.PsiTreeChangePreprocessor -import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider -import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys -import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension -import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension +import com.intellij.psi.search.GlobalSearchScope import com.intellij.testFramework.UsefulTestCase import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.psi.JetFile -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.android.synthetic.AndroidConfigurationKeys +import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider +import org.jetbrains.kotlin.psi.JetFile import java.io.File private class AndroidTestExternalDeclarationsProvider( @@ -39,12 +38,20 @@ private class AndroidTestExternalDeclarationsProvider( val supportV4: Boolean ) : ExternalDeclarationsProvider { override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection { - val parser = CliSyntheticFileGenerator(project, manifestPath, resPaths) - parser.supportV4 = supportV4 - return parser.getSyntheticFiles() ?: listOf() + val parser = CliSyntheticFileGeneratorForConversionTest(project, manifestPath, resPaths, supportV4) + return parser.getSyntheticFiles() } } +class CliSyntheticFileGeneratorForConversionTest( + project: Project, + manifestPath: String, + resDirectories: List, + supportV4: Boolean +) : CliSyntheticFileGenerator(project, manifestPath, resDirectories, supportV4) { + fun gen(scope: GlobalSearchScope) = generateSyntheticFiles(false, scope) +} + fun UsefulTestCase.createAndroidTestEnvironment( configuration: CompilerConfiguration, resPaths: List, diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExternalDeclarationsProvider.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExternalDeclarationsProvider.kt index 01e67011ef5..35e6cc93a7f 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExternalDeclarationsProvider.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/IDEAndroidExternalDeclarationsProvider.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.android.synthetic.idea import com.intellij.openapi.module.ModuleServiceManager import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.android.synthetic.idea.res.IDESyntheticFileGenerator import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo @@ -30,8 +31,8 @@ public class IDEAndroidExternalDeclarationsProvider(private val project: Project if (moduleInfo !is ModuleSourceInfo) return listOf() val module = moduleInfo.module - val parser = ModuleServiceManager.getService(module, javaClass())!! - val syntheticFiles = parser.getSyntheticFiles() + val parser = ModuleServiceManager.getService(module, javaClass()) as? IDESyntheticFileGenerator + val syntheticFiles = parser?.getSyntheticFiles() syntheticFiles?.forEach { it.moduleInfo = moduleInfo } return syntheticFiles ?: listOf() diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt index 9cdfca19f0f..f79ed249dad 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDEAndroidLayoutXmlFileManager.kt @@ -21,11 +21,11 @@ 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.AndroidModuleInfo import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager +import org.jetbrains.kotlin.android.synthetic.res.AndroidModuleInfo import org.jetbrains.kotlin.psi.JetProperty -public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.getProject()) { +public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutXmlFileManager(module.project) { override val androidModuleInfo: AndroidModuleInfo? by lazy { module.androidFacet?.toAndroidModuleInfo() } @@ -37,7 +37,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX val layoutFiles = getLayoutXmlFiles()[layoutPackageName] if (layoutFiles == null || layoutFiles.isEmpty()) return listOf() - val propertyName = property.getName() + val propertyName = property.name val attributes = arrayListOf() val visitor = AndroidXmlVisitor { retId, wClass, valueElement -> @@ -53,7 +53,7 @@ public class IDEAndroidLayoutXmlFileManager(val module: Module) : AndroidLayoutX private fun AndroidFacet.toAndroidModuleInfo(): AndroidModuleInfo? { val applicationPackage = manifest?.getPackage()?.toString() - val mainResDirectories = getAllResourceDirectories().map { it.getPath() } + val mainResDirectories = allResourceDirectories.map { it.path } return if (applicationPackage != null) { AndroidModuleInfo(applicationPackage, mainResDirectories) diff --git a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDESyntheticFileGenerator.kt b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDESyntheticFileGenerator.kt index a5b940e9ec0..ce87c4464ac 100644 --- a/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDESyntheticFileGenerator.kt +++ b/plugins/android-idea-plugin/src/org/jetbrains/kotlin/android/synthetic/idea/res/IDESyntheticFileGenerator.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.module.Module import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiFile import com.intellij.psi.impl.PsiTreeChangePreprocessor +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider.Result import org.jetbrains.kotlin.android.synthetic.AndroidConst @@ -27,17 +28,24 @@ import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreproces import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor import org.jetbrains.kotlin.android.synthetic.parseAndroidResource import org.jetbrains.kotlin.android.synthetic.res.AndroidResource -import org.jetbrains.kotlin.android.synthetic.res.AndroidSyntheticFile import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator +import org.jetbrains.kotlin.psi.JetFile class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(module.project) { private val supportV4: Boolean + private val javaPsiFacade: JavaPsiFacade by lazy { JavaPsiFacade.getInstance(module.project) } + private val moduleScope: GlobalSearchScope by lazy { module.getModuleWithDependenciesAndLibrariesScope(false) } + + private val cachedJetFiles: CachedValue> by lazy { + cachedValue { + Result.create(generateSyntheticJetFiles(generateSyntheticFiles(true, moduleScope)), psiTreeChangePreprocessor) + } + } + init { - val scope = module.getModuleWithDependenciesAndLibrariesScope(false) - supportV4 = JavaPsiFacade.getInstance(module.getProject()) - .findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, scope).isNotEmpty() + supportV4 = javaPsiFacade.findClasses(AndroidConst.SUPPORT_FRAGMENT_FQNAME, moduleScope).isNotEmpty() } override fun supportV4() = supportV4 @@ -48,16 +56,14 @@ class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(mod module.project.getExtensions(PsiTreeChangePreprocessor.EP_NAME).first { it is AndroidPsiTreeChangePreprocessor } } - override val cachedSources: CachedValue> by lazy { - cachedValue { - Result.create(generateSyntheticFiles(), psiTreeChangePreprocessor) - } - } + public override fun getSyntheticFiles(): List = cachedJetFiles.value - override fun extractLayoutResources(files: List): List { + override fun extractLayoutResources(files: List, scope: GlobalSearchScope): List { val widgets = arrayListOf() val visitor = AndroidXmlVisitor { id, widgetType, attribute -> - widgets.add(parseAndroidResource(id, widgetType)) + widgets += parseAndroidResource(id, widgetType) { + resolveFqClassNameForView(javaPsiFacade, scope, it) + } } files.forEach { it.accept(visitor) } diff --git a/plugins/android-idea-plugin/tests/org/jetbrains/kotlin/android/AbstractParserResultEqualityTest.kt b/plugins/android-idea-plugin/tests/org/jetbrains/kotlin/android/AbstractParserResultEqualityTest.kt index 94edf82ce45..4fdd161d83a 100644 --- a/plugins/android-idea-plugin/tests/org/jetbrains/kotlin/android/AbstractParserResultEqualityTest.kt +++ b/plugins/android-idea-plugin/tests/org/jetbrains/kotlin/android/AbstractParserResultEqualityTest.kt @@ -16,10 +16,10 @@ package org.jetbrains.kotlin.android -import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator 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.CliSyntheticFileGenerator import kotlin.test.assertEquals public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() { @@ -31,11 +31,11 @@ public abstract class AbstractParserResultEqualityTest : KotlinAndroidTestCase() "$path${it.name}/" } - val cliParser = CliSyntheticFileGenerator(project, path + "../AndroidManifest.xml", resDirs, false) + val cliParser = CliSyntheticFileGenerator(project, "$path../AndroidManifest.xml", resDirs, false) val ideParser = IDESyntheticFileGenerator(ModuleManager.getInstance(project).getModules()[0]) - val cliResult = cliParser.getSyntheticFiles()!!.joinToString("\n\n") - val ideResult = ideParser.getSyntheticFiles()!!.joinToString("\n\n") + val cliResult = cliParser.getSyntheticFiles().joinToString("\n\n") + val ideResult = ideParser.getSyntheticFiles().joinToString("\n\n") assertEquals(cliResult, ideResult) }