From 48a9226e1c311462619b4212af63906740f2571c Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Tue, 30 Jun 2020 12:24:31 +0000 Subject: [PATCH] Fix copy-paste from KTS files #KT-37517 Fixed --- .../kotlin/generators/tests/GenerateTests.kt | 22 ++++- .../KotlinCopyPasteReferenceProcessor.kt | 26 ++++- .../imports/implicitImport/expected.kts | 7 ++ .../imports/implicitImport/expected.names | 1 + .../imports/implicitImport/script.kts | 8 ++ .../imports/implicitImport/target.kts | 2 + .../implicitImport/template/template.kt | 58 +++++++++++ ...iptConfigurationInsertImportOnPasteTest.kt | 97 +++++++++++++++++++ .../script/AbstractScriptConfigurationTest.kt | 37 +++++-- ...ationInsertImportOnPasteTestGenerated.java | 56 +++++++++++ 10 files changed, 297 insertions(+), 17 deletions(-) create mode 100644 idea/testData/script/definition/imports/implicitImport/expected.kts create mode 100644 idea/testData/script/definition/imports/implicitImport/expected.names create mode 100644 idea/testData/script/definition/imports/implicitImport/script.kts create mode 100644 idea/testData/script/definition/imports/implicitImport/target.kts create mode 100644 idea/testData/script/definition/imports/implicitImport/template/template.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationInsertImportOnPasteTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationInsertImportOnPasteTestGenerated.java diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 93e0f57d556..20951f7cdf7 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -130,10 +130,7 @@ import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest import org.jetbrains.kotlin.idea.resolve.* import org.jetbrains.kotlin.idea.scratch.AbstractScratchLineMarkersTest import org.jetbrains.kotlin.idea.scratch.AbstractScratchRunActionTest -import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationCompletionTest -import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest -import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest -import org.jetbrains.kotlin.idea.script.AbstractScriptDefinitionsOrderTest +import org.jetbrains.kotlin.idea.script.* import org.jetbrains.kotlin.idea.slicer.AbstractSlicerLeafGroupingTest import org.jetbrains.kotlin.idea.slicer.AbstractSlicerMultiplatformTest import org.jetbrains.kotlin.idea.slicer.AbstractSlicerNullnessGroupingTest @@ -891,6 +888,23 @@ fun main(args: Array) { model("script/definition/completion", extension = null, recursive = false) } + testClass { + model( + "script/definition/imports", + testMethod = "doTestCopy", + testClassName = "Copy", + extension = null, + recursive = false + ) + model( + "script/definition/imports", + testMethod = "doTestCut", + testClassName = "Cut", + extension = null, + recursive = false + ) + } + testClass { model("script/definition/order", extension = null, recursive = false) } diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt index f7471bd7053..4feff42ede4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt @@ -27,11 +27,13 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.resolve.allowResolveInDispatchThread import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.codeInsight.ReviewAddedImports.reviewAddedImports import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests +import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.util.end import org.jetbrains.kotlin.idea.core.util.range import org.jetbrains.kotlin.idea.core.util.start @@ -48,6 +50,7 @@ import org.jetbrains.kotlin.idea.util.getSourceRoot import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.kdoc.psi.api.KDocElement import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.parsing.KotlinParserDefinition.Companion.STD_SCRIPT_EXT import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext @@ -428,15 +431,32 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor +val relocatedJarContents = configurations.creating { + attributes { + attribute(USAGE_ATTRIBUTE, JAVA_RUNTIME) + } +} diff --git a/idea/testData/script/definition/imports/implicitImport/target.kts b/idea/testData/script/definition/imports/implicitImport/target.kts new file mode 100644 index 00000000000..56cb67d7c89 --- /dev/null +++ b/idea/testData/script/definition/imports/implicitImport/target.kts @@ -0,0 +1,2 @@ +package to + \ No newline at end of file diff --git a/idea/testData/script/definition/imports/implicitImport/template/template.kt b/idea/testData/script/definition/imports/implicitImport/template/template.kt new file mode 100644 index 00000000000..3a87671d581 --- /dev/null +++ b/idea/testData/script/definition/imports/implicitImport/template/template.kt @@ -0,0 +1,58 @@ +package custom.scriptDefinition + +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.jvm.dependenciesFromClassContext +import kotlin.script.experimental.jvm.jvm +import kotlin.script.experimental.api.* + +@KotlinScript( + displayName = "Definition for tests", + fileExtension = "kts", + compilationConfiguration = TemplateDefinition::class +) +open class Template(val args: Array) + +@Suppress("UNCHECKED_CAST") +object TemplateDefinition : ScriptCompilationConfiguration( + { + baseClass(Base::class) + jvm { + dependenciesFromClassContext(TemplateDefinition::class) + } + ide { + acceptedLocations(ScriptAcceptedLocation.Everywhere) + } + } +) + +data class Attr(val name: String, val clazz: Class<*>) + +class KotlinPlatformType { + companion object { + val attribute = Attr("org.gradle.usage", Attr::class.java) + } +} + +class AttributesBuilder { + fun attribute(attribute: Attr, name: String) {} +} + +class ConfigurationsBuilder { + fun attributes(configuration: AttributesBuilder.() -> Unit) { + AttributesBuilder().apply(configuration) + } +} + +class Configurations { + fun creating(initializer: ConfigurationsBuilder.() -> Unit) { + ConfigurationsBuilder().apply(initializer) + } +} + +open class Base { + val configurations = Configurations() + + val USAGE_ATTRIBUTE = Attr("org.gradle.usage", Attr::class.java) + + val JAVA_RUNTIME = "java-runtime" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationInsertImportOnPasteTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationInsertImportOnPasteTest.kt new file mode 100644 index 00000000000..d617bf7539a --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationInsertImportOnPasteTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.script + +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.IdeActions +import com.intellij.openapi.actionSystem.Presentation +import com.intellij.openapi.actionSystem.ex.ActionManagerEx +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.editor.ex.EditorEx +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiManager +import com.intellij.util.ui.UIUtil +import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor +import org.jetbrains.kotlin.idea.codeInsight.ReviewAddedImports +import org.jetbrains.kotlin.idea.test.dumpTextWithErrors +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractScriptConfigurationInsertImportOnPasteTest : AbstractScriptConfigurationTest() { + private val NO_ERRORS_DUMP_DIRECTIVE = "// NO_ERRORS_DUMP" + private val DELETE_DEPENDENCIES_BEFORE_PASTE_DIRECTIVE = "// DELETE_DEPENDENCIES_BEFORE_PASTE" + + protected fun doTestCut(path: String) { + doTestAction(IdeActions.ACTION_CUT, path) + } + + protected fun doTestCopy(path: String) { + doTestAction(IdeActions.ACTION_COPY, path) + } + + private fun doTestAction(cutOrCopy: String, unused: String) { + val testDataFile = testDataFile() + val testFile = File(testDataFile, "script.kts") + val testFileText = FileUtil.loadFile(testFile, true) + val noErrorsDump = InTextDirectivesUtils.isDirectiveDefined(testFileText, NO_ERRORS_DUMP_DIRECTIVE) + + val scriptFile = configureScriptFile(testDataFile.path) + if (!noErrorsDump) { + val sourceFile = scriptFile.toKtFile() + val dumpTextWithErrors = sourceFile.dumpTextWithErrors() + assertEquals(sourceFile.text, dumpTextWithErrors) + } + + performEditorAction(cutOrCopy) + + KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested = emptyList() + ReviewAddedImports.importsToBeReviewed = emptyList() + + val importsToBeDeletedFile = File(testDataFile, "imports_to_delete") + ReviewAddedImports.importsToBeDeleted = if (importsToBeDeletedFile.exists()) { + importsToBeDeletedFile.readLines() + } else { + emptyList() + } + + val targetScript = createFileAndSyncDependencies(File(testDataFile, "target.kts")) + performEditorAction(IdeActions.ACTION_PASTE) + UIUtil.dispatchAllInvocationEvents() + + val namesToImportDump = KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested.joinToString("\n") + KotlinTestUtils.assertEqualsToFile(File(testDataFile, "expected.names"), namesToImportDump) + assertEquals(namesToImportDump, ReviewAddedImports.importsToBeReviewed.joinToString("\n")) + + val sourceFile = targetScript.toKtFile() + val sourceText = if (noErrorsDump) + sourceFile.text + else + sourceFile.dumpTextWithErrors() + KotlinTestUtils.assertEqualsToFile(File(testDataFile, "expected.kts"), sourceText) + } + + private fun VirtualFile.toKtFile() = PsiManager.getInstance(project).findFile(this) as KtFile + + private fun performEditorAction(actionId: String): Boolean { + val dataContext = getEditorDataContext() + val managerEx = ActionManagerEx.getInstanceEx() + val action = managerEx.getAction(actionId) + val event = AnActionEvent(null, dataContext, ActionPlaces.UNKNOWN, Presentation(), managerEx, 0) + action.beforeActionPerformedUpdate(event) + if (!event.presentation.isEnabled) { + return false + } + ActionUtil.performActionDumbAwareWithCallbacks(action, event, dataContext) + return true + } + + private fun getEditorDataContext() = (editor as EditorEx).dataContext + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt index d10bdb1ae76..8dba46182fc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt @@ -27,14 +27,15 @@ import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil +import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationTest.Companion.useDefaultTemplate import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.util.application.runWriteAction -import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.MockLibraryUtil import org.jetbrains.kotlin.test.TestJdkKind +import org.jetbrains.kotlin.test.TestMetadata import org.jetbrains.kotlin.test.util.addDependency import org.jetbrains.kotlin.test.util.projectLibrary import org.jetbrains.kotlin.utils.PathUtil @@ -43,13 +44,10 @@ import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR import java.io.File import java.util.regex.Pattern +import kotlin.reflect.full.findAnnotation import kotlin.script.dependencies.Environment import kotlin.script.experimental.api.ScriptDiagnostic -private val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names") -private const val useDefaultTemplate = "// DEPENDENCIES:" -private const val templatesSettings = "// TEMPLATES: " - // some bugs can only be reproduced when some module and script have intersecting library dependencies private const val configureConflictingModule = "// CONFLICTING_MODULE" @@ -62,6 +60,24 @@ internal val switches = listOf( abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { companion object { private const val SCRIPT_NAME = "script.kts" + + val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names") + const val useDefaultTemplate = "// DEPENDENCIES:" + const val templatesSettings = "// TEMPLATES: " + } + + protected fun testDataFile(fileName: String): File = File(testDataPath, fileName) + + protected fun testDataFile(): File = testDataFile(fileName()) + + protected fun testPath(fileName: String = fileName()): String = testDataFile(fileName).toString() + + protected fun testPath(): String = testPath(fileName()) + + protected open fun fileName(): String = KotlinTestUtils.getTestDataFileName(this::class.java, this.name) ?: (getTestName(false) + ".kt") + + override fun getTestDataPath(): String { + return this::class.findAnnotation()?.value ?: super.getTestDataPath() } override fun setUpModule() { @@ -84,12 +100,12 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { } } - protected fun configureScriptFile(path: String) { + protected fun configureScriptFile(path: String): VirtualFile { val mainScriptFile = findMainScript(path) - configureScriptFile(path, mainScriptFile) + return configureScriptFile(path, mainScriptFile) } - protected fun configureScriptFile(path: String, mainScriptFile: File) { + protected fun configureScriptFile(path: String, mainScriptFile: File): VirtualFile { val environment = createScriptEnvironment(mainScriptFile) registerScriptTemplateProvider(environment) @@ -136,7 +152,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { } } - createFileAndSyncDependencies(mainScriptFile) + return createFileAndSyncDependencies(mainScriptFile) } private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath") @@ -268,7 +284,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { } } - protected fun createFileAndSyncDependencies(scriptFile: File) { + protected fun createFileAndSyncDependencies(scriptFile: File): VirtualFile { var script: VirtualFile? = null if (module != null) { script = module.moduleFile?.parent?.findChild(scriptFile.name) @@ -284,6 +300,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { configureByExistingFile(script) loadScriptConfigurationSynchronously(script) + return script!! } protected open fun loadScriptConfigurationSynchronously(script: VirtualFile) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationInsertImportOnPasteTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationInsertImportOnPasteTestGenerated.java new file mode 100644 index 00000000000..eb7d608717f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationInsertImportOnPasteTestGenerated.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.script; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@RunWith(JUnit3RunnerWithInners.class) +public class ScriptConfigurationInsertImportOnPasteTestGenerated extends AbstractScriptConfigurationInsertImportOnPasteTest { + @TestMetadata("idea/testData/script/definition/imports") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Copy extends AbstractScriptConfigurationInsertImportOnPasteTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestCopy, this, testDataFilePath); + } + + public void testAllFilesPresentInCopy() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/script/definition/imports"), Pattern.compile("^([^\\.]+)$"), null, false); + } + + @TestMetadata("implicitImport") + public void testImplicitImport() throws Exception { + runTest("idea/testData/script/definition/imports/implicitImport/"); + } + } + + @TestMetadata("idea/testData/script/definition/imports") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Cut extends AbstractScriptConfigurationInsertImportOnPasteTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestCut, this, testDataFilePath); + } + + public void testAllFilesPresentInCut() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/script/definition/imports"), Pattern.compile("^([^\\.]+)$"), null, false); + } + + @TestMetadata("implicitImport") + public void testImplicitImport() throws Exception { + runTest("idea/testData/script/definition/imports/implicitImport/"); + } + } +}