IDEA-188810 move command execution to RunAnythingCommandCustomizer

This commit is contained in:
Dmitry.Krasilschikov
2018-04-24 14:12:43 +03:00
parent 9b8201a713
commit 8fc232367f
8 changed files with 243 additions and 110 deletions
@@ -31,6 +31,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actions.TextComponentEditorAction;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.impl.ModifierKeyDoubleClickHandler;
import com.intellij.openapi.module.Module;
@@ -431,8 +432,9 @@ public class RunAnythingAction extends AnAction implements CustomComponentAction
return;
}
VirtualFile directory = getWorkDirectory(module);
DataContext dataContext = createDataContext(directory, null, null, module, project);
if (value instanceof RunAnythingCommandItem) {
onDone = () -> ((RunAnythingCommandItem)value).run(project, createDataContext(directory, null, null));
onDone = () -> ((RunAnythingCommandItem)value).run(dataContext);
}
else if (value == null) {
onDone = () -> RunAnythingUtil.runOrCreateRunConfiguration(myDataContext, pattern, directory);
@@ -456,14 +458,16 @@ public class RunAnythingAction extends AnAction implements CustomComponentAction
Component c = comp;
if (c == null) c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
value.run(project, createDataContext(null, c, event));
value.run(createDataContext(null, c, event, null, project));
});
}
@NotNull
private static DataContext createDataContext(@Nullable VirtualFile directory,
@Nullable Component focusOwner,
@Nullable AnActionEvent event) {
@Nullable AnActionEvent event,
@Nullable Module module,
@NotNull Project project) {
HashMap<String, Object> map = ContainerUtil.newHashMap();
if (directory != null) {
map.put(CommonDataKeys.VIRTUAL_FILE.getName(), directory);
@@ -479,6 +483,12 @@ public class RunAnythingAction extends AnAction implements CustomComponentAction
map.put(FOCUS_COMPONENT_KEY_NAME.getName(), focusOwner);
}
if (module != null) {
map.put(LangDataKeys.MODULE.getName(), module);
}
map.put(CommonDataKeys.PROJECT.getName(), project);
return SimpleDataContext.getSimpleContext(map, DataContext.EMPTY_CONTEXT);
}
@@ -3,6 +3,7 @@ package org.jetbrains.plugins.ruby.ruby.actions;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.project.Project;
import com.intellij.util.ObjectUtils;
@@ -22,9 +23,10 @@ public class RunAnythingActionItem extends RunAnythingItem<AnAction> {
}
@Override
public void run(@NotNull Project project, @NotNull DataContext dataContext) {
public void run(@NotNull DataContext dataContext) {
Component focusOwner = dataContext.getData(RunAnythingAction.FOCUS_COMPONENT_KEY_NAME);
AnActionEvent event = dataContext.getData(RunAnythingAction.RUN_ANYTHING_EVENT_KEY);
Project project = dataContext.getData(CommonDataKeys.PROJECT);
RunAnythingUtil.performRunAnythingAction(myAction, project, focusOwner, event);
}
@@ -7,38 +7,27 @@ import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
import com.intellij.execution.util.ExecUtil;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfoRt;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.execution.ParametersListUtil;
import icons.RubyIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.RBundle;
import org.jetbrains.plugins.ruby.gem.GemsDataKeys;
import org.jetbrains.plugins.ruby.remote.RubyRemoteInterpreterManager;
import org.jetbrains.plugins.ruby.ruby.RModuleUtil;
import org.jetbrains.plugins.ruby.ruby.actions.commands.RunAnythingCommandCustomizer;
import org.jetbrains.plugins.ruby.ruby.actions.execution.RunAnythingRunProfile;
import org.jetbrains.plugins.ruby.ruby.run.RubyAbstractRunner;
import org.jetbrains.plugins.ruby.rvm.RVMSupportUtil;
import org.jetbrains.plugins.ruby.utils.OSUtil;
import org.jetbrains.plugins.ruby.version.management.rbenv.gemsets.RbenvGemsetManager;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
public class RunAnythingCommandItem extends RunAnythingItem<String> {
private static final Logger LOG = Logger.getInstance(RunAnythingCommandItem.class);
@@ -54,62 +43,33 @@ public class RunAnythingCommandItem extends RunAnythingItem<String> {
}
@Override
public void run(@NotNull Project project, @NotNull DataContext dataContext) {
super.run(project, dataContext);
public void run(@NotNull DataContext dataContext) {
super.run(dataContext);
VirtualFile workDirectory = dataContext.getData(CommonDataKeys.VIRTUAL_FILE);
Executor executor = dataContext.getData(RunAnythingAction.EXECUTOR_KEY);
LOG.assertTrue(workDirectory != null);
LOG.assertTrue(executor != null);
runCommand(workDirectory, project, myCommandLine, myModule, executor);
runCommand(workDirectory, myCommandLine, executor, dataContext);
}
public static void runCommand(@NotNull VirtualFile workDirectory,
@NotNull Project project,
@NotNull String commandString,
@Nullable Module module,
@NotNull Executor executor) {
@NotNull Executor executor,
@NotNull DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
LOG.assertTrue(project != null);
Collection<String> commands = RunAnythingCache.getInstance(project).getState().getCommands();
commands.remove(commandString);
commands.add(commandString);
Sdk sdk = RModuleUtil.getInstance().findRubySdkForModule(module);
GeneralCommandLine commandLine = new GeneralCommandLine().withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE);
String command = commandString;
Map<String, String> env = ContainerUtil.newHashMap(commandLine.getEffectiveEnvironment());
if (sdk != null && !RubyRemoteInterpreterManager.getInstance().isRemoteSdk(sdk)) {
if (RVMSupportUtil.isRVMInterpreter(sdk)) {
command = getRVMAwareCommand(sdk, commandString, project);
}
else if (RbenvGemsetManager.isRbenvSdk(sdk)) {
command = getRbenvAwareCommand(sdk, env, commandString, project, module);
}
else {
command = getRubyAwareCommand(sdk, env, commandString);
}
}
List<String> shellCommand = ContainerUtil.newArrayList(getShellCommand());
if (shellCommand.isEmpty()) {
shellCommand = ParametersListUtil.parse(command, false, true);
}
else {
shellCommand.add(command);
}
commandLine = new GeneralCommandLine(shellCommand)
.withEnvironment(env)
.withWorkDirectory(workDirectory.getPath());
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put(LangDataKeys.MODULE.getName(), module);
dataMap.put(GemsDataKeys.SDK.getName(), sdk);
dataContext = RunAnythingCommandCustomizer.customizeContext(dataContext);
GeneralCommandLine commandLine = RunAnythingCommandCustomizer.customizeCommandLine(dataContext, workDirectory, commandString);
try {
ExecutionEnvironmentBuilder.create(project, executor, new RunAnythingRunProfile(commandLine, commandString))
.dataContext(SimpleDataContext.getSimpleContext(dataMap, DataContext.EMPTY_CONTEXT))
.dataContext(dataContext)
.buildAndExecute();
}
catch (ExecutionException e) {
@@ -119,7 +79,7 @@ public class RunAnythingCommandItem extends RunAnythingItem<String> {
}
@NotNull
private static List<String> getShellCommand() {
public static List<String> getShellCommand() {
if (SystemInfoRt.isWindows) return ContainerUtil.immutableList(ExecUtil.getWindowsShellName(), "/c");
String shell = System.getenv("SHELL");
@@ -137,50 +97,6 @@ public class RunAnythingCommandItem extends RunAnythingItem<String> {
return commands;
}
private static String getRubyAwareCommand(@NotNull Sdk sdk, @NotNull Map<String, String> env, @NotNull String commandLine) {
VirtualFile sdkHomeDirectory = sdk.getHomeDirectory();
if (sdkHomeDirectory == null) return commandLine;
VirtualFile parent = sdkHomeDirectory.getParent();
if (parent == null) return commandLine;
final String path = FileUtil.toSystemDependentName(parent.getPath());
final String envName = OSUtil.getPathEnvVariableName();
final String newPath = OSUtil.prependToPathEnvVariable(env.get(envName), path);
env.put(envName, newPath);
return commandLine;
}
private static String getRbenvAwareCommand(@NotNull Sdk sdk,
@NotNull Map<String, String> env,
@NotNull String commandLine,
@NotNull Project project,
@Nullable Module module) {
String exeCommand = commandLine.contains(" ") ? StringUtil.substringBefore(commandLine, " ") : commandLine;
String shimsExec = RbenvGemsetManager.getShimsCommandPath(Objects.requireNonNull(exeCommand));
if (shimsExec == null || !RunAnythingCache.getInstance(project).CAN_RUN_RBENV) return commandLine;
RubyAbstractRunner.patchRbenvEnv(env, module, sdk);
return shimsExec + (commandLine.contains(" ") ? " " + StringUtil.substringAfter(commandLine, " ") : "");
}
@NotNull
private static String getRVMAwareCommand(@NotNull Sdk sdk, @NotNull String commandLine, @NotNull Project project) {
if (commandLine.startsWith("rvm ")) return commandLine;
String version = RVMSupportUtil.getRVMSdkVersion(sdk);
String gemset = RVMSupportUtil.getGemset(sdk);
if (version == null) return commandLine;
if (gemset != null) version += '@' + gemset;
if (!RunAnythingCache.getInstance(project).CAN_RUN_RVM) return commandLine;
return "rvm " + version + " do " + commandLine;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -1,7 +1,6 @@
package org.jetbrains.plugins.ruby.ruby.actions;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -53,9 +52,9 @@ public abstract class RunAnythingItem<T> {
/**
* Executes specific action on choosing current item in the list
*
* @param dataContext Use {@link DataContext} to extract focus owner component, original action event, working directory
* @param dataContext Use {@link DataContext} to extract focus owner component, original action event, working directory, module and project
*/
public void run(@NotNull Project project, @NotNull DataContext dataContext) {
public void run(@NotNull DataContext dataContext) {
triggerUsage();
}
}
@@ -4,7 +4,9 @@ import com.intellij.execution.Executor;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.actions.ChooseRunConfigurationPopup;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
@@ -14,6 +16,7 @@ import java.awt.*;
import java.util.Objects;
public class RunAnythingRunConfigurationItem extends RunAnythingItem<ChooseRunConfigurationPopup.ItemWrapper> {
private static final Logger LOG = Logger.getInstance(RunAnythingRunConfigurationItem.class);
static final String RUN_ANYTHING_RUN_CONFIGURATION_AD_TEXT =
RunAnythingAction.AD_MODULE_CONTEXT + " , " + RunAnythingAction.AD_DEBUG_TEXT;
@NotNull private final ChooseRunConfigurationPopup.ItemWrapper myWrapper;
@@ -23,8 +26,10 @@ public class RunAnythingRunConfigurationItem extends RunAnythingItem<ChooseRunCo
}
@Override
public void run(@NotNull Project project, @NotNull DataContext dataContext) {
super.run(project, dataContext);
public void run(@NotNull DataContext dataContext) {
super.run(dataContext);
Project project = dataContext.getData(CommonDataKeys.PROJECT);
LOG.assertTrue(project != null);
Object value = myWrapper.getValue();
if (value instanceof RunnerAndConfigurationSettings) {
@@ -3,7 +3,6 @@ package org.jetbrains.plugins.ruby.ruby.actions;
import com.intellij.execution.*;
import com.intellij.execution.actions.ChooseRunConfigurationPopup;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.execution.runners.ProgramRunner;
@@ -350,7 +349,7 @@ public class RunAnythingUtil {
if (runMatchedActivity(dataContext, pattern)) return;
RunAnythingCommandItem.runCommand(workDirectory, project, StringUtil.trim(pattern), module, RunAnythingAction.getExecutor());
RunAnythingCommandItem.runCommand(workDirectory, StringUtil.trim(pattern), RunAnythingAction.getExecutor(), dataContext);
}
private static boolean runMatchedActivity(@NotNull DataContext dataContext, @NotNull String pattern) {
@@ -0,0 +1,137 @@
package org.jetbrains.plugins.ruby.ruby.actions.commands;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.execution.ParametersListUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.gem.GemsDataKeys;
import org.jetbrains.plugins.ruby.remote.RubyRemoteInterpreterManager;
import org.jetbrains.plugins.ruby.ruby.RModuleUtil;
import org.jetbrains.plugins.ruby.ruby.actions.RunAnythingCache;
import org.jetbrains.plugins.ruby.ruby.run.RubyAbstractRunner;
import org.jetbrains.plugins.ruby.rvm.RVMSupportUtil;
import org.jetbrains.plugins.ruby.utils.OSUtil;
import org.jetbrains.plugins.ruby.version.management.rbenv.gemsets.RbenvGemsetManager;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.jetbrains.plugins.ruby.ruby.actions.RunAnythingCommandItem.getShellCommand;
public class RubyRunAnythingCommandCustomizer extends RunAnythingCommandCustomizer {
private static final Logger LOG = Logger.getInstance(RubyRunAnythingCommandCustomizer.class);
@NotNull
@Override
public GeneralCommandLine customizeCommandLine(@NotNull VirtualFile workDirectory,
@NotNull DataContext dataContext,
@NotNull GeneralCommandLine commandLine) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final Module module = LangDataKeys.MODULE.getData(dataContext);
Sdk sdk = RModuleUtil.getInstance().findRubySdkForModule(module);
String command = commandLine.getExePath();
LOG.assertTrue(project != null);
Map<String, String> env = ContainerUtil.newHashMap(commandLine.getEffectiveEnvironment());
if (sdk != null && !RubyRemoteInterpreterManager.getInstance().isRemoteSdk(sdk)) {
if (RVMSupportUtil.isRVMInterpreter(sdk)) {
command = getRVMAwareCommand(sdk, command, project);
}
else if (RbenvGemsetManager.isRbenvSdk(sdk)) {
command = getRbenvAwareCommand(sdk, env, command, project, module);
}
else {
command = getRubyAwareCommand(sdk, env, command);
}
}
List<String> shellCommand = ContainerUtil.newArrayList(getShellCommand());
if (shellCommand.isEmpty()) {
shellCommand = ParametersListUtil.parse(command, false, true);
}
else {
shellCommand.add(command);
}
commandLine = new GeneralCommandLine(shellCommand)
.withEnvironment(env)
.withWorkDirectory(workDirectory.getPath());
return commandLine;
}
@NotNull
@Override
public DataContext customizeDataContext(@NotNull DataContext dataContext) {
final Module module = LangDataKeys.MODULE.getData(dataContext);
Sdk sdk = RModuleUtil.getInstance().findRubySdkForModule(module);
HashMap<String, Object> dataMap = new HashMap<>();
if (sdk != null) {
dataMap.put(GemsDataKeys.SDK.getName(), sdk);
}
return SimpleDataContext.getSimpleContext(dataMap, dataContext);
}
private static String getRubyAwareCommand(@NotNull Sdk sdk, @NotNull Map<String, String> env, @NotNull String commandLine) {
VirtualFile sdkHomeDirectory = sdk.getHomeDirectory();
if (sdkHomeDirectory == null) return commandLine;
VirtualFile parent = sdkHomeDirectory.getParent();
if (parent == null) return commandLine;
final String path = FileUtil.toSystemDependentName(parent.getPath());
final String envName = OSUtil.getPathEnvVariableName();
final String newPath = OSUtil.prependToPathEnvVariable(env.get(envName), path);
env.put(envName, newPath);
return commandLine;
}
private static String getRbenvAwareCommand(@NotNull Sdk sdk,
@NotNull Map<String, String> env,
@NotNull String commandLine,
@NotNull Project project,
@Nullable Module module) {
String exeCommand = commandLine.contains(" ") ? StringUtil.substringBefore(commandLine, " ") : commandLine;
String shimsExec = RbenvGemsetManager.getShimsCommandPath(Objects.requireNonNull(exeCommand));
if (shimsExec == null || !RunAnythingCache.getInstance(project).CAN_RUN_RBENV) return commandLine;
RubyAbstractRunner.patchRbenvEnv(env, module, sdk);
return shimsExec + (commandLine.contains(" ") ? " " + StringUtil.substringAfter(commandLine, " ") : "");
}
@NotNull
private static String getRVMAwareCommand(@NotNull Sdk sdk, @NotNull String commandLine, @NotNull Project project) {
if (commandLine.startsWith("rvm ")) return commandLine;
String version = RVMSupportUtil.getRVMSdkVersion(sdk);
String gemset = RVMSupportUtil.getGemset(sdk);
if (version == null) return commandLine;
if (gemset != null) version += '@' + gemset;
if (!RunAnythingCache.getInstance(project).CAN_RUN_RVM) return commandLine;
return "rvm " + version + " do " + commandLine;
}
}
@@ -0,0 +1,65 @@
package org.jetbrains.plugins.ruby.ruby.actions.commands;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
/**
* This class customizes 'Run Anything' command line and its data context.
* E.g. it's possible to wrap command into a shell or/and patch environment variables.
*/
public abstract class RunAnythingCommandCustomizer {
public static final ExtensionPointName<RunAnythingCommandCustomizer> EP_NAME =
ExtensionPointName.create("org.jetbrains.plugins.ruby.runAnythingCommandCustomizer");
/**
* Customizes command line and pass it to others customizers
*
* @param workDirectory the working directory the command will be executed in
* @param dataContext {@link DataContext} to fetch module, project etc.
* @param commandLine command line to be executed
* @return customized command line
*/
@NotNull
public GeneralCommandLine customizeCommandLine(@NotNull VirtualFile workDirectory,
@NotNull DataContext dataContext,
@NotNull GeneralCommandLine commandLine) {
return commandLine;
}
/**
* Customizes data context and pass it to others providers
*
* @param dataContext {@link DataContext} to fetch module, project etc.
* @return customized {@link DataContext}
*/
@NotNull
public DataContext customizeDataContext(@NotNull DataContext dataContext) {
return dataContext;
}
@NotNull
public static GeneralCommandLine customizeCommandLine(@NotNull DataContext dataContext,
@NotNull VirtualFile workDirectory,
@NotNull String command) {
GeneralCommandLine commandLine = new GeneralCommandLine(command)
.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE);
for (RunAnythingCommandCustomizer customizer : EP_NAME.getExtensions()) {
commandLine = customizer.customizeCommandLine(workDirectory, dataContext, commandLine);
}
return commandLine;
}
@NotNull
public static DataContext customizeContext(@NotNull DataContext dataContext) {
for (RunAnythingCommandCustomizer customizer : EP_NAME.getExtensions()) {
dataContext = customizer.customizeDataContext(dataContext);
}
return dataContext;
}
}