RIDER-77734 Compound run configs are duplicated if it's started out of the toolbar
[idea/community.git] / platform / lang-impl / src / com / intellij / execution / dashboard / actions / ExecutorAction.java
1 // 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.
2 package com.intellij.execution.dashboard.actions;
3
4 import com.intellij.execution.*;
5 import com.intellij.execution.compound.CompoundRunConfiguration;
6 import com.intellij.execution.compound.SettingsAndEffectiveTarget;
7 import com.intellij.execution.configurations.RunConfiguration;
8 import com.intellij.execution.configurations.RuntimeConfigurationError;
9 import com.intellij.execution.configurations.RuntimeConfigurationException;
10 import com.intellij.execution.dashboard.RunDashboardRunConfigurationNode;
11 import com.intellij.execution.process.ProcessHandler;
12 import com.intellij.execution.runToolbar.RunToolbarProcessData;
13 import com.intellij.execution.runners.ExecutionEnvironment;
14 import com.intellij.execution.runners.ProgramRunner;
15 import com.intellij.execution.ui.RunContentDescriptor;
16 import com.intellij.execution.ui.RunContentManagerImpl;
17 import com.intellij.openapi.actionSystem.AnActionEvent;
18 import com.intellij.openapi.actionSystem.Presentation;
19 import com.intellij.openapi.actionSystem.UpdateInBackground;
20 import com.intellij.openapi.progress.ProgressManager;
21 import com.intellij.openapi.project.DumbAwareAction;
22 import com.intellij.openapi.project.DumbService;
23 import com.intellij.openapi.project.IndexNotReadyException;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.util.NlsActions;
27 import com.intellij.ui.content.Content;
28 import com.intellij.util.containers.JBIterable;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 import javax.swing.*;
33 import java.util.List;
34 import java.util.function.Consumer;
35
36 import static com.intellij.execution.dashboard.actions.RunDashboardActionUtils.getLeafTargets;
37
38 /**
39  * @author konstantin.aleev
40  */
41 public abstract class ExecutorAction extends DumbAwareAction implements UpdateInBackground {
42   private static final Key<List<RunDashboardRunConfigurationNode>> RUNNABLE_LEAVES_KEY =
43     Key.create("RUNNABLE_LEAVES_KEY");
44
45   protected ExecutorAction() {
46   }
47
48   protected ExecutorAction(@NlsActions.ActionText String text, @NlsActions.ActionDescription String description, Icon icon) {
49     super(text, description, icon);
50   }
51
52   @Override
53   public void update(@NotNull AnActionEvent e) {
54     Project project = e.getProject();
55     if (project == null) {
56       update(e, false);
57       return;
58     }
59     JBIterable<RunDashboardRunConfigurationNode> targetNodes = getLeafTargets(e);
60     boolean running = targetNodes.filter(node -> {
61       Content content = node.getContent();
62       return content != null && !RunContentManagerImpl.isTerminated(content);
63     }).isNotEmpty();
64     update(e, running);
65     List<RunDashboardRunConfigurationNode> runnableLeaves = targetNodes.filter(this::canRun).toList();
66     Presentation presentation = e.getPresentation();
67     if (!runnableLeaves.isEmpty()) {
68       presentation.putClientProperty(RUNNABLE_LEAVES_KEY, runnableLeaves);
69     }
70     presentation.setEnabled(!runnableLeaves.isEmpty());
71   }
72
73   private boolean canRun(@NotNull RunDashboardRunConfigurationNode node) {
74     ProgressManager.checkCanceled();
75
76     Project project = node.getProject();
77     return canRun(node.getConfigurationSettings(),
78                   null,
79                   DumbService.isDumb(project));
80   }
81
82   private boolean canRun(RunnerAndConfigurationSettings settings, ExecutionTarget target, boolean isDumb) {
83     if (isDumb && !settings.getType().isDumbAware()) return false;
84
85     String executorId = getExecutor().getId();
86     RunConfiguration configuration = settings.getConfiguration();
87     Project project = configuration.getProject();
88     if (configuration instanceof CompoundRunConfiguration) {
89       if (ExecutionTargetManager.getInstance(project).getTargetsFor(configuration).isEmpty()) return false;
90
91       List<SettingsAndEffectiveTarget> subConfigurations =
92         ((CompoundRunConfiguration)configuration).getConfigurationsWithEffectiveRunTargets();
93       if (subConfigurations.isEmpty()) return false;
94
95       RunManager runManager = RunManager.getInstance(project);
96       for (SettingsAndEffectiveTarget subConfiguration : subConfigurations) {
97         RunnerAndConfigurationSettings subSettings = runManager.findSettings(subConfiguration.getConfiguration());
98         if (subSettings == null || !canRun(subSettings, subConfiguration.getTarget(), isDumb)) {
99           return false;
100         }
101       }
102       return true;
103     }
104
105     if (!isValid(settings)) return false;
106
107     ProgramRunner<?> runner = ProgramRunner.getRunner(executorId, configuration);
108     if (runner == null) return false;
109
110     if (target == null) {
111       target = ExecutionTargetManager.getInstance(project).findTarget(configuration);
112       if (target == null) return false;
113     }
114     else if (!ExecutionTargetManager.canRun(configuration, target)) {
115       return false;
116     }
117     return !ExecutionManager.getInstance(project).isStarting(executorId, runner.getRunnerId());
118   }
119
120   private static boolean isValid(RunnerAndConfigurationSettings settings) {
121     try {
122       settings.checkSettings(null);
123       return true;
124     }
125     catch (RuntimeConfigurationError ex) {
126       return false;
127     }
128     catch (IndexNotReadyException | RuntimeConfigurationException ex) {
129       return true;
130     }
131   }
132
133   @Override
134   public void actionPerformed(@NotNull AnActionEvent e) {
135     Project project = e.getProject();
136     if (project == null) return;
137
138     List<RunDashboardRunConfigurationNode> runnableLeaves = e.getPresentation().getClientProperty(RUNNABLE_LEAVES_KEY);
139     if (runnableLeaves == null) return;
140
141     for (RunDashboardRunConfigurationNode node : runnableLeaves) {
142       run(node.getConfigurationSettings(), null, node.getDescriptor());
143     }
144   }
145
146   private void run(RunnerAndConfigurationSettings settings, ExecutionTarget target, RunContentDescriptor descriptor) {
147     runSubProcess(settings, target, descriptor, RunToolbarProcessData.prepareBaseSettingCustomization(settings, null));
148   }
149
150   private void runSubProcess(RunnerAndConfigurationSettings settings, ExecutionTarget target, RunContentDescriptor descriptor, @Nullable Consumer<ExecutionEnvironment> envCustomization) {
151     RunConfiguration configuration = settings.getConfiguration();
152     Project project = configuration.getProject();
153     RunManager runManager = RunManager.getInstance(project);
154     if (configuration instanceof CompoundRunConfiguration) {
155       List<SettingsAndEffectiveTarget> subConfigurations =
156         ((CompoundRunConfiguration)configuration).getConfigurationsWithEffectiveRunTargets();
157       for (SettingsAndEffectiveTarget subConfiguration : subConfigurations) {
158         RunnerAndConfigurationSettings subSettings = runManager.findSettings(subConfiguration.getConfiguration());
159         if (subSettings != null) {
160           runSubProcess(subSettings, subConfiguration.getTarget(), null, envCustomization);
161         }
162       }
163     }
164     else {
165       if (target == null) {
166         target = ExecutionTargetManager.getInstance(project).findTarget(configuration);
167         assert target != null : "No target for configuration of type " + configuration.getType().getDisplayName();
168       }
169       ProcessHandler processHandler = descriptor == null ? null : descriptor.getProcessHandler();
170
171       ExecutionManager.getInstance(project).restartRunProfile(project, getExecutor(), target, settings, processHandler, RunToolbarProcessData.prepareSuppressMainSlotCustomization(project, envCustomization));
172     }
173   }
174
175   protected abstract Executor getExecutor();
176
177   protected abstract void update(@NotNull AnActionEvent e, boolean running);
178 }