From 1d45dc8d81fde0a32e06d75f344d2efa73aa7f90 Mon Sep 17 00:00:00 2001 From: Natalia Selezneva Date: Thu, 2 Jul 2020 08:21:41 +0300 Subject: [PATCH] Optimize ScriptTemplatesFromDependenciesProvider No needs to convert file.url to VirtualFile because index already has getContainingFiles method Do not iterate through all children of VirtualFile to avoid VFS events initialisation for those files there is no guarantee that file is under source root so is can be not present is vfs at this moment --- .../kotlin/generators/tests/GenerateTests.kt | 4 + .../generators/tests/GenerateTests.kt.as40 | 57 +++++-- .../generators/tests/GenerateTests.kt.as41 | 57 +++++-- ...ractScriptTemplatesFromDependenciesTest.kt | 143 ++++++++++++++++++ ...ScriptTemplatesFromDependenciesTest.kt.192 | 0 ...emplatesFromDependenciesTestGenerated.java | 60 ++++++++ ...atesFromDependenciesTestGenerated.java.192 | 0 .../script/templates/MyTemplate1.classname | 0 .../script/templates/MyTemplate2.classname | 0 .../inJar/module/script.kts | 1 + .../templatesFromDependencies/inJar/test.kts | 8 + .../script/templates/MyTemplate.classname | 0 .../inTests/test.kts | 1 + .../script/templates/MyTemplate1.classname | 0 .../script/templates/MyTemplate2.classname | 0 .../multipleRoots/test.kts | 5 + .../script/templates/MyTemplate1.classname | 0 .../script/templates/MyTemplate2.classname | 0 .../multipleTemplates/test.kts | 5 + .../script/templates/MyTemplate.classname | 0 .../outsideRoots/test.kts | 1 + .../script/templates/MyTemplate.classname | 0 .../singleTemplate/test.kts | 4 + .../script/ScriptTemplatesClassRootsIndex.kt | 23 ++- ...ScriptTemplatesFromDependenciesProvider.kt | 110 +++++++------- 25 files changed, 396 insertions(+), 83 deletions(-) create mode 100644 idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt create mode 100644 idea/scripting-support/test/org/jetbrains/kotlin/idea/script/AbstractScriptTemplatesFromDependenciesTest.kt.192 create mode 100644 idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java create mode 100644 idea/scripting-support/test/org/jetbrains/kotlin/idea/script/ScriptTemplatesFromDependenciesTestGenerated.java.192 create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/META-INF/kotlin/script/templates/MyTemplate1.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inJar/jar/inner/META-INF/kotlin/script/templates/MyTemplate2.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inJar/module/script.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inJar/test.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inTests/module/test/META-INF/kotlin/script/templates/MyTemplate.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/inTests/test.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/module/src/foo/META-INF/kotlin/script/templates/MyTemplate2.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleRoots/test.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate1.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/module/resources/META-INF/kotlin/script/templates/MyTemplate2.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/multipleTemplates/test.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/META-INF/kotlin/script/templates/MyTemplate.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/outsideRoots/test.kts create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/module/resources/META-INF/kotlin/script/templates/MyTemplate.classname create mode 100644 idea/scripting-support/testData/script/templatesFromDependencies/singleTemplate/test.kts 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