package org.jetbrains.plugins.clojure.runner;
import com.intellij.execution.CantRunException;
import com.intellij.execution.CommonProgramRunConfigurationParameters;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.TextConsoleBuilderImpl;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.util.ProgramParametersUtil;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizer;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.clojure.ClojureBundle;
import org.jetbrains.plugins.clojure.config.ClojureConfigUtil;
import org.jetbrains.plugins.clojure.file.ClojureFileType;
import org.jetbrains.plugins.clojure.psi.api.ClojureFile;
import org.jetbrains.plugins.clojure.utils.ClojureUtils;
import java.io.File;
import java.util.*;
public class ClojureScriptRunConfiguration extends ModuleBasedConfiguration<RunConfigurationModule> implements CommonProgramRunConfigurationParameters {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.clojure.runner.ClojureScriptRunConfiguration");
private ClojureScriptConfigurationFactory factory;
private String scriptPath;
private String workDir;
private String vmParams;
private String scriptParams;
private boolean runInREPL;
private boolean runMainFunction;
private final Map<String, String> envs = new com.intellij.util.containers.hash.LinkedHashMap<String, String>();
public boolean passParentEnv = true;
// private static final String JLINE_CONSOLE_RUNNER = "jline.ConsoleRunner";
public ClojureScriptRunConfiguration(ClojureScriptConfigurationFactory factory, Project project, String name) {
super(name, new RunConfigurationModule(project), factory);
this.factory = factory;
}
public Collection<Module> getValidModules() {
Module[] modules = ModuleManager.getInstance(getProject()).getModules();
ArrayList<Module> res = new ArrayList<Module>();
for (Module module : modules) {
res.add(module);
}
return res;
}
public void setWorkDir(String dir) {
workDir = dir;
}
public String getWorkDir() {
return workDir;
}
public void readExternal(Element element) throws InvalidDataException {
PathMacroManager.getInstance(getProject()).expandPaths(element);
super.readExternal(element);
readModule(element);
scriptPath = JDOMExternalizer.readString(element, "path");
vmParams = JDOMExternalizer.readString(element, "vmparams");
scriptParams = JDOMExternalizer.readString(element, "params");
workDir = JDOMExternalizer.readString(element, "workDir");
runInREPL = Boolean.parseBoolean(JDOMExternalizer.readString(element, "repl"));
runMainFunction = Boolean.parseBoolean(JDOMExternalizer.readString(element, "main"));
workDir = getWorkDir();
envs.clear();
JDOMExternalizer.readMap(element, envs, null, "env");
}
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
writeModule(element);
JDOMExternalizer.write(element, "path", scriptPath);
JDOMExternalizer.write(element, "vmparams", vmParams);
JDOMExternalizer.write(element, "params", scriptParams);
JDOMExternalizer.write(element, "workDir", workDir);
JDOMExternalizer.write(element, "repl", runInREPL);
JDOMExternalizer.write(element, "main", runMainFunction);
JDOMExternalizer.writeMap(element, envs, null, "env");
PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element);
}
public void setEnvs(@NotNull Map<String, String> envs) {
this.envs.clear();
this.envs.putAll(envs);
}
@NotNull
public Map<String, String> getEnvs() {
return envs;
}
protected ModuleBasedConfiguration createInstance() {
return new ClojureScriptRunConfiguration(factory, getConfigurationModule().getProject(), getName());
}
@NotNull
public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
return new ClojureRunConfigurationEditor();
}
private static void configureScriptSystemClassPath(final ClojureConfigUtil.RunConfigurationParameters params, final Module module) throws CantRunException {
params.setJdk(JavaParameters.getModuleJdk(module));
params.configureByModule(module, JavaParameters.CLASSES_AND_TESTS);
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
OrderEntry[] entries = moduleRootManager.getOrderEntries();
Set<VirtualFile> cpVFiles = new LinkedHashSet<VirtualFile>();
for (OrderEntry orderEntry : entries) {
// Add module sources to classpath
if (orderEntry instanceof ModuleSourceOrderEntry) {
cpVFiles.addAll(Arrays.asList(orderEntry.getFiles(OrderRootType.SOURCES)));
}
}
for (VirtualFile file : cpVFiles) {
params.getClassPath().add(file.getPath());
}
if (!ClojureConfigUtil.isClojureConfigured(module)) {
params.getClassPath().add(ClojureConfigUtil.CLOJURE_SDK);
params.setDefaultClojureJarUsed(true);
}
}
private void configureJavaParams(ClojureConfigUtil.RunConfigurationParameters params, Module module) throws CantRunException {
// Setting up classpath
configureScriptSystemClassPath(params, module);
// add user parameters
params.getVMParametersList().addParametersString(vmParams);
params.setMainClass(ClojureUtils.CLOJURE_MAIN);
}
private void configureScript(ParametersList list) {
if (runInREPL) list.add("-i");
list.add(scriptPath);
if (runInREPL) list.add("-r");
list.addParametersString(scriptParams);
}
private void configureMainFunction(ParametersList list, String namespace) {
list.add("--main");
list.add(namespace);
}
public Module getModule() {
return getConfigurationModule().getModule();
}
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
final Module module = getModule();
if (module == null) {
throw new ExecutionException("Module is not specified");
}
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
final Sdk sdk = rootManager.getSdk();
if (sdk == null || !(sdk.getSdkType() instanceof JavaSdkType)) {
throw CantRunException.noJdkForModule(getModule());
}
final Project project = module.getProject();
if (!org.jetbrains.plugins.clojure.config.ClojureConfigUtil.isClojureConfigured(module)) {
Messages.showErrorDialog(project,
ClojureBundle.message("error.running.configuration.with.error.error.message", getName(),
ClojureBundle.message("clojure.lib.is.not.attached")),
ClojureBundle.message("run.error.message.title"));
ModulesConfigurator.showDialog(project, module.getName(), ClasspathEditor.NAME);
return null;
}
final ClojureConfigUtil.RunConfigurationParameters params =
new ClojureConfigUtil.RunConfigurationParameters();
final String namespace;
if (runMainFunction) {
final VirtualFile virtualFile = VfsUtil.findFileByIoFile(new File(scriptPath), true);
if (virtualFile == null) {
showCannotDetermineNamespaceError();
return null;
}
final PsiFile file = PsiManager.getInstance(getProject()).findFile(virtualFile);
if (!(file instanceof ClojureFile)) {
showCannotDetermineNamespaceError();
return null;
}
final String ns = ((ClojureFile) file).getNamespace();
if (ns == null) {
showCannotDetermineNamespaceError();
return null;
}
namespace = ns;
} else {
namespace = null;
}
final JavaCommandLineState state = new JavaCommandLineState(environment) {
protected JavaParameters createJavaParameters() throws ExecutionException {
ProgramParametersUtil.configureConfiguration(params, ClojureScriptRunConfiguration.this);
configureJavaParams(params, module);
final ParametersList list = params.getProgramParametersList();
if (runMainFunction) {
configureMainFunction(list, namespace);
} else {
configureScript(list);
}
return params;
}
};
final TextConsoleBuilderImpl builder = new TextConsoleBuilderImpl(project) {
private final ArrayList<Filter> filters = new ArrayList<Filter>();
@Override
public ConsoleView getConsole() {
final ConsoleViewImpl view = new ConsoleViewImpl(project, false);
for (Filter filter : filters) {
view.addMessageFilter(filter);
}
return view;
}
@Override
public void addFilter(Filter filter) {
filters.add(filter);
}
};
state.setConsoleBuilder(builder);
if (params.isDefaultClojureJarUsed()) {
ClojureConfigUtil.warningDefaultClojureJar(module);
}
return state;
}
private void showCannotDetermineNamespaceError() {
Messages.showErrorDialog(getProject(),
ClojureBundle.message("error.running.configuration.with.error.error.message", getName(),
ClojureBundle.message("cannot.determine.namespace", scriptPath)),
ClojureBundle.message("run.error.message.title"));
}
public void setScriptPath(String path) {
this.scriptPath = path;
}
public String getScriptPath() {
return scriptPath;
}
public String getVmParams() {
return vmParams;
}
public String getScriptParams() {
return scriptParams;
}
public void setVmParams(String params) {
vmParams = params;
}
public void setRunInREPL(boolean isEnabled) {
runInREPL = isEnabled;
}
public void setScriptParams(String params) {
scriptParams = params;
}
public boolean getRunInREPL() {
return runInREPL;
}
public boolean getRunMainFunction() {
return runMainFunction;
}
public void setRunMainFunction(boolean b) {
runMainFunction = b;
}
public void setPassParentEnvs(boolean passParentEnvs) {
this.passParentEnv = passParentEnvs;
}
public boolean isPassParentEnvs() {
return passParentEnv;
}
public void setProgramParameters(@Nullable String s) {
LOG.error("Don't add program parameters to Clojure script run configuration. Use Script parameters instead");
}
@Nullable
public String getProgramParameters() {
return null;
}
public void setWorkingDirectory(@Nullable String value) {
workDir = value;
}
public String getWorkingDirectory() {
return workDir;
}
}