diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/importing/KotlinDslScriptModelProcessor.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/importing/KotlinDslScriptModelProcessor.kt index 68941e38974..6a5a81365e5 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/importing/KotlinDslScriptModelProcessor.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/importing/KotlinDslScriptModelProcessor.kt @@ -101,6 +101,7 @@ private fun KotlinDslScriptsModel.toListOfScriptModels(project: Project): List() val models = mutableListOf() diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRoot.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRoot.kt index 4870394668a..0e1ee71a9a5 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRoot.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRoot.kt @@ -25,7 +25,9 @@ import java.io.File * * See [GradleBuildRootsManager] for more details. */ -sealed class GradleBuildRoot(private val lastModifiedFiles: LastModifiedFiles) { +sealed class GradleBuildRoot( + private val lastModifiedFiles: LastModifiedFiles +) { @Volatile var importing = false diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootData.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootData.kt index 2075893163b..987f83a6355 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootData.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootData.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel data class GradleBuildRootData( + val importTs: Long, val projectRoots: Collection, val templateClasspath: Collection, val models: Collection diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootDataSerializer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootDataSerializer.kt index 133826f3f34..7efa0432788 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootDataSerializer.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootDataSerializer.kt @@ -19,7 +19,7 @@ import java.io.DataInputStream import java.io.DataOutput internal object GradleBuildRootDataSerializer { - private val attribute = FileAttribute("kotlin-dsl-script-models", 5, false) + private val attribute = FileAttribute("kotlin-dsl-script-models", 6, false) fun read(buildRoot: VirtualFile): GradleBuildRootData? { return attribute.readAttribute(buildRoot)?.use { @@ -54,6 +54,7 @@ internal fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRoo strings.addStrings(it.imports) } strings.writeHeader() + output.writeLong(data.importTs) strings.writeStringIds(data.projectRoots) strings.writeStringIds(data.templateClasspath) output.writeList(data.models) { @@ -69,9 +70,9 @@ internal fun writeKotlinDslScriptModels(output: DataOutput, data: GradleBuildRoo internal fun readKotlinDslScriptModels(input: DataInputStream, buildRoot: String): GradleBuildRootData { val strings = StringsPool.reader(input) + val importTs = input.readLong() val projectRoots = strings.readStrings() val templateClasspath = strings.readStrings() - val models = input.readList { KotlinDslScriptModel( strings.readString(), @@ -83,7 +84,7 @@ internal fun readKotlinDslScriptModels(input: DataInputStream, buildRoot: String ) } - return GradleBuildRootData(projectRoots, templateClasspath, models) + return GradleBuildRootData(importTs, projectRoots, templateClasspath, models) } private object StringsPool { diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocator.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocator.kt index 22c2eebfb99..fdf9038f50b 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocator.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocator.kt @@ -61,11 +61,18 @@ abstract class GradleBuildRootsLocator { standalone } - class ScriptUnderRoot( + /** + * Timestamp of an moment when script file was discovered (indexed). + * Used to detect if that script was existed at the moment of import + */ + abstract fun getScriptFirstSeenTs(path: String): Long + + inner class ScriptUnderRoot( + val filePath: String, val root: GradleBuildRoot?, val script: GradleScriptInfo? = null, val standalone: Boolean = false, - val nearest: GradleBuildRoot? = null + val nearest: GradleBuildRoot? = root ) { val notificationKind: NotificationKind get() = when { @@ -76,13 +83,20 @@ abstract class GradleBuildRootsLocator { else -> when (nearest) { is Legacy -> NotificationKind.dontCare is New -> NotificationKind.wasNotImportedAfterCreation - is Imported -> NotificationKind.notEvaluatedInLastImport // todo: wasNotImportedAfterCreation + is Imported -> when { + wasImportedAndNotEvaluated -> NotificationKind.notEvaluatedInLastImport + else -> NotificationKind.wasNotImportedAfterCreation + } } } private val isImported: Boolean get() = script != null + private val wasImportedAndNotEvaluated: Boolean + get() = nearest is Imported && + getScriptFirstSeenTs(filePath) < nearest.data.importTs + override fun toString(): String { return "ScriptUnderRoot(root=$root, script=$script, standalone=$standalone, nearest=$nearest)" } @@ -96,7 +110,7 @@ abstract class GradleBuildRootsLocator { val scriptInfo = getScriptInfo(filePath) val imported = scriptInfo?.buildRoot - if (imported != null) return ScriptUnderRoot(imported, scriptInfo) + if (imported != null) return ScriptUnderRoot(filePath, imported, scriptInfo) if (filePath.endsWith("/build.gradle.kts") || filePath.endsWith("/settings.gradle.kts") || @@ -104,7 +118,7 @@ abstract class GradleBuildRootsLocator { ) { // build|settings|init.gradle.kts scripts should be located near gradle project root only val gradleBuild = roots.getBuildByProjectDir(filePath.substringBeforeLast("/")) - if (gradleBuild != null) return ScriptUnderRoot(gradleBuild) + if (gradleBuild != null) return ScriptUnderRoot(filePath, gradleBuild) } // other scripts: "included", "precompiled" scripts, scripts in unlinked projects, @@ -112,17 +126,17 @@ abstract class GradleBuildRootsLocator { val standaloneScriptRoot = roots.getStandaloneScriptRoot(filePath) if (standaloneScriptRoot != null) { - return ScriptUnderRoot(standaloneScriptRoot, standalone = true) + return ScriptUnderRoot(filePath, standaloneScriptRoot, standalone = true) } if (searchNearestLegacy) { val nearest = roots.findNearestRoot(filePath) return when (nearest) { - is Legacy -> ScriptUnderRoot(nearest) - else -> ScriptUnderRoot(null, nearest = nearest) + is Legacy -> ScriptUnderRoot(filePath, nearest) + else -> ScriptUnderRoot(filePath, null, nearest = nearest) } } else { - return ScriptUnderRoot(null) + return ScriptUnderRoot(filePath, null) } } } \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt index 7f3e353f350..65070a43d9c 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsManager.kt @@ -107,6 +107,8 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), override fun getScriptInfo(localPath: String): GradleScriptInfo? = manager.getLightScriptInfo(localPath) as? GradleScriptInfo + override fun getScriptFirstSeenTs(path: String): Long = 0 + fun fileChanged(filePath: String, ts: Long = System.currentTimeMillis()) { findAffectedFileRoot(filePath)?.fileChanged(filePath, ts) scheduleLastModifiedFilesSave() @@ -117,31 +119,34 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), updateNotifications { it.startsWith(workingDir) } } - fun update(build: KotlinDslGradleBuildSync) { + fun update(sync: KotlinDslGradleBuildSync) { // fast path for linked gradle builds without .gradle.kts support - if (build.models.isEmpty()) { - val root = getBuildRootByWorkingDir(build.workingDir) ?: return + if (sync.models.isEmpty()) { + val root = getBuildRootByWorkingDir(sync.workingDir) ?: return if (root is Imported && root.data.models.isEmpty()) return } try { - val root = actualizeBuildRoot(build.workingDir) ?: return - root.importing = false + val oldRoot = actualizeBuildRoot(sync.workingDir) ?: return + oldRoot.importing = false - if (root is Legacy) return + if (oldRoot is Legacy) return val templateClasspath = GradleScriptDefinitionsContributor.getDefinitionsTemplateClasspath(project) - val newData = GradleBuildRootData(build.projectRoots, templateClasspath, build.models) - val mergedData = if (build.failed && root is Imported) merge(root.data, newData) else newData + val newData = GradleBuildRootData(sync.ts, sync.projectRoots, templateClasspath, sync.models) + val mergedData = if (sync.failed && oldRoot is Imported) merge(oldRoot.data, newData) else newData val lastModifiedFilesReset = LastModifiedFiles() - val newSupport = tryCreateImportedRoot(build.workingDir, lastModifiedFilesReset) { mergedData } ?: return - GradleBuildRootDataSerializer.write(newSupport.dir ?: return, mergedData) - newSupport.saveLastModifiedFiles() + val newRoot = tryCreateImportedRoot( + sync.workingDir, + lastModifiedFilesReset + ) { mergedData } ?: return + GradleBuildRootDataSerializer.write(newRoot.dir ?: return, mergedData) + newRoot.saveLastModifiedFiles() - add(newSupport) + add(newRoot) } finally { - updateNotifications { it.startsWith(build.workingDir) } + updateNotifications { it.startsWith(sync.workingDir) } } } @@ -152,7 +157,7 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), val models = old.models.associateByTo(mutableMapOf()) { it.file } new.models.associateByTo(models) { it.file } - return GradleBuildRootData(roots, new.templateClasspath, models.values) + return GradleBuildRootData(new.importTs, roots, new.templateClasspath, models.values) } private val lastModifiedFilesSaveScheduled = AtomicBoolean() diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleBuildRootDataSerializerTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleBuildRootDataSerializerTest.kt index 1e97eed7db5..53bd554c4fd 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleBuildRootDataSerializerTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleBuildRootDataSerializerTest.kt @@ -20,6 +20,7 @@ class GradleBuildRootDataSerializerTest { @Test fun write() { val data = GradleBuildRootData( + 123, listOf("a", "b", "c"), listOf("a"), listOf( diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/AbstractGradleBuildRootsLocatorTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/AbstractGradleBuildRootsLocatorTest.kt index cd3f5e2d984..736ccccf38c 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/AbstractGradleBuildRootsLocatorTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/AbstractGradleBuildRootsLocatorTest.kt @@ -11,11 +11,14 @@ import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel import kotlin.test.assertEquals open class AbstractGradleBuildRootsLocatorTest { - private val scripts = mutableMapOf() + private val scripts = mutableMapOf() private val locator = MyRootsLocator() + class ScriptFixture(val introductionTs: Long, val info: GradleScriptInfo) + private inner class MyRootsLocator : GradleBuildRootsLocator() { - override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath] + override fun getScriptFirstSeenTs(path: String): Long = scripts[path]?.introductionTs ?: 0 + override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath]?.info fun accessRoots() = roots } @@ -26,13 +29,15 @@ open class AbstractGradleBuildRootsLocatorTest { fun newImportedGradleProject( dir: String, relativeProjectRoots: List = listOf(""), - relativeScripts: List = listOf("build.gradle.kts") + relativeScripts: List = listOf("build.gradle.kts"), + ts: Long = 0 ) { val pathPrefix = "$dir/" val root = Imported( dir, null, GradleBuildRootData( + ts, relativeProjectRoots.map { (pathPrefix + it).removeSuffix("/") }, listOf(), listOf() @@ -44,16 +49,19 @@ open class AbstractGradleBuildRootsLocatorTest { relativeScripts.forEach { val path = pathPrefix + it - scripts[path] = GradleScriptInfo( - root, - null, - KotlinDslScriptModel( - file = path, - inputs = GradleKotlinScriptConfigurationInputs("", 0, dir), - classPath = listOf(), - sourcePath = listOf(), - imports = listOf(), - messages = listOf() + scripts[path] = ScriptFixture( + 0, + GradleScriptInfo( + root, + null, + KotlinDslScriptModel( + file = path, + inputs = GradleKotlinScriptConfigurationInputs("", 0, dir), + classPath = listOf(), + sourcePath = listOf(), + imports = listOf(), + messages = listOf() + ) ) ) }