mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
extract ExecutorRegistryImpl inner actions
GitOrigin-RevId: f4bf959f622326d06d9c05118d0eaa0b57e098e3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8919a8ea99
commit
a1283d883d
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -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));
|
||||
|
||||
+39
@@ -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);
|
||||
}
|
||||
}
|
||||
+72
@@ -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);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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();
|
||||
|
||||
+6
-5
@@ -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)
|
||||
}
|
||||
|
||||
+4
-3
@@ -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 {
|
||||
|
||||
+4
-3
@@ -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
|
||||
|
||||
|
||||
+5
-4
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user