Merge remote-tracking branch 'origin/master'
[idea/community.git] / python / src / com / jetbrains / python / console / RunPythonConsoleAction.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.console;
17
18 import com.google.common.base.Function;
19 import com.google.common.base.Joiner;
20 import com.google.common.collect.Collections2;
21 import com.google.common.collect.Maps;
22 import com.intellij.openapi.actionSystem.AnAction;
23 import com.intellij.openapi.actionSystem.AnActionEvent;
24 import com.intellij.openapi.actionSystem.CommonDataKeys;
25 import com.intellij.openapi.actionSystem.LangDataKeys;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.module.Module;
28 import com.intellij.openapi.module.ModuleManager;
29 import com.intellij.openapi.project.DumbAware;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.projectRoots.Sdk;
32 import com.intellij.openapi.roots.ModuleRootManager;
33 import com.intellij.openapi.util.Pair;
34 import com.intellij.openapi.util.text.StringUtil;
35 import com.intellij.openapi.vfs.VirtualFile;
36 import com.intellij.util.PathMappingSettings;
37 import com.jetbrains.python.buildout.BuildoutFacet;
38 import com.jetbrains.python.remote.PyRemoteSdkCredentials;
39 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
40 import com.jetbrains.python.run.PythonCommandLineState;
41 import com.jetbrains.python.sdk.PySdkUtil;
42 import com.jetbrains.python.sdk.PythonSdkType;
43 import icons.PythonIcons;
44 import org.jetbrains.annotations.NotNull;
45
46 import java.util.Collection;
47 import java.util.List;
48
49 /**
50  * @author oleg
51  */
52 public class RunPythonConsoleAction extends AnAction implements DumbAware {
53
54   public RunPythonConsoleAction() {
55     super();
56     getTemplatePresentation().setIcon(PythonIcons.Python.Python);
57   }
58
59   @Override
60   public void update(final AnActionEvent e) {
61     e.getPresentation().setVisible(true);
62     e.getPresentation().setEnabled(false);
63     final Project project = e.getData(CommonDataKeys.PROJECT);
64     if (project != null) {
65       Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, e.getData(LangDataKeys.MODULE));
66       if (sdkAndModule.first != null) {
67         e.getPresentation().setEnabled(true);
68       }
69     }
70   }
71
72   public void actionPerformed(final AnActionEvent e) {
73     final Project project = e.getData(CommonDataKeys.PROJECT);
74     runPythonConsole(project, e.getData(LangDataKeys.MODULE));
75   }
76
77   @NotNull
78   public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule) {
79     assert project != null : "Project is null";
80
81     FileDocumentManager.getInstance().saveAllDocuments();
82
83     Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, contextModule);
84
85     Module module = sdkAndModule.second;
86     Sdk sdk = sdkAndModule.first;
87
88     assert sdk != null;
89
90     PathMappingSettings mappingSettings = getMappings(project, sdk);
91
92     String[] setupFragment;
93
94     PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
95     Collection<String> pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(),
96                                                                              settingsProvider.addSourceRoots());
97
98     if (mappingSettings != null) {
99       pythonPath = mappingSettings.convertToRemote(pythonPath);
100     }
101
102     String selfPathAppend = constructPythonPathCommand(pythonPath);
103
104     String customStartScript = settingsProvider.getCustomStartScript();
105
106     if (customStartScript.trim().length() > 0) {
107       selfPathAppend += "\n" + customStartScript.trim();
108     }
109
110     String workingDir = settingsProvider.getWorkingDirectory();
111     if (StringUtil.isEmpty(workingDir)) {
112       if (module != null && ModuleRootManager.getInstance(module).getContentRoots().length > 0) {
113         workingDir = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath();
114       }
115       else {
116         if (ModuleManager.getInstance(project).getModules().length > 0) {
117           VirtualFile[] roots = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getContentRoots();
118           if (roots.length > 0) {
119             workingDir = roots[0].getPath();
120           }
121         }
122       }
123     }
124
125     if (mappingSettings != null) {
126       workingDir = mappingSettings.convertToRemote(workingDir);
127     }
128
129     BuildoutFacet facet = null;
130     if (module != null) {
131       facet = BuildoutFacet.getInstance(module);
132     }
133
134     if (facet != null) {
135       List<String> path = facet.getAdditionalPythonPath();
136       if (mappingSettings != null) {
137         path = mappingSettings.convertToRemote(path);
138       }
139       String prependStatement = facet.getPathPrependStatement(path);
140       setupFragment = new String[]{prependStatement, selfPathAppend};
141     }
142     else {
143       setupFragment = new String[]{selfPathAppend};
144     }
145
146     return PydevConsoleRunner
147       .createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, Maps.newHashMap(settingsProvider.getEnvs()), setupFragment);
148   }
149
150   public static PathMappingSettings getMappings(Project project, Sdk sdk) {
151     PathMappingSettings mappingSettings = null;
152     if (PySdkUtil.isRemote(sdk)) {
153       PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
154       if (instance != null) {
155         mappingSettings =
156           instance.setupMappings(project, (PyRemoteSdkCredentials)sdk.getSdkAdditionalData(), null);
157       }
158     }
159     return mappingSettings;
160   }
161
162   @NotNull
163   private static Pair<Sdk, Module> findPythonSdkAndModule(Project project, Module contextModule) {
164     Sdk sdk = null;
165     Module module = null;
166     PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
167     String sdkHome = settings.getSdkHome();
168     if (sdkHome != null) {
169       sdk = PythonSdkType.findSdkByPath(sdkHome);
170       if (settings.getModuleName() != null) {
171         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
172       }
173       else {
174         module = contextModule;
175         if (module == null && ModuleManager.getInstance(project).getModules().length > 0) {
176           module = ModuleManager.getInstance(project).getModules()[0];
177         }
178       }
179     }
180     if (sdk == null && settings.isUseModuleSdk()) {
181       if (contextModule != null) {
182         module = contextModule;
183       }
184       else if (settings.getModuleName() != null) {
185         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
186       }
187       if (module != null) {
188         if (PythonSdkType.findPythonSdk(module) != null) {
189           sdk = PythonSdkType.findPythonSdk(module);
190         }
191       }
192     }
193     else if (contextModule != null) {
194       if (module == null) {
195         module = contextModule;
196       }
197       if (sdk == null) {
198         sdk = PythonSdkType.findPythonSdk(module);
199       }
200     }
201
202     if (sdk == null) {
203       for (Module m : ModuleManager.getInstance(project).getModules()) {
204         if (PythonSdkType.findPythonSdk(m) != null) {
205           sdk = PythonSdkType.findPythonSdk(m);
206           module = m;
207           break;
208         }
209       }
210     }
211     if (sdk == null) {
212       if (PythonSdkType.getAllSdks().size() > 0) {
213         //noinspection UnusedAssignment
214         sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk
215       }
216     }
217     return Pair.create(sdk, module);
218   }
219
220   public static String constructPythonPathCommand(Collection<String> pythonPath) {
221     final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
222       @Override
223       public String apply(String input) {
224         return "'" + input.replace("\\", "\\\\") + "'";
225       }
226     }));
227
228     return "sys.path.extend([" + path + "])";
229   }
230 }