extract ExecutorRegistryImpl inner actions

GitOrigin-RevId: f4bf959f622326d06d9c05118d0eaa0b57e098e3
This commit is contained in:
Gregory.Shrago
2024-03-25 10:04:38 +00:00
committed by intellij-monorepo-bot
parent 8919a8ea99
commit a1283d883d
14 changed files with 768 additions and 669 deletions
@@ -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<SpinningProgressIcon> spinningIconKey = Key.create("spinning-icon-key");
private final Set<String> contextActionIdSet = new HashSet<>();
private final Map<String, AnAction> idToAction = new HashMap<>();
private final Map<String, AnAction> 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<RunCurrentFileInfo> 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<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> 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<ConfigurationFromContext> configurationsFromContext = configurationContext.createConfigurationsFromContext();
List<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> filterConfigsThatHaveRunner(@NotNull List<? extends RunnerAndConfigurationSettings> 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<RunContentDescriptor> 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<RunContentDescriptor> runningDescriptors) {
return !configuration.isAllowRunningInParallel() && !runningDescriptors.isEmpty();
}
private @NotNull List<RunContentDescriptor> getRunningDescriptors(@NotNull Project project,
@NotNull RunnerAndConfigurationSettings selectedConfiguration) {
ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstanceIfCreated(project);
if (executionManager == null) return emptyList();
List<RunContentDescriptor> 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<RunnerAndConfigurationSettings> runConfigs = getRunCurrentFileActionStatus(e, true).myRunConfigs;
if (runConfigs.isEmpty()) {
return;
}
if (runConfigs.size() == 1) {
doRunCurrentFile(project, runConfigs.get(0), e.getDataContext());
return;
}
IPopupChooserBuilder<RunnerAndConfigurationSettings> 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<String> 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<RunnerAndConfigurationSettings> myRunConfigs;
private RunCurrentFileInfo(long psiModCount, @NotNull List<RunnerAndConfigurationSettings> runConfigs) {
myPsiModCount = psiModCount;
myRunConfigs = runConfigs;
}
}
@ApiStatus.Internal
public static class ExecutorGroupActionGroup extends NonEmptyActionGroup implements DumbAware {
protected final ExecutorGroup<?> myExecutorGroup;
private final Function<? super Executor, ? extends AnAction> myChildConverter;
protected ExecutorGroupActionGroup(@NotNull ExecutorGroup<?> executorGroup,
@NotNull Function<? super Executor, ? extends AnAction> 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<Executor> 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<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> runConfigs) {
return new RunCurrentFileActionStatus(true, tooltip, icon, runConfigs);
}
private RunCurrentFileActionStatus(boolean enabled,
@Nls @NotNull String tooltip,
@NotNull Icon icon,
@NotNull List<RunnerAndConfigurationSettings> runConfigs) {
myEnabled = enabled;
myTooltip = tooltip;
myIcon = icon;
myRunConfigs = runConfigs;
}
}
}
@@ -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<String> 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);
}
}
}
@@ -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<SpinningProgressIcon> spinningIconKey = Key.create("spinning-icon-key");
private static final Key<RunCurrentFileInfo> 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<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> 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<ConfigurationFromContext> configurationsFromContext = configurationContext.createConfigurationsFromContext();
List<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> filterConfigsThatHaveRunner(@NotNull List<? extends RunnerAndConfigurationSettings> 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<RunContentDescriptor> 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<RunContentDescriptor> runningDescriptors) {
return !configuration.isAllowRunningInParallel() && !runningDescriptors.isEmpty();
}
private @NotNull List<RunContentDescriptor> getRunningDescriptors(@NotNull Project project,
@NotNull RunnerAndConfigurationSettings selectedConfiguration) {
ExecutionManagerImpl executionManager = ExecutionManagerImpl.getInstanceIfCreated(project);
if (executionManager == null) return emptyList();
List<RunContentDescriptor> 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<RunnerAndConfigurationSettings> runConfigs = getRunCurrentFileActionStatus(e, true).myRunConfigs;
if (runConfigs.isEmpty()) {
return;
}
if (runConfigs.size() == 1) {
doRunCurrentFile(project, runConfigs.get(0), e.getDataContext());
return;
}
IPopupChooserBuilder<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> myRunConfigs) {
}
private record RunCurrentFileActionStatus(
boolean enabled,
@Nls @NotNull String tooltip,
@NotNull Icon icon,
@NotNull List<RunnerAndConfigurationSettings> 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<RunnerAndConfigurationSettings> runConfigs) {
return new RunCurrentFileActionStatus(true, tooltip, icon, runConfigs);
}
}
}
@@ -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<? super Executor, ? extends AnAction> myChildConverter;
public ExecutorGroupActionGroup(@NotNull ExecutorGroup<?> executorGroup,
@NotNull Function<? super Executor, ? extends AnAction> 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<Executor> 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);
}
}
@@ -447,11 +447,11 @@ public class RunConfigurationsComboBoxAction extends ComboBoxAction implements D
List<AnAction> 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));
@@ -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);
}
}
@@ -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);
}
}
@@ -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) {
@@ -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<? super IntentionActionDescriptor>, 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();
@@ -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)
}
@@ -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 {
@@ -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<in Executor, out AnAction>, override val process: RunToolbarProcess) :
ExecutorRunToolbarAction, ExecutorRegistryImpl.ExecutorGroupActionGroup(executorGroup, childConverter) {
childConverter: Function<in Executor, out AnAction>,
override val process: RunToolbarProcess)
: ExecutorRunToolbarAction, ExecutorGroupActionGroup(executorGroup, childConverter) {
override fun getRightSideType(): RTBarAction.Type = RTBarAction.Type.RIGHT_FLEXIBLE
@@ -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
@@ -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<AnAction>(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<out AnAction> {
var suffix = emptyList<AnAction>()
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()
}
}