Skip to content

Commit a0cbfea

Browse files
committed
feat: add project wizard
1 parent 8e29814 commit a0cbfea

File tree

8 files changed

+272
-0
lines changed

8 files changed

+272
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.github.tempest.framework.scaffolder.project
2+
3+
import com.github.tempest.framework.TempestIcons
4+
import com.github.tempest.framework.scaffolder.project.override.WebTemplateProjectWizardStep
5+
import com.intellij.ide.util.projectWizard.WebTemplateNewProjectWizardBase
6+
import com.intellij.ide.wizard.NewProjectWizardBaseStep
7+
import com.intellij.ide.wizard.NewProjectWizardChainStep.Companion.nextStep
8+
import com.intellij.ide.wizard.NewProjectWizardStep
9+
import com.intellij.ide.wizard.RootNewProjectWizardStep
10+
11+
class TempestGeneratorProjectWizard : WebTemplateNewProjectWizardBase() {
12+
override val id = "tempest-project"
13+
override val name = "Tempest"
14+
override val icon = TempestIcons.TEMPEST
15+
16+
val template = TempestProjectGenerator()
17+
18+
override fun createTemplateStep(parent: NewProjectWizardBaseStep): NewProjectWizardStep {
19+
return RootNewProjectWizardStep(parent.context)
20+
.nextStep { WebTemplateProjectWizardStep(parent, template) }
21+
}
22+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.github.tempest.framework.scaffolder.project
2+
3+
import com.intellij.ide.wizard.GeneratorNewProjectWizardBuilderAdapter
4+
5+
class TempestModuleBuilder: GeneratorNewProjectWizardBuilderAdapter(TempestGeneratorProjectWizard()) {
6+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.github.tempest.framework.scaffolder.project
2+
3+
import com.intellij.execution.configurations.PathEnvironmentVariableUtil
4+
import com.intellij.ide.util.projectWizard.WebProjectTemplate
5+
import com.intellij.openapi.module.Module
6+
import com.intellij.openapi.project.Project
7+
import com.intellij.openapi.vfs.VirtualFile
8+
import com.jetbrains.php.composer.ComposerProjectGenerator
9+
import com.jetbrains.php.composer.ComposerProjectSettings
10+
import com.jetbrains.php.composer.addDependency.ComposerPackage
11+
import com.jetbrains.php.composer.execution.ComposerExecution
12+
import com.jetbrains.php.composer.execution.executable.ExecutableComposerExecution
13+
import com.jetbrains.php.composer.execution.phar.PharComposerExecution
14+
import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl
15+
16+
class TempestProjectGenerator : WebProjectTemplate<TempestProjectGeneratorSettings>() {
17+
override fun getName() = "Tempest"
18+
19+
override fun generateProject(
20+
project: Project,
21+
baseDir: VirtualFile,
22+
settings: TempestProjectGeneratorSettings,
23+
module: Module
24+
) {
25+
val isDownload = PathEnvironmentVariableUtil.findExecutableInPathOnAnyOS("composer") == null
26+
val composerExecutable = when {
27+
isDownload -> {
28+
val phpInterpretersManager = PhpInterpretersManagerImpl.getInstance(project)
29+
PharComposerExecution(
30+
phpInterpretersManager.interpreters.firstOrNull()?.id,
31+
"",
32+
false,
33+
)
34+
}
35+
36+
else -> ExecutableComposerExecution("composer")
37+
}
38+
39+
println("generateProject: $baseDir, $settings, $module")
40+
val version = if (settings.version != "latest") settings.version else null
41+
42+
val composerSettings = ComposerProjectSettings(
43+
isDownload,
44+
ComposerPackage(settings.template),
45+
version,
46+
"--no-progress --no-interaction --ansi --remove-vcs",
47+
composerExecutable as ComposerExecution,
48+
null,
49+
settings.createGit,
50+
)
51+
ComposerProjectGenerator().generateProject(project, baseDir, composerSettings, module)
52+
53+
println("generateProject: $baseDir, $settings, $module")
54+
}
55+
56+
override fun createPeer() = TempestProjectPeer()
57+
58+
override fun getDescription() = "Tempest project template"
59+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.tempest.framework.scaffolder.project
2+
3+
data class TempestProjectGeneratorSettings(
4+
var version: String = "latest",
5+
var createGit: Boolean = true,
6+
var template: String = "tempest/app",
7+
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.github.tempest.framework.scaffolder.project
2+
3+
import com.intellij.ide.util.projectWizard.SettingsStep
4+
import com.intellij.openapi.application.ApplicationManager
5+
import com.intellij.openapi.application.ModalityState
6+
import com.intellij.openapi.application.ReadAction
7+
import com.intellij.openapi.ui.TextFieldWithBrowseButton
8+
import com.intellij.openapi.ui.ValidationInfo
9+
import com.intellij.platform.GeneratorPeerImpl
10+
import com.intellij.ui.MutableCollectionComboBoxModel
11+
import com.intellij.ui.dsl.builder.bindItem
12+
import com.intellij.ui.dsl.builder.bindSelected
13+
import com.intellij.ui.dsl.builder.panel
14+
import com.intellij.ui.dsl.builder.toNullableProperty
15+
import com.intellij.util.concurrency.AppExecutorUtil
16+
import com.jetbrains.php.composer.addDependency.ComposerPackage
17+
import com.jetbrains.php.composer.json.PhpComposerJsonCompletionContributor.PhpComposerPackageVersionsProvider
18+
import java.util.concurrent.Callable
19+
20+
class TempestProjectPeer() : GeneratorPeerImpl<TempestProjectGeneratorSettings>() {
21+
var versions = MutableCollectionComboBoxModel(mutableListOf("latest"))
22+
val mySettings = TempestProjectGeneratorSettings()
23+
24+
val myPanel = panel {
25+
row("Template") {
26+
comboBox(listOf("tempest/app"))
27+
.bindItem(mySettings::template.toNullableProperty())
28+
}
29+
row("Version") {
30+
comboBox(versions)
31+
.bindItem(mySettings::version.toNullableProperty())
32+
}
33+
row("Create Git") {
34+
checkBox("")
35+
.bindSelected(mySettings::createGit)
36+
}
37+
}
38+
39+
init {
40+
ApplicationManager.getApplication().invokeLater {
41+
ReadAction.nonBlocking(Callable {
42+
PhpComposerPackageVersionsProvider
43+
.fetchPackageVersions("tempest/app")
44+
.sortedWith(ComposerPackage.VERSIONS_COMPARATOR)
45+
.apply { versions.add(this) }
46+
})
47+
.finishOnUiThread(ModalityState.defaultModalityState()) {}
48+
.submit(AppExecutorUtil.getAppExecutorService())
49+
}
50+
}
51+
52+
override fun getComponent(myLocationField: TextFieldWithBrowseButton, checkValid: Runnable) = myPanel
53+
54+
override fun buildUI(settingsStep: SettingsStep) {
55+
settingsStep.addSettingsComponent(myPanel)
56+
}
57+
58+
override fun getSettings(): TempestProjectGeneratorSettings {
59+
myPanel.apply()
60+
return mySettings
61+
}
62+
63+
override fun validate(): ValidationInfo? {
64+
myPanel.validate()
65+
return myPanel.validateAll().firstOrNull()
66+
}
67+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.github.tempest.framework.scaffolder.project.override
2+
3+
import com.intellij.ide.util.projectWizard.ModuleNameLocationSettings
4+
import com.intellij.ide.util.projectWizard.SettingsStep
5+
import com.intellij.ide.util.projectWizard.WizardContext
6+
import com.intellij.ide.wizard.NewProjectWizardBaseStep
7+
import com.intellij.openapi.util.io.FileUtil
8+
import com.intellij.ui.dsl.builder.AlignX
9+
import com.intellij.ui.dsl.builder.BottomGap
10+
import com.intellij.ui.dsl.builder.Panel
11+
import com.intellij.util.PathUtil
12+
import java.io.File
13+
import javax.swing.JComponent
14+
15+
/**
16+
* Port of [com.intellij.ide.util.projectWizard.PanelBuilderSettingsStep]
17+
*/
18+
class PanelBuilderSettingsStep(
19+
private val wizardContext: WizardContext,
20+
private val builder: Panel,
21+
private val baseStep: NewProjectWizardBaseStep
22+
) : SettingsStep {
23+
override fun getContext(): WizardContext = wizardContext
24+
25+
override fun addSettingsField(label: String, field: JComponent) {
26+
with(builder) {
27+
row(label) {
28+
cell(field).align(AlignX.FILL)
29+
}.bottomGap(BottomGap.SMALL)
30+
}
31+
}
32+
33+
override fun addSettingsComponent(component: JComponent) {
34+
with(builder) {
35+
// row("") { <---- seems like a bug, it adds left padding
36+
row {
37+
cell(component).align(AlignX.FILL)
38+
}.bottomGap(BottomGap.SMALL)
39+
}
40+
}
41+
42+
override fun addExpertPanel(panel: JComponent) {
43+
addSettingsComponent(panel)
44+
}
45+
46+
override fun addExpertField(label: String, field: JComponent) {
47+
addSettingsField(label, field)
48+
}
49+
50+
override fun getModuleNameLocationSettings(): ModuleNameLocationSettings {
51+
return object : ModuleNameLocationSettings {
52+
override fun getModuleName(): String = baseStep.name
53+
54+
override fun setModuleName(moduleName: String) {
55+
baseStep.name = moduleName
56+
}
57+
58+
override fun getModuleContentRoot(): String {
59+
return FileUtil.toSystemDependentName(baseStep.path)
60+
.trimEnd(File.separatorChar) + File.separatorChar + baseStep.name
61+
}
62+
63+
override fun setModuleContentRoot(path: String) {
64+
baseStep.path = PathUtil.getParentPath(path)
65+
baseStep.name = PathUtil.getFileName(path)
66+
}
67+
}
68+
}
69+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.github.tempest.framework.scaffolder.project.override
2+
3+
import com.github.tempest.framework.scaffolder.project.TempestProjectGenerator
4+
import com.github.tempest.framework.scaffolder.project.TempestProjectGeneratorSettings
5+
import com.intellij.ide.highlighter.ModuleFileType
6+
import com.intellij.ide.wizard.AbstractNewProjectWizardStep
7+
import com.intellij.ide.wizard.NewProjectWizardBaseStep
8+
import com.intellij.openapi.module.WebModuleBuilder
9+
import com.intellij.openapi.project.Project
10+
import com.intellij.ui.dsl.builder.Panel
11+
import java.nio.file.Path
12+
13+
class WebTemplateProjectWizardStep(
14+
val parentStep: NewProjectWizardBaseStep,
15+
val template: TempestProjectGenerator,
16+
) : AbstractNewProjectWizardStep(parentStep) {
17+
val peer = template.createLazyPeer()
18+
19+
override fun setupUI(builder: Panel) {
20+
peer.value.buildUI(PanelBuilderSettingsStep(parentStep.context, builder, parentStep))
21+
}
22+
23+
override fun setupProject(project: Project) {
24+
webModuleBuilder().commitModule(project, null)
25+
}
26+
27+
private fun webModuleBuilder(): WebModuleBuilder<TempestProjectGeneratorSettings> {
28+
val moduleName = parentStep.name
29+
val projectPath = Path.of(parentStep.path, moduleName)
30+
31+
return WebModuleBuilder(template, peer).apply {
32+
contentEntryPath = projectPath.toString()
33+
moduleFilePath = projectPath.resolve(moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION).toString()
34+
name = moduleName
35+
}
36+
}
37+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
<fileBasedIndex
6060
implementation="com.github.tempest.framework.router.index.RoutesListIndex"/>
6161

62+
<directoryProjectGenerator
63+
implementation="com.github.tempest.framework.scaffolder.project.TempestProjectGenerator" />
64+
<moduleBuilder
65+
builderClass="com.github.tempest.framework.scaffolder.project.TempestModuleBuilder" />
66+
6267
<psi.referenceContributor
6368
language="PHP"
6469
implementation="com.github.tempest.framework.db.references.QueryBuilderReferenceContributor"/>

0 commit comments

Comments
 (0)