From b78bad497de1854c60eca49bf7c51baed5e9e8ee Mon Sep 17 00:00:00 2001 From: tanghanzheng Date: Sat, 10 Jun 2023 14:44:30 +0800 Subject: [PATCH] concept cloud plugin 2.0.0 213-231 --- .../build.gradle | 2 +- .../ConceptCloudWebModuleBuilder.java | 12 +- .../plugin/intellij/GenerateCodeAction.java | 1 - .../intellij/GenerateDomainCodeAction.java | 1 - .../ConceptAnonymousParallelOperationTrace.kt | 208 ++++++++++++++++++ .../plugin/intellij/ConceptButtonSelector.kt | 7 +- .../cloud/plugin/intellij/ConceptCell.kt | 163 ++++++++------ .../plugin/intellij/ConceptFormUiUtil.kt | 13 +- .../plugin/intellij/ConceptGraphProperty.kt | 167 +++++++++++++- .../intellij/ConceptGraphPropertyImpl.kt | 36 --- .../ConceptHideableTitledSeparator.kt | 3 +- .../plugin/intellij/ConceptJdkComboBox.kt | 2 +- .../cloud/plugin/intellij/ConceptLayout.kt | 6 +- .../plugin/intellij/ConceptLayoutBuilder.kt | 49 ++++- .../intellij/ConceptMigLayoutBuilder.kt | 4 +- .../plugin/intellij/ConceptMigLayoutRow.kt | 5 +- .../plugin/intellij/ConceptMigLayoutUtil.kt | 1 - .../intellij/ConceptProjectLocationField.kt | 10 +- .../plugin/intellij/ConceptPropertyGraph.kt | 80 +++++++ .../cloud/plugin/intellij/ConceptRow.kt | 59 +++-- .../plugin/intellij/ConceptStarterSettings.kt | 39 ++-- .../intellij/ConceptTextValidationFunction.kt | 129 +++++++++++ .../intellij/ConceptWebStarterInitialStep.kt | 142 +++++++----- .../ConceptWebStarterLibrariesStep.kt | 14 +- .../ConceptWebStarterModuleBuilder.kt | 12 +- .../intellij/ConceptWebStarterSettings.kt | 12 +- .../intellij/domain/DomainComponents.kt | 13 +- .../plugin/intellij/domain/DomainModel.kt | 33 +-- .../intellij/module/ModuleComponents.kt | 14 +- .../plugin/intellij/module/ModuleModel.kt | 29 +-- .../plugin/intellij/util/ConceptUtils.kt | 8 +- .../src/main/resources/META-INF/plugin.xml | 2 +- 32 files changed, 962 insertions(+), 314 deletions(-) create mode 100644 concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptAnonymousParallelOperationTrace.kt delete mode 100644 concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphPropertyImpl.kt create mode 100644 concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptPropertyGraph.kt create mode 100644 concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptTextValidationFunction.kt diff --git a/concept-cloud/concept-cloud-plugin-intellij/build.gradle b/concept-cloud/concept-cloud-plugin-intellij/build.gradle index d372a6b2b..046d198e0 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/build.gradle +++ b/concept-cloud/concept-cloud-plugin-intellij/build.gradle @@ -43,7 +43,7 @@ tasks { patchPluginXml { sinceBuild.set("213.5744.223") - untilBuild.set("") + untilBuild.set("232.0") //sinceBuild.set("212") //untilBuild.set("222.*") //sinceBuild.set("223") diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/ConceptCloudWebModuleBuilder.java b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/ConceptCloudWebModuleBuilder.java index cf02dbd8b..2dbebe94d 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/ConceptCloudWebModuleBuilder.java +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/ConceptCloudWebModuleBuilder.java @@ -20,7 +20,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -@SuppressWarnings("all") public class ConceptCloudWebModuleBuilder extends ConceptWebStarterModuleBuilder { private final Key> CONCEPT_CLOUD_CFV_KEY = new Key<>("CONCEPT_CLOUD_CFV_KEY"); @@ -29,7 +28,7 @@ public class ConceptCloudWebModuleBuilder extends ConceptWebStarterModuleBuilder @Override protected Url composeGeneratorUrl(@NotNull String s, @NotNull ConceptWebStarterContext webStarterContext) { ConceptWebStarterFrameworkVersion frameworkVersion = webStarterContext.getFrameworkVersion(); - String version = frameworkVersion.getId(); + String version = frameworkVersion == null ? "VERSION_NULL" : frameworkVersion.getId(); String location = "java/" + version + "/template.zip"; String url; if (s.endsWith("/")) { @@ -117,6 +116,9 @@ private void handleConceptGradle(File file) throws IOException { private String handleVersions(String content) { Versions versions = getVersions(); + if (versions == null) { + return content; + } return content .replaceAll("\\$V_GRADLE\\$", versions.gradle) .replaceAll("\\$V_SPRING_BOOT\\$", versions.springBoot) @@ -143,7 +145,7 @@ private ConceptFrameworkVersion getConceptFrameworkVersion() { private void createDir(String name, File parent) { File file = new File(parent, name); if (!file.exists()) { - file.mkdirs(); + boolean mkdirs = file.mkdirs(); } } @@ -151,11 +153,11 @@ private File createFile(String name, File parent) throws IOException { File file = new File(parent, name); File parentFile = new File(transform(file.getParent(), true)); if (!parentFile.exists()) { - parentFile.mkdirs(); + boolean mkdirs = parentFile.mkdirs(); } File create = new File(parentFile, file.getName()); if (!create.exists()) { - create.createNewFile(); + boolean newFile = create.createNewFile(); } return create; } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateCodeAction.java b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateCodeAction.java index 2e657c293..ad69e4fd3 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateCodeAction.java +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateCodeAction.java @@ -10,7 +10,6 @@ import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateDomainCodeAction.java b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateDomainCodeAction.java index 6c1b0e6d7..909c43cbe 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateDomainCodeAction.java +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/java/com/github/linyuzai/cloud/plugin/intellij/GenerateDomainCodeAction.java @@ -4,7 +4,6 @@ import com.github.linyuzai.cloud.plugin.intellij.domain.DomainFileGenerator; import com.github.linyuzai.cloud.plugin.intellij.domain.DomainModel; import com.github.linyuzai.cloud.plugin.intellij.util.ConceptDialog; -import com.intellij.openapi.ui.DialogBuilder; import com.intellij.ui.RecentsManager; import java.io.File; diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptAnonymousParallelOperationTrace.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptAnonymousParallelOperationTrace.kt new file mode 100644 index 000000000..edf077f4f --- /dev/null +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptAnonymousParallelOperationTrace.kt @@ -0,0 +1,208 @@ +package com.github.linyuzai.cloud.plugin.intellij + +import com.github.linyuzai.cloud.plugin.intellij.ConceptCompoundParallelOperationTrace.Companion.task +import com.intellij.openapi.Disposable +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Disposer +import com.intellij.util.containers.DisposableWrapperList +import org.jetbrains.annotations.NonNls +import java.util.concurrent.atomic.AtomicInteger + +class ConceptAnonymousParallelOperationTrace private constructor( + private val delegate: ConceptCompoundParallelOperationTrace +) : ConceptObservableOperationTrace by delegate { + constructor(@NonNls debugName: String? = null) : this(ConceptCompoundParallelOperationTrace(debugName)) + + fun startTask() = delegate.startTask(null) + fun finishTask() = delegate.finishTask(null) + + companion object { + fun ConceptAnonymousParallelOperationTrace.task(action: () -> R): R = delegate.task(null, action) + } +} + +interface ConceptObservableOperationTrace { + /** + * Checks that operations is completed. + */ + fun isOperationCompleted(): Boolean + + /** + * Subscribes listener with TTL on operation start event. + * Subscribed listener will be automatically unsubscribed when [ttl] is out or when [parentDisposable] is disposed. + * [parentDisposable] uses for early unsubscription when listener is called less than [ttl] times. + * + * @param ttl is a number of listener calls which should be passed to unsubscribe listener. + * @param listener is a listener function that will be called [ttl] times. + * @param parentDisposable is a subscription disposable. + */ + fun beforeOperation(ttl: Int, listener: () -> Unit, parentDisposable: Disposable) + + /** + * Subscribes listener on operation start event that will never been unsubscribed. + */ + fun beforeOperation(listener: () -> Unit) + + /** + * Subscribes listener on operation start event that unsubscribed when [parentDisposable] is disposed. + */ + fun beforeOperation(listener: () -> Unit, parentDisposable: Disposable) + + /** + * Subscribes listener with TTL on operation finish event. + * + * @see beforeOperation(Int, () -> Unit, Disposable) + */ + fun afterOperation(ttl: Int, listener: () -> Unit, parentDisposable: Disposable) + + /** + * Subscribes listener on operation finish event that will never been unsubscribed. + */ + fun afterOperation(listener: () -> Unit) + + /** + * Subscribes listener on operation finish event that unsubscribed when [parentDisposable] is disposed. + */ + fun afterOperation(listener: () -> Unit, parentDisposable: Disposable) +} + +class ConceptCompoundParallelOperationTrace(private val debugName: String? = null) : + ConceptAbstractObservableOperationTrace() { + + private val traces = LinkedHashMap() + + override fun isOperationCompleted(): Boolean { + synchronized(this) { + return traces.isEmpty() + } + } + + fun startTask(taskId: Id) { + val isOperationCompletedBeforeStart: Boolean + synchronized(this) { + isOperationCompletedBeforeStart = traces.isEmpty() + addTask(taskId) + } + if (isOperationCompletedBeforeStart) { + debug("Operation is started") + fireOperationStarted() + } + } + + fun finishTask(taskId: Id) { + val isOperationCompletedAfterFinish: Boolean + synchronized(this) { + if (!removeTask(taskId)) return + isOperationCompletedAfterFinish = traces.isEmpty() + } + if (isOperationCompletedAfterFinish) { + debug("Operation is finished") + fireOperationFinished() + } + } + + private fun addTask(taskId: Id) { + val taskCounter = traces.getOrPut(taskId) { 0 } + traces[taskId] = taskCounter + 1 + debug("Task is started with id `$taskId`") + } + + private fun removeTask(taskId: Id): Boolean { + debug("Task is finished with id `$taskId`") + val taskCounter = traces[taskId] ?: return false + when (taskCounter) { + 1 -> traces.remove(taskId) + else -> traces[taskId] = taskCounter - 1 + } + return taskCounter == 1 + } + + private fun debug(message: String) { + if (LOG.isDebugEnabled) { + val debugPrefix = if (debugName == null) "" else "$debugName: " + LOG.debug("$debugPrefix$message") + } + } + + companion object { + private val LOG = Logger.getInstance(ConceptCompoundParallelOperationTrace::class.java) + + fun ConceptCompoundParallelOperationTrace.task(taskId: Id, action: () -> R): R { + startTask(taskId) + try { + return action() + } finally { + finishTask(taskId) + } + } + } +} + +abstract class ConceptAbstractObservableOperationTrace : ConceptObservableOperationTrace { + + private val beforeOperationListeners = DisposableWrapperList<() -> Unit>() + private val afterOperationListeners = DisposableWrapperList<() -> Unit>() + + protected fun fireOperationStarted() { + beforeOperationListeners.forEach { it() } + } + + protected fun fireOperationFinished() { + afterOperationListeners.forEach { it() } + } + + override fun beforeOperation(ttl: Int, listener: () -> Unit, parentDisposable: Disposable) { + subscribe(ttl, listener, ::beforeOperation, parentDisposable) + } + + override fun beforeOperation(listener: () -> Unit) { + beforeOperationListeners.add(listener) + } + + override fun beforeOperation(listener: () -> Unit, parentDisposable: Disposable) { + beforeOperationListeners.add(listener, parentDisposable) + } + + override fun afterOperation(ttl: Int, listener: () -> Unit, parentDisposable: Disposable) { + subscribe(ttl, listener, ::afterOperation, parentDisposable) + } + + override fun afterOperation(listener: () -> Unit) { + afterOperationListeners.add(listener) + } + + override fun afterOperation(listener: () -> Unit, parentDisposable: Disposable) { + afterOperationListeners.add(listener, parentDisposable) + } +} + +@JvmName("subscribe0") +fun subscribe( + ttl: Int, + listener: () -> Unit, + subscribe: (() -> Unit, Disposable) -> Unit, + parentDisposable: Disposable +) { + val ttlCounter = ConceptTTLCounter(ttl, parentDisposable) + subscribe({ + ttlCounter.update() + listener() + }, ttlCounter) +} + +private class ConceptTTLCounter(ttl: Int, parentDisposable: Disposable) : Disposable { + private val ttlCounter = AtomicInteger(ttl) + + fun update() { + if (ttlCounter.decrementAndGet() == 0) { + Disposer.dispose(this) + } + } + + override fun dispose() {} + + init { + require(ttl > 0) + Disposer.register(parentDisposable, this) + } +} diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptButtonSelector.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptButtonSelector.kt index 2343b9506..289c6543c 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptButtonSelector.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptButtonSelector.kt @@ -5,7 +5,6 @@ import com.intellij.openapi.actionSystem.ex.ActionButtonLook import com.intellij.openapi.actionSystem.impl.ActionButton import com.intellij.openapi.actionSystem.impl.ActionButtonWithText import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl -import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.project.DumbAware import com.intellij.openapi.util.NlsActions import java.awt.Dimension @@ -13,7 +12,7 @@ import java.awt.Insets import java.util.function.Supplier import kotlin.math.max -fun ConceptRow.buttonSelector(options: Collection, property: GraphProperty, renderer: (T) -> String): ConceptButtonSelectorToolbar { +fun ConceptRow.buttonSelector(options: Collection, property: ConceptGraphProperty, renderer: (T) -> String): ConceptButtonSelectorToolbar { val actionGroup = DefaultActionGroup(options.map { ButtonSelectorAction(it, property, renderer(it)) }) val toolbar = ConceptButtonSelectorToolbar("ButtonSelector", actionGroup, true) toolbar.targetComponent = null // any data context is supported, suppress warning @@ -22,14 +21,14 @@ fun ConceptRow.buttonSelector(options: Collection, property: GraphPropert } class ButtonSelectorAction @JvmOverloads constructor(private val option: T, - private val property: GraphProperty, + private val property: ConceptGraphProperty, optionText: Supplier<@NlsActions.ActionText String>, optionDescription: Supplier<@NlsActions.ActionText String>? = null) : ToggleAction(optionText, optionDescription ?: Supplier { null }, null), DumbAware { @JvmOverloads constructor(option: T, - property: GraphProperty, + property: ConceptGraphProperty, @NlsActions.ActionText optionText: String, @NlsActions.ActionDescription optionDescription: String? = null) : this(option, property, Supplier { optionText }, optionDescription?.let { Supplier { optionDescription } }) diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptCell.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptCell.kt index 730da08b2..6835d2d57 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptCell.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptCell.kt @@ -14,14 +14,13 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleUtil import com.intellij.openapi.module.StdModuleTypes -import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.ui.ValidationInfo import com.intellij.openapi.ui.panel.ComponentPanelBuilder import com.intellij.openapi.ui.popup.JBPopupFactory -import com.intellij.openapi.util.NlsContexts +import com.intellij.openapi.util.SystemInfoRt import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.JavaCodeFragment @@ -39,12 +38,12 @@ import com.intellij.util.ui.JBFont import com.intellij.util.ui.StatusText import com.intellij.util.ui.UIUtil import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Nls import org.jetbrains.annotations.NonNls import java.awt.Component import java.awt.Dimension import java.awt.event.ActionEvent -import java.awt.event.ActionListener import java.awt.event.ItemEvent import java.awt.event.MouseEvent import java.util.* @@ -105,8 +104,8 @@ inline fun KMutableProperty0.toNullableBinding(defaultValu } class ConceptValidationInfoBuilder(val component: JComponent) { - fun error(@NlsContexts.DialogMessage message: String): ValidationInfo = ValidationInfo(message, component) - fun warning(@NlsContexts.DialogMessage message: String): ValidationInfo = + fun error(message: String): ValidationInfo = ValidationInfo(message, component) + fun warning(message: String): ValidationInfo = ValidationInfo(message, component).asWarning().withOKEnabled() } @@ -114,7 +113,7 @@ interface ConceptCellBuilder { val component: T fun comment( - @NlsContexts.DetailedDescription text: String, + text: String, maxLineLength: Int = 70, forComponent: Boolean = false ): ConceptCellBuilder @@ -151,7 +150,7 @@ interface ConceptCellBuilder { return this } - fun withGraphProperty(property: GraphProperty<*>): ConceptCellBuilder + fun withGraphProperty(property: ConceptGraphProperty<*>): ConceptCellBuilder fun enabled(isEnabled: Boolean) fun enableIf(predicate: ComponentPredicate): ConceptCellBuilder @@ -160,14 +159,13 @@ interface ConceptCellBuilder { fun visibleIf(predicate: ComponentPredicate): ConceptCellBuilder fun withErrorOnApplyIf( - @NlsContexts.DialogMessage message: String, + message: String, callback: (T) -> Boolean ): ConceptCellBuilder { withValidationOnApply { if (callback(it)) error(message) else null } return this } - @ApiStatus.Internal fun shouldSaveOnApply(): Boolean fun withLargeLeftGap(): ConceptCellBuilder @@ -224,6 +222,42 @@ val ConceptCellBuilder.selected const val UNBOUND_RADIO_BUTTON = "unbound.radio.button" +const val MNEMONIC = 0x1B.toChar() + +@Contract("null -> null; !null -> !null") +fun replaceMnemonicAmpersand(value: @Nls String?): @Nls String? { + if (value == null || value.indexOf('&') < 0 || value.indexOf(MNEMONIC) >= 0) { + return value + } + val builder = StringBuilder() + val macMnemonic = value.contains("&&") + var i = 0 + while (i < value.length) { + val c = value[i] + if (c == '\\') { + if (i < value.length - 1 && value[i + 1] == '&') { + builder.append('&') + i++ + } else { + builder.append(c) + } + } else if (c == '&') { + if (i < value.length - 1 && value[i + 1] == '&') { + if (SystemInfoRt.isMac) { + builder.append(MNEMONIC) + } + i++ + } else if (!SystemInfoRt.isMac || !macMnemonic) { + builder.append(MNEMONIC) + } + } else { + builder.append(c) + } + i++ + } + return builder.toString() +} + // separate class to avoid row related methods in the `cell { } ` @ConceptCellMarker abstract class ConceptCell : ConceptBaseBuilder { @@ -251,7 +285,7 @@ abstract class ConceptCell : ConceptBaseBuilder { val push = CCFlags.push fun label( - @NlsContexts.Label text: String, + text: String, style: UIUtil.ComponentStyle? = null, fontColor: UIUtil.FontColor? = null, bold: Boolean = false @@ -261,7 +295,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun label( - @NlsContexts.Label text: String, + text: String, font: JBFont, fontColor: UIUtil.FontColor? = null ): ConceptCellBuilder { @@ -270,7 +304,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun link( - @NlsContexts.LinkLabel text: String, + text: String, style: UIUtil.ComponentStyle? = null, action: () -> Unit ): ConceptCellBuilder { @@ -278,89 +312,89 @@ abstract class ConceptCell : ConceptBaseBuilder { return component(result) } - fun browserLink(@NlsContexts.LinkLabel text: String, url: String): ConceptCellBuilder { + fun browserLink(text: String, url: String): ConceptCellBuilder { val result = BrowserLink(text, url) return component(result) } fun buttonFromAction( - @NlsContexts.Button text: String, + text: String, @NonNls actionPlace: String, action: AnAction ): ConceptCellBuilder { - val button = JButton(BundleBase.replaceMnemonicAmpersand(text)) + val button = JButton(replaceMnemonicAmpersand(text)) button.addActionListener { ActionUtil.invokeAction(action, button, actionPlace, null, null) } return component(button) } fun button( - @NlsContexts.Button text: String, + text: String, actionListener: (event: ActionEvent) -> Unit ): ConceptCellBuilder { - val button = JButton(BundleBase.replaceMnemonicAmpersand(text)) + val button = JButton(replaceMnemonicAmpersand(text)) button.addActionListener(actionListener) return component(button) } inline fun checkBox( - @NlsContexts.Checkbox text: String, + text: String, isSelected: Boolean = false, - @NlsContexts.DetailedDescription comment: String? = null, + comment: String? = null, crossinline actionListener: (event: ActionEvent, component: JCheckBox) -> Unit ): ConceptCellBuilder { return checkBox(text, isSelected, comment) .applyToComponent { - addActionListener(ActionListener { actionListener(it, this) }) + addActionListener { actionListener(it, this) } } } @JvmOverloads fun checkBox( - @NlsContexts.Checkbox text: String, + text: String, isSelected: Boolean = false, - @NlsContexts.DetailedDescription comment: String? = null + comment: String? = null ): ConceptCellBuilder { val result = JBCheckBox(text, isSelected) return result(comment = comment) } fun checkBox( - @NlsContexts.Checkbox text: String, + text: String, prop: KMutableProperty0, - @NlsContexts.DetailedDescription comment: String? = null + comment: String? = null ): ConceptCellBuilder { return checkBox(text, prop.toBinding(), comment) } fun checkBox( - @NlsContexts.Checkbox text: String, + text: String, getter: () -> Boolean, setter: (Boolean) -> Unit, - @NlsContexts.DetailedDescription comment: String? = null + comment: String? = null ): ConceptCellBuilder { return checkBox(text, ConceptPropertyBinding(getter, setter), comment) } private fun checkBox( - @NlsContexts.Checkbox text: String, + text: String, modelBinding: ConceptPropertyBinding, - @NlsContexts.DetailedDescription comment: String? + comment: String? ): ConceptCellBuilder { val component = JBCheckBox(text, modelBinding.get()) return component(comment = comment).withSelectedBinding(modelBinding) } fun checkBox( - @NlsContexts.Checkbox text: String, - property: GraphProperty, - @NlsContexts.DetailedDescription comment: String? = null + text: String, + property: ConceptGraphProperty, + comment: String? = null ): ConceptCellBuilder { val component = JBCheckBox(text, property.get()) return component(comment = comment).withGraphProperty(property).applyToComponent { component.bind(property) } } open fun radioButton( - @NlsContexts.RadioButton text: String, + text: String, @Nls comment: String? = null ): ConceptCellBuilder { val component = JBRadioButton(text) @@ -369,7 +403,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } open fun radioButton( - @NlsContexts.RadioButton text: String, + text: String, getter: () -> Boolean, setter: (Boolean) -> Unit, @Nls comment: String? = null @@ -379,7 +413,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } open fun radioButton( - @NlsContexts.RadioButton text: String, + text: String, prop: KMutableProperty0, @Nls comment: String? = null ): ConceptCellBuilder { @@ -423,7 +457,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun comboBox( model: ComboBoxModel, - property: GraphProperty, + property: ConceptGraphProperty, renderer: ListCellRenderer? = null ): ConceptCellBuilder> { return comboBox(model, ConceptPropertyBinding(property::get, property::set).toNullable(), renderer) @@ -431,7 +465,10 @@ abstract class ConceptCell : ConceptBaseBuilder { .applyToComponent { bind(property) } } - fun modulesComboBox(project: Project, property: GraphProperty): ConceptCellBuilder { + fun modulesComboBox( + project: Project, + property: ConceptGraphProperty + ): ConceptCellBuilder { return modulesComboBox(project, property::get, property::set) .withGraphProperty(property) .applyToComponent { bind(property) } @@ -477,7 +514,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun packagesComboBox( project: Project, recentsKey: String, - property: GraphProperty + property: ConceptGraphProperty ): ConceptCellBuilder { return packagesComboBox(project, recentsKey, property::get, property::set) .withGraphProperty(property) @@ -515,7 +552,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun classesComboBox( project: Project, recentsKey: String, - property: GraphProperty, + property: ConceptGraphProperty, init: (ReferenceEditorComboWithBrowseButton.() -> Unit)? = null ): ConceptCellBuilder { return classesComboBox(project, recentsKey, property::get, property::set, init) @@ -640,7 +677,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun textField( - property: GraphProperty, + property: ConceptGraphProperty, columns: Int? = null, init: (JBTextField.() -> Unit)? = null ): ConceptCellBuilder { @@ -668,7 +705,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun textArea( - property: GraphProperty, + property: ConceptGraphProperty, rows: Int? = null, columns: Int? = null ): ConceptCellBuilder { @@ -690,7 +727,7 @@ abstract class ConceptCell : ConceptBaseBuilder { .withTextBinding(binding) } - fun scrollableTextArea(property: GraphProperty, rows: Int? = null): ConceptCellBuilder { + fun scrollableTextArea(property: ConceptGraphProperty, rows: Int? = null): ConceptCellBuilder { return scrollableTextArea(property::get, property::set, rows) .withGraphProperty(property) .applyToComponent { bind(property) } @@ -770,7 +807,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun textFieldWithHistoryWithBrowseButton( getter: () -> String, setter: (String) -> Unit, - @NlsContexts.DialogTitle browseDialogTitle: String, + browseDialogTitle: String, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), historyProvider: (() -> List)? = null, @@ -794,7 +831,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun textFieldWithBrowseButton( - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + browseDialogTitle: String? = null, value: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), @@ -807,7 +844,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun textFieldWithBrowseButton( prop: KMutableProperty0, - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + browseDialogTitle: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), fileChosen: ((chosenFile: VirtualFile) -> String)? = null @@ -819,7 +856,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun textFieldWithBrowseButton( getter: () -> String, setter: (String) -> Unit, - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + browseDialogTitle: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), fileChosen: ((chosenFile: VirtualFile) -> String)? = null @@ -830,7 +867,7 @@ abstract class ConceptCell : ConceptBaseBuilder { fun textFieldWithBrowseButton( modelBinding: ConceptPropertyBinding, - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + browseDialogTitle: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), fileChosen: ((chosenFile: VirtualFile) -> String)? = null @@ -843,9 +880,9 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun textFieldWithBrowseButton( - property: GraphProperty, - emptyTextProperty: GraphProperty, - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + property: ConceptGraphProperty, + emptyTextProperty: ConceptGraphProperty, + browseDialogTitle: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), fileChosen: ((chosenFile: VirtualFile) -> String)? = null @@ -855,8 +892,8 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun textFieldWithBrowseButton( - property: GraphProperty, - @NlsContexts.DialogTitle browseDialogTitle: String? = null, + property: ConceptGraphProperty, + browseDialogTitle: String? = null, project: Project? = null, fileChooserDescriptor: FileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor(), fileChosen: ((chosenFile: VirtualFile) -> String)? = null @@ -927,7 +964,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } fun expandableTextField( - prop: GraphProperty, + prop: ConceptGraphProperty, parser: Function> = ParametersListUtil.DEFAULT_LINE_PARSER, joiner: Function, String> = ParametersListUtil.DEFAULT_LINE_JOINER ) @@ -942,7 +979,7 @@ abstract class ConceptCell : ConceptBaseBuilder { */ @JvmOverloads fun panel( - @NlsContexts.BorderTitle title: String, + title: String, wrappedComponent: Component, hasSeparator: Boolean = true ): ConceptCellBuilder { @@ -966,11 +1003,11 @@ abstract class ConceptCell : ConceptBaseBuilder { }) } - fun comment(@NlsContexts.DetailedDescription text: String, maxLineLength: Int = -1): ConceptCellBuilder { + fun comment(text: String, maxLineLength: Int = -1): ConceptCellBuilder { return component(ComponentPanelBuilder.createCommentComponent(text, true, maxLineLength, true)) } - fun commentNoWrap(@NlsContexts.DetailedDescription text: String): ConceptCellBuilder { + fun commentNoWrap(text: String): ConceptCellBuilder { return component(ComponentPanelBuilder.createNonWrappingCommentComponent(text)) } @@ -989,7 +1026,7 @@ abstract class ConceptCell : ConceptBaseBuilder { operator fun T.invoke( vararg constraints: CCFlags, growPolicy: ConceptGrowPolicy? = null, - @NlsContexts.DetailedDescription comment: String? = null + comment: String? = null ): ConceptCellBuilder = component(this).apply { constraints(*constraints) if (comment != null) comment(comment) @@ -997,7 +1034,7 @@ abstract class ConceptCell : ConceptBaseBuilder { } } -private fun JBCheckBox.bind(property: GraphProperty) { +private fun JBCheckBox.bind(property: ConceptGraphProperty) { val mutex = AtomicBoolean() property.afterChange { mutex.lockOrSkip { @@ -1035,7 +1072,7 @@ fun listCellRenderer(renderer: SimpleListCellRenderer.(value: T, index: } } -fun ComboBox.bind(property: GraphProperty) { +fun ComboBox.bind(property: ConceptGraphProperty) { val mutex = AtomicBoolean() property.afterChange { mutex.lockOrSkip { @@ -1052,7 +1089,7 @@ fun ComboBox.bind(property: GraphProperty) { } } -fun ReferenceEditorComboWithBrowseButton.bind(property: GraphProperty) { +fun ReferenceEditorComboWithBrowseButton.bind(property: ConceptGraphProperty) { /*(childComponent as ComboBox).bind(property)*/ val mutex = AtomicBoolean() property.afterChange { @@ -1070,7 +1107,7 @@ fun ReferenceEditorComboWithBrowseButton.bind(property: GraphProperty) { }) } -fun ClassNameReferenceEditor.bind(property: GraphProperty) { +fun ClassNameReferenceEditor.bind(property: ConceptGraphProperty) { text = property.get() property.afterChange { text = it @@ -1083,7 +1120,7 @@ fun ClassNameReferenceEditor.bind(property: GraphProperty) { private val TextFieldWithBrowseButton.emptyText get() = (textField as JBTextField).emptyText -fun StatusText.bind(property: GraphProperty) { +fun StatusText.bind(property: ConceptGraphProperty) { text = property.get() property.afterChange { text = it @@ -1093,11 +1130,11 @@ fun StatusText.bind(property: GraphProperty) { } } -fun TextFieldWithBrowseButton.bind(property: GraphProperty) { +fun TextFieldWithBrowseButton.bind(property: ConceptGraphProperty) { textField.bind(property) } -fun JTextComponent.bind(property: GraphProperty) { +fun JTextComponent.bind(property: ConceptGraphProperty) { val mutex = AtomicBoolean() property.afterChange { mutex.lockOrSkip { diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptFormUiUtil.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptFormUiUtil.kt index db746529d..457cb3b18 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptFormUiUtil.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptFormUiUtil.kt @@ -3,7 +3,6 @@ package com.github.linyuzai.cloud.plugin.intellij import com.github.linyuzai.cloud.plugin.intellij.util.ConceptDialog import com.intellij.ide.IdeBundle import com.intellij.ide.starters.JavaStartersBundle -import com.intellij.ide.starters.shared.TextValidationFunction import com.intellij.openapi.Disposable import com.intellij.openapi.editor.event.DocumentListener import com.intellij.openapi.ui.* @@ -43,8 +42,8 @@ internal fun gridConstraint(col: Int, row: Int): GridBagConstraints { fun withValidationForText( builder: ConceptCellBuilder, - errorChecks: List, - warningChecks: TextValidationFunction?, + errorChecks: List, + warningChecks: ConceptTextValidationFunction?, dialog: ConceptDialog ): ConceptCellBuilder { if (errorChecks.isEmpty()) return builder @@ -101,8 +100,8 @@ fun withValidationForText( fun withValidationForEditorCombo( builder: ConceptCellBuilder, - errorChecks: List, - warningChecks: TextValidationFunction?, + errorChecks: List, + warningChecks: ConceptTextValidationFunction?, dialog: ConceptDialog ): ConceptCellBuilder { if (errorChecks.isEmpty()) return builder @@ -148,8 +147,8 @@ fun withValidationForEditorCombo( fun withValidation( builder: ConceptCellBuilder, - errorChecks: List, - warningChecks: TextValidationFunction?, + errorChecks: List, + warningChecks: ConceptTextValidationFunction?, validatedTextComponents: MutableList, parentDisposable: Disposable ): ConceptCellBuilder { diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphProperty.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphProperty.kt index b9b24591d..15eccfaa6 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphProperty.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphProperty.kt @@ -1,18 +1,17 @@ -/* package com.github.linyuzai.cloud.plugin.intellij import com.intellij.openapi.Disposable -import com.intellij.openapi.observable.properties.ObservableClearableProperty -import kotlin.properties.ReadWriteProperty +import com.intellij.openapi.observable.properties.* +import com.intellij.util.containers.DisposableWrapperList +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.atomic.AtomicReference import kotlin.reflect.KProperty -interface ConceptGraphProperty : ReadWriteProperty { +interface ConceptGraphProperty { - fun dependsOn(parent: ObservableClearableProperty<*>) + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = get() - fun dependsOn(parent: ObservableClearableProperty<*>, default: () -> T) - - fun afterPropagation(listener: () -> Unit) + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value) fun get(): T @@ -28,7 +27,153 @@ interface ConceptGraphProperty : ReadWriteProperty { fun afterReset(listener: () -> Unit, parentDisposable: Disposable) - override fun getValue(thisRef: Any?, property: KProperty<*>): T = get() + fun dependsOn(parent: ConceptGraphProperty<*>) + + fun dependsOn(parent: ConceptGraphProperty<*>, default: () -> T) + + fun afterPropagation(listener: () -> Unit) + + fun updateAndGet(update: (T) -> T): T +} + +class ConceptGraphPropertyImpl(private val propertyGraph: ConceptPropertyGraph, private val initial: () -> T) : + ConceptGraphProperty { + + private val changeListeners1 = CopyOnWriteArrayList<(T) -> Unit>() + private val resetListeners1 = CopyOnWriteArrayList<() -> Unit>() + + private val changeListeners2 = DisposableWrapperList<(T) -> Unit>() + private val resetListeners2 = DisposableWrapperList<() -> Unit>() + + init { + propertyGraph.register(this) + } + + override fun dependsOn(parent: ConceptGraphProperty<*>) { + propertyGraph.dependsOn(this, parent) + } + + override fun dependsOn(parent: ConceptGraphProperty<*>, default: () -> T) { + propertyGraph.dependsOn(this, parent, default) + } + + override fun afterPropagation(listener: () -> Unit) { + propertyGraph.afterPropagation(listener) + } + + protected fun fireChangeEvent(value: T) { + changeListeners1.forEach { it(value) } + } + + protected fun fireResetEvent() { + resetListeners1.forEach { it() } + } + + override fun afterChange(listener: (T) -> Unit) { + changeListeners1.add(listener) + } + + override fun afterReset(listener: () -> Unit) { + resetListeners1.add(listener) + } + + override fun afterChange(listener: (T) -> Unit, parentDisposable: Disposable) { + changeListeners2.add(listener, parentDisposable) + } + + override fun afterReset(listener: () -> Unit, parentDisposable: Disposable) { + resetListeners2.add(listener, parentDisposable) + } + + private val value = AtomicReference(UNINITIALIZED_VALUE) + + override fun get(): T { + return update { it } + } + + override fun set(value: T) { + this.value.set(value) + fireChangeEvent(value) + } + + override fun updateAndGet(update: (T) -> T): T { + val newValue = update(update) + fireChangeEvent(newValue) + return newValue + } + + @Suppress("UNCHECKED_CAST") + private fun update(update: (T) -> T): T { + val resolve = { it: Any? -> if (it === UNINITIALIZED_VALUE) initial() else it as T } + return value.updateAndGet { update(resolve(it)) } as T + } + + override fun reset() { + value.set(UNINITIALIZED_VALUE) + fireResetEvent() + } + + companion object { + private val UNINITIALIZED_VALUE = Any() + } +} + +private class ConceptGraphPropertyView( + private val instance: ConceptGraphProperty, + private val map: (S) -> T, + private val comap: (T) -> S +) : ConceptGraphProperty { + override fun dependsOn(parent: ConceptGraphProperty<*>) = + instance.dependsOn(parent) + + override fun dependsOn(parent: ConceptGraphProperty<*>, default: () -> T) = + instance.dependsOn(parent) { comap(default()) } + + override fun afterPropagation(listener: () -> Unit) = + instance.afterPropagation(listener) + + override fun updateAndGet(update: (T) -> T): T { + throw UnsupportedOperationException() + } + + override fun get(): T = + map(instance.get()) + + override fun set(value: T) = + instance.set(comap(value)) + + override fun reset() = + instance.reset() + + override fun afterChange(listener: (T) -> Unit) = + instance.afterChange { listener(map(it)) } + + override fun afterReset(listener: () -> Unit) = + instance.afterReset(listener) + + override fun afterChange(listener: (T) -> Unit, parentDisposable: Disposable) = + instance.afterChange({ listener(map(it)) }, parentDisposable) + + override fun afterReset(listener: () -> Unit, parentDisposable: Disposable) = + instance.afterReset(listener, parentDisposable) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + return instance == other + } + + override fun hashCode(): Int { + return instance.hashCode() + } +} + +fun ConceptGraphProperty.map(transform: (T) -> T) = transform(transform, { it }) +fun ConceptGraphProperty.comap(transform: (T) -> T) = transform({ it }, transform) +fun ConceptGraphProperty.transform(map: (S) -> T, comap: (T) -> S): ConceptGraphProperty = + ConceptGraphPropertyView(this, map, comap) + + +fun ConceptPropertyGraph.property(initial: () -> T): ConceptGraphProperty = ConceptGraphPropertyImpl(this, initial) - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value) -}*/ +fun ConceptPropertyGraph.graphPropertyView(initial: () -> T, map: (S) -> T, comap: (T) -> S): ConceptGraphProperty = + this.property { comap(initial()) }.transform(map, comap) diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphPropertyImpl.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphPropertyImpl.kt deleted file mode 100644 index 95d9d33e5..000000000 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptGraphPropertyImpl.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* -package com.github.linyuzai.cloud.plugin.intellij - -import com.intellij.openapi.observable.properties.* - -class ConceptGraphPropertyImpl(private val propertyGraph: PropertyGraph, initial: () -> T) - : ConceptGraphProperty, AtomicLazyProperty(initial) { - - override fun dependsOn(parent: ObservableClearableProperty<*>) { - propertyGraph.dependsOn(this, parent) - } - - override fun dependsOn(parent: ObservableClearableProperty<*>, default: () -> T) { - propertyGraph.dependsOn(this, parent, default) - } - - override fun afterPropagation(listener: () -> Unit) { - propertyGraph.afterPropagation(listener) - } - - init { - propertyGraph.register(this) - } - - companion object { - fun PropertyGraph.graphProperty(initial: () -> T): ConceptGraphProperty = ConceptGraphPropertyImpl(this, initial) - - fun PropertyGraph.graphPropertyView(initial: () -> T, map: (S) -> T, comap: (T) -> S): GraphProperty = - this.graphProperty { comap(initial()) }.transform(map, comap) - } - - fun GraphProperty.map(transform: (T) -> T) = transform(transform, { it }) - fun GraphProperty.comap(transform: (T) -> T) = transform({ it }, transform) - fun GraphProperty.transform(map: (S) -> T, comap: (T) -> S): GraphProperty = - GraphPropertyView(this, map, comap) -}*/ diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptHideableTitledSeparator.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptHideableTitledSeparator.kt index cfb88a340..ae058fd62 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptHideableTitledSeparator.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptHideableTitledSeparator.kt @@ -2,13 +2,12 @@ package com.github.linyuzai.cloud.plugin.intellij import com.intellij.icons.AllIcons import com.intellij.openapi.util.IconLoader -import com.intellij.openapi.util.NlsContexts import com.intellij.ui.TitledSeparator import java.awt.Cursor import java.awt.event.MouseAdapter import java.awt.event.MouseEvent -class ConceptHideableTitledSeparator(@NlsContexts.Separator title: String) : TitledSeparator(title) { +class ConceptHideableTitledSeparator(title: String) : TitledSeparator(title) { private var isExpanded: Boolean = true diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptJdkComboBox.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptJdkComboBox.kt index a3ff9eb07..2d46ce913 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptJdkComboBox.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptJdkComboBox.kt @@ -21,7 +21,7 @@ import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel import com.intellij.openapi.ui.Messages fun ConceptRow.sdkComboBox( - sdkModel: ProjectSdksModel, sdkProperty: GraphProperty, + sdkModel: ProjectSdksModel, sdkProperty: ConceptGraphProperty, project: Project?, moduleBuilder: ModuleBuilder ): ConceptCellBuilder { sdkModel.reset(project) diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayout.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayout.kt index ae8da3b7d..f199acaf2 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayout.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayout.kt @@ -1,7 +1,6 @@ package com.github.linyuzai.cloud.plugin.intellij import com.intellij.openapi.ui.DialogPanel -import com.intellij.openapi.util.NlsContexts import com.intellij.ui.components.DialogPanel import com.intellij.ui.layout.LCFlags import com.intellij.util.ui.JBUI @@ -9,7 +8,7 @@ import javax.swing.border.Border inline fun panel( vararg constraints: LCFlags, - @NlsContexts.DialogTitle title: String? = null, + title: String? = null, border: Border = JBUI.Borders.empty(10, 20, 5, 20), init: ConceptLayoutBuilder.() -> Unit ): DialogPanel { @@ -25,9 +24,6 @@ inline fun panel( @PublishedApi internal fun initPanel(builder: ConceptLayoutBuilder, panel: DialogPanel) { panel.preferredFocusedComponent = builder.builder.preferredFocusedComponent - panel.validateCallbacks = builder.builder.validateCallbacks - panel.componentValidateCallbacks = builder.builder.componentValidateCallbacks - panel.customValidationRequestors = builder.builder.customValidationRequestors panel.applyCallbacks = builder.builder.applyCallbacks panel.resetCallbacks = builder.builder.resetCallbacks panel.isModifiedCallbacks = builder.builder.isModifiedCallbacks diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayoutBuilder.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayoutBuilder.kt index 19520dacf..a54d7a738 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayoutBuilder.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptLayoutBuilder.kt @@ -4,7 +4,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.fileChooser.FileChooser import com.intellij.openapi.fileChooser.FileChooserDescriptor -import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.components.JBRadioButton import com.intellij.ui.layout.* @@ -13,12 +12,16 @@ import java.awt.event.ActionListener import javax.swing.AbstractButton import javax.swing.ButtonGroup -open class ConceptLayoutBuilder @PublishedApi internal constructor(@PublishedApi internal val builder: ConceptLayoutBuilderImpl) : ConceptRowBuilder by builder.rootRow { +open class ConceptLayoutBuilder @PublishedApi internal constructor(@PublishedApi internal val builder: ConceptLayoutBuilderImpl) : + ConceptRowBuilder by builder.rootRow { override fun withButtonGroup(title: String?, buttonGroup: ButtonGroup, body: () -> Unit) { builder.withButtonGroup(buttonGroup, body) } - inline fun buttonGroup(crossinline elementActionListener: () -> Unit, crossinline init: ConceptLayoutBuilder.() -> Unit): ButtonGroup { + inline fun buttonGroup( + crossinline elementActionListener: () -> Unit, + crossinline init: ConceptLayoutBuilder.() -> Unit + ): ButtonGroup { val group = ButtonGroup() builder.withButtonGroup(group) { @@ -39,37 +42,59 @@ open class ConceptLayoutBuilder @PublishedApi internal constructor(@PublishedApi } class ConceptCellBuilderWithButtonGroupProperty -@PublishedApi internal constructor(private val prop: ConceptPropertyBinding) { +@PublishedApi internal constructor(private val prop: ConceptPropertyBinding) { - fun ConceptCell.radioButton(@NlsContexts.RadioButton text: String, value: T, @Nls comment: String? = null): ConceptCellBuilder { + fun ConceptCell.radioButton( + text: String, + value: T, + @Nls comment: String? = null + ): ConceptCellBuilder { val component = JBRadioButton(text, prop.get() == value) return component(comment = comment).bindValue(value) } - fun ConceptCellBuilder.bindValue(value: T): ConceptCellBuilder = bindValueToProperty(prop, value) + fun ConceptCellBuilder.bindValue(value: T): ConceptCellBuilder = + bindValueToProperty(prop, value) } class ConceptRowBuilderWithButtonGroupProperty -@PublishedApi internal constructor(private val builder: ConceptRowBuilder, private val prop: ConceptPropertyBinding) : ConceptRowBuilder by builder { - - fun ConceptRow.radioButton(@NlsContexts.RadioButton text: String, value: T, @Nls comment: String? = null): ConceptCellBuilder { +@PublishedApi internal constructor( + private val builder: ConceptRowBuilder, + private val prop: ConceptPropertyBinding +) : ConceptRowBuilder by builder { + + fun ConceptRow.radioButton( + text: String, + value: T, + @Nls comment: String? = null + ): ConceptCellBuilder { val component = JBRadioButton(text, prop.get() == value) attachSubRowsEnabled(component) return component(comment = comment).bindValue(value) } - fun ConceptCellBuilder.bindValue(value: T): ConceptCellBuilder = bindValueToProperty(prop, value) + fun ConceptCellBuilder.bindValue(value: T): ConceptCellBuilder = + bindValueToProperty(prop, value) } -private fun ConceptCellBuilder.bindValueToProperty(prop: ConceptPropertyBinding, value: T): ConceptCellBuilder = apply { +private fun ConceptCellBuilder.bindValueToProperty( + prop: ConceptPropertyBinding, + value: T +): ConceptCellBuilder = apply { onApply { if (component.isSelected) prop.set(value) } onReset { component.isSelected = prop.get() == value } onIsModified { component.isSelected != (prop.get() == value) } } fun FileChooserDescriptor.chooseFile(event: AnActionEvent, fileChosen: (chosenFile: VirtualFile) -> Unit) { - FileChooser.chooseFile(this, event.getData(PlatformDataKeys.PROJECT), event.getData(PlatformDataKeys.CONTEXT_COMPONENT), null, fileChosen) + FileChooser.chooseFile( + this, + event.getData(PlatformDataKeys.PROJECT), + event.getData(PlatformDataKeys.CONTEXT_COMPONENT), + null, + fileChosen + ) } fun ConceptRow.attachSubRowsEnabled(component: AbstractButton) { diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutBuilder.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutBuilder.kt index 5a07daffa..d23b9c743 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutBuilder.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutBuilder.kt @@ -72,7 +72,7 @@ internal class ConceptMigLayoutBuilder(val spacing: ConceptSpacingConfiguration) try { body() - resetCallbacks.getOrPut(null, { SmartList() }).add { + resetCallbacks.getOrPut(null) { SmartList() }.add { selectRadioButtonInGroup(buttonGroup) } @@ -138,7 +138,7 @@ internal class ConceptMigLayoutBuilder(val spacing: ConceptSpacingConfiguration) val rowConstraints = AC() (container as JComponent).putClientProperty( - DialogWrapper.IS_VISUAL_PADDING_COMPENSATED_ON_COMPONENT_LEVEL_KEY, + "isVisualPaddingCompensatedOnComponentLevel", false ) var isLayoutInsetsAdjusted = false diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutRow.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutRow.kt index b1c62dec4..6777d6d5e 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutRow.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutRow.kt @@ -1,7 +1,6 @@ package com.github.linyuzai.cloud.plugin.intellij import com.intellij.icons.AllIcons -import com.intellij.openapi.observable.properties.GraphProperty import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.OnePixelDivider import com.intellij.openapi.ui.TextFieldWithBrowseButton @@ -560,9 +559,9 @@ private class ConceptCellBuilderImpl internal constructor( private val viewComponent: JComponent = component ) : ConceptCellBuilder, ConceptCheckboxCellBuilder, ConceptScrollPaneCellBuilder { private var applyIfEnabled = false - private var property: GraphProperty<*>? = null + private var property: ConceptGraphProperty<*>? = null - override fun withGraphProperty(property: GraphProperty<*>): ConceptCellBuilder { + override fun withGraphProperty(property: ConceptGraphProperty<*>): ConceptCellBuilder { this.property = property return this } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutUtil.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutUtil.kt index 0f3fa65e6..d30426f6f 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutUtil.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptMigLayoutUtil.kt @@ -1,6 +1,5 @@ package com.github.linyuzai.cloud.plugin.intellij - import net.miginfocom.layout.BoundSize import net.miginfocom.layout.LC import net.miginfocom.layout.UnitValue diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptProjectLocationField.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptProjectLocationField.kt index a24209e7b..3190485d3 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptProjectLocationField.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptProjectLocationField.kt @@ -2,24 +2,22 @@ package com.github.linyuzai.cloud.plugin.intellij import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.transform import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VirtualFile fun ConceptRow.projectLocationField( - locationProperty: GraphProperty, + locationProperty: ConceptGraphProperty, wizardContext: WizardContext ): ConceptCellBuilder { val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor().withFileFilter { it.isDirectory } val fileChosen = { file: VirtualFile -> getUiFilePath(file.path) } val title = "Select ${wizardContext.presentationName} File Directory" - //val property = locationProperty.map { getUiFilePath(it) }.comap { getModelFilePath(it) } - val property = locationProperty + val property = locationProperty.map { getUiFilePath(it) }.comap { getModelFilePath(it) } + /*val property = locationProperty .transform({ getUiFilePath(it) }, { it }) - .transform({ it }, { getModelFilePath(it) }) + .transform({ it }, { getModelFilePath(it) })*/ return this.textFieldWithBrowseButton(property, title, wizardContext.project, fileChooserDescriptor, fileChosen) } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptPropertyGraph.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptPropertyGraph.kt new file mode 100644 index 000000000..92f0ee057 --- /dev/null +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptPropertyGraph.kt @@ -0,0 +1,80 @@ +package com.github.linyuzai.cloud.plugin.intellij + +import com.github.linyuzai.cloud.plugin.intellij.ConceptAnonymousParallelOperationTrace.Companion.task +import com.intellij.openapi.util.RecursionManager +import org.jetbrains.annotations.TestOnly +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArrayList + +class ConceptPropertyGraph(debugName: String? = null, private val isBlockPropagation: Boolean = true) { + + private val propagation = ConceptAnonymousParallelOperationTrace((if (debugName == null) "" else " of $debugName") + ": Graph propagation") + private val properties = ConcurrentHashMap, PropertyNode>() + private val dependencies = ConcurrentHashMap>>() + private val recursionGuard = RecursionManager.createGuard(ConceptPropertyGraph::class.java.name) + + fun dependsOn(child: ConceptGraphProperty, parent: ConceptGraphProperty<*>) { + addDependency(child, parent) { reset() } + } + + fun dependsOn(child: ConceptGraphProperty, parent: ConceptGraphProperty<*>, update: () -> T) { + addDependency(child, parent) { updateAndGet { update() } } + } + + private fun addDependency(child: ConceptGraphProperty, parent: ConceptGraphProperty<*>, update: ConceptGraphProperty.() -> Unit) { + val childNode = properties[child] ?: throw IllegalArgumentException("Unregistered child property") + val parentNode = properties[parent] ?: throw IllegalArgumentException("Unregistered parent property") + dependencies.putIfAbsent(parentNode, CopyOnWriteArrayList()) + val children = dependencies.getValue(parentNode) + children.add(Dependency(childNode, child, update)) + } + + fun afterPropagation(listener: () -> Unit) { + propagation.afterOperation(listener) + } + + fun register(property: ConceptGraphProperty<*>) { + val node = PropertyNode() + properties[property] = node + property.afterChange { + recursionGuard.doPreventingRecursion(node, false) { + propagation.task { + node.isPropagationBlocked = isBlockPropagation + propagateChange(node) + } + } + } + property.afterReset { + node.isPropagationBlocked = false + } + } + + private fun propagateChange(parent: PropertyNode) { + val dependencies = dependencies[parent] ?: return + for (dependency in dependencies) { + val child = dependency.node + if (child.isPropagationBlocked) continue + recursionGuard.doPreventingRecursion(child, false) { + dependency.applyUpdate() + propagateChange(child) + } + } + } + + @TestOnly + fun isPropagationBlocked(property: ConceptGraphProperty<*>) = + properties.getValue(property).isPropagationBlocked + + private inner class PropertyNode { + @Volatile + var isPropagationBlocked = false + } + + private data class Dependency( + val node: PropertyNode, + val property: ConceptGraphProperty, + val update: ConceptGraphProperty.() -> Unit + ) { + fun applyUpdate() = property.update() + } +} \ No newline at end of file diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptRow.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptRow.kt index bad39885d..0ef0fa0ca 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptRow.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptRow.kt @@ -2,7 +2,6 @@ package com.github.linyuzai.cloud.plugin.intellij import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.panel.ComponentPanelBuilder -import com.intellij.openapi.util.NlsContexts import com.intellij.ui.components.Label import com.intellij.ui.components.noteComponent import com.intellij.ui.layout.* @@ -13,7 +12,7 @@ import javax.swing.JLabel import kotlin.reflect.KMutableProperty0 interface ConceptBaseBuilder { - fun withButtonGroup(@NlsContexts.BorderTitle title: String?, buttonGroup: ButtonGroup, body: () -> Unit) + fun withButtonGroup(title: String?, buttonGroup: ButtonGroup, body: () -> Unit) fun withButtonGroup(buttonGroup: ButtonGroup, body: () -> Unit) { withButtonGroup(null, buttonGroup, body) @@ -23,16 +22,18 @@ interface ConceptBaseBuilder { buttonGroup(null, init) } - fun buttonGroup(@NlsContexts.BorderTitle title:String? = null, init: () -> Unit) { + fun buttonGroup(title: String? = null, init: () -> Unit) { withButtonGroup(title, ButtonGroup(), init) } } interface ConceptRowBuilder : ConceptBaseBuilder { - fun createChildRow(label: JLabel? = null, - isSeparated: Boolean = false, - noGrid: Boolean = false, - @Nls title: String? = null): ConceptRow + fun createChildRow( + label: JLabel? = null, + isSeparated: Boolean = false, + noGrid: Boolean = false, + @Nls title: String? = null + ): ConceptRow fun createNoteOrCommentRow(component: JComponent): ConceptRow @@ -46,7 +47,7 @@ interface ConceptRowBuilder : ConceptBaseBuilder { return createChildRow(label?.let { Label(it) }, isSeparated = separated).apply(init) } - fun titledRow(@NlsContexts.BorderTitle title: String, init: ConceptRow.() -> Unit): ConceptRow + fun titledRow(title: String, init: ConceptRow.() -> Unit): ConceptRow /** * Creates row with a huge gap after it, that can be used to group related components. @@ -58,7 +59,7 @@ interface ConceptRowBuilder : ConceptBaseBuilder { * Creates row with hideable decorator. * It allows to hide some information under the titled decorator */ - fun hideableRow(@NlsContexts.Separator title: String, init: ConceptRow.() -> Unit): ConceptRow + fun hideableRow(title: String, init: ConceptRow.() -> Unit): ConceptRow /** * Hyperlinks are supported (``), new lines and `
` are supported only if no links (file issue if need). @@ -74,36 +75,56 @@ interface ConceptRowBuilder : ConceptBaseBuilder { /** * Creates a nested UI DSL panel, with a grid which is independent of this pane. */ - fun nestedPanel(@NlsContexts.BorderTitle title: String? = null, init: ConceptLayoutBuilder.() -> Unit): ConceptCellBuilder + fun nestedPanel(title: String? = null, init: ConceptLayoutBuilder.() -> Unit): ConceptCellBuilder fun onGlobalApply(callback: () -> Unit): ConceptRow fun onGlobalReset(callback: () -> Unit): ConceptRow fun onGlobalIsModified(callback: () -> Boolean): ConceptRow } -inline fun ConceptInnerCell.buttonGroup(prop: KMutableProperty0, crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptInnerCell.buttonGroup( + prop: KMutableProperty0, + crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit +) { buttonGroup(prop.toBinding(), init) } -inline fun ConceptInnerCell.buttonGroup(noinline getter: () -> T, noinline setter: (T) -> Unit, crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptInnerCell.buttonGroup( + noinline getter: () -> T, + noinline setter: (T) -> Unit, + crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit +) { buttonGroup(ConceptPropertyBinding(getter, setter), init) } -inline fun ConceptInnerCell.buttonGroup(binding: ConceptPropertyBinding, crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptInnerCell.buttonGroup( + binding: ConceptPropertyBinding, + crossinline init: ConceptCellBuilderWithButtonGroupProperty.() -> Unit +) { withButtonGroup(ButtonGroup()) { ConceptCellBuilderWithButtonGroupProperty(binding).init() } } -inline fun ConceptRowBuilder.buttonGroup(prop: KMutableProperty0, crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptRowBuilder.buttonGroup( + prop: KMutableProperty0, + crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit +) { buttonGroup(prop.toBinding(), init) } -inline fun ConceptRowBuilder.buttonGroup(noinline getter: () -> T, noinline setter: (T) -> Unit, crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptRowBuilder.buttonGroup( + noinline getter: () -> T, + noinline setter: (T) -> Unit, + crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit +) { buttonGroup(ConceptPropertyBinding(getter, setter), init) } -inline fun ConceptRowBuilder.buttonGroup(binding: ConceptPropertyBinding, crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit) { +inline fun ConceptRowBuilder.buttonGroup( + binding: ConceptPropertyBinding, + crossinline init: ConceptRowBuilderWithButtonGroupProperty.() -> Unit +) { withButtonGroup(ButtonGroup()) { ConceptRowBuilderWithButtonGroupProperty(this, binding).init() } @@ -158,7 +179,11 @@ abstract class ConceptRow : ConceptCell(), ConceptRowBuilder { // backward compatibility @Deprecated(level = DeprecationLevel.HIDDEN, message = "deprecated") - operator fun JComponent.invoke(vararg constraints: CCFlags, gapLeft: Int = 0, growPolicy: ConceptGrowPolicy? = null) { + operator fun JComponent.invoke( + vararg constraints: CCFlags, + gapLeft: Int = 0, + growPolicy: ConceptGrowPolicy? = null + ) { invoke(constraints = *constraints, growPolicy = growPolicy).withLeftGap(gapLeft) } } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptStarterSettings.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptStarterSettings.kt index c89b0af6c..61f2e502a 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptStarterSettings.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptStarterSettings.kt @@ -1,44 +1,42 @@ @file:JvmName("ConceptStarterSettings") + package com.github.linyuzai.cloud.plugin.intellij import com.intellij.openapi.projectRoots.JavaSdkVersion -import com.intellij.openapi.util.NlsContexts.DialogTitle -import com.intellij.openapi.util.NlsContexts.Label -import com.intellij.openapi.util.NlsSafe data class ConceptStarterLanguage( val id: String, - @NlsSafe val title: String, + val title: String, val languageId: String, val isBuiltIn: Boolean = false, - @NlsSafe val description: String? = null + val description: String? = null ) data class ConceptStarterTestRunner( val id: String, - @NlsSafe val title: String + val title: String ) data class ConceptStarterProjectType( val id: String, - @NlsSafe val title: String, - @NlsSafe val description: String? = null + val title: String, + val description: String? = null ) data class ConceptStarterAppType( val id: String, - @NlsSafe val title: String + val title: String ) data class ConceptStarterAppPackaging( val id: String, - @NlsSafe val title: String, - @NlsSafe val description: String? = null + val title: String, + val description: String? = null ) data class ConceptStarterLanguageLevel( val id: String, - @NlsSafe val title: String, + val title: String, /** * Version string that can be parsed with [JavaSdkVersion.fromVersionString]. */ @@ -46,12 +44,12 @@ data class ConceptStarterLanguageLevel( ) class ConceptCustomizedMessages { - var projectTypeLabel: @Label String? = null - var serverUrlDialogTitle: @DialogTitle String? = null - var dependenciesLabel: @Label String? = null - var selectedDependenciesLabel: @Label String? = null - var noDependenciesSelectedLabel: @Label String? = null - var frameworkVersionLabel: @Label String? = null + var projectTypeLabel: String? = null + var serverUrlDialogTitle: String? = null + var dependenciesLabel: String? = null + var selectedDependenciesLabel: String? = null + var noDependenciesSelectedLabel: String? = null + var frameworkVersionLabel: String? = null } class ConceptStarterWizardSettings( @@ -75,7 +73,6 @@ class ConceptPluginRecommendation( } interface ConceptLibraryInfo { - @get:NlsSafe val title: String val description: String? val links: List @@ -84,9 +81,9 @@ interface ConceptLibraryInfo { } class ConceptLibraryLink( - @NlsSafe + val url: String, - @NlsSafe + val title: String? = null ) diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptTextValidationFunction.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptTextValidationFunction.kt new file mode 100644 index 000000000..54fd1f552 --- /dev/null +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptTextValidationFunction.kt @@ -0,0 +1,129 @@ +package com.github.linyuzai.cloud.plugin.intellij + +import com.intellij.openapi.util.io.FileUtil +import com.intellij.psi.impl.PsiNameHelperImpl +import java.nio.file.InvalidPathException +import java.nio.file.Path +import java.nio.file.Paths +import java.util.regex.Pattern + +fun interface ConceptTextValidationFunction { + fun checkText(fieldText: String): String? +} + +val CHECK_NOT_EMPTY = ConceptTextValidationFunction { fieldText: String -> + if (fieldText.isEmpty()) { + return@ConceptTextValidationFunction "Field must be set" + } + null +} + +val CHECK_NO_WHITESPACES = ConceptTextValidationFunction { fieldText: String -> + if (fieldText.contains(" ")) { + return@ConceptTextValidationFunction "Space symbols are not allowed here" + } + null +} + +val CHECK_SIMPLE_NAME_FORMAT: ConceptTextValidationFunction = object : ConceptTextValidationFunction { + private val myPattern = Pattern.compile("[a-zA-Z0-9-._ ]*") // IDEA-235441 + override fun checkText(fieldText: String): String? { + return if (!myPattern.matcher(fieldText).matches()) { + "Only Latin characters, digits, spaces, '-', '_' and '.' are allowed here" + } else null + } +} + +val CHECK_ARTIFACT_SIMPLE_FORMAT: ConceptTextValidationFunction = object : ConceptTextValidationFunction { + private val myUsedSymbolsCheckPattern = Pattern.compile("[a-zA-Z0-9-_]*") + private val myFirstSymbolCheckPattern = Pattern.compile("[a-zA-Z_].*") + override fun checkText(fieldText: String): String? { + if (!myUsedSymbolsCheckPattern.matcher(fieldText).matches()) { + return "Only Latin characters, digits, '-' and '_' are allowed here" + } + return if (!myFirstSymbolCheckPattern.matcher(fieldText).matches()) { + "Must start with Latin character or '_'" + } else null + } +} + +// IDEA-235887 prohibit using some words reserved by Windows in group and artifact fields +val CHECK_NO_RESERVED_WORDS: ConceptTextValidationFunction = object : ConceptTextValidationFunction { + private val myPattern = Pattern.compile( + "(^|[ .])(con|prn|aux|nul|com\\d|lpt\\d)($|[ .])", + Pattern.CASE_INSENSITIVE + ) + + override fun checkText(fieldText: String): String? { + return if (myPattern.matcher(fieldText).find()) { + "Parts 'con', 'prn', 'aux', 'nul', 'com0', ..., 'com9' and 'lpt0', ..., 'lpt9' are not allowed here" + } else null + } +} + +val CHECK_PACKAGE_NAME = ConceptTextValidationFunction { fieldText: String? -> + if (!PsiNameHelperImpl.getInstance().isQualifiedName(fieldText)) { + return@ConceptTextValidationFunction "'$fieldText' is not a valid package name" + } + null +} + +val CHECK_GROUP_FORMAT: ConceptTextValidationFunction = object : ConceptTextValidationFunction { + private val myPatternForEntireText = Pattern.compile("[a-zA-Z\\d_.-]*") + private val myPatternForOneWord = Pattern.compile("[a-zA-Z_].*") + override fun checkText(fieldText: String): String? { + if (!myPatternForEntireText.matcher(fieldText).matches()) { + return "Only Latin characters, digits, '_', '-' and '.' are allowed here" + } + val firstSymbol = fieldText[0] + val lastSymbol = fieldText[fieldText.length - 1] + if (firstSymbol == '.' || lastSymbol == '.') { + return "Must not start or end with '.'" + } + if (fieldText.contains("..")) { + return "Must not contain '..' sequences" + } + val wordsBetweenDots = fieldText.split("\\.".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + for (word in wordsBetweenDots) { + if (!myPatternForOneWord.matcher(word).matches()) { + return "Part '$word' is incorrect, it must start with Latin character or ''_''" + } + } + return null + } +} + +val CHECK_LOCATION_FOR_ERROR = ConceptTextValidationFunction { fieldText: String? -> + val locationPath: Path + locationPath = try { + Paths.get(FileUtil.expandUserHome(fieldText!!)) + } catch (e: InvalidPathException) { + return@ConceptTextValidationFunction "The specified path is incorrect" + } + val file = locationPath.toFile() + if (file.exists()) { + if (!file.canWrite()) { + return@ConceptTextValidationFunction "Directory is not writable" + } + val children = file.list() + ?: return@ConceptTextValidationFunction "The specified path is not a directory" + } + null +} + +val CHECK_LOCATION_FOR_WARNING = ConceptTextValidationFunction { fieldText: String? -> + try { + val file = + Paths.get(FileUtil.expandUserHome(fieldText!!)).toFile() + if (file.exists()) { + val children = file.list() + if (children != null && children.size > 0) { + return@ConceptTextValidationFunction "Directory is not empty" + } + } + } catch (ipe: InvalidPathException) { + return@ConceptTextValidationFunction null + } + null +} \ No newline at end of file diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterInitialStep.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterInitialStep.kt index 28c2f4594..97b8f4c68 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterInitialStep.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterInitialStep.kt @@ -1,7 +1,10 @@ package com.github.linyuzai.cloud.plugin.intellij +import com.intellij.CommonBundle import com.intellij.icons.AllIcons import com.intellij.ide.BrowserUtil +import com.intellij.ide.JavaUiBundle +import com.intellij.ide.starters.JavaStartersBundle import com.intellij.ide.starters.local.StarterModuleBuilder import com.intellij.ide.starters.shared.* import com.intellij.ide.starters.shared.ValidationFunctions.* @@ -15,21 +18,19 @@ import com.intellij.openapi.actionSystem.DefaultActionGroup import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.GraphPropertyImpl.Companion.graphProperty -import com.intellij.openapi.observable.properties.PropertyGraph -import com.intellij.openapi.observable.properties.map +import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.projectRoots.JavaSdk +import com.intellij.openapi.projectRoots.JavaSdkVersion import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel -import com.intellij.openapi.roots.ui.configuration.validateJavaVersion -import com.intellij.openapi.roots.ui.configuration.validateSdk import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.InputValidator import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.popup.IconButton import com.intellij.openapi.util.Condition import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.io.FileUtil import com.intellij.ui.InplaceButton @@ -44,8 +45,6 @@ import java.io.File import java.io.IOException import java.net.MalformedURLException import java.net.URL -import java.nio.file.InvalidPathException -import java.nio.file.Paths import java.util.concurrent.Future import javax.swing.DefaultComboBoxModel import javax.swing.JComponent @@ -61,28 +60,28 @@ open class ConceptWebStarterInitialStep(contextProvider: ConceptWebStarterContex private val validatedTextComponents: MutableList = mutableListOf() - protected val propertyGraph: PropertyGraph = PropertyGraph() - private val entityNameProperty: GraphProperty = propertyGraph.graphProperty(::suggestName) - private val locationProperty: GraphProperty = propertyGraph.graphProperty(::suggestLocationByName) - private val groupIdProperty: GraphProperty = propertyGraph.graphProperty { starterContext.group } - private val artifactIdProperty: GraphProperty = propertyGraph.graphProperty { entityName } - private val packageNameProperty: GraphProperty = propertyGraph.graphProperty { starterContext.packageName } - private val sdkProperty: GraphProperty = propertyGraph.graphProperty { null } - - private val projectTypeProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.projectType } - private val languageProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.language } - private val packagingProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.packaging } - private val testFrameworkProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.testFramework } - private val languageLevelProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.languageLevel } - private val applicationTypeProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.applicationType } - private val exampleCodeProperty: GraphProperty = - propertyGraph.graphProperty { starterContext.includeExamples } + protected val propertyGraph: ConceptPropertyGraph = ConceptPropertyGraph() + private val entityNameProperty: ConceptGraphProperty = propertyGraph.property(::suggestName) + private val locationProperty: ConceptGraphProperty = propertyGraph.property(::suggestLocationByName) + private val groupIdProperty: ConceptGraphProperty = propertyGraph.property { starterContext.group } + private val artifactIdProperty: ConceptGraphProperty = propertyGraph.property { entityName } + private val packageNameProperty: ConceptGraphProperty = propertyGraph.property { starterContext.packageName } + private val sdkProperty: ConceptGraphProperty = propertyGraph.property { null } + + private val projectTypeProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.projectType } + private val languageProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.language } + private val packagingProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.packaging } + private val testFrameworkProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.testFramework } + private val languageLevelProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.languageLevel } + private val applicationTypeProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.applicationType } + private val exampleCodeProperty: ConceptGraphProperty = + propertyGraph.property { starterContext.includeExamples } private var entityName: String by entityNameProperty.map { it.trim() } private var location: String by locationProperty @@ -330,22 +329,6 @@ open class ConceptWebStarterInitialStep(contextProvider: ConceptWebStarterContex }.withVisualPadding() } - val CHECK_LOCATION_FOR_WARNING = TextValidationFunction { fieldText: String? -> - try { - val file = - Paths.get(FileUtil.expandUserHome(fieldText!!)).toFile() - if (file.exists()) { - val children = file.list() - if (children != null && children.size > 0) { - return@TextValidationFunction "Directory is not empty" - } - } - } catch (ipe: InvalidPathException) { - return@TextValidationFunction null - } - null - } - private fun createServerUrlLink(): ActionLink { val result = ActionLink(urlPreview(starterContext.serverUrl)) { BrowserUtil.browse(starterContext.serverUrl) @@ -395,6 +378,66 @@ open class ConceptWebStarterInitialStep(contextProvider: ConceptWebStarterContex return checkServerOptionsLoaded() } + fun validateSdk(sdkProperty: ConceptGraphProperty, sdkModel: ProjectSdksModel): Boolean { + return validateAndGetSdkValidationMessage(sdkProperty, sdkModel) == null + } + + private fun validateAndGetSdkValidationMessage(sdkProperty: ConceptGraphProperty, sdkModel: ProjectSdksModel): @NlsContexts.DialogMessage String? { + if (sdkProperty.get() == null) { + if (Messages.showDialog( + JavaUiBundle.message("prompt.confirm.project.no.jdk"), + JavaUiBundle.message("title.no.jdk.specified"), + arrayOf(CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()), 1, + Messages.getWarningIcon()) != Messages.YES) { + return JavaUiBundle.message("title.no.jdk.specified") + } + } + + try { + sdkModel.apply(null, true) + } + catch (e: ConfigurationException) { + //IDEA-98382 We should allow Next step if user has wrong SDK + if (Messages.showDialog( + JavaUiBundle.message("dialog.message.0.do.you.want.to.proceed", e.message), + e.title, arrayOf(CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()), 1, + Messages.getWarningIcon()) != Messages.YES) { + return e.message ?: e.title + } + } + return null + } + + fun validateJavaVersion(sdkProperty: ConceptGraphProperty, javaVersion: String?, technologyName: String? = null): Boolean { + val sdk = sdkProperty.get() + if (sdk != null) { + val wizardVersion = JavaSdk.getInstance().getVersion(sdk) + if (wizardVersion != null && javaVersion != null) { + val selectedVersion = JavaSdkVersion.fromVersionString(javaVersion) + if (selectedVersion != null && !wizardVersion.isAtLeast(selectedVersion)) { + val message = if (technologyName == null) { + JavaStartersBundle.message("message.java.version.not.supported.by.sdk", + selectedVersion.description, + sdk.name) + } + else { + JavaStartersBundle.message("message.java.version.not.supported.by.sdk.for.technology", + technologyName, + selectedVersion.description, + sdk.name, + wizardVersion.description) + } + + Messages.showErrorDialog(message, JavaStartersBundle.message("message.title.error")) + + return false + } + } + } + + return true + } + private fun checkServerOptionsLoaded(): Boolean { val request = currentRequest if (serverOptions != null && request == null) { @@ -512,6 +555,7 @@ open class ConceptWebStarterInitialStep(contextProvider: ConceptWebStarterContex } private fun getDisposed(): Condition = Condition { Disposer.isDisposed(parentDisposable) } + //ComponentManager.isDisposed() private fun configureServer() { val currentServerUrl = starterContext.serverUrl @@ -650,13 +694,13 @@ open class ConceptWebStarterInitialStep(contextProvider: ConceptWebStarterContex } @Suppress("SameParameterValue") - private fun ConceptCellBuilder.withSpecialValidation(vararg errorValidations: TextValidationFunction): ConceptCellBuilder { + private fun ConceptCellBuilder.withSpecialValidation(vararg errorValidations: ConceptTextValidationFunction): ConceptCellBuilder { return this.withSpecialValidation(errorValidations.asList(), null) } private fun ConceptCellBuilder.withSpecialValidation( - errorValidations: List, - warningValidation: TextValidationFunction? + errorValidations: List, + warningValidation: ConceptTextValidationFunction? ): ConceptCellBuilder { return withValidation(this, errorValidations, warningValidation, validatedTextComponents, parentDisposable) } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterLibrariesStep.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterLibrariesStep.kt index aeca3f821..b0ce06868 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterLibrariesStep.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterLibrariesStep.kt @@ -1,22 +1,17 @@ package com.github.linyuzai.cloud.plugin.intellij -import com.intellij.ide.starters.shared.* import com.intellij.ide.util.projectWizard.ModuleWizardStep import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.ide.wizard.AbstractWizard import com.intellij.openapi.Disposable import com.intellij.openapi.application.ModalityState import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.observable.properties.GraphProperty -import com.intellij.openapi.observable.properties.GraphPropertyImpl.Companion.graphProperty -import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Condition import com.intellij.openapi.util.Disposer -import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.ui.* @@ -69,9 +64,9 @@ open class ConceptWebStarterLibrariesStep(contextProvider: ConceptWebStarterCont private val selectedLibrariesPanel: ConceptSelectedLibrariesPanel by lazy { createSelectedLibrariesPanel() } private val frameworkVersionsModel: DefaultComboBoxModel = DefaultComboBoxModel() - protected val propertyGraph: PropertyGraph = PropertyGraph() - private val frameworkVersionProperty: GraphProperty = - propertyGraph.graphProperty { null } + protected val propertyGraph: ConceptPropertyGraph = ConceptPropertyGraph() + private val frameworkVersionProperty: ConceptGraphProperty = + propertyGraph.property { null } private val selectedDependencies: MutableSet = mutableSetOf() private var currentSearchString: String = "" @@ -203,7 +198,7 @@ open class ConceptWebStarterLibrariesStep(contextProvider: ConceptWebStarterCont val log = logger() val url = moduleBuilder.getGeneratorUrlInternal(starterContext.serverUrl, starterContext).toExternalForm() - log.info("Loading project from ${url}") + log.info("Loading project from $url") return HttpRequests .request(url) @@ -241,7 +236,6 @@ open class ConceptWebStarterLibrariesStep(contextProvider: ConceptWebStarterCont }) } - @NlsSafe private fun getFilename(contentDisposition: String?): String { val filenameField = "filename=" if (StringUtil.isEmpty(contentDisposition)) return "unknown" diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterModuleBuilder.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterModuleBuilder.kt index 87fe49405..7a4958625 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterModuleBuilder.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterModuleBuilder.kt @@ -1,4 +1,4 @@ -package com.github.linyuzai.cloud.plugin.intellij; +package com.github.linyuzai.cloud.plugin.intellij import com.google.gson.JsonElement import com.google.gson.JsonNull @@ -66,7 +66,7 @@ abstract class ConceptWebStarterModuleBuilder : ModuleBuilder() { override fun getModuleType(): ModuleType<*> = StdModuleTypes.JAVA override fun getParentGroup(): String = JavaModuleType.BUILD_TOOLS_GROUP - override fun getWeight(): Int = 0/*JavaModuleBuilder.BUILD_SYSTEM_WEIGHT + 10*/ + override fun getWeight(): Int = 2000/*JavaModuleBuilder.BUILD_SYSTEM_WEIGHT + 10*/ open fun getHelpId(): String? = null // Required settings @@ -354,7 +354,13 @@ abstract class ConceptWebStarterModuleBuilder : ModuleBuilder() { NotificationType.INFORMATION ) .addAction(NotificationAction.create("Enable plugins...") { _, notification -> - installAndEnable(toInstallOrEnable) { notification.expire() } + installAndEnable( + null, toInstallOrEnable, + showDialog = true, + selectAlInDialog = false, + modalityState = null + ) { notification.expire() } + //installAndEnable(toInstallOrEnable) { notification.expire() } }) .notify(project) diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterSettings.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterSettings.kt index 1bbada872..60e2ba6d1 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterSettings.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/ConceptWebStarterSettings.kt @@ -6,7 +6,6 @@ import com.intellij.ide.starters.shared.* import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.openapi.Disposable import com.intellij.openapi.util.Key -import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.UserDataHolderBase import com.intellij.util.io.Decompressor import org.jetbrains.annotations.Nls @@ -42,7 +41,7 @@ val SERVER_PACKAGING_TYPES: Key> = Key.create(" open class ConceptWebStarterFrameworkVersion( val id: String, - @NlsSafe val title: String, + val title: String, val isDefault: Boolean ) { override fun toString(): String { @@ -51,7 +50,7 @@ open class ConceptWebStarterFrameworkVersion( } open class ConceptWebStarterDependencyCategory( - @NlsSafe + val title: String, val dependencies: List ) { @@ -64,7 +63,7 @@ open class ConceptWebStarterDependencyCategory( open class ConceptWebStarterDependency( val id: String, - @NlsSafe + override val title: String, override val description: String? = null, override val links: List = emptyList(), @@ -89,7 +88,7 @@ sealed class ConceptDependencyState class ConceptDependencyUnavailable( @Nls(capitalization = Nls.Capitalization.Sentence) val message: String?, - @NlsSafe + val hint: String? = null ) : ConceptDependencyState() @@ -118,7 +117,8 @@ fun unzipSubfolder(tempZipFile: File, contentEntryDir: File) { } if (rootFolders.size != 1) { throw ConceptUnexpectedArchiveStructureException( - "The archive should have 1 subdirectory, but has: " + rootFolders.joinToString(",")) + "The archive should have 1 subdirectory, but has: " + rootFolders.joinToString(",") + ) } rootFolderName = rootFolders.iterator().next() } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainComponents.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainComponents.kt index b1f1e4b29..d6151b489 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainComponents.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainComponents.kt @@ -5,7 +5,6 @@ import com.github.linyuzai.cloud.plugin.intellij.util.ConceptDialog import com.github.linyuzai.cloud.plugin.intellij.util.withClassValidation import com.github.linyuzai.cloud.plugin.intellij.util.withPackageValidation import com.github.linyuzai.cloud.plugin.intellij.util.withTextValidation -import com.intellij.ide.starters.shared.ValidationFunctions import com.intellij.openapi.project.Project import com.intellij.openapi.ui.* import com.intellij.ui.components.JBTextArea @@ -52,7 +51,7 @@ object DomainComponents { model.userClass ).withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -67,7 +66,7 @@ object DomainComponents { model.domainPackage ).withPackageValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -75,8 +74,8 @@ object DomainComponents { textField(model.domainObjectClassName) .withTextValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY, - ValidationFunctions.CHECK_SIMPLE_NAME_FORMAT, + CHECK_NOT_EMPTY, + CHECK_SIMPLE_NAME_FORMAT, ) } @@ -84,8 +83,8 @@ object DomainComponents { textField(model.domainCollectionClassName) .withTextValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY, - ValidationFunctions.CHECK_SIMPLE_NAME_FORMAT, + CHECK_NOT_EMPTY, + CHECK_SIMPLE_NAME_FORMAT, ) } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainModel.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainModel.kt index 8bcec9c42..97b66aae2 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainModel.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/domain/DomainModel.kt @@ -1,5 +1,8 @@ package com.github.linyuzai.cloud.plugin.intellij.domain +import com.github.linyuzai.cloud.plugin.intellij.ConceptGraphProperty +import com.github.linyuzai.cloud.plugin.intellij.ConceptGraphPropertyImpl +import com.github.linyuzai.cloud.plugin.intellij.ConceptPropertyGraph import com.github.linyuzai.cloud.plugin.intellij.builder.lowercaseFirst import com.github.linyuzai.cloud.plugin.intellij.builder.toSampleName import com.intellij.openapi.module.Module @@ -15,14 +18,14 @@ data class DomainModel( val initDomainPackage: String, val initDomainObjectClassName: String ) { - val propertyGraph: PropertyGraph = PropertyGraph() - val userClass: GraphProperty = property { initUserClass } - val domainModule: GraphProperty = property { initDomainModule } - val domainPackage: GraphProperty = property { initDomainPackage } - val domainObjectClassName: GraphProperty = property { initDomainObjectClassName } - val domainCollectionClassName: GraphProperty = property { "${initDomainObjectClassName}s" } - val domainClassComment: GraphProperty = property { "" } - val domainPreview: GraphProperty = property(false) { "" } + val propertyGraph: ConceptPropertyGraph = ConceptPropertyGraph() + val userClass: ConceptGraphProperty = property { initUserClass } + val domainModule: ConceptGraphProperty = property { initDomainModule } + val domainPackage: ConceptGraphProperty = property { initDomainPackage } + val domainObjectClassName: ConceptGraphProperty = property { initDomainObjectClassName } + val domainCollectionClassName: ConceptGraphProperty = property { "${initDomainObjectClassName}s" } + val domainClassComment: ConceptGraphProperty = property { "" } + val domainPreview: ConceptGraphProperty = property(false) { "" } val domainProps: MutableList = CopyOnWriteArrayList() @@ -147,17 +150,17 @@ data class DomainModel( data class DomainProp( val model: DomainModel, var index: Int, - var propClass: GraphProperty = model.property { "" }, - var propName: GraphProperty = model.property { "" }, - var propNotNull: GraphProperty = model.property { false }, - var propNotEmpty: GraphProperty = model.property { false }, - var propComment: GraphProperty = model.property { "" }, + var propClass: ConceptGraphProperty = model.property { "" }, + var propName: ConceptGraphProperty = model.property { "" }, + var propNotNull: ConceptGraphProperty = model.property { false }, + var propNotEmpty: ConceptGraphProperty = model.property { false }, + var propComment: ConceptGraphProperty = model.property { "" }, var smartFill: Boolean = true, var onClassNameUpdateListener: ((String) -> Unit)? = null ) -fun DomainModel.property(preview: Boolean = true, init: () -> T): GraphProperty { - return GraphPropertyImpl(this.propertyGraph, init).apply { +fun DomainModel.property(preview: Boolean = true, init: () -> T): ConceptGraphProperty { + return ConceptGraphPropertyImpl(this.propertyGraph, init).apply { if (preview) { afterChange { preview() diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleComponents.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleComponents.kt index aae9d9e79..9d7c4e503 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleComponents.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleComponents.kt @@ -1,11 +1,11 @@ package com.github.linyuzai.cloud.plugin.intellij.module +import com.github.linyuzai.cloud.plugin.intellij.CHECK_NOT_EMPTY import com.github.linyuzai.cloud.plugin.intellij.GenerateCodeAction import com.github.linyuzai.cloud.plugin.intellij.panel import com.github.linyuzai.cloud.plugin.intellij.util.ConceptDialog import com.github.linyuzai.cloud.plugin.intellij.util.withClassValidation import com.github.linyuzai.cloud.plugin.intellij.util.withPackageValidation -import com.intellij.ide.starters.shared.ValidationFunctions import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.event.DocumentListener import com.intellij.openapi.project.Project @@ -39,7 +39,7 @@ object ModuleComponents { model.userClass ).withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -62,7 +62,7 @@ object ModuleComponents { model.modulePackage ).withPackageValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -124,7 +124,7 @@ object ModuleComponents { }) }.withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -142,7 +142,7 @@ object ModuleComponents { }) }.withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -160,7 +160,7 @@ object ModuleComponents { }) }.withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } @@ -178,7 +178,7 @@ object ModuleComponents { }) }.withClassValidation( dialog, - ValidationFunctions.CHECK_NOT_EMPTY + CHECK_NOT_EMPTY ) } diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleModel.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleModel.kt index 0c7f01220..f757274e5 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleModel.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/module/ModuleModel.kt @@ -1,6 +1,9 @@ package com.github.linyuzai.cloud.plugin.intellij.module import com.github.linyuzai.cloud.plugin.intellij.ConceptCloudUtils +import com.github.linyuzai.cloud.plugin.intellij.ConceptGraphProperty +import com.github.linyuzai.cloud.plugin.intellij.ConceptGraphPropertyImpl +import com.github.linyuzai.cloud.plugin.intellij.ConceptPropertyGraph import com.github.linyuzai.cloud.plugin.intellij.builder.TYPE_DOMAIN_COLLECTION import com.github.linyuzai.cloud.plugin.intellij.builder.TYPE_DOMAIN_REPOSITORY import com.intellij.openapi.module.Module @@ -25,17 +28,17 @@ data class ModuleModel( val initDomainServiceClass: String, val initDomainDescription: String ) { - val propertyGraph: PropertyGraph = PropertyGraph() - val userClass: GraphProperty = property { initUserClass } - val loginAnnotationClass: GraphProperty = property { initLoginAnnotationClass } - val moduleModule: GraphProperty = property { initModuleModule } - val modulePackage: GraphProperty = property { initModulePackage } - val domainObjectClass: GraphProperty = property { initDomainObjectClass } - val domainCollectionClass: GraphProperty = property { initDomainCollectionClass } - val domainRepositoryClass: GraphProperty = property { initDomainRepositoryClass } - val domainServiceClass: GraphProperty = property { initDomainServiceClass } - val domainDescription: GraphProperty = property { initDomainDescription } - val myBatisPlus: GraphProperty = property { true } + val propertyGraph: ConceptPropertyGraph = ConceptPropertyGraph() + val userClass: ConceptGraphProperty = property { initUserClass } + val loginAnnotationClass: ConceptGraphProperty = property { initLoginAnnotationClass } + val moduleModule: ConceptGraphProperty = property { initModuleModule } + val modulePackage: ConceptGraphProperty = property { initModulePackage } + val domainObjectClass: ConceptGraphProperty = property { initDomainObjectClass } + val domainCollectionClass: ConceptGraphProperty = property { initDomainCollectionClass } + val domainRepositoryClass: ConceptGraphProperty = property { initDomainRepositoryClass } + val domainServiceClass: ConceptGraphProperty = property { initDomainServiceClass } + val domainDescription: ConceptGraphProperty = property { initDomainDescription } + val myBatisPlus: ConceptGraphProperty = property { true } var autoFindDomainCollectionClass = true var autoFindDomainRepositoryClass = true @@ -124,6 +127,6 @@ data class ModuleModel( } } -fun ModuleModel.property(init: () -> T): GraphProperty { - return GraphPropertyImpl(this.propertyGraph, init) +fun ModuleModel.property(init: () -> T): ConceptGraphProperty { + return ConceptGraphPropertyImpl(this.propertyGraph, init) } \ No newline at end of file diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/util/ConceptUtils.kt b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/util/ConceptUtils.kt index e3f7c4081..931cf0582 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/util/ConceptUtils.kt +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/kotlin/com/github/linyuzai/cloud/plugin/intellij/util/ConceptUtils.kt @@ -1,28 +1,28 @@ package com.github.linyuzai.cloud.plugin.intellij.util import com.github.linyuzai.cloud.plugin.intellij.ConceptCellBuilder +import com.github.linyuzai.cloud.plugin.intellij.ConceptTextValidationFunction import com.github.linyuzai.cloud.plugin.intellij.withValidationForEditorCombo import com.github.linyuzai.cloud.plugin.intellij.withValidationForText -import com.intellij.ide.starters.shared.TextValidationFunction import javax.swing.JComponent fun ConceptCellBuilder.withTextValidation( dialog: ConceptDialog, - vararg errorValidations: TextValidationFunction, + vararg errorValidations: ConceptTextValidationFunction, ): ConceptCellBuilder { return withValidationForText(this, errorValidations.toList(), null, dialog) } fun ConceptCellBuilder.withPackageValidation( dialog: ConceptDialog, - vararg errorValidations: TextValidationFunction, + vararg errorValidations: ConceptTextValidationFunction, ): ConceptCellBuilder { return withValidationForEditorCombo(this, errorValidations.toList(), null, dialog) } fun ConceptCellBuilder.withClassValidation( dialog: ConceptDialog, - vararg errorValidations: TextValidationFunction, + vararg errorValidations: ConceptTextValidationFunction, ): ConceptCellBuilder { return withValidationForEditorCombo(this, errorValidations.toList(), null, dialog) } \ No newline at end of file diff --git a/concept-cloud/concept-cloud-plugin-intellij/src/main/resources/META-INF/plugin.xml b/concept-cloud/concept-cloud-plugin-intellij/src/main/resources/META-INF/plugin.xml index 4a71cba3f..e483c530f 100644 --- a/concept-cloud/concept-cloud-plugin-intellij/src/main/resources/META-INF/plugin.xml +++ b/concept-cloud/concept-cloud-plugin-intellij/src/main/resources/META-INF/plugin.xml @@ -8,7 +8,7 @@ 2.0.0 - +