MPP wizard: add common module at the same level with platform ones

Related to KT-23097
This commit is contained in:
Mikhail Glukhikh
2018-03-02 13:06:39 +03:00
parent 720c84d1d6
commit cc8a2b3123
2 changed files with 77 additions and 43 deletions
@@ -21,7 +21,7 @@ import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep import com.intellij.openapi.externalSystem.service.project.wizard.ExternalModuleSettingsStep
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ui.configuration.ModulesProvider import com.intellij.openapi.roots.ui.configuration.ModulesProvider
import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
@@ -31,40 +31,35 @@ import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder import org.jetbrains.plugins.gradle.service.project.wizard.GradleModuleBuilder
import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl
import javax.swing.Icon
class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() { class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
var commonModuleName: String? = null
var jvmModuleName: String? = null var jvmModuleName: String? = null
var jsModuleName: String? = null var jsModuleName: String? = null
override fun getBuilderId() = "kotlin.gradle.multiplatform" override fun getBuilderId() = "kotlin.gradle.multiplatform"
override fun getNodeIcon() = KotlinIcons.MPP override fun getNodeIcon(): Icon = KotlinIcons.MPP
override fun getPresentableName() = "Kotlin (Multiplatform - Experimental)" override fun getPresentableName() = "Kotlin (Multiplatform - Experimental)"
override fun getDescription() = override fun getDescription() =
"Multiplatform projects allow reusing the same code between multiple platforms supported by Kotlin. Such projects are built with Gradle." "Multiplatform projects allow reusing the same code between multiple platforms supported by Kotlin. Such projects are built with Gradle."
override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array<ModuleWizardStep> { override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array<ModuleWizardStep> {
super.createWizardSteps(wizardContext, modulesProvider) // initializes GradleModuleBuilder.myWizardContext super.createWizardSteps(wizardContext, modulesProvider) // initializes GradleModuleBuilder.myWizardContext
return arrayOf(KotlinGradleMultiplatformWizardStep(this, wizardContext), return arrayOf(
ExternalModuleSettingsStep( KotlinGradleMultiplatformWizardStep(this, wizardContext),
wizardContext, this, GradleProjectSettingsControl(externalProjectSettings)) ExternalModuleSettingsStep(wizardContext, this, GradleProjectSettingsControl(externalProjectSettings))
) )
} }
override fun setupRootModel(modifiableRootModel: ModifiableRootModel) {
super.setupRootModel(modifiableRootModel)
val module = modifiableRootModel.module
val buildScriptData = getBuildScriptData(module) ?: return
val sdk = modifiableRootModel.sdk
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(buildScriptData, sdk)
}
override fun setupModule(module: Module) { override fun setupModule(module: Module) {
super.setupModule(module) super.setupModule(module)
val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return val contentRoot = module.rootManager.contentRoots.firstOrNull() ?: return
setupCommonModule(module, contentRoot)
setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider()) setupPlatformModule(module, contentRoot, jvmModuleName, GradleKotlinMPPJavaFrameworkSupportProvider())
setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider()) setupPlatformModule(module, contentRoot, jsModuleName, GradleKotlinMPPJSFrameworkSupportProvider())
@@ -72,24 +67,42 @@ class KotlinGradleMultiplatformModuleBuilder : GradleModuleBuilder() {
settingsGradle?.let { settingsGradle?.let {
module.project.executeCommand("Update settings.gradle") { module.project.executeCommand("Update settings.gradle") {
val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand val doc = FileDocumentManager.getInstance().getDocument(it) ?: return@executeCommand
doc.insertString(doc.textLength, "include '$jvmModuleName', '$jsModuleName'") val commonPrefix = commonModuleName?.let { "'$it', " } ?: ""
doc.insertString(doc.textLength, "include $commonPrefix'$jvmModuleName', '$jsModuleName'")
FileDocumentManager.getInstance().saveDocument(doc) FileDocumentManager.getInstance().saveDocument(doc)
} }
} }
} }
private fun setupPlatformModule(module: Module, private fun setupChildModule(
contentRoot: VirtualFile, rootModule: Module,
platformModuleName: String?, contentRoot: VirtualFile,
supportProvider: GradleKotlinFrameworkSupportProvider) { childModuleName: String?,
if (platformModuleName.isNullOrEmpty()) return extendScript: (BuildScriptDataBuilder, Sdk?) -> Unit = { _, _ -> }
) {
val sdk = module.rootManager.sdk if (childModuleName.isNullOrEmpty()) return
val moduleDir = contentRoot.createChildDirectory(this, platformModuleName!!) val moduleDir = contentRoot.createChildDirectory(this, childModuleName!!)
val buildGradle = moduleDir.createChildData(null, "build.gradle") val buildGradle = moduleDir.createChildData(null, "build.gradle")
val buildScriptData = BuildScriptDataBuilder(buildGradle) val buildScriptData = BuildScriptDataBuilder(buildGradle)
supportProvider.addSupport(buildScriptData, sdk) extendScript(buildScriptData, rootModule.rootManager.sdk)
buildScriptData.addDependencyNotation("expectedBy project(\":\")")
VfsUtil.saveText(buildGradle, buildScriptData.buildConfigurationPart() + buildScriptData.buildMainPart()) VfsUtil.saveText(buildGradle, buildScriptData.buildConfigurationPart() + buildScriptData.buildMainPart())
} }
private fun setupCommonModule(
rootModule: Module,
contentRoot: VirtualFile
) = setupChildModule(rootModule, contentRoot, commonModuleName) { builder, sdk ->
GradleKotlinMPPCommonFrameworkSupportProvider().addSupport(builder, sdk)
}
private fun setupPlatformModule(
rootModule: Module,
contentRoot: VirtualFile,
platformModuleName: String?,
supportProvider: GradleKotlinFrameworkSupportProvider
) = setupChildModule(rootModule, contentRoot, platformModuleName) { builder, sdk ->
supportProvider.addSupport(builder, sdk)
val dependency = commonModuleName ?: ""
builder.addDependencyNotation("expectedBy project(\":$dependency\")")
}
} }
@@ -37,8 +37,10 @@ class KotlinGradleMultiplatformWizardStep(
private val wizardContext: WizardContext private val wizardContext: WizardContext
) : ModuleWizardStep() { ) : ModuleWizardStep() {
private val mainModuleNameComponent: LabeledComponent<JTextField> = private val rootModuleNameComponent: LabeledComponent<JTextField> =
LabeledComponent.create(JTextField(), "Main module name:", BorderLayout.WEST) LabeledComponent.create(JTextField(), "Root module name:", BorderLayout.WEST)
private val commonModuleNameComponent: LabeledComponent<JTextField> =
LabeledComponent.create(JTextField(), "Common module name:", BorderLayout.WEST)
private val jvmModuleNameComponent: LabeledComponent<JTextField> = private val jvmModuleNameComponent: LabeledComponent<JTextField> =
LabeledComponent.create(JTextField(), "JVM module name:", BorderLayout.WEST) LabeledComponent.create(JTextField(), "JVM module name:", BorderLayout.WEST)
private val jsModuleNameComponent: LabeledComponent<JTextField> = private val jsModuleNameComponent: LabeledComponent<JTextField> =
@@ -50,13 +52,14 @@ class KotlinGradleMultiplatformWizardStep(
init { init {
panel = object : JPanel(GridBagLayout()), PanelWithAnchor { panel = object : JPanel(GridBagLayout()), PanelWithAnchor {
private var anchor: JComponent? = mainModuleNameComponent.anchor private var anchor: JComponent? = rootModuleNameComponent.anchor
override fun getAnchor(): JComponent? = anchor override fun getAnchor(): JComponent? = anchor
override fun setAnchor(anchor: JComponent?) { override fun setAnchor(anchor: JComponent?) {
this.anchor = anchor this.anchor = anchor
mainModuleNameComponent.anchor = anchor rootModuleNameComponent.anchor = anchor
commonModuleNameComponent.anchor = anchor
jvmModuleNameComponent.anchor = anchor jvmModuleNameComponent.anchor = anchor
jsModuleNameComponent.anchor = anchor jsModuleNameComponent.anchor = anchor
} }
@@ -64,12 +67,12 @@ class KotlinGradleMultiplatformWizardStep(
val baseDir = wizardContext.projectFileDirectory val baseDir = wizardContext.projectFileDirectory
val projectName = wizardContext.projectName val projectName = wizardContext.projectName
val initialProjectName = projectName ?: ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", "") val initialProjectName = projectName ?: ProjectWizardUtil.findNonExistingFileName(baseDir, "untitled", "")
mainModuleNameComponent.component.text = initialProjectName rootModuleNameComponent.component.text = initialProjectName
mainModuleNameComponent.component.select(0, initialProjectName.length) rootModuleNameComponent.component.select(0, initialProjectName.length)
updateDerivedModuleNames() updateDerivedModuleNames()
mainModuleNameComponent.component.document.addDocumentListener(object : DocumentAdapter() { rootModuleNameComponent.component.document.addDocumentListener(object : DocumentAdapter() {
override fun textChanged(e: DocumentEvent?) { override fun textChanged(e: DocumentEvent?) {
if (syncEditing) { if (syncEditing) {
updateDerivedModuleNames() updateDerivedModuleNames()
@@ -84,18 +87,27 @@ class KotlinGradleMultiplatformWizardStep(
} }
} }
} }
commonModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener)
jvmModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener) jvmModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener)
jsModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener) jsModuleNameComponent.component.document.addDocumentListener(stopSyncEditingListener)
panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10)
panel.add( panel.add(
mainModuleNameComponent, rootModuleNameComponent,
GridBagConstraints( GridBagConstraints(
0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, 0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
JBUI.insets(10, 0, 0, 0), 0, 0 JBUI.insets(10, 0, 0, 0), 0, 0
) )
) )
panel.add(
commonModuleNameComponent,
GridBagConstraints(
0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0,
GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
JBUI.emptyInsets(), 0, 0
)
)
panel.add( panel.add(
jvmModuleNameComponent, jvmModuleNameComponent,
GridBagConstraints( GridBagConstraints(
@@ -126,8 +138,9 @@ class KotlinGradleMultiplatformWizardStep(
private fun updateDerivedModuleNames() { private fun updateDerivedModuleNames() {
inSyncUpdate = true inSyncUpdate = true
try { try {
jvmModuleNameComponent.component.text = "$mainModuleName-jvm" commonModuleNameComponent.component.text = "$rootModuleName-common"
jsModuleNameComponent.component.text = "$mainModuleName-js" jvmModuleNameComponent.component.text = "$rootModuleName-jvm"
jsModuleNameComponent.component.text = "$rootModuleName-js"
} finally { } finally {
inSyncUpdate = false inSyncUpdate = false
@@ -136,30 +149,38 @@ class KotlinGradleMultiplatformWizardStep(
override fun updateDataModel() { override fun updateDataModel() {
wizardContext.projectBuilder = builder wizardContext.projectBuilder = builder
wizardContext.projectName = mainModuleName wizardContext.projectName = rootModuleName
builder.projectId = ProjectId("", mainModuleName, "") builder.projectId = ProjectId("", rootModuleName, "")
builder.commonModuleName = commonModuleName
builder.jvmModuleName = jvmModuleName builder.jvmModuleName = jvmModuleName
builder.jsModuleName = jsModuleName builder.jsModuleName = jsModuleName
} }
override fun getComponent() = panel override fun getComponent() = panel
private val mainModuleName: String private val rootModuleName: String
get() = mainModuleNameComponent.component.text get() = rootModuleNameComponent.component.text
private val commonModuleName: String
get() = commonModuleNameComponent.component.text
private val jvmModuleName: String private val jvmModuleName: String
get() = jvmModuleNameComponent.component.text get() = jvmModuleNameComponent.component.text
private val jsModuleName: String private val jsModuleName: String
get() = jsModuleNameComponent.component.text get() = jsModuleNameComponent.component.text
override fun validate(): Boolean { override fun validate(): Boolean {
if (mainModuleName.isEmpty()) { if (rootModuleName.isEmpty()) {
throw ConfigurationException("Please specify the main module name") throw ConfigurationException("Please specify the root module name")
} }
if (jvmModuleName.isNotEmpty() && (jvmModuleName == mainModuleName || jvmModuleName == jsModuleName)) { if (commonModuleName.isNotEmpty()
&& (commonModuleName == rootModuleName || commonModuleName == jvmModuleName || commonModuleName == jsModuleName)
) {
throw ConfigurationException("The common module name should be distinct")
}
if (jvmModuleName.isNotEmpty() && (jvmModuleName == rootModuleName || jvmModuleName == jsModuleName)) {
throw ConfigurationException("The JVM module name should be distinct") throw ConfigurationException("The JVM module name should be distinct")
} }
if (jsModuleName.isNotEmpty() && jsModuleName == mainModuleName) { if (jsModuleName.isNotEmpty() && jsModuleName == rootModuleName) {
throw ConfigurationException("The IS module name should be distinct") throw ConfigurationException("The IS module name should be distinct")
} }
return true return true