diff --git a/platform/lang-api/src/com/intellij/execution/RunnerAndConfigurationSettings.java b/platform/lang-api/src/com/intellij/execution/RunnerAndConfigurationSettings.java index e061788536a4..b7194e91a68f 100644 --- a/platform/lang-api/src/com/intellij/execution/RunnerAndConfigurationSettings.java +++ b/platform/lang-api/src/com/intellij/execution/RunnerAndConfigurationSettings.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.Nullable; * settings. * * @author anna - * @see RunManager#createConfiguration(String, com.intellij.execution.configurations.ConfigurationFactory) + * @see RunManager#createConfiguration(String, ConfigurationFactory) */ public interface RunnerAndConfigurationSettings { /** @@ -129,7 +129,7 @@ public interface RunnerAndConfigurationSettings { /** * @deprecated - * @see ExecutionTargetManager#canRun(com.intellij.execution.RunnerAndConfigurationSettings, com.intellij.execution.ExecutionTarget) + * @see ExecutionTargetManager#canRun(RunnerAndConfigurationSettings, ExecutionTarget) */ @Deprecated @SuppressWarnings({"DeprecatedIsStillUsed", "unused"}) @@ -172,16 +172,20 @@ public interface RunnerAndConfigurationSettings { boolean isActivateToolWindowBeforeRun(); /** - * Returns the "Single instance only" flag (meaning that only one instance of this run configuration can be run at the same time). + * @deprecated Use {@link RunConfiguration#isAllowRunningInParallel()} */ - boolean isSingleton(); + @Deprecated + default boolean isSingleton() { + return !getConfiguration().isAllowRunningInParallel(); + } /** - * Sets the "Single instance only" flag (meaning that only one instance of this run configuration can be run at the same time). - * - * @param singleton the "Single instance" flag. + * @deprecated Use {@link RunConfiguration#setAllowRunningInParallel(boolean)}} */ - void setSingleton(boolean singleton); + @Deprecated + default void setSingleton(boolean value) { + getConfiguration().setAllowRunningInParallel(!value); + } /** * Sets the name of the folder under which the configuration is displayed in the "Run/Debug Configurations" dialog. diff --git a/platform/lang-api/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java b/platform/lang-api/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java index 3463a103599c..934849bce3f0 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java @@ -134,6 +134,14 @@ public abstract class ModuleBasedConfiguration> value) { } + + default boolean isAllowRunningInParallel() { + return false; + } + + default void setAllowRunningInParallel(boolean value) { + } } diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java index 90ce82999d90..ec74ac515acc 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java @@ -137,6 +137,7 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements runConfiguration.myOptions = createOptions(); runConfiguration.myOptions.copyFrom(myOptions); + runConfiguration.myOptions.resetModificationCount(); copyCopyableDataTo(runConfiguration); myBeforeRunTasks = myBeforeRunTasks.isEmpty() ? Collections.emptyList() : new SmartList<>(myBeforeRunTasks); @@ -324,6 +325,16 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements return false; } + @Override + public final boolean isAllowRunningInParallel() { + return getOptions().isAllowRunningInParallel(); + } + + @Override + public final void setAllowRunningInParallel(boolean value) { + getOptions().setAllowRunningInParallel(value); + } + /** * Called when configuration created via UI (Add Configuration). * Suitable to perform some initialization tasks (in most cases it is indicator that you do something wrong, so, please override this method with care and only if really need). diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationOptions.kt b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationOptions.kt index 6b2f9fed8e91..33e56b88dbfd 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationOptions.kt +++ b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationOptions.kt @@ -25,6 +25,10 @@ open class RunConfigurationOptions : BaseState() { @get:Property(surroundWithTag = false) @get:XCollection var logFiles by list() + + @com.intellij.configurationStore.Property(description = "Allow running in parallel") + @get:Transient + var isAllowRunningInParallel by property(false) } open class LocatableRunConfigurationOptions : RunConfigurationOptions() { diff --git a/platform/lang-api/src/com/intellij/execution/configurations/runConfigurationType.kt b/platform/lang-api/src/com/intellij/execution/configurations/runConfigurationType.kt index 0e6013cffc34..a2c7a0e2b30b 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/runConfigurationType.kt +++ b/platform/lang-api/src/com/intellij/execution/configurations/runConfigurationType.kt @@ -18,8 +18,8 @@ enum class RunConfigurationSingletonPolicy { val isPolicyConfigurable: Boolean get() = this != SINGLE_INSTANCE_ONLY && this != MULTIPLE_INSTANCE_ONLY - val isSingleton: Boolean - get() = this == SINGLE_INSTANCE || this == SINGLE_INSTANCE_ONLY + val isAllowRunningInParallel: Boolean + get() = this == MULTIPLE_INSTANCE || this == MULTIPLE_INSTANCE_ONLY } inline fun runConfigurationType(): T = ConfigurationTypeUtil.findConfigurationType(T::class.java) diff --git a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java index a22f50de2736..db77abc182e0 100644 --- a/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/ExecutorRegistryImpl.java @@ -286,7 +286,7 @@ public class ExecutorRegistryImpl extends ExecutorRegistry implements Disposable return contentDescriptor != null && executionManager.getExecutors(contentDescriptor).contains(myExecutor); }); - if (!runningDescriptors.isEmpty() && DefaultRunExecutor.EXECUTOR_ID.equals(myExecutor.getId()) && selectedConfiguration.isSingleton()) { + if (!configuration.isAllowRunningInParallel() && !runningDescriptors.isEmpty() && DefaultRunExecutor.EXECUTOR_ID.equals(myExecutor.getId())) { return AllIcons.Actions.Restart; } if (runningDescriptors.isEmpty()) { diff --git a/platform/lang-impl/src/com/intellij/execution/compound/CompoundRunConfiguration.kt b/platform/lang-impl/src/com/intellij/execution/compound/CompoundRunConfiguration.kt index ea5b1c5e724b..6674d53d393f 100644 --- a/platform/lang-impl/src/com/intellij/execution/compound/CompoundRunConfiguration.kt +++ b/platform/lang-impl/src/com/intellij/execution/compound/CompoundRunConfiguration.kt @@ -194,7 +194,7 @@ class CompoundRunConfiguration @JvmOverloads constructor(project: Project, name: } val settings = manager.findConfigurationByTypeAndName(configuration.type, configuration.name) - if (settings != null && settings.isSingleton && configuration == s.configuration) { + if (settings != null && !settings.configuration.isAllowRunningInParallel && configuration == s.configuration) { return@getRunningDescriptors true } } diff --git a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java index 82084a807382..a95f8d9f6ca2 100644 --- a/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java +++ b/platform/lang-impl/src/com/intellij/execution/dashboard/actions/ExecutorAction.java @@ -1,25 +1,11 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.execution.dashboard.actions; import com.intellij.execution.*; import com.intellij.execution.configurations.RuntimeConfigurationError; import com.intellij.execution.configurations.RuntimeConfigurationException; -import com.intellij.execution.dashboard.RunDashboardRunConfigurationNode; import com.intellij.execution.dashboard.RunDashboardManager; +import com.intellij.execution.dashboard.RunDashboardRunConfigurationNode; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.RunContentDescriptor; @@ -81,9 +67,13 @@ public abstract class ExecutorAction extends RunDashboardTreeLeafAction runningOfTheSameType = new SmartList<>(); - if (configuration != null && configuration.isSingleton()) { + if (configuration != null && !configuration.getConfiguration().isAllowRunningInParallel()) { runningOfTheSameType.addAll(getRunningDescriptorsOfTheSameConfigType(configuration)); } else if (isProcessRunning(contentToReuse)) { diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt index b2a029795919..185ddae0292d 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.kt @@ -292,9 +292,8 @@ open class RunManagerImpl @JvmOverloads constructor(val project: Project, shared internal fun createTemplateSettings(factory: ConfigurationFactory): RunnerAndConfigurationSettingsImpl { val configuration = factory.createTemplateConfiguration(project, this) - val template = RunnerAndConfigurationSettingsImpl(this, configuration, - isTemplate = true, - isSingleton = factory.singletonPolicy.isSingleton) + configuration.isAllowRunningInParallel = factory.singletonPolicy.isAllowRunningInParallel + val template = RunnerAndConfigurationSettingsImpl(this, configuration, isTemplate = true) if (configuration is UnknownRunConfiguration) { configuration.isDoNotStore = true } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt b/platform/lang-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt index 664ab9ec48a9..da8558fe820e 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt @@ -51,7 +51,6 @@ enum class RunConfigurationLevel { class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: RunManagerImpl, private var _configuration: RunConfiguration? = null, private var isTemplate: Boolean = false, - private var isSingleton: Boolean = true, var level: RunConfigurationLevel = RunConfigurationLevel.WORKSPACE) : Cloneable, RunnerAndConfigurationSettings, Comparable, SerializableScheme { companion object { @JvmStatic @@ -141,25 +140,19 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: isEditBeforeRun = value } - override fun isEditBeforeRun(): Boolean = isEditBeforeRun + override fun isEditBeforeRun() = isEditBeforeRun override fun setActivateToolWindowBeforeRun(value: Boolean) { isActivateToolWindowBeforeRun = value } - override fun isActivateToolWindowBeforeRun(): Boolean = isActivateToolWindowBeforeRun - - override fun setSingleton(value: Boolean) { - isSingleton = value - } - - override fun isSingleton(): Boolean = isSingleton + override fun isActivateToolWindowBeforeRun() = isActivateToolWindowBeforeRun override fun setFolderName(value: String?) { folderName = value } - override fun getFolderName(): String? = folderName + override fun getFolderName() = folderName fun readExternal(element: Element, isShared: Boolean) { isTemplate = element.getAttributeBooleanValue(TEMPLATE_FLAG_ATTRIBUTE) @@ -178,21 +171,6 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: folderName = element.getAttributeValue(FOLDER_NAME) val factory = manager.getFactory(element.getAttributeValue(CONFIGURATION_TYPE_ATTRIBUTE), element.getAttributeValue(FACTORY_NAME_ATTRIBUTE), !isTemplate) ?: return - wasSingletonSpecifiedExplicitly = false - if (isTemplate) { - isSingleton = factory.singletonPolicy.isSingleton - } - else { - val singletonStr = element.getAttributeValue(SINGLETON) - if (singletonStr.isNullOrEmpty()) { - isSingleton = factory.singletonPolicy.isSingleton - } - else { - wasSingletonSpecifiedExplicitly = true - isSingleton = singletonStr!!.toBoolean() - } - } - val configuration = factory.createTemplateConfiguration(manager.project, manager) if (!isTemplate) { // shouldn't call createConfiguration since it calls StepBeforeRunProviders that @@ -200,6 +178,21 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: configuration.name = element.getAttributeValue(NAME_ATTR) ?: return } + wasSingletonSpecifiedExplicitly = false + if (isTemplate) { + configuration.isAllowRunningInParallel = factory.singletonPolicy.isAllowRunningInParallel + } + else { + val singletonStr = element.getAttributeValue(SINGLETON) + if (singletonStr.isNullOrEmpty()) { + configuration.isAllowRunningInParallel = factory.singletonPolicy.isAllowRunningInParallel + } + else { + wasSingletonSpecifiedExplicitly = true + configuration.isAllowRunningInParallel = !singletonStr!!.toBoolean() + } + } + _configuration = configuration uniqueId = null @@ -259,8 +252,8 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: if (!isActivateToolWindowBeforeRun) { element.setAttribute(ACTIVATE_TOOLWINDOW_BEFORE_RUN, "false") } - if (wasSingletonSpecifiedExplicitly || isSingleton != factory.singletonPolicy.isSingleton) { - element.setAttribute(SINGLETON, isSingleton.toString()) + if (wasSingletonSpecifiedExplicitly || configuration.isAllowRunningInParallel != factory.singletonPolicy.isAllowRunningInParallel) { + element.setAttribute(SINGLETON, (!configuration.isAllowRunningInParallel).toString()) } if (isTemporary) { element.setAttribute(TEMPORARY_ATTRIBUTE, "true") @@ -352,7 +345,6 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(val manager: importFromTemplate(template.runnerSettings, runnerSettings) importFromTemplate(template.configurationPerRunnerSettings, configurationPerRunnerSettings) - isSingleton = template.isSingleton isEditBeforeRun = template.isEditBeforeRun isActivateToolWindowBeforeRun = template.isActivateToolWindowBeforeRun level = template.level diff --git a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form index 67427f3ccc2d..e6604fe45aa5 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form +++ b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.form @@ -3,7 +3,7 @@ - + @@ -95,12 +95,12 @@ - + - + diff --git a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.java b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.java index 948b9872855d..5bc3d693b280 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/SingleConfigurationConfigurable.java @@ -48,7 +48,7 @@ public final class SingleConfigurationConfigurable, node: MappingNode, factory: ConfigurationFactory) { - processor(factory, readObject(optionsClass, node)) + val instance = ReflectionUtil.newInstance(optionsClass) + if (instance is RunConfigurationOptions) { + // very important - set BEFORE read to ensure that user can set any value for isAllowRunningInParallel and it will be not overridden by us later + instance.isAllowRunningInParallel = factory.singletonPolicy.isAllowRunningInParallel + } + processor(factory, readObject(instance, node)) } } \ No newline at end of file diff --git a/plugins/configuration-script/src/com/intellij/configurationScript/beanConstructor.kt b/plugins/configuration-script/src/com/intellij/configurationScript/beanConstructor.kt index 475f3fdf1f0e..bb7e5f18f8f8 100644 --- a/plugins/configuration-script/src/com/intellij/configurationScript/beanConstructor.kt +++ b/plugins/configuration-script/src/com/intellij/configurationScript/beanConstructor.kt @@ -4,14 +4,12 @@ import com.intellij.configurationStore.properties.CollectionStoredProperty import com.intellij.configurationStore.properties.MapStoredProperty import com.intellij.openapi.components.BaseState import com.intellij.openapi.components.ScalarProperty -import com.intellij.util.ReflectionUtil import org.yaml.snakeyaml.nodes.MappingNode import org.yaml.snakeyaml.nodes.ScalarNode import org.yaml.snakeyaml.nodes.SequenceNode -internal fun readObject(optionsClass: Class, node: MappingNode): BaseState { - val state = ReflectionUtil.newInstance(optionsClass) - val properties = state.__getProperties() +internal fun readObject(instance: BaseState, node: MappingNode): BaseState { + val properties = instance.__getProperties() for (tuple in node.value) { val valueNode = tuple.valueNode val key = (tuple.keyNode as ScalarNode).value @@ -40,7 +38,7 @@ internal fun readObject(optionsClass: Class, node: MappingNode): } } } - return state + return instance } private fun readCollection(property: CollectionStoredProperty<*, *>, valueNode: SequenceNode) { diff --git a/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurationTemplateProvider.kt b/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurationTemplateProvider.kt index acf10f7255b1..f7b2dc2c0814 100644 --- a/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurationTemplateProvider.kt +++ b/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurationTemplateProvider.kt @@ -40,8 +40,9 @@ private class MyRunConfigurationTemplateProvider(private val project: Project) : } val configuration = factory.createTemplateConfiguration(runManager.project, runManager) + // see readRunConfiguration about how do we set isAllowRunningInParallel (configuration as RunConfigurationBase).setState(item.state as BaseState) - settings = RunnerAndConfigurationSettingsImpl(runManager, configuration, isTemplate = true, isSingleton = factory.singletonPolicy.isSingleton) + settings = RunnerAndConfigurationSettingsImpl(runManager, configuration, isTemplate = true) item.state = null item.settings = settings return settings diff --git a/plugins/configuration-script/src/com/intellij/configurationScript/updateSettingsProvider.kt b/plugins/configuration-script/src/com/intellij/configurationScript/updateSettingsProvider.kt index da9d6bbcda6e..ad9af246092a 100644 --- a/plugins/configuration-script/src/com/intellij/configurationScript/updateSettingsProvider.kt +++ b/plugins/configuration-script/src/com/intellij/configurationScript/updateSettingsProvider.kt @@ -33,7 +33,7 @@ internal fun readPluginsConfiguration(rootNode: MappingNode): PluginsConfigurati val keyNode = tuple.keyNode if (keyNode is ScalarNode && keyNode.value == Keys.plugins) { val valueNode = tuple.valueNode as? MappingNode ?: continue - return readObject(PluginsConfiguration::class.java, valueNode) as PluginsConfiguration + return readObject(PluginsConfiguration(), valueNode) as PluginsConfiguration } } return null diff --git a/plugins/configuration-script/test/ConfigurationSchemaTest.kt b/plugins/configuration-script/test/ConfigurationSchemaTest.kt index 218656c61c59..7fd5f51aab19 100644 --- a/plugins/configuration-script/test/ConfigurationSchemaTest.kt +++ b/plugins/configuration-script/test/ConfigurationSchemaTest.kt @@ -12,7 +12,7 @@ import com.jetbrains.jsonSchema.impl.JsonSchemaReader import org.intellij.lang.annotations.Language import java.nio.charset.StandardCharsets -class IntelliJConfigurationSchemaTest : CompletionTestCase() { +internal class IntelliJConfigurationSchemaTest : CompletionTestCase() { companion object { private val schemaFile by lazy { LightVirtualFile("scheme.json", JsonFileType.INSTANCE, generateConfigurationSchema(), StandardCharsets.UTF_8, 0) @@ -26,10 +26,15 @@ class IntelliJConfigurationSchemaTest : CompletionTestCase() { """.trimIndent()) - val variant = variants.first { it.lookupString == "env" } + checkDescription(variants, "env", "Environment variables") + checkDescription(variants, "isAllowRunningInParallel", "Allow running in parallel") + } + + private fun checkDescription(variants: List, name: String, expectedDescription: String) { + val variant = variants.first { it.lookupString == name } val presentation = LookupElementPresentation() variant.renderElement(presentation) - assertThat(presentation.typeText).isEqualTo("Environment variables") + assertThat(presentation.typeText).isEqualTo(expectedDescription) } private fun test(@Language("YAML") text: String): List {