d638fcbc0e938037dcdcc875f83ac44505f3a5f0
[idea/community.git] / python / src / com / jetbrains / python / console / PydevConsoleRunner.java
1 /*
2  * Copyright 2000-2016 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.intellij.execution.configurations.GeneralCommandLine;
22 import com.intellij.execution.console.LanguageConsoleView;
23 import com.intellij.lang.ASTNode;
24 import com.intellij.openapi.module.Module;
25 import com.intellij.openapi.module.ModuleManager;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.projectRoots.Sdk;
28 import com.intellij.openapi.util.Key;
29 import com.intellij.openapi.util.Pair;
30 import com.intellij.openapi.vfs.VirtualFile;
31 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
32 import com.intellij.psi.PsiElement;
33 import com.intellij.psi.PsiFile;
34 import com.intellij.util.PathMappingSettings;
35 import com.jetbrains.python.console.completion.PydevConsoleElement;
36 import com.jetbrains.python.console.parsing.PythonConsoleData;
37 import com.jetbrains.python.console.pydev.ConsoleCommunication;
38 import com.jetbrains.python.remote.PyRemotePathMapper;
39 import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
40 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
41 import com.jetbrains.python.run.PythonCommandLineState;
42 import com.jetbrains.python.sdk.PySdkUtil;
43 import com.jetbrains.python.sdk.PythonSdkType;
44 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47
48 import java.nio.charset.Charset;
49 import java.util.Collection;
50 import java.util.Map;
51
52 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonIOEncoding;
53 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered;
54
55 /**
56  * Created by Yuli Fiterman on 9/13/2016.
57  */
58 public interface PydevConsoleRunner {
59
60   Key<ConsoleCommunication> CONSOLE_KEY = new Key<>("PYDEV_CONSOLE_KEY");
61   Key<Sdk> CONSOLE_SDK = new Key<>("PYDEV_CONSOLE_SDK_KEY");
62
63   public interface ConsoleListener {
64     void handleConsoleInitialized(LanguageConsoleView consoleView);
65   }
66
67
68   @Nullable
69   static PyRemotePathMapper getPathMapper(@NotNull Project project, Sdk sdk, PyConsoleOptions.PyConsoleSettings consoleSettings) {
70     if (PySdkUtil.isRemote(sdk)) {
71       PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
72       if (instance != null) {
73         //noinspection ConstantConditions
74         PyRemotePathMapper remotePathMapper =
75           instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null);
76
77         PathMappingSettings mappingSettings = consoleSettings.getMappingSettings();
78
79         remotePathMapper.addAll(mappingSettings.getPathMappings(), PyRemotePathMapper.PyPathMappingType.USER_DEFINED);
80
81         return remotePathMapper;
82       }
83     }
84     return null;
85   }
86
87   @NotNull
88   static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) {
89     Sdk sdk = null;
90     Module module = null;
91     PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
92     String sdkHome = settings.getSdkHome();
93     if (sdkHome != null) {
94       sdk = PythonSdkType.findSdkByPath(sdkHome);
95       if (settings.getModuleName() != null) {
96         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
97       }
98       else {
99         module = contextModule;
100         if (module == null && ModuleManager.getInstance(project).getModules().length > 0) {
101           module = ModuleManager.getInstance(project).getModules()[0];
102         }
103       }
104     }
105     if (sdk == null && settings.isUseModuleSdk()) {
106       if (contextModule != null) {
107         module = contextModule;
108       }
109       else if (settings.getModuleName() != null) {
110         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
111       }
112       if (module != null) {
113         if (PythonSdkType.findPythonSdk(module) != null) {
114           sdk = PythonSdkType.findPythonSdk(module);
115         }
116       }
117     }
118     else if (contextModule != null) {
119       if (module == null) {
120         module = contextModule;
121       }
122       if (sdk == null) {
123         sdk = PythonSdkType.findPythonSdk(module);
124       }
125     }
126
127     if (sdk == null) {
128       for (Module m : ModuleManager.getInstance(project).getModules()) {
129         if (PythonSdkType.findPythonSdk(m) != null) {
130           sdk = PythonSdkType.findPythonSdk(m);
131           module = m;
132           break;
133         }
134       }
135     }
136     if (sdk == null) {
137       if (PythonSdkType.getAllSdks().size() > 0) {
138         //noinspection UnusedAssignment
139         sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk
140       }
141     }
142     return Pair.create(sdk, module);
143   }
144
145   static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
146     final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
147       @Override
148       public String apply(String input) {
149         return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'";
150       }
151     }));
152
153     return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_ENV, path);
154   }
155
156   static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs, @NotNull Project project) {
157     setCorrectStdOutEncoding(envs, project);
158
159     PythonSdkFlavor.initPythonPath(envs, true, PythonCommandLineState.getAddedPaths(sdk));
160     return envs;
161   }
162
163   /**
164    * Add required ENV var to Python task to set its stdout charset to current project charset to allow it print correctly.
165    *
166    * @param envs    map of envs to add variable
167    * @param project current project
168    */
169   static void setCorrectStdOutEncoding(@NotNull Map<String, String> envs, @NotNull Project project) {
170     final Charset defaultCharset = EncodingProjectManager.getInstance(project).getDefaultCharset();
171     final String encoding = defaultCharset.name();
172     setPythonIOEncoding(setPythonUnbuffered(envs), encoding);
173   }
174
175   /**
176    * Set command line charset as current project charset.
177    * Add required ENV var to Python task to set its stdout charset to current project charset to allow it print correctly.
178    *
179    * @param commandLine command line
180    * @param project     current project
181    */
182   static void setCorrectStdOutEncoding(@NotNull GeneralCommandLine commandLine, @NotNull Project project) {
183     final Charset defaultCharset = EncodingProjectManager.getInstance(project).getDefaultCharset();
184     commandLine.setCharset(defaultCharset);
185     setPythonIOEncoding(commandLine.getEnvironment(), defaultCharset.name());
186   }
187
188   static boolean isInPydevConsole(PsiElement element) {
189     return element instanceof PydevConsoleElement || getConsoleCommunication(element) != null;
190   }
191
192   static boolean isPythonConsole(@Nullable ASTNode element) {
193     return getPythonConsoleData(element) != null;
194   }
195
196   @Nullable
197   static PythonConsoleData getPythonConsoleData(@Nullable ASTNode element) {
198     if (element == null || element.getPsi() == null || element.getPsi().getContainingFile() == null) {
199       return null;
200     }
201
202     VirtualFile file = PydevConsoleRunnerImpl.getConsoleFile(element.getPsi().getContainingFile());
203
204     if (file == null) {
205       return null;
206     }
207     return file.getUserData(PyConsoleUtil.PYTHON_CONSOLE_DATA);
208   }
209
210   @Nullable
211   static ConsoleCommunication getConsoleCommunication(PsiElement element) {
212     final PsiFile containingFile = element.getContainingFile();
213     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_KEY) : null;
214   }
215
216   @Nullable
217   static Sdk getConsoleSdk(PsiElement element) {
218     final PsiFile containingFile = element.getContainingFile();
219     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_SDK) : null;
220   }
221
222   void runSync();
223
224   void run();
225
226   PydevConsoleCommunication getPydevConsoleCommunication();
227
228   void addConsoleListener(PydevConsoleRunnerImpl.ConsoleListener consoleListener);
229
230   PydevConsoleExecuteActionHandler getConsoleExecuteActionHandler();
231
232   PyConsoleProcessHandler getProcessHandler();
233
234   PythonConsoleView getConsoleView();
235 }