diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt
index c04f3a302b1..d5b50da34ca 100644
--- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleKotlinScriptConfigurationInputs.kt
@@ -10,14 +10,14 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsManager
-import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRoot
+import org.jetbrains.kotlin.idea.scripting.gradle.roots.*
import org.jetbrains.kotlin.psi.KtFile
/**
* Up to date of gradle script depends on following factors:
* 1. It is out of date when essential [sections] are changed. See [getGradleScriptInputsStamp].
* 2. When some related file is changed (other gradle script, gradle.properties file)
- * See [GradleBuildRoot.Linked.areRelatedFilesChangedBefore].
+ * See [GradleBuildRoot.areRelatedFilesChangedBefore].
*
* [lastModifiedTs] is needed to check if some related file was changed since last update
*/
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/MissingGradleScriptConfigurationNotificationProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/MissingGradleScriptConfigurationNotificationProvider.kt
index 30d693c4669..aad9dafd340 100644
--- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/MissingGradleScriptConfigurationNotificationProvider.kt
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/MissingGradleScriptConfigurationNotificationProvider.kt
@@ -14,6 +14,7 @@ import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotifications
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
+import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsLocator.NotificationKind.*
import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsManager
class MissingGradleScriptConfigurationNotificationProvider(private val project: Project) :
@@ -24,32 +25,63 @@ class MissingGradleScriptConfigurationNotificationProvider(private val project:
if (!isGradleKotlinScript(file)) return null
if (file.fileType != KotlinFileType.INSTANCE) return null
- val scriptUnderRoot = GradleBuildRootsManager.getInstance(project).findScriptBuildRoot(file) ?: return null
- return when {
- scriptUnderRoot.isUnrelatedScript -> EditorNotificationPanel().apply {
- text(KotlinIdeaGradleBundle.message("text.the.associated.gradle.project.isn.t.imported"))
-
- createActionLabel(KotlinIdeaGradleBundle.message("action.text.standalone")) {
- GradleBuildRootsManager.getInstance(project).addStandaloneScript(file)
+ val rootsManager = GradleBuildRootsManager.getInstance(project)
+ val scriptUnderRoot = rootsManager.findScriptBuildRoot(file) ?: return null
+ return when (scriptUnderRoot.notificationKind) {
+ dontCare -> null
+ outsideAnyting -> EditorNotificationPanel().apply {
+ text("Code insight unavailable (related Gradle project not linked)")
+ // todo: Code insight unavailable (cannot find related Gradle project)
+ createActionLabel("Link related Gradle project") {
+ runPartialGradleImport(project)
}
-
- val helpIcon = createActionLabel("") {}
- helpIcon.setIcon(AllIcons.General.ContextHelp)
- helpIcon.setUseIconAsLink(true)
- helpIcon.toolTipText = KotlinIdeaGradleBundle.message(
- "tool.tip.text.the.external.gradle.project.needs.to.be.imported.to.get.this.script.analyzed"
- )
}
- scriptUnderRoot.importRequired -> EditorNotificationPanel().apply {
- text(getMissingConfigurationNotificationText())
+ wasNotImportedAfterCreation -> EditorNotificationPanel().apply {
+ text("Code insight unavailable (Gradle project Sync required)")
createActionLabel(getMissingConfigurationActionText()) {
runPartialGradleImport(project)
}
}
- else -> null
+ notEvaluatedInLastImport -> EditorNotificationPanel().apply {
+ text(KotlinIdeaGradleBundle.message("text.the.associated.gradle.project.isn.t.imported"))
+
+ createActionLabel(KotlinIdeaGradleBundle.message("action.text.standalone")) {
+ rootsManager.updateStandaloneScripts {
+ removeStandaloneScript(file.path)
+ }
+ }
+
+ contextHelp(
+ KotlinIdeaGradleBundle.message("tool.tip.text.the.external.gradle.project.needs.to.be.imported.to.get.this.script.analyzed")
+ )
+ }
+ standalone -> EditorNotificationPanel().apply {
+ text("Standalone script")
+ createActionLabel("Disable script") {
+ rootsManager.updateStandaloneScripts {
+ addStandaloneScript(file.path)
+ }
+ }
+ contextHelp(
+ """
+
\
+
Configuration for this script will be loaded separately from Gradle project Sync. \
+
+
This can be expensive for large Gradle projects.
\
+
+ """
+ )
+ }
}
}
+ private fun EditorNotificationPanel.contextHelp(text: String) {
+ val helpIcon = createActionLabel("") {}
+ helpIcon.setIcon(AllIcons.General.ContextHelp)
+ helpIcon.setUseIconAsLink(true)
+ helpIcon.toolTipText = text
+ }
+
companion object {
private val KEY = Key.create("GradleScriptOutOfSourceNotification")
}
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 c55fb20790d..039077e5ae6 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
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.scripting.gradle.roots
-import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.annotations.TestOnly
@@ -18,6 +17,10 @@ import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import java.io.File
+@set:TestOnly
+@get:TestOnly
+internal var skipLastModifiedFilesLoading = false
+
/**
* [GradleBuildRoot] is a linked gradle build (don't confuse with gradle project and included build).
* Each [GradleBuildRoot] may have it's own Gradle version, Java home and other settings.
@@ -27,132 +30,114 @@ import java.io.File
* See [GradleBuildRootsManager] for more details.
*/
sealed class GradleBuildRoot {
- /**
- * The script not related to any Gradle build that is linked to IntelliJ Project,
- * or we cannot known what is it
- */
- @Suppress("CanSealedSubClassBeObject")
- class Unlinked : GradleBuildRoot()
+ @Volatile
+ var importing = false
- /**
- * Linked project, that may be itself: [Legacy], [New] or [Imported].
- */
- abstract class Linked : GradleBuildRoot() {
- @Volatile
- var importing = false
+ abstract val pathPrefix: String
- abstract val pathPrefix: String
+ abstract val projectRoots: Collection
- abstract val projectRoots: Collection
+ val dir: VirtualFile?
+ get() = LocalFileSystem.getInstance().findFileByPath(pathPrefix)
- val dir: VirtualFile?
- get() = LocalFileSystem.getInstance().findFileByPath(pathPrefix)
+ private lateinit var lastModifiedFiles: LastModifiedFiles
- private lateinit var lastModifiedFiles: LastModifiedFiles
+ fun loadLastModifiedFiles() {
+ val loaded = if (!skipLastModifiedFilesLoading) {
+ val dir = dir
+ if (dir != null) LastModifiedFiles.read(dir)
+ else null
+ } else null
- fun loadLastModifiedFiles() {
- val loaded = if (!skipLastModifiedFilesLoading) {
- val dir = dir
- if (dir != null) LastModifiedFiles.read(dir)
- else null
- } else null
+ lastModifiedFiles = loaded ?: LastModifiedFiles()
+ }
- lastModifiedFiles = loaded ?: LastModifiedFiles()
+ fun saveLastModifiedFiles() {
+ LastModifiedFiles.write(dir ?: return, lastModifiedFiles)
+ }
+
+ fun areRelatedFilesChangedBefore(file: VirtualFile, lastModified: Long): Boolean =
+ lastModifiedFiles.lastModifiedTimeStampExcept(file.path) < lastModified
+
+ fun fileChanged(filePath: String, ts: Long) {
+ lastModifiedFiles.fileChanged(ts, filePath)
+ }
+}
+
+sealed class WithoutScriptModels(settings: GradleProjectSettings) : GradleBuildRoot() {
+ final override val pathPrefix = settings.externalProjectPath!!
+ final override val projectRoots = settings.modules.takeIf { it.isNotEmpty() } ?: listOf(pathPrefix)
+
+ init {
+ loadLastModifiedFiles()
+ }
+}
+
+/**
+ * Gradle build with old Gradle version (<6.0)
+ */
+class Legacy(
+ settings: GradleProjectSettings
+) : WithoutScriptModels(settings) {
+ init {
+ loadLastModifiedFiles()
+ }
+}
+
+/**
+ * Linked but not yet imported Gradle build.
+ */
+class New(
+ settings: GradleProjectSettings
+) : WithoutScriptModels(settings) {
+ init {
+ loadLastModifiedFiles()
+ }
+}
+
+/**
+ * Imported Gradle build.
+ * Each imported build have info about all of it's Kotlin Build Scripts.
+ */
+class Imported(
+ override val pathPrefix: String,
+ val javaHome: File?,
+ val data: GradleBuildRootData
+) : GradleBuildRoot() {
+ override val projectRoots: Collection
+ get() = data.projectRoots
+
+ init {
+ loadLastModifiedFiles()
+ }
+
+ fun collectConfigurations(builder: ScriptClassRootsBuilder) {
+ if (javaHome != null) {
+ builder.sdks.addSdk(javaHome)
}
- fun saveLastModifiedFiles() {
- LastModifiedFiles.write(dir ?: return, lastModifiedFiles)
- }
+ val definitions = GradleScriptDefinitionsContributor.getDefinitions(builder.project)
- fun areRelatedFilesChangedBefore(file: VirtualFile, lastModified: Long): Boolean =
- lastModifiedFiles.lastModifiedTimeStampExcept(file.path) < lastModified
+ builder.addTemplateClassesRoots(data.templateClasspath)
- fun fileChanged(filePath: String, ts: Long) {
- lastModifiedFiles.fileChanged(ts, filePath)
+ data.models.forEach { script ->
+ val definition = selectScriptDefinition(script, definitions)
+
+ builder.addCustom(
+ script.file,
+ script.classPath,
+ script.sourcePath,
+ GradleScriptInfo(this, definition, script)
+ )
}
}
- abstract class WithoutScriptModels(settings: GradleProjectSettings) : Linked() {
- final override val pathPrefix = settings.externalProjectPath!!
- final override val projectRoots = settings.modules.takeIf { it.isNotEmpty() } ?: listOf(pathPrefix)
-
- init {
- loadLastModifiedFiles()
- }
- }
-
- /**
- * Gradle build with old Gradle version (<6.0)
- */
- class Legacy(
- settings: GradleProjectSettings
- ) : WithoutScriptModels(settings) {
- init {
- loadLastModifiedFiles()
- }
- }
-
- /**
- * Linked but not yet imported Gradle build.
- */
- class New(
- settings: GradleProjectSettings
- ) : WithoutScriptModels(settings) {
- init {
- loadLastModifiedFiles()
- }
- }
-
- /**
- * Imported Gradle build.
- * Each imported build have info about all of it's Kotlin Build Scripts.
- */
- class Imported(
- override val pathPrefix: String,
- val javaHome: File?,
- val data: GradleBuildRootData
- ) : Linked() {
- override val projectRoots: Collection
- get() = data.projectRoots
-
- init {
- loadLastModifiedFiles()
- }
-
- fun collectConfigurations(builder: ScriptClassRootsBuilder) {
- if (javaHome != null) {
- builder.sdks.addSdk(javaHome)
- }
-
- val definitions = GradleScriptDefinitionsContributor.getDefinitions(builder.project)
-
- builder.addTemplateClassesRoots(data.templateClasspath)
-
- data.models.forEach { script ->
- val definition = selectScriptDefinition(script, definitions)
-
- builder.addCustom(
- script.file,
- script.classPath,
- script.sourcePath,
- GradleScriptInfo(this, definition, script)
- )
- }
- }
-
- private fun selectScriptDefinition(
- script: KotlinDslScriptModel,
- definitions: List
- ): ScriptDefinition? {
- val file = LocalFileSystem.getInstance().findFileByPath(script.file) ?: return null
- val scriptSource = VirtualFileScriptSource(file)
- return definitions.firstOrNull { it.isScript(scriptSource) }
- }
- }
-
- companion object {
- @set:TestOnly
- @get:TestOnly
- internal var skipLastModifiedFilesLoading = false
+ private fun selectScriptDefinition(
+ script: KotlinDslScriptModel,
+ definitions: List
+ ): ScriptDefinition? {
+ val file = LocalFileSystem.getInstance().findFileByPath(script.file) ?: return null
+ val scriptSource = VirtualFileScriptSource(file)
+ return definitions.firstOrNull { it.isScript(scriptSource) }
}
}
\ No newline at end of file
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootIndex.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootIndex.kt
index 68e104f5c94..126124cc128 100644
--- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootIndex.kt
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootIndex.kt
@@ -7,15 +7,15 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots
import com.intellij.openapi.diagnostic.logger
-class GradleBuildRootIndex {
+class GradleBuildRootIndex : StandaloneScriptsUpdater {
private val log = logger()
- private val standaloneScriptRoots = mutableMapOf()
+ private val standaloneScriptRoots = mutableMapOf()
- private val byWorkingDir = HashMap()
- private val byProjectDir = HashMap()
+ private val byWorkingDir = HashMap()
+ private val byProjectDir = HashMap()
- val list: Collection
+ val list: Collection
get() = byWorkingDir.values
@Synchronized
@@ -34,8 +34,8 @@ class GradleBuildRootIndex {
fun getBuildByRootDir(dir: String) = byWorkingDir[dir]
@Synchronized
- fun findNearestRoot(path: String): GradleBuildRoot.Linked? {
- var max: Pair? = null
+ fun findNearestRoot(path: String): GradleBuildRoot? {
+ var max: Pair? = null
byWorkingDir.entries.forEach {
if (path.startsWith(it.key) && (max == null || it.key.length > max!!.first.length)) {
max = it.key to it.value
@@ -54,7 +54,7 @@ class GradleBuildRootIndex {
fun getStandaloneScriptRoot(path: String) = standaloneScriptRoots[path]
@Synchronized
- fun add(value: GradleBuildRoot.Linked): GradleBuildRoot.Linked? {
+ fun add(value: GradleBuildRoot): GradleBuildRoot? {
val prefix = value.pathPrefix
val old = byWorkingDir.put(prefix, value)
rebuildProjectRoots()
@@ -69,11 +69,15 @@ class GradleBuildRootIndex {
}
@Synchronized
- fun addStandaloneScript(path: String) {
+ override fun addStandaloneScript(path: String) {
computeStandaloneScriptRoot(path)
}
- var standaloneScripts: Collection
+ @Synchronized
+ override fun removeStandaloneScript(path: String) =
+ standaloneScriptRoots.remove(path)
+
+ override var standaloneScripts: Collection
@Synchronized get() = standaloneScriptRoots.keys
@Synchronized set(value) {
standaloneScriptRoots.clear()
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 3545e1affbb..22c2eebfb99 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
@@ -38,7 +38,7 @@ abstract class GradleBuildRootsLocator {
findAffectedFileRoot(filePath) != null ||
roots.isStandaloneScript(filePath)
- fun findAffectedFileRoot(filePath: String): GradleBuildRoot.Linked? {
+ fun findAffectedFileRoot(filePath: String): GradleBuildRoot? {
if (filePath.endsWith("/gradle.properties") ||
filePath.endsWith("/gradle.local")
) {
@@ -50,25 +50,42 @@ abstract class GradleBuildRootsLocator {
return roots.getBuildByRootDir(filePath.substring(0, buildDirIndex))
}
- return findScriptBuildRoot(filePath, searchNearestLegacy = false)?.root as? GradleBuildRoot.Linked
+ return findScriptBuildRoot(filePath, searchNearestLegacy = false)?.root as? GradleBuildRoot
+ }
+
+ enum class NotificationKind {
+ dontCare, // one of: imported, inside linked legacy gradle build
+ outsideAnyting, // suggest link related gradle build or just say that there is no one
+ wasNotImportedAfterCreation, // project not yet imported after this file was created
+ notEvaluatedInLastImport, // all other scripts, suggest to sync or mark as standalone
+ standalone
}
class ScriptUnderRoot(
val root: GradleBuildRoot?,
val script: GradleScriptInfo? = null,
- val standalone: Boolean = false
+ val standalone: Boolean = false,
+ val nearest: GradleBuildRoot? = null
) {
- val isUnrelatedScript: Boolean
- get() = root is GradleBuildRoot.Unlinked
-
- val importRequired: Boolean
- get() = root is GradleBuildRoot.Linked &&
- !isImported &&
- !root.importing &&
- !standalone
+ val notificationKind: NotificationKind
+ get() = when {
+ isImported -> NotificationKind.dontCare
+ standalone -> NotificationKind.standalone
+ nearest == null -> NotificationKind.outsideAnyting
+ nearest.importing -> NotificationKind.dontCare
+ else -> when (nearest) {
+ is Legacy -> NotificationKind.dontCare
+ is New -> NotificationKind.wasNotImportedAfterCreation
+ is Imported -> NotificationKind.notEvaluatedInLastImport // todo: wasNotImportedAfterCreation
+ }
+ }
private val isImported: Boolean
get() = script != null
+
+ override fun toString(): String {
+ return "ScriptUnderRoot(root=$root, script=$script, standalone=$standalone, nearest=$nearest)"
+ }
}
fun findScriptBuildRoot(gradleKtsFile: VirtualFile): ScriptUnderRoot? =
@@ -98,12 +115,14 @@ abstract class GradleBuildRootsLocator {
return ScriptUnderRoot(standaloneScriptRoot, standalone = true)
}
- // todo(gradle6): remove, it is required only for projects with old gradle
if (searchNearestLegacy) {
- val found = roots.findNearestRoot(filePath)
- if (found is GradleBuildRoot.Legacy) return ScriptUnderRoot(found)
+ val nearest = roots.findNearestRoot(filePath)
+ return when (nearest) {
+ is Legacy -> ScriptUnderRoot(nearest)
+ else -> ScriptUnderRoot(null, nearest = nearest)
+ }
+ } else {
+ return ScriptUnderRoot(null)
}
-
- return ScriptUnderRoot(GradleBuildRoot.Unlinked())
}
}
\ 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 0351aa7410d..0a08b31d0a6 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
@@ -21,11 +21,11 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.configuration.CompositeScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport.Companion.EPN
-import org.jetbrains.kotlin.idea.core.script.configuration.utils.getKtFile
import org.jetbrains.kotlin.idea.core.script.ucache.ScriptClassRootsBuilder
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.idea.scripting.gradle.*
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslGradleBuildSync
+import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
@@ -50,12 +50,10 @@ import java.util.concurrent.atomic.AtomicBoolean
* It also used to show related notification and floating actions depending on root kind, state and script state itself.
*
* Roots may be:
- * - [GradleBuildRoot.Unlinked] - The script not related to any Gradle build that is linked to IntelliJ Project,
- * or we cannot known what is it
- * - [GradleBuildRoot.Linked] - Linked project, that may be itself:
- * - [GradleBuildRoot.Legacy] - Gradle build with old Gradle version (<6.0)
- * - [GradleBuildRoot.New] - not yet imported
- * - [GradleBuildRoot.Imported] - imported
+ * - [GradleBuildRoot] - Linked project, that may be itself:
+ * - [Legacy] - Gradle build with old Gradle version (<6.0)
+ * - [New] - not yet imported
+ * - [Imported] - imported
*/
class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), ScriptingSupport {
private val manager: CompositeScriptConfigurationManager
@@ -69,14 +67,14 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
override fun isApplicable(file: VirtualFile): Boolean {
val scriptUnderRoot = findScriptBuildRoot(file) ?: return false
- if (scriptUnderRoot.root is GradleBuildRoot.Legacy) return false
+ if (scriptUnderRoot.root is Legacy) return false
if (roots.isStandaloneScript(file.path)) return false
return true
}
override fun isConfigurationLoadingInProgress(file: KtFile): Boolean {
return when (val root = findScriptBuildRoot(file.originalFile.virtualFile)?.root) {
- is GradleBuildRoot.Linked -> root.importing
+ is GradleBuildRoot -> root.importing
else -> false
}
}
@@ -98,7 +96,7 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
override fun collectConfigurations(builder: ScriptClassRootsBuilder) {
roots.list.forEach { root ->
- if (root is GradleBuildRoot.Imported) {
+ if (root is Imported) {
root.collectConfigurations(builder)
}
}
@@ -116,32 +114,32 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
fun markImportingInProgress(workingDir: String, inProgress: Boolean = true) {
actualizeBuildRoot(workingDir)?.importing = inProgress
- updateNotifications(workingDir)
- scriptConfigurationsAreUpToDate(project)
+ updateNotifications { it.startsWith(workingDir) }
+ hideNotificationForProjectImport(project)
}
fun update(build: KotlinDslGradleBuildSync) {
// fast path for linked gradle builds without .gradle.kts support
if (build.models.isEmpty()) {
val root = getBuildRootByWorkingDir(build.workingDir) ?: return
- if (root is GradleBuildRoot.Imported && root.data.models.isEmpty()) return
+ if (root is Imported && root.data.models.isEmpty()) return
}
val root = actualizeBuildRoot(build.workingDir) ?: return
root.importing = false
- if (root is GradleBuildRoot.Legacy) return
+ if (root is Legacy) return
val templateClasspath = GradleScriptDefinitionsContributor.getDefinitionsTemplateClasspath(project)
val newData = GradleBuildRootData(build.projectRoots, templateClasspath, build.models)
- val mergedData = if (build.failed && root is GradleBuildRoot.Imported) merge(root.data, newData) else newData
+ val mergedData = if (build.failed && root is Imported) merge(root.data, newData) else newData
val newSupport = tryCreateImportedRoot(build.workingDir) { mergedData } ?: return
GradleBuildRootDataSerializer.write(newSupport.dir ?: return, mergedData)
add(newSupport)
- scriptConfigurationsAreUpToDate(project)
+ hideNotificationForProjectImport(project)
}
private fun merge(old: GradleBuildRootData, new: GradleBuildRootData): GradleBuildRootData {
@@ -168,12 +166,22 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
}
}
- fun addStandaloneScript(file: VirtualFile) {
- roots.addStandaloneScript(file.path)
- roots.rebuildProjectRoots()
- updateNotifications(file.path)
- val ktFile = project.getKtFile(file) ?: return
- ScriptConfigurationManager.getInstance(project).getConfiguration(ktFile)
+ fun updateStandaloneScripts(update: StandaloneScriptsUpdater.() -> Unit) {
+ val changes = StandaloneScriptsUpdater.collectChanges(delegate = roots, update)
+
+ updateNotifications { it in changes.new || it in changes.removed }
+
+ runReadAction {
+ changes.new.forEach {
+ val virtualFile = LocalFileSystem.getInstance().findFileByPath(it)
+ if (virtualFile != null) {
+ val ktFile = PsiManager.getInstance(project).findFile(virtualFile) as? KtFile
+ if (ktFile != null) {
+ ScriptConfigurationManager.getInstance(project).getConfiguration(ktFile)
+ }
+ }
+ }
+ }
}
init {
@@ -218,7 +226,7 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
* Actually this should be true, but we may miss some change events.
* For that cases we are rechecking this on each Gradle Project sync (importing/reimporting)
*/
- private fun actualizeBuildRoot(workingDir: String): GradleBuildRoot.Linked? {
+ private fun actualizeBuildRoot(workingDir: String): GradleBuildRoot? {
val actualSettings = getGradleProjectSettings(workingDir)
val buildRoot = getBuildRootByWorkingDir(workingDir)
@@ -232,15 +240,15 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
}
}
- private fun GradleBuildRoot.Linked.checkActual(actualSettings: GradleProjectSettings?): Boolean {
+ private fun GradleBuildRoot.checkActual(actualSettings: GradleProjectSettings?): Boolean {
if (actualSettings == null) return false
- val knownAsSupported = this !is GradleBuildRoot.Legacy
+ val knownAsSupported = this !is Legacy
val shouldBeSupported = kotlinDslScriptsModelImportSupported(actualSettings.resolveGradleVersion().version)
return knownAsSupported == shouldBeSupported
}
- private fun reloadBuildRoot(rootPath: String): GradleBuildRoot.Linked? {
+ private fun reloadBuildRoot(rootPath: String): GradleBuildRoot? {
val settings = getGradleProjectSettings(rootPath)
if (settings == null) {
remove(rootPath)
@@ -267,45 +275,45 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
settings: GradleProjectSettings
) = data.copy(projectRoots = data.projectRoots.toSet() + settings.modules)
- private fun createOtherLinkedRoot(settings: GradleProjectSettings): GradleBuildRoot.Linked {
+ private fun createOtherLinkedRoot(settings: GradleProjectSettings): GradleBuildRoot {
val supported = kotlinDslScriptsModelImportSupported(settings.resolveGradleVersion().version)
return when {
- supported -> GradleBuildRoot.New(settings)
- else -> GradleBuildRoot.Legacy(settings)
+ supported -> New(settings)
+ else -> Legacy(settings)
}
}
private fun tryCreateImportedRoot(
externalProjectPath: String,
dataProvider: (buildRoot: VirtualFile) -> GradleBuildRootData?
- ): GradleBuildRoot.Imported? {
+ ): Imported? {
val buildRoot = VfsUtil.findFile(Paths.get(externalProjectPath), true) ?: return null
val data = dataProvider(buildRoot) ?: return null
val javaHome = ExternalSystemApiUtil
.getExecutionSettings(project, externalProjectPath, GradleConstants.SYSTEM_ID)
.javaHome?.let { File(it) }
- return GradleBuildRoot.Imported(externalProjectPath, javaHome, data)
+ return Imported(externalProjectPath, javaHome, data)
}
- private fun add(newRoot: GradleBuildRoot.Linked) {
+ private fun add(newRoot: GradleBuildRoot) {
val old = roots.add(newRoot)
- if (old is GradleBuildRoot.Imported && newRoot !is GradleBuildRoot.Imported) removeData(old.pathPrefix)
- if (old is GradleBuildRoot.Imported || newRoot is GradleBuildRoot.Imported) {
+ if (old is Imported && newRoot !is Imported) removeData(old.pathPrefix)
+ if (old is Imported || newRoot is Imported) {
updater.invalidateAndCommit()
}
- updateNotifications(newRoot.pathPrefix)
+ updateNotifications { it.startsWith(newRoot.pathPrefix) }
}
private fun remove(rootPath: String) {
val removed = roots.remove(rootPath)
- if (removed is GradleBuildRoot.Imported) {
+ if (removed is Imported) {
removeData(rootPath)
updater.invalidateAndCommit()
}
- updateNotifications(rootPath)
+ updateNotifications { it.startsWith(rootPath) }
}
private fun removeData(rootPath: String) {
@@ -317,11 +325,11 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(),
}
@Suppress("MemberVisibilityCanBePrivate")
- private fun updateNotifications(dir: String) {
+ private fun updateNotifications(shouldUpdatePath: (String) -> Boolean) {
if (!project.isOpen) return
val openedScripts = FileEditorManager.getInstance(project).openFiles.filter {
- it.path.startsWith(dir) && maybeAffectedGradleProjectFile(it.path)
+ shouldUpdatePath(it.path) && maybeAffectedGradleProjectFile(it.path)
}
if (openedScripts.isEmpty()) return
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleScriptInfo.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleScriptInfo.kt
index 5e830fd424b..6704cd6ff9e 100644
--- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleScriptInfo.kt
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleScriptInfo.kt
@@ -19,7 +19,7 @@ import kotlin.script.experimental.jvm.jdkHome
import kotlin.script.experimental.jvm.jvm
class GradleScriptInfo(
- val buildRoot: GradleBuildRoot.Imported,
+ val buildRoot: Imported,
scriptDefinition: ScriptDefinition?,
val model: KotlinDslScriptModel
) : ScriptClassRootsCache.LightScriptInfo(scriptDefinition) {
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt
new file mode 100644
index 00000000000..7f926a32111
--- /dev/null
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.scripting.gradle.roots
+
+interface StandaloneScriptsUpdater {
+ var standaloneScripts: Collection
+
+ fun addStandaloneScript(path: String)
+
+ fun removeStandaloneScript(path: String): GradleBuildRoot?
+
+ class Changes {
+ val new = mutableSetOf()
+ val removed = mutableSetOf()
+ }
+
+ companion object {
+ fun collectChanges(
+ delegate: StandaloneScriptsUpdater,
+ update: StandaloneScriptsUpdater.() -> Unit
+ ): Changes {
+ synchronized(delegate) {
+ val old = delegate.standaloneScripts.toSet()
+ delegate.update()
+ val changes = Changes()
+ val new = delegate.standaloneScripts
+
+ new.forEach {
+ if (it !in old) changes.new.add(it)
+ }
+
+ old.forEach {
+ if (it !in new) changes.removed.add(it)
+ }
+
+ return changes
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/utils.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/utils.kt
index 50475b053c5..c3e5ed3317a 100644
--- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/utils.kt
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/utils.kt
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
-import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.LeafPsiElement
@@ -64,7 +63,7 @@ fun getGradleScriptInputsStamp(
}
}
- val buildRoot = GradleBuildRootsManager.getInstance(project).findScriptBuildRoot(file)?.root as? GradleBuildRoot.Linked
+ val buildRoot = GradleBuildRootsManager.getInstance(project).findScriptBuildRoot(file)?.root as? GradleBuildRoot
GradleKotlinScriptConfigurationInputs(result.toString(), givenTimeStamp, buildRoot?.pathPrefix)
} else null
}
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 03948ace16d..3967ef868f9 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
@@ -7,10 +7,11 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots
import org.jetbrains.kotlin.idea.scripting.gradle.GradleKotlinScriptConfigurationInputs
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
+import kotlin.test.assertEquals
open class AbstractGradleBuildRootsLocatorTest {
init {
- GradleBuildRoot.skipLastModifiedFilesLoading = true
+ skipLastModifiedFilesLoading = true
}
private val scripts = mutableMapOf()
@@ -21,7 +22,7 @@ open class AbstractGradleBuildRootsLocatorTest {
fun accessRoots() = roots
}
- private fun add(root: GradleBuildRoot.Linked) {
+ private fun add(root: GradleBuildRoot) {
locator.accessRoots().add(root)
}
@@ -31,7 +32,7 @@ open class AbstractGradleBuildRootsLocatorTest {
relativeScripts: List = listOf("build.gradle.kts")
) {
val pathPrefix = "$dir/"
- val root = GradleBuildRoot.Imported(
+ val root = Imported(
dir,
null,
GradleBuildRootData(
@@ -62,4 +63,8 @@ open class AbstractGradleBuildRootsLocatorTest {
fun findScriptBuildRoot(filePath: String, searchNearestLegacy: Boolean = true) =
locator.findScriptBuildRoot(filePath, searchNearestLegacy)
+
+ fun assertNotificationKind(filePath: String, notificationKind: GradleBuildRootsLocator.NotificationKind) {
+ assertEquals(notificationKind, findScriptBuildRoot(filePath)?.notificationKind)
+ }
}
\ No newline at end of file
diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocatorTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocatorTest.kt
index a4765fd0f66..d31b799377e 100644
--- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocatorTest.kt
+++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocatorTest.kt
@@ -5,7 +5,10 @@
package org.jetbrains.kotlin.idea.scripting.gradle.roots
+import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsLocator.NotificationKind.dontCare
+import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsLocator.NotificationKind.wasNotImportedAfterCreation
import org.junit.Test
+import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -16,18 +19,14 @@ class GradleBuildRootsLocatorTest : AbstractGradleBuildRootsLocatorTest() {
// the build.gradle.kts under the project root will be definitive import at next import
// so, we should not treat it as unlinked
newImportedGradleProject("imported", relativeScripts = listOf())
- val scriptUnderRoot = findScriptBuildRoot("imported/build.gradle.kts")
- assertNotNull(scriptUnderRoot)
- assertTrue(scriptUnderRoot.importRequired)
- assertFalse(scriptUnderRoot.isUnrelatedScript)
+
+ assertNotificationKind("imported/build.gradle.kts", wasNotImportedAfterCreation)
}
@Test
fun testBuildGradleKtsNearProjectRoot() {
newImportedGradleProject("imported", relativeScripts = listOf("build.gradle.kts"))
- val scriptUnderRoot = findScriptBuildRoot("imported/build.gradle.kts")
- assertNotNull(scriptUnderRoot)
- assertFalse(scriptUnderRoot.importRequired)
- assertFalse(scriptUnderRoot.isUnrelatedScript)
+
+ assertNotificationKind("imported/build.gradle.kts", dontCare)
}
}
\ No newline at end of file