Wizard: Add initial version of the new project wizard
This commit is contained in:
+10
-1
@@ -621,11 +621,20 @@ tasks {
|
||||
)
|
||||
}
|
||||
|
||||
register("idea-new-project-wizard-tests") {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
":libraries:tools:new-project-wizard:test",
|
||||
":libraries:tools:new-project-wizard:new-project-wizard-cli:test"
|
||||
)
|
||||
}
|
||||
|
||||
register("idea-plugin-tests") {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
"idea-plugin-main-tests",
|
||||
"idea-plugin-additional-tests"
|
||||
"idea-plugin-additional-tests",
|
||||
"idea-new-project-wizard-tests"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies {
|
||||
compile(projectTests(":idea:idea-maven"))
|
||||
compile(projectTests(":j2k"))
|
||||
compile(projectTests(":nj2k"))
|
||||
compile(projectTests(":libraries:tools:new-project-wizard:new-project-wizard-cli"))
|
||||
compile(projectTests(":idea:idea-android"))
|
||||
compile(projectTests(":idea:scripting-support"))
|
||||
compile(projectTests(":jps-plugin"))
|
||||
|
||||
@@ -160,6 +160,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
@@ -1066,6 +1067,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/tests",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/testData"
|
||||
) {
|
||||
testClass<AbstractBuildFileGenerationTest> {
|
||||
model("buildFileGeneration", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move these tests into idea-completion module
|
||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||
|
||||
@@ -167,6 +167,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
@@ -1005,6 +1006,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/tests",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/testData"
|
||||
) {
|
||||
testClass<AbstractBuildFileGenerationTest> {
|
||||
model("buildFileGeneration", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move these tests into idea-completion module
|
||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||
|
||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
@@ -984,6 +985,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/tests",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/testData"
|
||||
) {
|
||||
testClass<AbstractBuildFileGenerationTest> {
|
||||
model("buildFileGeneration", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move these tests into idea-completion module
|
||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||
|
||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
@@ -984,6 +985,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/tests",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/testData"
|
||||
) {
|
||||
testClass<AbstractBuildFileGenerationTest> {
|
||||
model("buildFileGeneration", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move these tests into idea-completion module
|
||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||
|
||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.tools.projectWizard.cli.AbstractBuildFileGenerationTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
||||
@@ -984,6 +985,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/tests",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/testData"
|
||||
) {
|
||||
testClass<AbstractBuildFileGenerationTest> {
|
||||
model("buildFileGeneration", recursive = false, extension = null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: move these tests into idea-completion module
|
||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":libraries:tools:new-project-wizard"))
|
||||
implementation(project(":idea:ide-common"))
|
||||
implementation(project(":idea:idea-core"))
|
||||
implementation(project(":idea:idea-jvm"))
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
compileOnly(intellijCoreDep())
|
||||
compileOnly(intellijDep())
|
||||
compileOnly(intellijPluginDep("gradle"))
|
||||
|
||||
excludeInAndroidStudio(rootProject) {
|
||||
compileOnly(intellijPluginDep("maven"))
|
||||
}
|
||||
|
||||
Platform[191].orLower {
|
||||
compileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
|
||||
Platform[192].orHigher {
|
||||
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
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.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class IdeWizard(
|
||||
createPlugins: PluginsCreator,
|
||||
initialServices: List<Service>
|
||||
) : Wizard(createPlugins, initialServices) {
|
||||
private val allSettings = plugins.flatMap { it.declaredSettings }
|
||||
|
||||
init {
|
||||
context.settingContext.initPluginSettings(allSettings)
|
||||
}
|
||||
|
||||
var projectPath by setting(StructurePlugin::projectPath.reference)
|
||||
var projectName by setting(StructurePlugin::name.reference)
|
||||
|
||||
var groupId by setting(StructurePlugin::groupId.reference)
|
||||
var artifactId by setting(StructurePlugin::artifactId.reference)
|
||||
var buildSystemType by setting(BuildSystemPlugin::type.reference)
|
||||
|
||||
private fun <V : Any, T : SettingType<V>> setting(reference: SettingReference<V, T>) =
|
||||
object : ReadWriteProperty<Any?, V?> {
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: V?) {
|
||||
context.settingContext[reference] = value ?: return
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): V? =
|
||||
context.settingContext[reference]
|
||||
}
|
||||
}
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix
|
||||
import com.intellij.jarRepository.JarRepositoryManager
|
||||
import com.intellij.openapi.module.ModifiableModuleModel
|
||||
import com.intellij.openapi.module.ModuleTypeId
|
||||
import com.intellij.openapi.project.Project
|
||||
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
|
||||
import org.jetbrains.kotlin.config.ResourceKotlinRootType
|
||||
import org.jetbrains.kotlin.config.SourceKotlinRootType
|
||||
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.ir.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
|
||||
import java.nio.file.Path
|
||||
import com.intellij.openapi.module.Module as IdeaModule
|
||||
|
||||
class IdeaJpsService(
|
||||
private val project: Project,
|
||||
private val modulesModel: ModifiableModuleModel
|
||||
) : JpsService {
|
||||
override fun importProject(path: Path, modulesIrs: List<ModuleIR>): TaskResult<Unit> = runWriteAction {
|
||||
ProjectImporter(project, modulesModel, path, modulesIrs).import()
|
||||
}
|
||||
}
|
||||
|
||||
private class ProjectImporter(
|
||||
private val project: Project,
|
||||
private val modulesModel: ModifiableModuleModel,
|
||||
private val path: Path,
|
||||
private val modulesIrs: List<ModuleIR>
|
||||
) {
|
||||
private val librariesPath: Path
|
||||
get() = path / "libs"
|
||||
|
||||
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(),
|
||||
ModuleTypeId.JAVA_MODULE
|
||||
)
|
||||
val rootModel = ModuleRootManager.getInstance(module).modifiableModel
|
||||
val contentRoot = rootModel.addContentEntry(moduleIr.path.url)
|
||||
|
||||
SourcesetType.ALL.forEach { sourceset ->
|
||||
val isTest = sourceset == SourcesetType.test
|
||||
contentRoot.addSourceFolder(
|
||||
(moduleIr.path / "src" / sourceset.name / "kotlin").url,
|
||||
if (isTest) TestSourceKotlinRootType else SourceKotlinRootType
|
||||
)
|
||||
contentRoot.addSourceFolder(
|
||||
(moduleIr.path / "src" / sourceset.name / "resources").url,
|
||||
if (isTest) TestResourceKotlinRootType else ResourceKotlinRootType
|
||||
)
|
||||
}
|
||||
|
||||
rootModel.inheritSdk()
|
||||
rootModel.commit()
|
||||
addLibrariesToTheModule(moduleIr, module)
|
||||
return Success(module)
|
||||
}
|
||||
|
||||
private fun addLibrariesToTheModule(moduleIr: ModuleIR, module: IdeaModule) {
|
||||
moduleIr.irs.forEach { ir ->
|
||||
when {
|
||||
ir is LibraryDependencyIR && !ir.isKotlinStdlib -> {
|
||||
attachLibraryToModule(ir, module)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachLibraryToModule(
|
||||
libraryDependency: LibraryDependencyIR,
|
||||
module: IdeaModule
|
||||
) {
|
||||
val artifact = libraryDependency.artifact as? MavenArtifact ?: return
|
||||
val libraryProperties = RepositoryLibraryProperties(
|
||||
artifact.groupId,
|
||||
artifact.artifactId,
|
||||
libraryDependency.version.toString()
|
||||
)
|
||||
val classesRoots = downloadLibraryAndGetItsClasses(libraryProperties)
|
||||
|
||||
ModuleRootModificationUtil.addModuleLibrary(
|
||||
module,
|
||||
if (classesRoots.size > 1) libraryProperties.artifactId else null,
|
||||
OrderEntryFix.refreshAndConvertToUrls(classesRoots),
|
||||
emptyList(),
|
||||
when (libraryDependency.dependencyType) {
|
||||
DependencyType.MAIN -> DependencyScope.COMPILE
|
||||
DependencyType.TEST -> DependencyScope.TEST
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun downloadLibraryAndGetItsClasses(libraryProperties: RepositoryLibraryProperties) =
|
||||
JarRepositoryManager.loadDependenciesModal(
|
||||
project,
|
||||
libraryProperties,
|
||||
false,
|
||||
false,
|
||||
librariesPath.toString(),
|
||||
null
|
||||
).asSequence()
|
||||
.filter { it.type == OrderRootType.CLASSES }
|
||||
.map { PathUtil.getLocalPath(it.file) }
|
||||
.toList()
|
||||
}
|
||||
|
||||
private val Path.url
|
||||
get() = VfsUtil.pathToUrl(toString())
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.ide.projectWizard.ProjectSettingsStep
|
||||
import com.intellij.ide.util.projectWizard.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.module.ModifiableModuleModel
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleType
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.util.SystemProperties
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinModuleSettingStep
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Failure
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Success
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.isSuccess
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
|
||||
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.ui.PomWizardStepComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep.FirstWizardStepComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.SecondStepWizardComponent
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.JComponent
|
||||
import com.intellij.openapi.module.Module as IdeaModule
|
||||
|
||||
|
||||
class NewProjectWizardModuleBuilder : ModuleBuilder() {
|
||||
private val wizard = IdeWizard(Plugins.allPlugins, listOf(IdeaAndroidService()))
|
||||
|
||||
companion object {
|
||||
const val MODULE_BUILDER_ID = "kotlin.newProjectWizard.builder"
|
||||
}
|
||||
|
||||
private var wizardContext: WizardContext? = null
|
||||
|
||||
override fun getModuleType(): ModuleType<*> = NewProjectWizardModuleType()
|
||||
override fun createWizardSteps(
|
||||
wizardContext: WizardContext,
|
||||
modulesProvider: ModulesProvider
|
||||
): Array<ModuleWizardStep> {
|
||||
this.wizardContext = wizardContext
|
||||
return arrayOf(ModuleNewWizardSecondStep(wizard))
|
||||
}
|
||||
|
||||
override fun commit(
|
||||
project: Project,
|
||||
model: ModifiableModuleModel?,
|
||||
modulesProvider: ModulesProvider?
|
||||
): List<IdeaModule>? {
|
||||
val modulesModel = model ?: ModuleManager.getInstance(project).modifiableModel
|
||||
val success = wizard.apply(
|
||||
services = listOf(
|
||||
IdeaMavenService(project),
|
||||
IdeaGradleService(project),
|
||||
IdeaJpsService(project, modulesModel),
|
||||
IdeaFileSystemService(),
|
||||
IdeaAndroidService()
|
||||
),
|
||||
phases = GenerationPhase.startingFrom(GenerationPhase.FIRST_STEP)
|
||||
).onFailure { errors ->
|
||||
val errorMessages = errors.joinToString(separator = "\n") { it.message }
|
||||
Messages.showErrorDialog(project, errorMessages, "The following errors arose during project generation")
|
||||
}.isSuccess
|
||||
return when {
|
||||
!success -> null
|
||||
wizard.buildSystemType == BuildSystemType.Jps -> runWriteAction {
|
||||
modulesModel.modules.toList().onEach { setupModule(it) }
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun modifySettingsStep(settingsStep: SettingsStep): ModuleWizardStep {
|
||||
updateProjectNameAndPomDate(settingsStep)
|
||||
|
||||
return when (wizard.buildSystemType) {
|
||||
BuildSystemType.Jps -> {
|
||||
KotlinModuleSettingStep(
|
||||
JvmPlatforms.defaultJvmPlatform,
|
||||
this,
|
||||
settingsStep,
|
||||
wizardContext
|
||||
)
|
||||
}
|
||||
else -> PomWizardStep(settingsStep, wizard)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProjectNameAndPomDate(settingsStep: SettingsStep) {
|
||||
val suggestedProjectName = with(wizard.valuesReadingContext) {
|
||||
ProjectTemplatesPlugin::template.settingValue.suggestedProjectName.decapitalize()
|
||||
}
|
||||
settingsStep.moduleNameLocationSettings?.apply {
|
||||
val projectParentDirectory = moduleContentRoot.let { Paths.get(it).parent.toString() }
|
||||
moduleName = ProjectWizardUtil.findNonExistingFileName(projectParentDirectory, suggestedProjectName, "")
|
||||
}
|
||||
|
||||
settingsStep.safeAs<ProjectSettingsStep>()?.bindModuleSettings()
|
||||
|
||||
wizard.artifactId = suggestedProjectName
|
||||
wizard.groupId = SystemProperties.getUserName()?.let { "me.$it" } ?: suggestedProjectName
|
||||
}
|
||||
|
||||
override fun getCustomOptionsStep(context: WizardContext?, parentDisposable: Disposable?) =
|
||||
ModuleNewWizardFirstStep(wizard)
|
||||
|
||||
override fun setName(name: String) {
|
||||
wizard.projectName = name
|
||||
}
|
||||
|
||||
|
||||
override fun setModuleFilePath(path: String) = Unit
|
||||
|
||||
override fun setContentEntryPath(moduleRootPath: String) {
|
||||
wizard.projectPath = Paths.get(moduleRootPath)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class WizardStep(protected val wizard: IdeWizard, private val phase: GenerationPhase) : ModuleWizardStep() {
|
||||
override fun updateDataModel() = Unit // model is updated on every UI action
|
||||
override fun validate(): Boolean =
|
||||
when (val result = with(wizard.valuesReadingContext) { with(wizard) { validate(setOf(phase)) } }) {
|
||||
is Success<*> -> true
|
||||
is Failure -> {
|
||||
val messages = result.errors.joinToString(separator = "\n") { it.message }
|
||||
throw ConfigurationException(messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PomWizardStep(
|
||||
originalSettingStep: SettingsStep,
|
||||
wizard: IdeWizard
|
||||
) : WizardStep(wizard, GenerationPhase.PROJECT_GENERATION) {
|
||||
private val pomWizardStepComponent = PomWizardStepComponent(wizard.valuesReadingContext)
|
||||
|
||||
init {
|
||||
originalSettingStep.addSettingsComponent(component)
|
||||
pomWizardStepComponent.onInit()
|
||||
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent = pomWizardStepComponent.component
|
||||
}
|
||||
|
||||
|
||||
class ModuleNewWizardFirstStep(wizard: IdeWizard) : WizardStep(wizard, GenerationPhase.FIRST_STEP) {
|
||||
private val component = FirstWizardStepComponent(wizard)
|
||||
override fun getComponent(): JComponent = component.component
|
||||
|
||||
init {
|
||||
runPreparePhase()
|
||||
component.onInit()
|
||||
}
|
||||
|
||||
private fun runPreparePhase() = ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||
{
|
||||
wizard.apply(emptyList(), setOf(GenerationPhase.PREPARE)) { task ->
|
||||
ProgressManager.getInstance().progressIndicator.text = task.title ?: ""
|
||||
}
|
||||
},
|
||||
"",
|
||||
true,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
class ModuleNewWizardSecondStep(wizard: IdeWizard) : WizardStep(wizard, GenerationPhase.SECOND_STEP) {
|
||||
private val component = SecondStepWizardComponent(wizard)
|
||||
override fun getComponent(): JComponent = component.component
|
||||
|
||||
override fun _init() {
|
||||
component.onInit()
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import com.intellij.openapi.module.ModuleType
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import javax.swing.Icon
|
||||
|
||||
class NewProjectWizardModuleType: ModuleType<NewProjectWizardModuleBuilder>(NewProjectWizardModuleBuilder.MODULE_BUILDER_ID) {
|
||||
override fun getName(): String = "New Kotlin Project Wizard"
|
||||
override fun getDescription(): String = name
|
||||
override fun getNodeIcon(isOpened: Boolean): Icon = KotlinIcons.SMALL_LOGO
|
||||
override fun createModuleBuilder(): NewProjectWizardModuleBuilder = NewProjectWizardModuleBuilder()
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
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)
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
|
||||
abstract class Component : Displayable {
|
||||
private val subComponents = mutableListOf<Component>()
|
||||
|
||||
open fun onInit() {
|
||||
subComponents.forEach(Component::onInit)
|
||||
}
|
||||
|
||||
protected fun <C : Component> C.asSubComponent(): C = also {
|
||||
this@Component.subComponents += it
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DynamicComponent(private val valuesReadingContext: ValuesReadingContext) : Component() {
|
||||
//TODO do not use it in future
|
||||
protected val context = valuesReadingContext.context
|
||||
protected val eventManager
|
||||
get() = valuesReadingContext.context.eventManager
|
||||
|
||||
private var isInitialized: Boolean = false
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
var <V : Any, T : SettingType<V>> SettingReference<V, T>.value: V?
|
||||
get() = with(valuesReadingContext) { notRequiredSettingValue() }
|
||||
set(value) {
|
||||
with(context) { settingContext[this@value] = value!! }
|
||||
}
|
||||
|
||||
inline val <V : Any, reified T : SettingType<V>> PluginSettingPropertyReference<V, T>.value: V?
|
||||
get() = reference.value
|
||||
|
||||
init {
|
||||
valuesReadingContext.context.eventManager.addSettingUpdaterEventListener { reference ->
|
||||
if (isInitialized) onValueUpdated(reference)
|
||||
}
|
||||
}
|
||||
open fun onValueUpdated(reference: SettingReference<*, *>?) {}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.util.ui.HtmlPanel
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import java.awt.Font
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
|
||||
class DescriptionPanel(initialText: String? = null) : HtmlPanel() {
|
||||
private var bodyText: String? = initialText
|
||||
|
||||
fun updateText(text: String) {
|
||||
bodyText = text.asHtml()
|
||||
update()
|
||||
}
|
||||
|
||||
override fun getBody() = bodyText.orEmpty()
|
||||
|
||||
override fun getBodyFont(): Font = UIUtil.getButtonFont().deriveFont(Font.PLAIN)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import javax.swing.JComponent
|
||||
|
||||
interface Displayable {
|
||||
val component: JComponent
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.StringSettingComponent
|
||||
import javax.swing.Box
|
||||
import javax.swing.JSeparator
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
class PomWizardStepComponent(valuesReadingContext: ValuesReadingContext) : WizardStepComponent(valuesReadingContext) {
|
||||
private val groupIdComponent = StringSettingComponent(
|
||||
StructurePlugin::groupId.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
private val artifactIdComponent = StringSettingComponent(
|
||||
StructurePlugin::artifactId.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
private val versionComponent = StringSettingComponent(
|
||||
StructurePlugin::version.reference,
|
||||
valuesReadingContext,
|
||||
showLabel = true
|
||||
).asSubComponent()
|
||||
|
||||
|
||||
override val component = panel(VerticalLayout(0)) {
|
||||
add(Box.createVerticalStrut(10))
|
||||
add(JSeparator(SwingConstants.HORIZONTAL))
|
||||
add(Box.createVerticalStrut(10))
|
||||
add(groupIdComponent.component)
|
||||
add(artifactIdComponent.component)
|
||||
add(versionComponent.component)
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.components.JBList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ignore
|
||||
import javax.swing.DefaultListModel
|
||||
import javax.swing.JList
|
||||
import javax.swing.ListModel
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.event.ListDataEvent
|
||||
import javax.swing.event.ListDataListener
|
||||
|
||||
abstract class AbstractSingleSelectableListWithIcon<V>(initialValues: List<V> = emptyList()) : JBList<V>() {
|
||||
protected abstract fun ColoredListCellRenderer<V>.render(value: V)
|
||||
protected open fun onSelected(value: V?) {}
|
||||
|
||||
protected val model: DefaultListModel<V>
|
||||
get() = super.getModel() as DefaultListModel<V>
|
||||
|
||||
fun updateValues(newValues: List<V>) {
|
||||
setModel(createDefaultListModel(newValues))
|
||||
if (newValues.isNotEmpty()) {
|
||||
selectedIndex = 0
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
updateValues(initialValues)
|
||||
|
||||
selectionModel.addListSelectionListener { e ->
|
||||
if (e.valueIsAdjusting) return@addListSelectionListener
|
||||
val selectedValue = when (selectedIndex) {
|
||||
-1 -> null
|
||||
else -> this.model.getElementAt(selectedIndex)
|
||||
}
|
||||
onSelected(selectedValue)
|
||||
}
|
||||
|
||||
cellRenderer = object : ColoredListCellRenderer<V>() {
|
||||
override fun customizeCellRenderer(
|
||||
list: JList<out V>,
|
||||
value: V?,
|
||||
index: Int,
|
||||
selected: Boolean,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
render(value ?: return)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImmutableSingleSelectableListWithIcon<V>(
|
||||
values: List<V>,
|
||||
private val renderValue: ColoredListCellRenderer<V>.(V) -> Unit,
|
||||
emptyMessage: String? = null,
|
||||
private val onValueSelected: (V?) -> Unit = {}
|
||||
) : AbstractSingleSelectableListWithIcon<V>(values) {
|
||||
override fun ColoredListCellRenderer<V>.render(value: V) = renderValue(value)
|
||||
override fun onSelected(value: V?) = onValueSelected(value)
|
||||
|
||||
init {
|
||||
emptyMessage?.let { text ->
|
||||
emptyText.text = text
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
abstract class SubStep(
|
||||
val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
protected abstract fun buildContent(): JComponent
|
||||
|
||||
final override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
add(buildContent(), BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
|
||||
abstract class WizardStepComponent(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext)
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.layout.selectedValueIs
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.awt.event.ItemEvent
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JList
|
||||
|
||||
class DropDownComponent<T : DisplayableSettingItem>(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
initialValues: List<T> = emptyList(),
|
||||
labelText: String? = null,
|
||||
private val validator: SettingValidator<T> = settingValidator { ValidationResult.OK },
|
||||
private val iconProvider: (T) -> Icon? = { null },
|
||||
private val onAnyValueUpdate: (T) -> Unit = {}
|
||||
) : Component() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
|
||||
private var allowFireEventWhenValueUpdated: Boolean = true
|
||||
|
||||
private fun withoutActionFiring(action: () -> Unit) {
|
||||
val initialAllowFireEventWhenValueUpdated = allowFireEventWhenValueUpdated
|
||||
allowFireEventWhenValueUpdated = false
|
||||
action()
|
||||
allowFireEventWhenValueUpdated = initialAllowFireEventWhenValueUpdated
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val comboBox = ComboBox<T>(initialValues.toTypedArray<DisplayableSettingItem>() as Array<T>).apply {
|
||||
renderer = object : ColoredListCellRenderer<T>() {
|
||||
override fun customizeCellRenderer(
|
||||
list: JList<out T>,
|
||||
value: T?,
|
||||
index: Int,
|
||||
selected: Boolean,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
if (value == null) return
|
||||
icon = iconProvider(value)
|
||||
append(value.text)
|
||||
value.greyText?.let {
|
||||
append(" $it", SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
if (!this@apply.selectedValueIs(value).invoke()) {
|
||||
validator.validate(valuesReadingContext, value)
|
||||
.safeAs<ValidationResult.ValidationError>()
|
||||
?.messages
|
||||
?.firstOrNull()
|
||||
?.let { error ->
|
||||
append(" $error.", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addItemListener { e ->
|
||||
if (e?.stateChange == ItemEvent.SELECTED) {
|
||||
value = e.item as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateValues(newValues: List<T>) = withoutActionFiring {
|
||||
val oldValue = comboBox.selectedItem
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
comboBox.model = DefaultComboBoxModel(newValues.toTypedArray<DisplayableSettingItem>() as Array<T>)
|
||||
|
||||
if (newValues.isNotEmpty() && oldValue !in newValues) {
|
||||
value = newValues.first()
|
||||
}
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(comboBox, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
var value: T
|
||||
get() = comboBox.selectedItem as T
|
||||
set(value) {
|
||||
comboBox.selectedItem = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(newValue: T) {
|
||||
validate(newValue)
|
||||
if (allowFireEventWhenValueUpdated) {
|
||||
onAnyValueUpdate(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
initialValues.firstOrNull()?.let { first ->
|
||||
value = first
|
||||
validate(first)
|
||||
}
|
||||
}
|
||||
|
||||
fun validate(value: T = this.value) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, value)
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.textField
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class PathFieldComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val initialText: String = "",
|
||||
labelText: String? = "",
|
||||
private val validator: StringValidator = org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator { ValidationResult.OK },
|
||||
private val onSuccessValueUpdate: (String) -> Unit = {},
|
||||
private val onAnyValueUpdate: (String) -> Unit = {}
|
||||
) : Component() {
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
private val pathField = TextFieldWithBrowseButton(
|
||||
textField(initialText, this::fireValueChanged)
|
||||
).apply {
|
||||
addBrowseFolderListener(
|
||||
TextBrowseFolderListener(
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor(),
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(pathField, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
|
||||
var value: String
|
||||
get() = pathField.text
|
||||
set(value) {
|
||||
pathField.text = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(text: String) {
|
||||
validate(text)
|
||||
if (validationIndicator.validationState == ValidationResult.OK) {
|
||||
onSuccessValueUpdate(text)
|
||||
}
|
||||
onAnyValueUpdate(text)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
validate(initialText)
|
||||
}
|
||||
|
||||
private fun validate(text: String) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, text)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settingValidator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.textField
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class TextFieldComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val initialText: String = "",
|
||||
labelText: String? = "",
|
||||
private val validator: StringValidator = settingValidator { ValidationResult.OK },
|
||||
private val onSuccessValueUpdate: (String) -> Unit = {},
|
||||
private val onAnyValueUpdate: (String) -> Unit = {}
|
||||
) : Component() {
|
||||
private val validationIndicator = ValidationIndicator(defaultText = labelText, showText = true)
|
||||
private val textField = textField(initialText, this::fireValueChanged)
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(validationIndicator, BorderLayout.NORTH)
|
||||
add(textField, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
|
||||
var value: String
|
||||
get() = textField.text
|
||||
set(value) {
|
||||
textField.text = value
|
||||
fireValueChanged(value)
|
||||
}
|
||||
|
||||
private fun fireValueChanged(text: String) {
|
||||
validate(text)
|
||||
if (validationIndicator.validationState == ValidationResult.OK) {
|
||||
onSuccessValueUpdate(text)
|
||||
}
|
||||
onAnyValueUpdate(text)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
validate(initialText)
|
||||
}
|
||||
|
||||
private fun validate(text: String) {
|
||||
validationIndicator.validationState = validator.validate(valuesReadingContext, text)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import icons.GradleIcons
|
||||
import icons.MavenIcons
|
||||
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
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.BuildSystemType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
|
||||
|
||||
class BuildSystemTypeSettingComponent(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : SettingComponent<BuildSystemType, DropDownSettingType<BuildSystemType>>(
|
||||
BuildSystemPlugin::type.reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
private val listComponent = DropDownComponent(
|
||||
valuesReadingContext,
|
||||
setting.type.values,
|
||||
labelText = "Build System",
|
||||
validator = setting.validator,
|
||||
iconProvider = BuildSystemType::icon,
|
||||
onAnyValueUpdate = { value = it }
|
||||
).asSubComponent()
|
||||
|
||||
override val component = panel {
|
||||
add(listComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
if (reference == KotlinPlugin::projectKind.reference) {
|
||||
listComponent.component.updateUI()
|
||||
}
|
||||
listComponent.validate()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private val BuildSystemType.icon
|
||||
get() = when (this) {
|
||||
BuildSystemType.GradleKotlinDsl -> GradleIcons.Gradle
|
||||
BuildSystemType.GradleGroovyDsl -> GradleIcons.Gradle
|
||||
BuildSystemType.Maven -> MavenIcons.MavenLogo
|
||||
BuildSystemType.Jps -> AllIcons.Nodes.Module
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.IdeWizard
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
|
||||
class FirstWizardStepComponent(wizard: IdeWizard) : WizardStepComponent(wizard.valuesReadingContext) {
|
||||
private val buildSystemSubStep = BuildSystemSubStep(wizard.valuesReadingContext).asSubComponent()
|
||||
private val templatesSubStep = TemplatesSubStep(wizard.valuesReadingContext).asSubComponent()
|
||||
|
||||
override val component: JComponent = panel {
|
||||
add(templatesSubStep.component, BorderLayout.CENTER)
|
||||
add(buildSystemSubStep.component, BorderLayout.SOUTH)
|
||||
}
|
||||
}
|
||||
|
||||
class BuildSystemSubStep(valuesReadingContext: ValuesReadingContext) :
|
||||
SubStep(valuesReadingContext) {
|
||||
private val buildSystemSetting = BuildSystemTypeSettingComponent(valuesReadingContext).asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
bordered(needBottomEmptyBorder = false)
|
||||
add(buildSystemSetting.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
class TemplatesSubStep(valuesReadingContext: ValuesReadingContext) :
|
||||
SubStep(valuesReadingContext) {
|
||||
private val projectTemplateSettingComponent =
|
||||
ProjectTemplateSettingComponent(valuesReadingContext) { projectTemplate ->
|
||||
templateDescriptionComponent.setTemplate(projectTemplate)
|
||||
}.asSubComponent()
|
||||
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent().asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
add(projectTemplateSettingComponent.component, BorderLayout.CENTER)
|
||||
add(templateDescriptionComponent.component, BorderLayout.SOUTH)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
applySelectedTemplate()
|
||||
}
|
||||
|
||||
private fun applySelectedTemplate() {
|
||||
projectTemplateSettingComponent.value?.setsValues?.forEach { (setting, value) ->
|
||||
// TODO do not use settingContext directly
|
||||
context.settingContext[setting] = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
if (reference == ProjectTemplatesPlugin::template.reference) {
|
||||
applySelectedTemplate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateDescriptionComponent : Component() {
|
||||
private val descriptionPanel = DescriptionPanel()
|
||||
|
||||
fun setTemplate(template: ProjectTemplate) {
|
||||
descriptionPanel.updateText(template.htmlDescription)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
bordered()
|
||||
add(descriptionPanel, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.firstStep
|
||||
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.DropDownSettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates.ProjectTemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.ImmutableSingleSelectableListWithIcon
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.UiConstants
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.bordered
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ProjectTemplateSettingComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
private val onSelected: (ProjectTemplate) -> Unit
|
||||
) : SettingComponent<ProjectTemplate, DropDownSettingType<ProjectTemplate>>(
|
||||
ProjectTemplatesPlugin::template.reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
override val validationIndicator: ValidationIndicator? get() = null
|
||||
|
||||
private val list = ImmutableSingleSelectableListWithIcon(
|
||||
setting.type.values,
|
||||
renderValue = { value ->
|
||||
icon = KotlinIcons.SMALL_LOGO
|
||||
append(value.title)
|
||||
},
|
||||
onValueSelected = {
|
||||
onValueSelected(it!!)
|
||||
}
|
||||
).apply {
|
||||
border = BorderFactory.createEmptyBorder(
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE
|
||||
)
|
||||
}
|
||||
|
||||
private fun onValueSelected(selected: ProjectTemplate) {
|
||||
value = selected
|
||||
onSelected(selected)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel {
|
||||
bordered(needTopEmptyBorder = false, needInnerEmptyBorder = false)
|
||||
add(list, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
if (setting.type.values.isNotEmpty()) {
|
||||
list.selectedIndex = 0
|
||||
onValueSelected(setting.type.values.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.StringValidators
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.ModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.configuratorSettings
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.TextFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ModuleSettingsComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val validateModuleName =
|
||||
StringValidators.shouldNotBeBlank("Module name") and
|
||||
StringValidators.shouldBeValidIdentifier("Module Name")
|
||||
|
||||
private val moduleConfigurator = DropDownComponent<ModuleConfigurator>(
|
||||
valuesReadingContext,
|
||||
iconProvider = { it.icon },
|
||||
labelText = "Consider as"
|
||||
) { value ->
|
||||
module?.configurator = value
|
||||
}
|
||||
|
||||
private val moduleConfiguratorSettingsList = SettingsList(emptyList(), valuesReadingContext)
|
||||
|
||||
private val nameField = TextFieldComponent(
|
||||
valuesReadingContext,
|
||||
labelText = "Name",
|
||||
onAnyValueUpdate = { value ->
|
||||
module?.name = value
|
||||
eventManager.fireListeners(null)
|
||||
},
|
||||
validator = validateModuleName
|
||||
).asSubComponent()
|
||||
|
||||
|
||||
override val component: JComponent = panel(VerticalLayout(5)) {
|
||||
border = BorderFactory.createEmptyBorder(
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE,
|
||||
UiConstants.GAP_BORDER_SIZE
|
||||
)
|
||||
add(nameField.component)
|
||||
add(moduleConfigurator.component)
|
||||
add(moduleConfiguratorSettingsList.component)
|
||||
}
|
||||
|
||||
var module: Module? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (value != null) {
|
||||
updateModule(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateModule(module: Module) {
|
||||
nameField.value = module.name
|
||||
val newValues = ModuleConfigurator.BY_MODULE_KIND.getValue(module.configurator.moduleKind)
|
||||
.filter { configurator ->
|
||||
if (configurator.moduleKind == ModuleKind.target)
|
||||
configurator.moduleType == module.configurator.moduleType
|
||||
else false
|
||||
}
|
||||
|
||||
nameField.component.isVisible = module.kind != ModuleKind.target
|
||||
|| module.configurator.moduleType != ModuleType.common
|
||||
|
||||
moduleConfiguratorSettingsList.setSettings(module.configuratorSettings)
|
||||
|
||||
moduleConfigurator.apply {
|
||||
updateValues(newValues)
|
||||
component.isVisible = newValues.size > 1
|
||||
value = module.configurator
|
||||
component.updateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.util.ui.StatusText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import java.awt.Graphics
|
||||
import javax.swing.JPanel
|
||||
|
||||
class NothingSelectedComponent : Component() {
|
||||
override val component: JPanel = object : JPanel() {
|
||||
override fun paint(g: Graphics?) {
|
||||
super.paint(g)
|
||||
statusText.paint(this, g)
|
||||
}
|
||||
}
|
||||
|
||||
private val statusText = object : StatusText(component) {
|
||||
override fun isStatusVisible() = true
|
||||
|
||||
init {
|
||||
appendText("Neither Module nor Sourceset is selected")
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.IdeWizard
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor.ModulesEditorComponent
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
class SecondStepWizardComponent(wizard: IdeWizard) : WizardStepComponent(wizard.valuesReadingContext) {
|
||||
private val moduleEditorSubStep =
|
||||
ModulesEditorSubStep(wizard.valuesReadingContext, ::onNodeSelected).asSubComponent()
|
||||
private val templatesSubStep = ModuleSettingsSubStep(wizard).asSubComponent()
|
||||
|
||||
override val component = splitterFor(
|
||||
moduleEditorSubStep.component,
|
||||
templatesSubStep.component
|
||||
)
|
||||
|
||||
private fun onNodeSelected(data: DisplayableSettingItem?) {
|
||||
templatesSubStep.selectedNode = data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ModulesEditorSubStep(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
onNodeSelected: (data: DisplayableSettingItem?) -> Unit
|
||||
) : SubStep(valuesReadingContext) {
|
||||
private val moduleSettingComponent = ModulesEditorComponent(
|
||||
valuesReadingContext,
|
||||
onNodeSelected
|
||||
).asSubComponent()
|
||||
|
||||
override fun buildContent(): JComponent = panel {
|
||||
bordered(needInnerEmptyBorder = false, needLineBorder = false)
|
||||
border = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(0, 0, 0, UiConstants.GAP_BORDER_SIZE),
|
||||
border
|
||||
)
|
||||
add(moduleSettingComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ModuleSettingsSubStep(wizard: IdeWizard) : SubStep(wizard.valuesReadingContext) {
|
||||
private val sourcesetSettingsComponent = SourcesetSettingsComponent(wizard.valuesReadingContext).asSubComponent()
|
||||
private val moduleSettingsComponent = ModuleSettingsComponent(valuesReadingContext).asSubComponent()
|
||||
private val nothingSelectedComponent = NothingSelectedComponent().asSubComponent()
|
||||
|
||||
private val panel = panel {
|
||||
bordered()
|
||||
add(nothingSelectedComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
var selectedNode: DisplayableSettingItem? = null
|
||||
set(value) {
|
||||
field = value
|
||||
sourcesetSettingsComponent.sourceset = value as? Sourceset
|
||||
moduleSettingsComponent.module = value as? Module
|
||||
changeComponent()
|
||||
}
|
||||
|
||||
private fun changeComponent() {
|
||||
panel.removeAll()
|
||||
val component = when (selectedNode) {
|
||||
is Sourceset -> sourcesetSettingsComponent
|
||||
is Module -> moduleSettingsComponent
|
||||
else -> nothingSelectedComponent
|
||||
}
|
||||
panel.add(component.component, BorderLayout.CENTER)
|
||||
panel.updateUI()
|
||||
}
|
||||
|
||||
override fun buildContent() = panel
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionToolbarPosition
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.ui.ColoredListCellRenderer
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.IOSSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JComponent
|
||||
|
||||
class SourcesetDependenciesSettingsComponent(
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private val sourcesetDependenciesList = SourcesetDependenciesList()
|
||||
|
||||
private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(sourcesetDependenciesList).apply {
|
||||
setToolbarPosition(ActionToolbarPosition.RIGHT)
|
||||
setAddAction {
|
||||
val dialog = ChooseModuleDialog(allModulesExcludingCurrent, component)
|
||||
if (dialog.showAndGet()) {
|
||||
dialog.selectedModule?.let(sourcesetDependenciesList::addDependency)
|
||||
}
|
||||
}
|
||||
setRemoveAction {
|
||||
sourcesetDependenciesList.removeSelected()
|
||||
}
|
||||
setMoveDownAction(null)
|
||||
setMoveUpAction(null)
|
||||
}
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
sourcesetDependenciesList.sourceset = value
|
||||
}
|
||||
|
||||
|
||||
private val allModulesExcludingCurrent: List<Module>
|
||||
get() {
|
||||
val currentModule = sourceset?.parent
|
||||
|
||||
return KotlinPlugin::modules.value!!
|
||||
.withAllSubModules()
|
||||
.filter { module ->
|
||||
if (sourceset in module.sourcesets) return@filter false
|
||||
if (currentModule?.kind == ModuleKind.target && currentModule in module.subModules)
|
||||
return@filter false
|
||||
|
||||
if (module.configurator == AndroidSinglePlatformModuleConfigurator
|
||||
|| module.configurator == IOSSinglePlatformModuleConfigurator
|
||||
) return@filter false
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override val component = panel {
|
||||
add(toolbarDecorator.createPanelWithPopupHandler(sourcesetDependenciesList), BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
private class ChooseModuleDialog(modules: List<Module>, parent: JComponent) : DialogWrapper(parent, false) {
|
||||
private val list = ImmutableSingleSelectableListWithIcon(
|
||||
values = modules,
|
||||
emptyMessage = "There are no dependencies to add",
|
||||
renderValue = { value ->
|
||||
renderModule(value)
|
||||
}
|
||||
).bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
|
||||
override fun createCenterPanel() = panel {
|
||||
preferredSize = Dimension(preferredSize.width, 300)
|
||||
add(
|
||||
label("Please select the modules to add as dependencies:")
|
||||
.bordered(
|
||||
needLineBorder = false,
|
||||
needInnerEmptyBorder = false,
|
||||
needTopEmptyBorder = false
|
||||
),
|
||||
BorderLayout.NORTH
|
||||
)
|
||||
add(list, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
val selectedModule: Module?
|
||||
get() = list.selectedValue
|
||||
|
||||
init {
|
||||
title = "Choose Modules"
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
private class SourcesetDependenciesList : AbstractSingleSelectableListWithIcon<Module>() {
|
||||
override fun ColoredListCellRenderer<Module>.render(value: Module) {
|
||||
renderModule(value)
|
||||
}
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
updateValues(
|
||||
value?.dependencies?.mapNotNull { it.safeAs<ModuleBasedSourcesetDependency>()?.module } ?: return
|
||||
)
|
||||
updateUI()
|
||||
}
|
||||
|
||||
|
||||
fun addDependency(dependency: Module) {
|
||||
model.addElement(dependency)
|
||||
sourceset?.let { it.dependencies += ModuleBasedSourcesetDependency(dependency) }
|
||||
}
|
||||
|
||||
fun removeSelected() {
|
||||
val index = selectedIndex
|
||||
model.removeElementAt(selectedIndex)
|
||||
sourceset?.let { it.dependencies = it.dependencies.toMutableList().also { it.removeAt(index) } }
|
||||
if (model.size() > 0) {
|
||||
selectedIndex = index.coerceAtMost(model.size - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ColoredListCellRenderer<Module>.renderModule(module: Module) {
|
||||
append(module.path.asString())
|
||||
append(" ")
|
||||
append("(${module.greyText})", SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
icon = module.icon
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.ui.components.JBTabbedPane
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
|
||||
class SourcesetSettingsComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val templatesComponent = TemplatesComponent(valuesReadingContext).asSubComponent()
|
||||
private val dependenciesComponent = SourcesetDependenciesSettingsComponent(valuesReadingContext).asSubComponent()
|
||||
override val component = JBTabbedPane().apply {
|
||||
add("Template", templatesComponent.component)
|
||||
add("Dependencies", dependenciesComponent.component)
|
||||
}
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
dependenciesComponent.sourceset = value
|
||||
templatesComponent.sourceset = value
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MessagesEx
|
||||
import com.intellij.ui.JBSplitter
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.util.ui.StatusText
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.Template
|
||||
import org.jetbrains.kotlin.tools.projectWizard.templates.settings
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingsList
|
||||
import java.awt.*
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
class TemplatesComponent(valuesReadingContext: ValuesReadingContext) : DynamicComponent(valuesReadingContext) {
|
||||
private val chooseTemplateComponent: ChooseTemplateComponent =
|
||||
ChooseTemplateComponent(valuesReadingContext) { template ->
|
||||
sourceset?.template = template
|
||||
switchState(template)
|
||||
}
|
||||
|
||||
private val templateSettingsComponent = TemplateSettingsComponent(valuesReadingContext) {
|
||||
if (MessagesEx.showOkCancelDialog(
|
||||
component,
|
||||
"Do you want to remove selected template from module",
|
||||
"Remove selected template",
|
||||
"Remove",
|
||||
"Cancel",
|
||||
null
|
||||
) == Messages.OK
|
||||
) {
|
||||
switchState(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchState(selectedTemplate: Template?) {
|
||||
panel.removeAll()
|
||||
when (selectedTemplate) {
|
||||
null -> panel.add(chooseTemplateComponent.component, BorderLayout.CENTER)
|
||||
else -> {
|
||||
if (sourceset != null) {
|
||||
templateSettingsComponent.setTemplate(sourceset!!, selectedTemplate)
|
||||
}
|
||||
panel.add(templateSettingsComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
panel.updateUI()
|
||||
}
|
||||
|
||||
val panel = panel {
|
||||
add(chooseTemplateComponent.component, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel
|
||||
|
||||
var sourceset: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
switchState(value?.template)
|
||||
chooseTemplateComponent.selectedModule = value
|
||||
}
|
||||
}
|
||||
|
||||
class ChooseTemplateComponent(
|
||||
private val valuesReadingContext: ValuesReadingContext,
|
||||
private val onTemplateChosen: (Template) -> Unit
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private enum class State(val text: String) {
|
||||
MODULE_SELECTED_AND_TEMPLATES_AVAILABLE("You can configure a template for selected module"),
|
||||
MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE("No templates available for selected module"),
|
||||
NO_MODULE_SELECTED("Please select a module to configure")
|
||||
}
|
||||
|
||||
override val component: JPanel = object : JPanel() {
|
||||
override fun paint(g: Graphics?) {
|
||||
super.paint(g)
|
||||
statusText.paint(this, g)
|
||||
}
|
||||
}
|
||||
|
||||
private val statusText = object : StatusText(component) {
|
||||
override fun isStatusVisible() = true
|
||||
}
|
||||
|
||||
private val allTemplates
|
||||
get() = with(valuesReadingContext) {
|
||||
TemplatesPlugin::templates.propertyValue
|
||||
}
|
||||
|
||||
var selectedModule: Sourceset? = null
|
||||
set(value) {
|
||||
field = value
|
||||
onStateUpdated()
|
||||
}
|
||||
|
||||
init {
|
||||
onStateUpdated()
|
||||
}
|
||||
|
||||
private val availableTemplates
|
||||
get() = allTemplates.values.filter { template ->
|
||||
selectedModule!!.containingModuleType in template.moduleTypes
|
||||
&& selectedModule!!.sourcesetType in template.sourcesetTypes
|
||||
}
|
||||
|
||||
private val state: State
|
||||
get() = when {
|
||||
selectedModule == null -> State.NO_MODULE_SELECTED
|
||||
availableTemplates.isEmpty() -> State.MODULE_SELECTED_AND_NO_TEMPLATES_AVAILABLE
|
||||
else -> State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE
|
||||
}
|
||||
|
||||
private fun onStateUpdated() {
|
||||
statusText.clear()
|
||||
statusText.appendText(state.text)
|
||||
if (state == State.MODULE_SELECTED_AND_TEMPLATES_AVAILABLE) {
|
||||
statusText.appendSecondaryText("Configure", SimpleTextAttributes.LINK_ATTRIBUTES) {
|
||||
showDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun showDialog() {
|
||||
val availableTemplates = availableTemplates
|
||||
if (availableTemplates.isEmpty()) {
|
||||
MessagesEx.error(null, "No templates available for the selected module")
|
||||
} else {
|
||||
val dialog = TemplateListDialog(availableTemplates, component)
|
||||
if (dialog.showAndGet()) {
|
||||
onTemplateChosen(dialog.selectedTemplate ?: return)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TemplateListDialog(
|
||||
values: List<Template>,
|
||||
parent: JComponent
|
||||
) : DialogWrapper(parent, false) {
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent(
|
||||
needRemoveButton = false
|
||||
).apply {
|
||||
component.bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
}
|
||||
private val templatesList = ImmutableSingleSelectableListWithIcon(
|
||||
values = values,
|
||||
renderValue = { value ->
|
||||
icon = when {
|
||||
value.moduleTypes.size == 1 -> value.moduleTypes.single().icon
|
||||
else -> KotlinIcons.MPP
|
||||
}
|
||||
append(value.title)
|
||||
},
|
||||
onValueSelected = { templateDescriptor ->
|
||||
templateDescriptionComponent.updateSelectedTemplate(templateDescriptor)
|
||||
}
|
||||
).bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
|
||||
val selectedTemplate: Template?
|
||||
get() = templatesList.selectedValue
|
||||
|
||||
init {
|
||||
title = "Choose a template to configure"
|
||||
init()
|
||||
if (values.isNotEmpty()) {
|
||||
templateDescriptionComponent.updateSelectedTemplate(values.first())
|
||||
}
|
||||
}
|
||||
|
||||
override fun createCenterPanel() = panel(BorderLayout()) {
|
||||
preferredSize = Dimension(700, 300)
|
||||
val splitter = JBSplitter(false, .3f).apply {
|
||||
firstComponent = templatesList
|
||||
secondComponent = templateDescriptionComponent.component
|
||||
orientation = false
|
||||
}
|
||||
add(splitter, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateDescriptionComponent(
|
||||
needRemoveButton: Boolean = false,
|
||||
private val nonDefaultBackgroundColor: Color? = null,
|
||||
onRemoveButtonClicked: () -> Unit = {}
|
||||
) : Component() {
|
||||
private val removeButton = if (needRemoveButton) {
|
||||
hyperlinkLabel("Remove template", onClick = onRemoveButtonClicked)
|
||||
} else null
|
||||
|
||||
private val titleLabel = label("", bold = true) {
|
||||
font = UIUtil.getListFont().let { font ->
|
||||
Font(font.family, Font.BOLD, (font.size * 1.3).toInt())
|
||||
}
|
||||
}
|
||||
|
||||
private val title = panel {
|
||||
nonDefaultBackgroundColor?.let { background = it }
|
||||
add(titleLabel, BorderLayout.CENTER)
|
||||
removeButton?.let { add(it, BorderLayout.EAST) }
|
||||
}
|
||||
|
||||
private val descriptionLabel = DescriptionPanel()
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
nonDefaultBackgroundColor?.let { background = it }
|
||||
add(title, BorderLayout.NORTH)
|
||||
add(descriptionLabel, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setComponentsVisibility(isVisible: Boolean) {
|
||||
titleLabel.isVisible = isVisible
|
||||
descriptionLabel.isVisible = isVisible
|
||||
}
|
||||
|
||||
fun updateSelectedTemplate(template: Template?) {
|
||||
setComponentsVisibility(template != null)
|
||||
if (template != null) {
|
||||
titleLabel.text = template.title
|
||||
descriptionLabel.updateText(template.htmlDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TemplateSettingsComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
removeTemplate: () -> Unit
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private val templateDescriptionComponent = TemplateDescriptionComponent(
|
||||
needRemoveButton = true,
|
||||
nonDefaultBackgroundColor = UIUtil.getEditorPaneBackground(),
|
||||
onRemoveButtonClicked = removeTemplate
|
||||
).apply {
|
||||
component.bordered(needTopEmptyBorder = false, needBottomEmptyBorder = false)
|
||||
}
|
||||
|
||||
private val settings = SettingsList(emptyList(), valuesReadingContext).apply {
|
||||
component.bordered()
|
||||
}
|
||||
|
||||
fun setTemplate(sourceset: Sourceset, selectedTemplate: Template) {
|
||||
settings.setSettings(selectedTemplate.settings(sourceset))
|
||||
templateDescriptionComponent.updateSelectedTemplate(selectedTemplate)
|
||||
}
|
||||
|
||||
override val component: JComponent = splitterFor(
|
||||
templateDescriptionComponent.component,
|
||||
settings.component,
|
||||
vertical = true
|
||||
).apply {
|
||||
(this as JBSplitter).proportion = .7f
|
||||
}
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.openapi.ui.popup.ListPopup
|
||||
import com.intellij.openapi.ui.popup.PopupStep
|
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
||||
import com.intellij.ui.popup.PopupFactoryImpl
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.fullText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.asHtml
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.htmlText
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.icon
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.Icon
|
||||
|
||||
class CreateModuleOrTargetPopup private constructor(
|
||||
private val target: Module?,
|
||||
private val allowMultiplatform: Boolean,
|
||||
private val allowAndroid: Boolean,
|
||||
private val allowIos: Boolean,
|
||||
private val createTarget: (TargetConfigurator) -> Unit,
|
||||
private val createModule: (ModuleConfigurator) -> Unit
|
||||
) {
|
||||
|
||||
private fun TargetConfigurator.needToShow(): Boolean {
|
||||
val allTargets = target?.subModules ?: return false
|
||||
return canCoexistsWith(allTargets.map { it.configurator as TargetConfigurator })
|
||||
}
|
||||
|
||||
private fun DisplayableSettingItem.needToShow(): Boolean = when (this) {
|
||||
is TargetConfigurator -> needToShow()
|
||||
is TargetConfiguratorGroupWithSubItems -> subItems.any { it.needToShow() }
|
||||
else -> false
|
||||
}
|
||||
|
||||
private inner class ChooseModuleOrMppModuleStep : BaseListPopupStep<ModuleConfigurator>(
|
||||
"Module Type",
|
||||
buildList {
|
||||
if (allowMultiplatform) +MppModuleConfigurator
|
||||
+JvmSinglePlatformModuleConfigurator
|
||||
if (allowAndroid) +AndroidSinglePlatformModuleConfigurator
|
||||
|
||||
//todo ios support
|
||||
//if (allowIos) +IOSSinglePlatformModuleConfigurator
|
||||
}
|
||||
) {
|
||||
override fun getIconFor(value: ModuleConfigurator): Icon = value.icon
|
||||
override fun getTextFor(value: ModuleConfigurator): String = value.htmlText
|
||||
|
||||
override fun onChosen(selectedValue: ModuleConfigurator?, finalChoice: Boolean): PopupStep<*>? =
|
||||
when (selectedValue) {
|
||||
null -> PopupStep.FINAL_CHOICE
|
||||
else -> {
|
||||
createModule(selectedValue)
|
||||
PopupStep.FINAL_CHOICE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ChooseTargetTypeStep(
|
||||
targetConfiguratorGroup: TargetConfiguratorGroupWithSubItems,
|
||||
showTitle: Boolean
|
||||
) : BaseListPopupStep<DisplayableSettingItem>(
|
||||
"Target".takeIf { showTitle },
|
||||
targetConfiguratorGroup.subItems.filter { it.needToShow() }
|
||||
) {
|
||||
override fun getIconFor(value: DisplayableSettingItem): Icon? = when (value) {
|
||||
is DisplayableTargetConfiguratorGroup -> value.moduleType.icon
|
||||
is ModuleConfigurator -> value.moduleType.icon
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getTextFor(value: DisplayableSettingItem): String = value.htmlText
|
||||
|
||||
override fun onChosen(selectedValue: DisplayableSettingItem?, finalChoice: Boolean): PopupStep<*>? {
|
||||
when {
|
||||
finalChoice && selectedValue is TargetConfigurator -> createTarget(selectedValue)
|
||||
selectedValue is TargetConfiguratorGroupWithSubItems ->
|
||||
return ChooseTargetTypeStep(selectedValue, showTitle = false)
|
||||
}
|
||||
return PopupStep.FINAL_CHOICE
|
||||
}
|
||||
}
|
||||
|
||||
private fun create(): ListPopup? = when (target?.kind) {
|
||||
ModuleKind.target -> null
|
||||
ModuleKind.multiplatform -> ChooseTargetTypeStep(TargetConfigurationGroups.FIRST, showTitle = true)
|
||||
else -> when {
|
||||
allowMultiplatform || allowAndroid || allowIos -> ChooseModuleOrMppModuleStep()
|
||||
else -> {
|
||||
createModule(JvmSinglePlatformModuleConfigurator)
|
||||
null
|
||||
}
|
||||
}
|
||||
}?.let { PopupFactoryImpl.getInstance().createListPopup(it) }
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
target: Module?,
|
||||
allowMultiplatform: Boolean,
|
||||
allowAndroid: Boolean,
|
||||
allowIos: Boolean,
|
||||
createTarget: (TargetConfigurator) -> Unit,
|
||||
createModule: (ModuleConfigurator) -> Unit
|
||||
) = CreateModuleOrTargetPopup(
|
||||
target = target,
|
||||
allowMultiplatform = allowMultiplatform,
|
||||
allowAndroid = allowAndroid,
|
||||
allowIos = allowIos,
|
||||
createTarget = createTarget,
|
||||
createModule = createModule
|
||||
).create()
|
||||
}
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.ui.JBColor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ListSettingType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ProjectKind
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.SettingComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting.ValidationIndicator
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.JComponent
|
||||
|
||||
|
||||
class ModulesEditorComponent(
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
oneEntrySelected: (data: DisplayableSettingItem?) -> Unit
|
||||
) : SettingComponent<List<Module>, ListSettingType<Module>>(KotlinPlugin::modules.reference, valuesReadingContext) {
|
||||
private val tree: ModulesEditorTree =
|
||||
ModulesEditorTree(
|
||||
onSelected = { oneEntrySelected(it) },
|
||||
addModule = { component ->
|
||||
val isMppProject = KotlinPlugin::projectKind.value == ProjectKind.Multiplatform
|
||||
moduleCreator.create(
|
||||
target = null, // The empty tree case
|
||||
allowMultiplatform = isMppProject,
|
||||
allowAndroid = isMppProject,
|
||||
allowIos = isMppProject,
|
||||
allModules = value ?: emptyList(),
|
||||
createModule = { model.add(it) }
|
||||
)?.showInCenterOf(component)
|
||||
}
|
||||
)
|
||||
|
||||
private val model = TargetsModel(tree, ::value)
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
model.update()
|
||||
}
|
||||
|
||||
private val moduleCreator = NewModuleCreator()
|
||||
|
||||
private val toolbarDecorator = ModulesEditorToolbarDecorator(
|
||||
tree = tree,
|
||||
moduleCreator = moduleCreator,
|
||||
model = model,
|
||||
getModules = { value ?: emptyList() },
|
||||
isMultiplatformProject = { KotlinPlugin::projectKind.value == ProjectKind.Multiplatform }
|
||||
)
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
border = BorderFactory.createLineBorder(JBColor.border())
|
||||
add(toolbarDecorator.createToolPanel(), BorderLayout.CENTER)
|
||||
add(validationIndicator, BorderLayout.SOUTH)
|
||||
}
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = true).apply {
|
||||
background = tree.background
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionToolbarPosition
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.IOSSinglePlatformModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.MppModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.createPanelWithPopupHandler
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ModulesEditorToolbarDecorator(
|
||||
private val tree: ModulesEditorTree,
|
||||
private val moduleCreator: NewModuleCreator,
|
||||
private val model: TargetsModel,
|
||||
private val getModules: () -> List<Module>,
|
||||
private val isMultiplatformProject: () -> Boolean
|
||||
) {
|
||||
private val toolbarDecorator: ToolbarDecorator = ToolbarDecorator.createDecorator(tree).apply {
|
||||
setToolbarPosition(ActionToolbarPosition.TOP)
|
||||
setAddAction { button ->
|
||||
val allModules = getModules().withAllSubModules(includeSourcesets = false)
|
||||
val target = tree.selectedSettingItem?.safeAs<Module>()
|
||||
val isRootModule = target == null
|
||||
val popup = moduleCreator.create(
|
||||
target = tree.selectedSettingItem?.safeAs(),
|
||||
allowMultiplatform = isRootModule
|
||||
&& allModules.none { it.configurator == MppModuleConfigurator },
|
||||
allowAndroid = isRootModule
|
||||
&& isMultiplatformProject()
|
||||
&& allModules.none { it.configurator == AndroidSinglePlatformModuleConfigurator },
|
||||
allowIos = isRootModule
|
||||
&& isMultiplatformProject()
|
||||
&& allModules.none { it.configurator == IOSSinglePlatformModuleConfigurator },
|
||||
allModules = allModules,
|
||||
createModule = model::add
|
||||
)
|
||||
popup?.show(button.preferredPopupPoint!!)
|
||||
}
|
||||
setAddActionUpdater { event ->
|
||||
event.presentation.apply {
|
||||
isEnabled = when (val selected = tree.selectedSettingItem) {
|
||||
is Module -> selected.configurator.canContainSubModules
|
||||
is Sourceset -> false
|
||||
null -> true
|
||||
else -> false
|
||||
}
|
||||
text = "Add" + when (tree.selectedSettingItem?.safeAs<Module>()?.kind) {
|
||||
ModuleKind.multiplatform -> " Target"
|
||||
ModuleKind.singleplatform -> " Module"
|
||||
ModuleKind.target -> ""
|
||||
null -> ""
|
||||
}
|
||||
}
|
||||
event.presentation.isEnabled
|
||||
}
|
||||
setRemoveAction {
|
||||
val moduleKindText = selectedModuleKindText ?: "Module"
|
||||
if (Messages.showOkCancelDialog(
|
||||
tree,
|
||||
buildString {
|
||||
appendln("Do you want to remove selected $moduleKindText?")
|
||||
if (tree.selectedSettingItem.safeAs<Module>()?.kind != ModuleKind.target) {
|
||||
appendln("This will also remove all submodules.")
|
||||
}
|
||||
appendln()
|
||||
appendln("This action cannot be undone.")
|
||||
},
|
||||
"Remove selected $moduleKindText?",
|
||||
"Remove",
|
||||
"Cancel",
|
||||
AllIcons.General.QuestionDialog
|
||||
) == Messages.OK
|
||||
) {
|
||||
model.removeSelected()
|
||||
}
|
||||
}
|
||||
setRemoveActionUpdater { event ->
|
||||
event.presentation.apply {
|
||||
isEnabled = tree.selectedSettingItem is Module
|
||||
text = "Remove" + selectedModuleKindText?.let { " $it" }.orEmpty()
|
||||
}
|
||||
event.presentation.isEnabled
|
||||
}
|
||||
|
||||
setMoveDownAction(null)
|
||||
setMoveUpAction(null)
|
||||
}
|
||||
|
||||
private val selectedModuleKindText
|
||||
get() = tree.selectedSettingItem.safeAs<Module>()?.kindText
|
||||
|
||||
fun createToolPanel(): JComponent = toolbarDecorator
|
||||
.createPanelWithPopupHandler(tree)
|
||||
.apply {
|
||||
border = null
|
||||
}
|
||||
}
|
||||
|
||||
private val Module.kindText
|
||||
get() = when (kind) {
|
||||
ModuleKind.multiplatform -> "Module"
|
||||
ModuleKind.singleplatform -> "Module"
|
||||
ModuleKind.target -> "Target"
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ui.ColoredTreeCellRenderer
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Sourceset
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.icon
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JTree
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.DefaultTreeModel
|
||||
import javax.swing.tree.TreeSelectionModel
|
||||
|
||||
class ModulesEditorTree(
|
||||
private val onSelected: (DisplayableSettingItem?) -> Unit,
|
||||
private val addModule: (JComponent) -> Unit
|
||||
) : Tree(DefaultMutableTreeNode(PROJECT_USER_OBJECT)) {
|
||||
val model: DefaultTreeModel get() = super.getModel() as DefaultTreeModel
|
||||
|
||||
@Suppress("ClassName")
|
||||
object PROJECT_USER_OBJECT
|
||||
|
||||
fun reload() {
|
||||
val openPaths = (0 until rowCount).mapNotNull { row ->
|
||||
getPathForRow(row).takeIf { isExpanded(it) }
|
||||
}
|
||||
model.reload()
|
||||
openPaths.forEach(::expandPath)
|
||||
}
|
||||
|
||||
val selectedSettingItem
|
||||
get() = selectedNode?.userObject?.safeAs<DisplayableSettingItem>()
|
||||
|
||||
val selectedNode
|
||||
get() = getSelectedNodes(DefaultMutableTreeNode::class.java) {
|
||||
it.userObject is DisplayableSettingItem
|
||||
}.singleOrNull()
|
||||
|
||||
|
||||
init {
|
||||
getSelectionModel().selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
|
||||
setShowsRootHandles(true)
|
||||
|
||||
addTreeSelectionListener {
|
||||
onSelected(selectedSettingItem)
|
||||
}
|
||||
|
||||
emptyText.clear()
|
||||
emptyText.appendText("No modules created")
|
||||
emptyText.appendSecondaryText(
|
||||
"Add a module to the project",
|
||||
SimpleTextAttributes.LINK_PLAIN_ATTRIBUTES
|
||||
) {
|
||||
addModule(this)
|
||||
}
|
||||
|
||||
setCellRenderer(object : ColoredTreeCellRenderer() {
|
||||
override fun customizeCellRenderer(
|
||||
tree: JTree,
|
||||
value: Any?,
|
||||
selected: Boolean,
|
||||
expanded: Boolean,
|
||||
leaf: Boolean,
|
||||
row: Int,
|
||||
hasFocus: Boolean
|
||||
) {
|
||||
if (value?.safeAs<DefaultMutableTreeNode>()?.userObject == PROJECT_USER_OBJECT) {
|
||||
icon = AllIcons.Nodes.Project
|
||||
append("Project")
|
||||
return
|
||||
}
|
||||
val setting = (value as? DefaultMutableTreeNode)?.userObject as? DisplayableSettingItem
|
||||
?: return
|
||||
icon = when (setting) {
|
||||
is Module -> setting.icon
|
||||
is Sourceset -> AllIcons.Nodes.Module
|
||||
else -> null
|
||||
}
|
||||
append(setting.text)
|
||||
setting.greyText?.let { greyText ->
|
||||
append(" ")
|
||||
append("($greyText)", SimpleTextAttributes.GRAYED_ATTRIBUTES)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.TargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
|
||||
class NewModuleCreator {
|
||||
private fun suggestName(name: String, modules: List<Module>): String {
|
||||
val names = modules.map(Module::name).toSet()
|
||||
if (name !in names) return name
|
||||
var index = 1
|
||||
while ("${name}_$index" in names) {
|
||||
index++
|
||||
}
|
||||
return "${name}_$index"
|
||||
}
|
||||
|
||||
private fun newTarget(
|
||||
configurator: TargetConfigurator,
|
||||
allTargets: List<Module>
|
||||
): Module = MultiplatformTargetModule(
|
||||
suggestName(
|
||||
configurator.suggestedModuleName ?: configurator.moduleType.name,
|
||||
allTargets
|
||||
),
|
||||
configurator,
|
||||
SourcesetType.values().map { sourcesetType ->
|
||||
Sourceset(
|
||||
sourcesetType,
|
||||
configurator.moduleType,
|
||||
template = null,
|
||||
dependencies = emptyList()
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
fun create(
|
||||
target: Module?,
|
||||
allowMultiplatform: Boolean,
|
||||
allowAndroid: Boolean,
|
||||
allowIos: Boolean,
|
||||
allModules: List<Module>,
|
||||
createModule: (Module) -> Unit
|
||||
) = CreateModuleOrTargetPopup.create(
|
||||
target = target,
|
||||
allowMultiplatform = allowMultiplatform,
|
||||
allowAndroid = allowAndroid,
|
||||
allowIos = allowIos,
|
||||
createTarget = { targetConfigurator ->
|
||||
createModule(newTarget(targetConfigurator, target?.subModules.orEmpty()))
|
||||
},
|
||||
createModule = { configurator ->
|
||||
val name = suggestName(configurator.suggestedModuleName ?: "module", allModules)
|
||||
val sourcesets = when (configurator.moduleKind) {
|
||||
ModuleKind.multiplatform -> emptyList()
|
||||
else -> SourcesetType.values().map { sourcesetType ->
|
||||
Sourceset(
|
||||
sourcesetType,
|
||||
ModuleType.jvm,
|
||||
template = null,
|
||||
dependencies = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
val createdModule = Module(
|
||||
name,
|
||||
configurator.moduleKind,
|
||||
configurator,
|
||||
sourcesets,
|
||||
emptyList()
|
||||
)
|
||||
createModule(createdModule)
|
||||
}
|
||||
)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.secondStep.modulesEditor
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.TreePath
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
class TargetsModel(
|
||||
private val tree: ModulesEditorTree,
|
||||
private val value: KMutableProperty0<List<Module>?>
|
||||
) {
|
||||
private val root get() = tree.model.root as DefaultMutableTreeNode
|
||||
|
||||
private fun addToTheTree(module: Module, modifyValue: Boolean, parent: DefaultMutableTreeNode = root) {
|
||||
val pathsToExpand = mutableListOf<TreePath>()
|
||||
|
||||
fun add(moduleToAdd: Module, parentToAdd: DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode(moduleToAdd).apply {
|
||||
val userObject = parentToAdd.userObject
|
||||
when {
|
||||
userObject is Module && userObject.kind == ModuleKind.singleplatform -> {
|
||||
val indexOfLastModule = parent.children()
|
||||
.toList()
|
||||
.indexOfLast {
|
||||
it.safeAs<DefaultMutableTreeNode>()?.userObject is Module
|
||||
}
|
||||
if (indexOfLastModule == -1) parent.insert(this, 0)
|
||||
else parent.insert(this, indexOfLastModule)
|
||||
}
|
||||
else -> parentToAdd.add(this)
|
||||
}
|
||||
pathsToExpand += TreePath(path)
|
||||
|
||||
moduleToAdd.subModules.forEach { subModule ->
|
||||
add(subModule, this)
|
||||
}
|
||||
moduleToAdd.sourcesets.forEach { sourceset ->
|
||||
val sourcesetNode = DefaultMutableTreeNode(sourceset)
|
||||
add(sourcesetNode)
|
||||
pathsToExpand += TreePath(sourcesetNode.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(module, parent)
|
||||
|
||||
if (modifyValue) {
|
||||
when (val parentModule = parent.userObject) {
|
||||
ModulesEditorTree.PROJECT_USER_OBJECT -> value.set(value.get().orEmpty() + module)
|
||||
is Module -> parentModule.subModules += module
|
||||
}
|
||||
}
|
||||
tree.reload()
|
||||
pathsToExpand.forEach(tree::expandPath)
|
||||
}
|
||||
|
||||
fun add(module: Module) {
|
||||
addToTheTree(module, modifyValue = true, parent = tree.selectedNode ?: root)
|
||||
}
|
||||
|
||||
fun update() {
|
||||
root.removeAllChildren()
|
||||
value.get()?.forEach { addToTheTree(it, modifyValue = false) }
|
||||
if (value.get()?.isEmpty() == true) {
|
||||
tree.reload()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeSelected() {
|
||||
val selectedNode = tree.selectedNode?.takeIf { it.userObject is Module } ?: return
|
||||
when (val parent = selectedNode.parent.safeAs<DefaultMutableTreeNode>()?.userObject) {
|
||||
ModulesEditorTree.PROJECT_USER_OBJECT -> {
|
||||
val index = selectedNode.parent.getIndex(selectedNode)
|
||||
selectedNode.removeFromParent()
|
||||
value.set(value.get()?.toMutableList().also { it?.removeAt(index) })
|
||||
}
|
||||
is Module -> {
|
||||
parent.subModules = parent.subModules.filterNot { it === selectedNode.userObject }
|
||||
selectedNode.removeFromParent()
|
||||
}
|
||||
}
|
||||
tree.reload()
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.DropDownComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.PathFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.components.TextFieldComponent
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.panel
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.ItemEvent
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.DefaultComboBoxModel
|
||||
import javax.swing.JComponent
|
||||
|
||||
|
||||
sealed class DefaultSettingComponent<V : Any, T : SettingType<V>>(
|
||||
reference: SettingReference<V, T>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : SettingComponent<V, T>(reference, valuesReadingContext) {
|
||||
companion object {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <V : Any, T : SettingType<V>> create(
|
||||
setting: SettingReference<V, T>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
): DefaultSettingComponent<V, T> = when (setting.type) {
|
||||
VersionSettingType::class ->
|
||||
VersionSettingComponent(
|
||||
setting as SettingReference<Version, VersionSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
BooleanSettingType::class ->
|
||||
BooleanSettingComponent(
|
||||
setting as SettingReference<Boolean, BooleanSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
DropDownSettingType::class ->
|
||||
DropdownSettingComponent(
|
||||
setting as SettingReference<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
StringSettingType::class ->
|
||||
StringSettingComponent(
|
||||
setting as SettingReference<String, StringSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
PathSettingType::class ->
|
||||
PathSettingComponent(
|
||||
setting as SettingReference<Path, PathSettingType>,
|
||||
valuesReadingContext
|
||||
) as DefaultSettingComponent<V, T>
|
||||
else -> TODO(setting.type.qualifiedName!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VersionSettingComponent(
|
||||
reference: SettingReference<Version, VersionSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Version, VersionSettingType>(reference, valuesReadingContext) {
|
||||
private val settingLabel = label(setting.title)
|
||||
private val comboBox = ComboBox<Version>().apply {
|
||||
addItemListener { e ->
|
||||
if (e?.stateChange == ItemEvent.SELECTED) {
|
||||
value = e.item as Version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
val values = setting.defaultValue?.let(::listOf).orEmpty()
|
||||
comboBox.model = DefaultComboBoxModel<Version>(values.toTypedArray())
|
||||
|
||||
if (values.isNotEmpty()) {
|
||||
value = values.first()
|
||||
}
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = false)
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
add(
|
||||
panel {
|
||||
add(validationIndicator, BorderLayout.WEST)
|
||||
add(settingLabel, BorderLayout.CENTER)
|
||||
},
|
||||
BorderLayout.WEST
|
||||
)
|
||||
add(comboBox, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DropdownSettingComponent(
|
||||
reference: SettingReference<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<DisplayableSettingItem, DropDownSettingType<DisplayableSettingItem>>(
|
||||
reference,
|
||||
valuesReadingContext
|
||||
) {
|
||||
private val dropDownComponent = DropDownComponent(
|
||||
valuesReadingContext,
|
||||
setting.type.values,
|
||||
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
|
||||
}
|
||||
|
||||
class BooleanSettingComponent(
|
||||
reference: SettingReference<Boolean, BooleanSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Boolean, BooleanSettingType>(reference, valuesReadingContext) {
|
||||
private val checkBox = JBCheckBox(setting.title).apply {
|
||||
addChangeListener {
|
||||
value = this@apply.isSelected
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
super.onValueUpdated(reference)
|
||||
checkBox.isSelected = value ?: false
|
||||
}
|
||||
|
||||
override val validationIndicator = ValidationIndicator(showText = false)
|
||||
|
||||
override val component: JComponent by lazy(LazyThreadSafetyMode.NONE) {
|
||||
panel {
|
||||
// TODO add validation indicator
|
||||
add(checkBox, BorderLayout.CENTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StringSettingComponent(
|
||||
reference: SettingReference<String, StringSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext,
|
||||
showLabel: Boolean = true
|
||||
) : DefaultSettingComponent<String, StringSettingType>(reference, valuesReadingContext) {
|
||||
private val textFieldComponent = TextFieldComponent(
|
||||
valuesReadingContext,
|
||||
setting.defaultValue.orEmpty(),
|
||||
labelText = if (showLabel) setting.title else null,
|
||||
validator = setting.validator,
|
||||
onAnyValueUpdate = {
|
||||
value = it
|
||||
}
|
||||
).asSubComponent()
|
||||
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
textFieldComponent.value = value.orEmpty()
|
||||
}
|
||||
|
||||
override val component = textFieldComponent.component
|
||||
}
|
||||
|
||||
class PathSettingComponent(
|
||||
reference: SettingReference<Path, PathSettingType>,
|
||||
valuesReadingContext: ValuesReadingContext
|
||||
) : DefaultSettingComponent<Path, PathSettingType>(reference, valuesReadingContext) {
|
||||
private val pathFieldComponent = PathFieldComponent(
|
||||
valuesReadingContext,
|
||||
"",
|
||||
setting.title,
|
||||
validator = settingValidator { path ->
|
||||
setting.validator.validate(this, Paths.get(path))
|
||||
},
|
||||
onAnyValueUpdate = {
|
||||
value = Paths.get(it)
|
||||
}
|
||||
).asSubComponent()
|
||||
|
||||
override val validationIndicator: ValidationIndicator? = null
|
||||
|
||||
override val component = pathFieldComponent.component
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Displayable
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
|
||||
abstract class SettingComponent<V : Any, T: SettingType<V>>(
|
||||
val reference: SettingReference<V, T>,
|
||||
private val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext), Displayable {
|
||||
var value: V?
|
||||
get() = reference.value
|
||||
set(value) {
|
||||
reference.value = value
|
||||
}
|
||||
|
||||
val setting: Setting<V, T>
|
||||
get() = with(valuesReadingContext.context) {
|
||||
with(reference) { getSetting() }
|
||||
}
|
||||
|
||||
abstract val validationIndicator: ValidationIndicator?
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
updateValidationState()
|
||||
}
|
||||
|
||||
override fun onValueUpdated(reference: SettingReference<*, *>?) {
|
||||
component.isVisible = setting.isActive(valuesReadingContext)
|
||||
updateValidationState()
|
||||
}
|
||||
|
||||
private fun updateValidationState() {
|
||||
val value = value
|
||||
if (validationIndicator != null && value != null) {
|
||||
validationIndicator!!.validationState = setting.validator.validate(valuesReadingContext, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.ui.components.panels.VerticalLayout
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ValuesReadingContext
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.Component
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.DynamicComponent
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
|
||||
class SettingsList(
|
||||
settings: List<SettingReference<*, *>>,
|
||||
private val valuesReadingContext: ValuesReadingContext
|
||||
) : DynamicComponent(valuesReadingContext) {
|
||||
private val panel = JPanel(VerticalLayout(5))
|
||||
private var settingComponents: List<Component> = emptyList()
|
||||
|
||||
init {
|
||||
setSettings(settings)
|
||||
}
|
||||
|
||||
override val component: JComponent = panel
|
||||
|
||||
override fun onInit() {
|
||||
super.onInit()
|
||||
settingComponents.forEach { it.onInit() }
|
||||
}
|
||||
|
||||
fun setSettings(settings: List<SettingReference<*, *>>) {
|
||||
panel.removeAll()
|
||||
settingComponents = settings.map { setting ->
|
||||
DefaultSettingComponent.create(setting, valuesReadingContext)
|
||||
}
|
||||
settingComponents.forEach { setting -> setting.onInit(); panel.add(setting.component) }
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui.setting
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.util.ui.EmptyIcon
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.ValidationResult
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.label
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JPanel
|
||||
|
||||
class ValidationIndicator(
|
||||
defaultText: String? = null,
|
||||
private val showText: Boolean
|
||||
) : JPanel(BorderLayout()) {
|
||||
private val textLabel = defaultText?.let { label("$it: ") }
|
||||
private val errorLabel = label(" ").apply {
|
||||
foreground = JBColor.red
|
||||
}
|
||||
|
||||
init {
|
||||
border = BorderFactory.createEmptyBorder(3, 3, 3, 3)
|
||||
textLabel?.let { add(it, BorderLayout.WEST) }
|
||||
add(errorLabel, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
private fun setIcon(icon: Icon?) {
|
||||
if (textLabel != null) textLabel.icon = icon
|
||||
else errorLabel.icon = icon ?: EmptyIcon.ICON_16
|
||||
}
|
||||
|
||||
|
||||
var validationState: ValidationResult = ValidationResult.OK
|
||||
set(value) {
|
||||
field = value
|
||||
when (value) {
|
||||
ValidationResult.OK -> {
|
||||
setIcon(null)
|
||||
errorLabel.text = " "
|
||||
toolTipText = null
|
||||
}
|
||||
is ValidationResult.ValidationError -> {
|
||||
setIcon(AllIcons.General.Error)
|
||||
val errors = value.messages.firstOrNull().orEmpty()
|
||||
toolTipText = errors
|
||||
if (showText) errorLabel.text = errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.JBSplitter
|
||||
import com.intellij.ui.PopupHandler
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBTextField
|
||||
import com.intellij.util.ui.JBUI
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.ui.UiConstants.GAP_BORDER_SIZE
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.ModuleConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.SimpleTargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.TargetConfigurator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.event.DocumentEvent
|
||||
import javax.swing.event.DocumentListener
|
||||
|
||||
internal inline fun label(text: String, bold: Boolean = false, init: JBLabel.() -> Unit = {}) = JBLabel().apply {
|
||||
font = UIUtil.getButtonFont().deriveFont(if (bold) Font.BOLD else Font.PLAIN)
|
||||
this.text = text
|
||||
init()
|
||||
}
|
||||
|
||||
inline fun panel(layout: LayoutManager = BorderLayout(), init: JPanel.() -> Unit = {}) = JPanel(layout).apply(init)
|
||||
|
||||
fun textField(defaultValue: String?, onUpdated: (value: String) -> Unit) = JBTextField(defaultValue).apply {
|
||||
document.addDocumentListener(object : DocumentListener {
|
||||
override fun insertUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
override fun removeUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
override fun changedUpdate(e: DocumentEvent?) = onUpdated(this@apply.text)
|
||||
})
|
||||
}
|
||||
|
||||
internal fun hyperlinkLabel(
|
||||
text: String,
|
||||
onClick: () -> Unit
|
||||
) = DescriptionPanel().apply {
|
||||
cursor = Cursor(Cursor.HAND_CURSOR)
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent?) = onClick()
|
||||
})
|
||||
updateText("<a href='javascript:void(0)'>$text</a>".asHtml())
|
||||
}
|
||||
|
||||
internal fun String.asHtml() = "<html><body>$this</body></html>"
|
||||
|
||||
|
||||
val DisplayableSettingItem.htmlText
|
||||
get() = (text + greyText?.let { " <i>($greyText)</i>" }.orEmpty()).asHtml()
|
||||
|
||||
fun splitterFor(
|
||||
vararg components: JComponent,
|
||||
vertical: Boolean = false
|
||||
) = components.reduce { left, right ->
|
||||
JBSplitter(vertical, 1f / components.size).apply {
|
||||
firstComponent = left
|
||||
secondComponent = right
|
||||
dividerWidth = 1
|
||||
}
|
||||
}
|
||||
|
||||
val ModuleType.icon: Icon
|
||||
get() = when (this) {
|
||||
ModuleType.jvm -> KotlinIcons.SMALL_LOGO
|
||||
ModuleType.js -> KotlinIcons.JS
|
||||
ModuleType.native -> KotlinIcons.NATIVE
|
||||
ModuleType.common -> KotlinIcons.SMALL_LOGO
|
||||
}
|
||||
|
||||
|
||||
val Module.icon: Icon
|
||||
get() = when (kind) {
|
||||
ModuleKind.target -> configurator.moduleType.icon
|
||||
ModuleKind.multiplatform -> AllIcons.Nodes.Module
|
||||
ModuleKind.singleplatform -> AllIcons.Nodes.Module
|
||||
}
|
||||
|
||||
|
||||
val ModuleSubType.icon: Icon
|
||||
get() = moduleType.icon
|
||||
|
||||
val ModuleConfigurator.icon: Icon
|
||||
get() = when (this) {
|
||||
is SimpleTargetConfigurator -> moduleSubType.icon
|
||||
is TargetConfigurator -> moduleType.icon
|
||||
else -> AllIcons.Nodes.Module
|
||||
}
|
||||
|
||||
fun ToolbarDecorator.createPanelWithPopupHandler(popupTarget: JComponent) = createPanel().apply toolbarApply@{
|
||||
val actionGroup = DefaultActionGroup().apply {
|
||||
ToolbarDecorator.findAddButton(this@toolbarApply)?.let(this::add)
|
||||
ToolbarDecorator.findRemoveButton(this@toolbarApply)?.let(this::add)
|
||||
}
|
||||
PopupHandler.installPopupHandler(
|
||||
popupTarget,
|
||||
actionGroup,
|
||||
ActionPlaces.UNKNOWN,
|
||||
ActionManager.getInstance()
|
||||
)
|
||||
}
|
||||
|
||||
fun <C : JComponent> C.bordered(
|
||||
needTopEmptyBorder: Boolean = true,
|
||||
needBottomEmptyBorder: Boolean = true,
|
||||
needInnerEmptyBorder: Boolean = true,
|
||||
needLineBorder: Boolean = true
|
||||
) = apply {
|
||||
val lineBorder = if (needLineBorder) BorderFactory.createLineBorder(JBColor.border()) else null
|
||||
border = BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(
|
||||
if (needTopEmptyBorder) GAP_BORDER_SIZE else 0,
|
||||
0,
|
||||
if (needBottomEmptyBorder) GAP_BORDER_SIZE else 0,
|
||||
0
|
||||
),
|
||||
when {
|
||||
needInnerEmptyBorder -> BorderFactory.createCompoundBorder(
|
||||
lineBorder,
|
||||
BorderFactory.createEmptyBorder(GAP_BORDER_SIZE, GAP_BORDER_SIZE, GAP_BORDER_SIZE, GAP_BORDER_SIZE)
|
||||
)
|
||||
else -> lineBorder
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Copied from com.intellij.ide.projectWizard.ProjectSettingsStep.addField
|
||||
fun JPanel.addFieldWithLabel(label: String, field: JComponent) {
|
||||
val jLabel = JBLabel(label)
|
||||
jLabel.labelFor = field
|
||||
jLabel.verticalAlignment = SwingConstants.TOP
|
||||
add(
|
||||
jLabel,
|
||||
GridBagConstraints(
|
||||
0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
|
||||
GridBagConstraints.VERTICAL, JBUI.insets(5, 0, 5, 0), 4, 0
|
||||
)
|
||||
)
|
||||
add(
|
||||
field,
|
||||
GridBagConstraints(
|
||||
1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
|
||||
GridBagConstraints.HORIZONTAL, JBUI.insetsBottom(5), 0, 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
object UiConstants {
|
||||
const val GAP_BORDER_SIZE = 5
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard.ui
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.LibraryArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
|
||||
import org.jetbrains.kotlin.tools.projectWizard.library.NpmArtifact
|
||||
|
||||
fun LibraryArtifact.render() = when (this) {
|
||||
is MavenArtifact -> "$groupId:$artifactId"
|
||||
is NpmArtifact -> name
|
||||
}
|
||||
|
||||
@@ -122,5 +122,6 @@
|
||||
restartRequired="false"/>
|
||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||
|
||||
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -121,6 +121,6 @@
|
||||
defaultValue="false"
|
||||
restartRequired="true"/>
|
||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||
|
||||
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -112,6 +112,6 @@
|
||||
defaultValue="false"
|
||||
restartRequired="true"/>
|
||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||
|
||||
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("org.apache.velocity:velocity:1.7") // we have to use the old version as it is the same as bundled into IntelliJ
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
testImplementation(commonDep("junit:junit"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
@@ -0,0 +1,29 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlinStdlib())
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
implementation(project(":libraries:tools:new-project-wizard"))
|
||||
compileOnly(intellijDep()) { includeJars("snakeyaml-1.24") }
|
||||
|
||||
testImplementation(projectTests(":compiler:tests-common"))
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
testImplementation(commonDep("junit:junit"))
|
||||
testImplementation(intellijDep())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import YamlParsingError
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
import org.yaml.snakeyaml.parser.ParserException
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
class YamlSettingsParser(settings: List<PluginSetting<Any, *>>, private val parsingState: ParsingState) {
|
||||
private val settingByName = settings.associateBy { it.path }
|
||||
|
||||
fun parseYamlText(yaml: String): TaskResult<Map<SettingReference<*, *>, Any>> {
|
||||
val yamlObject = try {
|
||||
Success(Yaml().load<Any>(yaml) ?: emptyMap<String, Any>())
|
||||
} catch (e: ParserException) {
|
||||
Failure(YamlParsingError(e))
|
||||
} catch (e: Exception) {
|
||||
Failure(ExceptionErrorImpl(e))
|
||||
}
|
||||
return yamlObject.flatMap { map ->
|
||||
if (map is Map<*, *>) {
|
||||
val result = ComputeContext.runInComputeContextWithState(parsingState) {
|
||||
parseSettingValues(map, "")
|
||||
}
|
||||
result.map { (pluginSettings, newState) ->
|
||||
pluginSettings + newState.settingValues
|
||||
}
|
||||
} else Failure(
|
||||
BadSettingValueError("Settings file should be a map of settings")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseYamlFile(file: Path) = computeM {
|
||||
val (yaml) = safe { file.toFile().readText() }
|
||||
parseYamlText(yaml)
|
||||
}
|
||||
|
||||
private fun ParsingContext.parseSettingValues(
|
||||
data: Any,
|
||||
settingPath: String
|
||||
): TaskResult<Map<PluginSettingReference<*, *>, Any>> =
|
||||
if (settingPath in settingByName) compute {
|
||||
val setting = settingByName.getValue(settingPath)
|
||||
val (parsed) = setting.type.parse(this, data, settingPath)
|
||||
listOf(PluginSettingReference(setting) to parsed)
|
||||
} else {
|
||||
when (data) {
|
||||
is Map<*, *> -> {
|
||||
data.entries.mapCompute { (name, value) ->
|
||||
if (value == null) fail(BadSettingValueError("No value was found for a key `$settingPath`"))
|
||||
val prefix = if (settingPath.isEmpty()) "" else "$settingPath."
|
||||
val (children) = parseSettingValues(value, "$prefix$name")
|
||||
children.entries.map { (key, value) -> key to value }
|
||||
}.sequence().map { it.flatten() }
|
||||
}
|
||||
else -> Failure(
|
||||
BadSettingValueError("No value was found for a key `$settingPath`")
|
||||
)
|
||||
}
|
||||
}.map { it.toMap() }
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.wizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.YamlSettingsParser
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PipelineTask
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.PluginSettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.AndroidServiceImpl
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Service
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
import java.nio.file.Paths
|
||||
|
||||
class YamlWizard(
|
||||
private val yaml: String,
|
||||
private val path: String,
|
||||
createPlugins: (Context) -> List<Plugin>
|
||||
) : Wizard(createPlugins, listOf(AndroidServiceImpl())) {
|
||||
override fun apply(
|
||||
services: List<Service>,
|
||||
phases: Set<GenerationPhase>,
|
||||
onTaskExecuting: (PipelineTask) -> Unit
|
||||
): TaskResult<Unit> = computeM {
|
||||
super.apply(services, setOf(GenerationPhase.PREPARE), onTaskExecuting)
|
||||
val parsingData = with(valuesReadingContext) {
|
||||
ParsingState(TemplatesPlugin::templates.propertyValue, emptyMap())
|
||||
}
|
||||
val yamlParser = YamlSettingsParser(pluginSettings, parsingData)
|
||||
val (settingsValuesFromYaml) = yamlParser.parseYamlText(yaml)
|
||||
val settingValues = defaultPluginSettingValues + settingsValuesFromYaml
|
||||
settingValues.forEach { (path, value) -> context.settingContext[path] = value }
|
||||
context.settingContext[StructurePlugin::projectPath.reference] = Paths.get(path)
|
||||
super.apply(services, phases, onTaskExecuting)
|
||||
}
|
||||
|
||||
private val defaultPluginSettingValues
|
||||
get() = pluginSettings.mapNotNull { setting ->
|
||||
val defaultValue = setting.defaultValue ?: return@mapNotNull null
|
||||
PluginSettingReference(setting) to defaultValue
|
||||
} .toMap()
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Error
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.asString
|
||||
import org.yaml.snakeyaml.parser.ParserException
|
||||
|
||||
data class YamlParsingError(val exception: ParserException) : Error() {
|
||||
override val message: String
|
||||
get() = exception.asString()
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-android-extensions'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
applicationId 'testArtifactId'
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName '1.0'
|
||||
}
|
||||
buildTypes {
|
||||
'release' {
|
||||
isMinifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation kotlin('stdlib-jdk7')
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("kotlin-android-extensions")
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
android {
|
||||
compileSdkVersion(29)
|
||||
defaultConfig {
|
||||
applicationId = "testArtifactId"
|
||||
minSdkVersion(24)
|
||||
targetSdkVersion(29)
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation("androidx.appcompat:appcompat:1.1.0")
|
||||
implementation("androidx.core:core-ktx:1.1.0")
|
||||
implementation(kotlin("stdlib-jdk7"))
|
||||
implementation("androidx.constraintlayout:constraintlayout:1.1.3")
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
classpath kotlin('gradle-plugin', '1.3.61')
|
||||
}
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:3.2.1")
|
||||
classpath(kotlin("gradle-plugin", "1.3.61"))
|
||||
}
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type:
|
||||
name: android
|
||||
androidSdkPath: /home/ilya/Android/Sdk
|
||||
kind: singleplatform
|
||||
name: android
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
js('nodeJs') {
|
||||
nodejs {
|
||||
|
||||
}
|
||||
}
|
||||
js('browser') {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
nodeJsMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
nodeJsTest {
|
||||
|
||||
}
|
||||
browserMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
browserTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
js("nodeJs") {
|
||||
nodejs {
|
||||
|
||||
}
|
||||
}
|
||||
js("browser") {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val nodeJsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val nodeJsTest by getting
|
||||
val browserMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val browserTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jsNode
|
||||
kind: target
|
||||
name: nodeJs
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
- type: jsBrowser
|
||||
kind: target
|
||||
name: browser
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
sourceSets {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jvmTarget
|
||||
kind: target
|
||||
name: jvm
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>nya</artifactId>
|
||||
<groupId>testGroupId</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>nya</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>1.3.61</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>1.3.61</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Singleplatform
|
||||
modules:
|
||||
- type: JVM Module
|
||||
name: nya
|
||||
kind: singleplatform
|
||||
sourcesets:
|
||||
- type: main
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
def hostOs = System.getProperty("os.name")
|
||||
def isMingwX64 = hostOs.startsWith("Windows")
|
||||
org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests nativeTarget
|
||||
if (hostOs == "Mac OS X") nativeTarget = macosX64('myNative')
|
||||
else if (hostOs == "Linux") nativeTarget = linuxX64("myNative")
|
||||
else if (isMingwX64) return nativeTarget = mingwX64("myNative")
|
||||
else throw new GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
|
||||
nativeTarget.with {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = 'MAIN CLASS'
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
myNativeMain {
|
||||
|
||||
}
|
||||
myNativeTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
val hostOs = System.getProperty("os.name")
|
||||
val isMingwX64 = hostOs.startsWith("Windows")
|
||||
val nativeTarget = when {
|
||||
hostOs == "Mac OS X" -> macosX64("myNative")
|
||||
hostOs == "Linux" -> linuxX64("myNative")
|
||||
isMingwX64 -> mingwX64("myNative")
|
||||
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
}
|
||||
|
||||
nativeTarget.apply {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = "MAIN CLASS"
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val myNativeMain by getting
|
||||
val myNativeTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: nativeForCurrentSystem
|
||||
kind: target
|
||||
name: myNative
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
js('a') {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-jdk8')
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
||||
}
|
||||
aMain {
|
||||
dependencies {
|
||||
implementation kotlin('stdlib-js')
|
||||
}
|
||||
}
|
||||
aTest {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
jvm()
|
||||
js("a") {
|
||||
browser {
|
||||
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
val aMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-js"))
|
||||
}
|
||||
}
|
||||
val aTest by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: jvmTarget
|
||||
kind: target
|
||||
name: jvm
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
- type: jsBrowser
|
||||
kind: target
|
||||
name: a
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
|
||||
}
|
||||
group = 'testGroupId'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
linuxX64 {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = 'MAIN CLASS'
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
linuxX64Main {
|
||||
|
||||
}
|
||||
linuxX64Test {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.3.61"
|
||||
}
|
||||
group = "testGroupId"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
kotlin {
|
||||
linuxX64 {
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = "MAIN CLASS"
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
val linuxX64Main by getting
|
||||
val linuxX64Test by getting
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
kotlin:
|
||||
version: 1.3.61
|
||||
projectKind: Multiplatform
|
||||
modules:
|
||||
- type: multiplatform
|
||||
kind: multiplatform
|
||||
name: a
|
||||
subModules:
|
||||
- type: linuxX64Target
|
||||
kind: target
|
||||
name: linuxX64
|
||||
sourcesets:
|
||||
- type: main
|
||||
- type: test
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.cli
|
||||
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.ExceptionError
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.div
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.service.Services
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.wizard.YamlWizard
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
abstract class AbstractBuildFileGenerationTest : AbstractPluginBasedTest() {
|
||||
|
||||
fun doTest(directoryPath: String) {
|
||||
val directory = Paths.get(directoryPath)
|
||||
val testData = init(directory)
|
||||
if (KotlinDslPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.GRADLE_KOTLIN_DSL)
|
||||
}
|
||||
if (GroovyDslPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.GRADLE_GROOVY_DSL)
|
||||
}
|
||||
if (MavenPlugin::class in testData.pluginClasses) {
|
||||
doTest(directory, testData, BuildSystem.MAVEN)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doTest(directory: Path, testData: WizardTestData, buildSystem: BuildSystem) {
|
||||
val yaml = directory.resolve("settings.yaml").toFile().readText() + "\n" +
|
||||
defaultStructure + "\n" +
|
||||
buildSystem.yaml
|
||||
val tempDir = Files.createTempDirectory(null)
|
||||
val wizard = YamlWizard(yaml, tempDir.toString(), testData.createPlugins)
|
||||
val result = wizard.apply(Services.osServices, GenerationPhase.ALL)
|
||||
result.onFailure { errors ->
|
||||
errors.forEach { error ->
|
||||
if (error is ExceptionError) {
|
||||
throw error.exception
|
||||
}
|
||||
}
|
||||
fail(errors.joinToString("\n"))
|
||||
}
|
||||
|
||||
val expectedDirectory = (directory / EXPECTED_DIRECTORY_NAME).takeIf { Files.exists(it) } ?: directory
|
||||
|
||||
compareFiles(
|
||||
expectedDirectory.allBuildFiles(buildSystem), expectedDirectory,
|
||||
tempDir.allBuildFiles(buildSystem), tempDir
|
||||
)
|
||||
}
|
||||
|
||||
private fun Path.allBuildFiles(buildSystem: BuildSystem) =
|
||||
listFiles { it.fileName.toString() == buildSystem.buildFileName }
|
||||
|
||||
private enum class BuildSystem(val buildFileName: String, val yaml: String) {
|
||||
GRADLE_KOTLIN_DSL(
|
||||
buildFileName = "build.gradle.kts",
|
||||
yaml = """buildSystem:
|
||||
type: GradleKotlinDsl
|
||||
gradle:
|
||||
createGradleWrapper: false
|
||||
version: 5.4.1""".trimIndent()
|
||||
),
|
||||
GRADLE_GROOVY_DSL(
|
||||
buildFileName = "build.gradle",
|
||||
yaml = """buildSystem:
|
||||
type: GradleGroovyDsl
|
||||
gradle:
|
||||
createGradleWrapper: false
|
||||
version: 5.4.1""".trimIndent()
|
||||
),
|
||||
MAVEN(
|
||||
buildFileName = "pom.xml",
|
||||
yaml = """buildSystem:
|
||||
type: Maven""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXPECTED_DIRECTORY_NAME = "expected"
|
||||
|
||||
private val defaultStructure =
|
||||
"""structure:
|
||||
name: generatedProject
|
||||
groupId: testGroupId
|
||||
artifactId: testArtifactId
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.cli
|
||||
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Context
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.PluginReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
|
||||
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
|
||||
import java.nio.file.Path
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
abstract class AbstractPluginBasedTest : UsefulTestCase() {
|
||||
open val defaultPlugins: List<PluginReference> = listOf(
|
||||
StructurePlugin::class,
|
||||
TemplatesPlugin::class
|
||||
)
|
||||
|
||||
protected fun init(directory: Path): WizardTestData {
|
||||
val pluginNames = directory.resolve("plugins.txt").toFile().readLines().mapNotNull { name ->
|
||||
name.trim().takeIf { it.isNotBlank() }
|
||||
}.distinct()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val pluginClasses = defaultPlugins + pluginNames.map { pluginName ->
|
||||
Class.forName(pluginName).kotlin as PluginReference
|
||||
}
|
||||
|
||||
val createPlugins = { context: Context ->
|
||||
pluginClasses.map { pluginClass ->
|
||||
pluginClass.primaryConstructor!!.call(context)
|
||||
}
|
||||
}
|
||||
return WizardTestData(pluginClasses, createPlugins)
|
||||
}
|
||||
}
|
||||
|
||||
data class WizardTestData(
|
||||
val pluginClasses: List<PluginReference>,
|
||||
val createPlugins: (Context) -> List<Plugin>
|
||||
)
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.cli;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class BuildFileGenerationTestGenerated extends AbstractBuildFileGenerationTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInBuildFileGeneration() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration"), Pattern.compile("^([^\\.]+)$"), false);
|
||||
}
|
||||
|
||||
@TestMetadata("android")
|
||||
public void testAndroid() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/android/");
|
||||
}
|
||||
|
||||
@TestMetadata("jsNodeAndBrowserTargets")
|
||||
public void testJsNodeAndBrowserTargets() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/jsNodeAndBrowserTargets/");
|
||||
}
|
||||
|
||||
@TestMetadata("jvmTarget")
|
||||
public void testJvmTarget() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/jvmTarget/");
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinJvm")
|
||||
public void testKotlinJvm() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/kotlinJvm/");
|
||||
}
|
||||
|
||||
@TestMetadata("nativeForCurrentSystem")
|
||||
public void testNativeForCurrentSystem() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/nativeForCurrentSystem/");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleMultiplatform")
|
||||
public void testSimpleMultiplatform() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/simpleMultiplatform/");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNativeTarget")
|
||||
public void testSimpleNativeTarget() throws Exception {
|
||||
runTest("libraries/tools/new-project-wizard/new-project-wizard-cli/testData/buildFileGeneration/simpleNativeTarget/");
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.cli
|
||||
|
||||
import org.hamcrest.core.Is
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.junit.Assert.assertThat
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.stream.Collectors
|
||||
|
||||
internal fun Path.readFile() = toFile().readText().trim()
|
||||
|
||||
internal fun Path.listFiles(filter: (Path) -> Boolean) =
|
||||
Files.walk(this).filter { path ->
|
||||
Files.isRegularFile(path) && filter(path)
|
||||
}.collect(Collectors.toList()).sorted()
|
||||
|
||||
internal fun compareFiles(
|
||||
expectedFiles: List<Path>, expectedDir: Path,
|
||||
actualFiles: List<Path>, actualDir: Path
|
||||
) {
|
||||
val expectedFilesSorted = expectedFiles.sorted()
|
||||
val actualFilesSorted = actualFiles.sorted()
|
||||
|
||||
assertThat(
|
||||
actualFilesSorted.map { actualDir.relativize(it) },
|
||||
Is.`is`(expectedFilesSorted.map { expectedDir.relativize(it) })
|
||||
)
|
||||
|
||||
for ((actualFile, expectedFile) in actualFilesSorted zip expectedFilesSorted) {
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile.toFile(), actualFile.readFile())
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="${package.asCodePackage()}">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ${package.asCodePackage()}
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello World!")
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-${version}-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#foreach($property in $properties)
|
||||
${property.getFirst()}=${property.getSecond()}
|
||||
#end
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#foreach($property in $properties)
|
||||
${property.getFirst()}=${property.getSecond()}
|
||||
#end
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
rootProject.name = "${projectName}"
|
||||
|
||||
#foreach($subProject in $subProjects)
|
||||
include("${subProject}")
|
||||
#end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class Test {
|
||||
@Test
|
||||
fun testIt() {
|
||||
assertTrue(true)
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.RandomIdGenerator
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.safeAs
|
||||
|
||||
|
||||
sealed class Identificator {
|
||||
abstract val id: String
|
||||
|
||||
final override fun equals(other: Any?): Boolean = other.safeAs<Identificator>()?.id == id
|
||||
final override fun hashCode(): Int = id.hashCode()
|
||||
final override fun toString(): String = id
|
||||
}
|
||||
|
||||
class GeneratedIdentificator(prefix: String?) : Identificator() {
|
||||
override val id = """${prefix.orEmpty()}_${RandomIdGenerator.generate()}"""
|
||||
}
|
||||
|
||||
|
||||
interface IdentificatorOwner {
|
||||
val identificator: Identificator
|
||||
}
|
||||
|
||||
val IdentificatorOwner.id
|
||||
get() = identificator.id
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.EntitiesOwnerDescriptor
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.Parser
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import org.jetbrains.kotlin.tools.projectWizard.settings.DisplayableSettingItem
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
|
||||
interface SettingsOwner : EntitiesOwnerDescriptor {
|
||||
fun <V : Any, T : SettingType<V>> settingDelegate(
|
||||
create: (path: String) -> SettingBuilder<V, T>
|
||||
): ReadOnlyProperty<Any, Setting<V, T>>
|
||||
|
||||
// the functions to create different kinds of settings
|
||||
// for now should be overridden with specific settings types :(
|
||||
// TODO think a way yo emulate higher-kind types
|
||||
|
||||
fun <V : DisplayableSettingItem> dropDownSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: DropDownSettingType.Builder<V>.() -> Unit = {}
|
||||
): ReadOnlyProperty<Any, Setting<V, DropDownSettingType<V>>> = settingDelegate { path ->
|
||||
DropDownSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun stringSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: StringSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
StringSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun booleanSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: BooleanSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
BooleanSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun <V : Any> valueSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ValueSettingType.Builder<V>.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
ValueSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun versionSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: VersionSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
VersionSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
|
||||
fun <V : Any> listSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
parser: Parser<V>,
|
||||
init: ListSettingType.Builder<V>.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
ListSettingType.Builder(path, title, neededAtPhase, parser).apply(init)
|
||||
}
|
||||
|
||||
fun pathSetting(
|
||||
title: String,
|
||||
neededAtPhase: GenerationPhase,
|
||||
init: PathSettingType.Builder.() -> Unit = {}
|
||||
) = settingDelegate { path ->
|
||||
PathSettingType.Builder(path, title, neededAtPhase).apply(init)
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType
|
||||
|
||||
|
||||
sealed class CheckerRule {
|
||||
abstract fun check(context: ValuesReadingContext): Boolean
|
||||
}
|
||||
|
||||
data class RuleBySettingValue(
|
||||
val settingReference: SettingReference<Any, SettingType<Any>>,
|
||||
val expectedValue: Any
|
||||
) : CheckerRule() {
|
||||
override fun check(context: ValuesReadingContext): Boolean = with(context) {
|
||||
settingReference.notRequiredSettingValue() == expectedValue
|
||||
}
|
||||
}
|
||||
|
||||
data class OrRule(
|
||||
val left: CheckerRule,
|
||||
val right: CheckerRule
|
||||
) : CheckerRule() {
|
||||
override fun check(context: ValuesReadingContext): Boolean = left.check(context) || right.check(context)
|
||||
}
|
||||
|
||||
data class Checker(val rules: List<CheckerRule>) {
|
||||
fun check(context: ValuesReadingContext) =
|
||||
rules.all { rule -> rule.check(context) }
|
||||
|
||||
class Builder {
|
||||
private val rules = mutableListOf<CheckerRule>()
|
||||
|
||||
infix fun <V : Any, T: SettingType<V>> SettingReference<V, T>.shouldBeEqual(value: V) =
|
||||
RuleBySettingValue(this, value)
|
||||
|
||||
infix fun CheckerRule.or(other: CheckerRule) =
|
||||
OrRule(this, other)
|
||||
|
||||
fun extend(parent: Checker) {
|
||||
this.rules += parent.rules
|
||||
}
|
||||
|
||||
fun rule(rule: CheckerRule) {
|
||||
rules += rule
|
||||
}
|
||||
|
||||
fun build() = Checker(rules)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ALWAYS_AVAILABLE = Checker(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
fun checker(init: Checker.Builder.() -> Unit) =
|
||||
Checker.Builder().apply(init).build()
|
||||
|
||||
|
||||
interface ContextOwner {
|
||||
val context: Context
|
||||
}
|
||||
|
||||
interface ActivityCheckerOwner {
|
||||
val activityChecker: Checker
|
||||
|
||||
fun isActive(valuesReadingContext: ValuesReadingContext) = activityChecker.check(valuesReadingContext)
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package org.jetbrains.kotlin.tools.projectWizard.core
|
||||
|
||||
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
|
||||
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
class Context(private val pluginsCreator: PluginsCreator, val eventManager: EventManager) {
|
||||
private fun initPlugin(plugin: Plugin) {
|
||||
for (entityReference in plugin::class.memberProperties) {
|
||||
val type = entityReference.returnType.classifier.safeAs<KClass<*>>() ?: continue
|
||||
if (type.isSubclassOf(Entity::class)) {
|
||||
when (val entity = entityReference.getter.call(plugin)) {
|
||||
is Property<*> -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
propertyContext[entityReference as PropertyReference<Any>] = entity.defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val settingContext = SettingContext(eventManager::fireListeners)
|
||||
val propertyContext = PropertyContext()
|
||||
val taskContext = TaskContext()
|
||||
val plugins = pluginsCreator(this).onEach(::initPlugin)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getUnspecifiedSettings(phases: Set<GenerationPhase>): List<AnySetting> {
|
||||
val required = plugins
|
||||
.flatMap { plugin ->
|
||||
plugin.declaredSettings.mapNotNull { setting ->
|
||||
if (setting.neededAtPhase !in phases) return@mapNotNull null
|
||||
if (setting.isRequired) setting else null
|
||||
}
|
||||
}.toSet()
|
||||
val provided = settingContext.allPluginSettings.map { it.path }.toSet()
|
||||
return required.filterNot { it.path in provided }
|
||||
}
|
||||
|
||||
private val pipelineLineTasks: List<PipelineTask>
|
||||
get() = plugins
|
||||
.flatMap { it.declaredTasks.filterIsInstance<PipelineTask>() }
|
||||
|
||||
private fun task(reference: PipelineTaskReference) =
|
||||
taskContext.getEntity(reference) as? PipelineTask
|
||||
?: error(reference.path)
|
||||
|
||||
private val dependencyList: Map<PipelineTask, List<PipelineTask>>
|
||||
get() {
|
||||
val dependeeMap = pipelineLineTasks.flatMap { task ->
|
||||
task.after.map { after -> task to task(after) }
|
||||
}
|
||||
|
||||
val dependencyMap = pipelineLineTasks.flatMap { task ->
|
||||
task.before.map { before -> task(before) to task }
|
||||
}
|
||||
|
||||
return (dependeeMap + dependencyMap)
|
||||
.groupBy { it.first }
|
||||
.mapValues {
|
||||
it.value.map { it.second }
|
||||
}
|
||||
}
|
||||
|
||||
fun copy() = Context(pluginsCreator, eventManager)
|
||||
|
||||
fun sortTasks(): TaskResult<List<PipelineTask>> =
|
||||
TaskSorter().sort(pipelineLineTasks, dependencyList)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user