gradle.kts: standalone scripts support (without ui and persistence)

This commit is contained in:
Sergey Rostov
2020-05-28 23:30:37 +03:00
parent 5ed7abd15d
commit d8892ced9d
8 changed files with 84 additions and 55 deletions
@@ -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.<br/>See how to find logs <a href="{0}">here</a>.
text.see.manual.installation.instructions=See manual installation instructions <a href="https://kotlinlang.org/docs/reference/using-gradle.html">here</a>.
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
@@ -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<Project, ScriptConfigurationChangedNotification>(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
@@ -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)
}
}
@@ -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<EditorNotificationPanel>() {
@@ -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<EditorNotificationPanel>("GradleScriptOutOfSourceNotification")
}
@@ -10,6 +10,8 @@ import com.intellij.openapi.diagnostic.logger
class GradleBuildRootIndex {
private val log = logger<GradleBuildRootIndex>()
private val standaloneScriptRoots = mutableMapOf<String, GradleBuildRoot.Linked?>()
private val byWorkingDir = HashMap<String, GradleBuildRoot.Linked>()
private val byProjectDir = HashMap<String, GradleBuildRoot.Linked>()
@@ -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<String>
@Synchronized get() = standaloneScriptRoots.keys
@Synchronized set(value) {
standaloneScriptRoots.clear()
value.forEach(::computeStandaloneScriptRoot)
}
private fun computeStandaloneScriptRoot(path: String) {
standaloneScriptRoots[path] = findNearestRoot(path)
}
}
@@ -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)
@@ -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
@@ -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)
}
}