Switch to 181 platform

This commit is contained in:
Vyacheslav Gerasimov
2018-04-27 18:25:17 +03:00
parent df59af8ee8
commit bc403ce744
673 changed files with 25853 additions and 922 deletions
+3 -3
View File
@@ -67,8 +67,8 @@ fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}):
dependsOn(":test-instrumenter:jar")
jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1100m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true")
maxHeapSize = "1100m"
jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1600m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true")
maxHeapSize = "1600m"
systemProperty("idea.is.unit.test", "true")
systemProperty("idea.home.path", intellijRootDir().canonicalPath)
environment("NO_FS_ROOTS_ACCESS_CHECK", "true")
@@ -120,4 +120,4 @@ fun Task.useAndroidSdk() {
fun Task.useAndroidJar() {
TaskUtils.useAndroidJar(this)
}
}
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2017 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.
*/
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.task
import org.gradle.kotlin.dsl.the
import org.gradle.kotlin.dsl.*
import java.lang.Character.isLowerCase
import java.lang.Character.isUpperCase
fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}): Test = getOrCreateTask(taskName) {
doFirst {
val patterns = filter.includePatterns + ((filter as? DefaultTestFilter)?.commandLineIncludePatterns ?: emptySet())
if (patterns.isEmpty() || patterns.any { '*' in it }) return@doFirst
patterns.forEach { pattern ->
val maybeMethodName = pattern.substringAfterLast('.')
val maybeClassFqName = if (maybeMethodName.isFirstChar(::isLowerCase))
pattern.substringBeforeLast('.')
else
pattern
if (!maybeClassFqName.substringAfterLast('.').isFirstChar(::isUpperCase)) {
return@forEach
}
val classFileNameWithoutExtension = maybeClassFqName.replace('.', '/')
val classFileName = classFileNameWithoutExtension + ".class"
include {
val path = it.path
if (it.isDirectory) {
classFileNameWithoutExtension.startsWith(path)
} else {
path == classFileName || (path.endsWith(".class") && path.startsWith(classFileNameWithoutExtension + "$"))
}
}
}
}
doFirst {
val agent = tasks.findByPath(":test-instrumenter:jar")!!.outputs.files.singleFile
val args = project.findProperty("kotlin.test.instrumentation.args")?.let { "=$it" }.orEmpty()
jvmArgs("-javaagent:$agent$args")
}
dependsOn(":test-instrumenter:jar")
jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1100m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true")
maxHeapSize = "1100m"
systemProperty("idea.is.unit.test", "true")
systemProperty("idea.home.path", intellijRootDir().canonicalPath)
environment("NO_FS_ROOTS_ACCESS_CHECK", "true")
environment("PROJECT_CLASSES_DIRS", the<JavaPluginConvention>().sourceSets.getByName("test").output.classesDirs.asPath)
environment("PROJECT_BUILD_DIR", buildDir)
systemProperty("jps.kotlin.home", rootProject.extra["distKotlinHomeDir"])
systemProperty("kotlin.ni", if (rootProject.hasProperty("newInferenceTests")) "true" else "false")
body()
}
private inline fun String.isFirstChar(f: (Char) -> Boolean) = isNotEmpty() && f(first())
inline fun <reified T : Task> Project.getOrCreateTask(taskName: String, body: T.() -> Unit): T =
(tasks.findByName(taskName)?.let { it as T } ?: task<T>(taskName)).apply { body() }
object TaskUtils {
fun useAndroidSdk(task: Task) {
task.useAndroidConfiguration(systemPropertyName = "android.sdk", configName = "androidSdk")
}
fun useAndroidJar(task: Task) {
task.useAndroidConfiguration(systemPropertyName = "android.jar", configName = "androidJar")
}
}
private fun Task.useAndroidConfiguration(systemPropertyName: String, configName: String) {
val configuration = with(project) {
configurations.getOrCreate(configName)
.also {
dependencies.add(
configName,
dependencies.project(":custom-dependencies:android-sdk", configuration = configName)
)
}
}
dependsOn(configuration)
if (this is Test) {
doFirst {
systemProperty(systemPropertyName, configuration.singleFile.canonicalPath)
}
}
}
fun Task.useAndroidSdk() {
TaskUtils.useAndroidSdk(this)
}
fun Task.useAndroidJar() {
TaskUtils.useAndroidJar(this)
}
@@ -395,7 +395,7 @@ class KotlinCoreEnvironment private constructor(
}
companion object {
private val ideaCompatibleBuildNumber = "173.1"
private val ideaCompatibleBuildNumber = "181.1"
init {
setCompatibleBuild()
@@ -0,0 +1,629 @@
/*
* Copyright 2010-2017 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.cli.jvm.compiler
import com.intellij.codeInsight.ContainerProvider
import com.intellij.codeInsight.ExternalAnnotationsManager
import com.intellij.codeInsight.InferredAnnotationsManager
import com.intellij.codeInsight.runner.JavaMainMethodProvider
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.CoreJavaFileManager
import com.intellij.core.JavaCoreApplicationEnvironment
import com.intellij.core.JavaCoreProjectEnvironment
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.lang.MetaLanguage
import com.intellij.lang.java.JavaParserDefinition
import com.intellij.lang.jvm.facade.JvmElementProvider
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.application.TransactionGuardImpl
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.extensions.ExtensionsArea
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.*
import com.intellij.openapi.vfs.impl.ZipHandler
import com.intellij.psi.FileContextProvider
import com.intellij.psi.JavaModuleSystem
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.PsiManager
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.augment.TypeAnnotationModifier
import com.intellij.psi.compiled.ClassFileDecompilers
import com.intellij.psi.impl.JavaClassSupersImpl
import com.intellij.psi.impl.PsiElementFinderImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy
import com.intellij.psi.impl.file.impl.JavaFileManager
import com.intellij.psi.meta.MetaDataContributor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.BinaryFileStubBuilders
import com.intellij.psi.util.JavaClassSupers
import com.intellij.util.io.URLUtil
import com.intellij.util.lang.UrlClassLoader
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider
import org.jetbrains.kotlin.cli.common.script.CliScriptDependenciesProvider
import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink
import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.index.*
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.extensions.PreprocessedVirtualFileFactoryExtension
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptReportSink
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.util.zip.ZipFile
class KotlinCoreEnvironment private constructor(
parentDisposable: Disposable,
applicationEnvironment: JavaCoreApplicationEnvironment,
initialConfiguration: CompilerConfiguration,
configFiles: EnvironmentConfigFiles
) {
private val projectEnvironment: JavaCoreProjectEnvironment = object : KotlinCoreProjectEnvironment(parentDisposable, applicationEnvironment) {
override fun preregisterServices() {
registerProjectExtensionPoints(Extensions.getArea(project))
}
override fun registerJavaPsiFacade() {
with (project) {
registerService(CoreJavaFileManager::class.java, ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager)
val traceHolder = CliTraceHolder()
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(traceHolder)
val kotlinAsJavaSupport = CliKotlinAsJavaSupport(this, traceHolder)
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
registerService(KotlinAsJavaSupport::class.java, kotlinAsJavaSupport)
registerService(CodeAnalyzerInitializer::class.java, traceHolder)
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
val area = Extensions.getArea(this)
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(JavaElementFinder(this, kotlinAsJavaSupport))
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(
PsiElementFinderImpl(this, ServiceManager.getService(this, JavaFileManager::class.java)))
}
super.registerJavaPsiFacade()
}
}
private val sourceFiles = mutableListOf<KtFile>()
private val rootsIndex: JvmDependenciesDynamicCompoundIndex
private val packagePartProviders = mutableListOf<JvmPackagePartProvider>()
private val classpathRootsResolver: ClasspathRootsResolver
private val initialRoots: List<JavaRoot>
val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy()
init {
PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize")
.apply { isAccessible = true }
.setInt(null, FileUtilRt.LARGE_FOR_CONTENT_LOADING)
val project = projectEnvironment.project
ExpressionCodegenExtension.registerExtensionPoint(project)
SyntheticResolveExtension.registerExtensionPoint(project)
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
AnalysisHandlerExtension.registerExtensionPoint(project)
PackageFragmentProviderExtension.registerExtensionPoint(project)
StorageComponentContainerContributor.registerExtensionPoint(project)
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
JsSyntheticTranslateExtension.registerExtensionPoint(project)
CompilerConfigurationExtension.registerExtensionPoint(project)
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
try {
registrar.registerProjectComponents(project, configuration)
} catch (e: AbstractMethodError) {
throw IllegalStateException("The provided plugin ${registrar.javaClass.name} is not compatible with this version of compiler", e)
}
}
project.registerService(DeclarationProviderFactoryService::class.java, CliDeclarationProviderFactoryService(sourceFiles))
project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES))
registerProjectServicesForCLI(projectEnvironment)
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
registerProjectServices(projectEnvironment, messageCollector)
CompilerConfigurationExtension.getInstances(project).forEach {
it.updateConfiguration(configuration)
}
sourceFiles += CompileEnvironmentUtil.getKtFiles(project, getSourceRootsCheckingForDuplicates(), this.configuration, {
message ->
report(ERROR, message)
})
sourceFiles.sortBy { it.virtualFile.path }
// We should always support at least the standard script definition
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
ScriptDependenciesProvider.getInstance(project).let { importsProvider ->
configuration.addJvmClasspathRoots(
sourceFiles.mapNotNull(importsProvider::getScriptDependencies)
.flatMap { it.classpath }
.distinctBy { it.absolutePath })
}
}
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
})
val outputDirectory =
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
classpathRootsResolver = ClasspathRootsResolver(
PsiManager.getInstance(project),
messageCollector,
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
this::contentRootToVirtualFile,
javaModuleFinder,
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
outputDirectory?.let(this::findLocalFile)
)
val (initialRoots, javaModules) =
classpathRootsResolver.convertClasspathRoots(configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS))
this.initialRoots = initialRoots
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
messageCollector,
configuration,
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
)
}
val (roots, singleJavaFileRoots) =
initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
// REPL and kapt2 update classpath dynamically
rootsIndex = JvmDependenciesDynamicCompoundIndex().apply {
addIndex(JvmDependenciesIndexImpl(roots))
updateClasspathFromRootsIndex(this)
}
(ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl).initialize(
rootsIndex,
packagePartProviders,
SingleJavaFileRootsIndex(singleJavaFileRoots),
configuration.getBoolean(JVMConfigurationKeys.USE_FAST_CLASS_FILES_READING)
)
project.registerService(
JavaModuleResolver::class.java,
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
)
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
project.registerService(MetadataFinderFactory::class.java, finderFactory)
project.registerService(VirtualFileFinderFactory::class.java, finderFactory)
project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, fun(roots: List<File>) {
updateClasspath(roots.map { JavaSourceRoot(it, null) })
})
}
fun createPackagePartProvider(scope: GlobalSearchScope): JvmPackagePartProvider {
return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply {
addRoots(initialRoots)
packagePartProviders += this
(ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this)
}
}
private val VirtualFile.javaFiles: List<VirtualFile>
get() = mutableListOf<VirtualFile>().apply {
VfsUtilCore.processFilesRecursively(this@javaFiles) { file ->
if (file.fileType == JavaFileType.INSTANCE) {
add(file)
}
true
}
}
private val allJavaFiles: List<File>
get() = configuration.javaSourceRoots
.mapNotNull(this::findLocalFile)
.flatMap { it.javaFiles }
.map { File(it.canonicalPath) }
fun registerJavac(
javaFiles: List<File> = allJavaFiles,
kotlinFiles: List<KtFile> = sourceFiles,
arguments: Array<String>? = null,
bootClasspath: List<File>? = null,
sourcePath: List<File>? = null
): Boolean {
return JavacWrapperRegistrar.registerJavac(projectEnvironment.project, configuration, javaFiles, kotlinFiles, arguments, bootClasspath, sourcePath, LightClassGenerationSupport.getInstance(project))
}
private val applicationEnvironment: CoreApplicationEnvironment
get() = projectEnvironment.environment
val project: Project
get() = projectEnvironment.project
internal fun countLinesOfCode(sourceFiles: List<KtFile>): Int =
sourceFiles.sumBy { sourceFile ->
val text = sourceFile.text
StringUtil.getLineBreakCount(text) + (if (StringUtil.endsWithLineBreak(text)) 0 else 1)
}
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
index.indexedRoots.forEach {
projectEnvironment.addSourcesToClasspath(it.file)
}
}
fun updateClasspath(contentRoots: List<ContentRoot>): List<File>? {
// TODO: add new Java modules to CliJavaModuleResolver
val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots
for (packagePartProvider in packagePartProviders) {
packagePartProvider.addRoots(newRoots)
}
return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex ->
updateClasspathFromRootsIndex(newIndex)
newIndex.indexedRoots.mapNotNull { (file) ->
VfsUtilCore.virtualToIoFile(VfsUtilCore.getVirtualFileForJar(file) ?: file)
}.toList()
}.orEmpty()
}
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? {
return when (root) {
is JvmClasspathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root)
is JvmModulePathRoot -> if (root.file.isFile) findJarRoot(root.file) else findLocalFile(root)
is JavaSourceRoot -> findLocalFile(root)
else -> throw IllegalStateException("Unexpected root: $root")
}
}
internal fun findLocalFile(path: String): VirtualFile? =
applicationEnvironment.localFileSystem.findFileByPath(path)
private fun findLocalFile(root: JvmContentRoot): VirtualFile? {
return findLocalFile(root.file.absolutePath).also {
if (it == null) {
report(STRONG_WARNING, "Classpath entry points to a non-existent location: ${root.file}")
}
}
}
private fun findJarRoot(file: File): VirtualFile? =
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
private fun getSourceRootsCheckingForDuplicates(): Collection<String> {
val uniqueSourceRoots = linkedSetOf<String>()
configuration.kotlinSourceRoots.forEach { path ->
if (!uniqueSourceRoots.add(path)) {
report(STRONG_WARNING, "Duplicate source root: $path")
}
}
return uniqueSourceRoots
}
fun getSourceFiles(): List<KtFile> = sourceFiles
private fun report(severity: CompilerMessageSeverity, message: String) {
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(severity, message)
}
companion object {
private val ideaCompatibleBuildNumber = "173.1"
init {
setCompatibleBuild()
}
private val APPLICATION_LOCK = Object()
private var ourApplicationEnvironment: JavaCoreApplicationEnvironment? = null
private var ourProjectCount = 0
@JvmStatic
fun createForProduction(
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
): KotlinCoreEnvironment {
setCompatibleBuild()
val appEnv = getOrCreateApplicationEnvironmentForProduction(configuration)
// Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally
// breaks a lot of tests, therefore it is disabled for production and enabled for tests
if (System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() != true) {
// JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
// All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
Disposer.register(parentDisposable, Disposable {
synchronized (APPLICATION_LOCK) {
if (--ourProjectCount <= 0) {
disposeApplicationEnvironment()
}
}
})
}
val environment = KotlinCoreEnvironment(parentDisposable, appEnv, configuration, configFiles)
synchronized (APPLICATION_LOCK) {
ourProjectCount++
}
return environment
}
@JvmStatic
private fun setCompatibleBuild() {
PluginManagerCore.BUILD_NUMBER = ideaCompatibleBuildNumber
System.getProperties().setProperty("idea.plugins.compatible.build", ideaCompatibleBuildNumber)
}
@TestOnly
@JvmStatic
fun createForTests(
parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
): KotlinCoreEnvironment {
val configuration = initialConfiguration.copy()
if (configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty()) {
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
}
// Tests are supposed to create a single project and dispose it right after use
return KotlinCoreEnvironment(
parentDisposable,
createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true),
configuration,
extensionConfigs
)
}
// used in the daemon for jar cache cleanup
val applicationEnvironment: JavaCoreApplicationEnvironment? get() = ourApplicationEnvironment
private fun getOrCreateApplicationEnvironmentForProduction(configuration: CompilerConfiguration): JavaCoreApplicationEnvironment {
synchronized (APPLICATION_LOCK) {
if (ourApplicationEnvironment != null)
return ourApplicationEnvironment!!
val parentDisposable = Disposer.newDisposable()
ourApplicationEnvironment = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = false)
ourProjectCount = 0
Disposer.register(parentDisposable, Disposable {
synchronized (APPLICATION_LOCK) {
ourApplicationEnvironment = null
}
})
return ourApplicationEnvironment!!
}
}
private fun disposeApplicationEnvironment() {
synchronized (APPLICATION_LOCK) {
val environment = ourApplicationEnvironment ?: return
ourApplicationEnvironment = null
Disposer.dispose(environment.parentDisposable)
ZipHandler.clearFileAccessorCache()
}
}
private fun createApplicationEnvironment(
parentDisposable: Disposable,
configuration: CompilerConfiguration,
unitTestMode: Boolean
): JavaCoreApplicationEnvironment {
Extensions.cleanRootArea(parentDisposable)
registerAppExtensionPoints()
val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable, unitTestMode) {
override fun createJrtFileSystem(): VirtualFileSystem? = CoreJrtFileSystem()
}
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
registerApplicationServicesForCLI(applicationEnvironment)
registerApplicationServices(applicationEnvironment)
return applicationEnvironment
}
private fun registerAppExtensionPoints() {
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), FileContextProvider.EP_NAME, FileContextProvider::class.java)
//
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaDataContributor.EP_NAME, MetaDataContributor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, PsiAugmentProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider::class.java)
//
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContainerProvider.EP_NAME, ContainerProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java)
//
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage::class.java)
//
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java)
}
private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) {
fun File.hasConfigFile(configFile: String): Boolean =
if (isDirectory) File(this, "META-INF" + File.separator + configFile).exists()
else try {
ZipFile(this).use {
it.getEntry("META-INF/" + configFile) != null
}
}
catch (e: Throwable) {
false
}
val pluginRoot =
configuration.get(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT)?.let(::File)
?: configuration.get(CLIConfigurationKeys.COMPILER_JAR_LOCATOR)?.compilerJar
?: PathUtil.getResourcePathForClass(this::class.java).takeIf { it.hasConfigFile(configFilePath) }
// hack for load extensions when compiler run directly from project directory (e.g. in tests)
?: File("idea/src").takeIf { it.hasConfigFile(configFilePath) }
?: throw IllegalStateException(
"Unable to find extension point configuration $configFilePath " +
"(cp:\n ${(Thread.currentThread().contextClassLoader as? UrlClassLoader)?.urls?.joinToString("\n ") { it.file }})")
CoreApplicationEnvironment.registerExtensionPointAndExtensions(pluginRoot, configFilePath, Extensions.getRootArea())
}
private fun registerApplicationServicesForCLI(applicationEnvironment: JavaCoreApplicationEnvironment) {
// ability to get text from annotations xml files
applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml")
applicationEnvironment.registerParserDefinition(JavaParserDefinition())
}
// made public for Upsource
@Suppress("MemberVisibilityCanPrivate")
@JvmStatic
fun registerApplicationServices(applicationEnvironment: JavaCoreApplicationEnvironment) {
with(applicationEnvironment) {
registerFileType(KotlinFileType.INSTANCE, "kt")
registerFileType(KotlinFileType.INSTANCE, KotlinParserDefinition.STD_SCRIPT_SUFFIX)
registerParserDefinition(KotlinParserDefinition())
application.registerService(KotlinBinaryClassCache::class.java, KotlinBinaryClassCache())
application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
application.registerService(TransactionGuard::class.java, TransactionGuardImpl::class.java)
}
}
private fun registerProjectExtensionPoints(area: ExtensionsArea) {
CoreApplicationEnvironment.registerExtensionPoint(area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP_NAME, PsiElementFinder::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, JvmElementProvider.EP_NAME, JvmElementProvider::class.java)
}
// made public for Upsource
@JvmStatic
fun registerProjectServices(projectEnvironment: JavaCoreProjectEnvironment, messageCollector: MessageCollector?) {
with (projectEnvironment.project) {
val scriptDefinitionProvider = CliScriptDefinitionProvider()
registerService(ScriptDefinitionProvider::class.java, scriptDefinitionProvider)
registerService(ScriptDependenciesProvider::class.java, CliScriptDependenciesProvider(projectEnvironment.project, scriptDefinitionProvider))
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this))
registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver())
if (messageCollector != null) {
registerService(ScriptReportSink::class.java, CliScriptReportSink(messageCollector))
}
}
}
private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) {
/**
* Note that Kapt may restart code analysis process, and CLI services should be aware of that.
* Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid.
*/
}
private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) {
if (getBoolean(JVMConfigurationKeys.NO_JDK)) return
val jvmTarget = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
if (!jvmTarget) return
val jdkHome = get(JVMConfigurationKeys.JDK_HOME)
val (javaRoot, classesRoots) = if (jdkHome == null) {
val javaHome = File(System.getProperty("java.home"))
put(JVMConfigurationKeys.JDK_HOME, javaHome)
javaHome to PathUtil.getJdkClassesRootsFromCurrentJre()
}
else {
jdkHome to PathUtil.getJdkClassesRoots(jdkHome)
}
if (!CoreJrtFileSystem.isModularJdk(javaRoot)) {
if (classesRoots.isEmpty()) {
val messageCollector = get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
messageCollector?.report(ERROR, "No class roots are found in the JDK path: $javaRoot")
}
else {
addJvmSdkRoots(classesRoots)
}
}
}
}
}
@@ -17,6 +17,7 @@ dependencies {
compileOnly(project(":compiler:daemon-common"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(commonDep("net.rubygrapefruit", "native-platform"))
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
embeddedComponents(project(":compiler:daemon-common")) { isTransitive = false }
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform"))
@@ -0,0 +1,47 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
description = "Kotlin Daemon Client"
plugins {
kotlin("jvm")
id("jps-compatible")
}
jvmTarget = "1.6"
val nativePlatformVariants: List<String> by rootProject.extra
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:cli-common"))
compileOnly(project(":compiler:daemon-common"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(commonDep("net.rubygrapefruit", "native-platform"))
embeddedComponents(project(":compiler:daemon-common")) { isTransitive = false }
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform"))
nativePlatformVariants.forEach {
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform", "-$it"))
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
noDefaultJar()
runtimeJar(task<ShadowJar>("shadowJar")) {
from(the<JavaPluginConvention>().sourceSets.getByName("main").output)
fromEmbeddedComponents()
}
sourcesJar()
javadocJar()
dist()
ideaPlugin()
publish()
@@ -22,9 +22,7 @@ import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
@@ -80,10 +78,7 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
val fakeFile = object : ClsFileImpl(files.first().viewProvider) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
@@ -121,10 +116,6 @@ private fun createJavaFileStub(project: Project, packageFqName: FqName, files: C
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2017 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.asJava.builder
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import com.intellij.psi.impl.source.tree.TreeElement
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import java.lang.StringBuilder
data class LightClassBuilderResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics)
fun buildLightClass(
packageFqName: FqName,
files: Collection<KtFile>,
generateClassFilter: GenerationState.GenerateClassFilter,
context: LightClassConstructionContext,
generate: (state: GenerationState, files: Collection<KtFile>) -> Unit
): LightClassBuilderResult {
val project = files.first().project
try {
val classBuilderFactory = KotlinLightClassBuilderFactory(createJavaFileStub(project, packageFqName, files))
val state = GenerationState.Builder(
project,
classBuilderFactory,
context.module,
context.bindingContext,
files.toList(),
CompilerConfiguration.EMPTY
).generateDeclaredClassFilter(generateClassFilter).wantsDiagnostics(false).build()
state.beforeCompile()
generate(state, files)
val javaFileStub = classBuilderFactory.result()
ServiceManager.getService(project, StubComputationTracker::class.java)?.onStubComputed(javaFileStub, context)
return LightClassBuilderResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics)
}
catch (e: ProcessCanceledException) {
throw e
}
catch (e: RuntimeException) {
logErrorWithOSInfo(e, packageFqName, null)
throw e
}
}
private fun createJavaFileStub(project: Project, packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /*compiled = */true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val manager = PsiManager.getInstance(project)
val virtualFile = getRepresentativeVirtualFile(files)
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
override fun isPhysical() = false
override fun appendMirrorText(indentLevel: Int, buffer: StringBuilder) {
if (files.size == 1) {
LOG.error("Mirror text should never be calculated for light classes generated from a single file")
}
super.appendMirrorText(indentLevel, buffer)
}
override fun setMirror(element: TreeElement) {
if (files.size == 1) {
LOG.error("Mirror element should never be set for light classes generated from a single file")
}
super.setMirror(element)
}
override fun getMirror(): PsiElement {
if (files.size == 1) {
LOG.error("Mirror element should never be calculated for light classes generated from a single file")
}
return super.getMirror()
}
override fun getText(): String {
return files.singleOrNull()?.text ?: super.getText()
}
}
javaFileStub.psi = fakeFile
return javaFileStub
}
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
return files.first().viewProvider.virtualFile
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = if (virtualFile == null) "<null>" else virtualFile.path
LOG.error(
"Could not generate LightClass for $fqName declared in $path\n" +
"System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}",
cause
)
}
private val LOG = Logger.getInstance(LightClassBuilderResult::class.java)
@@ -16,8 +16,8 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.compiled.ClsFileImpl
@@ -33,9 +33,11 @@ open class FakeFileForLightClass(
private val lightClass: () -> KtLightClass,
private val stub: () -> PsiClassHolderFileStub<*>,
private val packageFqName: FqName = ktFile.packageFqName
) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?:
ktFile.originalFile.virtualFile ?:
ktFile.viewProvider.virtualFile)) {
) : ClsFileImpl(ktFile.viewProvider) {
override fun getVirtualFile(): VirtualFile =
ktFile.virtualFile ?: ktFile.originalFile.virtualFile ?: super.getVirtualFile()
override fun getPackageName() = packageFqName.asString()
override fun getStub() = stub()
@@ -83,4 +85,4 @@ open class FakeFileForLightClass(
}
override fun isPhysical() = false
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2016 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.asJava.elements
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.ClassFileViewProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.stubs.PsiClassHolderFileStub
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
open class FakeFileForLightClass(
val ktFile: KtFile,
private val lightClass: () -> KtLightClass,
private val stub: () -> PsiClassHolderFileStub<*>,
private val packageFqName: FqName = ktFile.packageFqName
) : ClsFileImpl(ClassFileViewProvider(ktFile.manager, ktFile.virtualFile ?:
ktFile.originalFile.virtualFile ?:
ktFile.viewProvider.virtualFile)) {
override fun getPackageName() = packageFqName.asString()
override fun getStub() = stub()
override fun getClasses() = arrayOf(lightClass())
override fun getNavigationElement() = ktFile
override fun accept(visitor: PsiElementVisitor) {
// Prevent access to compiled PSI
// TODO: More complex traversal logic may be implemented when necessary
}
// this should be equal to current compiler target language level
override fun getLanguageLevel() = LanguageLevel.JDK_1_6
override fun hashCode(): Int {
val thisClass = lightClass()
if (thisClass is KtLightClassForSourceDeclaration) return ktFile.hashCode()
return thisClass.hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is FakeFileForLightClass) return false
val thisClass = lightClass()
val anotherClass = other.lightClass()
if (thisClass is KtLightClassForSourceDeclaration) {
return anotherClass is KtLightClassForSourceDeclaration && ktFile == other.ktFile
}
return thisClass == anotherClass
}
override fun isEquivalentTo(another: PsiElement?) = this == another
override fun setPackageName(packageName: String) {
if (lightClass() is KtLightClassForFacade) {
ktFile.packageDirective?.fqName = FqName(packageName)
}
else {
super.setPackageName(packageName)
}
}
override fun isPhysical() = false
}
@@ -95,12 +95,13 @@ class KtLightAnnotationForSourceEntry(
override fun getReferences() = originalExpression?.references.orEmpty()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getNavigationElement() = originalExpression
override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile
override fun isPhysical(): Boolean = false
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0
override fun getParent() = parent
override fun getText() = originalExpression?.text.orEmpty()
override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile
override fun getContainingFile(): PsiFile? = if (originalExpression?.containingFile == kotlinOrigin.containingFile)
kotlinOrigin.containingFile else delegate.containingFile
override fun replace(newElement: PsiElement): PsiElement {
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
@@ -250,8 +251,6 @@ class KtLightAnnotationForSourceEntry(
else -> LightElementValue(value, parent, ktOrigin)
}
override fun isPhysical() = true
override fun getName() = null
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, {
@@ -0,0 +1,380 @@
/*
* Copyright 2010-2016 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.asJava.elements
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import org.jetbrains.annotations.NotNull
import org.jetbrains.annotations.Nullable
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeUtils
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.asJava.elements.lightAnnotations")
abstract class KtLightAbstractAnnotation(parent: PsiElement, computeDelegate: () -> PsiAnnotation) :
KtLightElementBase(parent), PsiAnnotation, KtLightElement<KtCallElement, PsiAnnotation> {
override val clsDelegate by lazyPub(computeDelegate)
override fun getNameReferenceElement() = clsDelegate.nameReferenceElement
override fun getOwner() = parent as? PsiAnnotationOwner
override fun getMetaData() = clsDelegate.metaData
override fun getParameterList() = clsDelegate.parameterList
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
open fun fqNameMatches(fqName: String): Boolean = qualifiedName == fqName
}
private typealias AnnotationValueOrigin = () -> PsiElement?
class KtLightAnnotationForSourceEntry(
private val qualifiedName: String,
override val kotlinOrigin: KtCallElement,
parent: PsiElement,
computeDelegate: () -> PsiAnnotation
) : KtLightAbstractAnnotation(parent, computeDelegate) {
override fun getQualifiedName() = qualifiedName
open inner class LightElementValue<out D : PsiElement>(
val delegate: D,
private val parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : PsiAnnotationMemberValue, PsiCompiledElement, PsiElement by delegate {
override fun getMirror(): PsiElement = delegate
val originalExpression: PsiElement? by lazyPub(valueOrigin)
fun getConstantValue(): Any? {
val expression = originalExpression as? KtExpression ?: return null
val annotationEntry = this@KtLightAnnotationForSourceEntry.kotlinOrigin
val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry)
return context[BindingContext.COMPILE_TIME_VALUE, expression]?.getValue(TypeUtils.NO_EXPECTED_TYPE)
}
override fun getReference() = references.singleOrNull()
override fun getReferences() = originalExpression?.references.orEmpty()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun getNavigationElement() = originalExpression
override fun isPhysical(): Boolean = originalExpression?.containingFile == kotlinOrigin.containingFile
override fun getTextRange() = originalExpression?.textRange ?: TextRange.EMPTY_RANGE
override fun getStartOffsetInParent() = originalExpression?.startOffsetInParent ?: 0
override fun getParent() = parent
override fun getText() = originalExpression?.text.orEmpty()
override fun getContainingFile(): PsiFile? = if (isPhysical) kotlinOrigin.containingFile else delegate.containingFile
override fun replace(newElement: PsiElement): PsiElement {
val value = (newElement as? PsiLiteral)?.value as? String ?: return this
val origin = originalExpression
val exprToReplace =
if (origin is KtCallExpression /*arrayOf*/) {
unwrapArray(origin.valueArguments)
}
else {
origin as? KtExpression
} ?: return this
exprToReplace.replace(KtPsiFactory(this).createExpression("\"${StringUtil.escapeStringCharacters(value)}\""))
return this
}
}
private fun getMemberValueAsCallArgument(memberValue: PsiElement, callHolder: KtCallElement): PsiElement? {
val resolvedCall = callHolder.getResolvedCall() ?: return null
val annotationConstructor = resolvedCall.resultingDescriptor
val parameterName =
memberValue.getNonStrictParentOfType<PsiNameValuePair>()?.name ?:
memberValue.getNonStrictParentOfType<PsiAnnotationMethod>()?.takeIf { it.containingClass?.isAnnotationType == true }?.name ?:
"value"
val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName } ?: return null
val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return null
return when (resolvedArgument) {
is DefaultValueArgument -> {
val psi = parameter.source.getPsi()
when (psi) {
is KtParameter -> psi.defaultValue
is PsiAnnotationMethod -> psi.defaultValue
else -> error("$psi of type ${psi?.javaClass}")
}
}
is ExpressionValueArgument -> {
val argExpression = resolvedArgument.valueArgument?.getArgumentExpression()
argExpression?.asKtCall()
?: argExpression
?: error("resolvedArgument ($resolvedArgument) has no arg expression")
}
is VarargValueArgument ->
memberValue.unwrapArray(resolvedArgument.arguments)
?: resolvedArgument.arguments.first().asElement().let {
(it as? KtValueArgument)
?.takeIf {
it.getSpreadElement() != null ||
it.getArgumentName() != null ||
it.getArgumentExpression() is KtCollectionLiteralExpression
}
?.getArgumentExpression() ?: it.parent
}
else -> error("resolvedArgument: ${resolvedArgument.javaClass} cant be processed")
}
}
private fun PsiElement.unwrapArray(arguments: List<ValueArgument>): PsiElement? {
val arrayInitializer = parent as? PsiArrayInitializerMemberValue ?: return null
val exprIndex = arrayInitializer.initializers.indexOf(this)
if (exprIndex < 0 || exprIndex >= arguments.size) return null
return arguments[exprIndex].getArgumentExpression()
}
open inner class LightExpressionValue<out D : PsiExpression>(
delegate: D,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightElementValue<D>(delegate, parent, valueOrigin), PsiExpression {
override fun getType(): PsiType? = delegate.type
}
inner class LightPsiLiteral(
delegate: PsiLiteralExpression,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiLiteralExpression>(delegate, parent, valueOrigin), PsiLiteralExpression {
override fun getValue() = delegate.value
}
inner class LightClassLiteral(
delegate: PsiClassObjectAccessExpression,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightExpressionValue<PsiClassObjectAccessExpression>(delegate, parent, valueOrigin), PsiClassObjectAccessExpression {
override fun getType() = delegate.type
override fun getOperand(): PsiTypeElement = delegate.operand
}
inner class LightArrayInitializerValue(
delegate: PsiArrayInitializerMemberValue,
parent: PsiElement,
valueOrigin: AnnotationValueOrigin
) : LightElementValue<PsiArrayInitializerMemberValue>(delegate, parent, valueOrigin), PsiArrayInitializerMemberValue {
private val _initializers by lazyPub {
delegate.initializers.mapIndexed { i, memberValue ->
wrapAnnotationValue(memberValue, this, {
originalExpression.let { ktOrigin ->
when {
ktOrigin is KtCallExpression
&& memberValue is PsiAnnotation
&& isAnnotationConstructorCall(ktOrigin, memberValue) -> ktOrigin
ktOrigin is KtValueArgumentList -> ktOrigin.arguments.getOrNull(i)?.getArgumentExpression()
ktOrigin is KtCallElement -> ktOrigin.valueArguments.getOrNull(i)?.getArgumentExpression()
ktOrigin is KtCollectionLiteralExpression -> ktOrigin.getInnerExpressions().getOrNull(i)
delegate.initializers.size == 1 -> ktOrigin
else -> null
}.also {
if (it == null)
LOG.error("error wrapping ${memberValue.javaClass} for ${ktOrigin?.javaClass} in ${ktOrigin?.containingFile}",
Attachment(
"source_fragments.txt",
"origin: '${psiReport(ktOrigin)}', delegate: ${psiReport(delegate)}, parent: ${psiReport(parent)}"
)
)
}
}
})
}.toTypedArray()
}
override fun getInitializers() = _initializers
}
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue, parent: PsiElement, ktOrigin: AnnotationValueOrigin): PsiAnnotationMemberValue =
when {
value is PsiLiteralExpression -> LightPsiLiteral(value, parent, ktOrigin)
value is PsiClassObjectAccessExpression -> LightClassLiteral(value, parent, ktOrigin)
value is PsiExpression -> LightExpressionValue(value, parent, ktOrigin)
value is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value, parent, ktOrigin)
value is PsiAnnotation -> {
val origin = ktOrigin()
val ktCallElement = origin?.asKtCall()
val qualifiedName = value.qualifiedName
if (qualifiedName != null && ktCallElement != null)
KtLightAnnotationForSourceEntry(qualifiedName, ktCallElement, parent, { value })
else {
LOG.error("can't convert ${origin?.javaClass} to KtCallElement in ${origin?.containingFile} (value = ${value.javaClass})",
Attachment("source_fragments.txt", "origin: '${psiReport(origin)}', value: '${psiReport(value)}'"))
LightElementValue(value, parent, ktOrigin) // or maybe create a LightErrorAnnotationMemberValue instead?
}
}
else -> LightElementValue(value, parent, ktOrigin)
}
override fun isPhysical() = true
override fun getName() = null
private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue = wrapAnnotationValue(value, this, {
getMemberValueAsCallArgument(value, kotlinOrigin)
})
override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) }
override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) }
override fun getParameterList(): PsiAnnotationParameterList = KtLightAnnotationParameterList(super.getParameterList())
inner class KtLightAnnotationParameterList(private val list: PsiAnnotationParameterList) : KtLightElementBase(this),
PsiAnnotationParameterList {
override val kotlinOrigin get() = null
override fun getAttributes(): Array<PsiNameValuePair> = list.attributes.map { KtLightPsiNameValuePair(it) }.toTypedArray()
inner class KtLightPsiNameValuePair(private val psiNameValuePair: PsiNameValuePair) : KtLightElementBase(this),
PsiNameValuePair {
override fun setValue(newValue: PsiAnnotationMemberValue): PsiAnnotationMemberValue = psiNameValuePair.setValue(newValue)
override fun getNameIdentifier(): PsiIdentifier? = psiNameValuePair.nameIdentifier
override fun getLiteralValue(): String? = (value as? PsiLiteralValue)?.value?.toString()
override val kotlinOrigin: KtElement? = null
override fun getValue(): PsiAnnotationMemberValue? = psiNameValuePair.value?.let { wrapAnnotationValue(it) }
}
}
override fun delete() = kotlinOrigin.delete()
override fun toString() = "@$qualifiedName"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || other::class.java != this::class.java) return false
return kotlinOrigin == (other as KtLightAnnotationForSourceEntry).kotlinOrigin
}
override fun hashCode() = kotlinOrigin.hashCode()
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
}
class KtLightNonSourceAnnotation(
parent: PsiElement, clsDelegate: PsiAnnotation
) : KtLightAbstractAnnotation(parent, { clsDelegate }) {
override val kotlinOrigin: KtAnnotationEntry? get() = null
override fun getQualifiedName() = clsDelegate.qualifiedName
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun findAttributeValue(attributeName: String?) = clsDelegate.findAttributeValue(attributeName)
override fun findDeclaredAttributeValue(attributeName: String?) = clsDelegate.findDeclaredAttributeValue(attributeName)
}
class KtLightNonExistentAnnotation(parent: KtLightElement<*, *>) : KtLightElementBase(parent), PsiAnnotation {
override val kotlinOrigin get() = null
override fun toString() = this.javaClass.name
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun getNameReferenceElement() = null
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName() = null
override fun getOwner() = parent as? PsiAnnotationOwner
override fun findDeclaredAttributeValue(attributeName: String?) = null
override fun getMetaData() = null
override fun getParameterList() = KtLightEmptyAnnotationParameterList(this)
override fun canNavigate(): Boolean = super<KtLightElementBase>.canNavigate()
override fun canNavigateToSource(): Boolean = super<KtLightElementBase>.canNavigateToSource()
override fun navigate(requestFocus: Boolean) = super<KtLightElementBase>.navigate(requestFocus)
}
class KtLightEmptyAnnotationParameterList(parent: PsiElement) : KtLightElementBase(parent), PsiAnnotationParameterList {
override val kotlinOrigin get() = null
override fun getAttributes(): Array<PsiNameValuePair> = emptyArray()
}
class KtLightNullabilityAnnotation(member: KtLightElement<*, PsiModifierListOwner>, parent: PsiElement) : KtLightAbstractAnnotation(parent, {
// searching for last because nullability annotations are generated after backend generates source annotations
member.clsDelegate.modifierList?.annotations?.findLast {
isNullabilityAnnotation(it.qualifiedName)
} ?: KtLightNonExistentAnnotation(member)
}) {
override fun fqNameMatches(fqName: String): Boolean {
if (!isNullabilityAnnotation(fqName)) return false
return super.fqNameMatches(fqName)
}
override val kotlinOrigin get() = null
override fun <T : PsiAnnotationMemberValue?> setDeclaredAttributeValue(attributeName: String?, value: T?) = cannotModify()
override fun findAttributeValue(attributeName: String?) = null
override fun getQualifiedName(): String? = clsDelegate.qualifiedName
override fun findDeclaredAttributeValue(attributeName: String?) = null
}
internal fun isNullabilityAnnotation(qualifiedName: String?) = qualifiedName in backendNullabilityAnnotations
private val backendNullabilityAnnotations = arrayOf(Nullable::class.java.name, NotNull::class.java.name)
private fun KtElement.getResolvedCall(): ResolvedCall<out CallableDescriptor>? {
val context = LightClassGenerationSupport.getInstance(this.project).analyze(this)
return this.getResolvedCall(context)
}
private fun isAnnotationConstructorCall(callExpression: KtCallExpression, psiAnnotation: PsiAnnotation) =
(callExpression.getResolvedCall()?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass?.fqNameUnsafe?.toString() == psiAnnotation.qualifiedName
private fun PsiElement.asKtCall(): KtCallElement? = (this as? KtElement)?.getResolvedCall()?.call?.callElement as? KtCallElement
private fun psiReport(psiElement: PsiElement?): String {
if (psiElement == null) return "null"
val text = try {
psiElement.text
}
catch (e: Exception) {
"${e.javaClass.simpleName}:${e.message}"
}
return "${psiElement.javaClass.canonicalName}[$text]"
}
@@ -137,3 +137,6 @@ fun KtClassOrObject.getOrCreateBody(): KtClassBody {
if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody
return add(newBody) as KtClassBody
}
val KtClassOrObject.allConstructors
get() = listOfNotNull(primaryConstructor) + secondaryConstructors
@@ -0,0 +1,139 @@
/*
* 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.psi
import com.intellij.lang.ASTNode
import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.CheckUtil
import com.intellij.psi.stubs.IStubElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
abstract class KtClassOrObject :
KtTypeParameterListOwnerStub<KotlinClassOrObjectStub<out KtClassOrObject>>, KtDeclarationContainer, KtNamedDeclaration,
KtPureClassOrObject {
constructor(node: ASTNode) : super(node)
constructor(stub: KotlinClassOrObjectStub<out KtClassOrObject>, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
fun getSuperTypeList(): KtSuperTypeList? = getStubOrPsiChild(KtStubElementTypes.SUPER_TYPE_LIST)
override fun getSuperTypeListEntries(): List<KtSuperTypeListEntry> = getSuperTypeList()?.entries.orEmpty()
fun addSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry): KtSuperTypeListEntry {
getSuperTypeList()?.let {
val single = it.entries.singleOrNull()
if (single != null && single.typeReference?.typeElement == null) {
return single.replace(superTypeListEntry) as KtSuperTypeListEntry
}
return EditCommaSeparatedListHelper.addItem(it, superTypeListEntries, superTypeListEntry)
}
val psiFactory = KtPsiFactory(this)
val specifierListToAdd = psiFactory.createSuperTypeCallEntry("A()").replace(superTypeListEntry).parent
val colon = addBefore(psiFactory.createColon(), getBody())
return (addAfter(specifierListToAdd, colon) as KtSuperTypeList).entries.first()
}
fun removeSuperTypeListEntry(superTypeListEntry: KtSuperTypeListEntry) {
val specifierList = getSuperTypeList() ?: return
assert(superTypeListEntry.parent === specifierList)
if (specifierList.entries.size > 1) {
EditCommaSeparatedListHelper.removeItem<KtElement>(superTypeListEntry)
} else {
deleteChildRange(findChildByType<PsiElement>(KtTokens.COLON) ?: specifierList, specifierList)
}
}
fun getAnonymousInitializers(): List<KtAnonymousInitializer> = getBody()?.anonymousInitializers.orEmpty()
fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY)
inline fun <reified T : KtDeclaration> addDeclaration(declaration: T): T {
val body = getOrCreateBody()
val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.lastChild!!, PsiWhiteSpace::class.java)
return body.addAfter(declaration, anchor) as T
}
inline fun <reified T : KtDeclaration> addDeclarationAfter(declaration: T, anchor: PsiElement?): T {
val anchorBefore = anchor ?: declarations.lastOrNull() ?: return addDeclaration(declaration)
return getOrCreateBody().addAfter(declaration, anchorBefore) as T
}
inline fun <reified T : KtDeclaration> addDeclarationBefore(declaration: T, anchor: PsiElement?): T {
val anchorAfter = anchor ?: declarations.firstOrNull() ?: return addDeclaration(declaration)
return getOrCreateBody().addBefore(declaration, anchorAfter) as T
}
fun isTopLevel(): Boolean = stub?.isTopLevel() ?: (parent is KtFile)
override fun isLocal(): Boolean = stub?.isLocal() ?: KtPsiUtil.isLocal(this)
override fun getDeclarations(): List<KtDeclaration> = getBody()?.declarations.orEmpty()
override fun getPresentation(): ItemPresentation? = ItemPresentationProviders.getItemPresentation(this)
override fun getPrimaryConstructor(): KtPrimaryConstructor? = getStubOrPsiChild(KtStubElementTypes.PRIMARY_CONSTRUCTOR)
override fun getPrimaryConstructorModifierList(): KtModifierList? = primaryConstructor?.modifierList
fun getPrimaryConstructorParameterList(): KtParameterList? = primaryConstructor?.valueParameterList
override fun getPrimaryConstructorParameters(): List<KtParameter> = getPrimaryConstructorParameterList()?.parameters.orEmpty()
override fun hasExplicitPrimaryConstructor(): Boolean = primaryConstructor != null
override fun hasPrimaryConstructor(): Boolean = hasExplicitPrimaryConstructor() || !hasSecondaryConstructors()
private fun hasSecondaryConstructors(): Boolean = !secondaryConstructors.isEmpty()
override fun getSecondaryConstructors(): List<KtSecondaryConstructor> = getBody()?.secondaryConstructors.orEmpty()
fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD)
fun getDeclarationKeyword(): PsiElement? =
findChildByType(
TokenSet.create(
KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD
)
)
override fun delete() {
CheckUtil.checkWritable(this)
val file = containingKtFile
if (!isTopLevel() || file.declarations.size > 1) {
super.delete()
} else {
file.delete()
}
}
}
fun KtClassOrObject.getOrCreateBody(): KtClassBody {
getBody()?.let { return it }
val newBody = KtPsiFactory(this).createEmptyClassBody()
if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody
return add(newBody) as KtClassBody
}
+1 -1
View File
@@ -31,7 +31,7 @@ dependencies {
testCompile(androidDxJar()) { isTransitive = false }
testCompile(intellijCoreDep()) { includeJars("intellij-core"); isTransitive = false }
testCompile(intellijDep()) {
includeJars("openapi", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "annotations", rootProject = rootProject)
includeJars("openapi", "platform-api", "platform-impl", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "bootstrap", "annotations", rootProject = rootProject)
isTransitive = false
}
}
@@ -0,0 +1,44 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testCompile(project(":core:descriptors"))
testCompile(project(":core:descriptors.jvm"))
testCompile(project(":core:deserialization"))
testCompile(project(":compiler:util"))
testCompile(project(":compiler:backend"))
testCompile(project(":compiler:ir.ir2cfg"))
testCompile(project(":compiler:frontend"))
testCompile(project(":compiler:frontend.java"))
testCompile(project(":compiler:util"))
testCompile(project(":compiler:cli-common"))
testCompile(project(":compiler:cli"))
testCompile(project(":compiler:light-classes"))
testCompile(project(":compiler:serialization"))
testCompile(project(":kotlin-preloader"))
testCompile(project(":compiler:daemon-common"))
testCompile(project(":js:js.serializer"))
testCompile(project(":js:js.frontend"))
testCompile(project(":js:js.translator"))
testCompileOnly(project(":plugins:android-extensions-compiler"))
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
testCompile(projectTests(":compiler:tests-common-jvm6"))
testCompileOnly(project(":kotlin-reflect-api"))
testCompile(commonDep("junit:junit"))
testCompile(androidDxJar()) { isTransitive = false }
testCompile(intellijCoreDep()) { includeJars("intellij-core"); isTransitive = false }
testCompile(intellijDep()) {
includeJars("openapi", "idea", "idea_rt", "guava", "trove4j", "picocontainer", "asm-all", "log4j", "jdom", "annotations", rootProject = rootProject)
isTransitive = false
}
}
sourceSets {
"main" { }
"test" { projectDefault() }
}
testsJar {}
@@ -429,11 +429,11 @@ public class KotlinTestUtils {
private static void deleteOnShutdown(File file) {
if (filesToDelete.isEmpty()) {
ShutDownTracker.getInstance().registerShutdownTask(() -> ShutDownTracker.invokeAndWait(true, true, () -> {
ShutDownTracker.getInstance().registerShutdownTask(() -> {
for (File victim : filesToDelete) {
FileUtil.delete(victim);
}
}));
});
}
filesToDelete.add(file);
File diff suppressed because it is too large Load Diff
+1
View File
@@ -95,6 +95,7 @@ dependencies {
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
testRuntime(intellijPluginDep("testng"))
}
+126
View File
@@ -0,0 +1,126 @@
import org.gradle.jvm.tasks.Jar
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testRuntime(intellijDep())
compile(projectDist(":kotlin-stdlib"))
compileOnly(project(":kotlin-reflect-api"))
compile(project(":core:descriptors"))
compile(project(":core:descriptors.jvm"))
compile(project(":compiler:backend"))
compile(project(":compiler:cli-common"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend.script"))
compile(project(":js:js.frontend"))
compile(project(":js:js.serializer"))
compile(project(":compiler:light-classes"))
compile(project(":compiler:util"))
compile(project(":kotlin-build-common"))
compile(project(":compiler:daemon-common"))
compile(projectRuntimeJar(":kotlin-daemon-client"))
compile(project(":kotlin-compiler-runner")) { isTransitive = false }
compile(project(":compiler:plugin-api"))
compile(project(":eval4j"))
compile(project(":j2k"))
compile(project(":idea:formatter"))
compile(project(":idea:idea-core"))
compile(project(":idea:ide-common"))
compile(project(":idea:idea-jps-common"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":plugins:uast-kotlin"))
compile(project(":plugins:uast-kotlin-idea"))
compile(project(":kotlin-script-util")) { isTransitive = false }
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
compile(commonDep("org.jetbrains", "markdown"))
compileOnly(project(":kotlin-daemon-client"))
compileOnly(intellijDep())
compileOnly(commonDep("com.google.code.findbugs", "jsr305"))
compileOnly(intellijPluginDep("IntelliLang"))
compileOnly(intellijPluginDep("copyright"))
compileOnly(intellijPluginDep("properties"))
compileOnly(intellijPluginDep("java-i18n"))
testCompile(project(":kotlin-test:kotlin-test-junit"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
testCompile(project(":idea:idea-jvm")) { isTransitive = false }
testCompile(project(":idea:idea-gradle")) { isTransitive = false }
testCompile(project(":idea:idea-maven")) { isTransitive = false }
testCompile(commonDep("junit:junit"))
testRuntime(project(":plugins:kapt3-idea")) { isTransitive = false }
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(projectDist(":kotlin-preloader"))
testCompile(project(":kotlin-sam-with-receiver-compiler-plugin")) { isTransitive = false }
testRuntime(project(":plugins:android-extensions-compiler"))
testRuntime(project(":plugins:android-extensions-ide")) { isTransitive = false }
testRuntime(project(":allopen-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlin-allopen-compiler-plugin"))
testRuntime(project(":noarg-ide-plugin")) { isTransitive = false }
testRuntime(project(":kotlin-noarg-compiler-plugin"))
testRuntime(project(":plugins:annotation-based-compiler-plugins-ide-support")) { isTransitive = false }
testRuntime(project(":sam-with-receiver-ide-plugin")) { isTransitive = false }
testRuntime(project(":idea:idea-android")) { isTransitive = false }
testRuntime(project(":plugins:lint")) { isTransitive = false }
testRuntime(project(":plugins:uast-kotlin"))
(rootProject.extra["compilerModules"] as Array<String>).forEach {
testRuntime(project(it))
}
testCompile(intellijPluginDep("IntelliLang"))
testCompile(intellijPluginDep("copyright"))
testCompile(intellijPluginDep("properties"))
testCompile(intellijPluginDep("java-i18n"))
testCompileOnly(intellijDep())
testCompileOnly(commonDep("com.google.code.findbugs", "jsr305"))
testCompileOnly(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
testCompileOnly(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("testng"))
}
sourceSets {
"main" {
projectDefault()
java.srcDirs("idea-completion/src",
"idea-live-templates/src",
"idea-repl/src")
resources.srcDirs("idea-repl/src").apply { include("META-INF/**") }
}
"test" {
projectDefault()
java.srcDirs(
"idea-completion/tests",
"idea-live-templates/tests")
}
}
projectTest {
dependsOn(":dist")
workingDir = rootDir
}
testsJar {}
classesDirsArtifact()
configureInstrumentation()
@@ -35,7 +35,7 @@ import com.intellij.util.xmlb.XmlSerializerUtil
return this
}
override fun loadState(state: KotlinCompilerWorkspaceSettings?) {
XmlSerializerUtil.copyBean(state!!, this)
override fun loadState(state: KotlinCompilerWorkspaceSettings) {
XmlSerializerUtil.copyBean(state, this)
}
}
@@ -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.idea.compiler.configuration
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.StoragePathMacros
import com.intellij.util.xmlb.XmlSerializerUtil
@State(
name = "KotlinCompilerWorkspaceSettings",
storages = arrayOf(
Storage(file = StoragePathMacros.WORKSPACE_FILE)
)
) class KotlinCompilerWorkspaceSettings : PersistentStateComponent<KotlinCompilerWorkspaceSettings> {
var preciseIncrementalEnabled: Boolean = true
var enableDaemon: Boolean = true
override fun getState(): KotlinCompilerWorkspaceSettings {
return this
}
override fun loadState(state: KotlinCompilerWorkspaceSettings?) {
XmlSerializerUtil.copyBean(state!!, this)
}
}
+1
View File
@@ -47,6 +47,7 @@ dependencies {
testRuntime(project(":allopen-ide-plugin"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
testRuntime(intellijPluginDep("copyright"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("gradle"))
+73
View File
@@ -0,0 +1,73 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testRuntime(intellijDep())
compileOnly(project(":kotlin-reflect-api"))
compile(project(":compiler:util"))
compile(project(":compiler:light-classes"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":idea"))
compile(project(":idea:idea-jvm"))
compile(project(":idea:idea-core"))
compile(project(":idea:ide-common"))
compile(project(":idea:idea-gradle"))
compile(androidDxJar())
compileOnly(project(":kotlin-android-extensions-runtime"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("android"))
testCompile(projectDist(":kotlin-test:kotlin-test-jvm"))
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
testCompile(project(":plugins:lint")) { isTransitive = false }
testCompile(project(":idea:idea-jvm"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-gradle"))
testCompile(commonDep("junit:junit"))
testCompile(intellijDep())
testCompile(intellijPluginDep("properties"))
testCompileOnly(intellijPluginDep("android"))
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:kapt3-idea"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("copyright"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("IntelliLang"))
testRuntime(intellijPluginDep("java-decompiler"))
testRuntime(intellijPluginDep("java-i18n"))
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("testng"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
projectTest {
workingDir = rootDir
useAndroidSdk()
}
testsJar {}
@@ -34,6 +34,7 @@ import com.intellij.ui.awt.RelativePoint
import com.intellij.util.Function
import org.jetbrains.android.dom.manifest.Manifest
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.android.resourceManagers.ModuleResourceManagers
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.psi.*
@@ -95,7 +96,8 @@ class KotlinAndroidLineMarkerProvider : LineMarkerProvider {
return
}
val files = androidFacet
val files = ModuleResourceManagers
.getInstance(androidFacet)
.localResourceManager
.findResourcesByFieldName(resClassName, info.fieldName)
.filterIsInstance<PsiFile>()
@@ -0,0 +1,135 @@
/*
* Copyright 2010-2017 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
import com.android.SdkConstants
import com.android.resources.ResourceType
import com.intellij.codeHighlighting.Pass
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.codeInsight.navigation.NavigationUtil
import com.intellij.icons.AllIcons
import com.intellij.ide.highlighter.XmlFileType
import com.intellij.navigation.GotoRelatedItem
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.ui.awt.RelativePoint
import com.intellij.util.Function
import org.jetbrains.android.dom.manifest.Manifest
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.awt.event.MouseEvent
import javax.swing.Icon
class KotlinAndroidLineMarkerProvider : LineMarkerProvider {
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? = null
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
elements.forEach {
(it as? KtClass)?.getLineMarkerInfo()?.let { marker -> result.add(marker) }
}
}
private fun KtClass.getLineMarkerInfo(): LineMarkerInfo<PsiElement>? {
val nameIdentifier = nameIdentifier ?: return null
val androidFacet = AndroidFacet.getInstance(this) ?: return null
val manifest = androidFacet.manifest ?: return null
val manifestItems = collectGoToRelatedManifestItems(manifest)
if (manifestItems.isEmpty() && !isClassWithLayoutXml()) {
return null
}
return LineMarkerInfo(
nameIdentifier,
nameIdentifier.textRange,
AllIcons.FileTypes.Xml,
Pass.LINE_MARKERS,
Function { "Related XML file" },
GutterIconNavigationHandler { e: MouseEvent, _: PsiElement ->
NavigationUtil
.getRelatedItemsPopup(
manifestItems + collectGoToRelatedLayoutItems(androidFacet),
"Go to Related Files")
.show(RelativePoint(e))
},
GutterIconRenderer.Alignment.RIGHT)
}
private fun KtClass.collectGoToRelatedLayoutItems(androidFacet: AndroidFacet): List<GotoRelatedItem> {
val resources = mutableSetOf<PsiFile>()
accept(object: KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
element.acceptChildren(this)
}
override fun visitReferenceExpression(expression: KtReferenceExpression) {
super.visitReferenceExpression(expression)
val resClassName = ResourceType.LAYOUT.getName()
val info = (expression as? KtSimpleNameExpression)?.let {
getReferredResourceOrManifestField(androidFacet, it, resClassName, true)
}
if (info == null || info.isFromManifest) {
return
}
val files = androidFacet
.localResourceManager
.findResourcesByFieldName(resClassName, info.fieldName)
.filterIsInstance<PsiFile>()
resources.addAll(files)
}
})
return resources.map { GotoRelatedLayoutItem(it) }
}
private fun KtClass.collectGoToRelatedManifestItems(manifest: Manifest): List<GotoRelatedItem> =
findComponentDeclarationInManifest(manifest)?.xmlAttributeValue?.let { listOf(GotoManifestItem(it)) } ?: emptyList()
private class GotoManifestItem(attributeValue: XmlAttributeValue) : GotoRelatedItem(attributeValue) {
override fun getCustomName(): String? = "AndroidManifest.xml"
override fun getCustomContainerName(): String? = ""
override fun getCustomIcon(): Icon? = XmlFileType.INSTANCE.icon
}
private class GotoRelatedLayoutItem(private val file: PsiFile) : GotoRelatedItem(file, "Layout Files") {
override fun getCustomContainerName(): String? = "(${file.containingDirectory.name})"
}
companion object {
private val CLASSES_WITH_LAYOUT_XML = arrayOf(
SdkConstants.CLASS_ACTIVITY,
SdkConstants.CLASS_FRAGMENT,
SdkConstants.CLASS_V4_FRAGMENT,
"android.widget.Adapter")
private fun KtClass.isClassWithLayoutXml(): Boolean {
val type = (unsafeResolveToDescriptor(BodyResolveMode.PARTIAL) as? ClassDescriptor)?.defaultType ?: return false
return CLASSES_WITH_LAYOUT_XML.any { type.isSubclassOf(it, true) }
}
}
}
@@ -23,8 +23,8 @@ import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
import com.android.resources.ResourceType;
import com.android.tools.idea.configurations.Configuration;
import com.android.tools.idea.configurations.ConfigurationManager;
import com.android.tools.idea.res.AppResourceRepository;
import com.android.tools.idea.res.LocalResourceRepository;
import com.android.tools.idea.res.ResourceHelper;
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
import com.android.utils.XmlUtils;
@@ -61,15 +61,14 @@ import java.awt.*;
import java.io.File;
import static com.android.SdkConstants.*;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_DRAWABLE;
import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE;
import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile;
/**
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
*/
public class ResourceReferenceAnnotatorUtil {
public static final int ICON_SIZE = 8;
@Nullable
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
try {
@@ -146,7 +145,7 @@ public class ResourceReferenceAnnotatorUtil {
ResourceItem item = frameworkResources.getResourceItem(type, name);
return item.getResourceValue(type, configuration.getFullConfig(), false);
} else {
LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true);
AppResourceRepository appResources = AppResourceRepository.getOrCreateInstance(module);
if (appResources == null) {
return null;
}
@@ -161,26 +160,27 @@ public class ResourceReferenceAnnotatorUtil {
@Nullable
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) {
if(virtualFile == null) {
return null;
}
VirtualFile parent = virtualFile.getParent();
if (parent == null) {
return null;
}
VirtualFile layout;
String parentName = parent.getName();
if (!parentName.startsWith(FD_RES_LAYOUT)) {
layout = pickLayoutFile(module, facet);
if (layout == null) {
return null;
}
} else {
layout = virtualFile;
}
VirtualFile parent = virtualFile.getParent();
if(parent == null) {
return null;
} else {
String parentName = parent.getName();
VirtualFile layout;
if(!parentName.startsWith("layout")) {
layout = ResourceHelper.pickAnyLayoutFile(module, facet);
if(layout == null) {
return null;
}
} else {
layout = virtualFile;
}
return facet.getConfigurationManager().getConfiguration(layout);
return ConfigurationManager.getOrCreateInstance(module).getConfiguration(layout);
}
}
}
public static class ColorRenderer extends GutterIconRenderer {
@@ -0,0 +1,270 @@
/*
* Copyright 2010-2017 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;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.ResourceItem;
import com.android.ide.common.resources.ResourceRepository;
import com.android.ide.common.resources.ResourceResolver;
import com.android.resources.ResourceType;
import com.android.tools.idea.configurations.Configuration;
import com.android.tools.idea.res.AppResourceRepository;
import com.android.tools.idea.res.LocalResourceRepository;
import com.android.tools.idea.res.ResourceHelper;
import com.android.tools.idea.ui.resourcechooser.ColorPicker;
import com.android.utils.XmlUtils;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ui.ColorIcon;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import static com.android.SdkConstants.*;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_DRAWABLE;
import static com.android.tools.idea.uibuilder.property.renderer.NlDefaultRenderer.ICON_SIZE;
import static org.jetbrains.android.AndroidColorAnnotator.pickLayoutFile;
/**
* Contains copied privates from AndroidColorAnnotator, so we could use them for Kotlin AndroidResourceReferenceAnnotator
*/
public class ResourceReferenceAnnotatorUtil {
@Nullable
public static File pickBitmapFromXml(@NotNull File file, @NotNull ResourceResolver resourceResolver, @NotNull Project project) {
try {
String xml = Files.toString(file, Charsets.UTF_8);
Document document = XmlUtils.parseDocumentSilently(xml, true);
if (document != null && document.getDocumentElement() != null) {
Element root = document.getDocumentElement();
String tag = root.getTagName();
Element target = null;
String attribute = null;
if ("vector".equals(tag)) {
// Vectors are handled in the icon cache
return file;
}
else if ("bitmap".equals(tag) || "nine-patch".equals(tag)) {
target = root;
attribute = ATTR_SRC;
}
else if ("selector".equals(tag) ||
"level-list".equals(tag) ||
"layer-list".equals(tag) ||
"transition".equals(tag)) {
NodeList children = root.getChildNodes();
for (int i = children.getLength() - 1; i >= 0; i--) {
Node item = children.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE && TAG_ITEM.equals(item.getNodeName())) {
target = (Element)item;
if (target.hasAttributeNS(ANDROID_URI, ATTR_DRAWABLE)) {
attribute = ATTR_DRAWABLE;
break;
}
}
}
}
else if ("clip".equals(tag) || "inset".equals(tag) || "scale".equals(tag)) {
target = root;
attribute = ATTR_DRAWABLE;
} else {
// <shape> etc - no bitmap to be found
return null;
}
if (attribute != null && target.hasAttributeNS(ANDROID_URI, attribute)) {
String src = target.getAttributeNS(ANDROID_URI, attribute);
ResourceValue value = resourceResolver.findResValue(src, false);
if (value != null) {
return ResourceHelper.resolveDrawable(resourceResolver, value, project);
}
}
}
} catch (Throwable ignore) {
// Not logging for now; afraid to risk unexpected crashes in upcoming preview. TODO: Re-enable.
//Logger.getInstance(AndroidColorAnnotator.class).warn(String.format("Could not read/render icon image %1$s", file), e);
}
return null;
}
/** Looks up the resource item of the given type and name for the given configuration, if any */
@Nullable
public static ResourceValue findResourceValue(ResourceType type,
String name,
boolean isFramework,
Module module,
Configuration configuration) {
if (isFramework) {
ResourceRepository frameworkResources = configuration.getFrameworkResources();
if (frameworkResources == null) {
return null;
}
if (!frameworkResources.hasResourceItem(type, name)) {
return null;
}
ResourceItem item = frameworkResources.getResourceItem(type, name);
return item.getResourceValue(type, configuration.getFullConfig(), false);
} else {
LocalResourceRepository appResources = AppResourceRepository.getAppResources(module, true);
if (appResources == null) {
return null;
}
if (!appResources.hasResourceItem(type, name)) {
return null;
}
return appResources.getConfiguredValue(type, name, configuration.getFullConfig());
}
}
/** Picks a suitable configuration to use for resource resolution */
@Nullable
public static Configuration pickConfiguration(AndroidFacet facet, Module module, PsiFile file) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) {
return null;
}
VirtualFile parent = virtualFile.getParent();
if (parent == null) {
return null;
}
VirtualFile layout;
String parentName = parent.getName();
if (!parentName.startsWith(FD_RES_LAYOUT)) {
layout = pickLayoutFile(module, facet);
if (layout == null) {
return null;
}
} else {
layout = virtualFile;
}
return facet.getConfigurationManager().getConfiguration(layout);
}
public static class ColorRenderer extends GutterIconRenderer {
private final PsiElement myElement;
private final Color myColor;
ColorRenderer(@NotNull PsiElement element, @Nullable Color color) {
myElement = element;
myColor = color;
}
@NotNull
@Override
public Icon getIcon() {
Color color = getCurrentColor();
return JBUI.scale(color == null ? EmptyIcon.create(ICON_SIZE) : new ColorIcon(ICON_SIZE, color));
}
@Nullable
private Color getCurrentColor() {
if (myColor != null) {
return myColor;
} else if (myElement instanceof XmlTag) {
return ResourceHelper.parseColor(((XmlTag)myElement).getValue().getText());
} else if (myElement instanceof XmlAttributeValue) {
return ResourceHelper.parseColor(((XmlAttributeValue)myElement).getValue());
} else {
return null;
}
}
@Override
public AnAction getClickAction() {
if (myColor != null) { // Cannot set colors that were derived
return null;
}
return new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext());
if (editor != null) {
// Need ARGB support in platform color chooser; see
// https://youtrack.jetbrains.com/issue/IDEA-123498
//final Color color =
// ColorChooser.chooseColor(editor.getComponent(), AndroidBundle.message("android.choose.color"), getCurrentColor());
final Color color = ColorPicker.showDialog(editor.getComponent(), "Choose Color", getCurrentColor(), true, null, false);
if (color != null) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (myElement instanceof XmlTag) {
((XmlTag)myElement).getValue().setText(ResourceHelper.colorToString(color));
} else if (myElement instanceof XmlAttributeValue) {
XmlAttribute attribute = PsiTreeUtil.getParentOfType(myElement, XmlAttribute.class);
if (attribute != null) {
attribute.setValue(ResourceHelper.colorToString(color));
}
}
}
});
}
}
}
};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ColorRenderer that = (ColorRenderer)o;
// TODO: Compare with modification count in app resources (if not framework)
if (myColor != null ? !myColor.equals(that.myColor) : that.myColor != null) return false;
if (!myElement.equals(that.myElement)) return false;
return true;
}
@Override
public int hashCode() {
int result = myElement.hashCode();
result = 31 * result + (myColor != null ? myColor.hashCode() : 0);
return result;
}
}
}
@@ -22,6 +22,7 @@ import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.android.sdk.AndroidSdkData
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
import java.io.File
@@ -56,7 +57,7 @@ class AndroidDexerImpl(val project: Project) : AndroidDexer {
private fun doGetAndroidDexFile(): File? {
for (module in ModuleManager.getInstance(project).modules) {
val androidFacet = AndroidFacet.getInstance(module) ?: continue
val sdkData = androidFacet.sdkData ?: continue
val sdkData = AndroidSdkData.getSdkData(androidFacet) ?: continue
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
?: continue
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2017 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.debugger
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
import java.io.File
import java.net.URLClassLoader
import java.security.ProtectionDomain
class AndroidDexerImpl(val project: Project) : AndroidDexer {
private val cachedDexWrapper = CachedValuesManager.getManager(project).createCachedValue({
val dexWrapper = doGetAndroidDexFile()?.let { dexJarFile ->
val androidDexWrapperName = AndroidDexWrapper::class.java.canonicalName
val classBytes = this.javaClass.classLoader.getResource(
androidDexWrapperName.replace('.', '/') + ".class").readBytes()
val dexClassLoader = object : URLClassLoader(arrayOf(dexJarFile.toURI().toURL()), this::class.java.classLoader) {
init {
defineClass(androidDexWrapperName, classBytes, 0, classBytes.size, null as ProtectionDomain?)
}
}
Class.forName(androidDexWrapperName, true, dexClassLoader).newInstance()
}
CachedValueProvider.Result.createSingleDependency(dexWrapper, ProjectRootModificationTracker.getInstance(project))
}, /* trackValue = */ false)
override fun dex(classes: Collection<ClassToLoad>): ByteArray? {
val dexWrapper = cachedDexWrapper.value
val dexMethod = dexWrapper::class.java.methods.firstOrNull { it.name == "dex" } ?: return null
return dexMethod.invoke(dexWrapper, classes) as? ByteArray ?: return null
}
private fun doGetAndroidDexFile(): File? {
for (module in ModuleManager.getInstance(project).modules) {
val androidFacet = AndroidFacet.getInstance(module) ?: continue
val sdkData = androidFacet.sdkData ?: continue
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
?: continue
val dxJar = File(latestBuildTool.location, "lib/dx.jar")
if (dxJar.exists()) {
return dxJar
}
}
return null
}
}
@@ -47,7 +47,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() {
// See lint's StringFormatDetector
private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])")
private val FOLD_MAX_LENGTH = 60
private val FORCE_PROJECT_RESOURCE_LOADING = true
private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode
private val RESOURCE_TYPES = listOf(ResourceType.STRING,
ResourceType.DIMEN,
@@ -254,6 +253,6 @@ class ResourceFoldingBuilder : FoldingBuilderEx() {
}
private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let {
AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING)
AppResourceRepository.findExistingInstance(it)
}
}
@@ -0,0 +1,259 @@
/*
* Copyright 2010-2017 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.folding
import com.android.SdkConstants.*
import com.android.ide.common.resources.configuration.FolderConfiguration
import com.android.ide.common.resources.configuration.LocaleQualifier
import com.android.resources.ResourceType
import com.android.tools.idea.folding.AndroidFoldingSettings
import com.android.tools.idea.res.AppResourceRepository
import com.android.tools.idea.res.LocalResourceRepository
import com.intellij.lang.ASTNode
import com.intellij.lang.folding.FoldingBuilderEx
import com.intellij.lang.folding.FoldingDescriptor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.editor.Document
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.SourceTreeToPsiMap
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.uast.*
import org.jetbrains.uast.visitor.AbstractUastVisitor
import java.util.regex.Pattern
class ResourceFoldingBuilder : FoldingBuilderEx() {
companion object {
// See lint's StringFormatDetector
private val FORMAT = Pattern.compile("%(\\d+\\$)?([-+#, 0(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])")
private val FOLD_MAX_LENGTH = 60
private val FORCE_PROJECT_RESOURCE_LOADING = true
private val UNIT_TEST_MODE: Boolean = ApplicationManager.getApplication().isUnitTestMode
private val RESOURCE_TYPES = listOf(ResourceType.STRING,
ResourceType.DIMEN,
ResourceType.INTEGER,
ResourceType.PLURALS)
}
private val isFoldingEnabled = AndroidFoldingSettings.getInstance().isCollapseAndroidStrings
override fun getPlaceholderText(node: ASTNode): String? {
tailrec fun UElement.unwrapReferenceAndGetValue(resources: LocalResourceRepository): String? = when (this) {
is UQualifiedReferenceExpression -> selector.unwrapReferenceAndGetValue(resources)
is UCallExpression -> (valueArguments.firstOrNull() as? UReferenceExpression)?.getAndroidResourceValue(resources, this)
else -> (this as? UReferenceExpression)?.getAndroidResourceValue(resources)
}
val element = SourceTreeToPsiMap.treeElementToPsi(node) ?: return null
val appResources = getAppResources(element) ?: return null
val uastContext = ServiceManager.getService(element.project, UastContext::class.java) ?: return null
return uastContext.convertElement(element, null, null)?.unwrapReferenceAndGetValue(appResources)
}
override fun buildFoldRegions(root: PsiElement, document: Document, quick: Boolean): Array<FoldingDescriptor> {
if (root !is KtFile || quick && !UNIT_TEST_MODE || !isFoldingEnabled || AndroidFacet.getInstance(root) == null) {
return emptyArray()
}
val file = root.toUElement()
val result = arrayListOf<FoldingDescriptor>()
file?.accept(object : AbstractUastVisitor() {
override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
node.getFoldingDescriptor()?.let { result.add(it) }
return super.visitSimpleNameReferenceExpression(node)
}
})
return result.toTypedArray()
}
override fun isCollapsedByDefault(node: ASTNode): Boolean = isFoldingEnabled
private fun UReferenceExpression.getFoldingDescriptor(): FoldingDescriptor? {
val resolved = resolve() ?: return null
val resourceType = resolved.getAndroidResourceType() ?: return null
if (resourceType !in RESOURCE_TYPES) return null
fun UElement.createFoldingDescriptor() = psi?.let { psi ->
val dependencies: Set<PsiElement> = setOf(psi)
FoldingDescriptor(psi.node, psi.textRange, null, dependencies)
}
val element = uastParent as? UQualifiedReferenceExpression ?: this
val getResourceValueCall = (element.uastParent as? UCallExpression)?.takeIf { it.isFoldableGetResourceValueCall() }
if (getResourceValueCall != null) {
val qualifiedCall = getResourceValueCall.uastParent as? UQualifiedReferenceExpression
if (qualifiedCall?.selector == getResourceValueCall) {
return qualifiedCall.createFoldingDescriptor()
}
return getResourceValueCall.createFoldingDescriptor()
}
return element.createFoldingDescriptor()
}
private fun UCallExpression.isFoldableGetResourceValueCall(): Boolean {
return methodName == "getString" ||
methodName == "getText" ||
methodName == "getInteger" ||
methodName?.startsWith("getDimension") ?: false ||
methodName?.startsWith("getQuantityString") ?: false
}
private fun PsiElement.getAndroidResourceType(): ResourceType? {
val elementType = parent as? PsiClass ?: return null
val elementPackage = elementType.parent as? PsiClass ?: return null
if (R_CLASS != elementPackage.name) return null
if (elementPackage.qualifiedName != "$ANDROID_PKG.$R_CLASS") {
return ResourceType.getEnum(elementType.name)
}
return null
}
private fun UReferenceExpression.getAndroidResourceValue(resources: LocalResourceRepository, call: UCallExpression? = null): String? {
val resourceType = resolve()?.getAndroidResourceType() ?: return null
val referenceConfig = FolderConfiguration().apply { localeQualifier = LocaleQualifier("xx") }
val key = resolvedName ?: return null
val resourceValue = resources.getResourceValue(resourceType, key, referenceConfig) ?: return null
val text = if (call != null) formatArguments(call, resourceValue) else resourceValue
if (resourceType == ResourceType.STRING || resourceType == ResourceType.PLURALS) {
return '"' + StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH - 2, 0) + '"'
}
else if (text.length <= 1) {
// Don't just inline empty or one-character replacements: they can't be expanded by a mouse click
// so are hard to use without knowing about the folding keyboard shortcut to toggle folding.
// This is similar to how IntelliJ 14 handles call parameters
return key + ": " + text
}
return StringUtil.shortenTextWithEllipsis(text, FOLD_MAX_LENGTH, 0)
}
private tailrec fun LocalResourceRepository.getResourceValue(
type: ResourceType,
name: String,
referenceConfig: FolderConfiguration): String? {
val value = getConfiguredValue(type, name, referenceConfig)?.value ?: return null
if (!value.startsWith('@')) {
return value
}
val (referencedTypeName, referencedName) = value.substring(1).split('/').takeIf { it.size == 2 } ?: return value
val referencedType = ResourceType.getEnum(referencedTypeName) ?: return value
return getResourceValue(referencedType, referencedName, referenceConfig)
}
// Converted from com.android.tools.idea.folding.InlinedResource#insertArguments
private fun formatArguments(callExpression: UCallExpression, formatString: String): String {
if (!formatString.contains('%')) {
return formatString
}
val args = callExpression.valueArguments
if (args.isEmpty() || !args.first().isPsiValid) {
return formatString
}
val matcher = FORMAT.matcher(formatString)
var index = 0
var prevIndex = 0
var nextNumber = 1
var start = 0
val sb = StringBuilder(2 * formatString.length)
while (true) {
if (matcher.find(index)) {
if ("%" == matcher.group(6)) {
index = matcher.end()
continue
}
val matchStart = matcher.start()
// Make sure this is not an escaped '%'
while (prevIndex < matchStart) {
val c = formatString[prevIndex]
if (c == '\\') {
prevIndex++
}
prevIndex++
}
if (prevIndex > matchStart) {
// We're in an escape, ignore this result
index = prevIndex
continue
}
index = matcher.end()
// Shouldn't throw a number format exception since we've already
// matched the pattern in the regexp
val number: Int
var numberString: String? = matcher.group(1)
if (numberString != null) {
// Strip off trailing $
numberString = numberString.substring(0, numberString.length - 1)
number = Integer.parseInt(numberString)
nextNumber = number + 1
}
else {
number = nextNumber++
}
if (number > 0 && number < args.size) {
val argExpression = args[number]
var value: Any? = argExpression.evaluate()
if (value == null) {
value = args[number].asSourceString()
}
for (i in start..matchStart - 1) {
sb.append(formatString[i])
}
sb.append("{")
sb.append(value)
sb.append('}')
start = index
}
}
else {
var i = start
val n = formatString.length
while (i < n) {
sb.append(formatString[i])
i++
}
break
}
}
return sb.toString()
}
private fun getAppResources(element: PsiElement): LocalResourceRepository? = ModuleUtilCore.findModuleForPsiElement(element)?.let {
AppResourceRepository.getAppResources(it, FORCE_PROJECT_RESOURCE_LOADING)
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.android.inspection
import com.android.tools.idea.model.AndroidModuleInfo
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
@@ -28,7 +29,11 @@ import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument
class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel
val compileSdk = AndroidFacet.getInstance(session.file)
?.let { facet -> AndroidModuleInfo.getInstance(facet) }
?.buildSdkVersion
?.apiLevel
if (compileSdk == null || compileSdk < 26) {
return KtVisitorVoid()
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2017 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.inspection
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.android.facet.AndroidFacet
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtPsiUtil.isUnsafeCast
import org.jetbrains.kotlin.psi.psiUtil.addTypeArgument
class TypeParameterFindViewByIdInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
val compileSdk = AndroidFacet.getInstance(session.file)?.androidModuleInfo?.buildSdkVersion?.apiLevel
if (compileSdk == null || compileSdk < 26) {
return KtVisitorVoid()
}
return object : KtVisitorVoid() {
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
if (expression.calleeExpression?.text != "findViewById" || expression.typeArguments.isNotEmpty()) {
return
}
val parentCast = (expression.parent as? KtBinaryExpressionWithTypeRHS)?.takeIf { isUnsafeCast(it) } ?: return
val typeText = parentCast.right?.getTypeTextWithoutQuestionMark() ?: return
val callableDescriptor = expression.resolveToCall()?.resultingDescriptor ?: return
if (callableDescriptor.name.asString() != "findViewById" || callableDescriptor.typeParameters.size != 1) {
return
}
holder.registerProblem(
parentCast,
"Can be converted to findViewById<$typeText>(...)",
ConvertCastToFindViewByIdWithTypeParameter())
}
}
}
class ConvertCastToFindViewByIdWithTypeParameter : LocalQuickFix {
override fun getFamilyName(): String = "Convert cast to findViewById with type parameter"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val cast = descriptor.psiElement as? KtBinaryExpressionWithTypeRHS ?: return
val typeText = cast.right?.getTypeTextWithoutQuestionMark() ?: return
val call = cast.left as? KtCallExpression ?: return
val newCall = call.copy() as KtCallExpression
val typeArgument = KtPsiFactory(call).createTypeArgument(typeText)
newCall.addTypeArgument(typeArgument)
cast.replace(newCall)
}
}
companion object {
fun KtTypeReference.getTypeTextWithoutQuestionMark(): String? =
(typeElement as? KtNullableType)?.innerType?.text ?: typeElement?.text
}
}
@@ -29,6 +29,7 @@ import org.jetbrains.android.dom.resources.Attr;
import org.jetbrains.android.dom.resources.DeclareStyleable;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ModuleResourceManagers;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
@@ -68,9 +69,10 @@ public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandl
collectManifestElements(nestedClassName, fieldName, facet, resourceList);
}
else {
ModuleResourceManagers managers = ModuleResourceManagers.getInstance(facet);
ResourceManager manager = info.isSystem()
? facet.getSystemResourceManager(false)
: facet.getLocalResourceManager();
? managers.getSystemResourceManager(false)
: managers.getLocalResourceManager();
if (manager == null) {
return null;
}
@@ -0,0 +1,147 @@
/*
* 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.navigation;
import com.android.resources.ResourceType;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlElement;
import org.jetbrains.android.dom.AndroidAttributeValue;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.dom.manifest.ManifestElementWithRequiredName;
import org.jetbrains.android.dom.resources.Attr;
import org.jetbrains.android.dom.resources.DeclareStyleable;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.LocalResourceManager;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.android.AndroidUtilKt;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// TODO: ask for extension point
// this class is mostly copied from org.jetbrains.android.AndroidGotoDeclarationHandler
public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandler {
@Override
public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) {
KtSimpleNameExpression referenceExpression = GotoResourceHelperKt.getReferenceExpression(sourceElement);
if (referenceExpression == null) {
return null;
}
AndroidFacet facet = AndroidUtilKt.getAndroidFacetForFile(referenceExpression);
if (facet == null) {
return null;
}
AndroidResourceUtil.MyReferredResourceFieldInfo info = GotoResourceHelperKt.getInfo(referenceExpression, facet);
if (info == null) return null;
String nestedClassName = info.getClassName();
String fieldName = info.getFieldName();
List<PsiElement> resourceList = new ArrayList<PsiElement>();
if (info.isFromManifest()) {
collectManifestElements(nestedClassName, fieldName, facet, resourceList);
}
else {
ResourceManager manager = info.isSystem()
? facet.getSystemResourceManager(false)
: facet.getLocalResourceManager();
if (manager == null) {
return null;
}
manager.collectLazyResourceElements(nestedClassName, fieldName, false, referenceExpression, resourceList);
if (manager instanceof LocalResourceManager) {
LocalResourceManager lrm = (LocalResourceManager) manager;
if (nestedClassName.equals(ResourceType.ATTR.getName())) {
for (Attr attr : lrm.findAttrs(fieldName)) {
resourceList.add(attr.getName().getXmlAttributeValue());
}
}
else if (nestedClassName.equals(ResourceType.STYLEABLE.getName())) {
for (DeclareStyleable styleable : lrm.findStyleables(fieldName)) {
resourceList.add(styleable.getName().getXmlAttributeValue());
}
for (Attr styleable : lrm.findStyleableAttributesByFieldName(fieldName)) {
resourceList.add(styleable.getName().getXmlAttributeValue());
}
}
}
}
if (resourceList.size() > 1) {
// Sort to ensure the output is stable, and to prefer the base folders
Collections.sort(resourceList, AndroidResourceUtil.RESOURCE_ELEMENT_COMPARATOR);
}
return resourceList.toArray(new PsiElement[resourceList.size()]);
}
private static void collectManifestElements(
@NotNull String nestedClassName,
@NotNull String fieldName,
@NotNull AndroidFacet facet,
@NotNull List<PsiElement> result
) {
Manifest manifest = facet.getManifest();
if (manifest == null) {
return;
}
List<? extends ManifestElementWithRequiredName> list;
if ("permission".equals(nestedClassName)) {
list = manifest.getPermissions();
}
else if ("permission_group".equals(nestedClassName)) {
list = manifest.getPermissionGroups();
}
else {
return;
}
for (ManifestElementWithRequiredName domElement : list) {
AndroidAttributeValue<String> nameAttribute = domElement.getName();
String name = nameAttribute.getValue();
if (AndroidUtils.equal(name, fieldName, false)) {
XmlElement psiElement = nameAttribute.getXmlAttributeValue();
if (psiElement != null) {
result.add(psiElement);
}
}
}
}
@Override
public String getActionText(DataContext context) {
return null;
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2017 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.quickfix
import com.android.SdkConstants
import com.android.tools.lint.checks.ApiDetector.REQUIRES_API_ANNOTATION
import com.intellij.codeInsight.FileModificationService
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
import org.jetbrains.android.util.AndroidBundle
import org.jetbrains.kotlin.android.hasBackingField
import org.jetbrains.kotlin.idea.util.addAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
class AddTargetApiQuickFix(
val api: Int,
val useRequiresApi: Boolean
) : AndroidLintQuickFix {
private companion object {
val FQNAME_TARGET_API = FqName(SdkConstants.FQCN_TARGET_API)
val FQNAME_REQUIRES_API = FqName(REQUIRES_API_ANNOTATION)
}
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
getAnnotationContainer(startElement, useRequiresApi) != null
override fun getName(): String = getAnnotationValue(false).let {
if (useRequiresApi) {
// Not Available in Android plugin 2.0
// AndroidBundle.message("android.lint.fix.add.requires.api", it)
"Add @RequiresApi($it) Annotation"
} else {
AndroidBundle.message("android.lint.fix.add.target.api", it)
}
}
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
val annotationContainer = getAnnotationContainer(startElement, useRequiresApi) ?: return
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
return
}
if (annotationContainer is KtModifierListOwner) {
annotationContainer.addAnnotation(
if (useRequiresApi) FQNAME_REQUIRES_API else FQNAME_TARGET_API,
getAnnotationValue(true),
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ")
}
}
private fun KtElement.isNewLineNeededForAnnotation() = !(this is KtParameter || this is KtTypeParameter || this is KtPropertyAccessor)
private fun getAnnotationValue(fullyQualified: Boolean) = getVersionField(api, fullyQualified)
private fun getAnnotationContainer(element: PsiElement, useRequiresApi: Boolean): PsiElement? {
return PsiTreeUtil.findFirstParent(element) {
if (useRequiresApi)
it.isRequiresApiAnnotationValidTarget()
else
it.isTargetApiAnnotationValidTarget()
}
}
private fun PsiElement.isRequiresApiAnnotationValidTarget(): Boolean {
return this is KtClassOrObject ||
(this is KtFunction && this !is KtFunctionLiteral) ||
(this is KtProperty && !isLocal && hasBackingField()) ||
this is KtPropertyAccessor
}
private fun PsiElement.isTargetApiAnnotationValidTarget(): Boolean {
return this is KtClassOrObject ||
(this is KtFunction && this !is KtFunctionLiteral) ||
this is KtPropertyAccessor
}
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2017 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.quickfix
import com.intellij.codeInsight.FileModificationService
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.surroundWith.statement.KotlinIfSurrounder
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class AddTargetVersionCheckQuickFix(val api: Int) : AndroidLintQuickFix {
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
val targetExpression = getTargetExpression(startElement)
val project = targetExpression?.project ?: return
val editor = targetExpression.findExistingEditor() ?: return
val file = targetExpression.containingFile
val documentManager = PsiDocumentManager.getInstance(project)
val document = documentManager.getDocument(file) ?: return
if (!FileModificationService.getInstance().prepareFileForWrite(file)) {
return
}
val surrounder = getSurrounder(targetExpression, "\"VERSION.SDK_INT < ${getVersionField(api, false)}\"")
val conditionRange = surrounder.surroundElements(project, editor, arrayOf(targetExpression)) ?: return
val conditionText = "android.os.Build.VERSION.SDK_INT >= ${getVersionField(api, true)}"
document.replaceString(conditionRange.startOffset, conditionRange.endOffset, conditionText)
documentManager.commitDocument(document)
ShortenReferences.DEFAULT.process(documentManager.getPsiFile(document) as KtFile,
conditionRange.startOffset,
conditionRange.startOffset + conditionText.length)
}
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean =
getTargetExpression(startElement) != null
override fun getName(): String = "Surround with if (VERSION.SDK_INT >= VERSION_CODES.${getVersionField(api, false)}) { ... }"
private fun getTargetExpression(element: PsiElement): KtElement? {
var current = PsiTreeUtil.getParentOfType(element, KtExpression::class.java)
while (current != null) {
if (current.parent is KtBlockExpression ||
current.parent is KtContainerNode ||
current.parent is KtWhenEntry ||
current.parent is KtFunction ||
current.parent is KtPropertyAccessor ||
current.parent is KtProperty ||
current.parent is KtReturnExpression ||
current.parent is KtDestructuringDeclaration) {
break
}
current = PsiTreeUtil.getParentOfType(current, KtExpression::class.java, true)
}
return current
}
private fun getSurrounder(element: KtElement, todoText: String?): KotlinIfSurrounder {
val used = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.USED_AS_EXPRESSION, element] ?: false
return if (used) {
object : KotlinIfSurrounder() {
override fun getCodeTemplate(): String = "if (a) { \n} else {\nTODO(${todoText ?: ""})\n}"
}
} else {
KotlinIfSurrounder()
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2017 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.quickfix
import com.android.sdklib.SdkVersionInfo.*
fun getVersionField(api: Int, fullyQualified: Boolean): String = getBuildCode(api)?.let {
if (fullyQualified) "android.os.Build.VERSION_CODES.$it" else it
} ?: api.toString()
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2017 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.quickfix
import com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX
import com.android.tools.lint.checks.ApiDetector
import com.android.tools.lint.checks.CommentDetector
import com.android.tools.lint.checks.ParcelDetector
import com.android.tools.lint.detector.api.Issue
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
import org.jetbrains.android.inspections.lint.AndroidLintQuickFixProvider
import java.util.regex.Pattern
class KotlinAndroidQuickFixProvider : AndroidLintQuickFixProvider {
override fun getQuickFixes(
issue: Issue,
startElement: PsiElement,
endElement: PsiElement,
message: String,
data: Any?
): Array<AndroidLintQuickFix> {
val fixes: Array<AndroidLintQuickFix> = when (issue) {
ApiDetector.UNSUPPORTED, ApiDetector.INLINED -> getApiQuickFixes(issue, startElement, message)
ParcelDetector.ISSUE -> arrayOf(ParcelableQuickFix())
else -> emptyArray()
}
if (issue != CommentDetector.STOP_SHIP) {
return fixes + SuppressLintQuickFix(issue.id)
}
return fixes
}
fun getApiQuickFixes(issue: Issue, element: PsiElement, message: String): Array<AndroidLintQuickFix> {
val api = getRequiredVersion(message)
if (api == -1) {
return AndroidLintQuickFix.EMPTY_ARRAY
}
val project = element.project
if (JavaPsiFacade.getInstance(project).findClass(REQUIRES_API_ANNOTATION, GlobalSearchScope.allScope(project)) != null) {
return arrayOf(AddTargetApiQuickFix(api, true), AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
}
return arrayOf(AddTargetApiQuickFix(api, false), AddTargetVersionCheckQuickFix(api))
}
private fun getRequiredVersion(errorMessage: String): Int {
val pattern = Pattern.compile("\\s(\\d+)\\s")
val matcher = pattern.matcher(errorMessage)
if (matcher.find()) {
return Integer.parseInt(matcher.group(1))
}
return -1
}
companion object {
val REQUIRES_API_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresApi"
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2017 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.quickfix
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
import org.jetbrains.android.util.AndroidBundle
import org.jetbrains.kotlin.android.canAddParcelable
import org.jetbrains.kotlin.android.implementParcelable
import org.jetbrains.kotlin.android.isParcelize
import org.jetbrains.kotlin.psi.KtClass
class ParcelableQuickFix : AndroidLintQuickFix {
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
startElement.getTargetClass()?.implementParcelable()
}
override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean {
val targetClass = startElement.getTargetClass() ?: return false
return targetClass.canAddParcelable() && !targetClass.isParcelize()
}
override fun getName(): String = AndroidBundle.message("implement.parcelable.intention.text")
private fun PsiElement.getTargetClass(): KtClass? = PsiTreeUtil.getParentOfType(this, KtClass::class.java, false)
}
@@ -0,0 +1,99 @@
/*
* Copyright 2010-2017 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.quickfix
import com.android.SdkConstants
import com.intellij.codeInsight.FileModificationService
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.android.inspections.lint.AndroidLintQuickFix
import org.jetbrains.android.inspections.lint.AndroidQuickfixContexts
import org.jetbrains.android.util.AndroidBundle
import org.jetbrains.kotlin.android.hasBackingField
import org.jetbrains.kotlin.idea.util.addAnnotation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
class SuppressLintQuickFix(id: String) : AndroidLintQuickFix {
private val lintId = getLintId(id)
override fun apply(startElement: PsiElement, endElement: PsiElement, context: AndroidQuickfixContexts.Context) {
val annotationContainer = PsiTreeUtil.findFirstParent(startElement, true) { it.isSuppressLintTarget() } ?: return
if (!FileModificationService.getInstance().preparePsiElementForWrite(annotationContainer)) {
return
}
val argument = "\"$lintId\""
when (annotationContainer) {
is KtModifierListOwner -> {
annotationContainer.addAnnotation(
FQNAME_SUPPRESS_LINT,
argument,
whiteSpaceText = if (annotationContainer.isNewLineNeededForAnnotation()) "\n" else " ",
addToExistingAnnotation = { entry -> addArgumentToAnnotation(entry, argument) })
}
}
}
override fun getName(): String = AndroidBundle.message(SUPPRESS_LINT_MESSAGE, lintId)
override fun isApplicable(
startElement: PsiElement,
endElement: PsiElement,
contextType: AndroidQuickfixContexts.ContextType
): Boolean = true
private fun addArgumentToAnnotation(entry: KtAnnotationEntry, argument: String): Boolean {
// add new arguments to an existing entry
val args = entry.valueArgumentList
val psiFactory = KtPsiFactory(entry)
val newArgList = psiFactory.createCallArguments("($argument)")
when {
args == null -> // new argument list
entry.addAfter(newArgList, entry.lastChild)
args.arguments.isEmpty() -> // replace '()' with a new argument list
args.replace(newArgList)
args.arguments.none { it.textMatches(argument) } ->
args.addArgument(newArgList.arguments[0])
}
return true
}
private fun getLintId(intentionId: String) =
if (intentionId.startsWith(INTENTION_NAME_PREFIX)) intentionId.substring(INTENTION_NAME_PREFIX.length) else intentionId
private fun KtElement.isNewLineNeededForAnnotation(): Boolean {
return !(this is KtParameter ||
this is KtTypeParameter ||
this is KtPropertyAccessor)
}
private fun PsiElement.isSuppressLintTarget(): Boolean {
return this is KtDeclaration &&
(this as? KtProperty)?.hasBackingField() ?: true &&
this !is KtFunctionLiteral &&
this !is KtDestructuringDeclaration
}
private companion object {
val INTENTION_NAME_PREFIX = "AndroidLint"
val SUPPRESS_LINT_MESSAGE = "android.lint.fix.suppress.lint.api.annotation"
val FQNAME_SUPPRESS_LINT = FqName(SdkConstants.FQCN_SUPPRESS_LINT)
}
}
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.lint
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.util.PathUtil
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2016 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.lint
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.util.PathUtil
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
import java.io.File
abstract class AbstractKotlinLintTest : KotlinAndroidTestCase() {
override fun setUp() {
super.setUp()
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
(myFixture as CodeInsightTestFixtureImpl).setVirtualFileFilter { false } // Allow access to tree elements.
ConfigLibraryUtil.configureKotlinRuntime(myModule)
ConfigLibraryUtil.addLibrary(myModule, "androidExtensionsRuntime", "dist/kotlinc/lib", arrayOf("android-extensions-runtime.jar"))
}
override fun tearDown() {
ConfigLibraryUtil.unConfigureKotlinRuntime(myModule)
ConfigLibraryUtil.removeLibrary(myModule, "androidExtensionsRuntime")
super.tearDown()
}
fun doTest(path: String) {
val ktFile = File(path)
val fileText = ktFile.readText()
val mainInspectionClassName = findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty class name")
val dependencies = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "// DEPENDENCY: ")
val inspectionClassNames = mutableListOf(mainInspectionClassName)
for (i in 2..100) {
val className = findStringWithPrefixes(ktFile.readText(), "// INSPECTION_CLASS$i: ") ?: break
inspectionClassNames += className
}
myFixture.enableInspections(*inspectionClassNames.map { className ->
val inspectionClass = Class.forName(className)
inspectionClass.newInstance() as InspectionProfileEntry
}.toTypedArray())
val additionalResourcesDir = File(ktFile.parentFile, getTestName(true))
if (additionalResourcesDir.exists()) {
for (file in additionalResourcesDir.listFiles()) {
if (file.isFile) {
myFixture.copyFileToProject(file.absolutePath, file.name)
}
else if (file.isDirectory) {
myFixture.copyDirectoryToProject(file.absolutePath, file.name)
}
}
}
val virtualFile = myFixture.copyFileToProject(ktFile.absolutePath, "src/${PathUtil.getFileName(path)}")
myFixture.configureFromExistingVirtualFile(virtualFile)
dependencies.forEach { dependency ->
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
}
myFixture.checkHighlighting(true, false, true)
}
}
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.android.quickfix
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.PathUtil
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
import org.jetbrains.android.inspections.lint.AndroidLintInspectionBase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2017 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.quickfix
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.PathUtil
import org.jetbrains.android.inspections.klint.AndroidLintInspectionBase
import org.jetbrains.kotlin.android.KotlinAndroidTestCase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
abstract class AbstractAndroidLintQuickfixTest : KotlinAndroidTestCase() {
override fun setUp() {
AndroidLintInspectionBase.invalidateInspectionShortName2IssueMap()
super.setUp()
}
fun doTest(path: String) {
val fileText = FileUtil.loadFile(File(path), true)
val intentionText = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INTENTION_TEXT: ") ?: error("Empty intention text")
val mainInspectionClassName = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// INSPECTION_CLASS: ") ?: error("Empty inspection class name")
val dependency = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// DEPENDENCY: ")
val intentionAvailable = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// INTENTION_NOT_AVAILABLE")
val inspection = Class.forName(mainInspectionClassName).newInstance() as InspectionProfileEntry
myFixture.enableInspections(inspection)
val sourceFile = myFixture.copyFileToProject(path, "src/${PathUtil.getFileName(path)}")
myFixture.configureFromExistingVirtualFile(sourceFile)
if (dependency != null) {
val (dependencyFile, dependencyTargetPath) = dependency.split(" -> ").map(String::trim)
myFixture.copyFileToProject("${PathUtil.getParentPath(path)}/$dependencyFile", "src/$dependencyTargetPath")
}
if (intentionAvailable) {
val intention = myFixture.getAvailableIntention(intentionText) ?: error("Failed to find intention")
myFixture.launchAction(intention)
myFixture.checkResultByFile(path + ".expected")
}
else {
assertNull("Intention should not be available", myFixture.availableIntentions.find { it.text == intentionText })
}
}
}
+1
View File
@@ -46,6 +46,7 @@ dependencies {
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
}
sourceSets {
+66
View File
@@ -0,0 +1,66 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testRuntime(intellijDep())
compileOnly(project(":idea"))
compileOnly(project(":idea:idea-jvm"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend.script"))
compile(project(":js:js.frontend"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijPluginDep("Groovy"))
compileOnly(intellijPluginDep("junit"))
testCompile(projectTests(":idea"))
testCompile(projectTests(":idea:idea-test-framework"))
testCompile(intellijPluginDep("gradle"))
testCompileOnly(intellijPluginDep("Groovy"))
testCompileOnly(intellijDep())
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-android"))
testRuntime(project(":plugins:kapt3-idea"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
// TODO: the order of the plugins matters here, consider avoiding order-dependency
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("testng"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
}
sourceSets {
"main" {
projectDefault()
resources.srcDir("res").apply { include("**") }
}
"test" { projectDefault() }
}
testsJar()
projectTest {
workingDir = rootDir
useAndroidSdk()
}
configureInstrumentation()
@@ -41,7 +41,7 @@ object ThreadTrackerPatcherForTeamCityTesting {
patched.compareAndSet(false, true)
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool()
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(true)
// Check setup was successful and patching isn't needed
val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2016 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.idea
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory
import com.intellij.testFramework.ThreadTracker
import java.lang.reflect.Modifier
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.atomic.AtomicBoolean
/*
Workaround for ThreadTracker.checkLeak() failures on TeamCity.
Currently TeamCity runs tests in classloader without boot.jar where IdeaForkJoinWorkerThreadFactory is defined. That
makes ForkJoinPool.commonPool() silently ignores java.util.concurrent.ForkJoinPool.common.threadFactory
(because of java.lang.ClassNotFoundException) option during factory initialization.
Standard names for ForkJoinPool threads doesn't pass ThreadTracker.checkLeak() check and that ruins tests constantly.
As it's allowed to reorder tests on TeamCity and any test can be first at some point, this patch should be applied at
some common place.
*/
object ThreadTrackerPatcherForTeamCityTesting {
private val patched = AtomicBoolean(false)
fun patchThreadTracker() {
if (patched.get()) return
patched.compareAndSet(false, true)
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool()
// Check setup was successful and patching isn't needed
val commonPoolFactoryName = ForkJoinPool.commonPool().factory::class.java.name
if (commonPoolFactoryName == IdeaForkJoinWorkerThreadFactory::class.java.name) {
return
}
try {
val wellKnownOffendersField = try {
ThreadTracker::class.java.getDeclaredField("wellKnownOffenders")
}
catch (communityPropertyNotFoundEx: NoSuchFieldException) {
ThreadTracker::class.java.declaredFields.single {
Modifier.isStatic(it.modifiers) && MutableSet::class.java.isAssignableFrom(it.type)
}
}
wellKnownOffendersField.isAccessible = true
@Suppress("UNCHECKED_CAST")
val wellKnownOffenders = wellKnownOffendersField.get(null) as MutableSet<String>
wellKnownOffenders.add("ForkJoinPool.commonPool-worker-")
println("Patching ThreadTracker was successful")
}
catch (e: NoSuchFieldException) {
println("Patching ThreadTracker failed: " + e)
}
catch (e: IllegalAccessException) {
println("Patching ThreadTracker failed: " + e)
}
}
}
@@ -50,7 +50,7 @@ class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>("kotlin
override fun getState() = this
override fun get() = this
override fun loadState(state: KotlinDebuggerSettings?) {
if (state != null) XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
override fun loadState(state: KotlinDebuggerSettings) {
XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.SimpleConfigurable
import com.intellij.openapi.util.Getter
import com.intellij.util.xmlb.XmlSerializerUtil
import com.intellij.xdebugger.XDebuggerUtil
import com.intellij.xdebugger.settings.DebuggerSettingsCategory
import com.intellij.xdebugger.settings.XDebuggerSettings
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSteppingConfigurableUi
@State(name = "KotlinDebuggerSettings", storages = arrayOf(Storage("kotlin_debug.xml")))
class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>("kotlin_debugger"), Getter<KotlinDebuggerSettings> {
var DEBUG_RENDER_DELEGATED_PROPERTIES: Boolean = true
var DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES: Boolean = true
var DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED: Boolean = false
companion object {
fun getInstance(): KotlinDebuggerSettings {
return XDebuggerUtil.getInstance()?.getDebuggerSettings(KotlinDebuggerSettings::class.java)!!
}
}
override fun createConfigurables(category: DebuggerSettingsCategory): Collection<Configurable?> {
return when (category) {
DebuggerSettingsCategory.STEPPING ->
listOf(SimpleConfigurable.create(
"reference.idesettings.debugger.kotlin.stepping",
"Kotlin",
KotlinSteppingConfigurableUi::class.java,
this))
DebuggerSettingsCategory.DATA_VIEWS ->
listOf(SimpleConfigurable.create(
"reference.idesettings.debugger.kotlin.data.view",
"Kotlin",
KotlinDelegatedPropertyRendererConfigurableUi::class.java,
this))
else -> listOf()
}
}
override fun getState() = this
override fun get() = this
override fun loadState(state: KotlinDebuggerSettings?) {
if (state != null) XmlSerializerUtil.copyBean<KotlinDebuggerSettings>(state, this)
}
}
@@ -93,7 +93,7 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
semaphore.down()
val nameRef = AtomicReference<KotlinType>()
val worker = object : KotlinRuntimeTypeEvaluator(
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator!!
) {
override fun typeCalculationFinished(type: KotlinType?) {
nameRef.set(type)
@@ -0,0 +1,434 @@
/*
* Copyright 2010-2016 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.idea.debugger.evaluate
import com.intellij.debugger.DebuggerManagerEx
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
import com.intellij.debugger.engine.evaluation.TextWithImports
import com.intellij.debugger.engine.events.DebuggerCommandImpl
import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTypesUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.concurrency.Semaphore
import com.intellij.xdebugger.XDebuggerManager
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
import com.sun.jdi.*
import org.jetbrains.annotations.TestOnly
import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.refactoring.j2k
import org.jetbrains.kotlin.idea.refactoring.j2kText
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
import org.jetbrains.kotlin.j2k.AfterConversionPass
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.concurrent.atomic.AtomicReference
class KotlinCodeFragmentFactory : CodeFragmentFactory() {
override fun createCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
val contextElement = getWrappedContextElement(project, context)
if (contextElement == null) {
LOG.warn("CodeFragment with null context created:\noriginalContext = ${context?.getElementTextWithContext()}")
}
val codeFragment = if (item.kind == CodeFragmentKind.EXPRESSION) {
KtExpressionCodeFragment(
project,
"fragment.kt",
item.text,
initImports(item.imports),
contextElement
)
} else {
KtBlockCodeFragment(
project,
"fragment.kt",
item.text,
initImports(item.imports),
contextElement
)
}
codeFragment.putCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR, { expression: KtExpression ->
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
val debuggerSession = debuggerContext.debuggerSession
if (debuggerSession == null || debuggerContext.suspendContext == null) {
null
} else {
val semaphore = Semaphore()
semaphore.down()
val nameRef = AtomicReference<KotlinType>()
val worker = object : KotlinRuntimeTypeEvaluator(
null, expression, debuggerContext, ProgressManager.getInstance().progressIndicator
) {
override fun typeCalculationFinished(type: KotlinType?) {
nameRef.set(type)
semaphore.up()
}
}
debuggerContext.debugProcess?.managerThread?.invoke(worker)
for (i in 0..50) {
ProgressManager.checkCanceled()
if (semaphore.waitFor(20)) break
}
nameRef.get()
}
})
if (contextElement != null && contextElement !is KtElement) {
codeFragment.putCopyableUserData(KtCodeFragment.FAKE_CONTEXT_FOR_JAVA_FILE, {
val emptyFile = createFakeFileWithJavaContextElement("", contextElement)
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
val debuggerSession = debuggerContext.debuggerSession
if ((debuggerSession == null || debuggerContext.suspendContext == null) && !ApplicationManager.getApplication().isUnitTestMode) {
LOG.warn("Couldn't create fake context element for java file, debugger isn't paused on breakpoint")
return@putCopyableUserData emptyFile
}
val frameDescriptor = getFrameInfo(contextElement, debuggerContext)
if (frameDescriptor == null) {
LOG.warn("Couldn't get info about 'this' and local variables for ${debuggerContext.sourcePosition.file.name}:${debuggerContext.sourcePosition.line}")
return@putCopyableUserData emptyFile
}
val receiverTypeReference =
frameDescriptor.thisObject?.let { createKotlinProperty(project, "this_0", it.type().name(), it) }?.typeReference
val receiverTypeText = receiverTypeReference?.let { "${it.text}." } ?: ""
val kotlinVariablesText =
frameDescriptor.visibleVariables.entries.associate { it.key.name() to it.value }.kotlinVariablesAsText(project)
val fakeFunctionText = "fun ${receiverTypeText}_java_locals_debug_fun_() {\n$kotlinVariablesText\n}"
val fakeFile = createFakeFileWithJavaContextElement(fakeFunctionText, contextElement)
val fakeFunction = fakeFile.declarations.firstOrNull() as? KtFunction
val fakeContext = (fakeFunction?.bodyExpression as? KtBlockExpression)?.statements?.lastOrNull()
return@putCopyableUserData wrapContextIfNeeded(project, contextElement, fakeContext) ?: emptyFile
})
}
return codeFragment
}
private fun getFrameInfo(contextElement: PsiElement?, debuggerContext: DebuggerContextImpl): FrameInfo? {
val semaphore = Semaphore()
semaphore.down()
var frameInfo: FrameInfo? = null
val worker = object : DebuggerCommandImpl() {
override fun action() {
try {
val frame = if (ApplicationManager.getApplication().isUnitTestMode)
contextElement?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.frameProxy?.stackFrame
else
debuggerContext.frameProxy?.stackFrame
val visibleVariables = frame?.let {
val values = it.getValues(it.visibleVariables())
values.filterValues { it != null }
} ?: emptyMap()
frameInfo = FrameInfo(frame?.thisObject(), visibleVariables)
} catch (ignored: AbsentInformationException) {
// Debug info unavailable
} finally {
semaphore.up()
}
}
}
debuggerContext.debugProcess?.managerThread?.invoke(worker)
for (i in 0..50) {
if (semaphore.waitFor(20)) break
}
return frameInfo
}
private class FrameInfo(val thisObject: Value?, val visibleVariables: Map<LocalVariable, Value>)
private fun initImports(imports: String?): String? {
if (imports != null && !imports.isEmpty()) {
return imports.split(KtCodeFragment.IMPORT_SEPARATOR)
.mapNotNull { fixImportIfNeeded(it) }
.joinToString(KtCodeFragment.IMPORT_SEPARATOR)
}
return null
}
private fun fixImportIfNeeded(import: String): String? {
// skip arrays
if (import.endsWith("[]")) {
return fixImportIfNeeded(import.removeSuffix("[]").trim())
}
// skip primitive types
if (PsiTypesUtil.boxIfPossible(import) != import) {
return null
}
return import
}
private fun getWrappedContextElement(project: Project, context: PsiElement?): PsiElement? {
val newContext = getContextElement(context)
if (newContext !is KtElement) return newContext
return wrapContextIfNeeded(project, context, newContext)
}
override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
val kotlinCodeFragment = createCodeFragment(item, context, project)
if (PsiTreeUtil.hasErrorElements(kotlinCodeFragment) && kotlinCodeFragment is KtExpressionCodeFragment) {
val javaExpression = try {
PsiElementFactory.SERVICE.getInstance(project).createExpressionFromText(item.text, context)
} catch (e: IncorrectOperationException) {
null
}
val importList = try {
kotlinCodeFragment.importsAsImportList()?.let {
(PsiFileFactory.getInstance(project).createFileFromText(
"dummy.java", JavaFileType.INSTANCE, it.text
) as? PsiJavaFile)?.importList
}
} catch (e: IncorrectOperationException) {
null
}
if (javaExpression != null && !PsiTreeUtil.hasErrorElements(javaExpression)) {
var convertedFragment: KtExpressionCodeFragment? = null
project.executeWriteCommand("Convert java expression to kotlin in Evaluate Expression") {
try {
val newText = javaExpression.j2kText()
val newImports = importList?.j2kText()
if (newText != null) {
convertedFragment = KtExpressionCodeFragment(
project,
kotlinCodeFragment.name,
newText,
newImports,
kotlinCodeFragment.context
)
AfterConversionPass(project, J2kPostProcessor(formatCode = false)).run(convertedFragment!!, range = null)
}
} catch (e: Throwable) {
// ignored because text can be invalid
LOG.error("Couldn't convert expression:\n`${javaExpression.text}`", e)
}
}
return convertedFragment ?: kotlinCodeFragment
}
}
return kotlinCodeFragment
}
override fun isContextAccepted(contextElement: PsiElement?): Boolean {
return when {
// PsiCodeBlock -> DummyHolder -> originalElement
contextElement is PsiCodeBlock -> isContextAccepted(contextElement.context?.context)
contextElement == null -> false
contextElement.language == KotlinFileType.INSTANCE.language -> true
contextElement.language == JavaFileType.INSTANCE.language -> {
getKotlinJvmRuntimeMarkerClass(contextElement.project, contextElement.resolveScope) != null
}
else -> false
}
}
override fun getFileType(): KotlinFileType = KotlinFileType.INSTANCE
override fun getEvaluatorBuilder() = KotlinEvaluationBuilder
companion object {
private val LOG = Logger.getInstance(this::class.java)
val LABEL_VARIABLE_VALUE_KEY: Key<Value> = Key.create<Value>("_label_variable_value_key_")
private const val DEBUG_LABEL_SUFFIX: String = "_DebugLabel"
@TestOnly
val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS")
fun getContextElement(elementAt: PsiElement?): PsiElement? {
if (elementAt == null) return null
if (elementAt is PsiCodeBlock) {
return getContextElement(elementAt.context?.context)
}
if (elementAt is KtLightClass) {
return getContextElement(elementAt.kotlinOrigin)
}
val containingFile = elementAt.containingFile
if (containingFile is PsiJavaFile) return elementAt
if (containingFile !is KtFile) return null
// elementAt can be PsiWhiteSpace when codeFragment is created from line start offset (in case of first opening EE window)
val lineStartOffset = if (elementAt is PsiWhiteSpace || elementAt is PsiComment) {
PsiTreeUtil.skipSiblingsForward(elementAt, PsiWhiteSpace::class.java, PsiComment::class.java)?.textOffset
?: elementAt.textOffset
} else {
elementAt.textOffset
}
fun KtElement.takeIfAcceptedAsCodeFragmentContext() = takeIf { KotlinEditorTextProvider.isAcceptedAsCodeFragmentContext(it) }
PsiTreeUtil.findElementOfClassAtOffset(containingFile, lineStartOffset, KtExpression::class.java, false)
?.takeIfAcceptedAsCodeFragmentContext()
?.let { return CodeInsightUtils.getTopmostElementAtOffset(it, lineStartOffset, KtExpression::class.java) }
KotlinEditorTextProvider.findExpressionInner(elementAt, true)
?.takeIfAcceptedAsCodeFragmentContext()
?.let { return it }
return containingFile
}
//internal for tests
fun createCodeFragmentForLabeledObjects(project: Project, markupMap: Map<*, ValueMarkup>): Pair<String, Map<String, Value>> {
@Suppress("UNCHECKED_CAST")
val variables = markupMap.entries.associate {
val (value, markup) = it
"${markup.text}$DEBUG_LABEL_SUFFIX" to value as? Value
}.filterValues { it != null } as Map<String, Value>
return variables.kotlinVariablesAsText(project) to variables
}
private fun Map<String, Value>.kotlinVariablesAsText(project: Project): String {
val sb = StringBuilder()
val psiNameHelper = PsiNameHelper.getInstance(project)
for ((variableName, variableValue) in entries) {
if (!psiNameHelper.isIdentifier(variableName)) continue
val variableTypeName = variableValue.type()?.name() ?: continue
val kotlinProperty = createKotlinProperty(project, variableName, variableTypeName, variableValue) ?: continue
sb.append("${kotlinProperty.text}\n")
}
sb.append("val _debug_context_val = 1\n")
return sb.toString()
}
private fun createKotlinProperty(project: Project, variableName: String, variableTypeName: String, value: Value): KtProperty? {
val actualClassDescriptor = value.asValue().asmType.getClassDescriptor(GlobalSearchScope.allScope(project))
if (actualClassDescriptor != null && actualClassDescriptor.defaultType.arguments.isEmpty()) {
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(actualClassDescriptor.defaultType.makeNullable())
return KtPsiFactory(project).createProperty(variableName.quoteIfNeeded(), renderedType, false)
}
fun String.addArraySuffix() = if (value is ArrayReference) this + "[]" else this
val className = variableTypeName.replace("$", ".").substringBefore("[]")
val classType = PsiType.getTypeByName(className, project, GlobalSearchScope.allScope(project))
val type = (if (value !is PrimitiveValue && classType.resolve() == null)
CommonClassNames.JAVA_LANG_OBJECT
else
className).addArraySuffix()
val field = PsiElementFactory.SERVICE.getInstance(project)
.createField(variableName, PsiType.getTypeByName(type, project, GlobalSearchScope.allScope(project)))
val ktField = field.j2k() as? KtProperty
ktField?.modifierList?.delete()
return ktField
}
}
private fun wrapContextIfNeeded(project: Project, originalContext: PsiElement?, newContext: KtElement?): KtElement? {
val markupMap: Map<*, ValueMarkup>? =
if (ApplicationManager.getApplication().isUnitTestMode)
NodeDescriptorImpl.getMarkupMap(originalContext?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.debugProcess)
else
(XDebuggerManager.getInstance(project).currentSession as? XDebugSessionImpl)?.valueMarkers?.allMarkers
if (markupMap == null || markupMap.isEmpty()) return newContext
val (text, labels) = createCodeFragmentForLabeledObjects(project, markupMap)
if (text.isEmpty()) return newContext
return createWrappingContext(text, labels, newContext, project)
}
private fun createFakeFileWithJavaContextElement(funWithLocalVariables: String, javaContext: PsiElement): KtFile {
val javaFile = javaContext.containingFile as? PsiJavaFile
val sb = StringBuilder()
javaFile?.packageName?.takeUnless { it.isBlank() }?.let {
sb.append("package ").append(it.quoteIfNeeded()).append("\n")
}
javaFile?.importList?.let { sb.append(it.text).append("\n") }
sb.append(funWithLocalVariables)
return KtPsiFactory(javaContext.project).createAnalyzableFile("fakeFileForJavaContextInDebugger.kt", sb.toString(), javaContext)
}
// internal for test
private fun createWrappingContext(
newFragmentText: String,
labels: Map<String, Value>,
originalContext: KtElement?,
project: Project
): KtElement? {
val codeFragment = KtPsiFactory(project).createBlockCodeFragment(newFragmentText, originalContext)
codeFragment.accept(object : KtTreeVisitorVoid() {
override fun visitProperty(property: KtProperty) {
val reference = labels[property.name]
if (reference != null) {
property.putUserData(LABEL_VARIABLE_VALUE_KEY, reference)
}
}
})
return codeFragment.getContentElement().statements.lastOrNull()
}
}
@@ -133,9 +133,13 @@ class KotlinLanguageInjector(
if (parts.ranges.isEmpty()) return
InjectorUtils.registerInjection(language, parts.ranges, file, registrar)
InjectorUtils.registerSupport(support, false, registrar)
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
if (parts.isUnparsable) java.lang.Boolean.TRUE else null)
InjectorUtils.registerSupport(support, false, ktHost, language)
InjectorUtils.putInjectedFileUserData(
ktHost,
language,
InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
if (parts.isUnparsable) java.lang.Boolean.TRUE else null
)
}
else {
InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar)
@@ -0,0 +1,413 @@
/*
* Copyright 2010-2016 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.idea.injection
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.lang.injection.MultiHostInjector
import com.intellij.lang.injection.MultiHostRegistrar
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.text.StringUtilRt
import com.intellij.patterns.PatternCondition
import com.intellij.patterns.PatternConditionPlus
import com.intellij.patterns.PsiClassNamePatternCondition
import com.intellij.patterns.ValuePatternCondition
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiTreeUtil
import org.intellij.plugins.intelliLang.Configuration
import org.intellij.plugins.intelliLang.inject.InjectorUtils
import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport
import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry
import org.intellij.plugins.intelliLang.inject.config.BaseInjection
import org.intellij.plugins.intelliLang.inject.config.InjectionPlace
import org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport
import org.intellij.plugins.intelliLang.util.AnnotationUtilEx
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.patterns.KotlinFunctionPattern
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.runInReadActionWithWriteActionPriority
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.util.aliasImportMap
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinLanguageInjector(
private val configuration: Configuration,
private val project: Project,
private val temporaryPlacesRegistry: TemporaryPlacesRegistry
) : MultiHostInjector {
companion object {
private val STRING_LITERALS_REGEXP = "\"([^\"]*)\"".toRegex()
private val ABSENT_KOTLIN_INJECTION = BaseInjection("ABSENT_KOTLIN_BASE_INJECTION")
}
var annotationInjectionsEnabled = Registry.`is`("kotlin.annotation.injection.enabled", false)
private val kotlinSupport: KotlinLanguageInjectionSupport? by lazy {
ArrayList(InjectorUtils.getActiveInjectionSupports()).filterIsInstance(KotlinLanguageInjectionSupport::class.java).firstOrNull()
}
private data class KotlinCachedInjection(val modificationCount: Long, val baseInjection: BaseInjection)
private var KtStringTemplateExpression.cachedInjectionWithModification: KotlinCachedInjection? by UserDataProperty(
Key.create<KotlinCachedInjection>("CACHED_INJECTION_WITH_MODIFICATION"))
override fun getLanguagesToInject(registrar: MultiHostRegistrar, context: PsiElement) {
val ktHost: KtStringTemplateExpression = context as? KtStringTemplateExpression ?: return
if (!context.isValidHost) return
val support = kotlinSupport ?: return
if (!ProjectRootsUtil.isInProjectOrLibSource(ktHost.containingFile.originalFile)) return
val needImmediateAnswer = with(ApplicationManager.getApplication()) { isDispatchThread && !isUnitTestMode }
val kotlinCachedInjection = ktHost.cachedInjectionWithModification
val modificationCount = PsiManager.getInstance(project).modificationTracker.modificationCount
val baseInjection = when {
needImmediateAnswer -> {
// Can't afford long counting or typing will be laggy. Force cache reuse even if it's outdated.
kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION
}
kotlinCachedInjection != null && (modificationCount == kotlinCachedInjection.modificationCount) ->
// Cache is up-to-date
kotlinCachedInjection.baseInjection
else -> {
fun computeAndCache(): BaseInjection {
val computedInjection = computeBaseInjection(ktHost, support, registrar) ?: ABSENT_KOTLIN_INJECTION
ktHost.cachedInjectionWithModification = KotlinCachedInjection(modificationCount, computedInjection)
return computedInjection
}
if (ApplicationManager.getApplication().isReadAccessAllowed && ProgressManager.getInstance().progressIndicator == null) {
// The action cannot be canceled by caller and by internal checkCanceled() calls.
// Force creating new indicator that is canceled on write action start, otherwise there might be lags in typing.
runInReadActionWithWriteActionPriority(::computeAndCache) ?: kotlinCachedInjection?.baseInjection ?: ABSENT_KOTLIN_INJECTION
}
else {
computeAndCache()
}
}
}
if (baseInjection == ABSENT_KOTLIN_INJECTION) {
return
}
val language = InjectorUtils.getLanguageByString(baseInjection.injectedLanguageId) ?: return
if (ktHost.hasInterpolation()) {
val file = ktHost.containingKtFile
val parts = splitLiteralToInjectionParts(baseInjection, ktHost) ?: return
if (parts.ranges.isEmpty()) return
InjectorUtils.registerInjection(language, parts.ranges, file, registrar)
InjectorUtils.registerSupport(support, false, registrar)
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
if (parts.isUnparsable) java.lang.Boolean.TRUE else null)
}
else {
InjectorUtils.registerInjectionSimple(ktHost, baseInjection, support, registrar)
}
}
@Suppress("FoldInitializerAndIfToElvis")
private fun computeBaseInjection(
ktHost: KtStringTemplateExpression,
support: KotlinLanguageInjectionSupport,
registrar: MultiHostRegistrar): BaseInjection? {
val containingFile = ktHost.containingFile
val tempInjectedLanguage = temporaryPlacesRegistry.getLanguageFor(ktHost, containingFile)
if (tempInjectedLanguage != null) {
InjectorUtils.putInjectedFileUserData(registrar, LanguageInjectionSupport.TEMPORARY_INJECTED_LANGUAGE, tempInjectedLanguage)
return BaseInjection(support.id).apply {
injectedLanguageId = tempInjectedLanguage.id
prefix = tempInjectedLanguage.prefix
suffix = tempInjectedLanguage.suffix
}
}
return findInjectionInfo(ktHost)?.toBaseInjection(support)
}
override fun elementsToInjectIn(): List<Class<out PsiElement>> {
return listOf(KtStringTemplateExpression::class.java)
}
private fun findInjectionInfo(place: KtElement, originalHost: Boolean = true): InjectionInfo? {
return injectWithExplicitCodeInstruction(place)
?: injectWithCall(place)
?: injectInAnnotationCall(place)
?: injectWithReceiver(place)
?: injectWithVariableUsage(place, originalHost)
}
private fun injectWithExplicitCodeInstruction(host: KtElement): InjectionInfo? {
val support = kotlinSupport ?: return null
return InjectionInfo.fromBaseInjection(support.findCommentInjection(host)) ?: support.findAnnotationInjectionLanguageId(host)
}
private fun injectWithReceiver(host: KtElement): InjectionInfo? {
val qualifiedExpression = host.parent as? KtDotQualifiedExpression ?: return null
if (qualifiedExpression.receiverExpression != host) return null
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
val callee = callExpression.calleeExpression ?: return null
if (isAnalyzeOff()) return null
val kotlinInjections = configuration.getInjections(KOTLIN_SUPPORT_ID)
val calleeName = callee.text
val possibleNames = collectPossibleNames(kotlinInjections)
if (calleeName !in possibleNames) {
return null
}
for (reference in callee.references) {
ProgressManager.checkCanceled()
val resolvedTo = reference.resolve()
if (resolvedTo is KtFunction) {
val injectionInfo = findInjection(resolvedTo.receiverTypeReference, kotlinInjections)
if (injectionInfo != null) {
return injectionInfo
}
}
}
return null
}
private fun collectPossibleNames(injections: List<BaseInjection>): Set<String> {
val result = HashSet<String>()
for (injection in injections) {
val injectionPlaces = injection.injectionPlaces
for (place in injectionPlaces) {
val placeStr = place.toString()
val literals = STRING_LITERALS_REGEXP.findAll(placeStr).map { it.groupValues[1] }
result.addAll(literals)
}
}
return result
}
private fun injectWithVariableUsage(host: KtElement, originalHost: Boolean): InjectionInfo? {
// Given place is not original host of the injection so we stop to prevent stepping through indirect references
if (!originalHost) return null
val ktProperty = host.parent as? KtProperty ?: return null
if (ktProperty.initializer != host) return null
if (isAnalyzeOff()) return null
val searchScope = LocalSearchScope(arrayOf(ktProperty.containingFile), "", true)
return ReferencesSearch.search(ktProperty, searchScope).asSequence().mapNotNull { psiReference ->
val element = psiReference.element as? KtElement ?: return@mapNotNull null
findInjectionInfo(element, false)
}.firstOrNull()
}
private fun injectWithCall(host: KtElement): InjectionInfo? {
val ktHost: KtElement = host
val argument = ktHost.parent as? KtValueArgument ?: return null
val callExpression = PsiTreeUtil.getParentOfType(ktHost, KtCallElement::class.java) ?: return null
val callee = getNameReference(callExpression.calleeExpression) ?: return null
if (isAnalyzeOff()) return null
for (reference in callee.references) {
ProgressManager.checkCanceled()
val resolvedTo = reference.resolve()
if (resolvedTo is PsiMethod) {
val injectionForJavaMethod = injectionForJavaMethod(argument, resolvedTo)
if (injectionForJavaMethod != null) {
return injectionForJavaMethod
}
}
else if (resolvedTo is KtFunction) {
val injectionForJavaMethod = injectionForKotlinCall(argument, resolvedTo, reference)
if (injectionForJavaMethod != null) {
return injectionForJavaMethod
}
}
}
return null
}
private fun getNameReference(callee: KtExpression?): KtNameReferenceExpression? {
if (callee is KtConstructorCalleeExpression)
return callee.constructorReferenceExpression as? KtNameReferenceExpression
return callee as? KtNameReferenceExpression
}
private fun injectInAnnotationCall(host: KtElement): InjectionInfo? {
if (!annotationInjectionsEnabled) return null
val argument = host.parent as? KtValueArgument ?: return null
val annotationEntry = argument.parent.parent as? KtCallElement ?: return null
if (!fastCheckInjectionsExists(annotationEntry)) return null
val calleeExpression = annotationEntry.calleeExpression ?: return null
val callee = getNameReference(calleeExpression)?.mainReference?.resolve()
when (callee) {
is PsiClass -> {
val psiClass = callee as? PsiClass ?: return null
val argumentName = argument.getArgumentName()?.asName?.identifier ?: "value"
val method = psiClass.findMethodsByName(argumentName, false).singleOrNull() ?: return null
return findInjection(method, configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID))
}
else -> return null
}
}
private fun injectionForJavaMethod(argument: KtValueArgument, javaMethod: PsiMethod): InjectionInfo? {
val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
val psiParameter = javaMethod.parameterList.parameters.getOrNull(argumentIndex) ?: return null
val injectionInfo = findInjection(psiParameter, configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID))
if (injectionInfo != null) {
return injectionInfo
}
val annotations = AnnotationUtilEx.getAnnotationFrom(
psiParameter,
configuration.advancedConfiguration.languageAnnotationPair,
true)
if (annotations.isNotEmpty()) {
return processAnnotationInjectionInner(annotations)
}
return null
}
private fun injectionForKotlinCall(argument: KtValueArgument, ktFunction: KtFunction, reference: PsiReference): InjectionInfo? {
val argumentIndex = (argument.parent as KtValueArgumentList).arguments.indexOf(argument)
val ktParameter = ktFunction.valueParameters.getOrNull(argumentIndex) ?: return null
val patternInjection = findInjection(ktParameter, configuration.getInjections(KOTLIN_SUPPORT_ID))
if (patternInjection != null) {
return patternInjection
}
// Found psi element after resolve can be obtained from compiled declaration but annotations parameters are lost there.
// Search for original descriptor from reference.
val ktReference = reference as? KtReference ?: return null
val bindingContext = ktReference.element.analyze(BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
val functionDescriptor = ktReference.resolveToDescriptors(bindingContext).singleOrNull() as? FunctionDescriptor ?: return null
val parameterDescriptor = functionDescriptor.valueParameters.getOrNull(argumentIndex) ?: return null
val injectAnnotation = parameterDescriptor.annotations.findAnnotation(FqName(AnnotationUtil.LANGUAGE)) ?: return null
val languageId = injectAnnotation.argumentValue("value")?.safeAs<StringValue>()?.value ?: return null
return InjectionInfo(languageId, null, null)
}
private fun findInjection(element: PsiElement?, injections: List<BaseInjection>): InjectionInfo? {
for (injection in injections) {
if (injection.acceptsPsiElement(element)) {
return InjectionInfo(injection.injectedLanguageId, injection.prefix, injection.suffix)
}
}
return null
}
private fun isAnalyzeOff(): Boolean {
return configuration.advancedConfiguration.dfaOption == Configuration.DfaOption.OFF
}
private fun processAnnotationInjectionInner(annotations: Array<PsiAnnotation>): InjectionInfo? {
val id = AnnotationUtilEx.calcAnnotationValue(annotations, "value")
val prefix = AnnotationUtilEx.calcAnnotationValue(annotations, "prefix")
val suffix = AnnotationUtilEx.calcAnnotationValue(annotations, "suffix")
return InjectionInfo(id, prefix, suffix)
}
private val injectableTargetClassShortNames = CachedValuesManager.getManager(project)
.createCachedValue({
CachedValueProvider.Result.create(HashSet<String>().apply {
for (injection in configuration.getInjections(JavaLanguageInjectionSupport.JAVA_SUPPORT_ID)) {
for (injectionPlace in injection.injectionPlaces) {
for (targetClassFQN in retrieveJavaPlaceTargetClassesFQNs(injectionPlace)) {
add(StringUtilRt.getShortName(targetClassFQN))
}
}
}
for (injection in configuration.getInjections(org.jetbrains.kotlin.idea.injection.KOTLIN_SUPPORT_ID)) {
for (injectionPlace in injection.injectionPlaces) {
for (targetClassFQN in retrieveKotlinPlaceTargetClassesFQNs(injectionPlace)) {
add(StringUtilRt.getShortName(targetClassFQN))
}
}
}
}, configuration)
}, false)
private fun fastCheckInjectionsExists(annotationEntry: KtCallElement): Boolean {
val referencedName = getNameReference(annotationEntry.calleeExpression)?.getReferencedName() ?: return false
val annotationShortName = annotationEntry.containingKtFile.aliasImportMap()[referencedName].singleOrNull() ?: referencedName
return annotationShortName in injectableTargetClassShortNames.value
}
private fun retrieveJavaPlaceTargetClassesFQNs(place: InjectionPlace): Collection<String> {
val classCondition = place.elementPattern.condition.conditions.firstOrNull { it.debugMethodName == "definedInClass" }
as? PatternConditionPlus<*, *> ?: return emptyList()
val psiClassNamePatternCondition = classCondition.valuePattern.condition.conditions.
firstIsInstanceOrNull<PsiClassNamePatternCondition>() ?: return emptyList()
val valuePatternCondition = psiClassNamePatternCondition.namePattern.condition.conditions.firstIsInstanceOrNull<ValuePatternCondition<String>>() ?: return emptyList()
return valuePatternCondition.values
}
private fun retrieveKotlinPlaceTargetClassesFQNs(place: InjectionPlace): Collection<String> {
val classNames = SmartList<String>()
fun collect(condition: PatternCondition<*>) {
when (condition) {
is PatternConditionPlus<*, *> -> condition.valuePattern.condition.conditions.forEach { collect(it) }
is KotlinFunctionPattern.DefinedInClassCondition -> classNames.add(condition.fqName)
}
}
place.elementPattern.condition.conditions.forEach { collect(it) }
return classNames
}
}
+1
View File
@@ -47,6 +47,7 @@ dependencies {
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
testRuntime(intellijPluginDep("smali"))
}
sourceSets {
+61
View File
@@ -0,0 +1,61 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":core:util.runtime"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:util"))
compile(project(":compiler:cli-common"))
compile(project(":kotlin-build-common"))
compile(project(":js:js.frontend"))
compile(project(":idea"))
compile(project(":idea:idea-jvm"))
compile(project(":idea:idea-jps-common"))
compileOnly(intellijDep())
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
testCompile(projectTests(":idea"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(projectTests(":idea:idea-test-framework"))
testCompileOnly(intellijDep())
testCompileOnly(intellijPluginDep("maven"))
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(project(":idea:idea-jvm"))
testRuntime(project(":idea:idea-android"))
testRuntime(project(":plugins:android-extensions-ide"))
testRuntime(project(":plugins:lint"))
testRuntime(project(":sam-with-receiver-ide-plugin"))
testRuntime(project(":allopen-ide-plugin"))
testRuntime(project(":noarg-ide-plugin"))
testRuntime(intellijDep())
// TODO: the order of the plugins matters here, consider avoiding order-dependency
testRuntime(intellijPluginDep("junit"))
testRuntime(intellijPluginDep("testng"))
testRuntime(intellijPluginDep("properties"))
testRuntime(intellijPluginDep("gradle"))
testRuntime(intellijPluginDep("Groovy"))
testRuntime(intellijPluginDep("coverage"))
testRuntime(intellijPluginDep("maven"))
testRuntime(intellijPluginDep("android"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
testsJar()
projectTest {
workingDir = rootDir
}
@@ -373,18 +373,16 @@ private enum class SourceType {
@State(
name = "AutoImportedSourceRoots",
storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))]
storages = [(Storage(file = StoragePathMacros.MODULE_FILE))]
)
class KotlinImporterComponent : PersistentStateComponent<KotlinImporterComponent.State> {
class State(var directories: List<String> = ArrayList())
val addedSources: MutableSet<String> = Collections.synchronizedSet(HashSet<String>())
override fun loadState(state: State?) {
override fun loadState(state: State) {
addedSources.clear()
if (state != null) {
addedSources.addAll(state.directories)
}
addedSources.addAll(state.directories)
}
override fun getState(): State {
@@ -0,0 +1,396 @@
/*
* Copyright 2010-2017 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.idea.maven
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.StoragePathMacros
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.CompilerModuleExtension
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.OrderEnumerator
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.util.AsyncResult
import com.intellij.util.PathUtil
import org.jdom.Element
import org.jdom.Text
import org.jetbrains.idea.maven.importing.MavenImporter
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter
import org.jetbrains.idea.maven.model.MavenPlugin
import org.jetbrains.idea.maven.project.*
import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.idea.facet.*
import org.jetbrains.kotlin.idea.framework.detectLibraryKind
import org.jetbrains.kotlin.idea.framework.libraryKind
import org.jetbrains.kotlin.idea.maven.configuration.KotlinMavenConfigurator
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
import java.util.*
interface MavenProjectImportHandler {
companion object : ProjectExtensionDescriptor<MavenProjectImportHandler>(
"org.jetbrains.kotlin.mavenProjectImportHandler",
MavenProjectImportHandler::class.java
)
operator fun invoke(facet: KotlinFacet, mavenProject: MavenProject)
}
class KotlinMavenImporter : MavenImporter(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID) {
companion object {
const val KOTLIN_PLUGIN_GROUP_ID = "org.jetbrains.kotlin"
const val KOTLIN_PLUGIN_ARTIFACT_ID = "kotlin-maven-plugin"
const val KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG = "sourceDirs"
}
override fun preProcess(
module: Module,
mavenProject: MavenProject,
changes: MavenProjectChanges,
modifiableModelsProvider: IdeModifiableModelsProvider
) {
}
override fun process(
modifiableModelsProvider: IdeModifiableModelsProvider,
module: Module,
rootModel: MavenRootModelAdapter,
mavenModel: MavenProjectsTree,
mavenProject: MavenProject,
changes: MavenProjectChanges,
mavenProjectToModuleName: MutableMap<MavenProject, String>,
postTasks: MutableList<MavenProjectsProcessorTask>
) {
if (changes.plugins) {
contributeSourceDirectories(mavenProject, module, rootModel)
}
}
override fun postProcess(
module: Module,
mavenProject: MavenProject,
changes: MavenProjectChanges,
modifiableModelsProvider: IdeModifiableModelsProvider
) {
super.postProcess(module, mavenProject, changes, modifiableModelsProvider)
if (changes.dependencies) {
scheduleDownloadStdlibSources(mavenProject, module)
val targetLibraryKind = detectPlatformByExecutions(mavenProject)?.libraryKind
if (targetLibraryKind != null) {
modifiableModelsProvider.getModifiableRootModel(module).orderEntries().forEachLibrary { library ->
if ((library as LibraryEx).kind == null) {
val model = modifiableModelsProvider.getModifiableLibraryModel(library) as LibraryEx.ModifiableModelEx
detectLibraryKind(model.getFiles(OrderRootType.CLASSES))?.let { model.kind = it }
}
true
}
}
}
configureFacet(mavenProject, modifiableModelsProvider, module)
}
private fun scheduleDownloadStdlibSources(mavenProject: MavenProject, module: Module) {
// TODO: here we have to process all kotlin libraries but for now we only handle standard libraries
val artifacts = mavenProject.dependencyArtifactIndex.data[KOTLIN_PLUGIN_GROUP_ID]?.values?.flatMap { it.filter { it.isResolved } }
?: emptyList()
val librariesWithNoSources = ArrayList<Library>()
OrderEnumerator.orderEntries(module).forEachLibrary { library ->
if (library.modifiableModel.getFiles(OrderRootType.SOURCES).isEmpty()) {
librariesWithNoSources.add(library)
}
true
}
val libraryNames = librariesWithNoSources.mapTo(HashSet()) { it.name }
val toBeDownloaded = artifacts.filter { it.libraryName in libraryNames }
if (toBeDownloaded.isNotEmpty()) {
MavenProjectsManager.getInstance(module.project)
.scheduleArtifactsDownloading(listOf(mavenProject), toBeDownloaded, true, false, AsyncResult())
}
}
private fun configureJSOutputPaths(
mavenProject: MavenProject,
modifiableRootModel: ModifiableRootModel,
facetSettings: KotlinFacetSettings,
mavenPlugin: MavenPlugin
) {
fun parentPath(path: String): String =
File(path).absoluteFile.parentFile.absolutePath
val sharedOutputFile = mavenPlugin.configurationElement?.getChild("outputFile")?.text
val compilerModuleExtension = modifiableRootModel.getModuleExtension(CompilerModuleExtension::class.java) ?: return
val buildDirectory = mavenProject.buildDirectory
val executions = mavenPlugin.executions
executions.forEach {
val explicitOutputFile = it.configurationElement?.getChild("outputFile")?.text ?: sharedOutputFile
if (PomFile.KotlinGoals.Js in it.goals) {
// see org.jetbrains.kotlin.maven.K2JSCompilerMojo
val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/js/${mavenProject.mavenId.artifactId}.js")
compilerModuleExtension.setCompilerOutputPath(parentPath(outputFilePath))
facetSettings.productionOutputPath = outputFilePath
}
if (PomFile.KotlinGoals.TestJs in it.goals) {
// see org.jetbrains.kotlin.maven.KotlinTestJSCompilerMojo
val outputFilePath = PathUtil.toSystemDependentName(explicitOutputFile ?: "$buildDirectory/test-js/${mavenProject.mavenId.artifactId}-tests.js")
compilerModuleExtension.setCompilerOutputPathForTests(parentPath(outputFilePath))
facetSettings.testOutputPath = outputFilePath
}
}
}
private fun getCompilerArgumentsByConfigurationElement(
mavenProject: MavenProject,
configuration: Element?,
platform: TargetPlatformKind<*>
): List<String> {
val arguments = when (platform) {
is TargetPlatformKind.Jvm -> K2JVMCompilerArguments()
TargetPlatformKind.JavaScript -> K2JSCompilerArguments()
TargetPlatformKind.Common -> K2MetadataCompilerArguments()
}
arguments.apiVersion = configuration?.getChild("apiVersion")?.text ?:
mavenProject.properties["kotlin.compiler.apiVersion"]?.toString()
arguments.languageVersion = configuration?.getChild("languageVersion")?.text ?:
mavenProject.properties["kotlin.compiler.languageVersion"]?.toString()
arguments.multiPlatform = configuration?.getChild("multiPlatform")?.text?.trim()?.toBoolean() ?: false
arguments.suppressWarnings = configuration?.getChild("nowarn")?.text?.trim()?.toBoolean() ?: false
configuration?.getChild("experimentalCoroutines")?.text?.trim()?.let {
arguments.coroutinesState = it
}
when (arguments) {
is K2JVMCompilerArguments -> {
arguments.classpath = configuration?.getChild("classpath")?.text
arguments.jdkHome = configuration?.getChild("jdkHome")?.text
arguments.jvmTarget = configuration?.getChild("jvmTarget")?.text ?:
mavenProject.properties["kotlin.compiler.jvmTarget"]?.toString()
arguments.javaParameters = configuration?.getChild("javaParameters")?.text?.toBoolean() ?: false
}
is K2JSCompilerArguments -> {
arguments.sourceMap = configuration?.getChild("sourceMap")?.text?.trim()?.toBoolean() ?: false
arguments.sourceMapPrefix = configuration?.getChild("sourceMapPrefix")?.text?.trim() ?: ""
arguments.sourceMapEmbedSources = configuration?.getChild("sourceMapEmbedSources")?.text?.trim() ?: "inlining"
arguments.outputFile = configuration?.getChild("outputFile")?.text
arguments.metaInfo = configuration?.getChild("metaInfo")?.text?.trim()?.toBoolean() ?: false
arguments.moduleKind = configuration?.getChild("moduleKind")?.text
arguments.main = configuration?.getChild("main")?.text
}
}
val additionalArgs = SmartList<String>().apply {
val argsElement = configuration?.getChild("args")
argsElement?.content?.forEach { argElement ->
when (argElement) {
is Text -> {
argElement.text?.let { addAll(splitArgumentString(it)) }
}
is Element -> {
if (argElement.name == "arg") {
addIfNotNull(argElement.text)
}
}
}
}
}
parseCommandLineArguments(additionalArgs, arguments)
return ArgumentUtils.convertArgumentsToStringList(arguments)
}
private val compilationGoals = listOf(
PomFile.KotlinGoals.Compile,
PomFile.KotlinGoals.TestCompile,
PomFile.KotlinGoals.Js,
PomFile.KotlinGoals.TestJs,
PomFile.KotlinGoals.MetaData
)
private fun configureFacet(mavenProject: MavenProject, modifiableModelsProvider: IdeModifiableModelsProvider, module: Module) {
val mavenPlugin = mavenProject.findPlugin(KotlinMavenConfigurator.GROUP_ID, KotlinMavenConfigurator.MAVEN_PLUGIN_ID) ?: return
val compilerVersion = mavenPlugin.version ?: LanguageVersion.LATEST_STABLE.versionString
val kotlinFacet = module.getOrCreateFacet(modifiableModelsProvider, false)
val platform = detectPlatform(mavenProject)
kotlinFacet.configureFacet(compilerVersion, LanguageFeature.Coroutines.defaultState, platform, modifiableModelsProvider)
val facetSettings = kotlinFacet.configuration.settings
val configuredPlatform = kotlinFacet.configuration.settings.targetPlatformKind!!
val configuration = mavenPlugin.configurationElement
val sharedArguments = getCompilerArgumentsByConfigurationElement(mavenProject, configuration, configuredPlatform)
val executionArguments = mavenPlugin.executions
?.firstOrNull { it.goals.any { it in compilationGoals } }
?.configurationElement?.let { getCompilerArgumentsByConfigurationElement(mavenProject, it, configuredPlatform) }
parseCompilerArgumentsToFacet(sharedArguments, emptyList(), kotlinFacet, modifiableModelsProvider)
if (executionArguments != null) {
parseCompilerArgumentsToFacet(executionArguments, emptyList(), kotlinFacet, modifiableModelsProvider)
}
if (facetSettings.compilerArguments is K2JSCompilerArguments) {
configureJSOutputPaths(mavenProject, modifiableModelsProvider.getModifiableRootModel(module), facetSettings, mavenPlugin)
}
MavenProjectImportHandler.getInstances(module.project).forEach { it(kotlinFacet, mavenProject) }
setImplementedModuleName(kotlinFacet, mavenProject, module)
kotlinFacet.noVersionAutoAdvance()
}
private fun detectPlatform(mavenProject: MavenProject) =
detectPlatformByExecutions(mavenProject) ?: detectPlatformByLibraries(mavenProject)
private fun detectPlatformByExecutions(mavenProject: MavenProject): TargetPlatformKind<*>? {
return mavenProject.findPlugin(KOTLIN_PLUGIN_GROUP_ID, KOTLIN_PLUGIN_ARTIFACT_ID)?.executions?.flatMap { it.goals }
?.mapNotNull { goal ->
when (goal) {
PomFile.KotlinGoals.Compile, PomFile.KotlinGoals.TestCompile -> TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]
PomFile.KotlinGoals.Js, PomFile.KotlinGoals.TestJs -> TargetPlatformKind.JavaScript
PomFile.KotlinGoals.MetaData -> TargetPlatformKind.Common
else -> null
}
}?.distinct()?.singleOrNull()
}
private fun detectPlatformByLibraries(mavenProject: MavenProject): TargetPlatformKind<*>? {
return TargetPlatformKind.ALL_PLATFORMS.firstOrNull {
it.mavenLibraryIds.any { mavenProject.findDependencies(KOTLIN_PLUGIN_GROUP_ID, it).isNotEmpty() }
}
}
// TODO in theory it should work like this but it doesn't as it couldn't unmark source roots that are not roots anymore.
// I believe this is something should be done by the underlying maven importer implementation or somewhere else in the IDEA
// For now there is a contributeSourceDirectories implementation that deals with the issue
// see https://youtrack.jetbrains.com/issue/IDEA-148280
// override fun collectSourceRoots(mavenProject: MavenProject, result: PairConsumer<String, JpsModuleSourceRootType<*>>) {
// for ((type, dir) in collectSourceDirectories(mavenProject)) {
// val jpsType: JpsModuleSourceRootType<*> = when (type) {
// SourceType.PROD -> JavaSourceRootType.SOURCE
// SourceType.TEST -> JavaSourceRootType.TEST_SOURCE
// }
//
// result.consume(dir, jpsType)
// }
// }
private fun contributeSourceDirectories(mavenProject: MavenProject, module: Module, rootModel: MavenRootModelAdapter) {
val directories = collectSourceDirectories(mavenProject)
val toBeAdded = directories.map { it.second }.toSet()
val state = module.kotlinImporterComponent
for ((type, dir) in directories) {
if (rootModel.getSourceFolder(File(dir)) == null) {
val jpsType: JpsModuleSourceRootType<*> = when (type) {
SourceType.TEST -> JavaSourceRootType.TEST_SOURCE
SourceType.PROD -> JavaSourceRootType.SOURCE
}
rootModel.addSourceFolder(dir, jpsType)
}
}
state.addedSources.filter { it !in toBeAdded }.forEach {
rootModel.unregisterAll(it, true, true)
state.addedSources.remove(it)
}
state.addedSources.addAll(toBeAdded)
}
private fun collectSourceDirectories(mavenProject: MavenProject): List<Pair<SourceType, String>> =
mavenProject.plugins.filter { it.isKotlinPlugin() }.flatMap { plugin ->
plugin.configurationElement.sourceDirectories().map { SourceType.PROD to it } +
plugin.executions.flatMap { execution ->
execution.configurationElement.sourceDirectories().map { execution.sourceType() to it }
}
}.distinct()
private fun setImplementedModuleName(kotlinFacet: KotlinFacet, mavenProject: MavenProject, module: Module) {
if (kotlinFacet.configuration.settings.targetPlatformKind == TargetPlatformKind.Common) {
kotlinFacet.configuration.settings.implementedModuleNames = emptyList()
} else {
val manager = MavenProjectsManager.getInstance(module.project)
val mavenDependencies = mavenProject.dependencies.mapNotNull { manager?.findProject(it) }
val implemented = mavenDependencies.filter { detectPlatformByExecutions(it) == TargetPlatformKind.Common }
kotlinFacet.configuration.settings.implementedModuleNames = implemented.map { manager.findModule(it)?.name ?: it.displayName }
}
}
}
private fun MavenPlugin.isKotlinPlugin() =
groupId == KotlinMavenImporter.KOTLIN_PLUGIN_GROUP_ID && artifactId == KotlinMavenImporter.KOTLIN_PLUGIN_ARTIFACT_ID
private fun Element?.sourceDirectories(): List<String> =
this?.getChildren(KotlinMavenImporter.KOTLIN_PLUGIN_SOURCE_DIRS_CONFIG)?.flatMap { it.children ?: emptyList() }?.map { it.textTrim }
?: emptyList()
private fun MavenPlugin.Execution.sourceType() =
goals.map { if (isTestGoalName(it)) SourceType.TEST else SourceType.PROD }
.distinct()
.singleOrNull() ?: SourceType.PROD
private fun isTestGoalName(goalName: String) = goalName.startsWith("test-")
private enum class SourceType {
PROD, TEST
}
@State(
name = "AutoImportedSourceRoots",
storages = [(Storage(id = "other", file = StoragePathMacros.MODULE_FILE))]
)
class KotlinImporterComponent : PersistentStateComponent<KotlinImporterComponent.State> {
class State(var directories: List<String> = ArrayList())
val addedSources: MutableSet<String> = Collections.synchronizedSet(HashSet<String>())
override fun loadState(state: State?) {
addedSources.clear()
if (state != null) {
addedSources.addAll(state.directories)
}
}
override fun getState(): State {
return State(addedSources.sorted())
}
}
private val Module.kotlinImporterComponent: KotlinImporterComponent
get() = getComponent(KotlinImporterComponent::class.java) ?: throw IllegalStateException("No maven importer state configured")
@@ -26,6 +26,7 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>maventest</groupId>
<artifactId>maventest</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<kotlin.version>$VERSION$</kotlin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>wrong-goal</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -69,7 +69,6 @@ abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFix
}
})
}
CodeInsightTestCase.fixTemplates()
}
override fun tearDown() {
@@ -0,0 +1,231 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.test
import com.intellij.codeInsight.CodeInsightTestCase
import com.intellij.codeInsight.daemon.impl.EditorTracker
import com.intellij.ide.startup.impl.StartupManagerImpl
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.psi.PsiManager
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.ProjectScope
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.LoggedErrorProcessor
import org.apache.log4j.Logger
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.facet.configureFacet
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestMetadata
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.rethrow
import java.io.File
import java.io.IOException
import java.util.*
import kotlin.reflect.full.findAnnotation
abstract class KotlinLightCodeInsightFixtureTestCase : KotlinLightCodeInsightFixtureTestCaseBase() {
private val exceptions = ArrayList<Throwable>()
protected val module: Module get() = myFixture.module
protected open val captureExceptions = true
override fun setUp() {
super.setUp()
(StartupManager.getInstance(project) as StartupManagerImpl).runPostStartupActivities()
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
project.getComponent(EditorTracker::class.java)?.projectOpened()
invalidateLibraryCache(project)
if (captureExceptions) {
LoggedErrorProcessor.setNewInstance(object : LoggedErrorProcessor() {
override fun processError(message: String?, t: Throwable?, details: Array<out String>?, logger: Logger) {
exceptions.addIfNotNull(t)
super.processError(message, t, details, logger)
}
})
}
CodeInsightTestCase.fixTemplates()
}
override fun tearDown() {
LoggedErrorProcessor.restoreDefaultProcessor()
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
doKotlinTearDown(project) {
super.tearDown()
}
if (exceptions.isNotEmpty()) {
exceptions.forEach { it.printStackTrace() }
throw AssertionError("Exceptions in other threads happened")
}
}
override fun getProjectDescriptor(): LightProjectDescriptor
= getProjectDescriptorFromFileDirective()
protected fun getProjectDescriptorFromTestName(): LightProjectDescriptor {
val testName = StringUtil.toLowerCase(getTestName(false))
if (testName.endsWith("runtime")) {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
}
else if (testName.endsWith("stdlib")) {
return ProjectDescriptorWithStdlibSources.INSTANCE
}
return KotlinLightProjectDescriptor.INSTANCE
}
protected fun getProjectDescriptorFromFileDirective(): LightProjectDescriptor {
if (!isAllFilesPresentInTest()) {
try {
val fileText = FileUtil.loadFile(File(testDataPath, fileName()), true)
val withLibraryDirective = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileText, "WITH_LIBRARY:")
if (!withLibraryDirective.isEmpty()) {
return SdkAndMockLibraryProjectDescriptor(
PluginTestCaseBase.getTestDataPathBase() + "/" + withLibraryDirective.get(
0
), true
)
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_SOURCES")) {
return ProjectDescriptorWithStdlibSources.INSTANCE
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_KOTLIN_TEST")) {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_KOTLIN_TEST
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_FULL_JDK")) {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME_WITH_REFLECT")) {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_WITH_REFLECT
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "RUNTIME") ||
InTextDirectivesUtils.isDirectiveDefined(fileText, "WITH_RUNTIME")) {
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "JS")) {
return KotlinStdJSProjectDescriptor
}
else if (InTextDirectivesUtils.isDirectiveDefined(fileText, "ENABLE_MULTIPLATFORM")) {
return KotlinProjectDescriptorWithFacet.KOTLIN_STABLE_WITH_MULTIPLATFORM
}
}
catch (e: IOException) {
throw rethrow(e)
}
}
return KotlinLightProjectDescriptor.INSTANCE
}
protected fun isAllFilesPresentInTest(): Boolean = KotlinTestUtils.isAllFilesPresentTest(getTestName(false))
protected open fun fileName(): String
= KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt")
protected fun performNotWriteEditorAction(actionId: String): Boolean {
val dataContext = (myFixture.editor as EditorEx).dataContext
val managerEx = ActionManagerEx.getInstanceEx()
val action = managerEx.getAction(actionId)
val event = AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, Presentation(), managerEx, 0)
action.update(event)
if (!event.presentation.isEnabled) {
return false
}
managerEx.fireBeforeActionPerformed(action, dataContext, event)
action.actionPerformed(event)
managerEx.fireAfterActionPerformed(action, dataContext, event)
return true
}
override fun getTestDataPath(): String {
return this::class.findAnnotation<TestMetadata>()?.value ?: super.getTestDataPath()
}
}
fun configureCompilerOptions(fileText: String, project: Project, module: Module) {
val version = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// LANGUAGE_VERSION: ")
val jvmTarget = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// JVM_TARGET: ")
val options = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// COMPILER_ARGUMENTS: ")
if (version != null || jvmTarget != null || options != null) {
configureLanguageAndApiVersion(project, module, version ?: LanguageVersion.LATEST_STABLE.versionString)
val facetSettings = KotlinFacet.get(module)!!.configuration.settings
if (jvmTarget != null) {
(facetSettings.compilerArguments as K2JVMCompilerArguments).jvmTarget = jvmTarget
}
if (options != null) {
val compilerSettings = facetSettings.compilerSettings ?: CompilerSettings().also {
facetSettings.compilerSettings = it
}
compilerSettings.additionalArguments = options
facetSettings.updateMergedArguments()
}
}
}
fun configureLanguageAndApiVersion(
project: Project,
module: Module,
languageVersion: String,
apiVersion: String? = null
) {
val accessToken = WriteAction.start()
try {
val modelsProvider = IdeModifiableModelsProviderImpl(project)
val facet = module.getOrCreateFacet(modelsProvider, useProjectSettings = false)
facet.configureFacet(languageVersion, LanguageFeature.State.DISABLED, null, modelsProvider)
if (apiVersion != null) {
facet.configuration.settings.apiLevel = LanguageVersion.fromVersionString(apiVersion)
}
modelsProvider.commit()
}
finally {
accessToken.finish()
}
}
fun Project.allKotlinFiles(): List<KtFile> {
val virtualFiles = FileTypeIndex.getFiles(KotlinFileType.INSTANCE, ProjectScope.getProjectScope(this))
return virtualFiles
.map { PsiManager.getInstance(this).findFile(it) }
.filterIsInstance<KtFile>()
}
fun Project.findFileWithCaret() =
allKotlinFiles().single { "<caret>" in VfsUtilCore.loadText(it.virtualFile) }
+4 -113
View File
@@ -1,118 +1,9 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintExternalAnnotator"/>
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintExternalAnnotator"/>
</extensions>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAddJavascriptInterface" displayName="addJavascriptInterface Called" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAddJavascriptInterfaceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAllowAllHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAllowAllHostnameVerifierInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAlwaysShowAction" displayName="Usage of showAsAction=always" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAlwaysShowActionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAppCompatMethod" displayName="Using Wrong AppCompat Method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAppCompatMethodInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAuthLeak" displayName="Code contains url auth" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAuthLeakInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBadHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBadHostnameVerifierInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBatteryLife" displayName="Battery Life Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBatteryLifeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitPrefEdits" displayName="Missing commit() on SharedPreference editor" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitTransaction" displayName="Missing commit() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCustomViewStyleable" displayName="Mismatched Styleable/Custom View Name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCustomViewStyleableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCutPasteId" displayName="Likely cut &amp; paste mistakes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCutPasteIdInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDefaultLocale" displayName="Implied default locale in case conversion" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDefaultLocaleInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDrawAllocation" displayName="Memory allocations within drawing code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintEasterEgg" displayName="Code contains easter egg" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintEasterEggInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedContentProvider" displayName="Content provider does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedContentProviderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedPreferenceActivity" displayName="PreferenceActivity should not be exported" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedPreferenceActivityInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedReceiver" displayName="Receiver does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedService" displayName="Exported service does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedServiceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintFloatMath" displayName="Using FloatMath instead of Math" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintFloatMathInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGetInstanceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGifUsageInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingApiWarning" displayName="Missing support for Google App Indexing Api" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingApiWarningInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingUrlErrorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingWarning" displayName="Missing support for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingWarningInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGrantAllUris" displayName="Content provider shares everything" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGrantAllUrisInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintHandlerLeak" displayName="Handler reference leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintHandlerLeakInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconColors" displayName="Icon colors do not follow the recommended visual style" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconColorsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDensities" displayName="Icon densities validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDensitiesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDipSize" displayName="Icon density-independent size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDipSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicates" displayName="Duplicated icons under different names" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicatesConfig" displayName="Identical bitmaps across various configurations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesConfigInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExpectedSize" displayName="Icon has incorrect size" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExpectedSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExtension" displayName="Icon format does not match the file extension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExtensionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLauncherShape" displayName="The launcher icon shape should use a distinct silhouette" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLauncherShapeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLocation" displayName="Image defined in density-independent drawable folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMissingDensityFolder" displayName="Missing density folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMissingDensityFolderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMixedNinePatch" displayName="Clashing PNG and 9-PNG files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMixedNinePatchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconNoDpi" displayName="Icon appears in both -nodpi and dpi folders" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconNoDpiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconXmlAndPng" displayName="Icon is specified both as .xml file and as a bitmap" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconXmlAndPngInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInconsistentLayout" displayName="Inconsistent Layouts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInconsistentLayoutInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInflateParams" displayName="Layout Inflation without a Parent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInlinedApi" displayName="Using inlined constants on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInvalidUsesTagAttribute" displayName="Invalid name attribute for uses element." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInvalidUsesTagAttributeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintJavascriptInterface" displayName="Missing @JavascriptInterface on methods" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLocalSuppress" displayName="@SuppressLint on invalid element" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogConditional" displayName="Unconditional Logging Calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogTagMismatch" displayName="Mismatched Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLongLogTag" displayName="Too Long Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMergeRootFrame" displayName="FrameLayout can be replaced with &lt;merge> tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMergeRootFrameInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingIntentFilterForMediaSearch" displayName="Missing intent-filter with action android.media.action.MEDIA_PLAY_FROM_SEARCH" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingIntentFilterForMediaSearchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingMediaBrowserServiceIntentFilter" displayName="Missing intent-filter with action android.media.browse.MediaBrowserService." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingMediaBrowserServiceIntentFilterInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingOnPlayFromSearch" displayName="Missing onPlayFromSearch." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingOnPlayFromSearchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingSuperCall" displayName="Missing Super Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintNewApi" displayName="Calling new methods on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverdraw" displayName="Overdraw: Painting regions more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverdrawInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverride" displayName="Method conflicts with new inherited method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverrideAbstract" displayName="Not overriding abstract methods on older platforms" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPackageManagerGetSignatures" displayName="Potential Multiple Certificate Exploit" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPackageManagerGetSignaturesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelClassLoader" displayName="Default Parcel Class Loader" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelClassLoaderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelCreator" displayName="Missing Parcelable CREATOR field" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPendingBindings" displayName="Missing Pending Bindings" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPendingBindingsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPluralsCandidate" displayName="Potential Plurals" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPluralsCandidateInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPrivateResource" displayName="Using private resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPrivateResourceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecycle" displayName="Missing recycle() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecyclerView" displayName="RecyclerView Problems" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecyclerViewInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRegistered" displayName="Class is not registered in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRegisteredInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRequiredSize" displayName="Missing layout_width or layout_height attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRequiredSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlCompat" displayName="Right-to-left text compatibility issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlCompatInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlEnabled" displayName="Using RTL attributes without enabling RTL support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlEnabledInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlHardcoded" displayName="Using left/right instead of start/end attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlHardcodedInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlSymmetry" displayName="Padding and margin symmetry" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlSymmetryInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSdCardPath" displayName="Hardcoded reference to /sdcard" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSecureRandom" displayName="Using a fixed seed with SecureRandom" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSecureRandomInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintServiceCast" displayName="Wrong system service casts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetJavaScriptEnabled" displayName="Using setJavaScriptEnabled" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetTextI18n" displayName="TextView Internationalization" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetTextI18nInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldReadable" displayName="File.setReadable() used to make file world-readable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldReadableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldWritable" displayName="File.setWritable() used to make file world-writable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldWritableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShiftFlags" displayName="Dangerous Flag Constant Declaration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShiftFlagsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShortAlarm" displayName="Short or Frequent Alarm" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShowToast" displayName="Toast created but not shown" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSimpleDateFormat" displayName="Implied locale in date format" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSimpleDateFormatInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSQLiteString" displayName="Using STRING instead of TEXT" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryCreateSocket" displayName="Insecure call to SSLCertificateSocketFactory.createSocket()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryCreateSocketInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryGetInsecure" displayName="Call to SSLCertificateSocketFactory.getInsecure()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryGetInsecureInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStopShip" displayName="Code contains STOPSHIP marker" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStopShipInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatCount" displayName="Formatting argument types incomplete or inconsistent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatCountInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatInvalid" displayName="Invalid format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatInvalidInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatMatches" displayName="String.format string doesn&apos;t match the XML format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatMatchesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSupportAnnotationUsage" displayName="Incorrect support annotation usage" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSuspiciousImport" displayName="&apos;import android.R&apos; statement" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSwitchIntDef" displayName="Missing @IntDef in Switch" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSwitchIntDefInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintTrustAllX509TrustManager" displayName="Insecure TLS/SSL trust manager" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintTrustAllX509TrustManagerInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUniqueConstants" displayName="Overlapping Enumeration Constants" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUniqueConstantsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnlocalizedSms" displayName="SMS phone number missing country code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnprotectedSMSBroadcastReceiver" displayName="Unprotected SMS BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnprotectedSMSBroadcastReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeDynamicallyLoadedCode" displayName="load used to dynamically load code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeDynamicallyLoadedCodeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeNativeCodeLocation" displayName="Native code outside library directory" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeNativeCodeLocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeProtectedBroadcastReceiver" displayName="Unsafe Protected BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeProtectedBroadcastReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnusedAttribute" displayName="Attribute unused on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnusedAttributeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseSparseArrays" displayName="HashMap can be replaced with SparseArray" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseValueOf" displayName="Should use valueOf instead of new" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintValidFragment" displayName="Fragment not instantiatable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintValidFragmentInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewConstructor" displayName="Missing View constructors for XML inflation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewHolder" displayName="View Holder Candidates" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewTag" displayName="Tagged object leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewTagInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldReadableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_READABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldReadableFilesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldWriteableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_WRITEABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldWriteableFilesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongCall" displayName="Using wrong draw/layout method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongViewCast" displayName="Mismatched view type" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongViewCastInspection"/>
<codeInspection.InspectionExtension implementation="org.jetbrains.android.inspections.klint.AndroidInspectionExtensionsFactory"/>
<extensions defaultExtensionNs="org.jetbrains.uast">
<uastLanguagePlugin implementation="org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin"/>
</extensions>
</idea-plugin>
+118
View File
@@ -0,0 +1,118 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintExternalAnnotator"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAddJavascriptInterface" displayName="addJavascriptInterface Called" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAddJavascriptInterfaceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAllowAllHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAllowAllHostnameVerifierInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAlwaysShowAction" displayName="Usage of showAsAction=always" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAlwaysShowActionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAppCompatMethod" displayName="Using Wrong AppCompat Method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAppCompatMethodInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintAuthLeak" displayName="Code contains url auth" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintAuthLeakInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBadHostnameVerifier" displayName="Insecure HostnameVerifier" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBadHostnameVerifierInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintBatteryLife" displayName="Battery Life Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintBatteryLifeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitPrefEdits" displayName="Missing commit() on SharedPreference editor" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitPrefEditsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCommitTransaction" displayName="Missing commit() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCommitTransactionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCustomViewStyleable" displayName="Mismatched Styleable/Custom View Name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCustomViewStyleableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintCutPasteId" displayName="Likely cut &amp; paste mistakes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintCutPasteIdInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDefaultLocale" displayName="Implied default locale in case conversion" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDefaultLocaleInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintDrawAllocation" displayName="Memory allocations within drawing code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintDrawAllocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintEasterEgg" displayName="Code contains easter egg" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintEasterEggInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedContentProvider" displayName="Content provider does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedContentProviderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedPreferenceActivity" displayName="PreferenceActivity should not be exported" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedPreferenceActivityInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedReceiver" displayName="Receiver does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintExportedService" displayName="Exported service does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintExportedServiceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintFloatMath" displayName="Using FloatMath instead of Math" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintFloatMathInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGetInstanceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGifUsageInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingApiWarning" displayName="Missing support for Google App Indexing Api" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingApiWarningInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingUrlErrorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGoogleAppIndexingWarning" displayName="Missing support for Google App Indexing" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGoogleAppIndexingWarningInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintGrantAllUris" displayName="Content provider shares everything" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintGrantAllUrisInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintHandlerLeak" displayName="Handler reference leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintHandlerLeakInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconColors" displayName="Icon colors do not follow the recommended visual style" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconColorsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDensities" displayName="Icon densities validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDensitiesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDipSize" displayName="Icon density-independent size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDipSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicates" displayName="Duplicated icons under different names" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconDuplicatesConfig" displayName="Identical bitmaps across various configurations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconDuplicatesConfigInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExpectedSize" displayName="Icon has incorrect size" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExpectedSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconExtension" displayName="Icon format does not match the file extension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconExtensionInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLauncherShape" displayName="The launcher icon shape should use a distinct silhouette" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLauncherShapeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconLocation" displayName="Image defined in density-independent drawable folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconLocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMissingDensityFolder" displayName="Missing density folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMissingDensityFolderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconMixedNinePatch" displayName="Clashing PNG and 9-PNG files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconMixedNinePatchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconNoDpi" displayName="Icon appears in both -nodpi and dpi folders" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconNoDpiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintIconXmlAndPng" displayName="Icon is specified both as .xml file and as a bitmap" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintIconXmlAndPngInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInconsistentLayout" displayName="Inconsistent Layouts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInconsistentLayoutInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInflateParams" displayName="Layout Inflation without a Parent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInflateParamsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInlinedApi" displayName="Using inlined constants on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInlinedApiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintInvalidUsesTagAttribute" displayName="Invalid name attribute for uses element." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintInvalidUsesTagAttributeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintJavascriptInterface" displayName="Missing @JavascriptInterface on methods" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintJavascriptInterfaceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLocalSuppress" displayName="@SuppressLint on invalid element" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLocalSuppressInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogConditional" displayName="Unconditional Logging Calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogConditionalInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLogTagMismatch" displayName="Mismatched Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLogTagMismatchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintLongLogTag" displayName="Too Long Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintLongLogTagInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMergeRootFrame" displayName="FrameLayout can be replaced with &lt;merge> tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMergeRootFrameInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingIntentFilterForMediaSearch" displayName="Missing intent-filter with action android.media.action.MEDIA_PLAY_FROM_SEARCH" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingIntentFilterForMediaSearchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingMediaBrowserServiceIntentFilter" displayName="Missing intent-filter with action android.media.browse.MediaBrowserService." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingMediaBrowserServiceIntentFilterInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingOnPlayFromSearch" displayName="Missing onPlayFromSearch." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingOnPlayFromSearchInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintMissingSuperCall" displayName="Missing Super Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintMissingSuperCallInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintNewApi" displayName="Calling new methods on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintNewApiInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverdraw" displayName="Overdraw: Painting regions more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverdrawInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverride" displayName="Method conflicts with new inherited method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintOverrideAbstract" displayName="Not overriding abstract methods on older platforms" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintOverrideAbstractInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPackageManagerGetSignatures" displayName="Potential Multiple Certificate Exploit" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPackageManagerGetSignaturesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelClassLoader" displayName="Default Parcel Class Loader" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelClassLoaderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintParcelCreator" displayName="Missing Parcelable CREATOR field" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintParcelCreatorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPendingBindings" displayName="Missing Pending Bindings" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPendingBindingsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPluralsCandidate" displayName="Potential Plurals" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPluralsCandidateInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintPrivateResource" displayName="Using private resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintPrivateResourceInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecycle" displayName="Missing recycle() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecycleInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRecyclerView" displayName="RecyclerView Problems" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRecyclerViewInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRegistered" displayName="Class is not registered in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRegisteredInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRequiredSize" displayName="Missing layout_width or layout_height attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRequiredSizeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlCompat" displayName="Right-to-left text compatibility issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlCompatInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlEnabled" displayName="Using RTL attributes without enabling RTL support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlEnabledInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlHardcoded" displayName="Using left/right instead of start/end attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlHardcodedInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintRtlSymmetry" displayName="Padding and margin symmetry" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintRtlSymmetryInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSdCardPath" displayName="Hardcoded reference to /sdcard" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSdCardPathInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSecureRandom" displayName="Using a fixed seed with SecureRandom" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSecureRandomInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintServiceCast" displayName="Wrong system service casts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintServiceCastInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetJavaScriptEnabled" displayName="Using setJavaScriptEnabled" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetJavaScriptEnabledInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetTextI18n" displayName="TextView Internationalization" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetTextI18nInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldReadable" displayName="File.setReadable() used to make file world-readable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldReadableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSetWorldWritable" displayName="File.setWritable() used to make file world-writable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSetWorldWritableInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShiftFlags" displayName="Dangerous Flag Constant Declaration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShiftFlagsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShortAlarm" displayName="Short or Frequent Alarm" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShortAlarmInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintShowToast" displayName="Toast created but not shown" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintShowToastInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSimpleDateFormat" displayName="Implied locale in date format" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSimpleDateFormatInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSQLiteString" displayName="Using STRING instead of TEXT" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSQLiteStringInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryCreateSocket" displayName="Insecure call to SSLCertificateSocketFactory.createSocket()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryCreateSocketInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSSLCertificateSocketFactoryGetInsecure" displayName="Call to SSLCertificateSocketFactory.getInsecure()" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSSLCertificateSocketFactoryGetInsecureInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStopShip" displayName="Code contains STOPSHIP marker" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStopShipInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatCount" displayName="Formatting argument types incomplete or inconsistent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatCountInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatInvalid" displayName="Invalid format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatInvalidInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintStringFormatMatches" displayName="String.format string doesn&apos;t match the XML format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintStringFormatMatchesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSupportAnnotationUsage" displayName="Incorrect support annotation usage" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSupportAnnotationUsageInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSuspiciousImport" displayName="&apos;import android.R&apos; statement" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSuspiciousImportInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintSwitchIntDef" displayName="Missing @IntDef in Switch" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintSwitchIntDefInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintTrustAllX509TrustManager" displayName="Insecure TLS/SSL trust manager" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintTrustAllX509TrustManagerInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUniqueConstants" displayName="Overlapping Enumeration Constants" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUniqueConstantsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnlocalizedSms" displayName="SMS phone number missing country code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnlocalizedSmsInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnprotectedSMSBroadcastReceiver" displayName="Unprotected SMS BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnprotectedSMSBroadcastReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeDynamicallyLoadedCode" displayName="load used to dynamically load code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeDynamicallyLoadedCodeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeNativeCodeLocation" displayName="Native code outside library directory" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeNativeCodeLocationInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnsafeProtectedBroadcastReceiver" displayName="Unsafe Protected BroadcastReceiver" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnsafeProtectedBroadcastReceiverInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUnusedAttribute" displayName="Attribute unused on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUnusedAttributeInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseSparseArrays" displayName="HashMap can be replaced with SparseArray" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseSparseArraysInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintUseValueOf" displayName="Should use valueOf instead of new" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintUseValueOfInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintValidFragment" displayName="Fragment not instantiatable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintValidFragmentInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewConstructor" displayName="Missing View constructors for XML inflation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewConstructorInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewHolder" displayName="View Holder Candidates" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewHolderInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintViewTag" displayName="Tagged object leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintViewTagInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldReadableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_READABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldReadableFilesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWorldWriteableFiles" displayName="openFileOutput() or similar call passing MODE_WORLD_WRITEABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWorldWriteableFilesInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongCall" displayName="Using wrong draw/layout method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongCallInspection"/>
<globalInspection language="kotlin" hasStaticDescription="true" shortName="AndroidKLintWrongViewCast" displayName="Mismatched view type" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidKLintWrongViewCastInspection"/>
<codeInspection.InspectionExtension implementation="org.jetbrains.android.inspections.klint.AndroidInspectionExtensionsFactory"/>
</extensions>
</idea-plugin>
+4
View File
@@ -100,6 +100,10 @@
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.android">
<androidLintQuickFixProvider implementation="org.jetbrains.kotlin.android.quickfix.KotlinAndroidQuickFixProvider" />
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
</extensions>
+106
View File
@@ -0,0 +1,106 @@
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="android-lint.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensions defaultExtensionNs="com.intellij">
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.navigation.KotlinAndroidGotoDeclarationHandler"/>
<moduleService serviceInterface="org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager"
serviceImplementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidLayoutXmlFileManager"/>
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor"/>
<errorHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsReportSubmitter"/>
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidGotoDeclarationHandler"/>
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection"
displayName="Illegal Android Identifier"
groupName="Kotlin Android"
enabledByDefault="true"
level="ERROR"/>
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.TypeParameterFindViewByIdInspection"
displayName="Cast can be converted to findViewById with type parameter"
groupName="Kotlin Android"
enabledByDefault="true"
cleanupTool="true"
level="WEAK WARNING"
language="kotlin" />
<codeInsight.lineMarkerProvider language="kotlin" implementationClass="org.jetbrains.kotlin.android.KotlinAndroidLineMarkerProvider"/>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.KotlinAndroidAddStringResource</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.AddActivityToManifest</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.AddServiceToManifest</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.AddBroadcastReceiverToManifest</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.ImplementParcelableAction</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.RemoveParcelableAction</className>
<category>Kotlin Android</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.android.intention.RedoParcelableAction</className>
<category>Kotlin Android</category>
</intentionAction>
<codeInsight.unresolvedReferenceQuickFixProvider
implementation="org.jetbrains.kotlin.android.inspection.KotlinAndroidResourceQuickFixProvider"/>
<lang.foldingBuilder language="kotlin" implementationClass="org.jetbrains.kotlin.android.folding.ResourceFoldingBuilder" />
<annotator language="kotlin" implementationClass="org.jetbrains.kotlin.android.AndroidResourceReferenceAnnotator" />
<referencesSearch implementation="org.jetbrains.kotlin.AndroidExtensionsReferenceSearchExecutor"/>
<externalProjectDataService implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleLibraryDataService"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsProjectResolverExtension"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidExtensionsExpressionCodegenExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.android.synthetic.AndroidExtensionPropertiesComponentContainerContributor"/>
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.IDEAndroidOnDestroyClassBuilderInterceptorExtension"/>
<packageFragmentProviderExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.res.IDEAndroidPackageFragmentProviderExtension"/>
<simpleNameReferenceExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidSimpleNameReferenceExtension"/>
<kotlinIndicesHelperExtension implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidIndicesHelperExtension"/>
<gradleProjectImportHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidExtensionsGradleImportHandler"/>
<quickFixContributor implementation="org.jetbrains.kotlin.android.quickfix.AndroidQuickFixRegistrar"/>
<projectConfigurator implementation="org.jetbrains.kotlin.android.configure.KotlinAndroidGradleModuleConfigurator"/>
<gradleModelFacade implementation="org.jetbrains.kotlin.android.configure.AndroidGradleModelFacade"/>
<completionInformationProvider implementation="org.jetbrains.kotlin.AndroidExtensionsCompletionInformationProvider" />
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/>
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
<androidDexer implementation="org.jetbrains.kotlin.android.debugger.AndroidDexerImpl"/>
<highlighterExtension implementation="org.jetbrains.kotlin.android.AndroidHighlighterExtension"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin.android.model">
<androidModuleInfoProvider implementation="org.jetbrains.kotlin.android.model.impl.AndroidModuleInfoProviderImpl"/>
</extensions>
</idea-plugin>
+1 -2
View File
@@ -6,7 +6,7 @@
<version>@snapshot@</version>
<vendor url="http://www.jetbrains.com">JetBrains</vendor>
<idea-version since-build="173.1" until-build="173.*"/>
<idea-version since-build="181.1" until-build="181.*"/>
<depends>com.intellij.modules.platform</depends>
<depends>com.intellij.modules.remoteServers</depends>
@@ -355,7 +355,6 @@
<internalFileTemplate name="Kotlin Class"/>
<internalFileTemplate name="Kotlin Enum"/>
<internalFileTemplate name="Kotlin Interface"/>
<internalFileTemplate name="Kotlin Script"/>
<gotoSymbolContributor implementation="org.jetbrains.kotlin.idea.goto.KotlinGotoSymbolContributor"/>
<gotoClassContributor implementation="org.jetbrains.kotlin.idea.goto.KotlinGotoClassContributor"/>
File diff suppressed because it is too large Load Diff
@@ -98,7 +98,7 @@ class JavaToKotlinAction : AnAction() {
var converterResult: JavaToKotlinConverter.FilesResult? = null
fun convert() {
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator)
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator!!)
}
@@ -117,7 +117,7 @@ class JavaToKotlinAction : AnAction() {
ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
runReadAction {
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator)
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator!!)
}
},
title,
@@ -0,0 +1,216 @@
/*
* 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.idea.actions
import com.intellij.codeInsight.navigation.NavigationUtil
import com.intellij.ide.scratch.ScratchFileService
import com.intellij.ide.scratch.ScratchRootType
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.ex.MessagesEx
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileVisitor
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiManager
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices
import org.jetbrains.kotlin.idea.j2k.J2kPostProcessor
import org.jetbrains.kotlin.idea.refactoring.toPsiFile
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.j2k.ConverterSettings
import org.jetbrains.kotlin.j2k.JavaToKotlinConverter
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import java.io.IOException
import java.util.*
class JavaToKotlinAction : AnAction() {
companion object {
private fun uniqueKotlinFileName(javaFile: VirtualFile): String {
val ioFile = File(javaFile.path.replace('/', File.separatorChar))
var i = 0
while (true) {
val fileName = javaFile.nameWithoutExtension + (if (i > 0) i else "") + ".kt"
if (!ioFile.resolveSibling(fileName).exists()) return fileName
i++
}
}
val title = "Convert Java to Kotlin"
private fun saveResults(javaFiles: List<PsiJavaFile>, convertedTexts: List<String>): List<VirtualFile> {
val result = ArrayList<VirtualFile>()
for ((psiFile, text) in javaFiles.zip(convertedTexts)) {
try {
val document = PsiDocumentManager.getInstance(psiFile.project).getDocument(psiFile)
if (document == null) {
MessagesEx.error(psiFile.project, "Failed to save conversion result: couldn't find document for " + psiFile.name).showLater()
continue
}
document.replaceString(0, document.textLength, text)
FileDocumentManager.getInstance().saveDocument(document)
val virtualFile = psiFile.virtualFile
if (ScratchRootType.getInstance().containsFile(virtualFile)) {
val mapping = ScratchFileService.getInstance().scratchesMapping
mapping.setMapping(virtualFile, KotlinFileType.INSTANCE.language)
}
else {
val fileName = uniqueKotlinFileName(virtualFile)
virtualFile.rename(this, fileName)
}
}
catch (e: IOException) {
MessagesEx.error(psiFile.project, e.message ?: "").showLater()
}
}
return result
}
fun convertFiles(javaFiles: List<PsiJavaFile>, project: Project, enableExternalCodeProcessing: Boolean = true): List<KtFile> {
var converterResult: JavaToKotlinConverter.FilesResult? = null
fun convert() {
val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices)
converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().progressIndicator)
}
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
::convert,
title,
true,
project)) return emptyList()
var externalCodeUpdate: (() -> Unit)? = null
if (enableExternalCodeProcessing && converterResult!!.externalCodeProcessing != null) {
val question = "Some code in the rest of your project may require corrections after performing this conversion. Do you want to find such code and correct it too?"
if (Messages.showOkCancelDialog(project, question, title, Messages.getQuestionIcon()) == Messages.OK) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
runReadAction {
externalCodeUpdate = converterResult!!.externalCodeProcessing!!.prepareWriteOperation(ProgressManager.getInstance().progressIndicator)
}
},
title,
true,
project)
}
}
return project.executeWriteCommand("Convert files from Java to Kotlin", null) {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project)
val newFiles = saveResults(javaFiles, converterResult!!.results)
externalCodeUpdate?.invoke()
PsiDocumentManager.getInstance(project).commitAllDocuments()
newFiles.singleOrNull()?.let {
FileEditorManager.getInstance(project).openFile(it, true)
}
newFiles.map { it.toPsiFile(project) as KtFile }
}
}
}
override fun actionPerformed(e: AnActionEvent) {
val javaFiles = selectedJavaFiles(e).filter { it.isWritable }.toList()
val project = CommonDataKeys.PROJECT.getData(e.dataContext)!!
val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull()
if (firstSyntaxError != null) {
val count = javaFiles.filter { PsiTreeUtil.hasErrorElements(it) }.count()
val question = firstSyntaxError.containingFile.name +
(if (count > 1) " and ${count - 1} more Java files" else " file") +
" contain syntax errors, the conversion result may be incorrect"
val okText = "Investigate Errors"
val cancelText = "Proceed with Conversion"
if (Messages.showOkCancelDialog(
project,
question,
title,
okText,
cancelText,
Messages.getWarningIcon()
) == Messages.OK) {
NavigationUtil.activateFileWithPsiElement(firstSyntaxError.navigationElement)
return
}
}
convertFiles(javaFiles, project)
}
override fun update(e: AnActionEvent) {
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return
val project = e.project ?: return
e.presentation.isEnabled = isAnyJavaFileSelected(project, virtualFiles)
}
private fun isAnyJavaFileSelected(project: Project, files: Array<VirtualFile>): Boolean {
val manager = PsiManager.getInstance(project)
if (files.any { manager.findFile(it) is PsiJavaFile && it.isWritable }) return true
return files.any { it.isDirectory && isAnyJavaFileSelected(project, it.children) }
}
private fun selectedJavaFiles(e: AnActionEvent): Sequence<PsiJavaFile> {
val virtualFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) ?: return sequenceOf()
val project = e.project ?: return sequenceOf()
return allJavaFiles(virtualFiles, project)
}
private fun allJavaFiles(filesOrDirs: Array<VirtualFile>, project: Project): Sequence<PsiJavaFile> {
val manager = PsiManager.getInstance(project)
return allFiles(filesOrDirs)
.asSequence()
.mapNotNull { manager.findFile(it) as? PsiJavaFile }
}
private fun allFiles(filesOrDirs: Array<VirtualFile>): Collection<VirtualFile> {
val result = ArrayList<VirtualFile>()
for (file in filesOrDirs) {
VfsUtilCore.visitChildrenRecursively(file, object : VirtualFileVisitor<Unit>() {
override fun visitFile(file: VirtualFile): Boolean {
result.add(file)
return true
}
})
}
return result
}
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtFile
class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) {
override fun beforeCharDeleted(c: Char, file: PsiFile, editor: Editor) {
if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return
val offset = editor?.caretModel?.offset ?: return
@@ -38,7 +38,7 @@ class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
editor.document.deleteString(offset, offset + 1)
}
override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean {
override fun charDeleted(c: Char, file: PsiFile, editor: Editor): Boolean {
return false
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2016 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.idea.editor
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtFile
class KotlinStringTemplateBackspaceHandler : BackspaceHandlerDelegate() {
override fun beforeCharDeleted(c: Char, file: PsiFile?, editor: Editor?) {
if (c != '{' || file !is KtFile || !CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) return
val offset = editor?.caretModel?.offset ?: return
val highlighter = (editor as EditorEx).highlighter
val iterator = highlighter.createIterator(offset)
if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_END) return
iterator.retreat()
if (iterator.tokenType != KtTokens.LONG_TEMPLATE_ENTRY_START) return
editor.document.deleteString(offset, offset + 1)
}
override fun charDeleted(c: Char, file: PsiFile?, editor: Editor?): Boolean {
return false
}
}
@@ -49,20 +49,9 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.ifEmpty
import java.util.ArrayList
import kotlin.collections.Collection
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashSet
import kotlin.collections.List
import kotlin.collections.Set
import kotlin.collections.emptyList
import kotlin.collections.emptySet
import kotlin.collections.filter
import kotlin.collections.flatMap
import kotlin.collections.forEach
import kotlin.collections.getOrPut
import kotlin.collections.listOf
import kotlin.collections.mapNotNullTo
import kotlin.collections.plus
class ChangeSuspendInHierarchyFix(
element: KtNamedFunction,
@@ -83,7 +72,7 @@ class ChangeSuspendInHierarchyFix(
private fun findAllFunctionToProcess(project: Project): Set<KtNamedFunction> {
val result = LinkedHashSet<KtNamedFunction>()
val progressIndicator = ProgressManager.getInstance().progressIndicator
val progressIndicator = ProgressManager.getInstance().progressIndicator!!
val function = element ?: return emptySet()
val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor
@@ -0,0 +1,193 @@
/*
* Copyright 2010-2017 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.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.ifEmpty
import java.util.ArrayList
import kotlin.collections.Collection
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashSet
import kotlin.collections.List
import kotlin.collections.Set
import kotlin.collections.emptyList
import kotlin.collections.emptySet
import kotlin.collections.filter
import kotlin.collections.flatMap
import kotlin.collections.forEach
import kotlin.collections.getOrPut
import kotlin.collections.listOf
import kotlin.collections.mapNotNullTo
import kotlin.collections.plus
class ChangeSuspendInHierarchyFix(
element: KtNamedFunction,
private val addModifier: Boolean
) : KotlinQuickFixAction<KtNamedFunction>(element) {
override fun getFamilyName(): String {
return if (addModifier) {
"Add 'suspend' modifier to all functions in hierarchy"
} else {
"Remove 'suspend' modifier from all functions in hierarchy"
}
}
override fun getText() = familyName
override fun startInWriteAction() = false
private fun findAllFunctionToProcess(project: Project): Set<KtNamedFunction> {
val result = LinkedHashSet<KtNamedFunction>()
val progressIndicator = ProgressManager.getInstance().progressIndicator
val function = element ?: return emptySet()
val functionDescriptor = function.unsafeResolveToDescriptor() as FunctionDescriptor
val baseFunctionDescriptors = functionDescriptor.findTopMostOverriddables()
baseFunctionDescriptors.forEach { baseFunctionDescriptor ->
val baseClassDescriptor = baseFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return@forEach
val baseClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, baseClassDescriptor) ?: return@forEach
val name = (baseClass as? PsiNamedElement)?.name ?: return@forEach
progressIndicator.text = "Looking for class $name inheritors..."
val classes = listOf(baseClass) + HierarchySearchRequest(baseClass, baseClass.useScope).searchInheritors()
classes.mapNotNullTo(result) {
val subClass = it.unwrapped as? KtClassOrObject ?: return@mapNotNullTo null
val classDescriptor = subClass.unsafeResolveToDescriptor() as ClassDescriptor
val substitutor = getTypeSubstitutor(baseClassDescriptor.defaultType, classDescriptor.defaultType)
?: return@mapNotNullTo null
val signatureInSubClass = baseFunctionDescriptor.substitute(substitutor) as FunctionDescriptor
val subFunctionDescriptor = classDescriptor.findCallableMemberBySignature(signatureInSubClass, true)
?: return@mapNotNullTo null
subFunctionDescriptor.source.getPsi() as? KtNamedFunction
}
}
return result
}
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val functions = project.runSynchronouslyWithProgress("Analyzing class hierarchy...", true) {
runReadAction { findAllFunctionToProcess(project) }
} ?: return
runWriteAction {
functions.forEach {
if (addModifier) {
it.addModifier(KtTokens.SUSPEND_KEYWORD)
}
else {
it.removeModifier(KtTokens.SUSPEND_KEYWORD)
}
}
}
}
companion object : KotlinIntentionActionsFactory() {
fun FunctionDescriptor.findTopMostOverriddables(): List<FunctionDescriptor> {
val overridablesCache = HashMap<FunctionDescriptor, List<FunctionDescriptor>>()
fun FunctionDescriptor.getOverridables(): List<FunctionDescriptor> {
return overridablesCache.getOrPut(this) {
val classDescriptor = containingDeclaration as? ClassDescriptorWithResolutionScopes ?: return emptyList()
DescriptorUtils.getSuperclassDescriptors(classDescriptor).flatMap { superClassDescriptor ->
if (superClassDescriptor !is ClassDescriptorWithResolutionScopes) return@flatMap emptyList<FunctionDescriptor>()
val candidates = superClassDescriptor.unsubstitutedMemberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, classDescriptor.defaultType)
?: return@flatMap emptyList<FunctionDescriptor>()
candidates.filter {
val signature = it.substitute(substitutor) as FunctionDescriptor
classDescriptor.findCallableMemberBySignature(signature, true) == this
}
}
}
}
return DFS.dfs(
listOf(this),
{ it?.getOverridables() ?: emptyList() },
object : DFS.CollectingNodeHandler<FunctionDescriptor, FunctionDescriptor, ArrayList<FunctionDescriptor>>(ArrayList()) {
override fun afterChildren(current: FunctionDescriptor) {
if (current.getOverridables().isEmpty()) {
result.add(current)
}
}
})
}
private fun Collection<DeclarationDescriptor>.getOverridables(
currentDescriptor: FunctionDescriptor
): List<DeclarationDescriptor> {
val currentClassDescriptor = currentDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList()
return filter {
if (it !is FunctionDescriptor || it == currentDescriptor) return@filter false
if (it.isSuspend == currentDescriptor.isSuspend) return@filter false
val containingClassDescriptor = it.containingDeclaration as? ClassDescriptor ?: return@filter false
if (!currentClassDescriptor.isSubclassOf(containingClassDescriptor)) return@filter false
val substitutor = getTypeSubstitutor(
containingClassDescriptor.defaultType,
currentClassDescriptor.defaultType
) ?: return@filter false
val signatureInCurrentClass = it.substitute(substitutor) ?: return@filter false
OverridingUtil.DEFAULT.isOverridableBy(signatureInCurrentClass, currentDescriptor, null).result ==
OverridingUtil.OverrideCompatibilityInfo.Result.CONFLICT
}
}
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val currentFunction = diagnostic.psiElement as? KtNamedFunction ?: return emptyList()
val currentDescriptor = currentFunction.unsafeResolveToDescriptor() as FunctionDescriptor
Errors.CONFLICTING_OVERLOADS.cast(diagnostic).a.getOverridables(currentDescriptor).ifEmpty { return emptyList() }
return listOf(
ChangeSuspendInHierarchyFix(currentFunction, true),
ChangeSuspendInHierarchyFix(currentFunction, false)
)
}
}
}
@@ -18,10 +18,14 @@ package org.jetbrains.kotlin.idea.quickfix.crossLanguage
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.QuickFixFactory
import com.intellij.lang.jvm.*
import com.intellij.lang.jvm.JvmClass
import com.intellij.lang.jvm.JvmElement
import com.intellij.lang.jvm.JvmModifier
import com.intellij.lang.jvm.JvmModifiersOwner
import com.intellij.lang.jvm.actions.*
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.codeStyle.SuggestedNameInfo
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
@@ -56,6 +60,7 @@ import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.storage.LockBasedStorageManager
@@ -160,14 +165,14 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
private fun fakeParametersExpressions(parameters: List<Pair<SuggestedNameInfo, List<ExpectedType>>>, project: Project): Array<PsiExpression>? =
when {
parameters.isEmpty() -> emptyArray()
else -> JavaPsiFacade
.getElementFactory(project)
.createParameterList(
parameters.map { it.name }.toTypedArray(),
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
parameters.map { it.first.names.firstOrNull() }.toTypedArray(),
parameters.map { JvmPsiConversionHelper.getInstance(project).asPsiType(it) ?: return null }.toTypedArray()
)
.parameters
.map(::FakeExpressionFromParameter)
@@ -243,10 +248,15 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
val modifier = request.modifier
val shouldPresent = request.shouldPresent
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
KtTokens.OPEN_KEYWORD to !shouldPresent
else
javaPsiModifiersMapping[modifier] to shouldPresent
//TODO: make similar to `createAddMethodActions`
val (kToken, shouldPresentMapped) = when {
modifier == JvmModifier.FINAL -> KtTokens.OPEN_KEYWORD to !shouldPresent
modifier == JvmModifier.PUBLIC && shouldPresent ->
kModifierOwner.visibilityModifierType()
?.takeIf { it != KtTokens.DEFAULT_VISIBILITY_KEYWORD }
?.let { it to false } ?: return emptyList()
else -> javaPsiModifiersMapping[modifier] to shouldPresent
}
if (kToken == null) return emptyList()
val action = if (shouldPresentMapped)
@@ -256,19 +266,18 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
return listOfNotNull(action)
}
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
override fun createAddConstructorActions(targetClass: JvmClass, request: CreateConstructorRequest): List<IntentionAction> {
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
if (request.typeParameters.isNotEmpty()) return emptyList()
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetKtClass.getResolutionFacade()
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
val parameterInfos = request.parameters.mapIndexed { index, param ->
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val name = param.name ?: "arg${index + 1}"
val helper = JvmPsiConversionHelper.getInstance(targetKtClass.project)
val parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
val parameterInfos = parameters.mapIndexed { index, param: Pair<SuggestedNameInfo, List<ExpectedType>> ->
val ktType = helper.asPsiType(param)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val name = param.first.names.firstOrNull() ?: "arg${index + 1}"
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
}
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
@@ -288,7 +297,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
val project = targetKtClass.project
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
val fakeParametersExpressions = fakeParametersExpressions(parameters, project) ?: return@run null
QuickFixFactory.getInstance()
.createChangeMethodSignatureFromUsageFix(
lightMethod,
@@ -369,7 +378,8 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
val resolutionFacade = targetContainer.getResolutionFacade()
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
val parameters = request.parameters as List<Pair<SuggestedNameInfo, List<ExpectedType>>>
val parameterInfos = parameters.map { (suggestedNames, expectedTypes) ->
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
}
val functionInfo = FunctionInfo(
@@ -388,4 +398,7 @@ class KotlinElementActionsFactory : JvmElementActionsFactory() {
}
return listOf(action)
}
}
}
private fun JvmPsiConversionHelper.asPsiType(param: Pair<SuggestedNameInfo, List<ExpectedType>>): PsiType? =
param.second.firstOrNull()?.theType?.let { convertType(it) }
@@ -0,0 +1,391 @@
/*
* Copyright 2010-2017 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.idea.quickfix.crossLanguage
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.QuickFixFactory
import com.intellij.lang.jvm.*
import com.intellij.lang.jvm.actions.*
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.appendModifier
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
import org.jetbrains.kotlin.load.java.lazy.child
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.supertypes
class KotlinElementActionsFactory : JvmElementActionsFactory() {
companion object {
val javaPsiModifiersMapping = mapOf(
JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
)
}
private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() {
override fun getText(): String = psiParam.name!!
override fun getProject(): Project = psiParam.project
override fun getParent(): PsiElement = psiParam.parent
override fun getType(): PsiType? = psiParam.type
override fun isValid(): Boolean = true
override fun getContainingFile(): PsiFile = psiParam.containingFile
override fun getReferenceName(): String? = psiParam.name
override fun resolve(): PsiElement? = psiParam
}
private class ModifierBuilder(
private val targetContainer: KtElement,
private val allowJvmStatic: Boolean = true
) {
private val psiFactory = KtPsiFactory(targetContainer.project)
val modifierList = psiFactory.createEmptyModifierList()
private fun JvmModifier.transformAndAppend(): Boolean {
javaPsiModifiersMapping[this]?.let {
modifierList.appendModifier(it)
return true
}
when (this) {
JvmModifier.STATIC -> {
if (allowJvmStatic && targetContainer is KtClassOrObject) {
addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME)
}
}
JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD)
else -> return false
}
return true
}
var isValid = true
private set
fun addJvmModifier(modifier: JvmModifier) {
isValid = isValid && modifier.transformAndAppend()
}
fun addJvmModifiers(modifiers: Iterable<JvmModifier>) {
modifiers.forEach { addJvmModifier(it) }
}
fun addAnnotation(fqName: FqName) {
if (!isValid) return
modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}"))
}
}
class CreatePropertyFix(
private val targetClass: JvmClass,
contextElement: KtElement,
propertyInfo: PropertyInfo
) : CreateCallableFromUsageFix<KtElement>(contextElement, listOf(propertyInfo)) {
override fun getFamilyName() = "Add property"
override fun getText(): String {
val info = callableInfos.first() as PropertyInfo
return buildString {
append("Add '")
if (info.isLateinitPreferred) {
append("lateinit ")
}
append(if (info.writable) "var" else "val")
append("' property '${info.name}' to '${targetClass.name}'")
}
}
}
private fun JvmClass.toKtClassOrFile(): KtElement? {
val psi = sourceElement
return when (psi) {
is KtClassOrObject -> psi
is KtLightClassForSourceDeclaration -> psi.kotlinOrigin
is KtLightClassForFacade -> psi.files.firstOrNull()
else -> null
}
}
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
when {
parameters.isEmpty() -> emptyArray()
else -> JavaPsiFacade
.getElementFactory(project)
.createParameterList(
parameters.map { it.name }.toTypedArray(),
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
)
.parameters
.map(::FakeExpressionFromParameter)
.toTypedArray()
}
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
val results = ArrayList<PsiTypeParameter>()
accept(
object : PsiTypeVisitor<Unit>() {
override fun visitArrayType(arrayType: PsiArrayType) {
arrayType.componentType.accept(this)
}
override fun visitClassType(classType: PsiClassType) {
(classType.resolve() as? PsiTypeParameter)?.let { results += it }
classType.parameters.forEach { it.accept(this) }
}
override fun visitWildcardType(wildcardType: PsiWildcardType) {
wildcardType.bound?.accept(this)
}
}
)
return results
}
private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? {
val typeParameters = collectTypeParameters()
val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java)
val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null }
val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy"))
val dummyClassDescriptor = ClassDescriptorImpl(
dummyPackageDescriptor,
Name.identifier("Dummy"),
Modality.FINAL,
ClassKind.CLASS,
emptyList(),
SourceElement.NO_SOURCE,
false,
LockBasedStorageManager.NO_LOCKS
)
val typeParameterResolver = object : TypeParameterResolver {
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi
val index = typeParameters.indexOf(psiTypeParameter)
if (index < 0) return null
return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor)
}
}
val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver)
val attributes = JavaTypeAttributes(TypeUsage.COMMON)
return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true)
}
private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo {
val candidateTypes = flatMapTo(LinkedHashSet<KotlinType>()) {
val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList()
when (it.theKind) {
ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType)
ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes()
}
}
if (candidateTypes.isEmpty()) {
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
return TypeInfo(nullableAnyType, Variance.INVARIANT)
}
return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList())
}
override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: return emptyList()
val modifier = request.modifier
val shouldPresent = request.shouldPresent
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
KtTokens.OPEN_KEYWORD to !shouldPresent
else
javaPsiModifiersMapping[modifier] to shouldPresent
if (kToken == null) return emptyList()
val action = if (shouldPresentMapped)
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
else
RemoveModifierFix(kModifierOwner, kToken, false)
return listOfNotNull(action)
}
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
if (request.typeParameters.isNotEmpty()) return emptyList()
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetKtClass.getResolutionFacade()
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
val parameterInfos = request.parameters.mapIndexed { index, param ->
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val name = param.name ?: "arg${index + 1}"
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
}
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
val constructorInfo = ConstructorInfo(
parameterInfos,
targetKtClass,
isPrimary = needPrimary,
modifierList = modifierBuilder.modifierList,
withBody = true
)
val addConstructorAction = object : CreateCallableFromUsageFix<KtElement>(targetKtClass, listOf(constructorInfo)) {
override fun getFamilyName() = "Add method"
override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'"
}
val changePrimaryConstructorAction = run {
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
val project = targetKtClass.project
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
QuickFixFactory.getInstance()
.createChangeMethodSignatureFromUsageFix(
lightMethod,
fakeParametersExpressions,
PsiSubstitutor.EMPTY,
targetKtClass,
false,
2
).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) }
}
return listOfNotNull(changePrimaryConstructorAction, addConstructorAction)
}
override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
val propertyInfo = PropertyInfo(
request.propertyName,
TypeInfo.Empty,
TypeInfo(ktType, Variance.INVARIANT),
request.setterRequired,
listOf(targetContainer),
modifierList = modifierBuilder.modifierList,
withInitializer = true
)
val propertyInfos = if (request.setterRequired) {
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
}
else {
listOf(propertyInfo)
}
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
}
override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply {
addJvmModifiers(request.modifiers)
addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME)
}
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade)
val writable = JvmModifier.FINAL !in request.modifiers
val propertyInfo = PropertyInfo(
request.fieldName,
TypeInfo.Empty,
typeInfo,
writable,
listOf(targetContainer),
isForCompanion = JvmModifier.STATIC in request.modifiers,
modifierList = modifierBuilder.modifierList,
withInitializer = true
)
val propertyInfos = if (writable) {
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
}
else {
listOf(propertyInfo)
}
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
}
override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) }
if (!modifierBuilder.isValid) return emptyList()
val resolutionFacade = targetContainer.getResolutionFacade()
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
}
val functionInfo = FunctionInfo(
request.methodName,
TypeInfo.Empty,
returnTypeInfo,
listOf(targetContainer),
parameterInfos,
isForCompanion = JvmModifier.STATIC in request.modifiers,
modifierList = modifierBuilder.modifierList,
preferEmptyBody = true
)
val action = object : CreateCallableFromUsageFix<KtElement>(targetContainer, listOf(functionInfo)) {
override fun getFamilyName() = "Add method"
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
}
return listOf(action)
}
}
@@ -54,9 +54,8 @@ class KotlinCallerChooser(
previousTree: Tree?,
callback: Consumer<Set<PsiElement>>
): CallerChooserBase<PsiElement>(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) {
override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet<PsiElement>, cancelCallback: Runnable): KotlinMethodNode {
return KotlinMethodNode(method, called, myProject, cancelCallback)
}
override fun createTreeNodeFor(method: PsiElement?, called: HashSet<PsiElement>?, cancelCallback: Runnable?) =
KotlinMethodNode(method, called ?: HashSet(), myProject, cancelCallback ?: Runnable {})
override fun findDeepestSuperMethods(method: PsiElement) =
method.toLightMethods().singleOrNull()?.findDeepestSuperMethods()
@@ -0,0 +1,124 @@
/*
* 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.idea.refactoring.changeSignature.ui
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClassOwner
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.changeSignature.CallerChooserBase
import com.intellij.refactoring.changeSignature.MethodNodeBase
import com.intellij.ui.ColoredTreeCellRenderer
import com.intellij.ui.JBColor
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.treeStructure.Tree
import com.intellij.util.Consumer
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaMethodDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.hierarchy.calls.CalleeReferenceProcessor
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import java.util.*
class KotlinCallerChooser(
declaration: PsiElement,
project: Project,
title: String,
previousTree: Tree?,
callback: Consumer<Set<PsiElement>>
): CallerChooserBase<PsiElement>(declaration, project, title, previousTree, "dummy." + KotlinFileType.EXTENSION, callback) {
override fun createTreeNode(method: PsiElement?, called: com.intellij.util.containers.HashSet<PsiElement>, cancelCallback: Runnable): KotlinMethodNode {
return KotlinMethodNode(method, called, myProject, cancelCallback)
}
override fun findDeepestSuperMethods(method: PsiElement) =
method.toLightMethods().singleOrNull()?.findDeepestSuperMethods()
override fun getEmptyCallerText() =
"Caller text \nwith highlighted callee call would be shown here"
override fun getEmptyCalleeText() =
"Callee text would be shown here"
}
class KotlinMethodNode(
method: PsiElement?,
called: HashSet<PsiElement>,
project: Project,
cancelCallback: Runnable
): MethodNodeBase<PsiElement>(method?.namedUnwrappedElement ?: method, called, project, cancelCallback) {
override fun createNode(caller: PsiElement, called: HashSet<PsiElement>) =
KotlinMethodNode(caller, called, myProject, myCancelCallback)
override fun customizeRendererText(renderer: ColoredTreeCellRenderer) {
val descriptor = when (myMethod) {
is KtFunction -> myMethod.unsafeResolveToDescriptor() as FunctionDescriptor
is KtClass -> (myMethod.unsafeResolveToDescriptor() as ClassDescriptor).unsubstitutedPrimaryConstructor ?: return
is PsiMethod -> myMethod.getJavaMethodDescriptor() ?: return
else -> throw AssertionError("Invalid declaration: ${myMethod.getElementTextWithContext()}")
}
val containerName = generateSequence<DeclarationDescriptor>(descriptor) { it.containingDeclaration }
.firstOrNull { it is ClassDescriptor }
?.name
val renderedFunction = KotlinCallHierarchyNodeDescriptor.renderNamedFunction(descriptor)
val renderedFunctionWithContainer =
containerName?.let {
"${if (it.isSpecial) "[Anonymous]" else it.asString()}.$renderedFunction"
} ?: renderedFunction
val attributes = if (isEnabled)
SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground())
else
SimpleTextAttributes.EXCLUDED_ATTRIBUTES
renderer.append(renderedFunctionWithContainer, attributes)
val packageName = (myMethod.containingFile as? PsiClassOwner)?.packageName ?: ""
renderer.append(" ($packageName)", SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY))
}
override fun computeCallers(): List<PsiElement> {
if (myMethod == null) return emptyList()
val callers = LinkedHashSet<PsiElement>()
val processor = object: CalleeReferenceProcessor(false) {
override fun onAccept(ref: PsiReference, element: PsiElement) {
if ((element is KtFunction || element is KtClass || element is PsiMethod) && element !in myCalled) {
callers.add(element)
}
}
}
val query = myMethod.getRepresentativeLightMethod()?.let { MethodReferencesSearch.search(it, it.useScope, true) }
?: ReferencesSearch.search(myMethod, myMethod.useScope)
query.forEach { processor.process(it) }
return callers.toList()
}
}
@@ -86,7 +86,7 @@ class CopyKotlinDeclarationDialog(
Pass { setErrorText(it, destinationComboBox) },
packageNameField.childComponent
)
classNameField.text = UsageViewUtil.getShortName(declaration)
classNameField.setText(UsageViewUtil.getShortName(declaration))
classNameField.selectAll()
}
@@ -0,0 +1,184 @@
/*
* Copyright 2010-2017 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.idea.refactoring.copy
import com.intellij.ide.util.DirectoryChooser
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiManager
import com.intellij.refactoring.HelpID
import com.intellij.refactoring.MoveDestination
import com.intellij.refactoring.PackageWrapper
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo
import com.intellij.ui.EditorTextField
import com.intellij.ui.RecentsManager
import com.intellij.ui.ReferenceEditorComboWithBrowseButton
import com.intellij.usageView.UsageViewUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.ui.FormBuilder
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.Pass
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
import org.jetbrains.kotlin.idea.util.sourceRoot
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import java.awt.BorderLayout
import java.awt.Font
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel
// Based on com.intellij.refactoring.copy.CopyClassDialog
class CopyKotlinDeclarationDialog(
declaration: KtNamedDeclaration,
private val defaultTargetDirectory: PsiDirectory?,
private val project: Project
) : DialogWrapper(project, true) {
private val informationLabel = JLabel()
private val classNameField = EditorTextField("")
private val packageLabel = JLabel()
private lateinit var packageNameField: ReferenceEditorComboWithBrowseButton
private val openInEditorCheckBox = CopyFilesOrDirectoriesDialog.createOpenInEditorCB()
private val destinationComboBox = object : DestinationFolderComboBox() {
override fun getTargetPackage() = packageNameField.text.trim()
override fun reportBaseInTestSelectionInSource() = true
}
private val originalFile = declaration.containingFile
var targetDirectory: MoveDestination? = null
private set
val targetSourceRoot: VirtualFile?
get() = ((destinationComboBox.comboBox.selectedItem as? DirectoryChooser.ItemWrapper)?.directory ?: originalFile).sourceRoot
init {
informationLabel.text = RefactoringBundle.message("copy.class.copy.0.1", UsageViewUtil.getType(declaration), UsageViewUtil.getLongName(declaration))
informationLabel.font = informationLabel.font.deriveFont(Font.BOLD)
init()
destinationComboBox.setData(
project,
defaultTargetDirectory,
Pass { setErrorText(it, destinationComboBox) },
packageNameField.childComponent
)
classNameField.text = UsageViewUtil.getShortName(declaration)
classNameField.selectAll()
}
override fun getPreferredFocusedComponent() = classNameField
override fun createCenterPanel() = JPanel(BorderLayout())
override fun createNorthPanel(): JComponent? {
val qualifiedName = qualifiedName
packageNameField = PackageNameReferenceEditorCombo(qualifiedName, project, RECENTS_KEY, RefactoringBundle.message("choose.destination.package"))
packageNameField.setTextFieldPreferredWidth(Math.max(qualifiedName.length + 5, 40))
packageLabel.text = RefactoringBundle.message("destination.package")
packageLabel.labelFor = packageNameField
val label = JLabel(RefactoringBundle.message("target.destination.folder"))
val isMultipleSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project).size > 1
destinationComboBox.isVisible = isMultipleSourceRoots
label.isVisible = isMultipleSourceRoots
label.labelFor = destinationComboBox
val panel = JPanel(BorderLayout())
panel.add(openInEditorCheckBox, BorderLayout.EAST)
return FormBuilder.createFormBuilder()
.addComponent(informationLabel)
.addLabeledComponent(RefactoringBundle.message("copy.files.new.name.label"), classNameField, UIUtil.LARGE_VGAP)
.addLabeledComponent(packageLabel, packageNameField)
.addLabeledComponent(label, destinationComboBox)
.addComponent(panel)
.panel
}
private val qualifiedName: String
get() = defaultTargetDirectory?.getPackage()?.qualifiedName ?: ""
val newName: String?
get() = classNameField.text
val openInEditor: Boolean
get() = openInEditorCheckBox.isSelected
private fun checkForErrors(): String? {
val packageName = packageNameField.text
val newName = newName
val manager = PsiManager.getInstance(project)
if (packageName.isNotEmpty() && !FqNameUnsafe(packageName).hasIdentifiersOnly()) {
return RefactoringBundle.message("invalid.target.package.name.specified")
}
if (newName.isNullOrEmpty()) {
return RefactoringBundle.message("no.class.name.specified")
}
try {
targetDirectory = destinationComboBox.selectDirectory(PackageWrapper(manager, packageName), false)
}
catch (e: IncorrectOperationException) {
return e.message
}
targetDirectory?.getTargetIfExists(defaultTargetDirectory)?.let {
val targetFileName = newName + "." + originalFile.virtualFile.extension
if (it.findFile(targetFileName) == originalFile) {
return "Can't copy class to the containing file"
}
}
return null
}
override fun doOKAction() {
val packageName = packageNameField.text
val errorString = checkForErrors()
if (errorString != null) {
if (errorString.isNotEmpty()) {
Messages.showMessageDialog(project, errorString, RefactoringBundle.message("error.title"), Messages.getErrorIcon())
}
classNameField.requestFocusInWindow()
return
}
RecentsManager.getInstance(project).registerRecentEntry(RECENTS_KEY, packageName)
CopyFilesOrDirectoriesDialog.saveOpenInEditorState(openInEditorCheckBox.isSelected)
super.doOKAction()
}
override fun getHelpId() = HelpID.COPY_CLASS
companion object {
@NonNls private val RECENTS_KEY = "CopyKotlinDeclarationDialog.RECENTS_KEY"
}
}
@@ -73,8 +73,8 @@ class KotlinAwareDelegatingMoveDestination(
}
filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) }
val progressIndicator = ProgressManager.getInstance().progressIndicator
progressIndicator?.pushState()
val progressIndicator = ProgressManager.getInstance().progressIndicator!!
progressIndicator.pushState()
val extraUsages = ArrayList<UsageInfo>()
try {
@@ -87,7 +87,7 @@ class KotlinAwareDelegatingMoveDestination(
}
}
finally {
progressIndicator?.popState()
progressIndicator.popState()
}
filesToProcess.forEach {
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2017 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.idea.refactoring.move.moveClassesOrPackages
import com.intellij.openapi.progress.ProgressManager
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiPackage
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.MoveDestination
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.refactoring.move.createMoveUsageInfoIfPossible
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.stubindex.KotlinExactPackagesIndex
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
class KotlinAwareDelegatingMoveDestination(
private val delegate: MoveDestination,
private val targetPackage: PsiPackage?,
private val targetDirectory: PsiDirectory?
) : MoveDestination by delegate {
override fun analyzeModuleConflicts(
elements: MutableCollection<PsiElement>,
conflicts: MultiMap<PsiElement, String>,
usages: Array<out UsageInfo>
) {
delegate.analyzeModuleConflicts(elements, conflicts, usages)
if (targetPackage == null || targetDirectory == null) return
val project = targetDirectory.project
val moveTarget = KotlinDirectoryMoveTarget(FqName(targetPackage.qualifiedName), targetDirectory)
val packagesIndex = KotlinExactPackagesIndex.getInstance()
val directoriesToMove = elements.flatMapTo(LinkedHashSet<PsiDirectory>()) {
(it as? PsiPackage)?.directories?.toList() ?: emptyList()
}
val projectScope = project.projectScope()
val filesToProcess = elements.flatMapTo(LinkedHashSet<KtFile>()) {
if (it is PsiPackage) packagesIndex[it.qualifiedName, project, projectScope] else emptyList()
}
val extraElementsForReferenceSearch = LinkedHashSet<PsiElement>()
val extraElementCollector = object : PsiRecursiveElementWalkingVisitor() {
override fun visitElement(element: PsiElement) {
if (element is KtNamedDeclaration && element.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
element.parentsWithSelf.lastOrNull { it is KtNamedDeclaration }?.let { extraElementsForReferenceSearch += it }
stopWalking()
}
super.visitElement(element)
}
}
filesToProcess.flatMap {it.declarations}.forEach { it.accept(extraElementCollector) }
val progressIndicator = ProgressManager.getInstance().progressIndicator
progressIndicator?.pushState()
val extraUsages = ArrayList<UsageInfo>()
try {
progressIndicator.text = "Looking for Usages"
for ((index, element) in extraElementsForReferenceSearch.withIndex()) {
progressIndicator.fraction = (index + 1)/extraElementsForReferenceSearch.size.toDouble()
ReferencesSearch.search(element, projectScope).mapNotNullTo(extraUsages) { ref ->
createMoveUsageInfoIfPossible(ref, element, true, false)
}
}
}
finally {
progressIndicator?.popState()
}
filesToProcess.forEach {
analyzeConflictsInFile(it, extraUsages, moveTarget, directoriesToMove, conflicts) {}
}
}
}
@@ -28,8 +28,8 @@ class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog(
elementsToMove: Array<out PsiElement>,
moveCallback: MoveCallback?
) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) {
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination {
val delegate = super.createDestination(aPackage, directory)
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination? {
val delegate = super.createDestination(aPackage, directory) ?: return null
return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory)
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2017 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.idea.refactoring.move.moveClassesOrPackages
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiPackage
import com.intellij.refactoring.MoveDestination
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesToNewDirectoryDialog
class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog(
directory: PsiDirectory,
elementsToMove: Array<out PsiElement>,
moveCallback: MoveCallback?
) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) {
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination {
val delegate = super.createDestination(aPackage, directory)
return KotlinAwareDelegatingMoveDestination(delegate, aPackage, directory)
}
}
@@ -163,7 +163,7 @@ sealed class MoveDeclarationsDelegate {
if (lightOuterClass != null) {
MoveInnerClassUsagesHandler.EP_NAME
.forLanguage(usage.element!!.language)
?.correctInnerClassUsage(usage, lightOuterClass)
?.correctInnerClassUsage(usage, lightOuterClass, outerInstanceParameterName)
}
}
}
@@ -0,0 +1,182 @@
/*
* Copyright 2010-2016 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.idea.refactoring.move.moveDeclarations
import com.intellij.psi.PsiElement
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.shorten.isToBeShortened
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
sealed class MoveDeclarationsDelegate {
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
open fun collectConflicts(
descriptor: MoveDeclarationsDescriptor,
internalUsages: MutableSet<UsageInfo>,
conflicts: MultiMap<PsiElement, String>
) {
}
open fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
}
open fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
}
object TopLevel : MoveDeclarationsDelegate() {
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
val sourcePackage = ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName)
val targetPackage = moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage
return ContainerChangeInfo(sourcePackage, targetPackage)
}
}
class NestedClass(
val newClassName: String? = null,
val outerInstanceParameterName: String? = null
) : MoveDeclarationsDelegate() {
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!)
val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject
val targetContainerFqName = moveTarget.targetContainerFqName
val newInfo = when {
targetContainerFqName == null -> ContainerInfo.UnknownPackage
movingToClass -> ContainerInfo.Class(targetContainerFqName)
else -> ContainerInfo.Package(targetContainerFqName)
}
return ContainerChangeInfo(originalInfo, newInfo)
}
override fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> {
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList()
return collectOuterInstanceReferences(classToMove)
}
private fun isValidTargetForImplicitCompanionAsDispatchReceiver(
moveDescriptor: MoveDeclarationsDescriptor,
companionDescriptor: ClassDescriptor
): Boolean {
val moveTarget = moveDescriptor.moveTarget
return when (moveTarget) {
is KotlinMoveTargetForCompanion -> true
is KotlinMoveTargetForExistingElement -> {
val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false
val targetClassDescriptor = targetClass.unsafeResolveToDescriptor() as ClassDescriptor
val companionClassDescriptor = companionDescriptor.containingDeclaration as? ClassDescriptor ?: return false
targetClassDescriptor.isSubclassOf(companionClassDescriptor)
}
else -> false
}
}
override fun collectConflicts(
descriptor: MoveDeclarationsDescriptor,
internalUsages: MutableSet<UsageInfo>,
conflicts: MultiMap<PsiElement, String>
) {
val usageIterator = internalUsages.iterator()
while (usageIterator.hasNext()) {
val usage = usageIterator.next()
val element = usage.element ?: continue
val isConflict = when (usage) {
is ImplicitCompanionAsDispatchReceiverUsageInfo -> {
if (!isValidTargetForImplicitCompanionAsDispatchReceiver(descriptor, usage.companionDescriptor)) {
conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}")
}
true
}
is OuterInstanceReferenceUsageInfo -> usage.reportConflictIfAny(conflicts)
else -> false
}
if (isConflict) {
usageIterator.remove()
}
}
}
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
with(originalDeclaration) {
newClassName?.let { setName(it) }
if (this is KtClass) {
if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) {
if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD)
if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD)
}
if (outerInstanceParameterName != null) {
val type = (containingClassOrObject!!.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
val parameter = KtPsiFactory(project)
.createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}")
createPrimaryConstructorParameterListIfAbsent().addParameter(parameter).isToBeShortened = true
}
}
}
}
override fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
if (outerInstanceParameterName == null) return
val psiFactory = KtPsiFactory(descriptor.project)
val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName)
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass
for (usage in usages) {
if (usage is MoveRenameUsageInfo) {
val referencedNestedClass = usage.referencedElement?.unwrapped as? KtClassOrObject
if (referencedNestedClass == classToMove) {
val outerClass = referencedNestedClass?.containingClassOrObject
val lightOuterClass = outerClass?.toLightClass()
if (lightOuterClass != null) {
MoveInnerClassUsagesHandler.EP_NAME
.forLanguage(usage.element!!.language)
?.correctInnerClassUsage(usage, lightOuterClass)
}
}
}
when (usage) {
is OuterInstanceReferenceUsageInfo.ExplicitThis -> {
usage.expression?.replace(newOuterInstanceRef)
}
is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> {
usage.callElement?.let { it.replace(psiFactory.createExpressionByPattern("$0.$1", outerInstanceParameterName, it)) }
}
}
}
}
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
@@ -85,11 +86,11 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
for ((index, usageInfo) in infos.withIndex()) {
if (usageInfo !is FileUsagesWrapper) continue
project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) {
runReadAction {
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
infos[index] = usageInfo.copy(usages = it)
}
ProgressManager.getInstance().progressIndicator?.text2 = "Processing ${usageInfo.psiFile.name}"
runReadAction {
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
infos[index] = usageInfo.copy(usages = it)
}
}
}
@@ -0,0 +1,150 @@
/*
* 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.idea.refactoring.move.moveFilesOrDirectories
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.Function
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
private data class FileUsagesWrapper(
val psiFile: KtFile,
val usages: List<UsageInfo>,
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) : UsageInfo(psiFile)
private class MoveContext(
val newParent: PsiDirectory,
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
)
private val fileHandler = MoveKotlinFileHandler()
private var fileToMoveContext: MutableMap<PsiFile, MoveContext>? = null
private fun getOrCreateMoveContextMap(): MutableMap<PsiFile, MoveContext> {
return fileToMoveContext ?: HashMap<PsiFile, MoveContext>().apply {
fileToMoveContext = this
invokeOnceOnCommandFinish { fileToMoveContext = null }
}
}
override fun findUsages(
filesToMove: MutableCollection<PsiFile>,
directoriesToMove: Array<out PsiDirectory>,
result: MutableCollection<UsageInfo>,
searchInComments: Boolean,
searchInNonJavaFiles: Boolean,
project: Project) {
filesToMove
.filterIsInstance<KtFile>()
.mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, false), null) }
}
override fun preprocessUsages(
project: Project,
files: MutableSet<PsiFile>,
infos: Array<UsageInfo>,
directory: PsiDirectory?,
conflicts: MultiMap<PsiElement, String>
) {
val psiPackage = directory?.getPackage() ?: return
val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory)
for ((index, usageInfo) in infos.withIndex()) {
if (usageInfo !is FileUsagesWrapper) continue
project.runSynchronouslyWithProgress("Analyzing conflicts in ${usageInfo.psiFile.name}", false) {
runReadAction {
analyzeConflictsInFile(usageInfo.psiFile, usageInfo.usages, moveTarget, files, conflicts) {
infos[index] = usageInfo.copy(usages = it)
}
}
}
}
}
override fun beforeMove(psiFile: PsiFile) {
}
// Actual move logic is implemented in postProcessUsages since usages are not available here
override fun move(
file: PsiFile,
moveDestination: PsiDirectory,
oldToNewElementsMapping: MutableMap<PsiElement, PsiElement>,
movedFiles: MutableList<PsiFile>,
listener: RefactoringElementListener?
): Boolean {
if (file !is KtFile) return false
val moveDeclarationsProcessor = fileHandler.initMoveProcessor(file, moveDestination, false)
val moveContextMap = getOrCreateMoveContextMap()
moveContextMap[file] = MoveContext(moveDestination, moveDeclarationsProcessor)
if (moveDeclarationsProcessor != null) {
moveDestination.getPackage()?.let { newPackage ->
file.packageDirective?.fqName = FqName(newPackage.qualifiedName).quoteIfNeeded()
}
}
return true
}
override fun afterMove(newElement: PsiElement) {
}
override fun postProcessUsages(usages: Array<out UsageInfo>, newDirMapper: Function<PsiDirectory, PsiDirectory>) {
val fileToMoveContext = fileToMoveContext ?: return
try {
val usagesToProcess = ArrayList<FileUsagesWrapper>()
usages
.filterIsInstance<FileUsagesWrapper>()
.forEach body@ {
val file = it.psiFile
val moveContext = fileToMoveContext[file] ?: return@body
MoveFilesOrDirectoriesUtil.doMoveFile(file, moveContext.newParent)
val moveDeclarationsProcessor = moveContext.moveDeclarationsProcessor ?: return@body
val movedFile = moveContext.newParent.findFile(file.name) ?: return@body
usagesToProcess += FileUsagesWrapper(movedFile as KtFile, it.usages, moveDeclarationsProcessor)
}
usagesToProcess.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) }
}
finally {
this.fileToMoveContext = null
}
}
}
@@ -91,11 +91,10 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
override fun findCollisions(
element: PsiElement,
newName: String?,
newName: String,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor
@@ -122,7 +121,7 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
else -> null
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
for (usage in usages) {
@@ -0,0 +1,142 @@
/*
* Copyright 2010-2016 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.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.SmartList
import java.util.*
class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
return element is KtClassOrObject || element is KtLightClass || element is KtConstructor<*> || element is KtTypeAlias
}
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS = enabled
}
override fun substituteElementToRename(element: PsiElement, editor: Editor?) = getClassOrObject(element)
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>) {
super.prepareRenaming(element, newName, allRenames)
val classOrObject = getClassOrObject(element) as? KtClassOrObject ?: return
val file = classOrObject.containingKtFile
val virtualFile = file.virtualFile
if (virtualFile != null) {
val nameWithoutExtensions = virtualFile.nameWithoutExtension
if (nameWithoutExtensions == classOrObject.name) {
val newFileName = newName + "." + virtualFile.extension
allRenames.put(file, newFileName)
RenamePsiElementProcessor.forElement(file).prepareRenaming(file, newFileName, allRenames)
}
}
}
override fun findReferences(element: PsiElement): Collection<PsiReference> {
if (element is KtObjectDeclaration && element.isCompanion()) {
return super.findReferences(element).filter { !it.isCompanionObjectClassReference() }
}
return super.findReferences(element)
}
private fun PsiReference.isCompanionObjectClassReference(): Boolean {
if (this !is KtSimpleNameReference) {
return false
}
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
return bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] != null
}
override fun findCollisions(
element: PsiElement,
newName: String?,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(descriptor, newName, collisions)
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
checkNewNameUsagesRetargeting(declaration, newName, collisions)
result += collisions
}
private fun getClassOrObject(element: PsiElement?): PsiElement? = when (element) {
is KtLightClass ->
when (element) {
is KtLightClassForSourceDeclaration -> element.kotlinOrigin
is KtLightClassForFacade -> element
else -> throw AssertionError("Should not be suggested to rename element of type " + element::class.java + " " + element)
}
is KtConstructor<*> ->
element.getContainingClassOrObject()
is KtClassOrObject, is KtTypeAlias -> element
else -> null
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
for (usage in usages) {
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
}
else {
simpleUsages += usage
}
}
element.ambiguousImportUsages = ambiguousImportUsages
super.renameElement(element, newName, simpleUsages.toTypedArray(), listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
}
}
@@ -40,7 +40,7 @@ class RenameKotlinFileProcessor : RenamePsiFileProcessor() {
override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element)
override fun prepareRenaming(element: PsiElement?,
override fun prepareRenaming(element: PsiElement,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope) {
@@ -0,0 +1,65 @@
/*
* 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.idea.refactoring.rename
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.rename.RenamePsiFileProcessor
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.psi.KtFile
class RenameKotlinFileProcessor : RenamePsiFileProcessor() {
class FileRenamingPsiClassWrapper(
private val psiClass: KtLightClass,
private val file: KtFile
) : KtLightClass by psiClass {
override fun isValid() = file.isValid
}
override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element)
override fun prepareRenaming(element: PsiElement?,
newName: String,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope) {
val jetFile = element as? KtFile ?: return
if (FileTypeManager.getInstance().getFileTypeByFileName(newName) != KotlinFileType.INSTANCE) {
return
}
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return
val fileInfo = JvmFileClassUtil.getFileClassInfoNoResolve(jetFile)
if (!fileInfo.withJvmName) {
val facadeFqName = fileInfo.facadeClassFqName
val project = jetFile.project
val facadeClass = JavaPsiFacade.getInstance(project)
.findClass(facadeFqName.asString(), GlobalSearchScope.moduleScope(module)) as? KtLightClass
if (facadeClass != null) {
allRenames[FileRenamingPsiClassWrapper(facadeClass, jetFile)] = PackagePartClassUtils.getFilePartShortName(newName)
}
}
}
}
@@ -88,11 +88,10 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
override fun findCollisions(
element: PsiElement,
newName: String?,
newName: String,
allRenames: Map<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.unwrapped as? KtNamedFunction ?: return
val descriptor = declaration.unsafeResolveToDescriptor()
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
@@ -115,7 +114,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected()
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? {
substituteForExpectOrActual(element)?.let { return it }
val wrappedMethod = wrapPsiMethod(element) ?: return element
@@ -179,11 +178,9 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
}
}
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) {
super.prepareRenaming(element, newName, allRenames, scope)
if (newName == null) return
if (element is KtLightMethod && getJvmName(element) == null) {
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
}
@@ -213,7 +210,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
}
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = SmartList<UsageInfo>()
for (usage in usages) {
@@ -0,0 +1,248 @@
/*
* Copyright 2010-2016 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.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Pass
import com.intellij.psi.*
import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameDialog
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.Pass
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsKotlinAware
import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.lang.IllegalStateException
import java.util.*
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
private val javaMethodProcessorInstance = RenameJavaMethodProcessor()
override fun canProcessElement(element: PsiElement): Boolean {
return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) || element is FunctionWithSupersWrapper
}
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD = enabled
}
private fun getJvmName(element: PsiElement): String? {
val descriptor = (element.unwrapped as? KtFunction)?.unsafeResolveToDescriptor() as? FunctionDescriptor ?: return null
return DescriptorUtils.getJvmName(descriptor)
}
override fun findReferences(element: PsiElement): Collection<PsiReference> {
val allReferences = super.findReferences(element)
return when {
getJvmName(element) == null -> allReferences
element is KtElement -> allReferences.filter { it is KtReference }
element is KtLightElement<*, *> -> allReferences.filterNot { it is KtReference }
else -> emptyList()
}
}
override fun findCollisions(
element: PsiElement,
newName: String?,
allRenames: Map<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.unwrapped as? KtNamedFunction ?: return
val descriptor = declaration.unsafeResolveToDescriptor()
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
result += SmartList<UsageInfo>().also { collisions ->
checkRedeclarations(descriptor, newName, collisions)
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
checkNewNameUsagesRetargeting(declaration, newName, collisions)
}
}
class FunctionWithSupersWrapper(
val originalDeclaration: KtNamedFunction,
val supers: List<PsiElement>
) : KtLightElement<KtNamedFunction, KtNamedFunction>, PsiNamedElement by originalDeclaration {
override val kotlinOrigin: KtNamedFunction?
get() = originalDeclaration
override val clsDelegate: KtNamedFunction
get() = originalDeclaration
}
private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected()
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
substituteForExpectOrActual(element)?.let { return it }
val wrappedMethod = wrapPsiMethod(element) ?: return element
val deepestSuperMethods = findDeepestSuperMethodsKotlinAware(wrappedMethod)
val substitutedJavaElement = when {
deepestSuperMethods.isEmpty() -> return element
wrappedMethod.isConstructor || deepestSuperMethods.size == 1 || element !is KtNamedFunction -> {
javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor)
}
else -> {
val chosenElements = checkSuperMethods(element, null, "rename")
if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod
}
}
if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
}
return substitutedJavaElement
}
override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass<PsiElement>) {
fun preprocessAndPass(substitutedJavaElement: PsiElement) {
val elementToProcess = if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
substitutedJavaElement.kotlinOrigin as? KtNamedFunction
}
else {
substitutedJavaElement
}
renameCallback.pass(elementToProcess)
}
substituteForExpectOrActual(element)?.let { return preprocessAndPass(it) }
val wrappedMethod = wrapPsiMethod(element)
val deepestSuperMethods = if (wrappedMethod != null) {
findDeepestSuperMethodsKotlinAware(wrappedMethod)
} else {
findDeepestSuperMethodsNoWrapping(element)
}
when {
deepestSuperMethods.isEmpty() -> preprocessAndPass(element)
wrappedMethod != null && (wrappedMethod.isConstructor || element !is KtNamedFunction) -> {
javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor, Pass(::preprocessAndPass))
}
else -> {
val declaration = element.unwrapped as? KtNamedFunction ?: return
checkSuperMethodsWithPopup(declaration, deepestSuperMethods.toList(), "Rename", editor) {
preprocessAndPass(if (it.size > 1) FunctionWithSupersWrapper(declaration, it) else wrappedMethod ?: element)
}
}
}
}
override fun createRenameDialog(project: Project, element: PsiElement, nameSuggestionContext: PsiElement?, editor: Editor?): RenameDialog {
val elementForDialog = (element as? FunctionWithSupersWrapper)?.originalDeclaration ?: element
return object : RenameDialog(project, elementForDialog, nameSuggestionContext, editor) {
override fun createRenameProcessor(newName: String) = RenameProcessor(getProject(), element, newName, isSearchInComments, isSearchInNonJavaFiles)
}
}
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
super.prepareRenaming(element, newName, allRenames, scope)
if (newName == null) return
if (element is KtLightMethod && getJvmName(element) == null) {
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
}
if (element is FunctionWithSupersWrapper) {
allRenames.remove(element)
}
for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) {
val psiMethod = wrapPsiMethod(declaration) ?: continue
allRenames[declaration] = newName
if (psiMethod.containingClass != null) {
psiMethod.forEachOverridingMethod { it ->
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
if (overrider is SyntheticElement) return@forEachOverridingMethod true
val overriderName = overrider.name
val baseName = psiMethod.name
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName)
if (newOverriderName != null) {
RenameProcessor.assertNonCompileElement(overrider)
allRenames.put(overrider, newOverriderName)
}
return@forEachOverridingMethod true
}
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope)
}
}
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = SmartList<UsageInfo>()
for (usage in usages) {
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
usage.apply()
continue
}
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
}
else {
simpleUsages += usage
}
}
element.ambiguousImportUsages = ambiguousImportUsages
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
(element.unwrapped as? KtNamedDeclaration)?.let(::dropOverrideKeywordIfNecessary)
}
private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) {
is PsiMethod -> element
is KtNamedFunction, is KtSecondaryConstructor -> runReadAction {
LightClassUtil.getLightClassMethod(element as KtFunction)
}
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
}
}
@@ -39,11 +39,10 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
override fun findCollisions(
element: PsiElement,
newName: String?,
newName: String,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
@@ -54,7 +53,7 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
result += collisions
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
super.renameElement(element, newName, usages, listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
@@ -0,0 +1,62 @@
/*
* 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.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.utils.SmartList
class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement) = element is KtParameter && element.ownerFunction is KtFunction
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled
}
override fun findCollisions(
element: PsiElement,
newName: String?,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(descriptor, newName, collisions)
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
checkNewNameUsagesRetargeting(declaration, newName, collisions)
result += collisions
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
super.renameElement(element, newName, usages, listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
}
}
@@ -196,11 +196,10 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
override fun findCollisions(
element: PsiElement,
newName: String?,
newName: String,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
@@ -237,8 +236,8 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
}
}
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null
override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? {
val namedUnwrappedElement = element.namedUnwrappedElement ?: return null
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
@@ -293,7 +292,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
override fun copy() = this
}
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) {
super.prepareRenaming(element, newName, allRenames, scope)
val namedUnwrappedElement = element.namedUnwrappedElement
@@ -303,7 +302,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
}
val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
val newPropertyName = if (element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
@@ -0,0 +1,479 @@
/*
* Copyright 2010-2016 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.idea.refactoring.rename
import com.intellij.navigation.NavigationItem
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Pass
import com.intellij.psi.*
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewUtil
import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
import org.jetbrains.kotlin.idea.core.isEnumCompanionPropertyWithEntryConflict
import org.jetbrains.kotlin.idea.core.unquote
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.SmartList
class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
val namedUnwrappedElement = element.namedUnwrappedElement
return namedUnwrappedElement is KtProperty || (namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar())
}
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD = enabled
}
private fun getJvmNames(element: PsiElement): Pair<String?, String?> {
val descriptor = (element.unwrapped as? KtDeclaration)?.unsafeResolveToDescriptor() as? PropertyDescriptor ?: return null to null
val getterName = descriptor.getter?.let { DescriptorUtils.getJvmName(it) }
val setterName = descriptor.setter?.let { DescriptorUtils.getJvmName(it) }
return getterName to setterName
}
override fun findReferences(element: PsiElement): Collection<PsiReference> {
val allReferences = super.findReferences(element).filterNot { it is KtDestructuringDeclarationReference }
val (getterJvmName, setterJvmName) = getJvmNames(element)
return when {
getterJvmName == null && setterJvmName == null -> allReferences
element is KtElement -> allReferences.filter {
it is KtReference
|| (getterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != setterJvmName)
|| (setterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != getterJvmName)
}
element is KtLightDeclaration<*, *> -> {
val name = element.name
if (name == getterJvmName || name == setterJvmName) allReferences.filterNot { it is KtReference } else allReferences
}
else -> emptyList()
}
}
private fun checkAccidentalOverrides(
declaration: KtNamedDeclaration,
newName: String,
descriptor: VariableDescriptor,
result: MutableList<UsageInfo>
) {
fun reportAccidentalOverride(candidate: PsiNamedElement) {
val what = UsageViewUtil.getType(declaration).capitalize()
val withWhat = candidate.renderDescription()
val where = candidate.representativeContainer()?.renderDescription() ?: return
val message = "$what after rename will clash with existing $withWhat in $where"
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
}
if (descriptor !is PropertyDescriptor) return
val initialClass = declaration.containingClassOrObject ?: return
val initialClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
val prototype = object : PropertyDescriptor by descriptor {
override fun getName() = Name.guessByFirstCharacter(newName)
}
DFS.dfs(
listOf(initialClassDescriptor),
DFS.Neighbors<ClassDescriptor> { DescriptorUtils.getSuperclassDescriptors(it) },
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
override fun beforeChildren(current: ClassDescriptor): Boolean {
if (current == initialClassDescriptor) return true
(current.findCallableMemberBySignature(prototype))?.let { candidateDescriptor ->
val candidate = candidateDescriptor.source.getPsi() as? PsiNamedElement ?: return false
reportAccidentalOverride(candidate)
return false
}
return true
}
override fun result() {
}
}
)
if (!declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
val initialPsiClass = initialClass.toLightClass() ?: return
val prototypes = declaration.toLightMethods().mapNotNull {
it as KtLightMethod
val methodName = accessorNameByPropertyName(newName, it) ?: return@mapNotNull null
object : KtLightMethod by it {
override fun getName() = methodName
}
}
DFS.dfs(
listOf(initialPsiClass),
DFS.Neighbors<PsiClass> { DirectClassInheritorsSearch.search(it) },
object : DFS.AbstractNodeHandler<PsiClass, Unit>() {
override fun beforeChildren(current: PsiClass): Boolean {
if (current == initialPsiClass) return true
if (current is KtLightClass) {
val property = current.kotlinOrigin?.findPropertyByName(newName) ?: return true
reportAccidentalOverride(property)
return false
}
for (psiMethod in prototypes) {
current.findMethodBySignature(psiMethod, false)?.let {
val candidate = it.unwrapped as? PsiNamedElement ?: return true
reportAccidentalOverride(candidate)
return false
}
}
return true
}
override fun result() {
}
}
)
}
}
override fun findCollisions(
element: PsiElement,
newName: String?,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(descriptor, newName, collisions)
checkAccidentalOverrides(declaration, newName, descriptor, collisions)
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
checkNewNameUsagesRetargeting(declaration, newName, collisions)
result += collisions
}
private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? {
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
return callableDeclaration
}
if (ApplicationManager.getApplication()!!.isUnitTestMode) return deepestSuperDeclaration
val containsText: String? =
deepestSuperDeclaration.fqName?.parent()?.asString() ?:
(deepestSuperDeclaration.parent as? KtClassOrObject)?.name
val result = Messages.showYesNoCancelDialog(
deepestSuperDeclaration.project,
if (containsText != null) "Do you want to rename base property from \n$containsText" else "Do you want to rename base property",
"Rename warning",
Messages.getQuestionIcon())
return when (result) {
Messages.YES -> deepestSuperDeclaration
Messages.NO -> callableDeclaration
else -> /* Cancel rename */ null
}
}
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
val namedUnwrappedElement = element?.namedUnwrappedElement ?: return null
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
val declarationToRename = chooseCallableToRename(callableDeclaration) ?: return null
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
if (element is KtLightMethod) {
val name = element.name
if (element.name != getterJvmName && element.name != setterJvmName) return declarationToRename
return declarationToRename.toLightMethods().firstOrNull { it.name == name }
}
return declarationToRename
}
override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass<PsiElement>) {
val namedUnwrappedElement = element.namedUnwrappedElement ?: return
val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration
?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
fun preprocessAndPass(substitutedJavaElement: PsiElement) {
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
val elementToProcess = if (element is KtLightMethod) {
val name = element.name
if (element.name != getterJvmName && element.name != setterJvmName) {
substitutedJavaElement
}
else {
substitutedJavaElement.toLightMethods().firstOrNull { it.name == name }
}
}
else substitutedJavaElement
renameCallback.pass(elementToProcess)
}
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
return preprocessAndPass(callableDeclaration)
}
val superPsiMethods = listOfNotNull(deepestSuperDeclaration.getRepresentativeLightMethod())
checkSuperMethodsWithPopup(callableDeclaration, superPsiMethods, "Rename", editor) {
preprocessAndPass(if (it.size > 1) deepestSuperDeclaration else callableDeclaration)
}
}
class PropertyMethodWrapper(private val propertyMethod: PsiMethod) : PsiNamedElement by propertyMethod, NavigationItem by propertyMethod {
override fun getName() = propertyMethod.name
override fun setName(name: String) = this
override fun copy() = this
}
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
super.prepareRenaming(element, newName, allRenames, scope)
val namedUnwrappedElement = element.namedUnwrappedElement
val propertyMethods = when(namedUnwrappedElement) {
is KtProperty -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) }
is KtParameter -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) }
else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
}
val newPropertyName = if (newName != null && element is KtLightMethod) propertyNameByAccessor(newName, element) else newName
val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement)
val getter = propertyMethods.getter as? KtLightMethod
val setter = propertyMethods.setter as? KtLightMethod
if (newPropertyName != null
&& getter != null && setter != null
&& (element == getter || element == setter)
&& propertyNameByAccessor(getter.name, getter) == propertyNameByAccessor(setter.name, setter)) {
val accessorToRename = if (element == getter) setter else getter
val newAccessorName = if (element == getter) JvmAbi.setterName(newPropertyName) else JvmAbi.getterName(newPropertyName)
if (ApplicationManager.getApplication().isUnitTestMode
|| Messages.showYesNoDialog("Do you want to rename ${accessorToRename.name}() as well?",
"Rename",
Messages.getQuestionIcon()) == Messages.YES) {
allRenames[accessorToRename] = newAccessorName
}
}
for (propertyMethod in propertyMethods) {
if (element is KtDeclaration && newPropertyName != null) {
val wrapper = PropertyMethodWrapper(propertyMethod)
when {
JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null ->
allRenames[wrapper] = JvmAbi.getterName(newPropertyName)
JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null ->
allRenames[wrapper] = JvmAbi.setterName(newPropertyName)
}
}
addRenameElements(propertyMethod, (element as PsiNamedElement).name, newPropertyName, allRenames, scope)
}
}
private enum class UsageKind {
SIMPLE_PROPERTY_USAGE,
GETTER_USAGE,
SETTER_USAGE
}
override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val newNameUnquoted = newName.unquote()
if (element is KtLightMethod) {
if (element.modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString()) != null) {
return super.renameElement(element, newName, usages, listener)
}
val origin = element.kotlinOrigin
val newPropertyName = propertyNameByAccessor(newNameUnquoted, element)
// Kotlin references to Kotlin property should not use accessor name
if (newPropertyName != null && (origin is KtProperty || origin is KtParameter)) {
val (ktUsages, otherUsages) = usages.partition { it.reference is KtSimpleNameReference }
super.renameElement(element, newName, otherUsages.toTypedArray(), listener)
renameElement(origin, newPropertyName.quoteIfNeeded(), ktUsages.toTypedArray(), listener)
return
}
}
if (element !is KtProperty && element !is KtParameter) {
super.renameElement(element, newName, usages, listener)
return
}
val name = (element as KtNamedDeclaration).name!!
val oldGetterName = JvmAbi.getterName(name)
val oldSetterName = JvmAbi.setterName(name)
if (isEnumCompanionPropertyWithEntryConflict(element, newNameUnquoted)) {
for ((i, usage) in usages.withIndex()) {
if (usage !is MoveRenameUsageInfo) continue
val ref = usage.reference ?: continue
// TODO: Enum value can't be accessed from Java in case of conflict with companion member
if (ref is KtReference) {
val newRef = (ref.bindToElement(element) as? KtSimpleNameExpression)?.mainReference ?: continue
usages[i] = MoveRenameUsageInfo(newRef, usage.referencedElement)
}
}
}
val adjustedUsages = if (element is KtParameter) usages.filterNot {
val refTarget = it.reference?.resolve()
refTarget is KtLightMethod && DataClassDescriptorResolver.isComponentLike(Name.guessByFirstCharacter(refTarget.name))
} else usages.toList()
val refKindUsages = adjustedUsages.groupBy { usage: UsageInfo ->
val refElement = usage.reference?.resolve()
if (refElement is PsiMethod) {
when (refElement.name) {
oldGetterName -> UsageKind.GETTER_USAGE
oldSetterName -> UsageKind.SETTER_USAGE
else -> UsageKind.SIMPLE_PROPERTY_USAGE
}
}
else {
UsageKind.SIMPLE_PROPERTY_USAGE
}
}
super.renameElement(element, JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(),
refKindUsages[UsageKind.SETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
null)
super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(),
refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
null)
super.renameElement(element, newName,
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
null)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
dropOverrideKeywordIfNecessary(element)
listener?.elementRenamed(element)
}
private fun addRenameElements(psiMethod: PsiMethod?,
oldName: String?,
newName: String?,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope) {
if (psiMethod == null) return
OverridingMethodsSearch.search(psiMethod, scope, true).forEach { overrider ->
val overriderElement = overrider.namedUnwrappedElement
if (overriderElement != null && overriderElement !is SyntheticElement) {
RenameProcessor.assertNonCompileElement(overriderElement)
val overriderName = overriderElement.name
if (overriderElement is PsiMethod) {
if (newName != null && Name.isValidIdentifier(newName)) {
val isGetter = overriderElement.parameterList.parametersCount == 0
allRenames[overriderElement] = if (isGetter) JvmAbi.getterName(newName) else JvmAbi.setterName(newName)
}
}
else {
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName)
if (newOverriderName != null) {
allRenames[overriderElement] = newOverriderName
}
}
}
}
}
private fun findDeepestOverriddenDeclaration(declaration: KtCallableDeclaration): KtCallableDeclaration? {
if (declaration.modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) {
val bindingContext = declaration.analyze()
var descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
if (descriptor is ValueParameterDescriptor) {
descriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor]
?: return declaration
}
if (descriptor != null) {
assert(descriptor is PropertyDescriptor) { "Property descriptor is expected" }
val supers = (descriptor as PropertyDescriptor).getDeepestSuperDeclarations()
// Take one of supers for now - API doesn't support substitute to several elements (IDEA-48796)
val deepest = supers.first()
if (deepest != descriptor) {
val superPsiElement = DescriptorToSourceUtils.descriptorToDeclaration(deepest)
return superPsiElement as? KtCallableDeclaration
}
}
}
return null
}
}

Some files were not shown because too many files have changed in this diff Show More