Implement global notification for configurations update of gradle scripts

For 193: it will be a simple notification at the bottom of the IDEA window
For 201: a floating notification in the editor is implemented near "Load Gradle Changes" icon
^KT-34443 Fixed
This commit is contained in:
Natalia Selezneva
2020-03-18 10:47:56 +03:00
parent e300b30de1
commit a35523b6d0
11 changed files with 213 additions and 12 deletions
@@ -53,3 +53,5 @@ notification.title.script.configuration.has.been.changed=Script configurations h
notification.text.script.configuration.has.been.changed=Gradle Project needs to be imported to load changes.
action.label.import.project=Import Project
action.label.enable.auto.import=Enable Auto-Import
action.text.load.script.configurations=Load Script Configurations
action.description.load.script.configurations=Gradle Kotlin DSL script configuration has been changed. Load changes to get script code insight without loading the external Gradle project.
@@ -7,10 +7,17 @@
package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.notification.*
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
@@ -25,4 +32,77 @@ fun runPartialGradleImport(project: Project) {
ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
.build()
)
}
private val kotlinDslNotificationGroupId = "Gradle Kotlin DSL Scripts"
private var Project.notificationPanel: ScriptConfigurationChangedNotification?
by UserDataProperty<Project, ScriptConfigurationChangedNotification>(Key.create("load.script.configuration.panel"))
fun showNotificationForProjectImport(project: Project, callback: () -> Unit) {
val existingPanel = project.notificationPanel
if (existingPanel != null) {
existingPanel.update(callback)
return
}
val notificationGroup = NotificationGroup.findRegisteredGroup(kotlinDslNotificationGroupId)
if (notificationGroup == null) {
NotificationsConfiguration.getNotificationsConfiguration().register(
kotlinDslNotificationGroupId, NotificationDisplayType.STICKY_BALLOON, false
)
}
val notification = ScriptConfigurationChangedNotification(project, callback)
project.notificationPanel = notification
notification.notify(project)
}
fun hideNotificationForProjectImport(project: Project): Boolean {
if (project.notificationPanel == null) return false
project.notificationPanel?.expire()
return true
}
private class ScriptConfigurationChangedNotification(val project: Project, onClick: () -> Unit) :
Notification(
kotlinDslNotificationGroupId,
KotlinIcons.LOAD_SCRIPT_CONFIGURATION,
KotlinIdeaGradleBundle.message("notification.title.script.configuration.has.been.changed"),
null,
KotlinIdeaGradleBundle.message("notification.text.script.configuration.has.been.changed"),
NotificationType.INFORMATION,
null
) {
init {
//NotificationGroup.findRegisteredGroup(s) ?: NotificationGroup.balloonGroup(s)
addAction(LoadConfigurationAction(onClick))
addAction(NotificationAction.createSimple(KotlinIdeaGradleBundle.message("action.label.enable.auto.import")) {
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
val projectSettings = gradleSettings.getLinkedProjectsSettings()
.filterIsInstance<GradleProjectSettings>()
.firstOrNull()
if (projectSettings != null) {
projectSettings.isUseAutoImport = true
}
})
}
fun update(onClick: () -> Unit) {
actions.filterIsInstance<LoadConfigurationAction>().forEach { it.onClick = onClick }
}
override fun expire() {
super.expire()
project.notificationPanel = null
}
private class LoadConfigurationAction(var onClick: () -> Unit) : AnAction(KotlinIdeaGradleBundle.message("action.label.import.project")) {
override fun actionPerformed(e: AnActionEvent) {
onClick()
}
}
}
@@ -7,13 +7,29 @@
package org.jetbrains.kotlin.idea.scripting.gradle
import com.intellij.diff.util.DiffUtil
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.testFramework.LightVirtualFileBase
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModelResolver
import org.jetbrains.plugins.gradle.service.project.GradleIncrementalResolverPolicy
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.plugins.gradle.service.project.GradlePartialResolverPolicy
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverExtension
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.util.function.Predicate
fun runPartialGradleImport(project: Project) {
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
@@ -25,9 +41,62 @@ fun runPartialGradleImport(project: Project) {
projectSettings.externalProjectPath,
ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
.projectResolverPolicy(
GradleIncrementalResolverPolicy {
it is KotlinDslScriptModelResolver
}
GradlePartialResolverPolicy(Predicate<GradleProjectResolverExtension?> { it is KotlinDslScriptModelResolver })
)
)
}
private var Project.notificationPanel: Boolean?
by UserDataProperty<Project, Boolean>(Key.create("load.script.configuration.panel"))
fun showNotificationForProjectImport(project: Project, callback: () -> Unit) {
project.notificationPanel = true
(ActionManager.getInstance().getAction("LoadConfigurationAction") as LoadConfigurationAction).onClick = callback
}
fun hideNotificationForProjectImport(project: Project): Boolean {
project.notificationPanel = false
return true
}
class LoadConfigurationAction : AnAction(
KotlinIdeaGradleBundle.message("action.text.load.script.configurations"),
KotlinIdeaGradleBundle.message("action.description.load.script.configurations"),
KotlinIcons.LOAD_SCRIPT_CONFIGURATION
) {
var onClick = {}
override fun actionPerformed(e: AnActionEvent) {
onClick()
}
override fun update(e: AnActionEvent) {
ensureValidActionVisibility(e)
e.presentation.description = KotlinIdeaGradleBundle.message("action.description.load.script.configurations")
}
private fun ensureValidActionVisibility(e: AnActionEvent) {
if (e.project?.notificationPanel != true) {
e.presentation.isVisible = false
return
}
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
when {
DiffUtil.isDiffEditor(editor) -> e.presentation.isVisible = false
!editor.isScriptEditor() -> e.presentation.isVisible = false
}
}
private fun Editor.isScriptEditor(): Boolean {
val documentManager = FileDocumentManager.getInstance()
val virtualFile = documentManager.getFile(document)
if (virtualFile is LightVirtualFileBase) return false
if (virtualFile == null || !virtualFile.isValid) return false
val project = project ?: return false
return virtualFile.findScriptDefinition(project) != null
}
}
@@ -0,0 +1,32 @@
/*
* 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
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationNotificationFactory
class GradleScriptConfigurationNotificationFactory : ScriptConfigurationNotificationFactory {
override fun showNotification(file: VirtualFile, project: Project, onClick: () -> Unit): Boolean {
if (isGradleKotlinScript(file) && isGradleImportCanBeUsed(project)) {
showNotificationForProjectImport(project, onClick)
return true
}
return false
}
override fun hideNotification(file: VirtualFile, project: Project): Boolean {
return hideNotificationForProjectImport(project)
}
private fun isGradleImportCanBeUsed(project: Project): Boolean {
val gradleVersion = getGradleVersion(project)
if (gradleVersion != null && kotlinDslScriptsModelImportSupported(gradleVersion)) {
return true
}
return false
}
}