diff --git a/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties b/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties index 9ccfb3dfaf4..8845a2368ae 100644 --- a/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties +++ b/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties @@ -1,3 +1,4 @@ +action.text.standalone=Add as standalone script action.label.text.load.script.configuration=Load related Gradle project action.text.install=Install action.text.show.kotlin.gradle.dsl.logs.in=Show Kotlin Gradle DSL Logs in {0} @@ -37,7 +38,7 @@ text.couldn.t.configure.kotlin.gradle.plugin.automatically=Couldn't configure ko text.default.kotlin.gradle.script=Default Kotlin Gradle Script text.gradle.dsl.logs.cannot.be.found.automatically.see.how.to.find.logs=Gradle DSL Logs cannot be found automatically.
See how to find logs here. text.see.manual.installation.instructions=See manual installation instructions here. -text.the.associated.gradle.project.isn.t.imported=The associated Gradle Project isn't imported. +text.the.associated.gradle.project.isn.t.imported=Cannot find related Gradle project. Kotlin DSL Code insight unavailable. text.was.modified={0} was modified title.configure.kotlin.gradle.plugin=Configure Kotlin-Gradle Plugin title.kotlin.build.script=Gradle Kotlin DSL Scripts errors diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleImportHelper.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleImportHelper.kt index 355934599b9..345d5213cb7 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleImportHelper.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleImportHelper.kt @@ -21,6 +21,8 @@ import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.util.GradleConstants +const val disableNotificationForProjectImport = true + fun runPartialGradleImport(project: Project) { getGradleProjectSettings(project).forEach { ExternalSystemUtil.refreshProject( @@ -51,6 +53,8 @@ private var Project.notificationPanel: ScriptConfigurationChangedNotification? by UserDataProperty(Key.create("load.script.configuration.panel")) fun scriptConfigurationsNeedToBeUpdated(project: Project) { + if (disableNotificationForProjectImport) return + if (autoReloadScriptConfigurations(project)) { // import should be run automatically by Gradle plugin return diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt index 11b93513757..379c02a9532 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt @@ -50,13 +50,6 @@ class GradleScriptListener(project: Project) : ScriptChangeListener(project) { } private fun checkUpToDate(vFile: VirtualFile) { - val upToDate = GradleBuildRootsManager.getInstance(project) - .getScriptInfo(vFile)?.model?.inputs?.isUpToDate(project, vFile) ?: return - - if (upToDate) { - scriptConfigurationsAreUpToDate(project) - } else { - scriptConfigurationsNeedToBeUpdated(project) - } + GradleBuildRootsManager.getInstance(project).checkUpToDate(vFile) } } \ No newline at end of file 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 67e2f43a170..30d693c4669 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 @@ -6,8 +6,6 @@ package org.jetbrains.kotlin.idea.scripting.gradle import com.intellij.icons.AllIcons -import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode -import com.intellij.openapi.externalSystem.util.ExternalSystemUtil import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key @@ -16,11 +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.legacy.GradleLegacyScriptConfigurationLoaderForOutOfProjectScripts -import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRoot import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsManager -import org.jetbrains.plugins.gradle.settings.GradleProjectSettings -import org.jetbrains.plugins.gradle.util.GradleConstants class MissingGradleScriptConfigurationNotificationProvider(private val project: Project) : EditorNotifications.Provider() { @@ -35,30 +29,18 @@ class MissingGradleScriptConfigurationNotificationProvider(private val project: scriptUnderRoot.isUnrelatedScript -> EditorNotificationPanel().apply { text(KotlinIdeaGradleBundle.message("text.the.associated.gradle.project.isn.t.imported")) - val linkProjectText = KotlinIdeaGradleBundle.message("action.label.text.load.script.configuration") - createActionLabel(linkProjectText) { - val newProjectSettings = GradleProjectSettings() - newProjectSettings.externalProjectPath = file.parent.path - ExternalSystemUtil.linkExternalProject( - GradleConstants.SYSTEM_ID, - newProjectSettings, - project, - { - - }, - false, - ProgressExecutionMode.IN_BACKGROUND_ASYNC - ) + createActionLabel(KotlinIdeaGradleBundle.message("action.text.standalone")) { + GradleBuildRootsManager.getInstance(project).addStandaloneScript(file) } - val link = createActionLabel("") {} - link.setIcon(AllIcons.General.ContextHelp) - link.setUseIconAsLink(true) - link.toolTipText = KotlinIdeaGradleBundle.message( - "tool.tip.text.the.external.gradle.project.needs.to.be.imported.to.get.this.script.analyzed", - linkProjectText + + 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.isNewScript -> EditorNotificationPanel().apply { + scriptUnderRoot.importRequired -> EditorNotificationPanel().apply { text(getMissingConfigurationNotificationText()) createActionLabel(getMissingConfigurationActionText()) { runPartialGradleImport(project) @@ -68,12 +50,6 @@ class MissingGradleScriptConfigurationNotificationProvider(private val project: } } - private val loaderForOutOfProjectScripts by lazy { - GradleLegacyScriptConfigurationLoaderForOutOfProjectScripts( - project - ) - } - companion object { private val KEY = Key.create("GradleScriptOutOfSourceNotification") } 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 81a1436d222..68e104f5c94 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 @@ -10,6 +10,8 @@ import com.intellij.openapi.diagnostic.logger class GradleBuildRootIndex { private val log = logger() + private val standaloneScriptRoots = mutableMapOf() + private val byWorkingDir = HashMap() private val byProjectDir = HashMap() @@ -24,6 +26,8 @@ class GradleBuildRootIndex { byProjectDir[it] = buildRoot } } + + standaloneScriptRoots.keys.forEach(::computeStandaloneScriptRoot) } @Synchronized @@ -43,6 +47,12 @@ class GradleBuildRootIndex { @Synchronized fun getBuildByProjectDir(projectDir: String) = byProjectDir[projectDir] + @Synchronized + fun isStandaloneScript(path: String) = path in standaloneScriptRoots + + @Synchronized + fun getStandaloneScriptRoot(path: String) = standaloneScriptRoots[path] + @Synchronized fun add(value: GradleBuildRoot.Linked): GradleBuildRoot.Linked? { val prefix = value.pathPrefix @@ -57,4 +67,20 @@ class GradleBuildRootIndex { rebuildProjectRoots() log.info("$prefix: removed") } + + @Synchronized + fun addStandaloneScript(path: String) { + computeStandaloneScriptRoot(path) + } + + var standaloneScripts: Collection + @Synchronized get() = standaloneScriptRoots.keys + @Synchronized set(value) { + standaloneScriptRoots.clear() + value.forEach(::computeStandaloneScriptRoot) + } + + private fun computeStandaloneScriptRoot(path: String) { + standaloneScriptRoots[path] = findNearestRoot(path) + } } \ 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 index 01eeac61aa1..3545e1affbb 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 @@ -32,12 +32,11 @@ abstract class GradleBuildRootsLocator { 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") + filePath.endsWith(".gradle.kts") fun isAffectedGradleProjectFile(filePath: String): Boolean = - findAffectedFileRoot(filePath) != null + findAffectedFileRoot(filePath) != null || + roots.isStandaloneScript(filePath) fun findAffectedFileRoot(filePath: String): GradleBuildRoot.Linked? { if (filePath.endsWith("/gradle.properties") || @@ -56,15 +55,17 @@ abstract class GradleBuildRootsLocator { class ScriptUnderRoot( val root: GradleBuildRoot?, - val script: GradleScriptInfo? = null + val script: GradleScriptInfo? = null, + val standalone: Boolean = false ) { val isUnrelatedScript: Boolean get() = root is GradleBuildRoot.Unlinked - val isNewScript: Boolean + val importRequired: Boolean get() = root is GradleBuildRoot.Linked && !isImported && - !root.importing + !root.importing && + !standalone private val isImported: Boolean get() = script != null @@ -92,6 +93,11 @@ abstract class GradleBuildRootsLocator { // other scripts: "included", "precompiled" scripts, scripts in unlinked projects, // or just random files with ".gradle.kts" ending + val standaloneScriptRoot = roots.getStandaloneScriptRoot(filePath) + if (standaloneScriptRoot != null) { + return ScriptUnderRoot(standaloneScriptRoot, standalone = true) + } + // todo(gradle6): remove, it is required only for projects with old gradle if (searchNearestLegacy) { val found = roots.findNearestRoot(filePath) 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 03bfa3132fd..0351aa7410d 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,6 +21,7 @@ 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.* @@ -69,6 +70,7 @@ 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 (roots.isStandaloneScript(file.path)) return false return true } @@ -79,8 +81,16 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), } } - // used in 201 - @Suppress("UNUSED") + @Suppress("MemberVisibilityCanBePrivate") // used in GradleImportHelper.kt.193 + fun checkUpToDate(file: VirtualFile) { + if (isConfigurationOutOfDate(file)) { + showNotificationForProjectImport(project) + } else { + hideNotificationForProjectImport(project) + } + } + + @Suppress("MemberVisibilityCanBePrivate") // used in GradleImportHelper.kt.201 fun isConfigurationOutOfDate(file: VirtualFile): Boolean { val script = getScriptInfo(file) ?: return false return !script.model.inputs.isUpToDate(project, file) @@ -158,6 +168,14 @@ 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) + } + init { getGradleProjectSettings(project).forEach { // don't call this.add, as we are inside scripting manager initialization @@ -298,15 +316,20 @@ class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), } } - private fun updateNotifications(dir1: String) { + @Suppress("MemberVisibilityCanBePrivate") + private fun updateNotifications(dir: String) { if (!project.isOpen) return val openedScripts = FileEditorManager.getInstance(project).openFiles.filter { - it.path.startsWith(dir1) && isGradleKotlinScript(it) + it.path.startsWith(dir) && maybeAffectedGradleProjectFile(it.path) } if (openedScripts.isEmpty()) return + openedScripts.forEach { + checkUpToDate(it) + } + GlobalScope.launch(EDT(project)) { if (project.isDisposed) return@launch 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 6882107aece..a4765fd0f66 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 @@ -18,7 +18,7 @@ class GradleBuildRootsLocatorTest : AbstractGradleBuildRootsLocatorTest() { newImportedGradleProject("imported", relativeScripts = listOf()) val scriptUnderRoot = findScriptBuildRoot("imported/build.gradle.kts") assertNotNull(scriptUnderRoot) - assertTrue(scriptUnderRoot.isNewScript) + assertTrue(scriptUnderRoot.importRequired) assertFalse(scriptUnderRoot.isUnrelatedScript) } @@ -27,7 +27,7 @@ class GradleBuildRootsLocatorTest : AbstractGradleBuildRootsLocatorTest() { newImportedGradleProject("imported", relativeScripts = listOf("build.gradle.kts")) val scriptUnderRoot = findScriptBuildRoot("imported/build.gradle.kts") assertNotNull(scriptUnderRoot) - assertFalse(scriptUnderRoot.isNewScript) + assertFalse(scriptUnderRoot.importRequired) assertFalse(scriptUnderRoot.isUnrelatedScript) } } \ No newline at end of file