2476bdc11f30df6df05f8f3155334fe7fc80ddbb
[idea/community.git] / python / src / com / jetbrains / python / console / PydevConsoleRunner.java
1 /*
2  * Copyright 2000-2015 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.CharMatcher;
19 import com.google.common.base.Function;
20 import com.google.common.base.Joiner;
21 import com.google.common.collect.Collections2;
22 import com.intellij.codeInsight.lookup.LookupManager;
23 import com.intellij.execution.ExecutionException;
24 import com.intellij.execution.ExecutionHelper;
25 import com.intellij.execution.Executor;
26 import com.intellij.execution.configurations.EncodingEnvironmentUtil;
27 import com.intellij.execution.configurations.GeneralCommandLine;
28 import com.intellij.execution.configurations.ParamsGroup;
29 import com.intellij.execution.configurations.PtyCommandLine;
30 import com.intellij.execution.console.ConsoleHistoryController;
31 import com.intellij.execution.console.LanguageConsoleView;
32 import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler;
33 import com.intellij.execution.executors.DefaultRunExecutor;
34 import com.intellij.execution.process.ProcessAdapter;
35 import com.intellij.execution.process.ProcessEvent;
36 import com.intellij.execution.process.ProcessOutputTypes;
37 import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory;
38 import com.intellij.execution.ui.RunContentDescriptor;
39 import com.intellij.icons.AllIcons;
40 import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
41 import com.intellij.internal.statistic.UsageTrigger;
42 import com.intellij.lang.ASTNode;
43 import com.intellij.openapi.actionSystem.*;
44 import com.intellij.openapi.actionSystem.ex.ActionUtil;
45 import com.intellij.openapi.application.ApplicationManager;
46 import com.intellij.openapi.application.ModalityState;
47 import com.intellij.openapi.application.Result;
48 import com.intellij.openapi.command.WriteCommandAction;
49 import com.intellij.openapi.diagnostic.Logger;
50 import com.intellij.openapi.editor.Caret;
51 import com.intellij.openapi.editor.Document;
52 import com.intellij.openapi.editor.Editor;
53 import com.intellij.openapi.editor.actionSystem.EditorAction;
54 import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
55 import com.intellij.openapi.editor.actions.SplitLineAction;
56 import com.intellij.openapi.editor.ex.EditorEx;
57 import com.intellij.openapi.fileEditor.FileDocumentManager;
58 import com.intellij.openapi.module.Module;
59 import com.intellij.openapi.module.ModuleManager;
60 import com.intellij.openapi.progress.ProgressIndicator;
61 import com.intellij.openapi.progress.ProgressManager;
62 import com.intellij.openapi.progress.Task;
63 import com.intellij.openapi.project.DumbAware;
64 import com.intellij.openapi.project.DumbAwareAction;
65 import com.intellij.openapi.project.Project;
66 import com.intellij.openapi.projectRoots.Sdk;
67 import com.intellij.openapi.ui.Messages;
68 import com.intellij.openapi.util.*;
69 import com.intellij.openapi.util.io.StreamUtil;
70 import com.intellij.openapi.util.text.StringUtil;
71 import com.intellij.openapi.vfs.CharsetToolkit;
72 import com.intellij.openapi.vfs.VirtualFile;
73 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
74 import com.intellij.psi.PsiElement;
75 import com.intellij.psi.PsiFile;
76 import com.intellij.remote.RemoteProcess;
77 import com.intellij.remote.Tunnelable;
78 import com.intellij.testFramework.LightVirtualFile;
79 import com.intellij.ui.GuiUtils;
80 import com.intellij.util.ArrayUtil;
81 import com.intellij.util.IJSwingUtilities;
82 import com.intellij.util.PathMappingSettings;
83 import com.intellij.util.TimeoutUtil;
84 import com.intellij.util.containers.ContainerUtil;
85 import com.intellij.util.net.NetUtils;
86 import com.intellij.util.ui.MessageCategory;
87 import com.intellij.util.ui.UIUtil;
88 import com.intellij.xdebugger.XDebugProcess;
89 import com.intellij.xdebugger.XDebugProcessStarter;
90 import com.intellij.xdebugger.XDebugSession;
91 import com.intellij.xdebugger.XDebuggerManager;
92 import com.jetbrains.python.PythonHelper;
93 import com.jetbrains.python.console.completion.PydevConsoleElement;
94 import com.jetbrains.python.console.parsing.PythonConsoleData;
95 import com.jetbrains.python.console.pydev.ConsoleCommunication;
96 import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
97 import com.jetbrains.python.debugger.PyDebugRunner;
98 import com.jetbrains.python.debugger.PySourcePosition;
99 import com.jetbrains.python.remote.PyRemotePathMapper;
100 import com.jetbrains.python.remote.PyRemoteProcessHandlerBase;
101 import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
102 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
103 import com.jetbrains.python.run.*;
104 import com.jetbrains.python.sdk.PySdkUtil;
105 import com.jetbrains.python.sdk.PythonSdkType;
106 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
107 import icons.PythonIcons;
108 import org.apache.xmlrpc.XmlRpcException;
109 import org.jetbrains.annotations.NotNull;
110 import org.jetbrains.annotations.Nullable;
111
112 import javax.swing.*;
113 import java.awt.*;
114 import java.awt.event.InputEvent;
115 import java.awt.event.KeyEvent;
116 import java.io.File;
117 import java.io.IOException;
118 import java.net.ServerSocket;
119 import java.nio.charset.Charset;
120 import java.util.*;
121 import java.util.List;
122
123 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonIOEncoding;
124 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered;
125
126 /**
127  * @author oleg
128  */
129 public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonConsoleView> {
130   public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
131   public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
132                                                      "sys.path.extend([" + WORKING_DIR_ENV + "])\n";
133   private static final Logger LOG = Logger.getInstance(PydevConsoleRunner.class.getName());
134   @SuppressWarnings("SpellCheckingInspection")
135   public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
136   public static final int PORTS_WAITING_TIMEOUT = 20000;
137   private static final String CONSOLE_FEATURE = "python.console";
138
139   @NotNull
140   private Sdk mySdk;
141   private GeneralCommandLine myGeneralCommandLine;
142   protected int[] myPorts;
143   private PydevConsoleCommunication myPydevConsoleCommunication;
144   private PyConsoleProcessHandler myProcessHandler;
145   protected PydevConsoleExecuteActionHandler myConsoleExecuteActionHandler;
146   private List<ConsoleListener> myConsoleListeners = ContainerUtil.createLockFreeCopyOnWriteList();
147   private final PyConsoleType myConsoleType;
148   private Map<String, String> myEnvironmentVariables;
149   private String myCommandLine;
150   @NotNull private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
151   private String[] myStatementsToExecute = ArrayUtil.EMPTY_STRING_ARRAY;
152
153   public static Key<ConsoleCommunication> CONSOLE_KEY = new Key<>("PYDEV_CONSOLE_KEY");
154
155   public static Key<Sdk> CONSOLE_SDK = new Key<>("PYDEV_CONSOLE_SDK_KEY");
156
157   private static final long APPROPRIATE_TO_WAIT = 60000;
158
159   private PyRemoteProcessHandlerBase myRemoteProcessHandlerBase;
160
161   private String myConsoleTitle = null;
162
163   public PydevConsoleRunner(@NotNull final Project project,
164                             @NotNull Sdk sdk,
165                             @NotNull final PyConsoleType consoleType,
166                             @Nullable final String workingDir,
167                             Map<String, String> environmentVariables,
168                             @NotNull
169                               PyConsoleOptions.PyConsoleSettings settingsProvider,
170                             String... statementsToExecute) {
171     super(project, consoleType.getTitle(), workingDir);
172     mySdk = sdk;
173     myConsoleType = consoleType;
174     myEnvironmentVariables = environmentVariables;
175     myConsoleSettings = settingsProvider;
176     myStatementsToExecute = statementsToExecute;
177   }
178
179   @Nullable
180   public static PyRemotePathMapper getPathMapper(@NotNull Project project, Sdk sdk, PyConsoleOptions.PyConsoleSettings consoleSettings) {
181     if (PySdkUtil.isRemote(sdk)) {
182       PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
183       if (instance != null) {
184         //noinspection ConstantConditions
185         PyRemotePathMapper remotePathMapper =
186           instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null);
187
188         PathMappingSettings mappingSettings = consoleSettings.getMappingSettings();
189
190         remotePathMapper.addAll(mappingSettings.getPathMappings(), PyRemotePathMapper.PyPathMappingType.USER_DEFINED);
191
192         return remotePathMapper;
193       }
194     }
195     return null;
196   }
197
198   @NotNull
199   public static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) {
200     Sdk sdk = null;
201     Module module = null;
202     PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
203     String sdkHome = settings.getSdkHome();
204     if (sdkHome != null) {
205       sdk = PythonSdkType.findSdkByPath(sdkHome);
206       if (settings.getModuleName() != null) {
207         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
208       }
209       else {
210         module = contextModule;
211         if (module == null && ModuleManager.getInstance(project).getModules().length > 0) {
212           module = ModuleManager.getInstance(project).getModules()[0];
213         }
214       }
215     }
216     if (sdk == null && settings.isUseModuleSdk()) {
217       if (contextModule != null) {
218         module = contextModule;
219       }
220       else if (settings.getModuleName() != null) {
221         module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName());
222       }
223       if (module != null) {
224         if (PythonSdkType.findPythonSdk(module) != null) {
225           sdk = PythonSdkType.findPythonSdk(module);
226         }
227       }
228     }
229     else if (contextModule != null) {
230       if (module == null) {
231         module = contextModule;
232       }
233       if (sdk == null) {
234         sdk = PythonSdkType.findPythonSdk(module);
235       }
236     }
237
238     if (sdk == null) {
239       for (Module m : ModuleManager.getInstance(project).getModules()) {
240         if (PythonSdkType.findPythonSdk(m) != null) {
241           sdk = PythonSdkType.findPythonSdk(m);
242           module = m;
243           break;
244         }
245       }
246     }
247     if (sdk == null) {
248       if (PythonSdkType.getAllSdks().size() > 0) {
249         //noinspection UnusedAssignment
250         sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk
251       }
252     }
253     return Pair.create(sdk, module);
254   }
255
256   public static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
257     final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
258       @Override
259       public String apply(String input) {
260         return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'";
261       }
262     }));
263
264     return command.replace(WORKING_DIR_ENV, path);
265   }
266
267   public static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs, @NotNull Project project) {
268     setCorrectStdOutEncoding(envs, project);
269
270     PythonSdkFlavor.initPythonPath(envs, true, PythonCommandLineState.getAddedPaths(sdk));
271     return envs;
272   }
273
274   /**
275    * Add required ENV var to Python task to set its stdout charset to current project charset to allow it print correctly.
276    *
277    * @param envs    map of envs to add variable
278    * @param project current project
279    */
280   public static void setCorrectStdOutEncoding(@NotNull final Map<String, String> envs, @NotNull final Project project) {
281     final Charset defaultCharset = getProjectDefaultCharset(project);
282     final String encoding = defaultCharset.name();
283     setPythonIOEncoding(setPythonUnbuffered(envs), encoding);
284   }
285
286   /**
287    * Set command line charset as current project charset.
288    * Add required ENV var to Python task to set its stdout charset to current project charset to allow it print correctly.
289    *
290    * @param commandLine command line
291    * @param project     current project
292    */
293   public static void setCorrectStdOutEncoding(@NotNull GeneralCommandLine commandLine, @NotNull final Project project) {
294     final Charset defaultCharset = getProjectDefaultCharset(project);
295     commandLine.setCharset(defaultCharset);
296     setPythonIOEncoding(commandLine.getEnvironment(), defaultCharset.name());
297   }
298
299   @NotNull
300   private static Charset getProjectDefaultCharset(@NotNull Project project) {
301     return EncodingProjectManager.getInstance(project).getDefaultCharset();
302   }
303
304   @Override
305   protected List<AnAction> fillToolBarActions(final DefaultActionGroup toolbarActions,
306                                               final Executor defaultExecutor,
307                                               final RunContentDescriptor contentDescriptor) {
308     AnAction backspaceHandlingAction = createBackspaceHandlingAction();
309     //toolbarActions.add(backspaceHandlingAction);
310     AnAction interruptAction = createInterruptAction();
311
312     AnAction rerunAction = createRerunAction();
313     toolbarActions.add(rerunAction);
314
315     List<AnAction> actions = super.fillToolBarActions(toolbarActions, defaultExecutor, contentDescriptor);
316
317     actions.add(0, rerunAction);
318
319     actions.add(backspaceHandlingAction);
320     actions.add(interruptAction);
321     actions.add(createTabCompletionAction());
322
323     actions.add(createSplitLineAction());
324
325     AnAction showVarsAction = new ShowVarsAction();
326     toolbarActions.add(showVarsAction);
327     toolbarActions.add(ConsoleHistoryController.getController(getConsoleView()).getBrowseHistory());
328
329     toolbarActions.add(new ConnectDebuggerAction());
330
331     toolbarActions.add(new NewConsoleAction());
332
333     return actions;
334   }
335
336   public void runSync() {
337     myPorts = findAvailablePorts(getProject(), myConsoleType);
338
339     assert myPorts != null;
340
341     myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, getWorkingDir(), myPorts);
342     myCommandLine = myGeneralCommandLine.getCommandLineString();
343
344     try {
345       super.initAndRun();
346     }
347     catch (ExecutionException e) {
348       LOG.warn("Error running console", e);
349       ExecutionHelper.showErrors(getProject(), Arrays.<Exception>asList(e), "Python Console", null);
350     }
351
352     ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to console", false) {
353       @Override
354       public void run(@NotNull final ProgressIndicator indicator) {
355         indicator.setText("Connecting to console...");
356         connect(myStatementsToExecute);
357       }
358     });
359   }
360
361   /**
362    * Opens console
363    */
364   public void open() {
365     run();
366   }
367
368
369   /**
370    * Creates new console tab
371    */
372   public void createNewConsole() {
373     run();
374   }
375
376   public void run() {
377     ApplicationManager.getApplication().invokeAndWait(() -> FileDocumentManager.getInstance().saveAllDocuments());
378
379     myPorts = findAvailablePorts(getProject(), myConsoleType);
380
381     assert myPorts != null;
382
383     myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, getWorkingDir(), myPorts);
384     myCommandLine = myGeneralCommandLine.getCommandLineString();
385
386     UIUtil
387       .invokeLaterIfNeeded(() -> ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to Console", false) {
388         @Override
389         public void run(@NotNull final ProgressIndicator indicator) {
390           indicator.setText("Connecting to console...");
391           try {
392             initAndRun(myStatementsToExecute);
393           }
394           catch (final Exception e) {
395             LOG.warn("Error running console", e);
396             UIUtil.invokeAndWaitIfNeeded(new Runnable() {
397               @Override
398               public void run() {
399                 showErrorsInConsole(e);
400               }
401             });
402           }
403         }
404       }));
405   }
406
407   private void showErrorsInConsole(Exception e) {
408     final Executor defaultExecutor = DefaultRunExecutor.getRunExecutorInstance();
409
410     DefaultActionGroup actionGroup = new DefaultActionGroup(createRerunAction());
411
412     final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN,
413                                                                                         actionGroup, false);
414
415     // Runner creating
416     final JPanel panel = new JPanel(new BorderLayout());
417     panel.add(actionToolbar.getComponent(), BorderLayout.WEST);
418
419     NewErrorTreeViewPanel errorViewPanel = new NewErrorTreeViewPanel(getProject(), null, false, false, null);
420
421     String[] messages = StringUtil.isNotEmpty(e.getMessage()) ? StringUtil.splitByLines(e.getMessage()) : ArrayUtil.EMPTY_STRING_ARRAY;
422     if (messages.length == 0) {
423       messages = new String[]{"Unknown error"};
424     }
425
426     errorViewPanel.addMessage(MessageCategory.ERROR, messages, null, -1, -1, null);
427     panel.add(errorViewPanel, BorderLayout.CENTER);
428
429
430     final RunContentDescriptor contentDescriptor =
431       new RunContentDescriptor(null, myProcessHandler, panel, "Error running console");
432
433     actionGroup.add(createCloseAction(defaultExecutor, contentDescriptor));
434
435     showConsole(defaultExecutor, contentDescriptor);
436   }
437
438   private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) {
439     final int[] ports;
440     try {
441       // File "pydev/console/pydevconsole.py", line 223, in <module>
442       // port, client_port = sys.argv[1:3]
443       ports = NetUtils.findAvailableSocketPorts(2);
444     }
445     catch (IOException e) {
446       ExecutionHelper.showErrors(project, Arrays.<Exception>asList(e), consoleType.getTitle(), null);
447       return null;
448     }
449     return ports;
450   }
451
452   protected GeneralCommandLine createCommandLine(@NotNull final Sdk sdk,
453                                                  @NotNull final Map<String, String> environmentVariables,
454                                                  String workingDir, int[] ports) {
455     return doCreateConsoleCmdLine(sdk, environmentVariables, workingDir, ports, PythonHelper.CONSOLE);
456   }
457
458   @NotNull
459   protected GeneralCommandLine doCreateConsoleCmdLine(Sdk sdk,
460                                                       Map<String, String> environmentVariables,
461                                                       String workingDir,
462                                                       int[] ports,
463                                                       PythonHelper helper) {
464     GeneralCommandLine cmd =
465       PythonCommandLineState.createPythonCommandLine(getProject(), new PythonConsoleRunParams(myConsoleSettings, workingDir, sdk,
466                                                                                               environmentVariables), false,
467                                                      PtyCommandLine.isEnabled() && !SystemInfo.isWindows);
468     cmd.withWorkDirectory(getWorkingDir());
469
470     ParamsGroup group = cmd.getParametersList().getParamsGroup(PythonCommandLineState.GROUP_SCRIPT);
471     helper.addToGroup(group, cmd);
472
473     for (int port : ports) {
474       group.addParameter(String.valueOf(port));
475     }
476
477     return cmd;
478   }
479
480   @Override
481   protected PythonConsoleView createConsoleView() {
482     PythonConsoleView consoleView = new PythonConsoleView(getProject(), getConsoleTitle(), mySdk);
483     myPydevConsoleCommunication.setConsoleFile(consoleView.getVirtualFile());
484     consoleView.addMessageFilter(new PythonTracebackFilter(getProject()));
485     return consoleView;
486   }
487
488   @Override
489   protected Process createProcess() throws ExecutionException {
490     if (PySdkUtil.isRemote(mySdk)) {
491       PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
492       if (manager != null) {
493         UsageTrigger.trigger(CONSOLE_FEATURE + ".remote");
494         return createRemoteConsoleProcess(manager, myGeneralCommandLine.getParametersList().getArray(),
495                                           myGeneralCommandLine.getEnvironment(), myGeneralCommandLine.getWorkDirectory());
496       }
497       throw new PythonRemoteInterpreterManager.PyRemoteInterpreterExecutionException();
498     }
499     else {
500       myCommandLine = myGeneralCommandLine.getCommandLineString();
501       Map<String, String> envs = myGeneralCommandLine.getEnvironment();
502       EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myGeneralCommandLine.getCharset());
503
504       UsageTrigger.trigger(CONSOLE_FEATURE + ".local");
505       final Process server = myGeneralCommandLine.createProcess();
506
507       try {
508         myPydevConsoleCommunication = new PydevConsoleCommunication(getProject(), myPorts[0], server, myPorts[1]);
509       }
510       catch (Exception e) {
511         throw new ExecutionException(e.getMessage());
512       }
513       return server;
514     }
515   }
516
517   private RemoteProcess createRemoteConsoleProcess(PythonRemoteInterpreterManager manager,
518                                                    String[] command,
519                                                    Map<String, String> env,
520                                                    File workDirectory)
521     throws ExecutionException {
522     PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
523     assert data != null;
524
525     GeneralCommandLine commandLine = new GeneralCommandLine();
526
527     commandLine.setWorkDirectory(workDirectory);
528
529     commandLine.withParameters(command);
530
531     commandLine.getEnvironment().putAll(env);
532
533     commandLine.getParametersList().set(0, PythonRemoteInterpreterManager.toSystemDependent(new File(data.getHelpersPath(),
534                                                                                                      PYDEV_PYDEVCONSOLE_PY)
535                                                                                               .getPath(),
536                                                                                             PySourcePosition.isWindowsPath(
537                                                                                               data.getInterpreterPath())
538     ));
539     commandLine.getParametersList().set(1, "0");
540     commandLine.getParametersList().set(2, "0");
541
542     try {
543       PyRemotePathMapper pathMapper = getPathMapper(getProject(), mySdk, myConsoleSettings);
544
545       assert pathMapper != null;
546
547       commandLine.putUserData(PyRemoteProcessStarter.OPEN_FOR_INCOMING_CONNECTION, true);
548
549       myRemoteProcessHandlerBase = PyRemoteProcessStarterManagerUtil
550         .getManager(data).startRemoteProcess(getProject(), commandLine, manager, data,
551                                              pathMapper);
552
553       myCommandLine = myRemoteProcessHandlerBase.getCommandLine();
554
555       RemoteProcess remoteProcess = myRemoteProcessHandlerBase.getProcess();
556
557       Couple<Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
558
559       if (remoteProcess instanceof Tunnelable) {
560         Tunnelable tunnelableProcess = (Tunnelable)remoteProcess;
561         tunnelableProcess.addLocalTunnel(myPorts[0], remotePorts.first);
562         tunnelableProcess.addRemoteTunnel(remotePorts.second, "localhost", myPorts[1]);
563
564         if (LOG.isDebugEnabled()) {
565           LOG.debug(String.format("Using tunneled communication for Python console: port %d (=> %d) on IDE side, " +
566                                   "port %d (=> %d) on pydevconsole.py side", myPorts[1], remotePorts.second, myPorts[0],
567                                   remotePorts.first));
568         }
569
570         myPydevConsoleCommunication = new PydevRemoteConsoleCommunication(getProject(), myPorts[0], remoteProcess, myPorts[1]);
571       }
572       else {
573         if (LOG.isDebugEnabled()) {
574           LOG.debug(String.format("Using direct communication for Python console: port %d on IDE side, port %d on pydevconsole.py side",
575                                   remotePorts.second, remotePorts.first));
576         }
577
578         myPydevConsoleCommunication =
579           new PydevRemoteConsoleCommunication(getProject(), remotePorts.first, remoteProcess, remotePorts.second);
580       }
581
582       return remoteProcess;
583     }
584     catch (Exception e) {
585       throw new ExecutionException(e.getMessage(), e);
586     }
587   }
588
589   private static Couple<Integer> getRemotePortsFromProcess(RemoteProcess process) throws ExecutionException {
590     Scanner s = new Scanner(process.getInputStream());
591
592     return Couple.of(readInt(s, process), readInt(s, process));
593   }
594
595   private static int readInt(Scanner s, Process process) throws ExecutionException {
596     long started = System.currentTimeMillis();
597
598     StringBuilder sb = new StringBuilder();
599     boolean flag = false;
600
601     while (System.currentTimeMillis() - started < PORTS_WAITING_TIMEOUT) {
602       if (s.hasNextLine()) {
603         String line = s.nextLine();
604         sb.append(line).append("\n");
605         try {
606           int i = Integer.parseInt(line);
607           if (flag) {
608             LOG.warn("Unexpected strings in output:\n" + sb.toString());
609           }
610           return i;
611         }
612         catch (NumberFormatException ignored) {
613           flag = true;
614           continue;
615         }
616       }
617
618       TimeoutUtil.sleep(200);
619
620       if (process.exitValue() != 0) {
621         String error;
622         try {
623           error = "Console process terminated with error:\n" + StreamUtil.readText(process.getErrorStream()) + sb.toString();
624         }
625         catch (Exception ignored) {
626           error = "Console process terminated with exit code " + process.exitValue() + ", output:" + sb.toString();
627         }
628         throw new ExecutionException(error);
629       }
630       else {
631         break;
632       }
633     }
634
635     throw new ExecutionException("Couldn't read integer value from stream");
636   }
637
638   @Override
639   protected PyConsoleProcessHandler createProcessHandler(final Process process) {
640     if (PySdkUtil.isRemote(mySdk)) {
641       PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
642       if (manager != null) {
643         PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
644         assert data != null;
645         myProcessHandler =
646           manager.createConsoleProcessHandler((RemoteProcess)process, getConsoleView(), myPydevConsoleCommunication,
647                                               myCommandLine, CharsetToolkit.UTF8_CHARSET,
648                                               manager.setupMappings(getProject(), data, null),
649                                               myRemoteProcessHandlerBase.getRemoteSocketToLocalHostProvider());
650       }
651       else {
652         LOG.error("Can't create remote console process handler");
653       }
654     }
655     else {
656       myProcessHandler = new PyConsoleProcessHandler(process, getConsoleView(), myPydevConsoleCommunication, myCommandLine,
657                                                      CharsetToolkit.UTF8_CHARSET);
658     }
659     return myProcessHandler;
660   }
661
662   public void initAndRun(final String... statements2execute) throws ExecutionException {
663     super.initAndRun();
664
665     connect(statements2execute);
666   }
667
668   public void connect(final String[] statements2execute) {
669     if (handshake()) {
670       ApplicationManager.getApplication().invokeLater(() -> {
671         // Propagate console communication to language console
672         final PythonConsoleView consoleView = getConsoleView();
673
674         consoleView.setConsoleCommunication(myPydevConsoleCommunication);
675         consoleView.setSdk(mySdk);
676         consoleView.setExecutionHandler(myConsoleExecuteActionHandler);
677         myProcessHandler.addProcessListener(new ProcessAdapter() {
678           @Override
679           public void onTextAvailable(ProcessEvent event, Key outputType) {
680             consoleView.print(event.getText(), outputType);
681           }
682         });
683
684         enableConsoleExecuteAction();
685
686         for (String statement : statements2execute) {
687           consoleView.executeStatement(statement + "\n", ProcessOutputTypes.SYSTEM);
688         }
689
690         fireConsoleInitializedEvent(consoleView);
691       });
692     }
693     else {
694       getConsoleView().print("Couldn't connect to console process.", ProcessOutputTypes.STDERR);
695       myProcessHandler.destroyProcess();
696       finishConsole();
697     }
698   }
699
700   @Override
701   protected String constructConsoleTitle(@NotNull String consoleTitle) {
702     if (myConsoleTitle == null) {
703       myConsoleTitle = super.constructConsoleTitle(consoleTitle);
704     }
705     return myConsoleTitle;
706   }
707
708   protected AnAction createRerunAction() {
709     return new RestartAction(this);
710   }
711
712   private AnAction createInterruptAction() {
713     AnAction anAction = new AnAction() {
714       @Override
715       public void actionPerformed(final AnActionEvent e) {
716         if (myPydevConsoleCommunication.isExecuting()) {
717           getConsoleView().print("^C", ProcessOutputTypes.SYSTEM);
718         }
719         myPydevConsoleCommunication.interrupt();
720       }
721
722       @Override
723       public void update(final AnActionEvent e) {
724         EditorEx consoleEditor = getConsoleView().getConsoleEditor();
725         boolean enabled = IJSwingUtilities.hasFocus(consoleEditor.getComponent()) && !consoleEditor.getSelectionModel().hasSelection();
726         e.getPresentation().setEnabled(enabled);
727       }
728     };
729     anAction
730       .registerCustomShortcutSet(KeyEvent.VK_C, InputEvent.CTRL_MASK, getConsoleView().getConsoleEditor().getComponent());
731     anAction.getTemplatePresentation().setVisible(false);
732     return anAction;
733   }
734
735   private AnAction createTabCompletionAction() {
736     final AnAction runCompletions = new AnAction() {
737       @Override
738       public void actionPerformed(AnActionEvent e) {
739
740         Editor editor = getConsoleView().getConsoleEditor();
741         if (LookupManager.getActiveLookup(editor) != null) {
742           AnAction replace = ActionManager.getInstance().getAction("EditorChooseLookupItemReplace");
743           ActionUtil.performActionDumbAware(replace, e);
744           return;
745         }
746         AnAction completionAction = ActionManager.getInstance().getAction("CodeCompletion");
747         if (completionAction == null) {
748           return;
749         }
750         ActionUtil.performActionDumbAware(completionAction, e);
751       }
752
753       @Override
754       public void update(AnActionEvent e) {
755         Editor editor = getConsoleView().getConsoleEditor();
756         if (LookupManager.getActiveLookup(editor) != null) {
757           e.getPresentation().setEnabled(false);
758         }
759         int offset = editor.getCaretModel().getOffset();
760         Document document = editor.getDocument();
761         int lineStart = document.getLineStartOffset(document.getLineNumber(offset));
762         String textToCursor = document.getText(new TextRange(lineStart, offset));
763         e.getPresentation().setEnabled(!CharMatcher.WHITESPACE.matchesAllOf(textToCursor));
764       }
765     };
766
767     runCompletions
768       .registerCustomShortcutSet(KeyEvent.VK_TAB, 0, getConsoleView().getConsoleEditor().getComponent());
769     runCompletions.getTemplatePresentation().setVisible(false);
770     return runCompletions;
771   }
772
773
774
775
776
777   private AnAction createBackspaceHandlingAction() {
778     final AnAction upAction = new AnAction() {
779       @Override
780       public void actionPerformed(final AnActionEvent e) {
781         new WriteCommandAction(getConsoleView().getProject(), getConsoleView().getFile()) {
782           @Override
783           protected void run(@NotNull final Result result) throws Throwable {
784             String text = getConsoleView().getEditorDocument().getText();
785             String newText = text.substring(0, text.length() - myConsoleExecuteActionHandler.getPythonIndent());
786             getConsoleView().getEditorDocument().setText(newText);
787             getConsoleView().getConsoleEditor().getCaretModel().moveToOffset(newText.length());
788           }
789         }.execute();
790       }
791
792       @Override
793       public void update(final AnActionEvent e) {
794         e.getPresentation()
795           .setEnabled(myConsoleExecuteActionHandler.getCurrentIndentSize() >= myConsoleExecuteActionHandler.getPythonIndent() &&
796                       isIndentSubstring(getConsoleView().getEditorDocument().getText()));
797       }
798     };
799     upAction.registerCustomShortcutSet(KeyEvent.VK_BACK_SPACE, 0, null);
800     upAction.getTemplatePresentation().setVisible(false);
801     return upAction;
802   }
803
804   private boolean isIndentSubstring(String text) {
805     int indentSize = myConsoleExecuteActionHandler.getPythonIndent();
806     return text.length() >= indentSize && CharMatcher.WHITESPACE.matchesAllOf(text.substring(text.length() - indentSize));
807   }
808
809   private void enableConsoleExecuteAction() {
810     myConsoleExecuteActionHandler.setEnabled(true);
811   }
812
813   private boolean handshake() {
814     boolean res;
815     long started = System.currentTimeMillis();
816     do {
817       try {
818         res = myPydevConsoleCommunication.handshake();
819       }
820       catch (XmlRpcException ignored) {
821         res = false;
822       }
823       if (res) {
824         break;
825       }
826       else {
827         long now = System.currentTimeMillis();
828         if (now - started > APPROPRIATE_TO_WAIT) {
829           break;
830         }
831         else {
832           TimeoutUtil.sleep(100);
833         }
834       }
835     }
836     while (true);
837     return res;
838   }
839
840   @Override
841   protected AnAction createStopAction() {
842     final AnAction generalStopAction = super.createStopAction();
843     return createConsoleStoppingAction(generalStopAction);
844   }
845
846   @Override
847   protected AnAction createCloseAction(Executor defaultExecutor, final RunContentDescriptor descriptor) {
848     final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, descriptor);
849
850     final AnAction stopAction = new DumbAwareAction() {
851       @Override
852       public void update(AnActionEvent e) {
853         generalCloseAction.update(e);
854       }
855
856       @Override
857       public void actionPerformed(AnActionEvent e) {
858         e = stopConsole(e);
859
860         clearContent(descriptor);
861
862         generalCloseAction.actionPerformed(e);
863       }
864     };
865     stopAction.copyFrom(generalCloseAction);
866     return stopAction;
867   }
868
869   protected void clearContent(RunContentDescriptor descriptor) {
870   }
871
872   private AnAction createConsoleStoppingAction(final AnAction generalStopAction) {
873     final AnAction stopAction = new DumbAwareAction() {
874       @Override
875       public void update(AnActionEvent e) {
876         generalStopAction.update(e);
877       }
878
879       @Override
880       public void actionPerformed(AnActionEvent e) {
881         e = stopConsole(e);
882
883         generalStopAction.actionPerformed(e);
884       }
885     };
886     stopAction.copyFrom(generalStopAction);
887     return stopAction;
888   }
889
890   private AnActionEvent stopConsole(AnActionEvent e) {
891     if (myPydevConsoleCommunication != null) {
892       e = new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(),
893                             e.getPresentation(), e.getActionManager(), e.getModifiers());
894       try {
895         closeCommunication();
896         // waiting for REPL communication before destroying process handler
897         Thread.sleep(300);
898       }
899       catch (Exception ignored) {
900         // Ignore
901       }
902     }
903     return e;
904   }
905
906   protected AnAction createSplitLineAction() {
907
908     class ConsoleSplitLineAction extends EditorAction {
909
910       private static final String CONSOLE_SPLIT_LINE_ACTION_ID = "Console.SplitLine";
911
912       public ConsoleSplitLineAction() {
913         super(new EditorWriteActionHandler() {
914
915           private final SplitLineAction mySplitLineAction = new SplitLineAction();
916
917           @Override
918           public boolean isEnabled(Editor editor, DataContext dataContext) {
919             return mySplitLineAction.getHandler().isEnabled(editor, dataContext);
920           }
921
922           @Override
923           public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
924             ((EditorWriteActionHandler)mySplitLineAction.getHandler()).executeWriteAction(editor, caret, dataContext);
925             editor.getCaretModel().getCurrentCaret().moveCaretRelatively(0, 1, false, true);
926           }
927         });
928       }
929
930       public void setup() {
931         EmptyAction.setupAction(this, CONSOLE_SPLIT_LINE_ACTION_ID, null);
932       }
933     }
934
935     ConsoleSplitLineAction action = new ConsoleSplitLineAction();
936     action.setup();
937     return action;
938   }
939
940   private void closeCommunication() {
941     if (!myProcessHandler.isProcessTerminated()) {
942       myPydevConsoleCommunication.close();
943     }
944   }
945
946   @NotNull
947   @Override
948   protected ProcessBackedConsoleExecuteActionHandler createExecuteActionHandler() {
949     myConsoleExecuteActionHandler =
950       new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), myPydevConsoleCommunication);
951     myConsoleExecuteActionHandler.setEnabled(false);
952     new ConsoleHistoryController(myConsoleType.getTypeId(), "", getConsoleView()).install();
953     return myConsoleExecuteActionHandler;
954   }
955
956   public PydevConsoleCommunication getPydevConsoleCommunication() {
957     return myPydevConsoleCommunication;
958   }
959
960   public static boolean isInPydevConsole(final PsiElement element) {
961     return element instanceof PydevConsoleElement || getConsoleCommunication(element) != null;
962   }
963
964   public static boolean isPythonConsole(@Nullable ASTNode element) {
965     return getPythonConsoleData(element) != null;
966   }
967
968   @Nullable
969   public static PythonConsoleData getPythonConsoleData(@Nullable ASTNode element) {
970     if (element == null || element.getPsi() == null || element.getPsi().getContainingFile() == null) {
971       return null;
972     }
973
974     VirtualFile file = getConsoleFile(element.getPsi().getContainingFile());
975
976     if (file == null) {
977       return null;
978     }
979     return file.getUserData(PyConsoleUtil.PYTHON_CONSOLE_DATA);
980   }
981
982   private static VirtualFile getConsoleFile(PsiFile psiFile) {
983     VirtualFile file = psiFile.getViewProvider().getVirtualFile();
984     if (file instanceof LightVirtualFile) {
985       file = ((LightVirtualFile)file).getOriginalFile();
986     }
987     return file;
988   }
989
990   @Nullable
991   public static ConsoleCommunication getConsoleCommunication(final PsiElement element) {
992     final PsiFile containingFile = element.getContainingFile();
993     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_KEY) : null;
994   }
995
996   @Nullable
997   public static Sdk getConsoleSdk(final PsiElement element) {
998     final PsiFile containingFile = element.getContainingFile();
999     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_SDK) : null;
1000   }
1001
1002   @Override
1003   protected boolean shouldAddNumberToTitle() {
1004     return true;
1005   }
1006
1007   public void addConsoleListener(ConsoleListener consoleListener) {
1008     myConsoleListeners.add(consoleListener);
1009   }
1010
1011   public void removeConsoleListener(ConsoleListener consoleListener) {
1012     myConsoleListeners.remove(consoleListener);
1013   }
1014
1015   private void fireConsoleInitializedEvent(LanguageConsoleView consoleView) {
1016     for (ConsoleListener listener : myConsoleListeners) {
1017       listener.handleConsoleInitialized(consoleView);
1018     }
1019   }
1020
1021
1022   public interface ConsoleListener {
1023     void handleConsoleInitialized(LanguageConsoleView consoleView);
1024   }
1025
1026
1027   private static class RestartAction extends AnAction {
1028     private PydevConsoleRunner myConsoleRunner;
1029
1030
1031     private RestartAction(PydevConsoleRunner runner) {
1032       copyFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN));
1033       getTemplatePresentation().setIcon(AllIcons.Actions.Restart);
1034       myConsoleRunner = runner;
1035     }
1036
1037     @Override
1038     public void actionPerformed(AnActionEvent e) {
1039       myConsoleRunner.rerun();
1040     }
1041   }
1042
1043   private void rerun() {
1044     new Task.Backgroundable(getProject(), "Restarting Console", true) {
1045       @Override
1046       public void run(@NotNull ProgressIndicator indicator) {
1047         if (myProcessHandler != null) {
1048           UIUtil.invokeLaterIfNeeded(() -> closeCommunication());
1049
1050           myProcessHandler.waitFor();
1051         }
1052
1053         GuiUtils.invokeLaterIfNeeded(() -> PydevConsoleRunner.this.run(), ModalityState.defaultModalityState());
1054       }
1055     }.queue();
1056   }
1057
1058   private class ShowVarsAction extends ToggleAction implements DumbAware {
1059     private boolean mySelected = false;
1060
1061     public ShowVarsAction() {
1062       super("Show Variables", "Shows active console variables", AllIcons.Debugger.Watches);
1063     }
1064
1065     @Override
1066     public boolean isSelected(AnActionEvent e) {
1067       return mySelected;
1068     }
1069
1070     @Override
1071     public void setSelected(AnActionEvent e, boolean state) {
1072       mySelected = state;
1073
1074       if (mySelected) {
1075         getConsoleView().showVariables(myPydevConsoleCommunication);
1076       }
1077       else {
1078         getConsoleView().restoreWindow();
1079       }
1080     }
1081   }
1082
1083
1084   private class ConnectDebuggerAction extends ToggleAction implements DumbAware {
1085     private boolean mySelected = false;
1086     private XDebugSession mySession = null;
1087
1088     public ConnectDebuggerAction() {
1089       super("Attach Debugger", "Enables tracing of code executed in console", AllIcons.Actions.StartDebugger);
1090     }
1091
1092     @Override
1093     public boolean isSelected(AnActionEvent e) {
1094       return mySelected;
1095     }
1096
1097     @Override
1098     public void update(AnActionEvent e) {
1099       if (mySession != null) {
1100         e.getPresentation().setEnabled(false);
1101       }
1102       else {
1103         e.getPresentation().setEnabled(true);
1104       }
1105     }
1106
1107     @Override
1108     public void setSelected(AnActionEvent e, boolean state) {
1109       mySelected = state;
1110
1111       if (mySelected) {
1112         try {
1113           mySession = connectToDebugger();
1114         }
1115         catch (Exception e1) {
1116           LOG.error(e1);
1117           Messages.showErrorDialog("Can't connect to debugger", "Error Connecting Debugger");
1118         }
1119       }
1120       else {
1121         //TODO: disable debugging
1122       }
1123     }
1124   }
1125
1126
1127   private static class NewConsoleAction extends AnAction implements DumbAware {
1128     public NewConsoleAction() {
1129       super("New Console", "Creates new python console", AllIcons.General.Add);
1130     }
1131
1132     @Override
1133     public void update(AnActionEvent e) {
1134       e.getPresentation().setEnabled(true);
1135     }
1136
1137     @Override
1138     public void actionPerformed(AnActionEvent e) {
1139       PydevConsoleRunner runner =
1140         PythonConsoleRunnerFactory.getInstance().createConsoleRunner(e.getData(CommonDataKeys.PROJECT), e.getData(LangDataKeys.MODULE));
1141       runner.createNewConsole();
1142     }
1143   }
1144
1145   private XDebugSession connectToDebugger() throws ExecutionException {
1146     final ServerSocket serverSocket = PythonCommandLineState.createServerSocket();
1147
1148     final XDebugSession session = XDebuggerManager.getInstance(getProject()).
1149       startSessionAndShowTab("Python Console Debugger", PythonIcons.Python.Python, null, true, new XDebugProcessStarter() {
1150         @NotNull
1151         public XDebugProcess start(@NotNull final XDebugSession session) {
1152           PythonDebugLanguageConsoleView debugConsoleView = new PythonDebugLanguageConsoleView(getProject(), mySdk);
1153
1154           PyConsoleDebugProcessHandler consoleDebugProcessHandler =
1155             new PyConsoleDebugProcessHandler(myProcessHandler);
1156
1157           PyConsoleDebugProcess consoleDebugProcess =
1158             new PyConsoleDebugProcess(session, serverSocket, debugConsoleView,
1159                                       consoleDebugProcessHandler);
1160
1161           PythonDebugConsoleCommunication communication =
1162             PyDebugRunner.initDebugConsoleView(getProject(), consoleDebugProcess, debugConsoleView, consoleDebugProcessHandler, session);
1163
1164           communication.addCommunicationListener(new ConsoleCommunicationListener() {
1165             @Override
1166             public void commandExecuted(boolean more) {
1167               session.rebuildViews();
1168             }
1169
1170             @Override
1171             public void inputRequested() {
1172             }
1173           });
1174
1175           myPydevConsoleCommunication.setDebugCommunication(communication);
1176           debugConsoleView.attachToProcess(consoleDebugProcessHandler);
1177
1178           consoleDebugProcess.waitForNextConnection();
1179
1180           try {
1181             consoleDebugProcess.connect(myPydevConsoleCommunication);
1182           }
1183           catch (Exception e) {
1184             LOG.error(e); //TODO
1185           }
1186
1187           myProcessHandler.notifyTextAvailable("\nDebugger connected.\n", ProcessOutputTypes.STDERR);
1188
1189           return consoleDebugProcess;
1190         }
1191       });
1192
1193     return session;
1194   }
1195
1196   public static PythonConsoleRunnerFactory factory() {
1197     return new PydevConsoleRunnerFactory();
1198   }
1199
1200   private static class PythonConsoleRunParams implements PythonRunParams {
1201     private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
1202     private String myWorkingDir;
1203     private Sdk mySdk;
1204     private Map<String, String> myEnvironmentVariables;
1205
1206     public PythonConsoleRunParams(@NotNull PyConsoleOptions.PyConsoleSettings consoleSettings,
1207                                   @NotNull String workingDir,
1208                                   @NotNull Sdk sdk,
1209                                   @NotNull Map<String, String> envs) {
1210       myConsoleSettings = consoleSettings;
1211       myWorkingDir = workingDir;
1212       mySdk = sdk;
1213       myEnvironmentVariables = envs;
1214       myEnvironmentVariables.putAll(consoleSettings.getEnvs());
1215     }
1216
1217     @Override
1218     public String getInterpreterOptions() {
1219       return myConsoleSettings.getInterpreterOptions();
1220     }
1221
1222     @Override
1223     public void setInterpreterOptions(String interpreterOptions) {
1224       throw new UnsupportedOperationException();
1225     }
1226
1227     @Override
1228     public String getWorkingDirectory() {
1229       return myWorkingDir;
1230     }
1231
1232     @Override
1233     public void setWorkingDirectory(String workingDirectory) {
1234       throw new UnsupportedOperationException();
1235     }
1236
1237     @Nullable
1238     @Override
1239     public String getSdkHome() {
1240       return mySdk.getHomePath();
1241     }
1242
1243     @Override
1244     public void setSdkHome(String sdkHome) {
1245       throw new UnsupportedOperationException();
1246     }
1247
1248     @Override
1249     public void setModule(Module module) {
1250       throw new UnsupportedOperationException();
1251     }
1252
1253     @Override
1254     public String getModuleName() {
1255       return myConsoleSettings.getModuleName();
1256     }
1257
1258     @Override
1259     public boolean isUseModuleSdk() {
1260       return myConsoleSettings.isUseModuleSdk();
1261     }
1262
1263     @Override
1264     public void setUseModuleSdk(boolean useModuleSdk) {
1265       throw new UnsupportedOperationException();
1266     }
1267
1268     @Override
1269     public boolean isPassParentEnvs() {
1270       return myConsoleSettings.isPassParentEnvs();
1271     }
1272
1273     @Override
1274     public void setPassParentEnvs(boolean passParentEnvs) {
1275       throw new UnsupportedOperationException();
1276     }
1277
1278     @Override
1279     public Map<String, String> getEnvs() {
1280       return myEnvironmentVariables;
1281     }
1282
1283     @Override
1284     public void setEnvs(Map<String, String> envs) {
1285       throw new UnsupportedOperationException();
1286     }
1287
1288     @Nullable
1289     @Override
1290     public PathMappingSettings getMappingSettings() {
1291       throw new UnsupportedOperationException();
1292     }
1293
1294     @Override
1295     public void setMappingSettings(@Nullable PathMappingSettings mappingSettings) {
1296       throw new UnsupportedOperationException();
1297     }
1298
1299     @Override
1300     public boolean shouldAddContentRoots() {
1301       return myConsoleSettings.shouldAddContentRoots();
1302     }
1303
1304     @Override
1305     public boolean shouldAddSourceRoots() {
1306       return myConsoleSettings.shouldAddSourceRoots();
1307     }
1308
1309     @Override
1310     public void setAddContentRoots(boolean flag) {
1311       throw new UnsupportedOperationException();
1312     }
1313
1314     @Override
1315     public void setAddSourceRoots(boolean flag) {
1316       throw new UnsupportedOperationException();
1317     }
1318   }
1319 }