From a1283d883d0d08155d337a009060fc2d264bab52 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Fri, 22 Mar 2024 04:03:43 +0400 Subject: [PATCH] extract ExecutorRegistryImpl inner actions GitOrigin-RevId: f4bf959f622326d06d9c05118d0eaa0b57e098e3 --- .../execution/ExecutorRegistryImpl.java | 644 +----------------- ...ConfigAndRunCurrentFileExecutorAction.java | 55 ++ .../execution/actions/ExecutorAction.java | 484 +++++++++++++ .../actions/ExecutorGroupActionGroup.java | 57 ++ .../RunConfigurationsComboBoxAction.java | 8 +- .../actions/RunCurrentFileExecutorAction.java | 39 ++ .../RunSpecifiedConfigExecutorAction.java | 72 ++ .../impl/EditConfigurationsDialog.java | 7 +- .../lineMarker/LineMarkerActionWrapper.java | 6 +- .../runToolbar/RunToolbarAdditionAction.kt | 11 +- .../RunToolbarChooserAdditionGroup.kt | 7 +- .../RunToolbarExecutorGroupAction.kt | 7 +- .../runToolbar/RunToolbarProcessAction.kt | 9 +- .../intellij/execution/ui/RunToolbarPopup.kt | 31 +- 14 files changed, 768 insertions(+), 669 deletions(-) create mode 100644 platform/execution-impl/src/com/intellij/execution/actions/EditRunConfigAndRunCurrentFileExecutorAction.java create mode 100644 platform/execution-impl/src/com/intellij/execution/actions/ExecutorAction.java create mode 100644 platform/execution-impl/src/com/intellij/execution/actions/ExecutorGroupActionGroup.java create mode 100644 platform/execution-impl/src/com/intellij/execution/actions/RunCurrentFileExecutorAction.java create mode 100644 platform/execution-impl/src/com/intellij/execution/actions/RunSpecifiedConfigExecutorAction.java diff --git a/platform/execution-impl/src/com/intellij/execution/ExecutorRegistryImpl.java b/platform/execution-impl/src/com/intellij/execution/ExecutorRegistryImpl.java index c911682021ce..fa2fddb4a90b 100644 --- a/platform/execution-impl/src/com/intellij/execution/ExecutorRegistryImpl.java +++ b/platform/execution-impl/src/com/intellij/execution/ExecutorRegistryImpl.java @@ -1,74 +1,39 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.execution; -import com.intellij.execution.actions.*; +import com.intellij.execution.actions.ExecutorGroupActionGroup; +import com.intellij.execution.actions.RunContextAction; +import com.intellij.execution.actions.RunNewConfigurationContextAction; import com.intellij.execution.compound.CompoundRunConfiguration; import com.intellij.execution.compound.SettingsAndEffectiveTarget; -import com.intellij.execution.configurations.LocatableConfiguration; import com.intellij.execution.configurations.RunConfiguration; -import com.intellij.execution.executors.DefaultRunExecutor; import com.intellij.execution.executors.ExecutorGroup; -import com.intellij.execution.impl.ExecutionManagerImpl; -import com.intellij.execution.impl.ExecutionManagerImplKt; -import com.intellij.execution.impl.RunDialog; -import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; import com.intellij.execution.runToolbar.*; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironmentBuilder; -import com.intellij.execution.runners.ExecutionUtil; import com.intellij.execution.runners.ProgramRunner; -import com.intellij.execution.ui.*; -import com.intellij.icons.AllIcons; -import com.intellij.ide.actions.NonEmptyActionGroup; +import com.intellij.execution.ui.RunConfigurationStartHistory; import com.intellij.ide.ui.ToolbarSettings; -import com.intellij.internal.statistic.collectors.fus.actions.persistence.ActionIdProvider; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.ActionRuntimeRegistrar; import com.intellij.openapi.actionSystem.impl.ActionConfigurationCustomizer; -import com.intellij.openapi.actionSystem.remoting.ActionRemotePermissionRequirements; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.ExtensionPointListener; import com.intellij.openapi.extensions.PluginDescriptor; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.ui.popup.IPopupChooserBuilder; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowId; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.ui.ExperimentalUI; -import com.intellij.ui.SpinningProgressIcon; -import com.intellij.util.ArrayUtil; -import com.intellij.util.IconUtil; import com.intellij.util.JavaCoroutines; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.text.UniqueNameGenerator; -import com.intellij.util.ui.JBUI; import kotlin.Unit; import kotlin.coroutines.Continuation; -import org.jetbrains.annotations.*; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; -import javax.swing.*; -import java.awt.*; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.util.List; import java.util.*; import java.util.function.Consumer; -import java.util.function.Function; - -import static java.util.Collections.emptyList; public final class ExecutorRegistryImpl extends ExecutorRegistry { private static final Logger LOG = Logger.getInstance(ExecutorRegistryImpl.class); @@ -78,8 +43,6 @@ public final class ExecutorRegistryImpl extends ExecutorRegistry { public static final String RUN_CONTEXT_GROUP_MORE = "RunContextGroupMore"; public static final String RUN_CONTEXT_EXECUTORS_GROUP = "RunContextExecutorsGroup"; - private static final Key spinningIconKey = Key.create("spinning-icon-key"); - private final Set contextActionIdSet = new HashSet<>(); private final Map idToAction = new HashMap<>(); private final Map contextActionIdToAction = new HashMap<>(); @@ -194,8 +157,8 @@ public final class ExecutorRegistryImpl extends ExecutorRegistry { } else { if (!process.isTemporaryProcess() && process.getShowInBar()) { - ExecutorAction wrappedAction = new RunToolbarProcessAction(process, executor); - ExecutorAction wrappedMainAction = new RunToolbarProcessMainAction(process, executor); + AnAction wrappedAction = new RunToolbarProcessAction(process, executor); + AnAction wrappedMainAction = new RunToolbarProcessMainAction(process, executor); registerActionInGroup(actionRegistrar, process.getActionId(), wrappedAction, RunToolbarProcess.RUN_WIDGET_GROUP, runWidgetIdToAction); @@ -318,562 +281,11 @@ public final class ExecutorRegistryImpl extends ExecutorRegistry { }); } - public static class ExecutorAction extends AnAction implements DumbAware, - ActionRemotePermissionRequirements.RunAccess, - ActionIdProvider { - private static final Key CURRENT_FILE_RUN_CONFIGS_KEY = Key.create("CURRENT_FILE_RUN_CONFIGS"); - - protected final Executor myExecutor; - - protected ExecutorAction(@NotNull Executor executor) { - super(executor::getStartActionText, executor::getDescription, executor::getIcon); - - myExecutor = executor; - } - - @Override - public @NotNull ActionUpdateThread getActionUpdateThread() { - return ActionUpdateThread.BGT; - } - - @Override - public void update(@NotNull AnActionEvent e) { - Presentation presentation = e.getPresentation(); - presentation.putClientProperty(ExecutorActionStatus.KEY, null); // by default - Project project = e.getProject(); - if (project == null || !project.isInitialized() || project.isDisposed()) { - presentation.setEnabled(false); - return; - } - - RunManager runManager = RunManager.getInstanceIfCreated(project); - if (runManager == null) { - presentation.setEnabled(false); - return; - } - - RunnerAndConfigurationSettings selectedSettings = getSelectedConfiguration(e); - boolean enabled = false; - ExecutorActionStatus actionStatus = null; - boolean runConfigAsksToHideDisabledExecutorButtons = false; - String text; - if (selectedSettings != null) { - if (DumbService.isDumb(project) && !selectedSettings.getType().isDumbAware()) { - presentation.setEnabled(false); - return; - } - - actionStatus = setupActionStatus(e, project, selectedSettings, presentation); - presentation.setIcon(getInformativeIcon(project, selectedSettings, e)); - RunConfiguration configuration = selectedSettings.getConfiguration(); - if (!isSuppressed(project)) { - enabled = RunnerHelper.canRun(project, myExecutor, configuration); - } - if (!(configuration instanceof CompoundRunConfiguration)) { - runConfigAsksToHideDisabledExecutorButtons = configuration.hideDisabledExecutorButtons(); - } - if (enabled) { - presentation.setDescription(myExecutor.getDescription()); - } - - if (ExperimentalUI.isNewUI() && - needRerunPresentation(selectedSettings.getConfiguration(), getRunningDescriptors(project, selectedSettings))) { - if (myExecutor.getId().equals(DefaultRunExecutor.EXECUTOR_ID)) { - text = ExecutionBundle.message("run.toolbar.widget.rerun.text", configuration); - } - else { - text = ExecutionBundle.message("run.toolbar.widget.restart.text", myExecutor.getActionName(), configuration.getName()); - } - } - else { - text = myExecutor.getStartActionText(configuration.getName()); - } - } - else { - if (RunConfigurationsComboBoxAction.hasRunCurrentFileItem(project)) { - // don't compute current file to run if editors are not yet loaded - if (!project.isDefault() && !StartupManager.getInstance(project).postStartupActivityPassed()) { - presentation.setEnabled(false); - return; - } - - RunCurrentFileActionStatus status = getRunCurrentFileActionStatus(e, false); - for (RunnerAndConfigurationSettings config : status.myRunConfigs) { - actionStatus = setupActionStatus(e, project, config, presentation); - if (actionStatus != ExecutorActionStatus.NORMAL) { - break; - } - } - enabled = status.myEnabled; - text = status.myTooltip; - presentation.setIcon(status.myIcon); - } - else { - text = getTemplatePresentation().getTextWithMnemonic(); - } - } - - if (actionStatus != ExecutorActionStatus.LOADING && (runConfigAsksToHideDisabledExecutorButtons || hideDisabledExecutorButtons())) { - presentation.setEnabledAndVisible(enabled); - } - else { - presentation.setEnabled(enabled); - } - - if (presentation.isVisible()) { - presentation.setVisible(myExecutor.isApplicable(project)); - } - presentation.putClientProperty(ExecutorActionStatus.KEY, actionStatus); - presentation.setText(text); - } - - private ExecutorActionStatus setupActionStatus(@NotNull AnActionEvent e, - Project project, - RunnerAndConfigurationSettings selectedSettings, - Presentation presentation) { - // We can consider to add spinning to the inlined run actions. But there is a problem with redrawing - ExecutorActionStatus status = ExecutorActionStatus.NORMAL; - if (ActionPlaces.NEW_UI_RUN_TOOLBAR.equals(e.getPlace())) { - RunStatusHistory startHistory = RunStatusHistory.getInstance(project); - - boolean isLoading = startHistory.firstOrNull(selectedSettings, it -> (it.getExecutorId().equals(myExecutor.getId()) && - it.getState() == RunState.SCHEDULED)) != null; - if (isLoading) { - status = ExecutorActionStatus.LOADING; - SpinningProgressIcon spinningIcon = presentation.getClientProperty(spinningIconKey); - if (spinningIcon == null) { - spinningIcon = new SpinningProgressIcon(); - spinningIcon.setIconColor(JBUI.CurrentTheme.RunWidget.ICON_COLOR); - presentation.putClientProperty(spinningIconKey, spinningIcon); - } - presentation.setDisabledIcon(spinningIcon); - } else { - if (!getRunningDescriptors(project, selectedSettings).isEmpty()) { - status = ExecutorActionStatus.RUNNING; - } - presentation.putClientProperty(spinningIconKey, null); - presentation.setDisabledIcon(null); - } - } - return status; - } - - protected boolean hideDisabledExecutorButtons() { - return false; - } - - private @NotNull RunCurrentFileActionStatus getRunCurrentFileActionStatus(@NotNull AnActionEvent e, boolean resetCache) { - Project project = Objects.requireNonNull(e.getProject()); - - VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); - if (files.length == 1) { - // There's only one visible editor, let's use the file from this editor, even if the editor is not in focus. - PsiFile psiFile = PsiManager.getInstance(project).findFile(files[0]); - if (psiFile == null) { - String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.not.runnable"); - return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); - } - - return getRunCurrentFileActionStatus(psiFile, resetCache, e); - } - - Editor editor = e.getData(CommonDataKeys.EDITOR); - if (editor == null) { - String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.no.focused.editor"); - return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); - } - - PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); - VirtualFile vFile = psiFile != null ? psiFile.getVirtualFile() : null; - if (psiFile == null || vFile == null || !ArrayUtil.contains(vFile, files)) { - // This is probably a special editor, like Python Console, which we don't want to use for the 'Run Current File' feature. - String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.no.focused.editor"); - return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); - } - - return getRunCurrentFileActionStatus(psiFile, resetCache, e); - } - - private @NotNull RunCurrentFileActionStatus getRunCurrentFileActionStatus(@NotNull PsiFile psiFile, boolean resetCache, - @NotNull AnActionEvent e) { - List runConfigs = getRunConfigsForCurrentFile(psiFile, resetCache); - if (runConfigs.isEmpty()) { - String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.not.runnable"); - return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); - } - - List runnableConfigs = filterConfigsThatHaveRunner(runConfigs); - if (runnableConfigs.isEmpty()) { - return RunCurrentFileActionStatus.createDisabled(myExecutor.getStartActionText(psiFile.getName()), myExecutor.getIcon()); - } - - Icon icon = myExecutor.getIcon(); - if (runnableConfigs.size() == 1) { - icon = getInformativeIcon(psiFile.getProject(), runnableConfigs.get(0), e); - } - else { - // myExecutor.getIcon() is the least preferred icon - // AllIcons.Actions.Restart is more preferred - // Other icons are the most preferred ones (like ExecutionUtil.getLiveIndicator()) - for (RunnerAndConfigurationSettings config : runnableConfigs) { - Icon anotherIcon = getInformativeIcon(psiFile.getProject(), config, e); - if (icon == myExecutor.getIcon() || (anotherIcon != myExecutor.getIcon() && anotherIcon != AllIcons.Actions.Restart)) { - icon = anotherIcon; - } - } - } - - return RunCurrentFileActionStatus.createEnabled(myExecutor.getStartActionText(psiFile.getName()), icon, runnableConfigs); - } - - @Override - public String getId() { - return myExecutor.getId(); - } - - @ApiStatus.Internal - public static List getRunConfigsForCurrentFile(@NotNull PsiFile psiFile, boolean resetCache) { - if (resetCache) { - psiFile.putUserData(CURRENT_FILE_RUN_CONFIGS_KEY, null); - } - - // Without this cache, an expensive method `ConfigurationContext.getConfigurationsFromContext()` is called too often for 2 reasons: - // - there are several buttons on the toolbar (Run, Debug, Profile, etc.), each runs ExecutorAction.update() during each action update session - // - the state of the buttons on the toolbar is updated several times a second, even if no files are being edited - - // The following few lines do pretty much the same as CachedValuesManager.getCachedValue(), but it's implemented without calling that - // method because it appeared to be too hard to satisfy both IdempotenceChecker.checkEquivalence() and CachedValueStabilityChecker.checkProvidersEquivalent(). - // The reason is that RunnerAndConfigurationSettings class doesn't implement equals(), and that CachedValueProvider would need to capture - // ConfigurationContext, which doesn't implement equals() either. - // Effectively, we need only one boolean value: whether the action is enabled or not, so it shouldn't be a problem that - // RunnerAndConfigurationSettings and ConfigurationContext don't implement equals() and this code doesn't pass CachedValuesManager checks. - - long psiModCount = PsiModificationTracker.getInstance(psiFile.getProject()).getModificationCount(); - RunCurrentFileInfo cache = psiFile.getUserData(CURRENT_FILE_RUN_CONFIGS_KEY); - - if (cache == null || cache.myPsiModCount != psiModCount) { - // The 'Run current file' feature doesn't depend on the caret position in the file, that's why ConfigurationContext is created like this. - ConfigurationContext configurationContext = new ConfigurationContext(psiFile); - - // The 'Run current file' feature doesn't reuse existing run configurations (by design). - List configurationsFromContext = configurationContext.createConfigurationsFromContext(); - - List runConfigs = configurationsFromContext == null - ? List.of() - : ContainerUtil.map(configurationsFromContext, - ConfigurationFromContext::getConfigurationSettings); - - VirtualFile vFile = psiFile.getVirtualFile(); - if (!runConfigs.isEmpty()) { - String filePath = vFile == null ? null : vFile.getPath(); - for (RunnerAndConfigurationSettings config : runConfigs) { - ((RunnerAndConfigurationSettingsImpl)config).setFilePathIfRunningCurrentFile(filePath); - } - } - - cache = new RunCurrentFileInfo(psiModCount, runConfigs); - psiFile.putUserData(CURRENT_FILE_RUN_CONFIGS_KEY, cache); - } - - return cache.myRunConfigs; - } - - private @NotNull List filterConfigsThatHaveRunner(@NotNull List runConfigs) { - return ContainerUtil.filter(runConfigs, config -> ProgramRunner.getRunner(myExecutor.getId(), config.getConfiguration()) != null); - } - - private static boolean isSuppressed(Project project) { - for (ExecutionActionSuppressor suppressor : ExecutionActionSuppressor.EP_NAME.getExtensionList()) { - if (suppressor.isSuppressed(project)) return true; - } - return false; - } - - protected Icon getInformativeIcon(@NotNull Project project, @NotNull RunnerAndConfigurationSettings selectedConfiguration, - @NotNull AnActionEvent e) { - RunConfiguration configuration = selectedConfiguration.getConfiguration(); - if (configuration instanceof RunnerIconProvider provider) { - Icon icon = provider.getExecutorIcon(configuration, myExecutor); - if (icon != null) { - return icon; - } - } - - List runningDescriptors = getRunningDescriptors(project, selectedConfiguration); - - if (needRerunPresentation(configuration, runningDescriptors)) { - if (ExperimentalUI.isNewUI() && myExecutor.getIcon() != myExecutor.getRerunIcon()) { - return myExecutor.getRerunIcon(); - } - if (DefaultRunExecutor.EXECUTOR_ID.equals(myExecutor.getId())) { - return AllIcons.Actions.Restart; - } - } - if (runningDescriptors.isEmpty()) { - return myExecutor.getIcon(); - } - - if (runningDescriptors.size() == 1) { - return ExecutionUtil.getLiveIndicator(myExecutor.getIcon()); - } - else { - return IconUtil.addText(myExecutor.getIcon(), RunToolbarPopupKt.runCounterToString(e, runningDescriptors.size())); - } - } - - private static boolean needRerunPresentation(RunConfiguration configuration, List runningDescriptors) { - return !configuration.isAllowRunningInParallel() && !runningDescriptors.isEmpty(); - } - - private @NotNull List getRunningDescriptors(@NotNull Project project, - @NotNull RunnerAndConfigurationSettings selectedConfiguration) { - ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstanceIfCreated(project); - if (executionManager == null) return emptyList(); - - List runningDescriptors = - executionManager.getRunningDescriptors(s -> ExecutionManagerImplKt.isOfSameType(s, selectedConfiguration)); - runningDescriptors = ContainerUtil.filter(runningDescriptors, descriptor -> executionManager.getExecutors(descriptor).contains(myExecutor)); - return runningDescriptors; - } - - protected @Nullable RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { - Project project = e.getProject(); - RunManager runManager = project == null ? null : RunManager.getInstanceIfCreated(project); - return runManager == null ? null : runManager.getSelectedConfiguration(); - } - - protected void run(@NotNull Project project, @NotNull RunnerAndConfigurationSettings settings, @NotNull DataContext dataContext) { - RunnerHelper.run(project, settings.getConfiguration(), settings, dataContext, myExecutor); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - final Project project = e.getProject(); - if (project == null || project.isDisposed()) { - return; - } - - RunnerAndConfigurationSettings selectedConfiguration = getSelectedConfiguration(e); - if (selectedConfiguration != null) { - run(project, selectedConfiguration, e.getDataContext()); - } - else if (RunConfigurationsComboBoxAction.hasRunCurrentFileItem(project)) { - runCurrentFile(e); - } - } - - private void runCurrentFile(@NotNull AnActionEvent e) { - Project project = Objects.requireNonNull(e.getProject()); - List runConfigs = getRunCurrentFileActionStatus(e, true).myRunConfigs; - if (runConfigs.isEmpty()) { - return; - } - - if (runConfigs.size() == 1) { - doRunCurrentFile(project, runConfigs.get(0), e.getDataContext()); - return; - } - - IPopupChooserBuilder builder = JBPopupFactory.getInstance() - .createPopupChooserBuilder(runConfigs) - .setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - RunnerAndConfigurationSettings runConfig = (RunnerAndConfigurationSettings)value; - JLabel result = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - result.setIcon(runConfig.getConfiguration().getIcon()); - result.setText(runConfig.getName()); - return result; - } - }) - .setItemChosenCallback(runConfig -> doRunCurrentFile(project, runConfig, e.getDataContext())); - - InputEvent inputEvent = e.getInputEvent(); - if (inputEvent instanceof MouseEvent) { - builder.createPopup().showUnderneathOf(inputEvent.getComponent()); - } - else { - Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); - if (editor == null) { - // Not expected to happen because we are running a file from the current editor. - LOG.warn("Run Current File (" + runConfigs + "): getSelectedTextEditor() == null"); - return; - } - - builder - .setTitle(myExecutor.getActionName()) - .createPopup() - .showInBestPositionFor(editor); - } - } - - protected void doRunCurrentFile(@NotNull Project project, - @NotNull RunnerAndConfigurationSettings runConfig, - @NotNull DataContext dataContext) { - ExecutionUtil.doRunConfiguration(runConfig, myExecutor, null, null, dataContext, env -> env.setRunningCurrentFile(true)); - } - } - - public static final class RunSpecifiedConfigExecutorAction extends ExecutorAction { - private final RunnerAndConfigurationSettings myRunConfig; - private final boolean myEditConfigBeforeRun; - - public RunSpecifiedConfigExecutorAction(@NotNull Executor executor, - @NotNull RunnerAndConfigurationSettings runConfig, - boolean editConfigBeforeRun) { + /** @deprecated Use {@link com.intellij.execution.actions.ExecutorAction} instead */ + @Deprecated(forRemoval = true) + public static class ExecutorAction extends com.intellij.execution.actions.ExecutorAction { + public ExecutorAction(@NotNull Executor executor) { super(executor); - myRunConfig = runConfig; - myEditConfigBeforeRun = editConfigBeforeRun; - } - - @Override - protected @NotNull RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { - return myRunConfig; - } - - @Override - protected boolean hideDisabledExecutorButtons() { - // no need in a list of disabled actions in the secondary menu of the Run Configuration item in the combo box drop-down menu. - return true; - } - - @Override - public void update(@NotNull AnActionEvent e) { - super.update(e); - - if (myEditConfigBeforeRun) { - Presentation presentation = e.getPresentation(); - presentation.setText(ExecutionBundle.message("choose.run.popup.edit")); - presentation.setDescription(ExecutionBundle.message("choose.run.popup.edit.description")); - presentation.setIcon(!ExperimentalUI.isNewUI() ? AllIcons.Actions.EditSource : null); - } - } - - @Override - protected void run(@NotNull Project project, @NotNull RunnerAndConfigurationSettings settings, @NotNull DataContext dataContext) { - LOG.assertTrue(myRunConfig == settings); - - if (myEditConfigBeforeRun) { - String dialogTitle = ExecutionBundle.message("dialog.title.edit.configuration.settings"); - if (!RunDialog.editConfiguration(project, myRunConfig, dialogTitle, myExecutor)) { - return; - } - } - - super.run(project, myRunConfig, dataContext); - - RunManager.getInstance(project).setSelectedConfiguration(myRunConfig); - } - } - - public static class RunCurrentFileExecutorAction extends ExecutorAction { - public RunCurrentFileExecutorAction(@NotNull Executor executor) { - super(executor); - } - - @Override - protected @Nullable RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { - return null; // null means 'run current file, not the selected run configuration' - } - - @Override - protected boolean hideDisabledExecutorButtons() { - // no need in a list of disabled actions in the secondary menu of the 'Current File' item in the combo box drop-down menu. - return true; - } - - @Override - public void update(@NotNull AnActionEvent e) { - Presentation presentation = e.getPresentation(); - if (e.getProject() == null || !RunConfigurationsComboBoxAction.hasRunCurrentFileItem(e.getProject())) { - presentation.setEnabledAndVisible(false); - return; - } - - super.update(e); - } - } - - public static final class EditRunConfigAndRunCurrentFileExecutorAction extends RunCurrentFileExecutorAction { - public EditRunConfigAndRunCurrentFileExecutorAction(@NotNull Executor executor) { - super(executor); - } - - @Override - public void update(@NotNull AnActionEvent e) { - super.update(e); - - Presentation presentation = e.getPresentation(); - presentation.setText(ExecutionBundle.message("run.configurations.popup.edit.run.config.and.run.current.file")); - presentation.setDescription(ExecutionBundle.message("run.configurations.popup.edit.run.config.and.run.current.file.description")); - presentation.setIcon(AllIcons.Actions.EditSource); - } - - @Override - protected void doRunCurrentFile(@NotNull Project project, - @NotNull RunnerAndConfigurationSettings runConfig, - @NotNull DataContext dataContext) { - String suggestedName = StringUtil.notNullize(((LocatableConfiguration)runConfig.getConfiguration()).suggestedName(), - runConfig.getName()); - List usedNames = ContainerUtil.map(RunManager.getInstance(project).getAllSettings(), RunnerAndConfigurationSettings::getName); - String uniqueName = UniqueNameGenerator.generateUniqueName(suggestedName, "", "", " (", ")", s -> !usedNames.contains(s)); - runConfig.setName(uniqueName); - - String dialogTitle = ExecutionBundle.message("dialog.title.edit.configuration.settings"); - if (RunDialog.editConfiguration(project, runConfig, dialogTitle, myExecutor)) { - RunManager.getInstance(project).setTemporaryConfiguration(runConfig); - super.doRunCurrentFile(project, runConfig, dataContext); - } - } - } - - private static final class RunCurrentFileInfo { - private final long myPsiModCount; - private final @NotNull List myRunConfigs; - - private RunCurrentFileInfo(long psiModCount, @NotNull List runConfigs) { - myPsiModCount = psiModCount; - myRunConfigs = runConfigs; - } - } - - @ApiStatus.Internal - public static class ExecutorGroupActionGroup extends NonEmptyActionGroup implements DumbAware { - protected final ExecutorGroup myExecutorGroup; - private final Function myChildConverter; - - protected ExecutorGroupActionGroup(@NotNull ExecutorGroup executorGroup, - @NotNull Function childConverter) { - myExecutorGroup = executorGroup; - myChildConverter = childConverter; - Presentation presentation = getTemplatePresentation(); - presentation.setText(executorGroup.getStartActionText()); - presentation.setIconSupplier(executorGroup::getIcon); - } - - @Override - public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) { - // RunExecutorSettings configurations can be modified, so we request current childExecutors on each call - List childExecutors = myExecutorGroup.childExecutors(); - AnAction[] result = new AnAction[childExecutors.size()]; - for (int i = 0; i < childExecutors.size(); i++) { - result[i] = myChildConverter.apply(childExecutors.get(i)); - } - return result; - } - - @Override - public void update(@NotNull AnActionEvent e) { - Project project = e.getProject(); - if (project == null || !project.isInitialized() || project.isDisposed()) { - e.getPresentation().setEnabled(false); - return; - } - if (!myExecutorGroup.isApplicable(project)) { - e.getPresentation().setEnabledAndVisible(false); - return; - } - super.update(e); } } @@ -961,32 +373,4 @@ public final class ExecutorRegistryImpl extends ExecutorRegistry { return true; } } - - private static final class RunCurrentFileActionStatus { - private final boolean myEnabled; - private final @Nls @NotNull String myTooltip; - private final @NotNull Icon myIcon; - - private final @NotNull List myRunConfigs; - - private static RunCurrentFileActionStatus createDisabled(@Nls @NotNull String tooltip, @NotNull Icon icon) { - return new RunCurrentFileActionStatus(false, tooltip, icon, emptyList()); - } - - private static RunCurrentFileActionStatus createEnabled(@Nls @NotNull String tooltip, - @NotNull Icon icon, - @NotNull List runConfigs) { - return new RunCurrentFileActionStatus(true, tooltip, icon, runConfigs); - } - - private RunCurrentFileActionStatus(boolean enabled, - @Nls @NotNull String tooltip, - @NotNull Icon icon, - @NotNull List runConfigs) { - myEnabled = enabled; - myTooltip = tooltip; - myIcon = icon; - myRunConfigs = runConfigs; - } - } } diff --git a/platform/execution-impl/src/com/intellij/execution/actions/EditRunConfigAndRunCurrentFileExecutorAction.java b/platform/execution-impl/src/com/intellij/execution/actions/EditRunConfigAndRunCurrentFileExecutorAction.java new file mode 100644 index 000000000000..8e258fe86855 --- /dev/null +++ b/platform/execution-impl/src/com/intellij/execution/actions/EditRunConfigAndRunCurrentFileExecutorAction.java @@ -0,0 +1,55 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.Executor; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.configurations.LocatableConfiguration; +import com.intellij.execution.impl.RunDialog; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.UniqueNameGenerator; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +@ApiStatus.Internal +public final class EditRunConfigAndRunCurrentFileExecutorAction extends RunCurrentFileExecutorAction { + public EditRunConfigAndRunCurrentFileExecutorAction(@NotNull Executor executor) { + super(executor); + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + Presentation presentation = e.getPresentation(); + presentation.setText(ExecutionBundle.message("run.configurations.popup.edit.run.config.and.run.current.file")); + presentation.setDescription(ExecutionBundle.message("run.configurations.popup.edit.run.config.and.run.current.file.description")); + presentation.setIcon(AllIcons.Actions.EditSource); + } + + @Override + protected void doRunCurrentFile(@NotNull Project project, + @NotNull RunnerAndConfigurationSettings runConfig, + @NotNull DataContext dataContext) { + String suggestedName = StringUtil.notNullize(((LocatableConfiguration)runConfig.getConfiguration()).suggestedName(), + runConfig.getName()); + List usedNames = ContainerUtil.map(RunManager.getInstance(project).getAllSettings(), RunnerAndConfigurationSettings::getName); + String uniqueName = UniqueNameGenerator.generateUniqueName(suggestedName, "", "", " (", ")", s -> !usedNames.contains(s)); + runConfig.setName(uniqueName); + + String dialogTitle = ExecutionBundle.message("dialog.title.edit.configuration.settings"); + if (RunDialog.editConfiguration(project, runConfig, dialogTitle, myExecutor)) { + RunManager.getInstance(project).setTemporaryConfiguration(runConfig); + super.doRunCurrentFile(project, runConfig, dataContext); + } + } +} diff --git a/platform/execution-impl/src/com/intellij/execution/actions/ExecutorAction.java b/platform/execution-impl/src/com/intellij/execution/actions/ExecutorAction.java new file mode 100644 index 000000000000..c7f2d19007fe --- /dev/null +++ b/platform/execution-impl/src/com/intellij/execution/actions/ExecutorAction.java @@ -0,0 +1,484 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.actions; + +import com.intellij.execution.*; +import com.intellij.execution.compound.CompoundRunConfiguration; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.impl.ExecutionManagerImpl; +import com.intellij.execution.impl.ExecutionManagerImplKt; +import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl; +import com.intellij.execution.runners.ExecutionUtil; +import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunState; +import com.intellij.execution.ui.RunStatusHistory; +import com.intellij.execution.ui.RunToolbarPopupKt; +import com.intellij.icons.AllIcons; +import com.intellij.internal.statistic.collectors.fus.actions.persistence.ActionIdProvider; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.remoting.ActionRemotePermissionRequirements; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.ui.popup.IPopupChooserBuilder; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.ui.ExperimentalUI; +import com.intellij.ui.SpinningProgressIcon; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IconUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.JBUI; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.Objects; + +import static java.util.Collections.emptyList; + +@ApiStatus.Internal +public class ExecutorAction extends AnAction + implements DumbAware, ActionRemotePermissionRequirements.RunAccess, ActionIdProvider { + + private static final Logger LOG = Logger.getInstance(ExecutorAction.class); + + private static final Key spinningIconKey = Key.create("spinning-icon-key"); + private static final Key CURRENT_FILE_RUN_CONFIGS_KEY = Key.create("CURRENT_FILE_RUN_CONFIGS"); + + protected final Executor myExecutor; + + protected ExecutorAction(@NotNull Executor executor) { + super(executor::getStartActionText, executor::getDescription, executor::getIcon); + + myExecutor = executor; + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } + + @Override + public void update(@NotNull AnActionEvent e) { + Presentation presentation = e.getPresentation(); + presentation.putClientProperty(ExecutorActionStatus.KEY, null); // by default + Project project = e.getProject(); + if (project == null || !project.isInitialized() || project.isDisposed()) { + presentation.setEnabled(false); + return; + } + + RunManager runManager = RunManager.getInstanceIfCreated(project); + if (runManager == null) { + presentation.setEnabled(false); + return; + } + + RunnerAndConfigurationSettings selectedSettings = getSelectedConfiguration(e); + boolean enabled = false; + ExecutorActionStatus actionStatus = null; + boolean runConfigAsksToHideDisabledExecutorButtons = false; + String text; + if (selectedSettings != null) { + if (DumbService.isDumb(project) && !selectedSettings.getType().isDumbAware()) { + presentation.setEnabled(false); + return; + } + + actionStatus = setupActionStatus(e, project, selectedSettings, presentation); + presentation.setIcon(getInformativeIcon(project, selectedSettings, e)); + RunConfiguration configuration = selectedSettings.getConfiguration(); + if (!isSuppressed(project)) { + enabled = ExecutorRegistryImpl.RunnerHelper.canRun(project, myExecutor, configuration); + } + if (!(configuration instanceof CompoundRunConfiguration)) { + runConfigAsksToHideDisabledExecutorButtons = configuration.hideDisabledExecutorButtons(); + } + if (enabled) { + presentation.setDescription(myExecutor.getDescription()); + } + + if (ExperimentalUI.isNewUI() && + needRerunPresentation(selectedSettings.getConfiguration(), getRunningDescriptors(project, selectedSettings))) { + if (myExecutor.getId().equals(DefaultRunExecutor.EXECUTOR_ID)) { + text = ExecutionBundle.message("run.toolbar.widget.rerun.text", configuration); + } + else { + text = ExecutionBundle.message("run.toolbar.widget.restart.text", myExecutor.getActionName(), configuration.getName()); + } + } + else { + text = myExecutor.getStartActionText(configuration.getName()); + } + } + else { + if (RunConfigurationsComboBoxAction.hasRunCurrentFileItem(project)) { + // don't compute current file to run if editors are not yet loaded + if (!project.isDefault() && !StartupManager.getInstance(project).postStartupActivityPassed()) { + presentation.setEnabled(false); + return; + } + + RunCurrentFileActionStatus status = getRunCurrentFileActionStatus(e, false); + for (RunnerAndConfigurationSettings config : status.myRunConfigs) { + actionStatus = setupActionStatus(e, project, config, presentation); + if (actionStatus != ExecutorActionStatus.NORMAL) { + break; + } + } + enabled = status.enabled; + text = status.tooltip; + presentation.setIcon(status.icon); + } + else { + text = getTemplatePresentation().getTextWithMnemonic(); + } + } + + if (actionStatus != ExecutorActionStatus.LOADING && (runConfigAsksToHideDisabledExecutorButtons || hideDisabledExecutorButtons())) { + presentation.setEnabledAndVisible(enabled); + } + else { + presentation.setEnabled(enabled); + } + + if (presentation.isVisible()) { + presentation.setVisible(myExecutor.isApplicable(project)); + } + presentation.putClientProperty(ExecutorActionStatus.KEY, actionStatus); + presentation.setText(text); + } + + private ExecutorActionStatus setupActionStatus(@NotNull AnActionEvent e, + Project project, + RunnerAndConfigurationSettings selectedSettings, + Presentation presentation) { + // We can consider to add spinning to the inlined run actions. But there is a problem with redrawing + ExecutorActionStatus status = ExecutorActionStatus.NORMAL; + if (ActionPlaces.NEW_UI_RUN_TOOLBAR.equals(e.getPlace())) { + RunStatusHistory startHistory = RunStatusHistory.getInstance(project); + + boolean isLoading = startHistory.firstOrNull(selectedSettings, it -> (it.getExecutorId().equals(myExecutor.getId()) && + it.getState() == RunState.SCHEDULED)) != null; + if (isLoading) { + status = ExecutorActionStatus.LOADING; + SpinningProgressIcon spinningIcon = presentation.getClientProperty(spinningIconKey); + if (spinningIcon == null) { + spinningIcon = new SpinningProgressIcon(); + spinningIcon.setIconColor(JBUI.CurrentTheme.RunWidget.ICON_COLOR); + presentation.putClientProperty(spinningIconKey, spinningIcon); + } + presentation.setDisabledIcon(spinningIcon); + } + else { + if (!getRunningDescriptors(project, selectedSettings).isEmpty()) { + status = ExecutorActionStatus.RUNNING; + } + presentation.putClientProperty(spinningIconKey, null); + presentation.setDisabledIcon(null); + } + } + return status; + } + + protected boolean hideDisabledExecutorButtons() { + return false; + } + + private @NotNull RunCurrentFileActionStatus getRunCurrentFileActionStatus(@NotNull AnActionEvent e, + boolean resetCache) { + Project project = Objects.requireNonNull(e.getProject()); + + VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles(); + if (files.length == 1) { + // There's only one visible editor, let's use the file from this editor, even if the editor is not in focus. + PsiFile psiFile = PsiManager.getInstance(project).findFile(files[0]); + if (psiFile == null) { + String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.not.runnable"); + return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); + } + + return getRunCurrentFileActionStatus(psiFile, resetCache, e); + } + + Editor editor = e.getData(CommonDataKeys.EDITOR); + if (editor == null) { + String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.no.focused.editor"); + return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); + } + + PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + VirtualFile vFile = psiFile != null ? psiFile.getVirtualFile() : null; + if (psiFile == null || vFile == null || !ArrayUtil.contains(vFile, files)) { + // This is probably a special editor, like Python Console, which we don't want to use for the 'Run Current File' feature. + String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.no.focused.editor"); + return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); + } + + return getRunCurrentFileActionStatus(psiFile, resetCache, e); + } + + private @NotNull RunCurrentFileActionStatus getRunCurrentFileActionStatus(@NotNull PsiFile psiFile, + boolean resetCache, + @NotNull AnActionEvent e) { + List runConfigs = getRunConfigsForCurrentFile(psiFile, resetCache); + if (runConfigs.isEmpty()) { + String tooltip = ExecutionBundle.message("run.button.on.toolbar.tooltip.current.file.not.runnable"); + return RunCurrentFileActionStatus.createDisabled(tooltip, myExecutor.getIcon()); + } + + List runnableConfigs = filterConfigsThatHaveRunner(runConfigs); + if (runnableConfigs.isEmpty()) { + return RunCurrentFileActionStatus.createDisabled(myExecutor.getStartActionText(psiFile.getName()), + myExecutor.getIcon()); + } + + Icon icon = myExecutor.getIcon(); + if (runnableConfigs.size() == 1) { + icon = getInformativeIcon(psiFile.getProject(), runnableConfigs.get(0), e); + } + else { + // myExecutor.getIcon() is the least preferred icon + // AllIcons.Actions.Restart is more preferred + // Other icons are the most preferred ones (like ExecutionUtil.getLiveIndicator()) + for (RunnerAndConfigurationSettings config : runnableConfigs) { + Icon anotherIcon = getInformativeIcon(psiFile.getProject(), config, e); + if (icon == myExecutor.getIcon() || (anotherIcon != myExecutor.getIcon() && anotherIcon != AllIcons.Actions.Restart)) { + icon = anotherIcon; + } + } + } + + return RunCurrentFileActionStatus.createEnabled(myExecutor.getStartActionText(psiFile.getName()), icon, + runnableConfigs); + } + + @Override + public String getId() { + return myExecutor.getId(); + } + + @ApiStatus.Internal + public static List getRunConfigsForCurrentFile(@NotNull PsiFile psiFile, boolean resetCache) { + if (resetCache) { + psiFile.putUserData(CURRENT_FILE_RUN_CONFIGS_KEY, null); + } + + // Without this cache, an expensive method `ConfigurationContext.getConfigurationsFromContext()` is called too often for 2 reasons: + // - there are several buttons on the toolbar (Run, Debug, Profile, etc.), each runs ExecutorAction.update() during each action update session + // - the state of the buttons on the toolbar is updated several times a second, even if no files are being edited + + // The following few lines do pretty much the same as CachedValuesManager.getCachedValue(), but it's implemented without calling that + // method because it appeared to be too hard to satisfy both IdempotenceChecker.checkEquivalence() and CachedValueStabilityChecker.checkProvidersEquivalent(). + // The reason is that RunnerAndConfigurationSettings class doesn't implement equals(), and that CachedValueProvider would need to capture + // ConfigurationContext, which doesn't implement equals() either. + // Effectively, we need only one boolean value: whether the action is enabled or not, so it shouldn't be a problem that + // RunnerAndConfigurationSettings and ConfigurationContext don't implement equals() and this code doesn't pass CachedValuesManager checks. + + long psiModCount = PsiModificationTracker.getInstance(psiFile.getProject()).getModificationCount(); + RunCurrentFileInfo cache = psiFile.getUserData(CURRENT_FILE_RUN_CONFIGS_KEY); + + if (cache == null || cache.myPsiModCount != psiModCount) { + // The 'Run current file' feature doesn't depend on the caret position in the file, that's why ConfigurationContext is created like this. + ConfigurationContext configurationContext = new ConfigurationContext(psiFile); + + // The 'Run current file' feature doesn't reuse existing run configurations (by design). + List configurationsFromContext = configurationContext.createConfigurationsFromContext(); + + List runConfigs = configurationsFromContext == null + ? List.of() + : ContainerUtil.map(configurationsFromContext, + ConfigurationFromContext::getConfigurationSettings); + + VirtualFile vFile = psiFile.getVirtualFile(); + if (!runConfigs.isEmpty()) { + String filePath = vFile == null ? null : vFile.getPath(); + for (RunnerAndConfigurationSettings config : runConfigs) { + ((RunnerAndConfigurationSettingsImpl)config).setFilePathIfRunningCurrentFile(filePath); + } + } + + cache = new RunCurrentFileInfo(psiModCount, runConfigs); + psiFile.putUserData(CURRENT_FILE_RUN_CONFIGS_KEY, cache); + } + + return cache.myRunConfigs; + } + + private @NotNull List filterConfigsThatHaveRunner(@NotNull List runConfigs) { + return ContainerUtil.filter(runConfigs, config -> ProgramRunner.getRunner(myExecutor.getId(), config.getConfiguration()) != null); + } + + private static boolean isSuppressed(Project project) { + for (ExecutionActionSuppressor suppressor : ExecutionActionSuppressor.EP_NAME.getExtensionList()) { + if (suppressor.isSuppressed(project)) return true; + } + return false; + } + + protected Icon getInformativeIcon(@NotNull Project project, @NotNull RunnerAndConfigurationSettings selectedConfiguration, + @NotNull AnActionEvent e) { + RunConfiguration configuration = selectedConfiguration.getConfiguration(); + if (configuration instanceof RunnerIconProvider provider) { + Icon icon = provider.getExecutorIcon(configuration, myExecutor); + if (icon != null) { + return icon; + } + } + + List runningDescriptors = getRunningDescriptors(project, selectedConfiguration); + + if (needRerunPresentation(configuration, runningDescriptors)) { + if (ExperimentalUI.isNewUI() && myExecutor.getIcon() != myExecutor.getRerunIcon()) { + return myExecutor.getRerunIcon(); + } + if (DefaultRunExecutor.EXECUTOR_ID.equals(myExecutor.getId())) { + return AllIcons.Actions.Restart; + } + } + if (runningDescriptors.isEmpty()) { + return myExecutor.getIcon(); + } + + if (runningDescriptors.size() == 1) { + return ExecutionUtil.getLiveIndicator(myExecutor.getIcon()); + } + else { + return IconUtil.addText(myExecutor.getIcon(), RunToolbarPopupKt.runCounterToString(e, runningDescriptors.size())); + } + } + + private static boolean needRerunPresentation(RunConfiguration configuration, List runningDescriptors) { + return !configuration.isAllowRunningInParallel() && !runningDescriptors.isEmpty(); + } + + private @NotNull List getRunningDescriptors(@NotNull Project project, + @NotNull RunnerAndConfigurationSettings selectedConfiguration) { + ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstanceIfCreated(project); + if (executionManager == null) return emptyList(); + + List runningDescriptors = + executionManager.getRunningDescriptors(s -> ExecutionManagerImplKt.isOfSameType(s, selectedConfiguration)); + runningDescriptors = + ContainerUtil.filter(runningDescriptors, descriptor -> executionManager.getExecutors(descriptor).contains(myExecutor)); + return runningDescriptors; + } + + protected @Nullable RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { + Project project = e.getProject(); + RunManager runManager = project == null ? null : RunManager.getInstanceIfCreated(project); + return runManager == null ? null : runManager.getSelectedConfiguration(); + } + + protected void run(@NotNull Project project, @NotNull RunnerAndConfigurationSettings settings, @NotNull DataContext dataContext) { + ExecutorRegistryImpl.RunnerHelper.run(project, settings.getConfiguration(), settings, dataContext, myExecutor); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final Project project = e.getProject(); + if (project == null || project.isDisposed()) { + return; + } + + RunnerAndConfigurationSettings selectedConfiguration = getSelectedConfiguration(e); + if (selectedConfiguration != null) { + run(project, selectedConfiguration, e.getDataContext()); + } + else if (RunConfigurationsComboBoxAction.hasRunCurrentFileItem(project)) { + runCurrentFile(e); + } + } + + private void runCurrentFile(@NotNull AnActionEvent e) { + Project project = Objects.requireNonNull(e.getProject()); + List runConfigs = getRunCurrentFileActionStatus(e, true).myRunConfigs; + if (runConfigs.isEmpty()) { + return; + } + + if (runConfigs.size() == 1) { + doRunCurrentFile(project, runConfigs.get(0), e.getDataContext()); + return; + } + + IPopupChooserBuilder builder = JBPopupFactory.getInstance() + .createPopupChooserBuilder(runConfigs) + .setRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + RunnerAndConfigurationSettings runConfig = (RunnerAndConfigurationSettings)value; + JLabel result = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + result.setIcon(runConfig.getConfiguration().getIcon()); + result.setText(runConfig.getName()); + return result; + } + }) + .setItemChosenCallback(runConfig -> doRunCurrentFile(project, runConfig, e.getDataContext())); + + InputEvent inputEvent = e.getInputEvent(); + if (inputEvent instanceof MouseEvent) { + builder.createPopup().showUnderneathOf(inputEvent.getComponent()); + } + else { + Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); + if (editor == null) { + // Not expected to happen because we are running a file from the current editor. + LOG.warn("Run Current File (" + runConfigs + "): getSelectedTextEditor() == null"); + return; + } + + builder + .setTitle(myExecutor.getActionName()) + .createPopup() + .showInBestPositionFor(editor); + } + } + + protected void doRunCurrentFile(@NotNull Project project, + @NotNull RunnerAndConfigurationSettings runConfig, + @NotNull DataContext dataContext) { + ExecutionUtil.doRunConfiguration(runConfig, myExecutor, null, null, dataContext, env -> env.setRunningCurrentFile(true)); + } + + private record RunCurrentFileInfo( + long myPsiModCount, + @NotNull List myRunConfigs) { + } + + + private record RunCurrentFileActionStatus( + boolean enabled, + @Nls @NotNull String tooltip, + @NotNull Icon icon, + @NotNull List myRunConfigs) { + + static @NotNull RunCurrentFileActionStatus createDisabled(@Nls @NotNull String tooltip, + @NotNull Icon icon) { + return new RunCurrentFileActionStatus(false, tooltip, icon, emptyList()); + } + + static @NotNull RunCurrentFileActionStatus createEnabled(@Nls @NotNull String tooltip, + @NotNull Icon icon, + @NotNull List runConfigs) { + return new RunCurrentFileActionStatus(true, tooltip, icon, runConfigs); + } + } +} diff --git a/platform/execution-impl/src/com/intellij/execution/actions/ExecutorGroupActionGroup.java b/platform/execution-impl/src/com/intellij/execution/actions/ExecutorGroupActionGroup.java new file mode 100644 index 000000000000..136d159b5783 --- /dev/null +++ b/platform/execution-impl/src/com/intellij/execution/actions/ExecutorGroupActionGroup.java @@ -0,0 +1,57 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.actions; + +import com.intellij.execution.Executor; +import com.intellij.execution.executors.ExecutorGroup; +import com.intellij.ide.actions.NonEmptyActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; + +@ApiStatus.Internal +public class ExecutorGroupActionGroup extends NonEmptyActionGroup implements DumbAware { + protected final ExecutorGroup myExecutorGroup; + private final Function myChildConverter; + + public ExecutorGroupActionGroup(@NotNull ExecutorGroup executorGroup, + @NotNull Function childConverter) { + myExecutorGroup = executorGroup; + myChildConverter = childConverter; + Presentation presentation = getTemplatePresentation(); + presentation.setText(executorGroup.getStartActionText()); + presentation.setIconSupplier(executorGroup::getIcon); + } + + @Override + public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) { + // RunExecutorSettings configurations can be modified, so we request current childExecutors on each call + List childExecutors = myExecutorGroup.childExecutors(); + AnAction[] result = new AnAction[childExecutors.size()]; + for (int i = 0; i < childExecutors.size(); i++) { + result[i] = myChildConverter.apply(childExecutors.get(i)); + } + return result; + } + + @Override + public void update(@NotNull AnActionEvent e) { + Project project = e.getProject(); + if (project == null || !project.isInitialized() || project.isDisposed()) { + e.getPresentation().setEnabled(false); + return; + } + if (!myExecutorGroup.isApplicable(project)) { + e.getPresentation().setEnabledAndVisible(false); + return; + } + super.update(e); + } +} diff --git a/platform/execution-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java b/platform/execution-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java index 802905c46080..f09afbb3ccd9 100644 --- a/platform/execution-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java +++ b/platform/execution-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java @@ -447,11 +447,11 @@ public class RunConfigurationsComboBoxAction extends ComboBoxAction implements D List result = new ArrayList<>(); forAllExecutors(o -> { if (executorFilter == null || executorFilter.test(o)) { - result.add(new ExecutorRegistryImpl.RunCurrentFileExecutorAction(o)); + result.add(new RunCurrentFileExecutorAction(o)); } }); result.add(Separator.getInstance()); - result.add(new ExecutorRegistryImpl.EditRunConfigAndRunCurrentFileExecutorAction(DefaultRunExecutor.getRunExecutorInstance())); + result.add(new EditRunConfigAndRunCurrentFileExecutorAction(DefaultRunExecutor.getRunExecutorInstance())); return result; } @@ -552,14 +552,14 @@ public class RunConfigurationsComboBoxAction extends ComboBoxAction implements D // Add actions similar to com.intellij.execution.actions.ChooseRunConfigurationPopup.ConfigurationActionsStep#buildActions forAllExecutors(o -> { if (executorFilter == null || executorFilter.test(o)) { - result.add(new ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(o, myConfiguration, false)); + result.add(new RunSpecifiedConfigExecutorAction(o, myConfiguration, false)); } }); result.add(Separator.create(ExperimentalUI.isNewUI() ? ExecutionBundle.message("choose.run.popup.separator") : null)); if (!ExperimentalUI.isNewUI()) { Executor runExecutor = DefaultRunExecutor.getRunExecutorInstance(); - result.add(new ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(runExecutor, myConfiguration, true)); + result.add(new RunSpecifiedConfigExecutorAction(runExecutor, myConfiguration, true)); } else { result.add(ActionManager.getInstance().getAction(IdeActions.ACTION_EDIT_RUN_CONFIGURATIONS)); diff --git a/platform/execution-impl/src/com/intellij/execution/actions/RunCurrentFileExecutorAction.java b/platform/execution-impl/src/com/intellij/execution/actions/RunCurrentFileExecutorAction.java new file mode 100644 index 000000000000..ed43fb6098ee --- /dev/null +++ b/platform/execution-impl/src/com/intellij/execution/actions/RunCurrentFileExecutorAction.java @@ -0,0 +1,39 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.actions; + +import com.intellij.execution.Executor; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public class RunCurrentFileExecutorAction extends ExecutorAction { + public RunCurrentFileExecutorAction(@NotNull Executor executor) { + super(executor); + } + + @Override + protected @Nullable RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { + return null; // null means 'run current file, not the selected run configuration' + } + + @Override + protected boolean hideDisabledExecutorButtons() { + // no need in a list of disabled actions in the secondary menu of the 'Current File' item in the combo box drop-down menu. + return true; + } + + @Override + public void update(@NotNull AnActionEvent e) { + Presentation presentation = e.getPresentation(); + if (e.getProject() == null || !RunConfigurationsComboBoxAction.hasRunCurrentFileItem(e.getProject())) { + presentation.setEnabledAndVisible(false); + return; + } + + super.update(e); + } +} diff --git a/platform/execution-impl/src/com/intellij/execution/actions/RunSpecifiedConfigExecutorAction.java b/platform/execution-impl/src/com/intellij/execution/actions/RunSpecifiedConfigExecutorAction.java new file mode 100644 index 000000000000..bb1858d7e8f7 --- /dev/null +++ b/platform/execution-impl/src/com/intellij/execution/actions/RunSpecifiedConfigExecutorAction.java @@ -0,0 +1,72 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.actions; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.Executor; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.impl.RunDialog; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.ui.ExperimentalUI; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public final class RunSpecifiedConfigExecutorAction extends ExecutorAction { + private static final Logger LOG = Logger.getInstance(RunSpecifiedConfigExecutorAction.class); + + private final RunnerAndConfigurationSettings myRunConfig; + private final boolean myEditConfigBeforeRun; + + public RunSpecifiedConfigExecutorAction(@NotNull Executor executor, + @NotNull RunnerAndConfigurationSettings runConfig, + boolean editConfigBeforeRun) { + super(executor); + myRunConfig = runConfig; + myEditConfigBeforeRun = editConfigBeforeRun; + } + + @Override + protected @NotNull RunnerAndConfigurationSettings getSelectedConfiguration(@NotNull AnActionEvent e) { + return myRunConfig; + } + + @Override + protected boolean hideDisabledExecutorButtons() { + // no need in a list of disabled actions in the secondary menu of the Run Configuration item in the combo box drop-down menu. + return true; + } + + @Override + public void update(@NotNull AnActionEvent e) { + super.update(e); + + if (myEditConfigBeforeRun) { + Presentation presentation = e.getPresentation(); + presentation.setText(ExecutionBundle.message("choose.run.popup.edit")); + presentation.setDescription(ExecutionBundle.message("choose.run.popup.edit.description")); + presentation.setIcon(!ExperimentalUI.isNewUI() ? AllIcons.Actions.EditSource : null); + } + } + + @Override + protected void run(@NotNull Project project, @NotNull RunnerAndConfigurationSettings settings, @NotNull DataContext dataContext) { + LOG.assertTrue(myRunConfig == settings); + + if (myEditConfigBeforeRun) { + String dialogTitle = ExecutionBundle.message("dialog.title.edit.configuration.settings"); + if (!RunDialog.editConfiguration(project, myRunConfig, dialogTitle, myExecutor)) { + return; + } + } + + super.run(project, myRunConfig, dataContext); + + RunManager.getInstance(project).setSelectedConfiguration(myRunConfig); + } +} diff --git a/platform/execution-impl/src/com/intellij/execution/impl/EditConfigurationsDialog.java b/platform/execution-impl/src/com/intellij/execution/impl/EditConfigurationsDialog.java index a99e4b77a472..b0fb4e07a3dc 100644 --- a/platform/execution-impl/src/com/intellij/execution/impl/EditConfigurationsDialog.java +++ b/platform/execution-impl/src/com/intellij/execution/impl/EditConfigurationsDialog.java @@ -2,6 +2,7 @@ package com.intellij.execution.impl; import com.intellij.execution.*; +import com.intellij.execution.actions.ExecutorAction; import com.intellij.execution.actions.RunConfigurationsComboBoxAction; import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.RunConfiguration; @@ -186,7 +187,7 @@ public class EditConfigurationsDialog extends SingleConfigurableEditor { myExecutorActions.forEach(action -> action.unregisterCustomShortcutSet(getContentPanel())); myExecutorActions.clear(); if (selected != null) { - ExecutorRegistryImpl.ExecutorAction action = createAction(selected, executor); + ExecutorAction action = createAction(selected, executor); DefaultActionGroup group = new DefaultActionGroup(); RunConfigurationsComboBoxAction.forAllExecutors(o -> { if (o != executor) { @@ -200,8 +201,8 @@ public class EditConfigurationsDialog extends SingleConfigurableEditor { } @NotNull - private ExecutorRegistryImpl.ExecutorAction createAction(@NotNull RunnerAndConfigurationSettings selected, @NotNull Executor executor) { - return new ExecutorRegistryImpl.ExecutorAction(executor) { + private ExecutorAction createAction(@NotNull RunnerAndConfigurationSettings selected, @NotNull Executor executor) { + return new ExecutorAction(executor) { { AnAction action = ActionManager.getInstance().getAction(executor.getId()); if (action != null) { diff --git a/platform/execution-impl/src/com/intellij/execution/lineMarker/LineMarkerActionWrapper.java b/platform/execution-impl/src/com/intellij/execution/lineMarker/LineMarkerActionWrapper.java index 55e19f4026dd..2f06b8519810 100644 --- a/platform/execution-impl/src/com/intellij/execution/lineMarker/LineMarkerActionWrapper.java +++ b/platform/execution-impl/src/com/intellij/execution/lineMarker/LineMarkerActionWrapper.java @@ -2,9 +2,9 @@ package com.intellij.execution.lineMarker; import com.intellij.codeInsight.intention.PriorityAction; -import com.intellij.execution.ExecutorRegistryImpl; import com.intellij.execution.Location; import com.intellij.execution.PsiLocation; +import com.intellij.execution.actions.ExecutorGroupActionGroup; import com.intellij.execution.actions.RunContextAction; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.*; @@ -51,9 +51,9 @@ public class LineMarkerActionWrapper extends ActionGroup implements PriorityActi // This is quickfix for IDEA-208231 // See com.intellij.codeInsight.daemon.impl.GutterIntentionMenuContributor.addActions(AnAction, List, GutterIconRenderer, AtomicInteger, DataContext)` if (myOrigin instanceof ExecutorAction) { - if (((ExecutorAction)myOrigin).getOrigin() instanceof ExecutorRegistryImpl.ExecutorGroupActionGroup) { + if (((ExecutorAction)myOrigin).getOrigin() instanceof ExecutorGroupActionGroup) { final AnAction[] children = - ((ExecutorRegistryImpl.ExecutorGroupActionGroup)((ExecutorAction)myOrigin).getOrigin()).getChildren(null); + ((ExecutorGroupActionGroup)((ExecutorAction)myOrigin).getOrigin()).getChildren(null); LOG.assertTrue(ContainerUtil.all(Arrays.asList(children), o -> o instanceof RunContextAction)); return ContainerUtil.mapNotNull(children, o -> { PsiElement element = myElement.getElement(); diff --git a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarAdditionAction.kt b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarAdditionAction.kt index 07de1ae38b83..93555e61d229 100644 --- a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarAdditionAction.kt +++ b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarAdditionAction.kt @@ -1,7 +1,7 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. 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.runToolbar -import com.intellij.execution.ExecutorRegistryImpl +import com.intellij.execution.actions.ExecutorAction import com.intellij.execution.executors.ExecutorGroup import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction @@ -9,7 +9,8 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.Presentation internal class RunToolbarAdditionAction(private val executorGroup: ExecutorGroup<*>, - val process: RunToolbarProcess, val selectedAction: () -> AnAction?) : AnAction() { + val process: RunToolbarProcess, val selectedAction: () -> AnAction?) + : AnAction() { init { updateAndGetVisibility(templatePresentation) @@ -27,16 +28,16 @@ internal class RunToolbarAdditionAction(private val executorGroup: ExecutorGroup private fun updateAndGetVisibility(presentation: Presentation): Boolean { val action = selectedAction() - return if (action is ExecutorRegistryImpl.ExecutorAction) { + return if (action is ExecutorAction) { presentation.copyFrom(action.getTemplatePresentation()) presentation.text = executorGroup.getRunToolbarActionText(action.templatePresentation.text) true - } else { + } + else { false } } - override fun actionPerformed(e: AnActionEvent) { selectedAction()?.actionPerformed(e) } diff --git a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarChooserAdditionGroup.kt b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarChooserAdditionGroup.kt index 0e3da54c1048..3babab78bc08 100644 --- a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarChooserAdditionGroup.kt +++ b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarChooserAdditionGroup.kt @@ -2,14 +2,15 @@ package com.intellij.execution.runToolbar import com.intellij.execution.Executor -import com.intellij.execution.ExecutorRegistryImpl.ExecutorGroupActionGroup +import com.intellij.execution.actions.ExecutorGroupActionGroup import com.intellij.execution.executors.ExecutorGroup import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent internal class RunToolbarChooserAdditionGroup(private val executorGroup: ExecutorGroup<*>, process: RunToolbarProcess, - childConverter: (Executor) -> AnAction) : ExecutorGroupActionGroup(executorGroup, - childConverter) { + childConverter: (Executor) -> AnAction) + : ExecutorGroupActionGroup(executorGroup, childConverter) { + var myProcess: RunToolbarProcess? = null init { diff --git a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarExecutorGroupAction.kt b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarExecutorGroupAction.kt index f68741ea7048..bdc53bb901ac 100644 --- a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarExecutorGroupAction.kt +++ b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarExecutorGroupAction.kt @@ -2,7 +2,7 @@ package com.intellij.execution.runToolbar import com.intellij.execution.Executor -import com.intellij.execution.ExecutorRegistryImpl +import com.intellij.execution.actions.ExecutorGroupActionGroup import com.intellij.execution.executors.ExecutorGroup import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent @@ -34,8 +34,9 @@ internal class RunToolbarExecutorGroupAction(private val group: RunToolbarExecut } internal class RunToolbarExecutorGroup(executorGroup: ExecutorGroup<*>, - childConverter: Function, override val process: RunToolbarProcess) : - ExecutorRunToolbarAction, ExecutorRegistryImpl.ExecutorGroupActionGroup(executorGroup, childConverter) { + childConverter: Function, + override val process: RunToolbarProcess) + : ExecutorRunToolbarAction, ExecutorGroupActionGroup(executorGroup, childConverter) { override fun getRightSideType(): RTBarAction.Type = RTBarAction.Type.RIGHT_FLEXIBLE diff --git a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarProcessAction.kt b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarProcessAction.kt index 99571f0d3cdf..d918c492818c 100644 --- a/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarProcessAction.kt +++ b/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarProcessAction.kt @@ -4,12 +4,13 @@ package com.intellij.execution.runToolbar import com.intellij.execution.Executor import com.intellij.execution.ExecutorRegistryImpl import com.intellij.execution.RunnerAndConfigurationSettings +import com.intellij.execution.actions.ExecutorAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import javax.swing.Icon -open class RunToolbarProcessAction(override val process: RunToolbarProcess, val executor: Executor) : ExecutorRegistryImpl.ExecutorAction(executor), ExecutorRunToolbarAction, DumbAware { +open class RunToolbarProcessAction(override val process: RunToolbarProcess, val executor: Executor) : ExecutorAction(executor), ExecutorRunToolbarAction, DumbAware { override fun displayTextInToolbar(): Boolean { return true @@ -25,7 +26,7 @@ open class RunToolbarProcessAction(override val process: RunToolbarProcess, val e.project?.let { project -> if (canRun(e)) { getSelectedConfiguration(e)?.let { - ExecutorRegistryImpl.RunnerHelper.run(project, it.configuration, it, e.dataContext, executor) + ExecutorRegistryImpl.RunnerHelper.run(project, it.configuration, it, e.dataContext, executor) } } } @@ -45,7 +46,7 @@ open class RunToolbarProcessAction(override val process: RunToolbarProcess, val } } - if(e.presentation.isVisible) { + if (e.presentation.isVisible) { e.presentation.isEnabled = canRun(e) } } @@ -55,7 +56,7 @@ open class RunToolbarProcessAction(override val process: RunToolbarProcess, val } protected fun canRun(e: AnActionEvent): Boolean { - return e.project?.let { project-> + return e.project?.let { project -> return getSelectedConfiguration(e)?.let { ExecutorRegistryImpl.RunnerHelper.canRun(project, executor, it.configuration) } ?: false diff --git a/platform/execution-impl/src/com/intellij/execution/ui/RunToolbarPopup.kt b/platform/execution-impl/src/com/intellij/execution/ui/RunToolbarPopup.kt index aebb659d91ad..ad3d1595e418 100644 --- a/platform/execution-impl/src/com/intellij/execution/ui/RunToolbarPopup.kt +++ b/platform/execution-impl/src/com/intellij/execution/ui/RunToolbarPopup.kt @@ -2,8 +2,11 @@ package com.intellij.execution.ui import com.intellij.execution.* +import com.intellij.execution.actions.ExecutorAction import com.intellij.execution.actions.RunConfigurationsComboBoxAction import com.intellij.execution.actions.RunConfigurationsComboBoxAction.SelectConfigAction +import com.intellij.execution.actions.RunCurrentFileExecutorAction +import com.intellij.execution.actions.RunSpecifiedConfigExecutorAction import com.intellij.execution.configurations.RunProfile import com.intellij.execution.executors.DefaultRunExecutor import com.intellij.execution.impl.ExecutionManagerImpl @@ -381,21 +384,21 @@ private fun createRunConfigurationWithInlines(project: Project, if (secondVersionAction != null) { listOf( secondVersionAction, - ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(activeExecutor!!, conf, false), + RunSpecifiedConfigExecutorAction(activeExecutor!!, conf, false), StopConfigurationInlineAction(activeExecutor, conf) ) } else { listOf( - ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(activeExecutor!!, conf, false), + RunSpecifiedConfigExecutorAction(activeExecutor!!, conf, false), StopConfigurationInlineAction(activeExecutor, conf) ) } } else { listOf( - ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(runExecutor, conf, false), - ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(debugExecutor, conf, false) + RunSpecifiedConfigExecutorAction(runExecutor, conf, false), + RunSpecifiedConfigExecutorAction(debugExecutor, conf, false) ) } val extraGroup = AdditionalRunningOptions.getInstance(project).getAdditionalActions(conf, false) @@ -404,7 +407,7 @@ private fun createRunConfigurationWithInlines(project: Project, var prefix = listOf(extraGroup) if (showRerunAndStopButtons) { val extraExecutor = if (activeExecutor === runExecutor) debugExecutor else runExecutor - prefix = prefix + ExecutorRegistryImpl.RunSpecifiedConfigExecutorAction(extraExecutor, conf, false) + prefix = prefix + RunSpecifiedConfigExecutorAction(extraExecutor, conf, false) } val pinActionText = if (isPinned) ExecutionBundle.message("run.toolbar.widget.dropdown.unpin.action.text") else ExecutionBundle.message("run.toolbar.widget.dropdown.pin.action.text") @@ -429,7 +432,7 @@ private fun createCurrentFileWithInlineActions(project: Project, if (DumbService.isDumb(project)) { return RunConfigurationsComboBoxAction.RunCurrentFileAction() } - val configs = selectedFile?.findPsiFile(project)?.let { ExecutorRegistryImpl.ExecutorAction.getRunConfigsForCurrentFile(it, false) } + val configs = selectedFile?.findPsiFile(project)?.let { ExecutorAction.getRunConfigsForCurrentFile(it, false) } ?: emptyList() val runRunningConfig = configs.firstOrNull { checkIfRunWithExecutor(it, runExecutor, project) } val debugRunningConfig = configs.firstOrNull { checkIfRunWithExecutor(it, debugExecutor, project) } @@ -442,29 +445,29 @@ private fun createCurrentFileWithInlineActions(project: Project, } }.also { it.templatePresentation.putClientProperty(ActionUtil.INLINE_ACTIONS, listOf( - ExecutorRegistryImpl.RunCurrentFileExecutorAction(runExecutor), - ExecutorRegistryImpl.RunCurrentFileExecutorAction(debugExecutor)) + RunCurrentFileExecutorAction(runExecutor), + RunCurrentFileExecutorAction(debugExecutor)) ) } } val inlineActions = when { runRunningConfig != null -> listOf( - ExecutorRegistryImpl.RunCurrentFileExecutorAction(runExecutor), + RunCurrentFileExecutorAction(runExecutor), StopConfigurationInlineAction(runExecutor, runRunningConfig)) debugRunningConfig != null -> listOf( - ExecutorRegistryImpl.RunCurrentFileExecutorAction(debugExecutor), + RunCurrentFileExecutorAction(debugExecutor), StopConfigurationInlineAction(debugExecutor, debugRunningConfig)) else -> listOf( - ExecutorRegistryImpl.RunCurrentFileExecutorAction(runExecutor), - ExecutorRegistryImpl.RunCurrentFileExecutorAction(debugExecutor)) + RunCurrentFileExecutorAction(runExecutor), + RunCurrentFileExecutorAction(debugExecutor)) } val res = object : RunConfigurationsComboBoxAction.RunCurrentFileAction() { override fun getChildren(e: AnActionEvent?): Array { var suffix = emptyList() - if (debugRunningConfig != null) suffix = suffix + ExecutorRegistryImpl.RunCurrentFileExecutorAction(runExecutor) - if (runRunningConfig != null) suffix = suffix + ExecutorRegistryImpl.RunCurrentFileExecutorAction(debugExecutor) + if (debugRunningConfig != null) suffix = suffix + RunCurrentFileExecutorAction(runExecutor) + if (runRunningConfig != null) suffix = suffix + RunCurrentFileExecutorAction(debugExecutor) return (suffix + getDefaultChildren(excludeRunAndDebug)).toTypedArray() } }