From 54d96a2dd7437f8eb9e1e598fbf168a434ae4da8 Mon Sep 17 00:00:00 2001 From: Natalia Selezneva Date: Wed, 5 Aug 2020 15:21:19 +0200 Subject: [PATCH] UI for standalone gradle scripts ^KT-39790 Fixed --- .../KotlinIdeaGradleBundle.properties | 3 + .../gradle/roots/GradleBuildRootIndex.kt | 57 ++++--- .../gradle/roots/GradleBuildRootsLocator.kt | 5 +- .../gradle/roots/GradleBuildRootsManager.kt | 2 +- .../gradle/roots/StandaloneScriptsUpdater.kt | 2 +- .../GradleScriptingSettingsProvider.kt | 142 ++++++++++++++++++ .../settings/KotlinStandaloneScriptsModel.kt | 28 ++++ .../settings/StandaloneScriptsStorage.kt | 33 ++++ .../AbstractGradleBuildRootsLocatorTest.kt | 22 ++- .../messages/KotlinBundle.properties | 1 + idea/resources/META-INF/gradle.xml | 3 + idea/resources/META-INF/gradle.xml.193 | 2 + idea/resources/META-INF/scripting-support.xml | 4 + .../KotlinScriptingSettingsConfigurable.form | 59 ++------ .../KotlinScriptingSettingsConfigurable.java | 57 ++++++- ...tlinScriptingSettingsConfigurable.java.193 | 55 ++++++- ...criptingSupportSpecificSettingsProvider.kt | 23 +++ 17 files changed, 412 insertions(+), 86 deletions(-) create mode 100644 idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/GradleScriptingSettingsProvider.kt create mode 100644 idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/KotlinStandaloneScriptsModel.kt create mode 100644 idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/StandaloneScriptsStorage.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportSpecificSettingsProvider.kt diff --git a/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties b/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties index c8878fced06..96d2d276755 100644 --- a/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties +++ b/idea/idea-gradle/res/messages/KotlinIdeaGradleBundle.properties @@ -56,6 +56,9 @@ action.text.install=Install action.text.show.kotlin.gradle.dsl.logs.in=Show Kotlin Gradle DSL Logs in {0} build.0.project=Build {0} change.build.gradle.configuration=Change build.gradle configuration +standalone.scripts.settings.title=Manage Standalone Scripts +standalone.scripts.settings.column.name=Path +gradle.scripts.settings.title=Gradle Kotlin DSL Scripts description.text.a.single.platform.kotlin.library.or.application.targeting.javascript=A single-platform Kotlin library or application targeting JavaScript description.text.a.single.platform.kotlin.library.or.application.targeting.js.for.browser=A single-platform Kotlin library or application targeting JavaScript for browser description.text.a.single.platform.kotlin.library.or.application.targeting.js.for.node.js=A single-platform Kotlin library or application targeting JavaScript for Node.js 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 126124cc128..708299f0d2d 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 @@ -6,11 +6,39 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.scripting.gradle.settings.StandaloneScriptsStorage -class GradleBuildRootIndex : StandaloneScriptsUpdater { +class GradleBuildRootIndex(private val project: Project) : StandaloneScriptsUpdater { private val log = logger() - private val standaloneScriptRoots = mutableMapOf() + private inner class StandaloneScriptRootsCache { + private val standaloneScripts: MutableSet + get() = StandaloneScriptsStorage.getInstance(project)?.files ?: hashSetOf() + + private val standaloneScriptRoots = mutableMapOf().apply { + standaloneScripts.forEach(::updateRootsCache) + } + + fun all(): List = standaloneScripts.toList() + fun get(path: String): GradleBuildRoot? = standaloneScriptRoots[path] + + fun add(path: String) { + standaloneScripts.add(path) + updateRootsCache(path) + } + + fun remove(path: String): GradleBuildRoot? { + standaloneScripts.remove(path) + return standaloneScriptRoots.remove(path) + } + + fun updateRootsCache(path: String) { + standaloneScriptRoots[path] = findNearestRoot(path) + } + } + + private val standaloneScriptRoots by lazy { StandaloneScriptRootsCache() } private val byWorkingDir = HashMap() private val byProjectDir = HashMap() @@ -27,7 +55,7 @@ class GradleBuildRootIndex : StandaloneScriptsUpdater { } } - standaloneScriptRoots.keys.forEach(::computeStandaloneScriptRoot) + standaloneScriptRoots.all().forEach(standaloneScriptRoots::updateRootsCache) } @Synchronized @@ -48,10 +76,10 @@ class GradleBuildRootIndex : StandaloneScriptsUpdater { fun getBuildByProjectDir(projectDir: String) = byProjectDir[projectDir] @Synchronized - fun isStandaloneScript(path: String) = path in standaloneScriptRoots + fun isStandaloneScript(path: String) = path in standaloneScriptRoots.all() @Synchronized - fun getStandaloneScriptRoot(path: String) = standaloneScriptRoots[path] + fun getStandaloneScriptRoot(path: String) = standaloneScriptRoots.get(path) @Synchronized fun add(value: GradleBuildRoot): GradleBuildRoot? { @@ -70,21 +98,14 @@ class GradleBuildRootIndex : StandaloneScriptsUpdater { @Synchronized override fun addStandaloneScript(path: String) { - computeStandaloneScriptRoot(path) + standaloneScriptRoots.add(path) } @Synchronized - override fun removeStandaloneScript(path: String) = - standaloneScriptRoots.remove(path) - - override var standaloneScripts: Collection - @Synchronized get() = standaloneScriptRoots.keys - @Synchronized set(value) { - standaloneScriptRoots.clear() - value.forEach(::computeStandaloneScriptRoot) - } - - private fun computeStandaloneScriptRoot(path: String) { - standaloneScriptRoots[path] = findNearestRoot(path) + override fun removeStandaloneScript(path: String): GradleBuildRoot? { + return standaloneScriptRoots.remove(path) } + + override val standaloneScripts: Collection + @Synchronized get() = standaloneScriptRoots.all() } \ 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 3c0783ab27e..90373c30ec4 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 @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots +import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile /** @@ -13,8 +14,8 @@ import com.intellij.openapi.vfs.VirtualFile * * @see GradleBuildRootsManager for details. */ -abstract class GradleBuildRootsLocator { - protected val roots = GradleBuildRootIndex() +abstract class GradleBuildRootsLocator(project: Project) { + protected val roots = GradleBuildRootIndex(project) abstract fun getScriptInfo(localPath: String): GradleScriptInfo? 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 8d396e04847..a6e6ceec57e 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 @@ -63,7 +63,7 @@ import java.util.concurrent.atomic.AtomicBoolean * - [New] - not yet imported * - [Imported] - imported */ -class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(), ScriptingSupport { +class GradleBuildRootsManager(val project: Project) : GradleBuildRootsLocator(project), ScriptingSupport { private val manager: CompositeScriptConfigurationManager get() = ScriptConfigurationManager.getInstance(project) as CompositeScriptConfigurationManager diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt index 7f926a32111..2706020e377 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/roots/StandaloneScriptsUpdater.kt @@ -6,7 +6,7 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots interface StandaloneScriptsUpdater { - var standaloneScripts: Collection + val standaloneScripts: Collection fun addStandaloneScript(path: String) diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/GradleScriptingSettingsProvider.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/GradleScriptingSettingsProvider.kt new file mode 100644 index 00000000000..96016f4fffc --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/GradleScriptingSettingsProvider.kt @@ -0,0 +1,142 @@ +/* + * 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.settings + +import com.intellij.openapi.fileChooser.FileChooser +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.options.UnnamedConfigurable +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.wm.IdeFocusManager +import com.intellij.ui.ToolbarDecorator +import com.intellij.ui.table.JBTable +import com.intellij.ui.table.TableView +import com.intellij.util.ui.JBUI +import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle +import org.jetbrains.kotlin.idea.script.configuration.ScriptingSupportSpecificSettingsProvider +import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsLocator.NotificationKind +import org.jetbrains.kotlin.idea.scripting.gradle.roots.GradleBuildRootsManager +import org.jetbrains.kotlin.idea.scripting.gradle.settings.KotlinStandaloneScriptsModel.Companion.createModel +import java.awt.BorderLayout +import javax.swing.JComponent +import javax.swing.JPanel +import javax.swing.ListSelectionModel +import javax.swing.plaf.BorderUIResource.TitledBorderUIResource + +class GradleSettingsProvider(private val project: Project) : ScriptingSupportSpecificSettingsProvider() { + override val title: String = KotlinIdeaGradleBundle.message("gradle.scripts.settings.title") + + override fun createConfigurable(): UnnamedConfigurable { + return StandaloneScriptsUIComponent(project) + } +} + +class StandaloneScriptsUIComponent(private val project: Project) : UnnamedConfigurable { + private val fileChooser: FileChooserDescriptor = object : FileChooserDescriptor( + true, false, false, + false, false, true + ) { + override fun isFileSelectable(file: VirtualFile): Boolean { + val rootsManager = GradleBuildRootsManager.getInstance(project) + val scriptUnderRoot = rootsManager.findScriptBuildRoot(file) + val notificationKind = scriptUnderRoot?.notificationKind + return notificationKind == NotificationKind.legacyOutside || notificationKind == NotificationKind.notEvaluatedInLastImport + } + } + + private var panel: JPanel? = null + + private val scriptsFromStorage = StandaloneScriptsStorage.getInstance(project)?.files?.toList() ?: emptyList() + private val scriptsInTable = scriptsFromStorage.toMutableList() + + private val table: JBTable + private val model: KotlinStandaloneScriptsModel + + init { + model = createModel(scriptsInTable) + + table = TableView(model) + table.preferredScrollableViewportSize = JBUI.size(300, -1) + table.selectionModel.selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION + + model.addTableModelListener(table) + } + + override fun reset() { + scriptsInTable.clear() + scriptsInTable.addAll(scriptsFromStorage) + model.items = scriptsInTable + } + + override fun apply() { + GradleBuildRootsManager.getInstance(project).updateStandaloneScripts { + val previousScripts = scriptsFromStorage + + scriptsInTable + .filterNot { previousScripts.contains(it) } + .forEach { addStandaloneScript(it) } + + previousScripts + .filterNot { scriptsInTable.contains(it) } + .forEach { removeStandaloneScript(it) } + } + } + + override fun createComponent(): JComponent? { + if (panel == null) { + val panel = JPanel(BorderLayout()) + panel.border = TitledBorderUIResource(KotlinIdeaGradleBundle.message("standalone.scripts.settings.title")) + panel.add( + ToolbarDecorator.createDecorator(table) + .setAddAction { addScripts() } + .setRemoveAction { removeScripts() } + .setRemoveActionUpdater { table.selectedRow >= 0 } + .disableUpDownActions() + .createPanel() + ) + this.panel = panel + } + return panel + } + + override fun isModified(): Boolean { + return scriptsFromStorage != scriptsInTable + } + + override fun disposeUIResources() { + panel = null + } + + override fun cancel() { + reset() + } + + private fun addScripts() { + val chosen = FileChooser.chooseFiles(fileChooser, project, null) + for (chosenFile in chosen) { + val path = chosenFile.path + + if (scriptsInTable.contains(path)) continue + + scriptsInTable.add(path) + model.addRow(path) + } + } + + private fun removeScripts() { + val selected = table.selectedRows + if (selected == null || selected.isEmpty()) { + return + } + for ((removedCount, indexToRemove) in selected.withIndex()) { + val row = indexToRemove - removedCount + scriptsInTable.removeAt(row) + model.removeRow(row) + } + IdeFocusManager.getGlobalInstance() + .doWhenFocusSettlesDown { IdeFocusManager.getGlobalInstance().requestFocus(table, true) } + } +} \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/KotlinStandaloneScriptsModel.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/KotlinStandaloneScriptsModel.kt new file mode 100644 index 00000000000..526b4b84291 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/KotlinStandaloneScriptsModel.kt @@ -0,0 +1,28 @@ +/* + * 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.settings + +import com.intellij.util.ui.ColumnInfo +import com.intellij.util.ui.ListTableModel +import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle + +class KotlinStandaloneScriptsModel private constructor(scripts: MutableList) : + ListTableModel( + arrayOf(object : ColumnInfo(KotlinIdeaGradleBundle.message("standalone.scripts.settings.column.name")) { + override fun valueOf(item: String?): String? = item + }), + scripts + ) { + + override fun setItems(items: List) { + super.setItems(items.toMutableList()) + } + + companion object { + fun createModel(scripts: Collection): KotlinStandaloneScriptsModel = + KotlinStandaloneScriptsModel(scripts.toMutableList()) + } +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/StandaloneScriptsStorage.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/StandaloneScriptsStorage.kt new file mode 100644 index 00000000000..169c8f4b06d --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/settings/StandaloneScriptsStorage.kt @@ -0,0 +1,33 @@ +/* + * 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.settings + +import com.intellij.openapi.components.* +import com.intellij.openapi.project.Project +import com.intellij.util.xmlb.XmlSerializerUtil + +@State( + name = "StandaloneScriptsStorage", + storages = [Storage(StoragePathMacros.CACHE_FILE)] +) +class StandaloneScriptsStorage : PersistentStateComponent { + var files: MutableSet = hashSetOf() + + fun getScripts(): List = files.toList() + + override fun getState(): StandaloneScriptsStorage? { + return this + } + + override fun loadState(state: StandaloneScriptsStorage) { + XmlSerializerUtil.copyBean(state, this) + } + + companion object { + fun getInstance(project: Project): StandaloneScriptsStorage? = + ServiceManager.getService(project, StandaloneScriptsStorage::class.java) + } +} \ No newline at end of file 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 index c328966846d..9ef1e81de31 100644 --- 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 @@ -5,18 +5,34 @@ package org.jetbrains.kotlin.idea.scripting.gradle.roots +import com.intellij.mock.MockProjectEx +import com.intellij.openapi.Disposable +import junit.framework.TestCase import org.jetbrains.kotlin.idea.scripting.gradle.GradleKotlinScriptConfigurationInputs import org.jetbrains.kotlin.idea.scripting.gradle.LastModifiedFiles import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel -import kotlin.test.assertEquals -open class AbstractGradleBuildRootsLocatorTest { +open class AbstractGradleBuildRootsLocatorTest : TestCase() { private val scripts = mutableMapOf() private val locator = MyRootsLocator() class ScriptFixture(val introductionTs: Long, val info: GradleScriptInfo) - private inner class MyRootsLocator : GradleBuildRootsLocator() { + lateinit var disposable: Disposable + + override fun setUp() { + super.setUp() + + disposable = Disposable { } + } + + override fun tearDown() { + disposable.dispose() + + super.tearDown() + } + + private inner class MyRootsLocator : GradleBuildRootsLocator(MockProjectEx(disposable)) { override fun getScriptFirstSeenTs(path: String): Long = scripts[path]?.introductionTs ?: 0 override fun getScriptInfo(localPath: String): GradleScriptInfo? = scripts[localPath]?.info fun accessRoots() = roots diff --git a/idea/resources-en/messages/KotlinBundle.properties b/idea/resources-en/messages/KotlinBundle.properties index 3c6de8b1317..566ce14db1b 100644 --- a/idea/resources-en/messages/KotlinBundle.properties +++ b/idea/resources-en/messages/KotlinBundle.properties @@ -2218,6 +2218,7 @@ 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.title=Manage Script Definitions 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 diff --git a/idea/resources/META-INF/gradle.xml b/idea/resources/META-INF/gradle.xml index da16af2ca64..d9cd19bf75f 100644 --- a/idea/resources/META-INF/gradle.xml +++ b/idea/resources/META-INF/gradle.xml @@ -19,6 +19,8 @@ + + @@ -48,6 +50,7 @@ restartRequired="false"/> + diff --git a/idea/resources/META-INF/gradle.xml.193 b/idea/resources/META-INF/gradle.xml.193 index ffd62505562..61cae8560a0 100644 --- a/idea/resources/META-INF/gradle.xml.193 +++ b/idea/resources/META-INF/gradle.xml.193 @@ -19,6 +19,8 @@ + + diff --git a/idea/resources/META-INF/scripting-support.xml b/idea/resources/META-INF/scripting-support.xml index a06d4ae68c6..c2f33888fef 100644 --- a/idea/resources/META-INF/scripting-support.xml +++ b/idea/resources/META-INF/scripting-support.xml @@ -3,6 +3,10 @@ + + diff --git a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.form b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.form index b5b3ab7a1c8..2ce7a3bd300 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.form +++ b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.form @@ -1,6 +1,6 @@
- + @@ -8,56 +8,23 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java index bf7c0a34489..1c21433bfa1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java +++ b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java @@ -4,11 +4,15 @@ */ package org.jetbrains.kotlin.idea.script.configuration; -import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; +import com.intellij.ui.TitledSeparator; import com.intellij.ui.ToolbarDecorator; +import com.intellij.ui.components.panels.VerticalLayout; import com.intellij.ui.table.TableView; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,30 +22,42 @@ import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings; import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition; import javax.swing.*; -import java.awt.*; +import java.util.ArrayList; +import java.util.List; -public class KotlinScriptingSettingsConfigurable implements SearchableConfigurable, Configurable.NoScroll { +public class KotlinScriptingSettingsConfigurable implements SearchableConfigurable { public static final String ID = "preferences.language.Kotlin.scripting"; private JPanel root; private JPanel panelScriptDefinitionsChooser; + private JPanel additionalSettingsPanel; - private KotlinScriptDefinitionsModel model; + private final List scriptingSuppportSettingsConfigurables = new ArrayList<>(); + private final KotlinScriptDefinitionsModel model; + + private final Project project; private final ScriptDefinitionsManager manager; private final KotlinScriptingSettings settings; public KotlinScriptingSettingsConfigurable(Project project) { + this.project = project; manager = ScriptDefinitionsManager.Companion.getInstance(project); settings = KotlinScriptingSettings.Companion.getInstance(project); + model = KotlinScriptDefinitionsModel.Companion.createModel(manager.getAllDefinitions(), settings); } @Nullable @Override public JComponent createComponent() { - model = KotlinScriptDefinitionsModel.Companion.createModel(manager.getAllDefinitions(), settings); + panelScriptDefinitionsChooser.setLayout(new VerticalLayout(0)); + + panelScriptDefinitionsChooser.add(new TitledSeparator(KotlinBundle.message("kotlin.script.definitions.title"))); + + JLabel commentLabel = new JLabel(KotlinBundle.message("kotlin.script.definitions.model.name.autoReloadScriptDependencies.description")); + commentLabel.setForeground(UIUtil.getContextHelpForeground()); + panelScriptDefinitionsChooser.add(commentLabel); - panelScriptDefinitionsChooser.setLayout(new BorderLayout()); TableView table = new TableView<>(model); panelScriptDefinitionsChooser.add( ToolbarDecorator.createDecorator(table) @@ -49,18 +65,36 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab .disableRemoveAction() .createPanel() ); + table.setVisibleRowCount(12); - table.setVisibleRowCount(15); + additionalSettingsPanel.setLayout(new VerticalLayout(0)); + + List providers = + ScriptingSupportSpecificSettingsProvider.SETTINGS_PROVIDERS.getExtensionList(project); + for (ScriptingSupportSpecificSettingsProvider provider : providers) { + additionalSettingsPanel.add(new TitledSeparator(provider.getTitle())); + + UnnamedConfigurable configurable = provider.createConfigurable(); + scriptingSuppportSettingsConfigurables.add(configurable); + + additionalSettingsPanel.add(configurable.createComponent()); + } return root; } @Override public boolean isModified() { + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + if (supportSpecificSetting.isModified()) { + return true; + } + } + return isScriptDefinitionsChanged(); } @Override - public void apply() { + public void apply() throws ConfigurationException { if (isScriptDefinitionsChanged()) { for (ModelDescriptor item : model.getItems()) { ScriptDefinition definition = item.getDefinition(); @@ -72,11 +106,18 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab manager.reorderScriptDefinitions(); } + + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + supportSpecificSetting.apply(); + } } @Override public void reset() { model.setDefinitions(manager.getAllDefinitions(), settings); + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + supportSpecificSetting.reset(); + } } private boolean isScriptDefinitionsChanged() { diff --git a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java.193 b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java.193 index f306b270818..7b644fd8bae 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java.193 +++ b/idea/src/org/jetbrains/kotlin/idea/script/configuration/KotlinScriptingSettingsConfigurable.java.193 @@ -4,11 +4,15 @@ */ package org.jetbrains.kotlin.idea.script.configuration; -import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; +import com.intellij.ui.TitledSeparator; import com.intellij.ui.ToolbarDecorator; +import com.intellij.ui.components.panels.VerticalLayout; import com.intellij.ui.table.TableView; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,30 +22,42 @@ import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings; import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition; import javax.swing.*; -import java.awt.*; +import java.util.ArrayList; +import java.util.List; -public class KotlinScriptingSettingsConfigurable implements SearchableConfigurable, Configurable.NoScroll { +public class KotlinScriptingSettingsConfigurable implements SearchableConfigurable { public static final String ID = "preferences.language.Kotlin.scripting"; private JPanel root; private JPanel panelScriptDefinitionsChooser; + private JPanel additionalSettingsPanel; - private KotlinScriptDefinitionsModel model; + private final List scriptingSuppportSettingsConfigurables = new ArrayList<>(); + private final KotlinScriptDefinitionsModel model; + + private final Project project; private final ScriptDefinitionsManager manager; private final KotlinScriptingSettings settings; public KotlinScriptingSettingsConfigurable(Project project) { + this.project = project; manager = ScriptDefinitionsManager.Companion.getInstance(project); settings = KotlinScriptingSettings.Companion.getInstance(project); + model = KotlinScriptDefinitionsModel.Companion.createModel(manager.getAllDefinitions(), settings); } @Nullable @Override public JComponent createComponent() { - model = KotlinScriptDefinitionsModel.Companion.createModel(manager.getAllDefinitions(), settings); + panelScriptDefinitionsChooser.setLayout(new VerticalLayout(0)); + + panelScriptDefinitionsChooser.add(new TitledSeparator(KotlinBundle.message("kotlin.script.definitions.title"))); + + JLabel commentLabel = new JLabel(KotlinBundle.message("kotlin.script.definitions.model.name.autoReloadScriptDependencies.description")); + commentLabel.setForeground(UIUtil.getContextHelpForeground()); + panelScriptDefinitionsChooser.add(commentLabel); - panelScriptDefinitionsChooser.setLayout(new BorderLayout()); TableView table = new TableView<>(model); panelScriptDefinitionsChooser.add( ToolbarDecorator.createDecorator(table) @@ -50,16 +66,34 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab .createPanel() ); + additionalSettingsPanel.setLayout(new VerticalLayout(0)); + + List providers = + ScriptingSupportSpecificSettingsProvider.SETTINGS_PROVIDERS.getExtensionList(project); + for (ScriptingSupportSpecificSettingsProvider provider : providers) { + additionalSettingsPanel.add(new TitledSeparator(provider.getTitle())); + + UnnamedConfigurable configurable = provider.createConfigurable(); + scriptingSuppportSettingsConfigurables.add(configurable); + + additionalSettingsPanel.add(configurable.createComponent()); + } return root; } @Override public boolean isModified() { + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + if (supportSpecificSetting.isModified()) { + return true; + } + } + return isScriptDefinitionsChanged(); } @Override - public void apply() { + public void apply() throws ConfigurationException { if (isScriptDefinitionsChanged()) { for (ModelDescriptor item : model.getItems()) { ScriptDefinition definition = item.getDefinition(); @@ -71,11 +105,18 @@ public class KotlinScriptingSettingsConfigurable implements SearchableConfigurab manager.reorderScriptDefinitions(); } + + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + supportSpecificSetting.apply(); + } } @Override public void reset() { model.setDefinitions(manager.getAllDefinitions(), settings); + for (UnnamedConfigurable supportSpecificSetting : scriptingSuppportSettingsConfigurables) { + supportSpecificSetting.reset(); + } } private boolean isScriptDefinitionsChanged() { diff --git a/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportSpecificSettingsProvider.kt b/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportSpecificSettingsProvider.kt new file mode 100644 index 00000000000..2201a8ef83d --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportSpecificSettingsProvider.kt @@ -0,0 +1,23 @@ +/* + * 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.script.configuration + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.options.UnnamedConfigurable + +abstract class ScriptingSupportSpecificSettingsProvider { + + abstract val title: String + + abstract fun createConfigurable(): UnnamedConfigurable + + companion object { + @JvmField + val SETTINGS_PROVIDERS: ExtensionPointName = + ExtensionPointName.create("org.jetbrains.kotlin.scripting.idea.settings.provider") + } +} +