updating run configuration on class rename (KT-6731)
This commit is contained in:
committed by
Nikolay Krasko
parent
4fbd982059
commit
247ffdccbe
@@ -36,7 +36,10 @@ import com.intellij.openapi.util.InvalidDataException;
|
|||||||
import com.intellij.openapi.util.WriteExternalException;
|
import com.intellij.openapi.util.WriteExternalException;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiClass;
|
import com.intellij.psi.PsiClass;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiMethod;
|
import com.intellij.psi.PsiMethod;
|
||||||
|
import com.intellij.refactoring.listeners.RefactoringElementAdapter;
|
||||||
|
import com.intellij.refactoring.listeners.RefactoringElementListener;
|
||||||
import kotlin.KotlinPackage;
|
import kotlin.KotlinPackage;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
@@ -51,6 +54,7 @@ import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionFqnNameIndex;
|
|||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.JetDeclaration;
|
import org.jetbrains.kotlin.psi.JetDeclaration;
|
||||||
|
import org.jetbrains.kotlin.psi.JetDeclarationContainer;
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction;
|
import org.jetbrains.kotlin.psi.JetNamedFunction;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||||
@@ -58,7 +62,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class JetRunConfiguration extends ModuleBasedConfiguration<RunConfigurationModule>
|
public class JetRunConfiguration extends ModuleBasedConfiguration<RunConfigurationModule>
|
||||||
implements CommonJavaRunConfigurationParameters {
|
implements CommonJavaRunConfigurationParameters, RefactoringListenerProvider {
|
||||||
|
|
||||||
public String MAIN_CLASS_NAME;
|
public String MAIN_CLASS_NAME;
|
||||||
public String VM_PARAMETERS;
|
public String VM_PARAMETERS;
|
||||||
@@ -218,6 +222,36 @@ public class JetRunConfiguration extends ModuleBasedConfiguration<RunConfigurati
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public RefactoringElementListener getRefactoringElementListener(PsiElement element) {
|
||||||
|
if (element instanceof JetDeclarationContainer) {
|
||||||
|
FqName name = KotlinRunConfigurationProducer.Companion.getStartClassFqName((JetDeclarationContainer) element);
|
||||||
|
if (name != null && name.asString().equals(MAIN_CLASS_NAME)) {
|
||||||
|
return new RefactoringElementAdapter() {
|
||||||
|
@Override
|
||||||
|
public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
|
||||||
|
updateMainClassName(newElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void elementRenamedOrMoved(@NotNull PsiElement newElement) {
|
||||||
|
updateMainClassName(newElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMainClassName(PsiElement element) {
|
||||||
|
JetDeclarationContainer container = KotlinRunConfigurationProducer.Companion.getEntryPointContainer(element);
|
||||||
|
FqName name = KotlinRunConfigurationProducer.Companion.getStartClassFqName(container);
|
||||||
|
if (name != null) {
|
||||||
|
MAIN_CLASS_NAME = name.asString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class MyJavaCommandLineState extends JavaCommandLineState {
|
private static class MyJavaCommandLineState extends JavaCommandLineState {
|
||||||
|
|
||||||
private final JetRunConfiguration myConfiguration;
|
private final JetRunConfiguration myConfiguration;
|
||||||
|
|||||||
@@ -53,20 +53,6 @@ public class KotlinRunConfigurationProducer : RunConfigurationProducer<JetRunCon
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStartClassFqName(container: JetDeclarationContainer?): FqName? = when(container) {
|
|
||||||
null -> null
|
|
||||||
is JetFile -> PackageClassUtils.getPackageClassFqName(container.getPackageFqName())
|
|
||||||
is JetClassOrObject -> {
|
|
||||||
if (container is JetObjectDeclaration && container.isCompanion()) {
|
|
||||||
val containerClass = container.getParentOfType<JetClass>(true)
|
|
||||||
containerClass?.getFqName()
|
|
||||||
} else {
|
|
||||||
container.getFqName()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Invalid entry-point container: " + (container as PsiElement).getText())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getEntryPointContainer(location: Location<*>?): JetDeclarationContainer? {
|
private fun getEntryPointContainer(location: Location<*>?): JetDeclarationContainer? {
|
||||||
if (location == null) return null
|
if (location == null) return null
|
||||||
if (DumbService.getInstance(location.getProject()).isDumb()) return null
|
if (DumbService.getInstance(location.getProject()).isDumb()) return null
|
||||||
@@ -77,31 +63,7 @@ public class KotlinRunConfigurationProducer : RunConfigurationProducer<JetRunCon
|
|||||||
|
|
||||||
val locationElement = location.getPsiElement()
|
val locationElement = location.getPsiElement()
|
||||||
|
|
||||||
val psiFile = locationElement.getContainingFile()
|
return getEntryPointContainer(locationElement)
|
||||||
if (!(psiFile is JetFile && ProjectRootsUtil.isInProjectOrLibSource(psiFile))) return null
|
|
||||||
|
|
||||||
val resolutionFacade = psiFile.getResolutionFacade()
|
|
||||||
val mainFunctionDetector = MainFunctionDetector { resolutionFacade.resolveToDescriptor(it) as FunctionDescriptor }
|
|
||||||
|
|
||||||
var currentElement = locationElement.declarationContainer(false)
|
|
||||||
while (currentElement != null) {
|
|
||||||
var entryPointContainer = currentElement
|
|
||||||
if (entryPointContainer is JetClass) {
|
|
||||||
entryPointContainer = entryPointContainer.getCompanionObjects().singleOrNull()
|
|
||||||
}
|
|
||||||
if (entryPointContainer != null && mainFunctionDetector.hasMain(entryPointContainer.getDeclarations())) return entryPointContainer
|
|
||||||
currentElement = (currentElement as PsiElement).declarationContainer(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun PsiElement.declarationContainer(strict: Boolean): JetDeclarationContainer? {
|
|
||||||
val element = if (strict)
|
|
||||||
PsiTreeUtil.getParentOfType(this, javaClass<JetClassOrObject>(), javaClass<JetFile>())
|
|
||||||
else
|
|
||||||
PsiTreeUtil.getNonStrictParentOfType(this, javaClass<JetClassOrObject>(), javaClass<JetFile>())
|
|
||||||
return element as JetDeclarationContainer?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupConfigurationByQName(module: Module,
|
private fun setupConfigurationByQName(module: Module,
|
||||||
@@ -118,4 +80,49 @@ public class KotlinRunConfigurationProducer : RunConfigurationProducer<JetRunCon
|
|||||||
return configuration.getRunClass() == startClassFQName.asString() &&
|
return configuration.getRunClass() == startClassFQName.asString() &&
|
||||||
context.getModule() == configuration.getConfigurationModule().getModule()
|
context.getModule() == configuration.getConfigurationModule().getModule()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
public fun getEntryPointContainer(locationElement: PsiElement): JetDeclarationContainer? {
|
||||||
|
val psiFile = locationElement.getContainingFile()
|
||||||
|
if (!(psiFile is JetFile && ProjectRootsUtil.isInProjectOrLibSource(psiFile))) return null
|
||||||
|
|
||||||
|
val resolutionFacade = psiFile.getResolutionFacade()
|
||||||
|
val mainFunctionDetector = MainFunctionDetector { resolutionFacade.resolveToDescriptor(it) as FunctionDescriptor }
|
||||||
|
|
||||||
|
var currentElement = locationElement.declarationContainer(false)
|
||||||
|
while (currentElement != null) {
|
||||||
|
var entryPointContainer = currentElement
|
||||||
|
if (entryPointContainer is JetClass) {
|
||||||
|
entryPointContainer = entryPointContainer.getCompanionObjects().singleOrNull()
|
||||||
|
}
|
||||||
|
if (entryPointContainer != null && mainFunctionDetector.hasMain(entryPointContainer.getDeclarations())) return entryPointContainer
|
||||||
|
currentElement = (currentElement as PsiElement).declarationContainer(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getStartClassFqName(container: JetDeclarationContainer?): FqName? = when(container) {
|
||||||
|
null -> null
|
||||||
|
is JetFile -> PackageClassUtils.getPackageClassFqName(container.getPackageFqName())
|
||||||
|
is JetClassOrObject -> {
|
||||||
|
if (container is JetObjectDeclaration && container.isCompanion()) {
|
||||||
|
val containerClass = container.getParentOfType<JetClass>(true)
|
||||||
|
containerClass?.getFqName()
|
||||||
|
} else {
|
||||||
|
container.getFqName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid entry-point container: " + (container as PsiElement).getText())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiElement.declarationContainer(strict: Boolean): JetDeclarationContainer? {
|
||||||
|
val element = if (strict)
|
||||||
|
PsiTreeUtil.getParentOfType(this, javaClass<JetClassOrObject>(), javaClass<JetFile>())
|
||||||
|
else
|
||||||
|
PsiTreeUtil.getNonStrictParentOfType(this, javaClass<JetClassOrObject>(), javaClass<JetFile>())
|
||||||
|
return element as JetDeclarationContainer?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package renameTest
|
||||||
|
|
||||||
|
import kotlin.platform.platformStatic
|
||||||
|
|
||||||
|
object Foo {
|
||||||
|
platformStatic fun main(args: Array<String>) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import com.intellij.codeInsight.CodeInsightTestCase
|
|||||||
import com.intellij.execution.Executor
|
import com.intellij.execution.Executor
|
||||||
import com.intellij.execution.Location
|
import com.intellij.execution.Location
|
||||||
import com.intellij.execution.PsiLocation
|
import com.intellij.execution.PsiLocation
|
||||||
|
import com.intellij.execution.RunManagerEx
|
||||||
import com.intellij.execution.actions.ConfigurationContext
|
import com.intellij.execution.actions.ConfigurationContext
|
||||||
import com.intellij.execution.configurations.JavaCommandLine
|
import com.intellij.execution.configurations.JavaCommandLine
|
||||||
import com.intellij.execution.configurations.JavaParameters
|
import com.intellij.execution.configurations.JavaParameters
|
||||||
@@ -34,17 +35,21 @@ import com.intellij.openapi.roots.ModuleRootModificationUtil
|
|||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiComment
|
import com.intellij.psi.PsiComment
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.refactoring.RefactoringFactory
|
||||||
import com.intellij.testFramework.MapDataContext
|
import com.intellij.testFramework.MapDataContext
|
||||||
import com.intellij.testFramework.PlatformTestCase
|
import com.intellij.testFramework.PlatformTestCase
|
||||||
import com.intellij.testFramework.PsiTestUtil
|
import com.intellij.testFramework.PsiTestUtil
|
||||||
import org.jetbrains.kotlin.idea.search.allScope
|
import org.jetbrains.kotlin.idea.search.allScope
|
||||||
|
import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex
|
||||||
import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionFqnNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionFqnNameIndex
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil.configureKotlinJsRuntimeAndSdk
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil.configureKotlinJsRuntimeAndSdk
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil.configureKotlinRuntimeAndSdk
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil.configureKotlinRuntimeAndSdk
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.psi.JetFunction
|
||||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
||||||
import org.jetbrains.kotlin.psi.JetTreeVisitorVoid
|
import org.jetbrains.kotlin.psi.JetTreeVisitorVoid
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
@@ -98,6 +103,19 @@ class RunConfigurationTest: CodeInsightTestCase() {
|
|||||||
doTest(ConfigLibraryUtil::configureKotlinJsRuntimeAndSdk)
|
doTest(ConfigLibraryUtil::configureKotlinJsRuntimeAndSdk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testUpdateOnClassRename() {
|
||||||
|
val createModuleResult = configureModule(moduleDirPath("module"), getTestProject().getBaseDir()!!)
|
||||||
|
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(createModuleResult.module, PluginTestCaseBase.mockJdk())
|
||||||
|
|
||||||
|
val runConfiguration = createConfigurationFromObject("renameTest.Foo", save = true)
|
||||||
|
|
||||||
|
val obj = JetFullClassNameIndex.getInstance().get("renameTest.Foo", getTestProject(), getTestProject().allScope()).single()
|
||||||
|
val rename = RefactoringFactory.getInstance(getTestProject()).createRename(obj, "Bar")
|
||||||
|
rename.run()
|
||||||
|
|
||||||
|
Assert.assertEquals("renameTest.Bar", runConfiguration.MAIN_CLASS_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
private fun doTest(configureRuntime: (Module, Sdk) -> Unit) {
|
private fun doTest(configureRuntime: (Module, Sdk) -> Unit) {
|
||||||
val baseDir = getTestProject().getBaseDir()!!
|
val baseDir = getTestProject().getBaseDir()!!
|
||||||
val createModuleResult = configureModule(moduleDirPath("module"), baseDir)
|
val createModuleResult = configureModule(moduleDirPath("module"), baseDir)
|
||||||
@@ -140,10 +158,24 @@ class RunConfigurationTest: CodeInsightTestCase() {
|
|||||||
private fun createConfigurationFromMain(mainFqn: String): JetRunConfiguration {
|
private fun createConfigurationFromMain(mainFqn: String): JetRunConfiguration {
|
||||||
val mainFunction = JetTopLevelFunctionFqnNameIndex.getInstance().get(mainFqn, getTestProject(), getTestProject().allScope()).first()
|
val mainFunction = JetTopLevelFunctionFqnNameIndex.getInstance().get(mainFqn, getTestProject(), getTestProject().allScope()).first()
|
||||||
|
|
||||||
val dataContext = MapDataContext()
|
return createConfigurationFromElement(mainFunction)
|
||||||
dataContext.put(Location.DATA_KEY, PsiLocation(getTestProject(), mainFunction))
|
}
|
||||||
|
|
||||||
return ConfigurationContext.getFromContext(dataContext)!!.getConfiguration()!!.getConfiguration() as JetRunConfiguration
|
private fun createConfigurationFromObject(objectFqn: String, save: Boolean = false): JetRunConfiguration {
|
||||||
|
val obj = JetFullClassNameIndex.getInstance().get(objectFqn, getTestProject(), getTestProject().allScope()).single()
|
||||||
|
val mainFunction = obj.getDeclarations().single { it is JetFunction && it.getName() == "main" }
|
||||||
|
return createConfigurationFromElement(mainFunction, save)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createConfigurationFromElement(element: PsiElement?, save: Boolean = false): JetRunConfiguration {
|
||||||
|
val dataContext = MapDataContext()
|
||||||
|
dataContext.put(Location.DATA_KEY, PsiLocation(getTestProject(), element))
|
||||||
|
|
||||||
|
val runnerAndConfigurationSettings = ConfigurationContext.getFromContext(dataContext)!!.getConfiguration()
|
||||||
|
if (save) {
|
||||||
|
RunManagerEx.getInstanceEx(myProject).setTemporaryConfiguration(runnerAndConfigurationSettings)
|
||||||
|
}
|
||||||
|
return runnerAndConfigurationSettings!!.getConfiguration() as JetRunConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureModule(moduleDir: String, outputParentDir: VirtualFile, configModule: Module = getModule()): CreateModuleResult {
|
private fun configureModule(moduleDir: String, outputParentDir: VirtualFile, configModule: Module = getModule()): CreateModuleResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user