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