Wizard: introduce ServicesManager & correctly handle disabled gradle & maven Idea plugins

This commit is contained in:
Ilya Kirillov
2019-12-12 20:22:30 +03:00
parent 9011eecfdf
commit f927fb3471
41 changed files with 361 additions and 279 deletions
@@ -4,16 +4,24 @@ import org.jetbrains.kotlin.tools.projectWizard.core.PluginsCreator
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
import org.jetbrains.kotlin.tools.projectWizard.core.service.ServicesManager
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
import org.jetbrains.kotlin.tools.projectWizard.wizard.service.IdeaWizardService
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class IdeWizard(
createPlugins: PluginsCreator,
initialServices: List<Service>
) : Wizard(createPlugins, initialServices) {
initialServices: List<WizardService>
) : Wizard(
createPlugins,
ServicesManager(initialServices) { services ->
services.firstOrNull { it is IdeaWizardService }
?: services.firstOrNull()
}
) {
private val allSettings = plugins.flatMap { it.declaredSettings }
init {
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
import org.jetbrains.kotlin.tools.projectWizard.wizard.service.*
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.PomWizardStepComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep.FirstWizardStepComponent
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.SecondStepWizardComponent
@@ -34,7 +35,7 @@ import com.intellij.openapi.module.Module as IdeaModule
class NewProjectWizardModuleBuilder : ModuleBuilder() {
private val wizard = IdeWizard(Plugins.allPlugins, listOf(IdeaAndroidService()))
private val wizard = IdeWizard(Plugins.allPlugins, IdeaServices.PROJECT_INDEPENDENT)
companion object {
const val MODULE_BUILDER_ID = "kotlin.newProjectWizard.builder"
@@ -62,13 +63,8 @@ class NewProjectWizardModuleBuilder : ModuleBuilder() {
): List<IdeaModule>? {
val modulesModel = model ?: ModuleManager.getInstance(project).modifiableModel
val success = wizard.apply(
services = listOf(
IdeaMavenService(project),
IdeaGradleService(project),
IdeaJpsService(project, modulesModel),
IdeaFileSystemService(),
IdeaAndroidService()
),
services = IdeaServices.createScopeDependent(project, modulesModel) +
IdeaServices.PROJECT_INDEPENDENT,
phases = GenerationPhase.startingFrom(GenerationPhase.FIRST_STEP)
).onFailure { errors ->
val errorMessages = errors.joinToString(separator = "\n") { it.message }
@@ -1,91 +0,0 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.android.sdk.AndroidSdkData
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.safe
import org.jetbrains.kotlin.tools.projectWizard.core.service.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
import org.jetbrains.plugins.gradle.action.ImportProjectFromScriptAction
import java.nio.file.Path
class IdeaFileSystemService : FileSystemService {
override fun createDirectory(path: Path): TaskResult<Unit> = safe {
runWriteAction<Unit> {
VfsUtil.createDirectoryIfMissing(path.toString())
}
}
override fun createFile(path: Path, text: String): TaskResult<Unit> = safe {
runWriteAction {
val directoryPath = path.parent
val directory = VfsUtil.createDirectoryIfMissing(directoryPath.toFile().toString())!!
val virtualFile = directory.createChildData(this, path.fileName.toString())
VfsUtil.saveText(virtualFile, text)
}
}
}
class IdeaGradleService(private val project: Project) : GradleService {
// We have to call action directly as there is no common way
// to import Gradle project in all IDEAs from 183 to 193
override fun importProject(
path: Path,
modulesIrs: List<ModuleIR>
): TaskResult<Unit> = safe {
val virtualFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
val dataContext = SimpleDataContext.getSimpleContext(
mapOf(
CommonDataKeys.PROJECT.name to project,
CommonDataKeys.VIRTUAL_FILE.name to virtualFile
),
null
)
val action = ImportProjectFromScriptAction()
val event = AnActionEvent.createFromAnAction(action, null, ActionPlaces.UNKNOWN, dataContext)
action.actionPerformed(event)
}
}
class IdeaMavenService(private val project: Project) : MavenService {
override fun importProject(
path: Path,
modulesIrs: List<ModuleIR>
): TaskResult<Unit> = safe {
val mavenProjectManager = MavenProjectsManager.getInstance(project)
val rootFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
mavenProjectManager.addManagedFilesOrUnignore(rootFile.findAllPomFiles())
}
private fun VirtualFile.findAllPomFiles(): List<VirtualFile> {
val result = mutableListOf<VirtualFile>()
fun VirtualFile.find() {
when {
!isDirectory && name == "pom.xml" -> result += this
isDirectory -> children.forEach(VirtualFile::find)
}
}
find()
return result
}
}
class IdeaAndroidService : AndroidService {
override fun isValidAndroidSdk(path: Path): Boolean =
//todo use android plugin for that?
AndroidServiceImpl().isValidAndroidSdk(path)
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.ide.plugins.PluginManagerCore
import org.jetbrains.kotlin.tools.projectWizard.core.service.BuildSystemAvailabilityWizardService
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.isGradle
class IdeaBuildSystemAvailabilityWizardService : BuildSystemAvailabilityWizardService, IdeaWizardService {
override fun isAvailable(buildSystemType: BuildSystemType): Boolean = when {
buildSystemType.isGradle -> !PluginManagerCore.isDisabled("org.jetbrains.plugins.gradle")
buildSystemType == BuildSystemType.Maven -> !PluginManagerCore.isDisabled("org.jetbrains.idea.maven")
else -> true
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.openapi.vfs.VfsUtil
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.safe
import org.jetbrains.kotlin.tools.projectWizard.core.service.FileSystemWizardService
import java.nio.file.Path
class IdeaFileSystemWizardService : FileSystemWizardService, IdeaWizardService {
override fun createDirectory(path: Path): TaskResult<Unit> = safe {
runWriteAction<Unit> {
VfsUtil.createDirectoryIfMissing(path.toString())
}
}
override fun createFile(path: Path, text: String): TaskResult<Unit> = safe {
runWriteAction {
val directoryPath = path.parent
val directory =
VfsUtil.createDirectoryIfMissing(directoryPath.toFile().toString())!!
val virtualFile = directory.createChildData(this, path.fileName.toString())
VfsUtil.saveText(virtualFile, text)
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.safe
import org.jetbrains.kotlin.tools.projectWizard.core.service.ProjectImportingWizardService
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.isGradle
import org.jetbrains.plugins.gradle.action.ImportProjectFromScriptAction
import java.nio.file.Path
class IdeaGradleWizardService(private val project: Project) : ProjectImportingWizardService,
IdeaWizardService {
override fun isSuitableFor(buildSystemType: BuildSystemType): Boolean =
buildSystemType.isGradle
// We have to call action directly as there is no common way
// to import Gradle project in all IDEAs from 183 to 193
override fun importProject(
path: Path,
modulesIrs: List<ModuleIR>
): TaskResult<Unit> = safe {
val virtualFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
val dataContext = SimpleDataContext.getSimpleContext(
mapOf(
CommonDataKeys.PROJECT.name to project,
CommonDataKeys.VIRTUAL_FILE.name to virtualFile
),
null
)
val action = ImportProjectFromScriptAction()
val event = AnActionEvent.createFromAnAction(
action,
null,
ActionPlaces.UNKNOWN,
dataContext
)
action.actionPerformed(event)
}
}
@@ -1,4 +1,9 @@
package org.jetbrains.kotlin.tools.projectWizard.wizard
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
import com.intellij.jarRepository.JarRepositoryManager
@@ -9,7 +14,6 @@ import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.util.PathUtil
import org.jetbrains.idea.maven.utils.library.RepositoryLibraryProperties
@@ -19,19 +23,24 @@ import org.jetbrains.kotlin.config.TestResourceKotlinRootType
import org.jetbrains.kotlin.config.TestSourceKotlinRootType
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.service.JpsService
import org.jetbrains.kotlin.tools.projectWizard.core.service.ProjectImportingWizardService
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
import java.nio.file.Path
import com.intellij.openapi.module.Module as IdeaModule
class IdeaJpsService(
class IdeaJpsWizardService(
private val project: Project,
private val modulesModel: ModifiableModuleModel
) : JpsService {
) : ProjectImportingWizardService, IdeaWizardService {
override fun isSuitableFor(buildSystemType: BuildSystemType): Boolean =
buildSystemType == BuildSystemType.Jps
override fun importProject(path: Path, modulesIrs: List<ModuleIR>): TaskResult<Unit> = runWriteAction {
ProjectImporter(project, modulesModel, path, modulesIrs).import()
ProjectImporter(project, modulesModel, path, modulesIrs)
.import()
}
}
@@ -47,7 +56,6 @@ private class ProjectImporter(
fun import() = modulesIrs.mapSequence { convertModule(it) } andThen
safe { modulesModel.commit() }
private fun convertModule(moduleIr: ModuleIR): TaskResult<IdeaModule> {
val module = modulesModel.newModule(
(moduleIr.path / "${moduleIr.name}.iml").toString(),
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.kotlin.tools.projectWizard.core.TaskResult
import org.jetbrains.kotlin.tools.projectWizard.core.safe
import org.jetbrains.kotlin.tools.projectWizard.core.service.ProjectImportingWizardService
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
import java.nio.file.Path
class IdeaMavenWizardService(private val project: Project) : ProjectImportingWizardService,
IdeaWizardService {
override fun isSuitableFor(buildSystemType: BuildSystemType): Boolean =
buildSystemType == BuildSystemType.Maven
override fun importProject(
path: Path,
modulesIrs: List<ModuleIR>
): TaskResult<Unit> = safe {
val mavenProjectManager = MavenProjectsManager.getInstance(project)
val rootFile = LocalFileSystem.getInstance().findFileByPath(path.toString())!!
mavenProjectManager.addManagedFilesOrUnignore(rootFile.findAllPomFiles())
}
private fun VirtualFile.findAllPomFiles(): List<VirtualFile> {
val result = mutableListOf<VirtualFile>()
fun VirtualFile.find() {
when {
!isDirectory && name == "pom.xml" -> result += this
isDirectory -> children.forEach(VirtualFile::find)
}
}
find()
return result
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.wizard.service
import com.intellij.openapi.module.ModifiableModuleModel
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.tools.projectWizard.core.service.WizardService
interface IdeaWizardService : WizardService
object IdeaServices {
val PROJECT_INDEPENDENT: List<IdeaWizardService> = listOf(
IdeaFileSystemWizardService(),
IdeaBuildSystemAvailabilityWizardService()
)
fun createScopeDependent(project: Project, model: ModifiableModuleModel) = listOf(
IdeaGradleWizardService(project),
IdeaMavenWizardService(project),
IdeaJpsWizardService(project, model)
)
}
@@ -22,8 +22,9 @@ import javax.swing.JList
class DropDownComponent<T : DisplayableSettingItem>(
private val valuesReadingContext: ValuesReadingContext,
initialValues: List<T> = emptyList(),
private val initialValues: List<T> = emptyList(),
labelText: String? = null,
private val filter: (T) -> Boolean = { true },
private val validator: SettingValidator<T> = settingValidator { ValidationResult.OK },
private val iconProvider: (T) -> Icon? = { null },
private val onAnyValueUpdate: (T) -> Unit = {}
@@ -42,7 +43,7 @@ class DropDownComponent<T : DisplayableSettingItem>(
}
@Suppress("UNCHECKED_CAST")
private val comboBox = ComboBox<T>(initialValues.toTypedArray<DisplayableSettingItem>() as Array<T>).apply {
private val comboBox = ComboBox<T>().apply {
renderer = object : ColoredListCellRenderer<T>() {
override fun customizeCellRenderer(
list: JList<out T>,
@@ -76,13 +77,23 @@ class DropDownComponent<T : DisplayableSettingItem>(
}
}
fun updateValues(newValues: List<T>) = withoutActionFiring {
val oldValue = comboBox.selectedItem
@Suppress("UNCHECKED_CAST")
comboBox.model = DefaultComboBoxModel(newValues.toTypedArray<DisplayableSettingItem>() as Array<T>)
override fun onInit() {
super.onInit()
updateValues(initialValues)
}
if (newValues.isNotEmpty() && oldValue !in newValues) {
value = newValues.first()
fun updateValues(newValues: List<T>) {
val newValuesFiltered = newValues.filter(filter)
val oldValue = comboBox.selectedItem
withoutActionFiring {
@Suppress("UNCHECKED_CAST")
comboBox.model = DefaultComboBoxModel(newValuesFiltered.toTypedArray<DisplayableSettingItem>() as Array<T>)
}
if (oldValue !in newValuesFiltered) {
newValuesFiltered.firstOrNull()?.let { newValue ->
value = newValuesFiltered.first()
}
}
}
@@ -106,13 +117,6 @@ class DropDownComponent<T : DisplayableSettingItem>(
}
}
init {
initialValues.firstOrNull()?.let { first ->
value = first
validate(first)
}
}
fun validate(value: T = this.value) {
validationIndicator.validationState = validator.validate(valuesReadingContext, value)
}
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
import com.intellij.icons.AllIcons
import icons.GradleIcons
import icons.MavenIcons
import icons.OpenapiIcons
import org.jetbrains.kotlin.tools.projectWizard.core.entity.DropDownSettingType
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
@@ -18,7 +19,7 @@ import java.awt.BorderLayout
class BuildSystemTypeSettingComponent(
valuesReadingContext: ValuesReadingContext
private val valuesReadingContext: ValuesReadingContext
) : SettingComponent<BuildSystemType, DropDownSettingType<BuildSystemType>>(
BuildSystemPlugin::type.reference,
valuesReadingContext
@@ -28,6 +29,7 @@ class BuildSystemTypeSettingComponent(
valuesReadingContext,
setting.type.values,
labelText = "Build System",
filter = { value -> setting.type.filter(valuesReadingContext, reference, value) },
validator = setting.validator,
iconProvider = BuildSystemType::icon,
onAnyValueUpdate = { value = it }
@@ -51,6 +53,6 @@ private val BuildSystemType.icon
get() = when (this) {
BuildSystemType.GradleKotlinDsl -> GradleIcons.Gradle
BuildSystemType.GradleGroovyDsl -> GradleIcons.Gradle
BuildSystemType.Maven -> MavenIcons.MavenLogo
BuildSystemType.Maven -> OpenapiIcons.RepositoryLibraryLogo
BuildSystemType.Jps -> AllIcons.Nodes.Module
}
@@ -100,7 +100,7 @@ class VersionSettingComponent(
class DropdownSettingComponent(
reference: SettingReference<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>,
valuesReadingContext: ValuesReadingContext
private val valuesReadingContext: ValuesReadingContext
) : DefaultSettingComponent<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>(
reference,
valuesReadingContext
@@ -108,22 +108,13 @@ class DropdownSettingComponent(
private val dropDownComponent = DropDownComponent(
valuesReadingContext,
setting.type.values,
filter = { value -> setting.type.filter(valuesReadingContext, reference, value) },
labelText = setting.title,
onAnyValueUpdate = { newValue ->
value = newValue
}
).asSubComponent()
override fun onInit() {
super.onInit()
val valuesFiltered = setting.type.values.filter { setting.type.filter(reference, it) }
dropDownComponent.updateValues(valuesFiltered)
if (valuesFiltered.isNotEmpty()) {
value = valuesFiltered.first()
}
}
override val validationIndicator: ValidationIndicator? = null
override val component: JComponent = dropDownComponent.component
}