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
This commit is contained in:
Natalia Selezneva
2020-07-02 08:21:41 +03:00
parent 45600830d3
commit 1d45dc8d81
25 changed files with 396 additions and 83 deletions
@@ -1041,6 +1041,10 @@ fun main(args: Array<String>) {
testClass<AbstractScratchLineMarkersTest> {
model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS)
}
testClass<AbstractScriptTemplatesFromDependenciesTest> {
model("script/templatesFromDependencies", extension = null, recursive = false)
}
}
testGroup("idea/idea-maven/test", "idea/idea-maven/testData") {
@@ -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<String>) {
testGroup("idea/scripting-support/test", "idea/scripting-support/testData") {
testClass<AbstractScratchRunActionTest> {
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<AbstractScratchLineMarkersTest> {
model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS)
}
testClass<AbstractScriptTemplatesFromDependenciesTest> {
model("script/templatesFromDependencies", extension = null, recursive = false)
}
}
/*
@@ -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<String>) {
testGroup("idea/scripting-support/test", "idea/scripting-support/testData") {
testClass<AbstractScratchRunActionTest> {
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<AbstractScratchLineMarkersTest> {
model("scratch/lineMarker", testMethod = "doScratchTest", pattern = KT_OR_KTS)
}
testClass<AbstractScriptTemplatesFromDependenciesTest> {
model("script/templatesFromDependencies", extension = null, recursive = false)
}
}
/*
@@ -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<VirtualFile> = 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<ScriptTemplatesFromDependenciesProvider>(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<VirtualFile>) {
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<String>) {
val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NAME:")
assertOrderedEquals("Template names are different", names.sorted(), expected.sorted())
}
private fun checkTemplateClasspath(fileText: String, classpath: Collection<File>) {
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")
}
}
@@ -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/");
}
}
@@ -0,0 +1 @@
// needed for module creation
@@ -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
@@ -0,0 +1 @@
// ROOT: /module/test/META-INF/kotlin/script/templates/MyTemplate.classname
@@ -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
@@ -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
@@ -0,0 +1,4 @@
// ROOT: /module/resources/META-INF/kotlin/script/templates/MyTemplate.classname
// NAME: MyTemplate
@@ -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<String, Void>(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<String?, Void?> {
return Collections.singletonMap(inputData.file.url, null)
return Collections.singletonMap(VALUE, null)
}
}
@@ -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<String>()
val classpath = LinkedHashSet<File>()
ReadAction
.nonBlocking<List<VirtualFile>> {
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<VirtualFile>): Pair<Collection<String>, Collection<File>> {
val rootDirToTemplates: MutableMap<VirtualFile, MutableList<VirtualFile>> = hashMapOf()
for (file in files) {
val dir = file.parent?.parent?.parent?.parent?.parent ?: continue
rootDirToTemplates.getOrPut(dir) { arrayListOf() }.add(file)
}
val templates = LinkedHashSet<String>()
val classpath = LinkedHashSet<File>()
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
}
}