diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index dc5bf4b7523..a4286e49d75 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -1041,6 +1041,10 @@ fun main(args: Array) { testClass { model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS) } + + testClass { + model("script/templatesFromDependencies", extension = null, recursive = false) + } } testGroup("idea/idea-maven/test", "idea/idea-maven/testData") { diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40 index 8318d94daa5..3bfc7007e20 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40 +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40 @@ -119,10 +119,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.AbstractSlicerNullnessGroupingTest import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest @@ -841,20 +838,60 @@ fun main(args: Array) { testGroup("idea/scripting-support/test", "idea/scripting-support/testData") { testClass { - model("scratch", extension = "kts", testMethod = "doScratchCompilingTest", testClassName = "ScratchCompiling", recursive = false) + model( + "scratch", + extension = "kts", + testMethod = "doScratchCompilingTest", + testClassName = "ScratchCompiling", + recursive = false + ) model("scratch", extension = "kts", testMethod = "doScratchReplTest", testClassName = "ScratchRepl", recursive = false) - model("scratch/multiFile", extension = null, testMethod = "doScratchMultiFileTest", testClassName = "ScratchMultiFile", recursive = false) + model( + "scratch/multiFile", + extension = null, + testMethod = "doScratchMultiFileTest", + testClassName = "ScratchMultiFile", + recursive = false + ) - model("worksheet", extension = "ws.kts", testMethod = "doWorksheetCompilingTest", testClassName = "WorksheetCompiling", recursive = false) - model("worksheet", extension = "ws.kts", testMethod = "doWorksheetReplTest", testClassName = "WorksheetRepl", recursive = false) - model("worksheet/multiFile", extension = null, testMethod = "doWorksheetMultiFileTest", testClassName = "WorksheetMultiFile", recursive = false) + model( + "worksheet", + extension = "ws.kts", + testMethod = "doWorksheetCompilingTest", + testClassName = "WorksheetCompiling", + recursive = false + ) + model( + "worksheet", + extension = "ws.kts", + testMethod = "doWorksheetReplTest", + testClassName = "WorksheetRepl", + recursive = false + ) + model( + "worksheet/multiFile", + extension = null, + testMethod = "doWorksheetMultiFileTest", + testClassName = "WorksheetMultiFile", + recursive = false + ) - model("scratch/rightPanelOutput", extension = "kts", testMethod = "doRightPreviewPanelOutputTest", testClassName = "ScratchRightPanelOutput", recursive = false) + model( + "scratch/rightPanelOutput", + extension = "kts", + testMethod = "doRightPreviewPanelOutputTest", + testClassName = "ScratchRightPanelOutput", + recursive = false + ) } testClass { model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS) } + + testClass { + model("script/templatesFromDependencies", extension = null, recursive = false) + } } /* diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 index 8318d94daa5..3bfc7007e20 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 @@ -119,10 +119,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.AbstractSlicerNullnessGroupingTest import org.jetbrains.kotlin.idea.slicer.AbstractSlicerTreeTest @@ -841,20 +838,60 @@ fun main(args: Array) { testGroup("idea/scripting-support/test", "idea/scripting-support/testData") { testClass { - model("scratch", extension = "kts", testMethod = "doScratchCompilingTest", testClassName = "ScratchCompiling", recursive = false) + model( + "scratch", + extension = "kts", + testMethod = "doScratchCompilingTest", + testClassName = "ScratchCompiling", + recursive = false + ) model("scratch", extension = "kts", testMethod = "doScratchReplTest", testClassName = "ScratchRepl", recursive = false) - model("scratch/multiFile", extension = null, testMethod = "doScratchMultiFileTest", testClassName = "ScratchMultiFile", recursive = false) + model( + "scratch/multiFile", + extension = null, + testMethod = "doScratchMultiFileTest", + testClassName = "ScratchMultiFile", + recursive = false + ) - model("worksheet", extension = "ws.kts", testMethod = "doWorksheetCompilingTest", testClassName = "WorksheetCompiling", recursive = false) - model("worksheet", extension = "ws.kts", testMethod = "doWorksheetReplTest", testClassName = "WorksheetRepl", recursive = false) - model("worksheet/multiFile", extension = null, testMethod = "doWorksheetMultiFileTest", testClassName = "WorksheetMultiFile", recursive = false) + model( + "worksheet", + extension = "ws.kts", + testMethod = "doWorksheetCompilingTest", + testClassName = "WorksheetCompiling", + recursive = false + ) + model( + "worksheet", + extension = "ws.kts", + testMethod = "doWorksheetReplTest", + testClassName = "WorksheetRepl", + recursive = false + ) + model( + "worksheet/multiFile", + extension = null, + testMethod = "doWorksheetMultiFileTest", + testClassName = "WorksheetMultiFile", + recursive = false + ) - model("scratch/rightPanelOutput", extension = "kts", testMethod = "doRightPreviewPanelOutputTest", testClassName = "ScratchRightPanelOutput", recursive = false) + model( + "scratch/rightPanelOutput", + extension = "kts", + testMethod = "doRightPreviewPanelOutputTest", + testClassName = "ScratchRightPanelOutput", + recursive = false + ) } testClass { model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS) } + + testClass { + model("script/templatesFromDependencies", extension = null, recursive = false) + } } /* diff --git a/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt new file mode 100644 index 00000000000..ba54c186db5 --- /dev/null +++ b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt @@ -0,0 +1,143 @@ +/* + * 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.ide.projectView.actions.MarkRootActionBase +import com.intellij.openapi.module.JavaModuleType +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.guessProjectDir +import com.intellij.openapi.roots.* +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.HeavyPlatformTestCase +import com.intellij.testFramework.PsiTestUtil +import com.intellij.util.indexing.FileBasedIndex +import org.jetbrains.jps.model.java.JavaResourceRootType +import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.JUnit3RunnerWithInnersForJPS +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.MockLibraryUtil +import org.jetbrains.kotlin.test.util.addDependency +import org.jetbrains.kotlin.test.util.jarRoot +import org.jetbrains.kotlin.test.util.projectLibrary +import org.junit.runner.RunWith +import java.io.File + +@RunWith(JUnit3RunnerWithInnersForJPS::class) +abstract class AbstractScriptTemplatesFromDependenciesTest : HeavyPlatformTestCase() { + + companion object { + private const val testFileName = "test.kts" + } + + fun doTest(path: String) { + val projectRoot = project.guessProjectDir() ?: return + val testDataDir = File(path) + + copyDirContentsTo( + LocalFileSystem.getInstance().findFileByIoFile(testDataDir)!!, + projectRoot + ) + + testDataDir + .listFiles { f -> f.name.startsWith("module") } + ?.filter { it.exists() } + ?.forEach { + createTestModule(it) + } + + ModuleManager.getInstance(project).modules.forEach { module -> + testDataDir + .listFiles { f -> f.name.startsWith("jar") } + ?.map { folder -> + ModuleRootManager.getInstance(module).modifiableModel.apply { + val vFile = VfsUtil.findFileByIoFile(folder, true)!! + MarkRootActionBase.findContentEntry(this, vFile)?.addExcludeFolder(vFile) + runWriteAction { this@apply.commit() } + } + + packJar(folder) + } + ?.forEach { jar -> + module.addDependency( + projectLibrary( + libraryName = "script-library", + classesRoot = jar.jarRoot + ) + ) + } + } + + val roots: Collection = FileBasedIndex.getInstance().getContainingFiles( + ScriptTemplatesClassRootsIndex.KEY, + ScriptTemplatesClassRootsIndex.VALUE, + GlobalSearchScope.allScope(project) + ) + + val testFile = File(path, testFileName) + val fileText = testFile.readText() + + checkRoots(fileText, roots) + + val provider = ScriptDefinitionContributor.find(project) + ?: error("Cannot find ScriptTemplatesFromDependenciesProvider") + + val (templates, classpath) = provider.getTemplateClassPath(roots.toList()) + + checkTemplateNames(fileText, templates) + checkTemplateClasspath(fileText, classpath) + } + + private fun String.removeTestDirPrefix(): String = this.substringAfterLast(getTestName(true)) + + private fun checkRoots(fileText: String, roots: Collection) { + val actual = roots.map { it.path.removeTestDirPrefix() } + val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// ROOT:") + + assertOrderedEquals("Roots are different", actual.sorted(), expected.sorted()) + } + + private fun checkTemplateNames(fileText: String, names: Collection) { + val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NAME:") + + assertOrderedEquals("Template names are different", names.sorted(), expected.sorted()) + } + + private fun checkTemplateClasspath(fileText: String, classpath: Collection) { + val actual = classpath.map { + FileUtil.toSystemIndependentName(it.path).removeTestDirPrefix() + } + + val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// CLASSPATH:") + + assertOrderedEquals("Roots are different", actual.sorted(), expected.sorted()) + } + + private fun createTestModule(dir: File): Module { + val newModule = createModuleAt(name, project, JavaModuleType.getModuleType(), dir.path) + + dir.listFiles()?.forEach { + val root = VfsUtil.findFileByIoFile(it, true) ?: return@forEach + when (it.name) { + "src" -> PsiTestUtil.addSourceRoot(newModule, root) + "test" -> PsiTestUtil.addSourceRoot(newModule, root, true) + "resources" -> PsiTestUtil.addSourceRoot(newModule, root, JavaResourceRootType.RESOURCE) + } + } + return newModule + } + + private fun packJar(dir: File): File { + val contentDir = KotlinTestUtils.tmpDirForReusableFolder("folderForLibrary-${getTestName(true)}") + return MockLibraryUtil.createJarFile(contentDir, dir, "templates") + } +} \ No newline at end of file diff --git a/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt.192 b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt.192 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java new file mode 100644 index 00000000000..105cc6349ca --- /dev/null +++ b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java @@ -0,0 +1,60 @@ +/* + * 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") +@TestMetadata("idea/scripting-support/testData/script/templatesFromDependencies") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class ScriptTemplatesFromDependenciesTestGenerated extends AbstractScriptTemplatesFromDependenciesTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInTemplatesFromDependencies() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/scripting-support/testData/script/templatesFromDependencies"), Pattern.compile("^([^\\.]+)$"), null, false); + } + + @TestMetadata("inJar") + public void testInJar() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/inJar/"); + } + + @TestMetadata("inTests") + public void testInTests() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/inTests/"); + } + + @TestMetadata("multipleRoots") + public void testMultipleRoots() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/"); + } + + @TestMetadata("multipleTemplates") + public void testMultipleTemplates() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/"); + } + + @TestMetadata("outsideRoots") + public void testOutsideRoots() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/"); + } + + @TestMetadata("singleTemplate") + public void testSingleTemplate() throws Exception { + runTest("idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/"); + } +} diff --git a/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java.192 b/idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java.192 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/META-INF/kotlin/script/templates/MyTemplate1.classname b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/META-INF/kotlin/script/templates/MyTemplate1.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/inner/META-INF/kotlin/script/templates/MyTemplate2.classname b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/inner/META-INF/kotlin/script/templates/MyTemplate2.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inJar/module/script.kts b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/module/script.kts new file mode 100644 index 00000000000..a157c397372 --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/module/script.kts @@ -0,0 +1 @@ +// needed for module creation \ No newline at end of file diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inJar/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/test.kts new file mode 100644 index 00000000000..5f20ee9e709 --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/inJar/test.kts @@ -0,0 +1,8 @@ +// ROOT: /templates.jar!/META-INF/kotlin/script/templates/MyTemplate1.classname +// ROOT: /templates.jar!/inner/META-INF/kotlin/script/templates/MyTemplate2.classname + +// NAME: MyTemplate1 +// CLASSPATH: /templates.jar + + + diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inTests/module/test/META-INF/kotlin/script/templates/MyTemplate.classname b/idea/scripting-support/testData/script/templatesFromDependencies/inTests/module/test/META-INF/kotlin/script/templates/MyTemplate.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/inTests/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/inTests/test.kts new file mode 100644 index 00000000000..943c4151626 --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/inTests/test.kts @@ -0,0 +1 @@ +// ROOT: /module/test/META-INF/kotlin/script/templates/MyTemplate.classname diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname b/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/src/foo/META-INF/kotlin/script/templates/MyTemplate2.classname b/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/src/foo/META-INF/kotlin/script/templates/MyTemplate2.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/test.kts new file mode 100644 index 00000000000..12e8efe5c4a --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/test.kts @@ -0,0 +1,5 @@ +// ROOT: /module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname +// ROOT: /module/src/foo/META-INF/kotlin/script/templates/MyTemplate2.classname + + +// NAME: MyTemplate1 \ No newline at end of file diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname b/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate2.classname b/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate2.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/test.kts new file mode 100644 index 00000000000..4370f9c6a3c --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/test.kts @@ -0,0 +1,5 @@ +// ROOT: /module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname +// ROOT: /module/resources/META-INF/kotlin/script/templates/MyTemplate2.classname + +// NAME: MyTemplate1 +// NAME: MyTemplate2 \ No newline at end of file diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/META-INF/kotlin/script/templates/MyTemplate.classname b/idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/META-INF/kotlin/script/templates/MyTemplate.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/test.kts new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/test.kts @@ -0,0 +1 @@ + diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/module/resources/META-INF/kotlin/script/templates/MyTemplate.classname b/idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/module/resources/META-INF/kotlin/script/templates/MyTemplate.classname new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/test.kts b/idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/test.kts new file mode 100644 index 00000000000..00dae0d2e27 --- /dev/null +++ b/idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/test.kts @@ -0,0 +1,4 @@ +// ROOT: /module/resources/META-INF/kotlin/script/templates/MyTemplate.classname + +// NAME: MyTemplate + diff --git a/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesClassRootsIndex.kt b/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesClassRootsIndex.kt index 67b84a664f9..7fee45ce9a6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesClassRootsIndex.kt +++ b/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesClassRootsIndex.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.indexing.* import com.intellij.util.io.IOUtil import com.intellij.util.io.KeyDescriptor +import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_PATH import java.io.DataInput import java.io.DataOutput @@ -21,6 +22,7 @@ class ScriptTemplatesClassRootsIndex : companion object { val KEY = ID.create(ScriptTemplatesClassRootsIndex::class.java.canonicalName) + const val VALUE = "MY_VALUE" private val suffix = SCRIPT_DEFINITION_MARKERS_PATH.removeSuffix("/") } @@ -37,29 +39,26 @@ class ScriptTemplatesClassRootsIndex : override fun getVersion(): Int = 1 - override fun indexDirectories(): Boolean = true + override fun indexDirectories(): Boolean = false override fun acceptInput(file: VirtualFile): Boolean { - return file.isDirectory && file.path.endsWith(suffix) + val parent = file.parent ?: return false + return parent.isDirectory + && parent.path.endsWith(suffix) + && file.path.endsWith(SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT) } - override fun save(out: DataOutput, value: String) { - IOUtil.writeUTF(out, value) - } + override fun save(out: DataOutput, value: String) = IOUtil.writeUTF(out, value) - override fun read(input: DataInput): String? { - return IOUtil.readUTF(input) - } + override fun read(input: DataInput): String? = IOUtil.readUTF(input) - override fun getHashCode(value: String): Int { - return value.hashCode() - } + override fun getHashCode(value: String): Int = value.hashCode() override fun isEqual(val1: String?, val2: String?): Boolean { return val1 == val2 } override fun map(inputData: FileContent): Map { - return Collections.singletonMap(inputData.file.url, null) + return Collections.singletonMap(VALUE, null) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesProvider.kt b/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesProvider.kt index 897a643f15f..e8936f1b06d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesProvider.kt @@ -12,17 +12,16 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.roots.* import com.intellij.openapi.vfs.* +import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.concurrency.AppExecutorUtil import com.intellij.util.indexing.FileBasedIndex import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionSourceAsContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT -import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_PATH import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.getEnvironment import java.io.File -import java.net.URL import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock import kotlin.script.experimental.host.ScriptingHostConfiguration @@ -104,61 +103,21 @@ class ScriptTemplatesFromDependenciesProvider(private val project: Project) : Sc logger.debug("async script definitions update started") } - val templates = LinkedHashSet() - val classpath = LinkedHashSet() - ReadAction .nonBlocking> { - val fileManager = VirtualFileManager.getInstance() - FileBasedIndex.getInstance().getAllKeys(ScriptTemplatesClassRootsIndex.KEY, project).mapNotNull { - val vFile = fileManager.findFileByUrl(it) - - // see SCRIPT_DEFINITION_MARKERS_PATH - vFile?.parent?.parent?.parent?.parent - } + FileBasedIndex.getInstance().getContainingFiles( + ScriptTemplatesClassRootsIndex.KEY, + ScriptTemplatesClassRootsIndex.VALUE, + GlobalSearchScope.allScope(project) + ).filterNotNull() } .inSmartMode(project) .expireWith(project) .submit(AppExecutorUtil.getAppExecutorService()) - .onSuccess { roots -> - roots.forEach { root -> - if (logger.isDebugEnabled) { - logger.debug("root matching SCRIPT_DEFINITION_MARKERS_PATH found: ${root.path}") - } + .onSuccess { files -> + val (templates, classpath) = getTemplateClassPath(files) - val orderEntriesForFile = ProjectFileIndex.getInstance(project).getOrderEntriesForFile(root) - .filter { - if (it is ModuleSourceOrderEntry) { - !ModuleRootManager.getInstance(it.ownerModule).fileIndex.isInTestSourceContent(root) - } else { - it is LibraryOrSdkOrderEntry - } - } - .takeIf { it.isNotEmpty() } ?: return@forEach - - root.findFileByRelativePath(SCRIPT_DEFINITION_MARKERS_PATH)?.children?.forEach { resourceFile -> - if (resourceFile.isValid && !resourceFile.isDirectory) { - templates.add(resourceFile.name.removeSuffix(SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT)) - } - } - - - // assuming that all libraries are placed into classes roots - // TODO: extract exact library dependencies instead of putting all module dependencies into classpath - // minimizing the classpath needed to use the template by taking cp only from modules with new templates found - // on the other hand the approach may fail if some module contains a template without proper classpath, while - // the other has properly configured classpath, so assuming that the dependencies are set correctly everywhere - orderEntriesForFile.forEach { - classpath.addAll( - OrderEnumerator.orderEntries(it.ownerModule).withoutSdk().classesRoots.mapNotNull { - it.canonicalPath?.removeSuffix("!/").let(::File) - } - ) - } - } - } - .onProcessed { - if (templates.isEmpty()) return@onProcessed onEarlyEnd() + if (templates.isEmpty()) return@onSuccess onEarlyEnd() val newTemplates = TemplatesWithCp(templates.toList(), classpath.toList()) if (newTemplates == oldTemplates) { @@ -166,7 +125,7 @@ class ScriptTemplatesFromDependenciesProvider(private val project: Project) : Sc inProgress = false } - return@onProcessed + return@onSuccess } if (logger.isDebugEnabled) { @@ -219,4 +178,53 @@ class ScriptTemplatesFromDependenciesProvider(private val project: Project) : Sc inProgress = false } } + + // public for tests + fun getTemplateClassPath(files: List): Pair, Collection> { + val rootDirToTemplates: MutableMap> = hashMapOf() + for (file in files) { + val dir = file.parent?.parent?.parent?.parent?.parent ?: continue + rootDirToTemplates.getOrPut(dir) { arrayListOf() }.add(file) + } + + val templates = LinkedHashSet() + val classpath = LinkedHashSet() + + rootDirToTemplates.forEach { (root, templateFiles) -> + if (logger.isDebugEnabled) { + logger.debug("root matching SCRIPT_DEFINITION_MARKERS_PATH found: ${root.path}") + } + + val orderEntriesForFile = ProjectFileIndex.getInstance(project).getOrderEntriesForFile(root) + .filter { + if (it is ModuleSourceOrderEntry) { + if (ModuleRootManager.getInstance(it.ownerModule).fileIndex.isInTestSourceContent(root)) { + return@filter false + } + + it.getFiles(OrderRootType.SOURCES).contains(root) + } else { + it is LibraryOrSdkOrderEntry && it.getFiles(OrderRootType.CLASSES).contains(root) + } + } + .takeIf { it.isNotEmpty() } ?: return@forEach + + templateFiles.forEach { + templates.add(it.name.removeSuffix(SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT)) + } + + // assuming that all libraries are placed into classes roots + // TODO: extract exact library dependencies instead of putting all module dependencies into classpath + // minimizing the classpath needed to use the template by taking cp only from modules with new templates found + // on the other hand the approach may fail if some module contains a template without proper classpath, while + // the other has properly configured classpath, so assuming that the dependencies are set correctly everywhere + orderEntriesForFile.flatMap { + OrderEnumerator.orderEntries(it.ownerModule).withoutSdk().classesRoots.mapNotNull { + classpath.add(it.canonicalPath?.removeSuffix("!/").let(::File)) + } + } + } + + return templates to classpath + } } \ No newline at end of file