Scripting setting: auto reloading of script dependencies is now per definition

This commit is contained in:
Natalia Selezneva
2020-05-28 18:26:30 +03:00
parent 8726d3dc4e
commit fd07d5f301
21 changed files with 323 additions and 134 deletions
@@ -17,6 +17,7 @@ import com.intellij.ui.HyperlinkLabel
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
object LoadScriptConfigurationNotificationFactory {
fun showNotification(file: VirtualFile, project: Project, onClick: () -> Unit): Boolean {
@@ -50,7 +51,7 @@ object LoadScriptConfigurationNotificationFactory {
return@withSelectedEditor
}
val panel = NewLoadConfigurationNotificationPanel(onClick, project)
val panel = NewLoadConfigurationNotificationPanel(onClick, project, this@addLoadConfigurationNotificationPanel)
notificationPanel = panel
manager.addTopComponent(this, panel)
}
@@ -72,7 +73,8 @@ object LoadScriptConfigurationNotificationFactory {
private class NewLoadConfigurationNotificationPanel(
var onClick: () -> Unit,
project: Project
project: Project,
file: VirtualFile
) : EditorNotificationPanel() {
init {
@@ -83,7 +85,9 @@ object LoadScriptConfigurationNotificationFactory {
createComponentActionLabel(KotlinIdeaCoreBundle.message("notification.action.text.enable.auto.reload")) {
onClick()
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
val scriptDefinition = file.findScriptDefinition(project) ?: return@createComponentActionLabel
KotlinScriptingSettings.getInstance(project).setAutoReloadConfigurations(scriptDefinition, true)
}
}
@@ -171,6 +171,11 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
}
fun reorderScriptDefinitions() = lock.write {
definitions?.forEach {
it.order = KotlinScriptingSettings.getInstance(project).getScriptDefinitionOrder(it)
}
definitions = definitions?.sortedBy { it.order }
updateDefinitions()
}
@@ -197,10 +202,6 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
private fun updateDefinitions() {
assert(lock.isWriteLocked) { "updateDefinitions should only be called under the write lock" }
definitions = definitions?.sortedBy {
KotlinScriptingSettings.getInstance(project).getScriptDefinitionOrder(it)
}
val fileTypeManager = FileTypeManager.getInstance()
val newExtensions = getKnownFilenameExtensions().filter {
@@ -18,6 +18,7 @@ import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
import org.jetbrains.kotlin.psi.UserDataProperty
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
@@ -45,7 +46,7 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
}
}
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project, this@addScriptDependenciesNotificationPanel)
notificationPanel = panel
manager.addTopComponent(this, panel)
}
@@ -81,7 +82,8 @@ private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel
private class NewScriptDependenciesNotificationPanel(
val onClick: () -> Unit,
val compilationConfigurationResult: ScriptCompilationConfigurationWrapper,
project: Project
project: Project,
file: VirtualFile
) : EditorNotificationPanel() {
init {
@@ -92,7 +94,9 @@ private class NewScriptDependenciesNotificationPanel(
createComponentActionLabel(KotlinIdeaCoreBundle.message("notification.action.text.enable.auto.reload")) {
onClick()
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
val scriptDefinition = file.findScriptDefinition(project) ?: return@createComponentActionLabel
KotlinScriptingSettings.getInstance(project).setAutoReloadConfigurations(scriptDefinition, true)
}
}
@@ -174,7 +174,7 @@ class DefaultScriptingSupport(manager: CompositeScriptConfigurationManager) : De
if (forceSync) {
loaders.firstOrNull { it.loadDependencies(isFirstLoad, file, scriptDefinition, loadingContext) }
} else {
val autoReloadEnabled = KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
val autoReloadEnabled = KotlinScriptingSettings.getInstance(project).autoReloadConfigurations(scriptDefinition)
val postponeLoading = isPostponedLoad && !autoReloadEnabled
if (postponeLoading) {
@@ -206,7 +206,8 @@ class DefaultScriptingSupport(manager: CompositeScriptConfigurationManager) : De
val applied = cached?.applied
if (applied != null && applied.inputs.isUpToDate(project, virtualFile)) {
// in case user reverted to applied configuration
suggestOrSaveConfiguration(virtualFile, applied, skipNotification = isLoadingPostponed)
val skipNotification = isLoadingPostponed || KotlinScriptingSettings.getInstance(project).autoReloadConfigurations(scriptDefinition)
suggestOrSaveConfiguration(virtualFile, applied, skipNotification)
} else if (cached == null || !cached.isUpToDate(project, virtualFile)) {
// don't start loading if nothing was changed
// (in case we checking for up-to-date and loading concurrently)
@@ -280,7 +281,6 @@ class DefaultScriptingSupport(manager: CompositeScriptConfigurationManager) : De
} else {
val autoReload = skipNotification
|| oldConfiguration == null
|| KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|| ApplicationManager.getApplication().isUnitTestModeWithoutScriptLoadingNotification
if (autoReload) {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
import org.jetbrains.kotlin.idea.core.script.debug
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@@ -43,7 +44,11 @@ open class DefaultScriptConfigurationLoader(val project: Project) : ScriptConfig
val result = getConfigurationThroughScriptingApi(ktFile, virtualFile, scriptDefinition)
context.suggestNewConfiguration(virtualFile, result)
if (KotlinScriptingSettings.getInstance(project).autoReloadConfigurations(scriptDefinition)) {
context.saveNewConfiguration(virtualFile, result)
} else {
context.suggestNewConfiguration(virtualFile, result)
}
return true
}
@@ -14,7 +14,7 @@ interface ScriptConfigurationLoadingContext {
/**
* Show notification about new configuration with suggestion to apply it.
* User may disable this notifications, in this case configuration will be saved immediately.
* User may disable this notifications, in this case [saveNewConfiguration] should be called
*
* If configuration is null, then the result will be treated as failed, and
* reports will be displayed immediately.
@@ -10,9 +10,11 @@ import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.project.Project
import com.intellij.util.addOptionTag
import com.intellij.util.attribute
import com.intellij.util.getAttributeBooleanValue
import org.jdom.Element
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings.KotlinScriptDefinitionValue.Companion.DEFAULT
import org.jetbrains.kotlin.idea.util.application.executeOnPooledThread
import org.jetbrains.kotlin.idea.util.application.getServiceSafe
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@@ -20,8 +22,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
name = "KotlinScriptingSettings",
storages = [Storage("kotlinScripting.xml")]
)
class KotlinScriptingSettings : PersistentStateComponent<Element> {
var isAutoReloadEnabled = false
class KotlinScriptingSettings(private val project: Project) : PersistentStateComponent<Element> {
/**
* true if notification about multiple script definition applicable for one script file is suppressed
@@ -33,13 +34,6 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
override fun getState(): Element {
val definitionsRootElement = Element("KotlinScriptingSettings")
if (isAutoReloadEnabled) {
definitionsRootElement.addOptionTag(
KotlinScriptingSettings::isAutoReloadEnabled.name,
isAutoReloadEnabled.toString()
)
}
if (suppressDefinitionsCheck) {
definitionsRootElement.addOptionTag(
KotlinScriptingSettings::suppressDefinitionsCheck.name,
@@ -59,9 +53,6 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
}
override fun loadState(state: Element) {
state.getOptionTag(KotlinScriptingSettings::isAutoReloadEnabled.name)?.let {
isAutoReloadEnabled = it
}
state.getOptionTag(KotlinScriptingSettings::suppressDefinitionsCheck.name)?.let {
suppressDefinitionsCheck = it
}
@@ -70,29 +61,63 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
for (scriptDefinitionElement in scriptDefinitionsList) {
scriptDefinitions[scriptDefinitionElement.toKey()] = scriptDefinitionElement.toValue()
}
if (scriptDefinitionsList.isNotEmpty()) {
executeOnPooledThread {
ScriptDefinitionsManager.getInstance(project).reorderScriptDefinitions()
}
}
}
fun setOrder(scriptDefinition: ScriptDefinition, order: Int) {
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(order = order) ?:
KotlinScriptDefinitionValue(order)
scriptDefinitions[scriptDefinition.toKey()] =
scriptDefinitions[scriptDefinition.toKey()]?.copy(order = order) ?: KotlinScriptDefinitionValue(order)
}
fun setEnabled(scriptDefinition: ScriptDefinition, isEnabled: Boolean) {
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?:
KotlinScriptDefinitionValue(scriptDefinitions.size, isEnabled)
scriptDefinitions[scriptDefinition.toKey()] =
scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?: KotlinScriptDefinitionValue(
scriptDefinition.order,
isEnabled = isEnabled
)
}
fun setAutoReloadConfigurations(scriptDefinition: ScriptDefinition, autoReloadScriptDependencies: Boolean) {
scriptDefinitions[scriptDefinition.toKey()] =
scriptDefinitions[scriptDefinition.toKey()]?.copy(autoReloadConfigurations = autoReloadScriptDependencies)
?: KotlinScriptDefinitionValue(
scriptDefinition.order,
autoReloadConfigurations = autoReloadScriptDependencies
)
}
fun getScriptDefinitionOrder(scriptDefinition: ScriptDefinition): Int {
return scriptDefinitions[scriptDefinition.toKey()]?.order ?: Integer.MAX_VALUE
return scriptDefinitions[scriptDefinition.toKey()]?.order ?: DEFAULT.order
}
fun isScriptDefinitionEnabled(scriptDefinition: ScriptDefinition): Boolean {
return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: true
return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: DEFAULT.isEnabled
}
private data class KotlinScriptDefinitionKey(val definitionName: String, val className: String)
private data class KotlinScriptDefinitionValue(val order: Int, val isEnabled: Boolean = true)
fun autoReloadConfigurations(scriptDefinition: ScriptDefinition): Boolean {
return scriptDefinitions[scriptDefinition.toKey()]?.autoReloadConfigurations ?: DEFAULT.autoReloadConfigurations
}
private data class KotlinScriptDefinitionKey(
val definitionName: String,
val className: String
)
private data class KotlinScriptDefinitionValue(
val order: Int,
val isEnabled: Boolean = true,
val autoReloadConfigurations: Boolean = false
) {
companion object {
val DEFAULT = KotlinScriptDefinitionValue(Integer.MAX_VALUE)
}
}
private fun Element.toKey() = KotlinScriptDefinitionKey(
getAttributeValue(KotlinScriptDefinitionKey::definitionName.name),
@@ -104,8 +129,8 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
private fun Element.addScriptDefinitionContentElement(definition: KotlinScriptDefinitionKey, settings: KotlinScriptDefinitionValue) {
addElement(SCRIPT_DEFINITION_TAG).apply {
attribute(KotlinScriptDefinitionKey::className.name, definition.className)
attribute(KotlinScriptDefinitionKey::definitionName.name, definition.definitionName)
setAttribute(KotlinScriptDefinitionKey::className.name, definition.className)
setAttribute(KotlinScriptDefinitionKey::definitionName.name, definition.definitionName)
addElement(KotlinScriptDefinitionValue::order.name).apply {
text = settings.order.toString()
@@ -116,6 +141,11 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
text = settings.isEnabled.toString()
}
}
if (settings.autoReloadConfigurations) {
addElement(KotlinScriptDefinitionValue::autoReloadConfigurations.name).apply {
text = settings.autoReloadConfigurations.toString()
}
}
}
}
@@ -126,10 +156,14 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
}
private fun Element.toValue(): KotlinScriptDefinitionValue {
val order = getChildText(KotlinScriptDefinitionValue::order.name)?.toInt() ?: Integer.MAX_VALUE
val isEnabled = getChildText(KotlinScriptDefinitionValue::isEnabled.name)?.toBoolean() ?: true
val order = getChildText(KotlinScriptDefinitionValue::order.name)?.toInt()
?: DEFAULT.order
val isEnabled = getChildText(KotlinScriptDefinitionValue::isEnabled.name)?.toBoolean()
?: DEFAULT.isEnabled
val autoReloadScriptDependencies = getChildText(KotlinScriptDefinitionValue::autoReloadConfigurations.name)?.toBoolean()
?: DEFAULT.autoReloadConfigurations
return KotlinScriptDefinitionValue(order, isEnabled)
return KotlinScriptDefinitionValue(order, isEnabled, autoReloadScriptDependencies)
}
private fun Element.getOptionTag(name: String) =
@@ -34,11 +34,28 @@ fun runPartialGradleImport(project: Project) {
fun getMissingConfigurationNotificationText() = KotlinIdeaGradleBundle.message("script.configurations.will.be.available.after.import")
fun getMissingConfigurationActionText() = KotlinIdeaGradleBundle.message("action.label.import.project")
fun autoReloadScriptConfigurations(project: Project): Boolean {
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
val projectSettings = gradleSettings.getLinkedProjectsSettings()
.filterIsInstance<GradleProjectSettings>()
.firstOrNull()
if (projectSettings != null) {
return projectSettings.isUseAutoImport
}
return false
}
private const 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) {
fun scriptConfigurationsNeedToBeUpdated(project: Project) {
if (autoReloadScriptConfigurations(project)) {
// import should be run automatically by Gradle plugin
return
}
val existingPanel = project.notificationPanel
if (existingPanel != null) {
return
@@ -56,7 +73,7 @@ fun showNotificationForProjectImport(project: Project) {
notification.notify(project)
}
fun hideNotificationForProjectImport(project: Project): Boolean {
fun scriptConfigurationsAreUpToDate(project: Project): Boolean {
if (project.notificationPanel == null) return false
project.notificationPanel?.expire()
return true
@@ -16,10 +16,12 @@ import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
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.openapi.vfs.VirtualFile
import com.intellij.testFramework.LightVirtualFileBase
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModelResolver
import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsManager
import org.jetbrains.plugins.gradle.service.project.GradlePartialResolverPolicy
@@ -40,9 +42,21 @@ fun runPartialGradleImport(project: Project) {
fun getMissingConfigurationNotificationText() = KotlinIdeaGradleBundle.message("script.configurations.will.be.available.after.load.changes")
fun getMissingConfigurationActionText() = KotlinIdeaGradleBundle.message("action.text.load.script.configurations")
fun showNotificationForProjectImport(project: Project) {}
fun autoReloadScriptConfigurations(project: Project): Boolean {
return GradleScriptDefinitionsContributor.getDefinitions(project).any {
KotlinScriptingSettings.getInstance(project).autoReloadConfigurations(it)
}
}
fun hideNotificationForProjectImport(project: Project): Boolean = true
fun scriptConfigurationsNeedToBeUpdated(project: Project) {
if (autoReloadScriptConfigurations(project)) {
runPartialGradleImport(project)
} else {
// notification is shown in LoadConfigurationAction
}
}
fun scriptConfigurationsAreUpToDate(project: Project): Boolean = true
class LoadConfigurationAction : AnAction(
KotlinIdeaGradleBundle.message("action.text.load.script.configurations"),
@@ -56,29 +70,37 @@ class LoadConfigurationAction : AnAction(
override fun update(e: AnActionEvent) {
ensureValidActionVisibility(e)
e.presentation.description = KotlinIdeaGradleBundle.message("action.description.load.script.configurations")
}
private fun ensureValidActionVisibility(e: AnActionEvent) {
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
e.presentation.isVisible = getNotificationVisibility(editor)
}
private fun getNotificationVisibility(editor: Editor): Boolean {
if (DiffUtil.isDiffEditor(editor)) {
e.presentation.isVisible = false
return
return false
}
e.presentation.isVisible = editor.getNotificationVisibility()
val project = editor.project ?: return false
val file = getKotlinScriptFile(editor) ?: return false
if (autoReloadScriptConfigurations(project)) {
return false
}
return GradleBuildRootsManager.getInstance(project).isConfigurationOutOfDate(file)
}
private fun Editor.getNotificationVisibility(): Boolean {
val project = project ?: return false
val documentManager = FileDocumentManager.getInstance()
val virtualFile = documentManager.getFile(document)
if (virtualFile is LightVirtualFileBase) return false
if (virtualFile == null || !virtualFile.isValid) return false
return GradleBuildRootsManager.getInstance(project).isConfigurationOutOfDate(virtualFile)
}
private fun getKotlinScriptFile(editor: Editor): VirtualFile? {
return FileDocumentManager.getInstance()
.getFile(editor.document)
?.takeIf {
it !is LightVirtualFileBase
&& it.isValid
&& it.fileType != KotlinFileType.INSTANCE
&& isGradleKotlinScript(it)
}
}
}
@@ -0,0 +1,30 @@
/*
* 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 org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
class GradleKotlinScriptDefinitionWrapper(
hostConfiguration: ScriptingHostConfiguration,
legacyDefinition: KotlinScriptDefinitionFromAnnotatedTemplate,
gradleVersion: String,
) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition) {
override val compilationConfiguration by lazy {
ScriptCompilationConfigurationFromDefinition(
hostConfiguration,
legacyDefinition
).with {
ScriptCompilationConfiguration.ide.acceptedLocations.put(listOf(ScriptAcceptedLocation.Project))
}
}
override val canAutoReloadScriptConfigurationsBeSwitchedOff = !kotlinDslScriptsModelImportSupported(gradleVersion)
override val canDefinitionBeSwitchedOff: Boolean = false
}
@@ -0,0 +1,30 @@
/*
* 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 org.jetbrains.kotlin.scripting.definitions.ScriptCompilationConfigurationFromDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.location.ScriptExpectedLocation
class GradleKotlinScriptDefinitionWrapper(
hostConfiguration: ScriptingHostConfiguration,
legacyDefinition: KotlinScriptDefinitionFromAnnotatedTemplate,
gradleVersion: String,
) : ScriptDefinition.FromLegacy(hostConfiguration, legacyDefinition) {
override val compilationConfiguration by lazy {
ScriptCompilationConfigurationFromDefinition(
hostConfiguration,
legacyDefinition
).with {
ScriptCompilationConfiguration.ide.acceptedLocations.put(listOf(ScriptAcceptedLocation.Project))
}
}
override val canDefinitionBeSwitchedOff: Boolean = false
}
@@ -32,13 +32,12 @@ import java.util.concurrent.atomic.AtomicBoolean
import kotlin.reflect.KClass
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.experimental.dependencies.ScriptReport
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class GradleScriptDefinitionsContributor(private val project: Project) : ScriptDefinitionSourceAsContributor {
@@ -232,17 +231,12 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
additionalResolverClasspath(gradleLibDir)
).map {
it.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.let { legacyDef ->
@Suppress("DEPRECATION")
if (legacyDef.scriptExpectedLocations.contains(ScriptExpectedLocation.Project)) null
else {
// Expand scope for old gradle script definition
ScriptDefinition.FromLegacy(
it.hostConfiguration,
GradleKotlinScriptDefinitionFromAnnotatedTemplate(
legacyDef
)
)
}
// Expand scope for old gradle script definition
GradleKotlinScriptDefinitionWrapper(
it.hostConfiguration,
legacyDef,
projectSettings.resolveGradleVersion().version
)
} ?: it
}
}
@@ -306,10 +300,3 @@ class GradleScriptDefinitionsContributor(private val project: Project) : ScriptD
}
}
class GradleKotlinScriptDefinitionFromAnnotatedTemplate(
base: KotlinScriptDefinitionFromAnnotatedTemplate
) : KotlinScriptDefinitionFromAnnotatedTemplate(base.template, base.environment, base.templateClasspath) {
@Suppress("DEPRECATION")
override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = listOf(ScriptExpectedLocation.Project)
}
@@ -54,9 +54,9 @@ class GradleScriptListener(project: Project) : ScriptChangeListener(project) {
.getScriptInfo(vFile)?.model?.inputs?.isUpToDate(project, vFile) ?: return
if (upToDate) {
hideNotificationForProjectImport(project)
scriptConfigurationsAreUpToDate(project)
} else {
showNotificationForProjectImport(project)
scriptConfigurationsNeedToBeUpdated(project)
}
}
}
@@ -182,7 +182,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
fun markImportingInProgress(workingDir: String, inProgress: Boolean = true) {
actualizeBuildRoot(workingDir)?.importing = inProgress
updateNotifications(workingDir)
hideNotificationForProjectImport(project)
scriptConfigurationsAreUpToDate(project)
}
fun update(build: KotlinDslGradleBuildSync) {
@@ -206,7 +206,7 @@ class GradleBuildRootsManager(val project: Project) : ScriptingSupport() {
add(newSupport)
hideNotificationForProjectImport(project)
scriptConfigurationsAreUpToDate(project)
}
private fun merge(old: GradleBuildRootData, new: GradleBuildRootData): GradleBuildRootData {
@@ -2203,6 +2203,8 @@ action.usage.update.text=Usage update
progress.title.analyze.extraction.data=Analyze extraction data...
fix.move.file.to.package.dir.name.text=source root
move.refactoring.error.text.cannot.perform.refactoring.since.the.following.files.already.exist=Cannot perform refactoring since the following files already exist:\n\n
kotlin.script.definitions.model.name.autoReloadScriptDependencies.description=Enable auto reload if you want to load script configurations automatically on file change
kotlin.script.definitions.model.name.autoReloadScriptDependencies=Auto Reload
kotlin.script.definitions.model.name.is.enabled=Is Enabled
kotlin.script.definitions.model.name.pattern.extension=Pattern/Extension
kotlin.script.definitions.model.name.name=Name
@@ -11,14 +11,24 @@ import com.intellij.internal.statistic.service.fus.collectors.ProjectUsagesColle
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.codeInsight.KotlinCodeInsightSettings
import org.jetbrains.kotlin.idea.codeInsight.KotlinCodeInsightWorkspaceSettings
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
class IDESettingsFUSCollector : ProjectUsagesCollector() {
override fun getMetrics(project: Project): Set<MetricEvent> {
val metrics = mutableSetOf<MetricEvent>()
val scriptingAutoReloadEnabled = KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
metrics.add(MetricEvent("scriptingAutoReloadEnabled", flagUsage(scriptingAutoReloadEnabled)))
for (definition in ScriptDefinitionsManager.getInstance(project).getAllDefinitions()) {
if (definition.canAutoReloadScriptConfigurationsBeSwitchedOff) {
val scriptingAutoReloadEnabled = KotlinScriptingSettings.getInstance(project).autoReloadConfigurations(definition)
val data = FeatureUsageData()
.addData("enabled", scriptingAutoReloadEnabled)
.addData("definition_name", definition.name)
.addData("pluginVersion", KotlinPluginUtil.getPluginVersion())
metrics.add(MetricEvent("scriptingAutoReloadEnabled", data))
}
}
val settings: KotlinCodeInsightSettings = KotlinCodeInsightSettings.getInstance()
val projectSettings: KotlinCodeInsightWorkspaceSettings = KotlinCodeInsightWorkspaceSettings.getInstance(project)
@@ -10,19 +10,27 @@ import com.intellij.ui.BooleanTableCellRenderer
import com.intellij.util.ui.ColumnInfo
import com.intellij.util.ui.ListTableModel
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
import org.jetbrains.kotlin.idea.core.script.configuration.CompositeScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import javax.swing.JTable
class KotlinScriptDefinitionsModelDescriptor(val definition: ScriptDefinition, var isEnabled: Boolean)
class ModelDescriptor(
val definition: ScriptDefinition,
var isEnabled: Boolean,
var autoReloadConfigurations: Boolean
)
class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<KotlinScriptDefinitionsModelDescriptor>) :
ListTableModel<KotlinScriptDefinitionsModelDescriptor>(
class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<ModelDescriptor>) :
ListTableModel<ModelDescriptor>(
arrayOf(
ScriptDefinitionName(),
ScriptDefinitionPattern(),
ScriptDefinitionIsEnabled()
ScriptDefinitionIsEnabled(),
ScriptDefinitionAutoReloadConfigurations()
),
definitions,
0
@@ -30,19 +38,25 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
fun getDefinitions() = items.map { it.definition }
fun setDefinitions(definitions: List<ScriptDefinition>, settings: KotlinScriptingSettings) {
items = definitions.mapTo(arrayListOf()) { KotlinScriptDefinitionsModelDescriptor(it, settings.isScriptDefinitionEnabled(it)) }
items = definitions.mapTo(arrayListOf()) {
ModelDescriptor(
it,
settings.isScriptDefinitionEnabled(it),
settings.autoReloadConfigurations(it)
)
}
}
private class ScriptDefinitionName : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, String>(
private class ScriptDefinitionName : ColumnInfo<ModelDescriptor, String>(
KotlinBundle.message("kotlin.script.definitions.model.name.name")
) {
override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor) = item.definition.name
override fun valueOf(item: ModelDescriptor) = item.definition.name
}
private class ScriptDefinitionPattern : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, String>(
private class ScriptDefinitionPattern : ColumnInfo<ModelDescriptor, String>(
KotlinBundle.message("kotlin.script.definitions.model.name.pattern.extension")
) {
override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor): String {
override fun valueOf(item: ModelDescriptor): String {
val definition = item.definition
return definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()?.scriptFilePattern?.pattern
?: (definition as? ScriptDefinition.FromConfigurations)?.filePathPattern
@@ -50,27 +64,47 @@ class KotlinScriptDefinitionsModel private constructor(definitions: MutableList<
}
}
private class ScriptDefinitionIsEnabled : ColumnInfo<KotlinScriptDefinitionsModelDescriptor, Boolean>(
private class ScriptDefinitionIsEnabled : BooleanColumn(
KotlinBundle.message("kotlin.script.definitions.model.name.is.enabled")
) {
override fun valueOf(item: KotlinScriptDefinitionsModelDescriptor): Boolean = item.isEnabled
override fun setValue(item: KotlinScriptDefinitionsModelDescriptor, value: Boolean) {
override fun valueOf(item: ModelDescriptor): Boolean = item.isEnabled
override fun setValue(item: ModelDescriptor, value: Boolean) {
item.isEnabled = value
}
override fun getEditor(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellEditor()
override fun getRenderer(item: KotlinScriptDefinitionsModelDescriptor?) = BooleanTableCellRenderer()
override fun isCellEditable(item: KotlinScriptDefinitionsModelDescriptor) =
item.definition.asLegacyOrNull<StandardIdeScriptDefinition>() == null
override fun isCellEditable(item: ModelDescriptor): Boolean {
return item.definition.asLegacyOrNull<StandardIdeScriptDefinition>() == null
&& item.definition.canDefinitionBeSwitchedOff
}
}
private class ScriptDefinitionAutoReloadConfigurations : BooleanColumn(
KotlinBundle.message("kotlin.script.definitions.model.name.autoReloadScriptDependencies")
) {
override fun valueOf(item: ModelDescriptor): Boolean = item.autoReloadConfigurations
override fun setValue(item: ModelDescriptor, value: Boolean) {
item.autoReloadConfigurations = value
}
override fun isCellEditable(item: ModelDescriptor): Boolean {
return item.definition.canAutoReloadScriptConfigurationsBeSwitchedOff
}
}
companion object {
fun createModel(definitions: List<ScriptDefinition>, settings: KotlinScriptingSettings): KotlinScriptDefinitionsModel =
KotlinScriptDefinitionsModel(definitions.mapTo(arrayListOf()) {
KotlinScriptDefinitionsModelDescriptor(
ModelDescriptor(
it,
settings.isScriptDefinitionEnabled(it)
settings.isScriptDefinitionEnabled(it),
settings.autoReloadConfigurations(it)
)
})
}
}
private abstract class BooleanColumn(message: String) : ColumnInfo<ModelDescriptor, Boolean>(message) {
override fun getEditor(item: ModelDescriptor?) = BooleanTableCellEditor()
override fun getRenderer(item: ModelDescriptor?) = BooleanTableCellRenderer()
override fun getWidth(table: JTable?): Int = 90
}
@@ -8,7 +8,7 @@
<properties/>
<border type="none"/>
<children>
<grid id="6126a" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="6126a" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -18,13 +18,13 @@
<children>
<vspacer id="7a19">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="b9ce9" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="b9ce9" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<clientProperties>
@@ -47,16 +47,16 @@
</vspacer>
</children>
</grid>
<component id="54c9d" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/KotlinBundle" key="kotlin.script.definitions.model.name.autoReloadScriptDependencies.description"/>
</properties>
</component>
</children>
</grid>
<component id="cc285" class="javax.swing.JCheckBox" binding="scriptDependenciesAutoReload">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/KotlinBundle" key="reload.script.dependencies.on.file.change"/>
</properties>
</component>
</children>
</grid>
</children>
@@ -25,7 +25,6 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab
private JPanel root;
private JPanel panelScriptDefinitionsChooser;
private JCheckBox scriptDependenciesAutoReload;
private KotlinScriptDefinitionsModel model;
@@ -55,19 +54,18 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab
@Override
public boolean isModified() {
return isModified(scriptDependenciesAutoReload, settings.isAutoReloadEnabled())
|| isScriptDefinitionsChanged();
return isScriptDefinitionsChanged();
}
@Override
public void apply() {
settings.setAutoReloadEnabled(scriptDependenciesAutoReload.isSelected());
if (isScriptDefinitionsChanged()) {
for (KotlinScriptDefinitionsModelDescriptor item : model.getItems()) {
for (ModelDescriptor item : model.getItems()) {
ScriptDefinition definition = item.getDefinition();
settings.setOrder(definition, model.getItems().indexOf(item));
int order = model.getItems().indexOf(item);
settings.setOrder(definition, order);
settings.setEnabled(definition, item.isEnabled());
settings.setAutoReloadConfigurations(definition, item.getAutoReloadConfigurations());
}
manager.reorderScriptDefinitions();
@@ -76,16 +74,17 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab
@Override
public void reset() {
scriptDependenciesAutoReload.setSelected(settings.isAutoReloadEnabled());
model.setDefinitions(manager.getAllDefinitions(), settings);
}
private boolean isScriptDefinitionsChanged() {
for (KotlinScriptDefinitionsModelDescriptor item : model.getItems()) {
for (ModelDescriptor item : model.getItems()) {
if (settings.isScriptDefinitionEnabled(item.getDefinition()) != item.isEnabled()) {
return true;
}
if (settings.autoReloadConfigurations(item.getDefinition()) != item.getAutoReloadConfigurations()) {
return true;
}
}
return !model.getDefinitions().equals(manager.getAllDefinitions());
}
@@ -20,17 +20,20 @@ class ScriptOptionsSaveTest : KotlinLightCodeInsightFixtureTestCase() {
fun testSaveAutoReload() {
val project = myFixture.project
val settings = KotlinScriptingSettings.getInstance(project)
val initialAutoReload = settings.isAutoReloadEnabled
val definition = ScriptDefinitionsManager.getInstance(project).getAllDefinitions().first()
val initialAutoReload = settings.autoReloadConfigurations(definition)
settings.isAutoReloadEnabled = !initialAutoReload
settings.setAutoReloadConfigurations(definition, !initialAutoReload)
assertEquals(
"isAutoReloadEnabled should be set to true",
"<KotlinScriptingSettings><option name=\"isAutoReloadEnabled\" value=\"true\" /></KotlinScriptingSettings>",
"true",
XMLOutputter().outputString(settings.state)
.substringAfter("<autoReloadConfigurations>")
.substringBefore("</autoReloadConfigurations>")
)
settings.isAutoReloadEnabled = initialAutoReload
settings.setAutoReloadConfigurations(definition, initialAutoReload)
}
fun testSaveScriptDefinitionOff() {
@@ -45,8 +48,10 @@ class ScriptOptionsSaveTest : KotlinLightCodeInsightFixtureTestCase() {
assertEquals(
"scriptDefinition should be off",
"<KotlinScriptingSettings><scriptDefinition><order>0</order><isEnabled>false</isEnabled></scriptDefinition></KotlinScriptingSettings>",
XMLOutputter().outputString(settings.state).replace("scriptDefinition .*\">".toRegex(), "scriptDefinition>")
"false",
XMLOutputter().outputString(settings.state)
.substringAfter("<isEnabled>")
.substringBefore("</isEnabled>")
)
settings.setEnabled(scriptDefinition, initialIsEnabled)
@@ -41,6 +41,11 @@ abstract class ScriptDefinition : UserDataHolderBase() {
open val isDefault = false
// Store IDE-related settings in script definition
var order: Int = Integer.MAX_VALUE
open val canAutoReloadScriptConfigurationsBeSwitchedOff: Boolean get() = true
open val canDefinitionBeSwitchedOff: Boolean get() = true
abstract val baseClassType: KotlinType
abstract val compilerOptions: Iterable<String>
abstract val annotationsForSamWithReceivers: List<String>
@@ -59,7 +64,7 @@ abstract class ScriptDefinition : UserDataHolderBase() {
override val legacyDefinition: KotlinScriptDefinition
) : ScriptDefinition() {
override val compilationConfiguration by lazy {
override val compilationConfiguration: ScriptCompilationConfiguration by lazy {
ScriptCompilationConfigurationFromDefinition(
hostConfiguration,
legacyDefinition