diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 8c3c5379392..252628460f1 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -13,6 +13,21 @@
+
+
diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/ScriptingSupport.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/ScriptingSupport.kt
index ef3f31a0956..bad6d933696 100644
--- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/ScriptingSupport.kt
+++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/ScriptingSupport.kt
@@ -31,10 +31,10 @@ import org.jetbrains.kotlin.psi.KtFile
*
* @sample GradleBuildRootsManager
*/
-abstract class ScriptingSupport {
- abstract fun isApplicable(file: VirtualFile): Boolean
- abstract fun isConfigurationLoadingInProgress(file: KtFile): Boolean
- abstract fun collectConfigurations(builder: ScriptClassRootsBuilder)
+interface ScriptingSupport {
+ fun isApplicable(file: VirtualFile): Boolean
+ fun isConfigurationLoadingInProgress(file: KtFile): Boolean
+ fun collectConfigurations(builder: ScriptClassRootsBuilder)
companion object {
val EPN: ExtensionPointName =
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 fdc9a682d12..c04f3a302b1 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
@@ -15,10 +15,9 @@ 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
+ * 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.Linked.areRelatedFilesChangedBefore].
*
* [lastModifiedTs] is needed to check if some related file was changed since last update
*/
@@ -35,7 +34,7 @@ data class GradleKotlinScriptConfigurationInputs(
return buildRoot == null ||
GradleBuildRootsManager.getInstance(project)
- .getBuildRoot(buildRoot)
+ .getBuildRootByWorkingDir(buildRoot)
?.areRelatedFilesChangedBefore(file, lastModifiedTs) ?: false
} catch (cancel: ProcessCanceledException) {
return false
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 d6dfa80232a..c55fb20790d 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
@@ -8,6 +8,7 @@ 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
import org.jetbrains.kotlin.idea.core.script.ucache.ScriptClassRootsBuilder
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptDefinitionsContributor
import org.jetbrains.kotlin.idea.scripting.gradle.LastModifiedFiles
@@ -40,8 +41,6 @@ sealed class GradleBuildRoot {
@Volatile
var importing = false
- abstract val manager: GradleBuildRootsManager
-
abstract val pathPrefix: String
abstract val projectRoots: Collection
@@ -52,7 +51,13 @@ sealed class GradleBuildRoot {
private lateinit var lastModifiedFiles: LastModifiedFiles
fun loadLastModifiedFiles() {
- lastModifiedFiles = dir?.let { LastModifiedFiles.read(it) } ?: LastModifiedFiles()
+ val loaded = if (!skipLastModifiedFilesLoading) {
+ val dir = dir
+ if (dir != null) LastModifiedFiles.read(dir)
+ else null
+ } else null
+
+ lastModifiedFiles = loaded ?: LastModifiedFiles()
}
fun saveLastModifiedFiles() {
@@ -64,7 +69,6 @@ sealed class GradleBuildRoot {
fun fileChanged(filePath: String, ts: Long) {
lastModifiedFiles.fileChanged(ts, filePath)
- manager.scheduleLastModifiedFilesSave()
}
}
@@ -81,7 +85,6 @@ sealed class GradleBuildRoot {
* Gradle build with old Gradle version (<6.0)
*/
class Legacy(
- override val manager: GradleBuildRootsManager,
settings: GradleProjectSettings
) : WithoutScriptModels(settings) {
init {
@@ -93,7 +96,6 @@ sealed class GradleBuildRoot {
* Linked but not yet imported Gradle build.
*/
class New(
- override val manager: GradleBuildRootsManager,
settings: GradleProjectSettings
) : WithoutScriptModels(settings) {
init {
@@ -106,14 +108,10 @@ sealed class GradleBuildRoot {
* Each imported build have info about all of it's Kotlin Build Scripts.
*/
class Imported(
- override val manager: GradleBuildRootsManager,
override val pathPrefix: String,
val javaHome: File?,
val data: GradleBuildRootData
) : Linked() {
- val project: Project
- get() = manager.project
-
override val projectRoots: Collection
get() = data.projectRoots
@@ -126,7 +124,7 @@ sealed class GradleBuildRoot {
builder.sdks.addSdk(javaHome)
}
- val definitions = GradleScriptDefinitionsContributor.getDefinitions(project)
+ val definitions = GradleScriptDefinitionsContributor.getDefinitions(builder.project)
builder.addTemplateClassesRoots(data.templateClasspath)
@@ -151,4 +149,10 @@ sealed class GradleBuildRoot {
return definitions.firstOrNull { it.isScript(scriptSource) }
}
}
+
+ companion object {
+ @set:TestOnly
+ @get:TestOnly
+ internal var skipLastModifiedFilesLoading = false
+ }
}
\ No newline at end of file
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
new file mode 100644
index 00000000000..a5ef4ff1116
--- /dev/null
+++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocator.kt
@@ -0,0 +1,92 @@
+/*
+ * 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
+
+import com.intellij.openapi.vfs.VirtualFile
+import org.jetbrains.kotlin.utils.addToStdlib.lastIndexOfOrNull
+
+/**
+ * Internal logic about finding script root for [GradleBuildRootsManager].
+ * Extracted only for tests.
+ *
+ * @see GradleBuildRootsManager for details.
+ */
+abstract class GradleBuildRootsLocator {
+ protected val roots = GradleBuildRootIndex()
+
+ abstract fun getScriptInfo(localPath: String): GradleScriptInfo?
+
+ fun getBuildRootByWorkingDir(gradleWorkingDir: String) =
+ roots.getBuildByRootDir(gradleWorkingDir)
+
+ fun getScriptInfo(file: VirtualFile): GradleScriptInfo? =
+ getScriptInfo(file.localPath)
+
+ private val VirtualFile.localPath
+ get() = path
+
+ fun maybeAffectedGradleProjectFile(filePath: String): Boolean =
+ filePath.endsWith("/gradle.properties") ||
+ filePath.endsWith("/gradle.local") ||
+ filePath.endsWith("/gradle-wrapper.properties") ||
+ filePath.endsWith("/build.gradle.kts") ||
+ filePath.endsWith("/settings.gradle.kts") ||
+ filePath.endsWith("/init.gradle.kts")
+
+ fun isAffectedGradleProjectFile(filePath: String): Boolean =
+ findAffectedFileRoot(filePath) != null
+
+ fun findAffectedFileRoot(filePath: String): GradleBuildRoot.Linked? {
+ if (filePath.endsWith("/gradle.properties") ||
+ filePath.endsWith("/gradle.local")
+ ) {
+ return roots.getBuildByProjectDir(filePath.substringBeforeLast("/"))
+ } else if (filePath.endsWith("/gradle-wrapper.properties")) {
+ val gradleWrapperDirIndex = filePath.lastIndexOfOrNull('/') ?: return null
+ val gradleDirIndex = filePath.lastIndexOfOrNull('/', gradleWrapperDirIndex - 1) ?: return null
+ val buildDirIndex = filePath.lastIndexOfOrNull('/', gradleDirIndex - 1) ?: return null
+ return roots.getBuildByRootDir(filePath.substring(0, buildDirIndex))
+ }
+
+ return findScriptBuildRoot(filePath, searchNearestLegacy = false)?.root as? GradleBuildRoot.Linked
+ }
+
+ class ScriptUnderRoot(
+ val root: GradleBuildRoot?,
+ val script: GradleScriptInfo? = null
+ )
+
+ fun findScriptBuildRoot(gradleKtsFile: VirtualFile): ScriptUnderRoot? =
+ findScriptBuildRoot(gradleKtsFile.path)
+
+ protected fun findScriptBuildRoot(filePath: String, searchNearestLegacy: Boolean = true): ScriptUnderRoot? {
+ if (!filePath.endsWith(".gradle.kts")) return null
+
+ val scriptInfo = getScriptInfo(filePath)
+ val imported = scriptInfo?.buildRoot
+ if (imported != null) return ScriptUnderRoot(imported, scriptInfo)
+
+ if (filePath.endsWith("/build.gradle.kts") ||
+ filePath.endsWith("/settings.gradle.kts") ||
+ filePath.endsWith("/init.gradle.kts")
+ ) {
+ // 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)
+ }
+
+ // other scripts: "included", "precompiled" scripts, scripts in unlinked projects,
+ // or just random files with ".gradle.kts" ending
+
+ // 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)
+ }
+
+ 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 ea384c98be9..06bad6871ba 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
@@ -20,13 +20,13 @@ import kotlinx.coroutines.launch
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.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.psi.KtFile
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
-import org.jetbrains.kotlin.utils.addToStdlib.lastIndexOfOrNull
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
import org.jetbrains.plugins.gradle.settings.*
import org.jetbrains.plugins.gradle.util.GradleConstants
@@ -56,15 +56,13 @@ import java.util.concurrent.atomic.AtomicBoolean
* - [GradleBuildRoot.New] - not yet imported
* - [GradleBuildRoot.Imported] - imported
*/
-class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
+class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), ScriptingSupport {
private val manager: CompositeScriptConfigurationManager
get() = ScriptConfigurationManager.getInstance(project) as CompositeScriptConfigurationManager
private val updater
get() = manager.updater
- private val roots = GradleBuildRootIndex()
-
////////////
/// ScriptingSupport.Provider implementation:
@@ -98,87 +96,14 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
//////////////////
- private val VirtualFile.localPath
- get() = path
-
- fun getScriptInfo(file: VirtualFile): GradleScriptInfo? =
- getScriptInfo(file.localPath)
-
- fun getScriptInfo(localPath: String): GradleScriptInfo? =
+ override fun getScriptInfo(localPath: String): GradleScriptInfo? =
manager.getLightScriptInfo(localPath) as? GradleScriptInfo
- class ScriptUnderRoot(
- val root: GradleBuildRoot?,
- val script: GradleScriptInfo? = null
- )
-
- fun findScriptBuildRoot(gradleKtsFile: VirtualFile): ScriptUnderRoot? =
- findScriptBuildRoot(gradleKtsFile.path)
-
- /**
- * Fast method that can return false positive
- */
- fun maybeAffectedGradleProjectFile(filePath: String): Boolean =
- filePath.endsWith("/gradle.properties") ||
- filePath.endsWith("/gradle.local") ||
- filePath.endsWith("/gradle-wrapper.properties") ||
- filePath.endsWith("/build.gradle.kts") ||
- filePath.endsWith("/settings.gradle.kts") ||
- filePath.endsWith("/init.gradle.kts")
-
- fun isAffectedGradleProjectFile(filePath: String): Boolean =
- findAffectedFileRoot(filePath) != null
-
fun fileChanged(filePath: String, ts: Long = System.currentTimeMillis()) {
findAffectedFileRoot(filePath)?.fileChanged(filePath, ts)
+ scheduleLastModifiedFilesSave()
}
- private fun findAffectedFileRoot(filePath: String): GradleBuildRoot.Linked? {
- if (filePath.endsWith("/gradle.properties") ||
- filePath.endsWith("/gradle.local")
- ) {
- return roots.getBuildByProjectDir(filePath.substringBeforeLast("/"))
- } else if (filePath.endsWith("/gradle-wrapper.properties")) {
- val gradleWrapperDirIndex = filePath.lastIndexOfOrNull('/') ?: return null
- val gradleDirIndex = filePath.lastIndexOfOrNull('/', gradleWrapperDirIndex - 1) ?: return null
- val buildDirIndex = filePath.lastIndexOfOrNull('/', gradleDirIndex - 1) ?: return null
- return roots.getBuildByRootDir(filePath.substring(0, buildDirIndex))
- }
-
- return findScriptBuildRoot(filePath, searchNearestLegacy = false)?.root as? GradleBuildRoot.Linked
- }
-
- private fun findScriptBuildRoot(filePath: String, searchNearestLegacy: Boolean = true): ScriptUnderRoot? {
- if (!filePath.endsWith(".gradle.kts")) return null
-
- val scriptInfo = getScriptInfo(filePath)
- val imported = scriptInfo?.buildRoot
- if (imported != null) return ScriptUnderRoot(imported, scriptInfo)
-
- if (filePath.endsWith("/build.gradle.kts") ||
- filePath.endsWith("/settings.gradle.kts") ||
- filePath.endsWith("/init.gradle.kts")
- ) {
- // 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)
- }
-
- // other scripts: "included", "precompiled" scripts, scripts in unlinked projects,
- // or just random files with ".gradle.kts" ending
-
- // 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)
- }
-
- return ScriptUnderRoot(GradleBuildRoot.Unlinked())
- }
-
- fun getBuildRoot(gradleWorkingDir: String) =
- roots.getBuildByRootDir(gradleWorkingDir)
-
fun markImportingInProgress(workingDir: String, inProgress: Boolean = true) {
actualizeBuildRoot(workingDir)?.importing = inProgress
updateNotifications(workingDir)
@@ -188,7 +113,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
fun update(build: KotlinDslGradleBuildSync) {
// fast path for linked gradle builds without .gradle.kts support
if (build.models.isEmpty()) {
- val root = roots.getBuildByRootDir(build.workingDir) ?: return
+ val root = getBuildRootByWorkingDir(build.workingDir) ?: return
if (root is GradleBuildRoot.Imported && root.data.models.isEmpty()) return
}
@@ -277,7 +202,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
*/
private fun actualizeBuildRoot(workingDir: String): GradleBuildRoot.Linked? {
val actualSettings = getGradleProjectSettings(workingDir)
- val buildRoot = roots.getBuildByRootDir(workingDir)
+ val buildRoot = getBuildRootByWorkingDir(workingDir)
return when {
buildRoot != null -> when {
@@ -335,7 +260,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
.getExecutionSettings(project, externalProjectPath, GradleConstants.SYSTEM_ID)
.javaHome?.let { File(it) }
- return GradleBuildRoot.Imported(this, externalProjectPath, javaHome, data)
+ return GradleBuildRoot.Imported(externalProjectPath, javaHome, data)
}
private fun add(newRoot: GradleBuildRoot.Linked) {
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
new file mode 100644
index 00000000000..03948ace16d
--- /dev/null
+++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/AbstractGradleBuildRootsLocatorTest.kt
@@ -0,0 +1,65 @@
+/*
+ * 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
+
+import org.jetbrains.kotlin.idea.scripting.gradle.GradleKotlinScriptConfigurationInputs
+import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
+
+open class AbstractGradleBuildRootsLocatorTest {
+ init {
+ GradleBuildRoot.skipLastModifiedFilesLoading = true
+ }
+
+ private val scripts = mutableMapOf()
+ private val locator = MyRootsLocator()
+
+ private inner class MyRootsLocator : GradleBuildRootsLocator() {
+ override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath]
+ fun accessRoots() = roots
+ }
+
+ private fun add(root: GradleBuildRoot.Linked) {
+ locator.accessRoots().add(root)
+ }
+
+ fun newImportedGradleProject(
+ dir: String,
+ relativeProjectRoots: List = listOf(""),
+ relativeScripts: List = listOf("build.gradle.kts")
+ ) {
+ val pathPrefix = "$dir/"
+ val root = GradleBuildRoot.Imported(
+ dir,
+ null,
+ GradleBuildRootData(
+ relativeProjectRoots.map { (pathPrefix + it).removeSuffix("/") },
+ listOf(),
+ listOf()
+ )
+ )
+
+ add(root)
+
+ 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()
+ )
+ )
+ }
+ }
+
+ fun findScriptBuildRoot(filePath: String, searchNearestLegacy: Boolean = true) =
+ locator.findScriptBuildRoot(filePath, searchNearestLegacy)
+}
\ 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
new file mode 100644
index 00000000000..471b758d575
--- /dev/null
+++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/roots/GradleBuildRootsLocatorTest.kt
@@ -0,0 +1,16 @@
+/*
+ * 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
+
+import org.junit.Test
+
+class GradleBuildRootsLocatorTest : AbstractGradleBuildRootsLocatorTest() {
+ @Test
+ fun testUnlinkedBuildGradleKtsNearProjectRoot() {
+ newImportedGradleProject("imported", relativeScripts = listOf())
+ assert(findScriptBuildRoot("imported/build.gradle.kts")?.root is GradleBuildRoot.Unlinked)
+ }
+}
\ No newline at end of file