Add script files changes monitoring, logic for initialization during index rebuilding
This commit is contained in:
committed by
Pavel V. Talanov
parent
8dac72f465
commit
5bea5bcc37
@@ -69,6 +69,13 @@ class KotlinScriptExternalDependenciesUnion(val dependencies: Iterable<KotlinScr
|
|||||||
override val sources: Iterable<File> get() = dependencies.flatMap { it.sources }
|
override val sources: Iterable<File> get() = dependencies.flatMap { it.sources }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Iterable<File>.isSameClasspathAs(other: Iterable<File>): Boolean {
|
||||||
|
val c1 = map { it.canonicalPath }.toHashSet()
|
||||||
|
val c2 = other.map { it.canonicalPath }.toHashSet()
|
||||||
|
return c1.size == c2.size &&
|
||||||
|
c1.zip(c2).all { it.first == it.second }
|
||||||
|
}
|
||||||
|
|
||||||
data class ScriptParameter(val name: Name, val type: KotlinType)
|
data class ScriptParameter(val name: Name, val type: KotlinType)
|
||||||
|
|
||||||
fun <TF> getFileName(file: TF): String = when (file) {
|
fun <TF> getFileName(file: TF): String = when (file) {
|
||||||
|
|||||||
+31
@@ -49,6 +49,7 @@ class KotlinScriptExternalImportsProvider(val project: Project, private val scri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized for initial caching, additional handling of possible duplicates to save a call to distinct
|
||||||
fun <TF> cacheExternalImports(files: Iterable<TF>): Unit = cacheLock.write {
|
fun <TF> cacheExternalImports(files: Iterable<TF>): Unit = cacheLock.write {
|
||||||
val uncached = hashSetOf<String>()
|
val uncached = hashSetOf<String>()
|
||||||
files.forEach { file ->
|
files.forEach { file ->
|
||||||
@@ -71,6 +72,36 @@ class KotlinScriptExternalImportsProvider(val project: Project, private val scri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized for update, no special duplicates handling
|
||||||
|
fun <TF: Any> updateExternalImportsCache(files: Iterable<TF>): Iterable<TF> = cacheLock.write {
|
||||||
|
files.mapNotNull { file ->
|
||||||
|
val path = getFilePath(file)
|
||||||
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
|
if (scriptDef != null) {
|
||||||
|
val deps = scriptDef.getDependenciesFor(file, project)
|
||||||
|
val oldDeps = cache[path]
|
||||||
|
when {
|
||||||
|
deps != null && (oldDeps == null ||
|
||||||
|
!deps.classpath.isSameClasspathAs(oldDeps.classpath) || !deps.sources.isSameClasspathAs(oldDeps.sources)) -> {
|
||||||
|
// changed or new
|
||||||
|
cache.put(path, deps)
|
||||||
|
cacheOfNulls.remove(path)
|
||||||
|
file
|
||||||
|
}
|
||||||
|
deps != null -> {
|
||||||
|
// same as before
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (cache.remove(path) != null || cacheOfNulls.remove(path)) file // cleared
|
||||||
|
else null // same as before
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else null // not a script
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun invalidateCaches() {
|
fun invalidateCaches() {
|
||||||
cacheLock.write {
|
cacheLock.write {
|
||||||
cache.keys.toList().apply {
|
cache.keys.toList().apply {
|
||||||
|
|||||||
+36
-5
@@ -17,24 +17,33 @@
|
|||||||
package org.jetbrains.kotlin.idea.core.script
|
package org.jetbrains.kotlin.idea.core.script
|
||||||
|
|
||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
|
||||||
|
import com.intellij.openapi.util.EmptyRunnable
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileManager
|
||||||
|
import com.intellij.openapi.vfs.newvfs.BulkFileListener
|
||||||
|
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
|
||||||
import com.intellij.psi.search.FileTypeIndex
|
import com.intellij.psi.search.FileTypeIndex
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.util.indexing.IndexableSetContributor
|
import com.intellij.util.indexing.IndexableSetContributor
|
||||||
import com.intellij.util.io.URLUtil
|
import com.intellij.util.io.URLUtil
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.FileLibraryScope
|
import org.jetbrains.kotlin.idea.caches.resolve.FileLibraryScope
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.script.*
|
import org.jetbrains.kotlin.script.*
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.read
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
class KotlinScriptConfigurationManager(
|
class KotlinScriptConfigurationManager(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
|
private val dumbService: DumbService,
|
||||||
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
||||||
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider?
|
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider?
|
||||||
) {
|
) {
|
||||||
@@ -47,11 +56,28 @@ class KotlinScriptConfigurationManager(
|
|||||||
reloadScriptDefinitions()
|
reloadScriptDefinitions()
|
||||||
// TODO: sort out read/write action business and if possible make it lazy (e.g. move to getAllScriptsClasspath)
|
// TODO: sort out read/write action business and if possible make it lazy (e.g. move to getAllScriptsClasspath)
|
||||||
runReadAction { cacheAllScriptsExtraImports() }
|
runReadAction { cacheAllScriptsExtraImports() }
|
||||||
|
|
||||||
|
project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
|
||||||
|
override fun after(events: List<VFileEvent>) {
|
||||||
|
val isChanged = scriptExternalImportsProvider?.updateExternalImportsCache( events.mapNotNull { it.file })?.any() ?: false
|
||||||
|
if (isChanged) {
|
||||||
|
// TODO: consider more fine-grained update
|
||||||
|
cacheLock.write {
|
||||||
|
allScriptsClasspathCache = null
|
||||||
|
}
|
||||||
|
notifyRootsChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private var allScriptsClasspathCache: List<VirtualFile>? = null
|
private var allScriptsClasspathCache: List<VirtualFile>? = null
|
||||||
private val cacheLock = ReentrantReadWriteLock()
|
private val cacheLock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
private fun notifyRootsChanged() {
|
||||||
|
runWriteAction { ProjectRootManagerEx.getInstanceEx(project)?.makeRootsChange(EmptyRunnable.getInstance(), false, true) }
|
||||||
|
}
|
||||||
|
|
||||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
||||||
scriptExternalImportsProvider
|
scriptExternalImportsProvider
|
||||||
?.getExternalImports(file)
|
?.getExternalImports(file)
|
||||||
@@ -61,12 +87,17 @@ class KotlinScriptConfigurationManager(
|
|||||||
|
|
||||||
fun getAllScriptsClasspath(): List<VirtualFile> = cacheLock.read {
|
fun getAllScriptsClasspath(): List<VirtualFile> = cacheLock.read {
|
||||||
if (allScriptsClasspathCache == null) {
|
if (allScriptsClasspathCache == null) {
|
||||||
allScriptsClasspathCache =
|
dumbService.runWhenSmart {
|
||||||
(scriptExternalImportsProvider?.getKnownCombinedClasspath() ?: emptyList())
|
cacheLock.write {
|
||||||
.distinct()
|
allScriptsClasspathCache =
|
||||||
.mapNotNull { it.classpathEntryToVfs() }
|
(scriptExternalImportsProvider?.getKnownCombinedClasspath() ?: emptyList())
|
||||||
|
.distinct()
|
||||||
|
.mapNotNull { it.classpathEntryToVfs() }
|
||||||
|
}
|
||||||
|
notifyRootsChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return allScriptsClasspathCache!!
|
return allScriptsClasspathCache ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun File.classpathEntryToVfs(): VirtualFile =
|
private fun File.classpathEntryToVfs(): VirtualFile =
|
||||||
|
|||||||
Reference in New Issue
Block a user