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