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") {
|
register("idea-plugin-tests") {
|
||||||
dependsOn("dist")
|
dependsOn("dist")
|
||||||
dependsOn(
|
dependsOn(
|
||||||
"idea-plugin-main-tests",
|
"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(":idea:idea-maven"))
|
||||||
compile(projectTests(":j2k"))
|
compile(projectTests(":j2k"))
|
||||||
compile(projectTests(":nj2k"))
|
compile(projectTests(":nj2k"))
|
||||||
|
compile(projectTests(":libraries:tools:new-project-wizard:new-project-wizard-cli"))
|
||||||
compile(projectTests(":idea:idea-android"))
|
compile(projectTests(":idea:idea-android"))
|
||||||
compile(projectTests(":idea:scripting-support"))
|
compile(projectTests(":idea:scripting-support"))
|
||||||
compile(projectTests(":jps-plugin"))
|
compile(projectTests(":jps-plugin"))
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
|||||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
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.AbstractSerializationIrBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
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
|
//TODO: move these tests into idea-completion module
|
||||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
|||||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
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.AbstractSerializationPluginBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
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
|
//TODO: move these tests into idea-completion module
|
||||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
|||||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
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.AbstractSerializationPluginBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
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
|
//TODO: move these tests into idea-completion module
|
||||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
|||||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
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.AbstractSerializationPluginBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
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
|
//TODO: move these tests into idea-completion module
|
||||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ import org.jetbrains.kotlin.search.AbstractAnnotatedMembersSearchTest
|
|||||||
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
import org.jetbrains.kotlin.search.AbstractInheritorsSearchTest
|
||||||
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
import org.jetbrains.kotlin.shortenRefs.AbstractShortenRefsTest
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
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.AbstractSerializationPluginBytecodeListingTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
import org.jetbrains.kotlinx.serialization.AbstractSerializationPluginDiagnosticTest
|
||||||
import org.jetbrains.kotlinx.serialization.AbstractSerializationIrBytecodeListingTest
|
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
|
//TODO: move these tests into idea-completion module
|
||||||
testGroup("idea/tests", "idea/idea-completion/testData") {
|
testGroup("idea/tests", "idea/idea-completion/testData") {
|
||||||
testClass<AbstractCodeFragmentCompletionHandlerTest> {
|
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"/>
|
restartRequired="false"/>
|
||||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||||
|
|
||||||
|
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
@@ -121,6 +121,6 @@
|
|||||||
defaultValue="false"
|
defaultValue="false"
|
||||||
restartRequired="true"/>
|
restartRequired="true"/>
|
||||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||||
|
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
@@ -112,6 +112,6 @@
|
|||||||
defaultValue="false"
|
defaultValue="false"
|
||||||
restartRequired="true"/>
|
restartRequired="true"/>
|
||||||
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
|
||||||
|
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</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