Configuration providers don't create configurations for common code

Remove special code that creates configurations
    and patches them for common modules
This should be done via MultipleRunLocationsProvider EP
This commit is contained in:
Pavel V. Talanov
2018-07-04 19:47:16 +02:00
parent 25882c5b46
commit 456bf9808d
6 changed files with 636 additions and 33 deletions
@@ -12,7 +12,6 @@ import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.configurations.ModuleBasedConfiguration
import com.intellij.execution.junit.*
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
@@ -21,13 +20,11 @@ import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfiguration>(JUnitConfigurationType.getInstance()) {
@@ -86,20 +83,21 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfig
return false
}
val ktFile = leaf.containingFile as KtFile
val module = location.module ?: return false
val targetPlatform = TargetPlatformDetector.getPlatform(ktFile)
if (targetPlatform != JvmPlatform && targetPlatform != TargetPlatform.Common) {
val targetPlatform = TargetPlatformDetector.getPlatform(module)
if (targetPlatform != JvmPlatform) {
return false
}
configuration.setModule(module)
val methodLocation = getTestMethodLocation(leaf)
if (methodLocation != null) {
val originalModule = configuration.configurationModule.module
configuration.beMethodConfiguration(methodLocation)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
@@ -109,23 +107,12 @@ class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfig
configuration.beClassConfiguration(testClass)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
return false
}
private fun fixJdkForCommonModule(ktFile: KtFile, configuration: JUnitConfiguration) {
val implModule = ktFile.module?.findJvmImplementationModule() ?: return
val sdk = ModuleRootManager.getInstance(implModule).sdk
if (sdk != null) {
configuration.setModule(implModule)
configuration.isAlternativeJrePathEnabled = true
configuration.alternativeJrePath = sdk.homePath
}
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
val leaf = fromContext.sourceElement
getTestClass(leaf)?.let { testClass ->
@@ -0,0 +1,180 @@
/*
* Copyright 2010-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.run
import com.intellij.execution.*
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.configurations.ModuleBasedConfiguration
import com.intellij.execution.junit.*
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfiguration>(JUnitConfigurationType.getInstance()) {
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return other.isProducedBy(JUnitConfigurationProducer::class.java) || other.isProducedBy(PatternConfigurationProducer::class.java)
}
override fun isConfigurationFromContext(
configuration: JUnitConfiguration,
context: ConfigurationContext
): Boolean {
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
val leaf = context.location?.psiElement ?: return false
val methodLocation = getTestMethodLocation(leaf)
val testClass = getTestClass(leaf)
val testObject = configuration.testObject
if (!testObject.isConfiguredByElement(configuration, testClass, methodLocation?.psiElement, null, null)) {
return false
}
return settingsMatchTemplate(configuration, context)
}
// copied from JUnitConfigurationProducer in IDEA
private fun settingsMatchTemplate(configuration: JUnitConfiguration, context: ConfigurationContext): Boolean {
val predefinedConfiguration = context.getOriginalConfiguration(JUnitConfigurationType.getInstance())
val vmParameters = (predefinedConfiguration as? CommonJavaRunConfigurationParameters)?.vmParameters
if (vmParameters != null && configuration.vmParameters != vmParameters) return false
val template = RunManager.getInstance(configuration.project).getConfigurationTemplate(configurationFactory)
val predefinedModule = (template.configuration as ModuleBasedConfiguration<*>).configurationModule.module
val configurationModule = configuration.configurationModule.module
return configurationModule == context.location?.module || configurationModule == predefinedModule
}
override fun setupConfigurationFromContext(
configuration: JUnitConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (DumbService.getInstance(context.project).isDumb) return false
val location = context.location ?: return false
val leaf = location.psiElement
if (!ProjectRootsUtil.isInProjectOrLibSource(leaf)) {
return false
}
if (leaf.containingFile !is KtFile) {
return false
}
val ktFile = leaf.containingFile as KtFile
val targetPlatform = TargetPlatformDetector.getPlatform(ktFile)
if (targetPlatform != JvmPlatform && targetPlatform != TargetPlatform.Common) {
return false
}
val methodLocation = getTestMethodLocation(leaf)
if (methodLocation != null) {
val originalModule = configuration.configurationModule.module
configuration.beMethodConfiguration(methodLocation)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
val testClass = getTestClass(leaf)
if (testClass != null) {
val originalModule = configuration.configurationModule.module
configuration.beClassConfiguration(testClass)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
return false
}
private fun fixJdkForCommonModule(ktFile: KtFile, configuration: JUnitConfiguration) {
val implModule = ktFile.module?.findJvmImplementationModule() ?: return
val sdk = ModuleRootManager.getInstance(implModule).sdk
if (sdk != null) {
configuration.setModule(implModule)
configuration.isAlternativeJrePathEnabled = true
configuration.alternativeJrePath = sdk.homePath
}
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
val leaf = fromContext.sourceElement
getTestClass(leaf)?.let { testClass ->
val fromContextSubstitute = object : ConfigurationFromContext() {
override fun getConfigurationSettings() = fromContext.configurationSettings
override fun setConfigurationSettings(configurationSettings: RunnerAndConfigurationSettings) {
fromContext.configurationSettings = configurationSettings
}
override fun getSourceElement() = testClass
}
// TODO: use TestClassConfigurationProducer when constructor becomes public
return object : AbstractTestClassConfigurationProducer(JUnitConfigurationType.getInstance()){}
.onFirstRun(fromContextSubstitute, context, performRunnable)
}
super.onFirstRun(fromContext, context, performRunnable)
}
companion object {
fun getTestClass(leaf: PsiElement): PsiClass? {
val containingFile = leaf.containingFile as? KtFile ?: return null
var ktClass = leaf.getParentOfType<KtClass>(false)
if (!ktClass.isJUnitTestClass()) {
ktClass = getTestClassInFile(containingFile)
}
return ktClass?.toLightClass()
}
fun getTestMethodLocation(leaf: PsiElement): Location<PsiMethod>? {
val function = leaf.getParentOfType<KtNamedFunction>(false) ?: return null
val owner = PsiTreeUtil.getParentOfType(function, KtFunction::class.java, KtClass::class.java)
if (owner is KtClass) {
val delegate = owner.toLightClass() ?: return null
val method = delegate.methods.firstOrNull() { it.navigationElement == function } ?: return null
val methodLocation = PsiLocation.fromPsiElement(method)
if (JUnitUtil.isTestMethod(methodLocation, false)) {
return methodLocation
}
}
return null
}
private fun KtClass?.isJUnitTestClass() =
this?.toLightClass()?.let { JUnitUtil.isTestClass(it, false, true) } ?: false
private fun getTestClassInFile(ktFile: KtFile) =
ktFile.declarations.filterIsInstance<KtClass>().singleOrNull { it.isJUnitTestClass() }
}
}
@@ -0,0 +1,180 @@
/*
* Copyright 2010-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.run
import com.intellij.execution.*
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.ConfigurationFromContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.configurations.ModuleBasedConfiguration
import com.intellij.execution.junit.*
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
class KotlinJUnitRunConfigurationProducer : RunConfigurationProducer<JUnitConfiguration>(JUnitConfigurationType.getInstance()) {
override fun shouldReplace(self: ConfigurationFromContext, other: ConfigurationFromContext): Boolean {
return other.isProducedBy(JUnitConfigurationProducer::class.java) || other.isProducedBy(PatternConfigurationProducer::class.java)
}
override fun isConfigurationFromContext(
configuration: JUnitConfiguration,
context: ConfigurationContext
): Boolean {
if (RunConfigurationProducer.getInstance(PatternConfigurationProducer::class.java).isMultipleElementsSelected(context)) {
return false
}
val leaf = context.location?.psiElement ?: return false
val methodLocation = getTestMethodLocation(leaf)
val testClass = getTestClass(leaf)
val testObject = configuration.testObject
if (!testObject.isConfiguredByElement(configuration, testClass, methodLocation?.psiElement, null, null)) {
return false
}
return settingsMatchTemplate(configuration, context)
}
// copied from JUnitConfigurationProducer in IDEA
private fun settingsMatchTemplate(configuration: JUnitConfiguration, context: ConfigurationContext): Boolean {
val predefinedConfiguration = context.getOriginalConfiguration(JUnitConfigurationType.getInstance())
val vmParameters = (predefinedConfiguration as? CommonJavaRunConfigurationParameters)?.vmParameters
if (vmParameters != null && configuration.vmParameters != vmParameters) return false
val template = RunManager.getInstance(configuration.project).getConfigurationTemplate(configurationFactory)
val predefinedModule = (template.configuration as ModuleBasedConfiguration<*>).configurationModule.module
val configurationModule = configuration.configurationModule.module
return configurationModule == context.location?.module || configurationModule == predefinedModule
}
override fun setupConfigurationFromContext(
configuration: JUnitConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>
): Boolean {
if (DumbService.getInstance(context.project).isDumb) return false
val location = context.location ?: return false
val leaf = location.psiElement
if (!ProjectRootsUtil.isInProjectOrLibSource(leaf)) {
return false
}
if (leaf.containingFile !is KtFile) {
return false
}
val ktFile = leaf.containingFile as KtFile
val targetPlatform = TargetPlatformDetector.getPlatform(ktFile)
if (targetPlatform != JvmPlatform && targetPlatform != TargetPlatform.Common) {
return false
}
val methodLocation = getTestMethodLocation(leaf)
if (methodLocation != null) {
val originalModule = configuration.configurationModule.module
configuration.beMethodConfiguration(methodLocation)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
val testClass = getTestClass(leaf)
if (testClass != null) {
val originalModule = configuration.configurationModule.module
configuration.beClassConfiguration(testClass)
configuration.restoreOriginalModule(originalModule)
JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location)
fixJdkForCommonModule(ktFile, configuration)
return true
}
return false
}
private fun fixJdkForCommonModule(ktFile: KtFile, configuration: JUnitConfiguration) {
val implModule = ktFile.module?.findJvmImplementationModule() ?: return
val sdk = ModuleRootManager.getInstance(implModule).sdk
if (sdk != null) {
configuration.setModule(implModule)
configuration.isAlternativeJrePathEnabled = true
configuration.alternativeJrePath = sdk.homePath
}
}
override fun onFirstRun(fromContext: ConfigurationFromContext, context: ConfigurationContext, performRunnable: Runnable) {
val leaf = fromContext.sourceElement
getTestClass(leaf)?.let { testClass ->
val fromContextSubstitute = object : ConfigurationFromContext() {
override fun getConfigurationSettings() = fromContext.configurationSettings
override fun setConfigurationSettings(configurationSettings: RunnerAndConfigurationSettings) {
fromContext.configurationSettings = configurationSettings
}
override fun getSourceElement() = testClass
}
// TODO: use TestClassConfigurationProducer when constructor becomes public
return object : AbstractTestClassConfigurationProducer(JUnitConfigurationType.getInstance()){}
.onFirstRun(fromContextSubstitute, context, performRunnable)
}
super.onFirstRun(fromContext, context, performRunnable)
}
companion object {
fun getTestClass(leaf: PsiElement): PsiClass? {
val containingFile = leaf.containingFile as? KtFile ?: return null
var ktClass = leaf.getParentOfType<KtClass>(false)
if (!ktClass.isJUnitTestClass()) {
ktClass = getTestClassInFile(containingFile)
}
return ktClass?.toLightClass()
}
fun getTestMethodLocation(leaf: PsiElement): Location<PsiMethod>? {
val function = leaf.getParentOfType<KtNamedFunction>(false) ?: return null
val owner = PsiTreeUtil.getParentOfType(function, KtFunction::class.java, KtClass::class.java)
if (owner is KtClass) {
val delegate = owner.toLightClass() ?: return null
val method = delegate.methods.firstOrNull() { it.navigationElement == function } ?: return null
val methodLocation = PsiLocation.fromPsiElement(method)
if (JUnitUtil.isTestMethod(methodLocation, false)) {
return methodLocation
}
}
return null
}
private fun KtClass?.isJUnitTestClass() =
this?.toLightClass()?.let { JUnitUtil.isTestClass(it, false, true) } ?: false
private fun getTestClassInFile(ktFile: KtFile) =
ktFile.declarations.filterIsInstance<KtClass>().singleOrNull { it.isJUnitTestClass() }
}
}
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
@@ -28,13 +27,9 @@ import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.platform.impl.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
@@ -47,7 +42,7 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
sourceElement: Ref<PsiElement>): Boolean {
val location = context.location ?: return false
val module = location.module ?: return false
val container = getEntryPointContainer(location)
val container = getEntryPointContainer(location) ?: return false
val startClassFQName = getStartClassFqName(container) ?: return false
configuration.setModule(module)
@@ -63,7 +58,7 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
val module = location.module ?: return null
if (TargetPlatformDetector.getPlatform(module) !is JvmPlatform && module.findJvmImplementationModule() == null) {
if (TargetPlatformDetector.getPlatform(module) !is JvmPlatform) {
return null
}
val locationElement = location.psiElement
@@ -72,7 +67,8 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
}
override fun isConfigurationFromContext(configuration: KotlinRunConfiguration, context: ConfigurationContext): Boolean {
val startClassFQName = getStartClassFqName(getEntryPointContainer(context.location)) ?: return false
val entryPointContainer = getEntryPointContainer(context.location) ?: return false
val startClassFQName = getStartClassFqName(entryPointContainer) ?: return false
return configuration.runClass == startClassFQName &&
context.module == configuration.configurationModule.module
@@ -98,8 +94,7 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
return null
}
fun getStartClassFqName(container: KtDeclarationContainer?): String? = when(container) {
null -> null
fun getStartClassFqName(container: KtDeclarationContainer): String? = when(container) {
is KtFile -> container.javaFileFacadeFqName.asString()
is KtClassOrObject -> {
if (!container.isValid) {
@@ -126,8 +121,3 @@ class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfigu
}
}
fun Module.findJvmImplementationModule(): Module? {
if (!platform.isCommon) return null
return implementingModules.firstOrNull { it.platform.isJvm }
}
@@ -0,0 +1,133 @@
/*
* 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.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import com.intellij.psi.util.ClassUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.platform.impl.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfiguration>(KotlinRunConfigurationType.getInstance()) {
override fun setupConfigurationFromContext(configuration: KotlinRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>): Boolean {
val location = context.location ?: return false
val module = location.module ?: return false
val container = getEntryPointContainer(location)
val startClassFQName = getStartClassFqName(container) ?: return false
configuration.setModule(module)
configuration.runClass = startClassFQName
configuration.setGeneratedName()
return true
}
private fun getEntryPointContainer(location: Location<*>?): KtDeclarationContainer? {
if (location == null) return null
if (DumbService.getInstance(location.project).isDumb) return null
val module = location.module ?: return null
if (TargetPlatformDetector.getPlatform(module) !is JvmPlatform && module.findJvmImplementationModule() == null) {
return null
}
val locationElement = location.psiElement
return getEntryPointContainer(locationElement)
}
override fun isConfigurationFromContext(configuration: KotlinRunConfiguration, context: ConfigurationContext): Boolean {
val startClassFQName = getStartClassFqName(getEntryPointContainer(context.location)) ?: return false
return configuration.runClass == startClassFQName &&
context.module == configuration.configurationModule.module
}
companion object {
fun getEntryPointContainer(locationElement: PsiElement): KtDeclarationContainer? {
val psiFile = locationElement.containingFile
if (!(psiFile is KtFile && ProjectRootsUtil.isInProjectOrLibSource(psiFile))) return null
val mainFunctionDetector = MainFunctionDetector { it.resolveToDescriptorIfAny(BodyResolveMode.FULL) }
var currentElement = locationElement.declarationContainer(false)
while (currentElement != null) {
var entryPointContainer = currentElement
if (entryPointContainer is KtClass) {
entryPointContainer = entryPointContainer.companionObjects.singleOrNull()
}
if (entryPointContainer != null && mainFunctionDetector.hasMain(entryPointContainer.declarations)) return entryPointContainer
currentElement = (currentElement as PsiElement).declarationContainer(true)
}
return null
}
fun getStartClassFqName(container: KtDeclarationContainer?): String? = when(container) {
null -> null
is KtFile -> container.javaFileFacadeFqName.asString()
is KtClassOrObject -> {
if (!container.isValid) {
null
}
else if (container is KtObjectDeclaration && container.isCompanion()) {
val containerClass = container.getParentOfType<KtClass>(true)
containerClass?.toLightClass()?.let { ClassUtil.getJVMClassName(it) }
}
else {
container.toLightClass()?.let { ClassUtil.getJVMClassName(it) }
}
}
else -> throw IllegalArgumentException("Invalid entry-point container: " + (container as PsiElement).text)
}
private fun PsiElement.declarationContainer(strict: Boolean): KtDeclarationContainer? {
val element = if (strict)
PsiTreeUtil.getParentOfType(this, KtClassOrObject::class.java, KtFile::class.java)
else
PsiTreeUtil.getNonStrictParentOfType(this, KtClassOrObject::class.java, KtFile::class.java)
return element as KtDeclarationContainer?
}
}
}
fun Module.findJvmImplementationModule(): Module? {
if (!platform.isCommon) return null
return implementingModules.firstOrNull { it.platform.isJvm }
}
@@ -0,0 +1,133 @@
/*
* 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.run
import com.intellij.execution.Location
import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiElement
import com.intellij.psi.util.ClassUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.project.implementingModules
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.platform
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.platform.impl.isCommon
import org.jetbrains.kotlin.platform.impl.isJvm
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KotlinRunConfigurationProducer : RunConfigurationProducer<KotlinRunConfiguration>(KotlinRunConfigurationType.getInstance()) {
override fun setupConfigurationFromContext(configuration: KotlinRunConfiguration,
context: ConfigurationContext,
sourceElement: Ref<PsiElement>): Boolean {
val location = context.location ?: return false
val module = location.module ?: return false
val container = getEntryPointContainer(location)
val startClassFQName = getStartClassFqName(container) ?: return false
configuration.setModule(module)
configuration.runClass = startClassFQName
configuration.setGeneratedName()
return true
}
private fun getEntryPointContainer(location: Location<*>?): KtDeclarationContainer? {
if (location == null) return null
if (DumbService.getInstance(location.project).isDumb) return null
val module = location.module ?: return null
if (TargetPlatformDetector.getPlatform(module) !is JvmPlatform && module.findJvmImplementationModule() == null) {
return null
}
val locationElement = location.psiElement
return getEntryPointContainer(locationElement)
}
override fun isConfigurationFromContext(configuration: KotlinRunConfiguration, context: ConfigurationContext): Boolean {
val startClassFQName = getStartClassFqName(getEntryPointContainer(context.location)) ?: return false
return configuration.runClass == startClassFQName &&
context.module == configuration.configurationModule.module
}
companion object {
fun getEntryPointContainer(locationElement: PsiElement): KtDeclarationContainer? {
val psiFile = locationElement.containingFile
if (!(psiFile is KtFile && ProjectRootsUtil.isInProjectOrLibSource(psiFile))) return null
val mainFunctionDetector = MainFunctionDetector { it.resolveToDescriptorIfAny(BodyResolveMode.FULL) }
var currentElement = locationElement.declarationContainer(false)
while (currentElement != null) {
var entryPointContainer = currentElement
if (entryPointContainer is KtClass) {
entryPointContainer = entryPointContainer.companionObjects.singleOrNull()
}
if (entryPointContainer != null && mainFunctionDetector.hasMain(entryPointContainer.declarations)) return entryPointContainer
currentElement = (currentElement as PsiElement).declarationContainer(true)
}
return null
}
fun getStartClassFqName(container: KtDeclarationContainer?): String? = when(container) {
null -> null
is KtFile -> container.javaFileFacadeFqName.asString()
is KtClassOrObject -> {
if (!container.isValid) {
null
}
else if (container is KtObjectDeclaration && container.isCompanion()) {
val containerClass = container.getParentOfType<KtClass>(true)
containerClass?.toLightClass()?.let { ClassUtil.getJVMClassName(it) }
}
else {
container.toLightClass()?.let { ClassUtil.getJVMClassName(it) }
}
}
else -> throw IllegalArgumentException("Invalid entry-point container: " + (container as PsiElement).text)
}
private fun PsiElement.declarationContainer(strict: Boolean): KtDeclarationContainer? {
val element = if (strict)
PsiTreeUtil.getParentOfType(this, KtClassOrObject::class.java, KtFile::class.java)
else
PsiTreeUtil.getNonStrictParentOfType(this, KtClassOrObject::class.java, KtFile::class.java)
return element as KtDeclarationContainer?
}
}
}
fun Module.findJvmImplementationModule(): Module? {
if (!platform.isCommon) return null
return implementingModules.firstOrNull { it.platform.isJvm }
}