UI for standalone gradle scripts
^KT-39790 Fixed
This commit is contained in:
committed by
Vladimir Dolzhenko
parent
fc874e72b1
commit
54d96a2dd7
@@ -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
|
||||
|
||||
+39
-18
@@ -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<GradleBuildRootIndex>()
|
||||
|
||||
private val standaloneScriptRoots = mutableMapOf<String, GradleBuildRoot?>()
|
||||
private inner class StandaloneScriptRootsCache {
|
||||
private val standaloneScripts: MutableSet<String>
|
||||
get() = StandaloneScriptsStorage.getInstance(project)?.files ?: hashSetOf()
|
||||
|
||||
private val standaloneScriptRoots = mutableMapOf<String, GradleBuildRoot?>().apply {
|
||||
standaloneScripts.forEach(::updateRootsCache)
|
||||
}
|
||||
|
||||
fun all(): List<String> = 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<String, GradleBuildRoot>()
|
||||
private val byProjectDir = HashMap<String, GradleBuildRoot>()
|
||||
@@ -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<String>
|
||||
@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<String>
|
||||
@Synchronized get() = standaloneScriptRoots.all()
|
||||
}
|
||||
+3
-2
@@ -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?
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle.roots
|
||||
|
||||
interface StandaloneScriptsUpdater {
|
||||
var standaloneScripts: Collection<String>
|
||||
val standaloneScripts: Collection<String>
|
||||
|
||||
fun addStandaloneScript(path: String)
|
||||
|
||||
|
||||
+142
@@ -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) }
|
||||
}
|
||||
}
|
||||
+28
@@ -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<String>) :
|
||||
ListTableModel<String>(
|
||||
arrayOf(object : ColumnInfo<String, String>(KotlinIdeaGradleBundle.message("standalone.scripts.settings.column.name")) {
|
||||
override fun valueOf(item: String?): String? = item
|
||||
}),
|
||||
scripts
|
||||
) {
|
||||
|
||||
override fun setItems(items: List<String>) {
|
||||
super.setItems(items.toMutableList())
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createModel(scripts: Collection<String>): KotlinStandaloneScriptsModel =
|
||||
KotlinStandaloneScriptsModel(scripts.toMutableList())
|
||||
}
|
||||
}
|
||||
+33
@@ -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<StandaloneScriptsStorage> {
|
||||
var files: MutableSet<String> = hashSetOf()
|
||||
|
||||
fun getScripts(): List<String> = 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)
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -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<String, ScriptFixture>()
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user