Merge branch 'multiline-console' of https://github.com/fitermay/intellij-community...
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 11 Oct 2016 02:27:08 +0000 (04:27 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 11 Oct 2016 02:27:08 +0000 (04:27 +0200)
47 files changed:
platform/lang-api/src/com/intellij/execution/Executor.java
platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java
platform/lang-impl/src/com/intellij/execution/runners/AbstractConsoleRunnerWithHistory.java
platform/lang-impl/src/com/intellij/execution/runners/ConsoleTitleGen.kt [new file with mode: 0644]
platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java
platform/platform-impl/src/com/intellij/execution/impl/ConsoleViewUtil.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
platform/platform-resources/src/META-INF/LangExtensionPoints.xml
python/educational-python/src/META-INF/PyCharmEduPlugin.xml
python/ide/src/META-INF/PyCharmCorePlugin.xml
python/ide/src/META-INF/pycharm-core.xml
python/pydevSrc/com/jetbrains/python/console/pydev/AbstractConsoleCommunication.java
python/pydevSrc/com/jetbrains/python/console/pydev/ConsoleCommunication.java
python/pydevSrc/com/jetbrains/python/console/pydev/IPydevXmlRpcClient.java
python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java [deleted file]
python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt [new file with mode: 0644]
python/python-pydev.iml
python/src/META-INF/python-core-common.xml
python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java
python/src/com/jetbrains/python/codeInsight/PyKeywordTypedHandler.java
python/src/com/jetbrains/python/console/ConsolePromptDecorator.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PyConsoleCopyHandler.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PyConsoleOptions.java
python/src/com/jetbrains/python/console/PyConsoleStarter.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PyConsoleToolWindowExecutor.java [new file with mode: 0644]
python/src/com/jetbrains/python/console/PyConsoleUtil.java
python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java [deleted file]
python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PydevConsoleRunner.java
python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java
python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java [new file with mode: 0644]
python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java
python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java [deleted file]
python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java [deleted file]
python/src/com/jetbrains/python/console/PythonConsoleView.java
python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java [deleted file]
python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java [deleted file]
python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt [new file with mode: 0644]
python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.java [deleted file]
python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.kt [new file with mode: 0644]
python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java
python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java
python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt [new file with mode: 0644]

index 3275cbd5112543dbabd782ed1f0e4f6f15517dc8..d5cec10d5f8420fbeaabd3bc3192e288345e40c5 100644 (file)
@@ -33,6 +33,9 @@ import javax.swing.*;
  */
 public abstract class Executor {
   public static final ExtensionPointName<Executor> EXECUTOR_EXTENSION_NAME = ExtensionPointName.create("com.intellij.executor");
+  /* Extension point for non-user facing executors, in order to use RunContentManager with custom toolwindows */
+  public static final ExtensionPointName<Executor> INTERNAL_EXECUTOR_EXTENSION_NAME =
+    ExtensionPointName.create("com.intellij.internal_executor");
 
   /**
    * Returns the ID of the toolwindow in which the run tabs created by this executor will be displayed.
@@ -41,6 +44,7 @@ public abstract class Executor {
    * {@link com.intellij.openapi.wm.ToolWindowId#DEBUG}).
    */
   public abstract String getToolWindowId();
+
   public abstract Icon getToolWindowIcon();
 
   /**
@@ -70,6 +74,7 @@ public abstract class Executor {
 
   /**
    * Returns the unique ID of the executor.
+   *
    * @return the ID of the executor.
    */
   @NotNull
index 9053311700aa7c4d8ffa05d95d2698744b3609c8..4a6d810db29114912e7846d2565164c3af157a91 100644 (file)
@@ -159,7 +159,7 @@ public class LanguageConsoleImpl extends ConsoleViewImpl implements LanguageCons
   }
 
   @Override
-  protected final JComponent createCenterComponent() {
+  protected JComponent createCenterComponent() {
     initComponents();
     return myPanel;
   }
index 86e3656003613e8824d0b6caa2f6186ed3753295..6ba35e0774efd7cae1893f82570a6c621700d6f4 100644 (file)
@@ -15,9 +15,6 @@
  */
 package com.intellij.execution.runners;
 
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.ExecutionHelper;
 import com.intellij.execution.ExecutionManager;
@@ -32,10 +29,8 @@ import com.intellij.execution.ui.actions.CloseAction;
 import com.intellij.ide.CommonActionsManager;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Computable;
 import com.intellij.ui.JBColor;
 import com.intellij.ui.SideBorder;
-import com.intellij.util.NotNullFunction;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
@@ -44,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
 import javax.swing.*;
 import java.awt.*;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * This class provides basic functionality for running consoles.
@@ -78,40 +74,42 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
   public void initAndRun() throws ExecutionException {
     // Create Server process
     final Process process = createProcess();
-    UIUtil.invokeLaterIfNeeded(() -> initConsoleUI(process));
-  }
+    UIUtil.invokeLaterIfNeeded(() -> {
+      // Init console view
+      myConsoleView = createConsoleView();
+      if (myConsoleView instanceof JComponent) {
+        ((JComponent)myConsoleView).setBorder(new SideBorder(JBColor.border(), SideBorder.LEFT));
+      }
+      myProcessHandler = createProcessHandler(process);
 
-  private void initConsoleUI(Process process) {
-    // Init console view
-    myConsoleView = createConsoleView();
-    if (myConsoleView instanceof JComponent) {
-      ((JComponent)myConsoleView).setBorder(new SideBorder(JBColor.border(), SideBorder.LEFT));
-    }
-    myProcessHandler = createProcessHandler(process);
+      myConsoleExecuteActionHandler = createExecuteActionHandler();
 
-    myConsoleExecuteActionHandler = createExecuteActionHandler();
+      ProcessTerminatedListener.attach(myProcessHandler);
 
-    ProcessTerminatedListener.attach(myProcessHandler);
+      myProcessHandler.addProcessListener(new ProcessAdapter() {
+        @Override
+        public void processTerminated(ProcessEvent event) {
+          finishConsole();
+        }
+      });
 
-    myProcessHandler.addProcessListener(new ProcessAdapter() {
-      @Override
-      public void processTerminated(ProcessEvent event) {
-        finishConsole();
-      }
-    });
+      // Attach to process
+      myConsoleView.attachToProcess(myProcessHandler);
 
-    // Attach to process
-    myConsoleView.attachToProcess(myProcessHandler);
+      // Runner creating
+      createContentDescriptorAndActions();
 
-    // Runner creating
-    createContentDescriptorAndActions();
+      // Run
+      myProcessHandler.startNotify();
+    });
+  }
 
-    // Run
-    myProcessHandler.startNotify();
+  protected Executor getExecutor() {
+    return DefaultRunExecutor.getRunExecutorInstance();
   }
 
   protected void createContentDescriptorAndActions() {
-    final Executor defaultExecutor = DefaultRunExecutor.getRunExecutorInstance();
+    final Executor defaultExecutor = getExecutor();
     final DefaultActionGroup toolbarActions = new DefaultActionGroup();
     final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false);
 
@@ -143,29 +141,7 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
   }
 
   protected String constructConsoleTitle(final @NotNull String consoleTitle) {
-    if (shouldAddNumberToTitle()) {
-      List<String> activeConsoleNames = getActiveConsoleNames(consoleTitle);
-      int max = 0;
-      for (String name : activeConsoleNames) {
-        if (max == 0) {
-          max = 1;
-        }
-        try {
-          int num = Integer.parseInt(name.substring(consoleTitle.length() + 1, name.length() - 1));
-          if (num > max) {
-            max = num;
-          }
-        }
-        catch (Exception ignored) {
-          //skip
-        }
-      }
-      if (max >= 1) {
-        return consoleTitle + "(" + (max + 1) + ")";
-      }
-    }
-
-    return consoleTitle;
+    return new ConsoleTitleGen(myProject, consoleTitle, shouldAddNumberToTitle()).makeTitle();
   }
 
   public boolean isAutoFocusContent() {
@@ -264,24 +240,5 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
     return myConsoleExecuteActionHandler;
   }
 
-  protected List<String> getActiveConsoleNames(final String consoleTitle) {
-    return getActiveConsolesFromRunToolWindow(consoleTitle);
-  }
-
-  protected List<String> getActiveConsolesFromRunToolWindow(final String consoleTitle) {
-    List<RunContentDescriptor> consoles = ExecutionHelper.collectConsolesByDisplayName(myProject, dom -> dom.contains(consoleTitle));
 
-    return FluentIterable.from(consoles).filter(new Predicate<RunContentDescriptor>() {
-      @Override
-      public boolean apply(RunContentDescriptor input) {
-        ProcessHandler handler = input.getProcessHandler();
-        return handler != null && !handler.isProcessTerminated();
-      }
-    }).transform(new Function<RunContentDescriptor, String>() {
-      @Override
-      public String apply(RunContentDescriptor input) {
-        return input.getDisplayName();
-      }
-    }).toList();
-  }
 }
diff --git a/platform/lang-impl/src/com/intellij/execution/runners/ConsoleTitleGen.kt b/platform/lang-impl/src/com/intellij/execution/runners/ConsoleTitleGen.kt
new file mode 100644 (file)
index 0000000..6bd1425
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.runners
+
+import com.intellij.execution.ExecutionHelper
+import com.intellij.execution.process.ProcessHandler
+import com.intellij.execution.ui.RunContentDescriptor
+import com.intellij.openapi.project.Project
+import java.util.stream.Collectors
+
+/**
+ * Created by Yuli Fiterman on 9/13/2016.
+ */
+open class ConsoleTitleGen @JvmOverloads constructor(private val myProject: Project, private val consoleTitle: String, private val shouldAddNumberToTitle: Boolean = true) {
+
+  fun makeTitle(): String {
+
+    if (shouldAddNumberToTitle) {
+      val activeConsoleNames = getActiveConsoles(consoleTitle)
+      var max = 0
+      for (name in activeConsoleNames) {
+        if (max == 0) {
+          max = 1
+        }
+        try {
+          val num = Integer.parseInt(name.substring(consoleTitle.length + 1, name.length - 1))
+          if (num > max) {
+            max = num
+          }
+        }
+        catch (ignored: Exception) {
+          //skip
+        }
+
+      }
+      if (max >= 1) {
+        return consoleTitle + "(" + (max + 1) + ")"
+      }
+    }
+
+    return consoleTitle
+  }
+
+
+  open protected fun getActiveConsoles(consoleTitle: String): List<String> {
+    val consoles = ExecutionHelper.collectConsolesByDisplayName(myProject) { dom -> dom.contains(consoleTitle) }
+
+    return consoles.filter({ input ->
+      val handler = input.processHandler
+      handler != null && !handler.isProcessTerminated
+    }).map({ it.displayName });
+  }
+}
index 189b6fa2a98274001b1f52310a7ddf7c077c9da7..e53676eb12bcbe730eeeb291b485f628ceac36bc 100644 (file)
@@ -30,6 +30,7 @@ import com.intellij.openapi.actionSystem.DataProvider;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
@@ -87,7 +88,11 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
     }
 
     for (Executor executor : ExecutorRegistry.getInstance().getRegisteredExecutors()) {
-      registerToolwindow(executor, toolWindowManager);
+      registerToolwindow(executor, toolWindowManager, true);
+    }
+
+    for (Executor executor : Extensions.getExtensions(Executor.INTERNAL_EXECUTOR_EXTENSION_NAME)) {
+      registerToolwindow(executor, toolWindowManager, false);
     }
 
     toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter() {
@@ -116,7 +121,9 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
   public void dispose() {
   }
 
-  private void registerToolwindow(@NotNull final Executor executor, @NotNull ToolWindowManagerEx toolWindowManager) {
+  private void registerToolwindow(@NotNull final Executor executor,
+                                  @NotNull ToolWindowManagerEx toolWindowManager,
+                                  boolean autoRemoveOnEmpty) {
     final String toolWindowId = executor.getToolWindowId();
     if (toolWindowManager.getToolWindow(toolWindowId) != null) {
       return;
@@ -146,7 +153,9 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
 
     toolWindow.setIcon(executor.getToolWindowIcon());
     myToolwindowIdToBaseIconMap.put(toolWindowId, executor.getToolWindowIcon());
-    new ContentManagerWatcher(toolWindow, contentManager);
+    if (autoRemoveOnEmpty) {
+      new ContentManagerWatcher(toolWindow, contentManager);
+    }
     contentManager.addContentManagerListener(new ContentManagerAdapter() {
       @Override
       public void selectionChanged(final ContentManagerEvent event) {
@@ -255,7 +264,8 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
     }
 
     final ContentManager contentManager = getContentManagerForRunner(executor);
-    RunContentDescriptor oldDescriptor = chooseReuseContentForDescriptor(contentManager, descriptor, executionId, descriptor.getDisplayName());
+    RunContentDescriptor oldDescriptor =
+      chooseReuseContentForDescriptor(contentManager, descriptor, executionId, descriptor.getDisplayName());
     final Content content;
     if (oldDescriptor == null) {
       content = createNewContent(contentManager, descriptor, executor);
@@ -363,7 +373,9 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
   }
 
   @Override
-  public void showRunContent(@NotNull Executor info, @NotNull RunContentDescriptor descriptor, @Nullable RunContentDescriptor contentToReuse) {
+  public void showRunContent(@NotNull Executor info,
+                             @NotNull RunContentDescriptor descriptor,
+                             @Nullable RunContentDescriptor contentToReuse) {
     copyContentAndBehavior(descriptor, contentToReuse);
     showRunContent(info, descriptor, descriptor.getExecutionId());
   }
@@ -409,7 +421,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
       return null;
     }
     final RunContentDescriptor oldDescriptor = getRunContentDescriptorByContent(content);
-    if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited() ) {
+    if (oldDescriptor != null && !oldDescriptor.isContentReuseProhibited()) {
       //content.setExecutionId(executionId);
       return oldDescriptor;
     }
@@ -608,7 +620,8 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
       }
       final boolean destroyProcess;
       //noinspection deprecation
-      if (processHandler.isSilentlyDestroyOnClose() || Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
+      if (processHandler.isSilentlyDestroyOnClose() ||
+          Boolean.TRUE.equals(processHandler.getUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE))) {
         destroyProcess = true;
       }
       else {
@@ -641,7 +654,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
 
       {
         if (killable) {
-          String cancelText= ExecutionBundle.message("terminating.process.progress.kill");
+          String cancelText = ExecutionBundle.message("terminating.process.progress.kill");
           setCancelText(cancelText);
           setCancelTooltipText(cancelText);
         }
index 338cb602916a2324af8188e54c4282fea50c4f1a..a85e11bdf3ca7ba9027cd39830f12419c6de5b1d 100644 (file)
@@ -54,6 +54,8 @@ import static com.intellij.execution.ui.ConsoleViewContentType.registerNewConsol
 public class ConsoleViewUtil {
 
   public static final Key<Boolean> EDITOR_IS_CONSOLE_VIEW = Key.create("EDITOR_IS_CONSOLE_VIEW");
+  public static final Key<Boolean> EDITOR_IS_CONSOLE_HISTORY_VIEW = Key.create("EDITOR_IS_CONSOLE_HISTORY_VIEW");
+
   private static final Key<Boolean> REPLACE_ACTION_ENABLED = Key.create("REPLACE_ACTION_ENABLED");
 
   public static EditorEx setupConsoleEditor(Project project, final boolean foldingOutlineShown, final boolean lineMarkerAreaShown) {
@@ -196,12 +198,21 @@ public class ConsoleViewUtil {
   }
 
   public static void printWithHighlighting(@NotNull ConsoleView console, @NotNull String text, @NotNull SyntaxHighlighter highlighter) {
+    printWithHighlighting(console, text, highlighter, null);
+  }
+
+  public static void printWithHighlighting(@NotNull ConsoleView console, @NotNull String text,
+                                           @NotNull SyntaxHighlighter highlighter,
+                                           Runnable doOnNewLine) {
     Lexer lexer = highlighter.getHighlightingLexer();
     lexer.start(text, 0, text.length(), 0);
 
     IElementType tokenType;
     while ((tokenType = lexer.getTokenType()) != null) {
       console.print(lexer.getTokenText(), getContentTypeForToken(tokenType, highlighter));
+      if (doOnNewLine != null && "\n".equals(lexer.getTokenText())) {
+        doOnNewLine.run();
+      }
       lexer.advance();
     }
   }
index 0f506735c6c4cac2735d7bb05acccb20c13d2576..3b9b000b88faddfea46e50b9ff5d7a3c49cefe6d 100644 (file)
@@ -456,6 +456,20 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
           }
           visLinesIterator.advance();
         }
+        if (startVisualLine == 0 && endVisualLine == 0) { //allow paining gutters for empty documents
+          String s = gutterProvider.getLineText(0, myEditor);
+          final EditorFontType style = gutterProvider.getStyle(0, myEditor);
+          final Color bg = gutterProvider.getBgColor(0, myEditor);
+          if (bg != null) {
+            g.setColor(bg);
+            g.fillRect(x, 0, annotationSize, lineHeight);
+          }
+          g.setColor(myEditor.getColorsScheme().getColor(gutterProvider.getColor(0, myEditor)));
+          g.setFont(myEditor.getColorsScheme().getFont(style));
+          if (!StringUtil.isEmpty(s)) {
+            g.drawString(s, GAP_BETWEEN_ANNOTATIONS / 2 + x, myEditor.getAscent());
+          }
+        }
 
         x += annotationSize;
       }
@@ -711,7 +725,7 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
   private void calcAnnotationsSize() {
     myTextAnnotationGuttersSize = 0;
     final FontMetrics fontMetrics = myEditor.getFontMetrics(Font.PLAIN);
-    final int lineCount = myEditor.getDocument().getLineCount();
+    final int lineCount = Math.max(myEditor.getDocument().getLineCount(), 1);
     for (int j = 0; j < myTextAnnotationGutters.size(); j++) {
       TextAnnotationGutterProvider gutterProvider = myTextAnnotationGutters.get(j);
       int gutterSize = 0;
index 4ac7053380b88e51dd9c8dcd43ff1edd707f8421..cec19d6cc0fdb550e241e27ef3d8e6f36bc1d441 100644 (file)
@@ -9,7 +9,8 @@
     <extensionPoint name="generalCodeStyleOptionsProvider" beanClass="com.intellij.application.options.GeneralCodeStyleOptionsProviderEP">
       <with attribute="instance" implements="com.intellij.application.options.GeneralCodeStyleOptionsProvider"/>
     </extensionPoint>
-    <extensionPoint name="autoImportOptionsProvider" beanClass="com.intellij.application.options.editor.AutoImportOptionsProviderEP" area="IDEA_PROJECT">
+    <extensionPoint name="autoImportOptionsProvider" beanClass="com.intellij.application.options.editor.AutoImportOptionsProviderEP"
+                    area="IDEA_PROJECT">
       <with attribute="instance" implements="com.intellij.application.options.editor.AutoImportOptionsProvider"/>
     </extensionPoint>
     <!--suppress ExtensionPointBeanClass -->
     <extensionPoint name="executor"
                     interface="com.intellij.execution.Executor"/>
 
+    <extensionPoint name="internal_executor"
+                    interface="com.intellij.execution.Executor"/>
+
+
     <extensionPoint name="executionTargetProvider"
                     interface="com.intellij.execution.ExecutionTargetProvider"/>
 
 
     <extensionPoint name="goto.nonProjectScopeDisabler" beanClass="com.intellij.ide.actions.NonProjectScopeDisablerEP"/>
     <extensionPoint name="searchEverywhereClassifier" interface="com.intellij.ide.actions.SearchEverywhereClassifier"/>
-    <extensionPoint qualifiedName="com.intellij.equivalenceDescriptorProvider" interface="com.intellij.dupLocator.equivalence.EquivalenceDescriptorProvider"/>
+    <extensionPoint qualifiedName="com.intellij.equivalenceDescriptorProvider"
+                    interface="com.intellij.dupLocator.equivalence.EquivalenceDescriptorProvider"/>
 
     <extensionPoint name="previewPanelProvider" interface="com.intellij.openapi.preview.PreviewPanelProvider" area="IDEA_PROJECT"/>
     <extensionPoint name="inspectionElementsMerger" interface="com.intellij.codeInspection.ex.InspectionElementsMerger"/>
index 31efe5e5c30e3025246feb3c149250be9dab43f2..79f3dd061d98f17e3a68dfa49b7dca310125a3e0 100644 (file)
     </component>
     <component>
       <interface-class>com.jetbrains.python.console.PythonConsoleRunnerFactory</interface-class>
-      <implementation-class>com.jetbrains.python.console.PythonToolWindowConsoleRunnerFactory</implementation-class>
+      <implementation-class>com.jetbrains.python.console.PydevConsoleRunnerFactory</implementation-class>
     </component>
   </application-components>
 
+  <extensions defaultExtensionNs="com.intellij">
+    <internal_executor id="PyConsoleToolWindowExecutor" implementation="com.jetbrains.python.console.PyConsoleToolWindowExecutor"/>
+    <postStartupActivity implementation="com.jetbrains.python.console.PyConsoleStarter"/>
+
+  </extensions>
+
   <extensions defaultExtensionNs="com.intellij">
       <programRunner implementation="com.jetbrains.python.edu.debugger.PyEduDebugRunner"/>
       <executor implementation="com.jetbrains.python.edu.debugger.PyEduDebugExecutor" order="first,after run"/>
index ac8651e95fadeb7015882834da1087ed40940a33..47ad96b0654579658acf44f6e8802ce67e7bbda2 100644 (file)
@@ -7,10 +7,14 @@
       <implementation-class>com.jetbrains.python.PyCharmInitialConfigurator</implementation-class>
       <headless-implementation-class/>
     </component>
-
     <component>
       <interface-class>com.jetbrains.python.console.PythonConsoleRunnerFactory</interface-class>
-      <implementation-class>com.jetbrains.python.console.PythonToolWindowConsoleRunnerFactory</implementation-class>
+      <implementation-class>com.jetbrains.python.console.PydevConsoleRunnerFactory</implementation-class>
     </component>
+
   </application-components>
+  <extensions defaultExtensionNs="com.intellij">
+    <internal_executor id="PyConsoleToolWindowExecutor" implementation="com.jetbrains.python.console.PyConsoleToolWindowExecutor"/>
+    <postStartupActivity implementation="com.jetbrains.python.console.PyConsoleStarter"/>
+  </extensions>
 </idea-plugin>
index 3f167adddb8fb3d1aff0c86216375002ddd05075..042067272baf4252a1a995ef7c60ed2894e138e5 100644 (file)
     </component>
   </project-components>
 
-  <project-components>
-    <component>
-      <implementation-class>com.jetbrains.python.console.PythonConsoleToolWindow</implementation-class>
-    </component>
-  </project-components>
 
   <module value="com.intellij.modules.xml"/>
 
@@ -58,7 +53,8 @@
                         serviceImplementation="com.jetbrains.python.PythonModuleTypeManager"/>
 
     <fileStructureGroupRuleProvider implementation="com.intellij.usages.impl.rules.FileGroupingRuleProvider" order="first"/>
-    <fileStructureGroupRuleProvider implementation="com.jetbrains.python.findUsages.PyClassGroupingRuleProvider" order="before py-function"/>
+    <fileStructureGroupRuleProvider implementation="com.jetbrains.python.findUsages.PyClassGroupingRuleProvider"
+                                    order="before py-function"/>
     <diffPreviewProvider implementation="com.jetbrains.python.configuration.PyDiffPreviewProvider"/>
 
     <applicationService serviceInterface="com.jetbrains.python.run.PyCommonOptionsFormFactory"
     <renameHandler implementation="com.intellij.platform.renameProject.ProjectFolderRenameHandler"/>
 
     <!-- Console -->
-    <toolWindow id="Python Console" anchor="bottom" icon="PythonIcons.Python.PythonConsoleToolWindow"
-                factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
 
     <directoryIndexExcludePolicy implementation="com.jetbrains.python.PyDirectoryIndexExcludePolicy"/>
+
   </extensions>
 
   <actions>
@@ -96,7 +91,7 @@
       <add-to-group group-id="PlatformOpenProjectGroup" anchor="after" relative-to-action="NewDirectoryProject"/>
     </action>
 
-    <action id="SaveAs" class="com.intellij.ide.actions.SaveAsAction" text="Save As.." >
+    <action id="SaveAs" class="com.intellij.ide.actions.SaveAsAction" text="Save As..">
       <add-to-group group-id="FileOpenGroup" anchor="after" relative-to-action="OpenFile"/>
     </action>
 
             icon="AllIcons.RunConfigurations.RerunFailedTests"/>
 
     <group id="WelcomeScreen.Platform.NewProject">
-      <action id="WelcomeScreen.CreateDirectoryProject" class="com.jetbrains.python.newProject.actions.PyCharmNewProjectStep" icon="AllIcons.Welcome.CreateNewProject"/>
+      <action id="WelcomeScreen.CreateDirectoryProject" class="com.jetbrains.python.newProject.actions.PyCharmNewProjectStep"
+              icon="AllIcons.Welcome.CreateNewProject"/>
       <action id="WelcomeScreen.OpenDirectoryProject" class="com.intellij.ide.actions.OpenFileAction" icon="AllIcons.General.OpenProject"/>
 
       <add-to-group group-id="WelcomeScreen.QuickStart" anchor="first"/>
index 03598551f8266598072a13d6b6a74807b0d22436..09e2b519e21582b1906b8f4a7c9c5f7cac4ce378 100644 (file)
@@ -51,6 +51,7 @@ public abstract class AbstractConsoleCommunication implements ConsoleCommunicati
     return waitingForInput;
   }
 
+
   @Override
   public void addCommunicationListener(ConsoleCommunicationListener listener) {
     communicationListeners.add(listener);
index 301717148bbd8812c46f920d4561136b46255d6d..d348fe6dce0430ed938bebc5647b1ef15e545164 100644 (file)
@@ -18,6 +18,9 @@ public interface ConsoleCommunication {
 
   boolean isExecuting();
 
+  boolean needsMore();
+
+
   void execInterpreter(ConsoleCodeFragment code, Function<InterpreterResponse, Object> callback);
 
   void interrupt();
index 2477e9f9d60a3e6a05dbfb2313ba50bb18ff65f4..622e14a2f49e0f6c5ebae21532b5a8a89e510326 100644 (file)
@@ -19,4 +19,7 @@ public interface IPydevXmlRpcClient {
      * @throws XmlRpcException
      */
     Object execute(String command, Object[] args) throws XmlRpcException;
+
+    Object execute(String command, Object[] args, long timeoutMillis) throws XmlRpcException;
+
 }
diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java b/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.java
deleted file mode 100644 (file)
index ad9991f..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.jetbrains.python.console.pydev;
-
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.util.net.NetUtils;
-import org.apache.xmlrpc.*;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Vector;
-
-/**
- * Subclass of XmlRpcClient that will monitor the process so that if the process is destroyed, we stop waiting
- * for messages from it.
- *
- * @author Fabio
- */
-public class PydevXmlRpcClient implements IPydevXmlRpcClient {
-
-  /**
-   * Internal xml-rpc client (responsible for the actual communication with the server)
-   */
-  private final XmlRpcClient impl;
-
-  /**
-   * The process where the server is being executed.
-   */
-  private final Process process;
-
-  /**
-   * ItelliJ Logging
-   */
-  private static final Logger LOG = Logger.getInstance(PydevXmlRpcClient.class.getName());
-
-  private static final long TIME_LIMIT = 60000;
-
-
-  /**
-   * Constructor (see fields description)
-   */
-  public PydevXmlRpcClient(Process process, int port) throws MalformedURLException {
-    XmlRpc.setDefaultInputEncoding("UTF8"); //even though it uses UTF anyway
-    impl = new XmlRpcClientLite(NetUtils.getLocalHostString(), port);
-    //this.impl = new XmlRpcClient(url, new CommonsXmlRpcTransportFactory(url));
-    this.process = process;
-  }
-
-  /**
-   * Executes a command in the server.
-   * <p/>
-   * Within this method, we should be careful about being able to return if the server dies.
-   * If we wanted to have a timeout, this would be the place to add it.
-   *
-   * @return the result from executing the given command in the server.
-   */
-  @Override
-  public Object execute(String command, Object[] args) throws XmlRpcException {
-    final Object[] result = new Object[]{null};
-
-    //make an async call so that we can keep track of not actually having an answer.
-    impl.executeAsync(command, new Vector(Arrays.asList(args)), new AsyncCallback() {
-
-      @Override
-      public void handleError(Exception error, URL url, String method) {
-        result[0] = new Object[]{error.getMessage()};
-      }
-
-      @Override
-      public void handleResult(Object recievedResult, URL url, String method) {
-        result[0] = recievedResult;
-      }
-    });
-
-    long started = System.currentTimeMillis();
-    //busy loop waiting for the answer (or having the console die).
-    while (result[0] == null && System.currentTimeMillis() - started < TIME_LIMIT) {
-      try {
-        if (process != null) {
-          int exitValue = process.exitValue();
-          result[0] = new Object[]{String.format("Console already exited with value: %s while waiting for an answer.\n", exitValue)};
-          //ok, we have an exit value!
-          break;
-        }
-      }
-      catch (IllegalThreadStateException e) {
-        //that's ok... let's sleep a bit
-        synchronized (this) {
-          try {
-            wait(10);
-          }
-          catch (InterruptedException e1) {
-            LOG.error(e1);
-          }
-        }
-      }
-    }
-    if (result[0] == null) {
-      throw new XmlRpcException(-1, "Timeout while connecting to server");
-    }
-    return result[0];
-  }
-}
diff --git a/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt b/python/pydevSrc/com/jetbrains/python/console/pydev/PydevXmlRpcClient.kt
new file mode 100644 (file)
index 0000000..6a01ff3
--- /dev/null
@@ -0,0 +1,101 @@
+package com.jetbrains.python.console.pydev
+
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.util.net.NetUtils
+import org.apache.xmlrpc.*
+
+import java.net.MalformedURLException
+import java.net.URL
+import java.util.Arrays
+import java.util.Vector
+import java.util.concurrent.TimeUnit
+
+/**
+ * Subclass of XmlRpcClient that will monitor the process so that if the process is destroyed, we stop waiting
+ * for messages from it.
+
+ * @author Fabio
+ */
+class PydevXmlRpcClient
+/**
+ * Constructor (see fields description)
+ */
+@Throws(MalformedURLException::class)
+constructor(private val process: Process, port: Int) : IPydevXmlRpcClient {
+
+
+  /**
+   * Internal xml-rpc client (responsible for the actual communication with the server)
+   */
+  private val impl: XmlRpcClient
+
+
+  init {
+    XmlRpc.setDefaultInputEncoding("UTF8") //even though it uses UTF anyway
+    impl = XmlRpcClientLite(NetUtils.getLocalHostString(), port)
+  }
+
+
+  override fun execute(command: String, args: Array<Any>): Any {
+    return execute(command, args, TIME_LIMIT)
+  }
+
+  /**
+   * Executes a command in the server.
+   *
+   *
+   * Within this method, we should be careful about being able to return if the server dies.
+   * If we wanted to have a timeout, this would be the place to add it.
+
+   * @return the result from executing the given command in the server.
+   */
+  @Throws(XmlRpcException::class)
+  override fun execute(command: String, args: Array<Any>, timeoutMillis: Long): Any {
+    val result = arrayOf<Any?>(null)
+
+    //make an async call so that we can keep track of not actually having an answer.
+    impl.executeAsync(command, Vector(Arrays.asList(*args)), object : AsyncCallback {
+
+      override fun handleError(error: Exception, url: URL, method: String) {
+        result[0] = makeError(error.message ?: "Unknown Error")
+      }
+
+      override fun handleResult(recievedResult: Any, url: URL, method: String) {
+        result[0] = recievedResult
+      }
+    })
+
+    val started = System.currentTimeMillis()
+    val progress = ProgressManager.getInstance().progressIndicator
+    //busy loop waiting for the answer (or having the console die).
+    while (result[0] == null && System.currentTimeMillis() - started < TIME_LIMIT) {
+
+      progress?.let {
+        progress.checkCanceled()
+      }
+      val exitValue = process.waitFor(10, TimeUnit.MILLISECONDS)
+      if (exitValue) {
+        result[0] = makeError(String.format("Console already exited with value: %s while waiting for an answer.\n", exitValue))
+        break
+      }
+    }
+
+    return result[0] ?: throw XmlRpcException(-1, "Timeout while connecting to server")
+
+  }
+
+  fun makeError(error: String): Array<Any> {
+    return arrayOf(error)
+  }
+
+  companion object {
+
+    /**
+     * ItelliJ Logging
+     */
+    private val LOG = Logger.getInstance(PydevXmlRpcClient::class.java.name)
+
+    private val TIME_LIMIT: Long = 60000
+  }
+}
index 4f1b6190919bf6e10129e71e6b16716883e1f91f..369b9f5258a23acd1df8c4b348d34d89afedf915 100644 (file)
@@ -12,6 +12,6 @@
     <orderEntry type="library" name="Guava" level="project" />
     <orderEntry type="library" name="xpp3-1.1.4-min" level="project" />
     <orderEntry type="module" module-name="util" />
+    <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
   </component>
-</module>
-
+</module>
\ No newline at end of file
index 46fbce43ef25a8ba3f86a96bc519e74c5b849f7e..7c2d825b4d077705bbba880f4600940b872e4cb8 100644 (file)
@@ -49,6 +49,8 @@
     <enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
     <enterHandlerDelegate implementation="com.jetbrains.python.editor.PyEnterAtIndentHandler" order="first"/>
     <enterHandlerDelegate implementation="com.jetbrains.python.editor.PyEnterBetweenBracketsHandler"/>
+    <editorActionHandler action="EditorCopy" implementationClass="com.jetbrains.python.console.PyConsoleCopyHandler"/>
+
     <editor.backspaceModeOverride language="Python" implementationClass="com.intellij.codeInsight.editorActions.SmartBackspaceDisabler"/>
     <sdkType implementation="com.jetbrains.python.sdk.PythonSdkType"/>
     <gotoClassContributor implementation="com.jetbrains.python.PyGotoClassContributor"/>
index 08fab4314898a0a834b4b00592d24cba81dbb456..bc00af821211c229f33e3281f70131de83a55f6e 100644 (file)
@@ -33,7 +33,6 @@ import com.intellij.util.Consumer;
 import com.jetbrains.python.console.PyCodeExecutor;
 import com.jetbrains.python.console.PydevConsoleRunner;
 import com.jetbrains.python.console.PythonConsoleRunnerFactory;
-import com.jetbrains.python.console.PythonConsoleToolWindow;
 import com.jetbrains.python.psi.PyFile;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -180,22 +179,9 @@ public class PyExecuteSelectionAction extends AnAction {
   }
 
   private static Collection<RunContentDescriptor> getConsoles(Project project) {
-    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project);
 
-    if (toolWindow != null && toolWindow.getToolWindow().isVisible()) {
-      RunContentDescriptor selectedContentDescriptor = toolWindow.getSelectedContentDescriptor();
-      return selectedContentDescriptor != null ? Lists.newArrayList(selectedContentDescriptor) : Lists.<RunContentDescriptor>newArrayList();
-    }
-
-    Collection<RunContentDescriptor> descriptors =
-      ExecutionHelper.findRunningConsole(project, dom -> dom.getExecutionConsole() instanceof PyCodeExecutor && isAlive(dom));
 
-    if (descriptors.isEmpty() && toolWindow != null) {
-      return toolWindow.getConsoleContentDescriptors();
-    }
-    else {
-      return descriptors;
-    }
+    return ExecutionHelper.findRunningConsole(project, dom -> dom.getExecutionConsole() instanceof PyCodeExecutor && isAlive(dom));
   }
 
   private static boolean isAlive(RunContentDescriptor dom) {
@@ -217,33 +203,21 @@ public class PyExecuteSelectionAction extends AnAction {
   private static void startConsole(final Project project,
                                    final Consumer<PyCodeExecutor> consumer,
                                    Module context) {
-    final PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project);
-
-    if (toolWindow != null) {
-      toolWindow.activate(() -> {
-        List<RunContentDescriptor> descs = toolWindow.getConsoleContentDescriptors();
 
-        RunContentDescriptor descriptor = descs.get(0);
-        if (descriptor != null && descriptor.getExecutionConsole() instanceof PyCodeExecutor) {
-          consumer.consume((PyCodeExecutor)descriptor.getExecutionConsole());
+    PythonConsoleRunnerFactory consoleRunnerFactory = PythonConsoleRunnerFactory.getInstance();
+    PydevConsoleRunner runner = consoleRunnerFactory.createConsoleRunner(project, null);
+    runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
+      @Override
+      public void handleConsoleInitialized(LanguageConsoleView consoleView) {
+        if (consoleView instanceof PyCodeExecutor) {
+          consumer.consume((PyCodeExecutor)consoleView);
         }
-      });
-    }
-    else {
-      PythonConsoleRunnerFactory consoleRunnerFactory = PythonConsoleRunnerFactory.getInstance();
-      PydevConsoleRunner runner = consoleRunnerFactory.createConsoleRunner(project, null);
-      runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
-        @Override
-        public void handleConsoleInitialized(LanguageConsoleView consoleView) {
-          if (consoleView instanceof PyCodeExecutor) {
-            consumer.consume((PyCodeExecutor)consoleView);
-          }
-        }
-      });
-      runner.run();
-    }
+      }
+    });
+    runner.run();
   }
 
+
   private static boolean canFindConsole(AnActionEvent e) {
     Project project = e.getProject();
     if (project != null) {
index 3d85f8bb19c0fbd6b123cfd880ee1b0b460f533f..22906b308d8ff6145ba0783a97120679cae3fbf3 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
 import com.jetbrains.python.PythonFileType;
 import com.jetbrains.python.psi.PyStringLiteralExpression;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Handles overtyping ':' in definitions.
@@ -35,26 +36,33 @@ public class PyKeywordTypedHandler extends TypedHandlerDelegate {
   public Result beforeCharTyped(char character, Project project, Editor editor, PsiFile file, FileType fileType) {
     if (!(fileType instanceof PythonFileType)) return Result.CONTINUE; // else we'd mess up with other file types!
     if (character == ':') {
-      final Document document = editor.getDocument();
-      final int offset = editor.getCaretModel().getOffset();
-
-      PsiElement token = file.findElementAt(offset - 1);
-      if (token == null || offset >= document.getTextLength()) return Result.CONTINUE; // sanity check: beyond EOL
-
-      PsiElement here_elt = file.findElementAt(offset);
-      if (here_elt == null) return Result.CONTINUE; 
-      if (here_elt instanceof PyStringLiteralExpression || here_elt.getParent() instanceof PyStringLiteralExpression) return Result.CONTINUE;
-
-      // double colons aren't found in Python's syntax, so we can safely overtype a colon everywhere but strings.
-      String here_text = here_elt.getText();
-      if (":".equals(here_text)) {
-        editor.getCaretModel().moveToOffset(offset + 1); // overtype, that is, jump over
-        return Result.STOP;
+      Result res = getOverTypeResult(editor, file);
+      if (res == Result.STOP) {
+        return res;
       }
       PyUnindentingInsertHandler.unindentAsNeeded(project, editor, file);
     }
-
     return Result.CONTINUE; // the default
   }
 
+  @Nullable
+  public Result getOverTypeResult(Editor editor, PsiFile file) {
+    final Document document = editor.getDocument();
+    final int offset = editor.getCaretModel().getOffset();
+
+    PsiElement token = file.findElementAt(offset - 1);
+    if (token == null || offset >= document.getTextLength()) return Result.CONTINUE; // sanity check: beyond EOL
+
+    PsiElement here_elt = file.findElementAt(offset);
+    if (here_elt == null) return Result.CONTINUE;
+    if (here_elt instanceof PyStringLiteralExpression || here_elt.getParent() instanceof PyStringLiteralExpression) return Result.CONTINUE;
+
+    // double colons aren't found in Python's syntax, so we can safely overtype a colon everywhere but strings.
+    String here_text = here_elt.getText();
+    if (":".equals(here_text)) {
+      editor.getCaretModel().moveToOffset(offset + 1); // overtype, that is, jump over
+      return Result.STOP;
+    }
+    return null;
+  }
 }
diff --git a/python/src/com/jetbrains/python/console/ConsolePromptDecorator.kt b/python/src/com/jetbrains/python/console/ConsolePromptDecorator.kt
new file mode 100644 (file)
index 0000000..69aa813
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console
+
+import com.intellij.execution.ui.ConsoleViewContentType
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.EditorLinePainter
+import com.intellij.openapi.editor.LineExtensionInfo
+import com.intellij.openapi.editor.TextAnnotationGutterProvider
+import com.intellij.openapi.editor.colors.ColorKey
+import com.intellij.openapi.editor.colors.EditorFontType
+import com.intellij.openapi.editor.ex.EditorEx
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.util.ui.UIUtil
+import com.jetbrains.python.console.parsing.PythonConsoleData
+
+import java.awt.*
+
+/**
+ * Created by Yuli Fiterman on 9/16/2016.
+ */
+class ConsolePromptDecorator(private val myEditorEx: EditorEx, private val myConsoleData: PythonConsoleData) : EditorLinePainter(), TextAnnotationGutterProvider {
+
+  var mainPrompt = ""
+    set(mainPrompt) {
+      if (this.mainPrompt != mainPrompt) {
+        field = mainPrompt
+        UIUtil.invokeLaterIfNeeded { myEditorEx.gutterComponentEx.revalidateMarkup() }
+      }
+    }
+
+  var promptAttributes = ConsoleViewContentType.USER_INPUT
+    set(promptAttributes) {
+      field = promptAttributes
+      myEditorEx.colorsScheme.setColor(promptColor, promptAttributes.attributes.foregroundColor)
+
+      UIUtil.invokeLaterIfNeeded { myEditorEx.gutterComponentEx.revalidateMarkup() }
+    }
+
+  val indentPrompt: String
+    get() =
+    extend(if (myConsoleData.isIPythonEnabled) {
+      PyConsoleUtil.IPYTHON_INDENT_PROMPT
+    }
+    else {
+      PyConsoleUtil.INDENT_PROMPT
+
+    }, mainPrompt.length)
+
+  init {
+    myEditorEx.colorsScheme.setColor(promptColor, this.promptAttributes.attributes.foregroundColor)
+  }
+
+
+  override fun getLineExtensions(project: Project, file: VirtualFile, lineNumber: Int): Collection<LineExtensionInfo>? {
+
+    return null
+
+
+  }
+
+  override fun getLineText(line: Int, editor: Editor): String? {
+    if (line == 0) {
+      return mainPrompt
+    }
+    else if (line > 0) {
+      return indentPrompt
+    }
+    else {
+      return null
+    }
+  }
+
+
+  override fun getToolTip(line: Int, editor: Editor): String? {
+    return null
+  }
+
+  override fun getStyle(line: Int, editor: Editor): EditorFontType {
+    return EditorFontType.CONSOLE_PLAIN
+  }
+
+  override fun getColor(line: Int, editor: Editor): ColorKey? {
+    return promptColor
+  }
+
+  override fun getBgColor(line: Int, editor: Editor): Color? {
+    var backgroundColor: Color? = this.promptAttributes.attributes.backgroundColor
+    if (backgroundColor == null) {
+      backgroundColor = myEditorEx.backgroundColor
+    }
+    return backgroundColor
+  }
+
+  override fun getPopupActions(line: Int, editor: Editor): List<AnAction>? {
+    return null
+  }
+
+  override fun gutterClosed() {
+
+  }
+
+  companion object {
+    private val promptColor = ColorKey.createColorKey("CONSOLE_PROMPT_COLOR")
+
+    private fun extend(s: String, len: Int): String {
+      var res = s
+      while (res.length < len) {
+        res = " " + res
+      }
+
+      return res
+
+    }
+  }
+}
+
diff --git a/python/src/com/jetbrains/python/console/PyConsoleCopyHandler.kt b/python/src/com/jetbrains/python/console/PyConsoleCopyHandler.kt
new file mode 100644 (file)
index 0000000..83bf934
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console
+
+import com.intellij.execution.impl.ConsoleViewUtil
+import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.editor.Caret
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.actionSystem.EditorActionHandler
+import com.intellij.openapi.editor.ex.EditorEx
+import com.intellij.openapi.editor.richcopy.settings.RichCopySettings
+import com.intellij.openapi.ide.CopyPasteManager
+import com.intellij.openapi.util.Key
+import com.intellij.openapi.util.Ref
+import com.intellij.openapi.util.TextRange
+import java.awt.datatransfer.StringSelection
+
+/**
+ * Created by Yuli Fiterman on 9/17/2016.
+ */
+class PyConsoleCopyHandler(val originalHandler: EditorActionHandler) : EditorActionHandler() {
+
+  override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext) {
+    if (!RichCopySettings.getInstance().isEnabled) {
+      return originalHandler.execute(editor, null, dataContext);
+    }
+    if (true != editor.getUserData(ConsoleViewUtil.EDITOR_IS_CONSOLE_HISTORY_VIEW)) {
+      return originalHandler.execute(editor, null, dataContext);
+
+    }
+    doCopyWithoutPrompt(editor as EditorEx);
+  }
+
+  private fun doCopyWithoutPrompt(editor: EditorEx) {
+    val start = editor.selectionModel.selectionStart
+    val end = editor.selectionModel.selectionEnd
+    val document = editor.document
+    val beginLine = document.getLineNumber(start)
+    val endLine = document.getLineNumber(end)
+    val sb = StringBuilder()
+    for (i in beginLine..endLine) {
+      var lineStart = document.getLineStartOffset(i)
+      val r = Ref.create<Int>()
+      editor.markupModel.processRangeHighlightersOverlappingWith(lineStart, lineStart) {
+        val length = it.getUserData(PROMPT_LENGTH_MARKER) ?: return@processRangeHighlightersOverlappingWith true
+        r.set(length)
+        false
+      }
+      if (!r.isNull) {
+        lineStart += r.get()
+      }
+      val rangeStart = Math.max(lineStart, start)
+      val rangeEnd = Math.min(document.getLineEndOffset(i), end)
+      if (rangeStart < rangeEnd) {
+        sb.append(document.getText(TextRange(rangeStart, rangeEnd)))
+        sb.append("\n")
+      }
+    }
+    if (!sb.isEmpty()) {
+      CopyPasteManager.getInstance().setContents(StringSelection(sb.toString()))
+    }
+  }
+
+  companion object {
+    @JvmField
+    val PROMPT_LENGTH_MARKER: Key<Int?> = Key.create<Int>("PROMPT_LENGTH_MARKER");
+
+  }
+}
diff --git a/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt b/python/src/com/jetbrains/python/console/PyConsoleEnterHandler.kt
new file mode 100644 (file)
index 0000000..c5a4054
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console
+
+import com.intellij.ide.DataManager
+import com.intellij.openapi.actionSystem.IdeActions
+import com.intellij.openapi.application.Result
+import com.intellij.openapi.command.WriteCommandAction
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.editor.actionSystem.EditorActionManager
+import com.intellij.openapi.editor.ex.EditorEx
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.*
+import com.intellij.psi.impl.source.codeStyle.IndentHelperImpl
+import com.intellij.psi.util.PsiTreeUtil
+import com.jetbrains.python.PyTokenTypes
+import com.jetbrains.python.PythonFileType
+import com.jetbrains.python.psi.PyStatement
+import com.jetbrains.python.psi.PyStatementListContainer
+import com.jetbrains.python.psi.PyStringLiteralExpression
+import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl
+
+/**
+ * Created by Yuli Fiterman on 9/20/2016.
+ */
+class PyConsoleEnterHandler {
+  fun handleEnterPressed(editor: EditorEx): Boolean {
+    val project = editor.project ?: throw IllegalArgumentException()
+    if (editor.document.lineCount != 0) { // move to end of line
+      editor.selectionModel.removeSelection()
+      val caretPosition = editor.caretModel.logicalPosition
+      val lineEndOffset = editor.document.getLineEndOffset(caretPosition.line)
+      editor.caretModel.moveToOffset(lineEndOffset)
+    }
+    val psiMgr = PsiDocumentManager.getInstance(project)
+    psiMgr.commitDocument(editor.document)
+
+    val caretOffset = editor.expectedCaretOffset
+    val atElement = findFirstNoneSpaceElement(psiMgr.getPsiFile(editor.document)!!, caretOffset)
+    var insideDocString = false
+    atElement?.let {
+      insideDocString = isElementInsideDocString(atElement, caretOffset)
+    }
+    val prevLine = getLineAtOffset(editor.document, caretOffset)
+    if (prevLine.isBlank() && !insideDocString) {
+      return true
+    }
+
+    val isCellMagic = prevLine.trim().startsWith("%%") && !prevLine.trimEnd().endsWith("?")
+    val isCellHelp = prevLine.trim().startsWith("%%") && prevLine.trimEnd().endsWith("?")
+    val isLineCellMagic = prevLine.trim().startsWith("%")
+    val hasCompleteStatement = if (atElement != null && !insideDocString && !isCellMagic) {
+      isCellHelp || isLineCellMagic || checkComplete(atElement)
+    }
+    else {
+      false
+    }
+
+    val enterHandler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_ENTER)
+
+    object : WriteCommandAction<Nothing>(project) {
+
+      @Throws(Throwable::class)
+      override fun run(result: Result<Nothing>) {
+        enterHandler.execute(editor, null, DataManager.getInstance().getDataContext(editor.component))
+      }
+    }.execute()
+
+    /* If we have an indent we don't want to execute either */
+    val currentLine = getLineAtOffset(editor.document, editor.expectedCaretOffset)
+    val indent = IndentHelperImpl.getIndent(project, PythonFileType.INSTANCE, currentLine, false)
+    if (indent > 0) {
+      return false
+    }
+
+    return hasCompleteStatement
+
+
+  }
+
+  private fun isElementInsideDocString(atElement: PsiElement, caretOffset: Int): Boolean {
+    return (atElement.context is PyStringLiteralExpression &&
+        (PyTokenTypes.TRIPLE_NODES.contains(atElement.node.elementType)
+            || atElement.node.elementType === PyTokenTypes.DOCSTRING)
+        && (atElement.textRange.endOffset > caretOffset || !isCompletDocString(atElement.text)))
+  }
+
+  private fun checkComplete(el: PsiElement): Boolean {
+    var el = el
+    while (el.parent !is PsiFile && el.parent != null) {
+      el = el.parent
+    }
+    if (el !is PyStatement) {
+      return false
+    }
+    val container = PsiTreeUtil.findChildOfType(el, PyStatementListContainer::class.java, false)
+    if (container != null) {
+      return false
+    }
+
+    return PsiTreeUtil.findChildOfType(el, PsiErrorElement::class.java, false) == null
+  }
+
+  private fun findFirstNoneSpaceElement(psiFile: PsiFile, offset: Int): PsiElement? {
+    for (i in offset downTo 0) {
+      val el = psiFile.findElementAt(i)
+      if (el != null && el !is PsiWhiteSpace) {
+        return el
+      }
+    }
+    return null
+  }
+
+  private fun getLineAtOffset(doc: Document, offset: Int): String {
+    val line = doc.getLineNumber(offset)
+    val start = doc.getLineStartOffset(line)
+    val end = doc.getLineEndOffset(line)
+    return doc.getText(TextRange(start, end))
+  }
+
+  private fun isCompletDocString(str: String): Boolean {
+    val prefixLen = PyStringLiteralExpressionImpl.getPrefixLength(str)
+    val text = str.substring(prefixLen)
+    for (token in arrayOf("\"\"\"", "'''")) {
+      if (text.length >= 2 * token.length && text.startsWith(token) && text.endsWith(token)) {
+        return true
+      }
+    }
+
+    return false
+
+  }
+}
\ No newline at end of file
index 34eff005481c8656e1a89e65d48bcdf773f44008..bac5c4d5378e7ca7f68e5b56df5500428aac5c45 100644 (file)
@@ -88,7 +88,7 @@ public class PyConsoleOptions implements PersistentStateComponent<PyConsoleOptio
 
   @Tag("console-settings")
   public static class PyConsoleSettings implements PythonRunParams {
-    public String myCustomStartScript = PydevConsoleRunner.CONSOLE_START_COMMAND;
+    public String myCustomStartScript = PydevConsoleRunnerImpl.CONSOLE_START_COMMAND;
     public String mySdkHome = null;
     public String myInterpreterOptions = "";
     public boolean myUseModuleSdk;
diff --git a/python/src/com/jetbrains/python/console/PyConsoleStarter.kt b/python/src/com/jetbrains/python/console/PyConsoleStarter.kt
new file mode 100644 (file)
index 0000000..b5ea727
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console
+
+import com.intellij.execution.ExecutionManager
+import com.intellij.ide.util.PropertiesComponent
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.application.ModalityState
+import com.intellij.openapi.application.TransactionGuard
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.startup.StartupActivity
+import com.intellij.openapi.util.Condition
+import com.intellij.openapi.wm.ToolWindow
+import com.intellij.openapi.wm.ToolWindowManager
+import com.intellij.openapi.wm.ex.ToolWindowManagerEx
+import com.intellij.openapi.wm.ex.ToolWindowManagerListener
+import com.intellij.openapi.wm.impl.content.ToolWindowContentUi
+import com.intellij.util.Alarm
+
+/**
+ * Created by Yuli Fiterman on 9/12/2016.
+ */
+class PyConsoleStarter : StartupActivity {
+  override fun runActivity(project: Project) {
+    if (!ApplicationManager.getApplication().isUnitTestMode) {
+      ConsoleStateTracker(project)
+    }
+  }
+
+  private class ConsoleStateTracker(private val project: Project) {
+    private val VISIBLE: String = "com.jetbrains.python.console.ToolWindowVisible"
+    private var toolWindowVisible: Boolean = PropertiesComponent.getInstance(project).getBoolean(VISIBLE, false)
+      set(value) {
+        if (value != field) {
+          PropertiesComponent.getInstance(project).setValue(VISIBLE, value, false)
+        }
+        field = value
+      }
+
+    private val toolWindow: ToolWindow?
+      get() {
+        return ToolWindowManager.getInstance(project).getToolWindow(PyConsoleToolWindowExecutor.TOOLWINDOW_ID)
+      }
+
+
+    init {
+      ExecutionManager.getInstance(project).contentManager //Using this for the side effect. Force init
+      toolWindow!!.isAutoHide = false
+      toolWindow!!.isToHideOnEmptyContent = true
+      toolWindow!!.component.putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true")
+
+
+      val tvm = ToolWindowManager.getInstance(project) as ToolWindowManagerEx
+
+      tvm.addToolWindowManagerListener(object : ToolWindowManagerListener {
+        override fun toolWindowRegistered(id: String) {
+        }
+
+        override fun stateChanged() {
+          val toolW = toolWindow ?: return;
+          if (toolW.isVisible && toolW.contentManager.contentCount == 0) {
+            val runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(project, null);
+            runner.runSync();
+          }
+          if (toolWindowVisible != toolW.isVisible) {
+            ApplicationManager.getApplication().invokeLater(
+                {
+                  toolWindowVisible = toolWindow?.isVisible ?: false
+                }, ModalityState.NON_MODAL, { !project.isOpen || project.isDisposed })
+
+
+          }
+        }
+      })
+
+
+      if (toolWindowVisible) {
+        toolWindow!!.setAvailable(true) {
+          TransactionGuard.submitTransaction(project, Runnable { toolWindow!!.show(null) })
+
+        }
+      }
+
+    }
+  }
+
+}
diff --git a/python/src/com/jetbrains/python/console/PyConsoleToolWindowExecutor.java b/python/src/com/jetbrains/python/console/PyConsoleToolWindowExecutor.java
new file mode 100644 (file)
index 0000000..fb5a683
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console;
+
+import com.intellij.execution.Executor;
+import com.intellij.openapi.extensions.Extensions;
+import icons.PythonIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+ * Created by Yuli Fiterman on 9/11/2016.
+ */
+public class PyConsoleToolWindowExecutor extends Executor {
+
+
+  public static final String ID = "PyConsoleToolWindowExecutor";
+  public static final String TOOLWINDOW_ID = "Python Console";
+
+  @Nullable
+  public static PyConsoleToolWindowExecutor findInstance() {
+    for (Executor t : Extensions.getExtensions(INTERNAL_EXECUTOR_EXTENSION_NAME)) {
+      if (PyConsoleToolWindowExecutor.class.isInstance(t)) {
+        return (PyConsoleToolWindowExecutor)t;
+      }
+    }
+
+    return null;
+  }
+
+  @Override
+  public String getToolWindowId() {
+    return TOOLWINDOW_ID;
+  }
+
+  @Override
+  public Icon getToolWindowIcon() {
+    return PythonIcons.Python.PythonConsoleToolWindow;
+  }
+
+  @NotNull
+  @Override
+  public Icon getIcon() {
+    return PythonIcons.Python.PythonConsoleToolWindow;
+  }
+
+  @Override
+  public Icon getDisabledIcon() {
+    return null;
+  }
+
+  @Override
+  public String getDescription() {
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public String getActionName() {
+    return "Run Python Console";
+  }
+
+  @NotNull
+  @Override
+  public String getId() {
+    return ID;
+  }
+
+  @NotNull
+  @Override
+  public String getStartActionText() {
+    return "Starting Python Console";
+  }
+
+  @Override
+  public String getContextActionId() {
+    return "";
+  }
+
+  @Override
+  public String getHelpId() {
+    return null;
+  }
+}
index f37ed1eb78f5040cfd9291427cc597867f159408..a2218e70270ef0b4a70b2a1ed3198c524f98aeb3 100644 (file)
@@ -30,10 +30,12 @@ import org.jetbrains.annotations.Nullable;
  * @author traff
  */
 public class PyConsoleUtil {
-  public static final String ORDINARY_PROMPT = ">>> ";
-  public static final String INPUT_PROMPT = ">? ";
-  public static final String INDENT_PROMPT = "... ";
-  static final String HELP_PROMPT = "help> ";
+  public static final String ORDINARY_PROMPT = ">>>";
+  public static final String INPUT_PROMPT = ">?";
+  public static final String INDENT_PROMPT = "...";
+  public static final String IPYTHON_INDENT_PROMPT = "...:";
+
+  static final String HELP_PROMPT = "help>";
   public static final String EXECUTING_PROMPT = "";
 
   private static final String IPYTHON_PAGING_PROMPT = "---Return to continue, q to quit---";
@@ -44,8 +46,7 @@ public class PyConsoleUtil {
     HELP_PROMPT,
     IPYTHON_PAGING_PROMPT
   };
-  public static final String DOUBLE_QUOTE_MULTILINE = "\"\"\"";
-  public static final String SINGLE_QUOTE_MULTILINE = "'''";
+
 
   static final Key<PythonConsoleData> PYTHON_CONSOLE_DATA = Key.create("python-console-data");
 
@@ -90,22 +91,12 @@ public class PyConsoleUtil {
     return string;
   }
 
-  public static boolean isMultilineStarts(String line, String substring) {
-    return StringUtil.getOccurrenceCount(line, substring) % 2 == 1;
-  }
 
   public static void scrollDown(final Editor currentEditor) {
     ApplicationManager.getApplication().invokeLater(
       () -> currentEditor.getCaretModel().moveToOffset(currentEditor.getDocument().getTextLength()));
   }
 
-  public static boolean isSingleQuoteMultilineStarts(String line) {
-    return isMultilineStarts(line, SINGLE_QUOTE_MULTILINE);
-  }
-
-  public static boolean isDoubleQuoteMultilineStarts(String line) {
-    return isMultilineStarts(line, DOUBLE_QUOTE_MULTILINE);
-  }
 
   public static boolean detectIPythonImported(@NotNull String text, final ConsoleViewContentType outputType) {
     return text.contains("PyDev console: using IPython ") && outputType == ConsoleViewContentType.ERROR_OUTPUT;
index 3398c978090139951a934512473f60f9c77fac38..d546ebb449d12dbbb308a67de06a0bb0d62ab04b 100644 (file)
@@ -101,6 +101,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
 
   private boolean myExecuting;
   private PythonDebugConsoleCommunication myDebugCommunication;
+  private boolean myNeedsMore = false;
 
   /**
    * Initializes the xml-rpc communication.
@@ -137,7 +138,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
    */
   public synchronized void close() {
     if (this.myClient != null) {
-      new Task.Backgroundable(myProject, "Close console communication", true) {
+      new Task.Backgroundable(myProject, "Close Console Communication", true) {
         @Override
         public void run(@NotNull ProgressIndicator indicator) {
           try {
@@ -227,6 +228,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
   }
 
   private Object execNotifyFinished(boolean more) {
+    myNeedsMore = more;
     setExecuting(false);
     notifyCommandExecuted(more);
     return true;
@@ -326,7 +328,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
     if (waitingForInput) {
       return "Unable to get description: waiting for input.";
     }
-    return myClient.execute(GET_DESCRIPTION, new Object[]{text}).toString();
+    return myClient.execute(GET_DESCRIPTION, new Object[]{text}, 5000).toString();
   }
 
   /**
@@ -438,6 +440,10 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
             }
           }
         }
+        if (nextResponse.more) {
+          myNeedsMore = true;
+          notifyCommandExecuted(true);
+        }
         onResponseReceived.fun(nextResponse);
       }, "Waiting for REPL response", true, myProject);
     }
@@ -458,6 +464,10 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
     return myExecuting;
   }
 
+  public boolean needsMore() {
+    return myNeedsMore;
+  }
+
   @Override
   public PyDebugValue evaluate(String expression, boolean execute, boolean doTrunc) throws PyDebuggerException {
     if (myClient != null) {
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.java
deleted file mode 100644 (file)
index 3ec2c23..0000000
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.console;
-
-import com.intellij.codeInsight.hint.HintManager;
-import com.intellij.execution.console.LanguageConsoleView;
-import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler;
-import com.intellij.execution.process.ProcessHandler;
-import com.intellij.execution.ui.ConsoleViewContentType;
-import com.intellij.openapi.application.Result;
-import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorModificationUtil;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.editor.markup.TextAttributes;
-import com.intellij.openapi.fileTypes.PlainTextLanguage;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
-import com.intellij.psi.impl.source.codeStyle.IndentHelperImpl;
-import com.intellij.util.Function;
-import com.intellij.util.ui.UIUtil;
-import com.jetbrains.python.PythonFileType;
-import com.jetbrains.python.PythonLanguage;
-import com.jetbrains.python.console.pydev.ConsoleCommunication;
-import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
-import com.jetbrains.python.console.pydev.InterpreterResponse;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.awt.*;
-
-/**
- * @author traff
- */
-public class PydevConsoleExecuteActionHandler extends ProcessBackedConsoleExecuteActionHandler implements ConsoleCommunicationListener {
-  private final LanguageConsoleView myConsoleView;
-
-  private String myInMultilineStringState = null;
-  private StringBuilder myInputBuffer;
-  private int myCurrentIndentSize = 0;
-
-  private final ConsoleCommunication myConsoleCommunication;
-  private boolean myEnabled = false;
-
-  private int myIpythonInputPromptCount = 1;
-
-  public PydevConsoleExecuteActionHandler(LanguageConsoleView consoleView,
-                                          ProcessHandler processHandler,
-                                          ConsoleCommunication consoleCommunication) {
-    super(processHandler, false);
-    myConsoleView = consoleView;
-    myConsoleCommunication = consoleCommunication;
-    myConsoleCommunication.addCommunicationListener(this);
-  }
-
-  @Override
-  public void processLine(@NotNull final String text) {
-    processLine(text, false);
-  }
-
-  public void processLine(@NotNull final String text, boolean execAnyway) {
-    int indentBefore = myCurrentIndentSize;
-    if (text.isEmpty()) {
-      processOneLine(text);
-    }
-    else {
-      if (StringUtil.countNewLines(text.trim()) > 0) {
-        executeMultiLine(text);
-      }
-      else {
-        processOneLine(text);
-      }
-    }
-    if (execAnyway && myCurrentIndentSize > 0 && indentBefore == 0) { //if code was indented and we need to exec anyway
-      finishExecution();
-    }
-  }
-
-  private void executeMultiLine(@NotNull String text) {
-    if (myInputBuffer == null) {
-      myInputBuffer = new StringBuilder();
-    }
-    myInputBuffer.append(text);
-
-    sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), false));
-  }
-
-  public void clearInputBuffer() {
-    myInputBuffer = null;
-  }
-
-  private void processOneLine(String line) {
-    int indentSize = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false);
-    line = StringUtil.trimTrailing(line);
-    if (StringUtil.isEmptyOrSpaces(line)) {
-      doProcessLine("\n");
-    }
-    else if (indentSize == 0 &&
-             indentSize < myCurrentIndentSize &&
-             !PyConsoleIndentUtil.shouldIndent(line) &&
-             !myConsoleCommunication.isWaitingForInput()) {
-      doProcessLine("\n");
-      doProcessLine(line);
-    }
-    else {
-      doProcessLine(line);
-    }
-  }
-
-  public void doProcessLine(final String line) {
-    if (myInputBuffer == null) {
-      myInputBuffer = new StringBuilder();
-    }
-
-    if (!StringUtil.isEmptyOrSpaces(line)) {
-      myInputBuffer.append(line);
-      if (!line.endsWith("\n")) {
-        myInputBuffer.append("\n");
-      }
-    }
-
-    if (StringUtil.isEmptyOrSpaces(line) && StringUtil.isEmptyOrSpaces(myInputBuffer.toString())) {
-      myInputBuffer.append("");
-    }
-
-    // multiline strings handling
-    if (myInMultilineStringState != null) {
-      if (PyConsoleUtil.isDoubleQuoteMultilineStarts(line) || PyConsoleUtil.isSingleQuoteMultilineStarts(line)) {
-        myInMultilineStringState = null;
-        // restore language
-        myConsoleView.setLanguage(PythonLanguage.getInstance());
-        myConsoleView.setPrompt(PyConsoleUtil.ORDINARY_PROMPT);
-      }
-      else {
-        if (line.equals("\n")) {
-          myInputBuffer.append("\n");
-        }
-        return;
-      }
-    }
-    else {
-      if (PyConsoleUtil.isDoubleQuoteMultilineStarts(line)) {
-        myInMultilineStringState = PyConsoleUtil.DOUBLE_QUOTE_MULTILINE;
-      }
-      else if (PyConsoleUtil.isSingleQuoteMultilineStarts(line)) {
-        myInMultilineStringState = PyConsoleUtil.SINGLE_QUOTE_MULTILINE;
-      }
-      if (myInMultilineStringState != null) {
-        // change language
-        myConsoleView.setLanguage(PlainTextLanguage.INSTANCE);
-        myConsoleView.setPrompt(PyConsoleUtil.INDENT_PROMPT);
-        return;
-      }
-    }
-
-    // Process line continuation
-    if (line.endsWith("\\")) {
-      myConsoleView.setPrompt(PyConsoleUtil.INDENT_PROMPT);
-      return;
-    }
-
-    if (!StringUtil.isEmptyOrSpaces(line)) {
-      int indent = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false);
-      boolean flag = false;
-      if (PyConsoleIndentUtil.shouldIndent(line)) {
-        indent += getPythonIndent();
-        flag = true;
-      }
-      if ((myCurrentIndentSize > 0 && indent > 0) || flag) {
-        setCurrentIndentSize(indent);
-        indentEditor(myConsoleView.getConsoleEditor(), indent);
-        more();
-
-        myConsoleCommunication.notifyCommandExecuted(true);
-        return;
-      }
-    }
-
-
-    sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), true));
-  }
-
-  private void sendLineToConsole(@NotNull final ConsoleCommunication.ConsoleCodeFragment code) {
-    if (!StringUtil.isEmptyOrSpaces(code.getText())) {
-      myIpythonInputPromptCount += 1;
-    }
-    if (myConsoleCommunication != null) {
-      final boolean waitedForInputBefore = myConsoleCommunication.isWaitingForInput();
-      if (myConsoleCommunication.isWaitingForInput()) {
-        myInputBuffer.setLength(0);
-      }
-      else {
-        executingPrompt();
-      }
-      myConsoleCommunication.execInterpreter(code, interpreterResponse -> {
-        // clear
-        myInputBuffer = null;
-
-        //notify listeners like xdebugger variables view re-builder
-        getConsoleCommunication().notifyCommandExecuted(interpreterResponse.more);
-
-        // Handle prompt
-        if (interpreterResponse.more) {
-          more();
-          if (myCurrentIndentSize == 0) {
-            // compute current indentation
-            setCurrentIndentSize(
-              IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, lastLine(code.getText()), false) + getPythonIndent());
-            // In this case we can insert indent automatically
-            final EditorEx editor = myConsoleView.getConsoleEditor();
-            UIUtil.invokeLaterIfNeeded(() -> indentEditor(editor, myCurrentIndentSize));
-          }
-        }
-        else {
-          if (!myConsoleCommunication.isWaitingForInput()) {
-            inPrompt();
-          }
-          setCurrentIndentSize(0);
-        }
-
-        return null;
-      });
-      // After requesting input we got no call back to change prompt, change it manually
-      if (waitedForInputBefore && !myConsoleCommunication.isWaitingForInput()) {
-        myIpythonInputPromptCount -= 1;
-        inPrompt();
-        setCurrentIndentSize(0);
-      }
-    }
-  }
-
-  private static String lastLine(@NotNull String text) {
-    String[] lines = StringUtil.splitByLinesDontTrim(text);
-    return lines[lines.length - 1];
-  }
-
-  private void inPrompt() {
-    if (ipythonEnabled()) {
-      ipythonInPrompt();
-    }
-    else {
-      ordinaryPrompt();
-    }
-  }
-
-  private void ordinaryPrompt() {
-    if (!myConsoleCommunication.isExecuting()) {
-      if (!PyConsoleUtil.ORDINARY_PROMPT.equals(myConsoleView.getPrompt())) {
-        myConsoleView.setPrompt(PyConsoleUtil.ORDINARY_PROMPT);
-        PyConsoleUtil.scrollDown(myConsoleView.getCurrentEditor());
-      }
-    }
-    else {
-      executingPrompt();
-    }
-  }
-
-  private boolean ipythonEnabled() {
-    return PyConsoleUtil.getOrCreateIPythonData(myConsoleView.getVirtualFile()).isIPythonEnabled();
-  }
-
-  private void ipythonInPrompt() {
-    myConsoleView.setPromptAttributes(new ConsoleViewContentType("", ConsoleViewContentType.USER_INPUT_KEY) {
-      @Override
-      public TextAttributes getAttributes() {
-        TextAttributes attrs = super.getAttributes();
-        attrs.setFontType(Font.PLAIN);
-        return attrs;
-      }
-    });
-    myConsoleView.setPrompt("In[" + myIpythonInputPromptCount + "]:");
-    PyConsoleUtil.scrollDown(myConsoleView.getCurrentEditor());
-  }
-
-  private void executingPrompt() {
-    myConsoleView.setPrompt(PyConsoleUtil.EXECUTING_PROMPT);
-  }
-
-  private void more() {
-    if (!PyConsoleUtil.INDENT_PROMPT.equals(myConsoleView.getPrompt())) {
-      myConsoleView.setPrompt(PyConsoleUtil.INDENT_PROMPT);
-      PyConsoleUtil.scrollDown(myConsoleView.getCurrentEditor());
-    }
-  }
-
-  public static String getPrevCommandRunningMessage() {
-    return "Previous command is still running. Please wait or press Ctrl+C in console to interrupt.";
-  }
-
-  @Override
-  public void commandExecuted(boolean more) {
-    if (!more && !ipythonEnabled() && !myConsoleCommunication.isWaitingForInput()) {
-      ordinaryPrompt();
-    }
-  }
-
-  @Override
-  public void inputRequested() {
-    final LanguageConsoleView console = myConsoleView;
-    final Editor currentEditor = console.getConsoleEditor();
-
-    if (!PyConsoleUtil.INPUT_PROMPT.equals(console.getPrompt()) && !PyConsoleUtil.HELP_PROMPT.equals(console.getPrompt())) {
-      console.setPrompt(PyConsoleUtil.INPUT_PROMPT);
-      PyConsoleUtil.scrollDown(currentEditor);
-    }
-    setCurrentIndentSize(1);
-  }
-
-  public void finishExecution() {
-    final LanguageConsoleView console = myConsoleView;
-    final Editor currentEditor = console.getConsoleEditor();
-
-    if (myInputBuffer != null) {
-      processLine("\n");
-    }
-
-    cleanEditor(currentEditor);
-    //console.setPrompt(PyConsoleHighlightingUtil.ORDINARY_PROMPT);
-  }
-
-  public int getCurrentIndentSize() {
-    return myCurrentIndentSize;
-  }
-
-  public void setCurrentIndentSize(int currentIndentSize) {
-    myCurrentIndentSize = currentIndentSize;
-    VirtualFile file = getConsoleFile();
-    if (file != null) {
-      PyConsoleUtil.setCurrentIndentSize(file, currentIndentSize);
-    }
-  }
-
-  @Nullable
-  private VirtualFile getConsoleFile() {
-    if (myConsoleView != null) {
-      return myConsoleView.getFile().getVirtualFile();
-    }
-    else {
-      return null;
-    }
-  }
-
-  public int getPythonIndent() {
-    return CodeStyleSettingsManager.getSettings(getProject()).getIndentSize(PythonFileType.INSTANCE);
-  }
-
-  private void indentEditor(final Editor editor, final int indentSize) {
-    new WriteCommandAction(getProject()) {
-      @Override
-      protected void run(@NotNull Result result) throws Throwable {
-        EditorModificationUtil.insertStringAtCaret(editor, IndentHelperImpl.fillIndent(getProject(), PythonFileType.INSTANCE, indentSize));
-      }
-    }.execute();
-  }
-
-  private void cleanEditor(final Editor editor) {
-    new WriteCommandAction(getProject()) {
-      @Override
-      protected void run(@NotNull Result result) throws Throwable {
-        editor.getDocument().setText("");
-      }
-    }.execute();
-  }
-
-  private Project getProject() {
-    return myConsoleView.getProject();
-  }
-
-  public String getCantExecuteMessage() {
-    if (!isEnabled()) {
-      return getConsoleIsNotEnabledMessage();
-    }
-    else if (!canExecuteNow()) {
-      return getPrevCommandRunningMessage();
-    }
-    else {
-      return "Can't execute the command";
-    }
-  }
-
-  @Override
-  public void runExecuteAction(@NotNull LanguageConsoleView console) {
-    if (isEnabled()) {
-      if (!canExecuteNow()) {
-        HintManager.getInstance().showErrorHint(console.getConsoleEditor(), getPrevCommandRunningMessage());
-      }
-      else {
-        doRunExecuteAction(console);
-      }
-    }
-    else {
-      HintManager.getInstance().showErrorHint(console.getConsoleEditor(), getConsoleIsNotEnabledMessage());
-    }
-  }
-
-  private void doRunExecuteAction(LanguageConsoleView console) {
-    if (shouldCopyToHistory(console)) {
-      copyToHistoryAndExecute(console);
-    }
-    else {
-      processLine(console.getConsoleEditor().getDocument().getText());
-    }
-  }
-
-  private static boolean shouldCopyToHistory(@NotNull LanguageConsoleView console) {
-    return !PyConsoleUtil.isPagingPrompt(console.getPrompt());
-  }
-
-  private void copyToHistoryAndExecute(LanguageConsoleView console) {
-    super.runExecuteAction(console);
-  }
-
-  public boolean canExecuteNow() {
-    return !myConsoleCommunication.isExecuting() || myConsoleCommunication.isWaitingForInput();
-  }
-
-  protected String getConsoleIsNotEnabledMessage() {
-    return "Console is not enabled.";
-  }
-
-  protected void setEnabled(boolean flag) {
-    myEnabled = flag;
-  }
-
-  public boolean isEnabled() {
-    return myEnabled;
-  }
-
-  public ConsoleCommunication getConsoleCommunication() {
-    return myConsoleCommunication;
-  }
-}
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.kt b/python/src/com/jetbrains/python/console/PydevConsoleExecuteActionHandler.kt
new file mode 100644 (file)
index 0000000..55e5c2b
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console
+
+import com.intellij.codeInsight.hint.HintManager
+import com.intellij.execution.console.LanguageConsoleView
+import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler
+import com.intellij.execution.process.ProcessHandler
+import com.intellij.execution.ui.ConsoleViewContentType
+import com.intellij.openapi.application.Result
+import com.intellij.openapi.command.WriteCommandAction
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.markup.TextAttributes
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager
+import com.jetbrains.python.PythonFileType
+import com.jetbrains.python.console.pydev.ConsoleCommunication
+import com.jetbrains.python.console.pydev.ConsoleCommunicationListener
+import java.awt.Font
+
+/**
+ * @author traff
+ */
+open class PydevConsoleExecuteActionHandler(private val myConsoleView: LanguageConsoleView,
+                                            processHandler: ProcessHandler,
+                                            val consoleCommunication: ConsoleCommunication) : ProcessBackedConsoleExecuteActionHandler(processHandler, false), ConsoleCommunicationListener {
+
+  private val project = myConsoleView.project
+  private val myEnterHandler = PyConsoleEnterHandler()
+  private var myIpythonInputPromptCount = 1
+
+  var isEnabled = false
+    set(value) {
+      field = value
+      updateConsoleState()
+    }
+
+  init {
+    this.consoleCommunication.addCommunicationListener(this)
+  }
+
+
+  override fun processLine(text: String) {
+    executeMultiLine(text)
+  }
+
+
+  private fun executeMultiLine(text: String) {
+    val commandText = if (!text.endsWith("\n")) {
+      text + "\n"
+    }
+    else {
+      text
+    }
+    val singleLine = text.count { it == '\n' } < 2
+    sendLineToConsole(ConsoleCommunication.ConsoleCodeFragment(commandText, singleLine))
+  }
+
+  private fun sendLineToConsole(code: ConsoleCommunication.ConsoleCodeFragment) {
+
+    val consoleComm = consoleCommunication
+    if (!consoleComm.isWaitingForInput) {
+      executingPrompt()
+    }
+    if (ipythonEnabled && !consoleComm.isWaitingForInput && !code.getText().isBlank()) {
+      ++myIpythonInputPromptCount;
+    }
+
+    consoleComm.execInterpreter(code) {}
+  }
+
+
+  private fun updateConsoleState() {
+    if (!isEnabled) {
+      executingPrompt()
+    }
+    else if (consoleCommunication.isWaitingForInput) {
+      waitingForInputPrompt()
+    }
+    else if (canExecuteNow()) {
+      if (consoleCommunication.needsMore()) {
+        more()
+      }
+      else {
+        inPrompt()
+      }
+    }
+    else {
+      executingPrompt()
+    }
+  }
+
+
+  private fun inPrompt() {
+    if (ipythonEnabled) {
+      ipythonInPrompt()
+    }
+    else {
+      ordinaryPrompt()
+    }
+  }
+
+  private fun ordinaryPrompt() {
+    if (PyConsoleUtil.ORDINARY_PROMPT != myConsoleView.prompt) {
+      myConsoleView.prompt = PyConsoleUtil.ORDINARY_PROMPT
+      PyConsoleUtil.scrollDown(myConsoleView.currentEditor)
+    }
+
+
+  }
+
+  private val ipythonEnabled: Boolean
+    get() = PyConsoleUtil.getOrCreateIPythonData(myConsoleView.virtualFile).isIPythonEnabled
+
+  private fun ipythonInPrompt() {
+    myConsoleView.setPromptAttributes(object : ConsoleViewContentType("", ConsoleViewContentType.USER_INPUT_KEY) {
+      override fun getAttributes(): TextAttributes {
+        val attrs = super.getAttributes()
+        attrs.fontType = Font.PLAIN
+        return attrs
+      }
+    })
+
+    myConsoleView.prompt = "In[$myIpythonInputPromptCount]:"
+    PyConsoleUtil.scrollDown(myConsoleView.currentEditor)
+  }
+
+  private fun executingPrompt() {
+    myConsoleView.prompt = PyConsoleUtil.EXECUTING_PROMPT
+  }
+
+  private fun waitingForInputPrompt() {
+    if (PyConsoleUtil.INPUT_PROMPT != myConsoleView.prompt && PyConsoleUtil.HELP_PROMPT != myConsoleView.prompt) {
+      myConsoleView.prompt = PyConsoleUtil.INPUT_PROMPT
+      PyConsoleUtil.scrollDown(myConsoleView.currentEditor)
+    }
+  }
+
+  private fun more() {
+    val prompt = if (ipythonEnabled) {
+      PyConsoleUtil.IPYTHON_INDENT_PROMPT
+    }
+    else {
+      PyConsoleUtil.INDENT_PROMPT
+    }
+    if (prompt != myConsoleView.prompt) {
+      myConsoleView.prompt = prompt
+      PyConsoleUtil.scrollDown(myConsoleView.currentEditor)
+    }
+
+
+  }
+
+  override fun commandExecuted(more: Boolean) = updateConsoleState()
+
+  override fun inputRequested() = updateConsoleState()
+
+
+  val pythonIndent: Int
+    get() = CodeStyleSettingsManager.getSettings(project).getIndentSize(PythonFileType.INSTANCE)
+
+
+
+  val cantExecuteMessage: String
+    get() {
+      if (!isEnabled) {
+        return consoleIsNotEnabledMessage
+      }
+      else if (!canExecuteNow()) {
+        return prevCommandRunningMessage
+      }
+      else {
+        return "Can't execute the command"
+      }
+    }
+
+  override fun runExecuteAction(console: LanguageConsoleView) {
+    if (isEnabled) {
+      if (!canExecuteNow()) {
+        HintManager.getInstance().showErrorHint(console.consoleEditor, prevCommandRunningMessage)
+      }
+      else {
+        doRunExecuteAction(console)
+      }
+    }
+    else {
+      HintManager.getInstance().showErrorHint(console.consoleEditor, consoleIsNotEnabledMessage)
+    }
+  }
+
+
+  private fun stripEmptyLines(editor: Editor) {
+    object : WriteCommandAction<Nothing>(project) {
+      @Throws(Throwable::class)
+      override fun run(result: Result<Nothing>) {
+        val document = editor.document
+        val lineCount = document.lineCount
+        if (lineCount < 2)
+          return
+        for (count in lineCount - 1 downTo 1) {
+          val lineEndOffset = document.getLineEndOffset(count)
+          val lineStartOffset = document.getLineStartOffset(count)
+          val textRange = TextRange(lineStartOffset, lineEndOffset)
+          val text = document.getText(textRange)
+          if (text.isEmpty()) {
+            document.deleteString(lineStartOffset - 1, lineStartOffset)
+          }
+          else {
+            break
+          }
+
+
+        }
+      }
+    }.execute()
+  }
+
+
+  private fun doRunExecuteAction(console: LanguageConsoleView) {
+
+    val isComplete = myEnterHandler.handleEnterPressed(console.consoleEditor)
+    if (isComplete || consoleCommunication.isWaitingForInput) {
+
+      stripEmptyLines(myConsoleView.consoleEditor)
+      if (shouldCopyToHistory(console)) {
+        copyToHistoryAndExecute(console)
+      }
+      else {
+        processLine(myConsoleView.consoleEditor.document.text)
+      }
+    }
+
+  }
+
+  private fun copyToHistoryAndExecute(console: LanguageConsoleView) = super.runExecuteAction(console)
+
+  fun canExecuteNow(): Boolean = !consoleCommunication.isExecuting || consoleCommunication.isWaitingForInput
+
+  protected open val consoleIsNotEnabledMessage: String
+    get() = notEnabledMessage
+
+  companion object {
+
+    val prevCommandRunningMessage: String
+      get() = "Previous command is still running. Please wait or press Ctrl+C in console to interrupt."
+
+    val notEnabledMessage: String
+      get() = "Console is not enabled."
+
+    private fun shouldCopyToHistory(console: LanguageConsoleView): Boolean {
+      return !PyConsoleUtil.isPagingPrompt(console.prompt)
+    }
+  }
+}
index 938dff0b3344869ce6fae8e363f1720cb00a1a40..d638fcbc0e938037dcdcc875f83ac44505f3a5f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 package com.jetbrains.python.console;
 
-import com.google.common.base.CharMatcher;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Collections2;
-import com.intellij.codeInsight.lookup.LookupManager;
-import com.intellij.execution.ExecutionException;
-import com.intellij.execution.ExecutionHelper;
-import com.intellij.execution.Executor;
-import com.intellij.execution.configurations.EncodingEnvironmentUtil;
 import com.intellij.execution.configurations.GeneralCommandLine;
-import com.intellij.execution.configurations.ParamsGroup;
-import com.intellij.execution.configurations.PtyCommandLine;
-import com.intellij.execution.console.ConsoleHistoryController;
 import com.intellij.execution.console.LanguageConsoleView;
-import com.intellij.execution.console.ProcessBackedConsoleExecuteActionHandler;
-import com.intellij.execution.executors.DefaultRunExecutor;
-import com.intellij.execution.process.ProcessAdapter;
-import com.intellij.execution.process.ProcessEvent;
-import com.intellij.execution.process.ProcessOutputTypes;
-import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory;
-import com.intellij.execution.ui.RunContentDescriptor;
-import com.intellij.icons.AllIcons;
-import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
-import com.intellij.internal.statistic.UsageTrigger;
 import com.intellij.lang.ASTNode;
-import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.actionSystem.ex.ActionUtil;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.Result;
-import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.actionSystem.EditorAction;
-import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
-import com.intellij.openapi.editor.actions.SplitLineAction;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleManager;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.Task;
-import com.intellij.openapi.project.DumbAware;
-import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.*;
-import com.intellij.openapi.util.io.StreamUtil;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vfs.CharsetToolkit;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
-import com.intellij.remote.RemoteProcess;
-import com.intellij.remote.Tunnelable;
-import com.intellij.testFramework.LightVirtualFile;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.IJSwingUtilities;
 import com.intellij.util.PathMappingSettings;
-import com.intellij.util.TimeoutUtil;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.net.NetUtils;
-import com.intellij.util.ui.MessageCategory;
-import com.intellij.util.ui.UIUtil;
-import com.intellij.xdebugger.XDebugProcess;
-import com.intellij.xdebugger.XDebugProcessStarter;
-import com.intellij.xdebugger.XDebugSession;
-import com.intellij.xdebugger.XDebuggerManager;
-import com.jetbrains.python.PythonHelper;
 import com.jetbrains.python.console.completion.PydevConsoleElement;
 import com.jetbrains.python.console.parsing.PythonConsoleData;
 import com.jetbrains.python.console.pydev.ConsoleCommunication;
-import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
-import com.jetbrains.python.debugger.PyDebugRunner;
-import com.jetbrains.python.debugger.PySourcePosition;
 import com.jetbrains.python.remote.PyRemotePathMapper;
-import com.jetbrains.python.remote.PyRemoteProcessHandlerBase;
 import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
-import com.jetbrains.python.run.*;
+import com.jetbrains.python.run.PythonCommandLineState;
 import com.jetbrains.python.sdk.PySdkUtil;
 import com.jetbrains.python.sdk.PythonSdkType;
 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
-import icons.PythonIcons;
-import org.apache.xmlrpc.XmlRpcException;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.io.File;
-import java.io.IOException;
-import java.net.ServerSocket;
 import java.nio.charset.Charset;
-import java.util.*;
-import java.util.List;
+import java.util.Collection;
+import java.util.Map;
 
 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonIOEncoding;
 import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered;
 
 /**
- * @author oleg
+ * Created by Yuli Fiterman on 9/13/2016.
  */
-public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonConsoleView> {
-  public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
-  public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
-                                                     "sys.path.extend([" + WORKING_DIR_ENV + "])\n";
-  private static final Logger LOG = Logger.getInstance(PydevConsoleRunner.class.getName());
-  @SuppressWarnings("SpellCheckingInspection")
-  public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
-  public static final int PORTS_WAITING_TIMEOUT = 20000;
-  private static final String CONSOLE_FEATURE = "python.console";
+public interface PydevConsoleRunner {
 
-  @NotNull
-  private Sdk mySdk;
-  private GeneralCommandLine myGeneralCommandLine;
-  protected int[] myPorts;
-  private PydevConsoleCommunication myPydevConsoleCommunication;
-  private PyConsoleProcessHandler myProcessHandler;
-  protected PydevConsoleExecuteActionHandler myConsoleExecuteActionHandler;
-  private List<ConsoleListener> myConsoleListeners = ContainerUtil.createLockFreeCopyOnWriteList();
-  private final PyConsoleType myConsoleType;
-  private Map<String, String> myEnvironmentVariables;
-  private String myCommandLine;
-  @NotNull private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
-  private String[] myStatementsToExecute = ArrayUtil.EMPTY_STRING_ARRAY;
-
-  public static Key<ConsoleCommunication> CONSOLE_KEY = new Key<>("PYDEV_CONSOLE_KEY");
-
-  public static Key<Sdk> CONSOLE_SDK = new Key<>("PYDEV_CONSOLE_SDK_KEY");
-
-  private static final long APPROPRIATE_TO_WAIT = 60000;
-
-  private PyRemoteProcessHandlerBase myRemoteProcessHandlerBase;
-
-  private String myConsoleTitle = null;
+  Key<ConsoleCommunication> CONSOLE_KEY = new Key<>("PYDEV_CONSOLE_KEY");
+  Key<Sdk> CONSOLE_SDK = new Key<>("PYDEV_CONSOLE_SDK_KEY");
 
-  public PydevConsoleRunner(@NotNull final Project project,
-                            @NotNull Sdk sdk,
-                            @NotNull final PyConsoleType consoleType,
-                            @Nullable final String workingDir,
-                            Map<String, String> environmentVariables,
-                            @NotNull
-                              PyConsoleOptions.PyConsoleSettings settingsProvider,
-                            String... statementsToExecute) {
-    super(project, consoleType.getTitle(), workingDir);
-    mySdk = sdk;
-    myConsoleType = consoleType;
-    myEnvironmentVariables = environmentVariables;
-    myConsoleSettings = settingsProvider;
-    myStatementsToExecute = statementsToExecute;
+  public interface ConsoleListener {
+    void handleConsoleInitialized(LanguageConsoleView consoleView);
   }
 
+
   @Nullable
-  public static PyRemotePathMapper getPathMapper(@NotNull Project project, Sdk sdk, PyConsoleOptions.PyConsoleSettings consoleSettings) {
+  static PyRemotePathMapper getPathMapper(@NotNull Project project, Sdk sdk, PyConsoleOptions.PyConsoleSettings consoleSettings) {
     if (PySdkUtil.isRemote(sdk)) {
       PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance();
       if (instance != null) {
@@ -194,7 +85,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
   }
 
   @NotNull
-  public static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) {
+  static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) {
     Sdk sdk = null;
     Module module = null;
     PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings();
@@ -251,7 +142,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
     return Pair.create(sdk, module);
   }
 
-  public static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
+  static String constructPythonPathCommand(Collection<String> pythonPath, String command) {
     final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() {
       @Override
       public String apply(String input) {
@@ -259,10 +150,10 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
       }
     }));
 
-    return command.replace(WORKING_DIR_ENV, path);
+    return command.replace(PydevConsoleRunnerImpl.WORKING_DIR_ENV, path);
   }
 
-  public static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs, @NotNull Project project) {
+  static Map<String, String> addDefaultEnvironments(Sdk sdk, Map<String, String> envs, @NotNull Project project) {
     setCorrectStdOutEncoding(envs, project);
 
     PythonSdkFlavor.initPythonPath(envs, true, PythonCommandLineState.getAddedPaths(sdk));
@@ -275,8 +166,8 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
    * @param envs    map of envs to add variable
    * @param project current project
    */
-  public static void setCorrectStdOutEncoding(@NotNull final Map<String, String> envs, @NotNull final Project project) {
-    final Charset defaultCharset = getProjectDefaultCharset(project);
+  static void setCorrectStdOutEncoding(@NotNull Map<String, String> envs, @NotNull Project project) {
+    final Charset defaultCharset = EncodingProjectManager.getInstance(project).getDefaultCharset();
     final String encoding = defaultCharset.name();
     setPythonIOEncoding(setPythonUnbuffered(envs), encoding);
   }
@@ -288,688 +179,27 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
    * @param commandLine command line
    * @param project     current project
    */
-  public static void setCorrectStdOutEncoding(@NotNull GeneralCommandLine commandLine, @NotNull final Project project) {
-    final Charset defaultCharset = getProjectDefaultCharset(project);
+  static void setCorrectStdOutEncoding(@NotNull GeneralCommandLine commandLine, @NotNull Project project) {
+    final Charset defaultCharset = EncodingProjectManager.getInstance(project).getDefaultCharset();
     commandLine.setCharset(defaultCharset);
     setPythonIOEncoding(commandLine.getEnvironment(), defaultCharset.name());
   }
 
-  @NotNull
-  private static Charset getProjectDefaultCharset(@NotNull Project project) {
-    return EncodingProjectManager.getInstance(project).getDefaultCharset();
-  }
-
-  @Override
-  protected List<AnAction> fillToolBarActions(final DefaultActionGroup toolbarActions,
-                                              final Executor defaultExecutor,
-                                              final RunContentDescriptor contentDescriptor) {
-    AnAction backspaceHandlingAction = createBackspaceHandlingAction();
-    //toolbarActions.add(backspaceHandlingAction);
-    AnAction interruptAction = createInterruptAction();
-
-    AnAction rerunAction = createRerunAction();
-    toolbarActions.add(rerunAction);
-
-    List<AnAction> actions = super.fillToolBarActions(toolbarActions, defaultExecutor, contentDescriptor);
-
-    actions.add(0, rerunAction);
-
-    actions.add(backspaceHandlingAction);
-    actions.add(interruptAction);
-    actions.add(createTabCompletionAction());
-
-    actions.add(createSplitLineAction());
-
-    AnAction showVarsAction = new ShowVarsAction();
-    toolbarActions.add(showVarsAction);
-    toolbarActions.add(ConsoleHistoryController.getController(getConsoleView()).getBrowseHistory());
-
-    toolbarActions.add(new ConnectDebuggerAction());
-
-    toolbarActions.add(new NewConsoleAction());
-
-    return actions;
-  }
-
-  public void runSync() {
-    myPorts = findAvailablePorts(getProject(), myConsoleType);
-
-    assert myPorts != null;
-
-    myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, getWorkingDir(), myPorts);
-    myCommandLine = myGeneralCommandLine.getCommandLineString();
-
-    try {
-      super.initAndRun();
-    }
-    catch (ExecutionException e) {
-      LOG.warn("Error running console", e);
-      ExecutionHelper.showErrors(getProject(), Arrays.<Exception>asList(e), "Python Console", null);
-    }
-
-    ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to console", false) {
-      @Override
-      public void run(@NotNull final ProgressIndicator indicator) {
-        indicator.setText("Connecting to console...");
-        connect(myStatementsToExecute);
-      }
-    });
-  }
-
-  /**
-   * Opens console
-   */
-  public void open() {
-    run();
-  }
-
-
-  /**
-   * Creates new console tab
-   */
-  public void createNewConsole() {
-    run();
-  }
-
-  public void run() {
-    ApplicationManager.getApplication().invokeAndWait(() -> FileDocumentManager.getInstance().saveAllDocuments());
-
-    myPorts = findAvailablePorts(getProject(), myConsoleType);
-
-    assert myPorts != null;
-
-    myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, getWorkingDir(), myPorts);
-    myCommandLine = myGeneralCommandLine.getCommandLineString();
-
-    UIUtil
-      .invokeLaterIfNeeded(() -> ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to Console", false) {
-        @Override
-        public void run(@NotNull final ProgressIndicator indicator) {
-          indicator.setText("Connecting to console...");
-          try {
-            initAndRun(myStatementsToExecute);
-          }
-          catch (final Exception e) {
-            LOG.warn("Error running console", e);
-            UIUtil.invokeAndWaitIfNeeded(new Runnable() {
-              @Override
-              public void run() {
-                showErrorsInConsole(e);
-              }
-            });
-          }
-        }
-      }));
-  }
-
-  private void showErrorsInConsole(Exception e) {
-    final Executor defaultExecutor = DefaultRunExecutor.getRunExecutorInstance();
-
-    DefaultActionGroup actionGroup = new DefaultActionGroup(createRerunAction());
-
-    final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN,
-                                                                                        actionGroup, false);
-
-    // Runner creating
-    final JPanel panel = new JPanel(new BorderLayout());
-    panel.add(actionToolbar.getComponent(), BorderLayout.WEST);
-
-    NewErrorTreeViewPanel errorViewPanel = new NewErrorTreeViewPanel(getProject(), null, false, false, null);
-
-    String[] messages = StringUtil.isNotEmpty(e.getMessage()) ? StringUtil.splitByLines(e.getMessage()) : ArrayUtil.EMPTY_STRING_ARRAY;
-    if (messages.length == 0) {
-      messages = new String[]{"Unknown error"};
-    }
-
-    errorViewPanel.addMessage(MessageCategory.ERROR, messages, null, -1, -1, null);
-    panel.add(errorViewPanel, BorderLayout.CENTER);
-
-
-    final RunContentDescriptor contentDescriptor =
-      new RunContentDescriptor(null, myProcessHandler, panel, "Error running console");
-
-    actionGroup.add(createCloseAction(defaultExecutor, contentDescriptor));
-
-    showConsole(defaultExecutor, contentDescriptor);
-  }
-
-  private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) {
-    final int[] ports;
-    try {
-      // File "pydev/console/pydevconsole.py", line 223, in <module>
-      // port, client_port = sys.argv[1:3]
-      ports = NetUtils.findAvailableSocketPorts(2);
-    }
-    catch (IOException e) {
-      ExecutionHelper.showErrors(project, Arrays.<Exception>asList(e), consoleType.getTitle(), null);
-      return null;
-    }
-    return ports;
-  }
-
-  protected GeneralCommandLine createCommandLine(@NotNull final Sdk sdk,
-                                                 @NotNull final Map<String, String> environmentVariables,
-                                                 String workingDir, int[] ports) {
-    return doCreateConsoleCmdLine(sdk, environmentVariables, workingDir, ports, PythonHelper.CONSOLE);
-  }
-
-  @NotNull
-  protected GeneralCommandLine doCreateConsoleCmdLine(Sdk sdk,
-                                                      Map<String, String> environmentVariables,
-                                                      String workingDir,
-                                                      int[] ports,
-                                                      PythonHelper helper) {
-    GeneralCommandLine cmd =
-      PythonCommandLineState.createPythonCommandLine(getProject(), new PythonConsoleRunParams(myConsoleSettings, workingDir, sdk,
-                                                                                              environmentVariables), false,
-                                                     PtyCommandLine.isEnabled() && !SystemInfo.isWindows);
-    cmd.withWorkDirectory(getWorkingDir());
-
-    ParamsGroup group = cmd.getParametersList().getParamsGroup(PythonCommandLineState.GROUP_SCRIPT);
-    helper.addToGroup(group, cmd);
-
-    for (int port : ports) {
-      group.addParameter(String.valueOf(port));
-    }
-
-    return cmd;
-  }
-
-  @Override
-  protected PythonConsoleView createConsoleView() {
-    PythonConsoleView consoleView = new PythonConsoleView(getProject(), getConsoleTitle(), mySdk);
-    myPydevConsoleCommunication.setConsoleFile(consoleView.getVirtualFile());
-    consoleView.addMessageFilter(new PythonTracebackFilter(getProject()));
-    return consoleView;
-  }
-
-  @Override
-  protected Process createProcess() throws ExecutionException {
-    if (PySdkUtil.isRemote(mySdk)) {
-      PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
-      if (manager != null) {
-        UsageTrigger.trigger(CONSOLE_FEATURE + ".remote");
-        return createRemoteConsoleProcess(manager, myGeneralCommandLine.getParametersList().getArray(),
-                                          myGeneralCommandLine.getEnvironment(), myGeneralCommandLine.getWorkDirectory());
-      }
-      throw new PythonRemoteInterpreterManager.PyRemoteInterpreterExecutionException();
-    }
-    else {
-      myCommandLine = myGeneralCommandLine.getCommandLineString();
-      Map<String, String> envs = myGeneralCommandLine.getEnvironment();
-      EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myGeneralCommandLine.getCharset());
-
-      UsageTrigger.trigger(CONSOLE_FEATURE + ".local");
-      final Process server = myGeneralCommandLine.createProcess();
-
-      try {
-        myPydevConsoleCommunication = new PydevConsoleCommunication(getProject(), myPorts[0], server, myPorts[1]);
-      }
-      catch (Exception e) {
-        throw new ExecutionException(e.getMessage());
-      }
-      return server;
-    }
-  }
-
-  private RemoteProcess createRemoteConsoleProcess(PythonRemoteInterpreterManager manager,
-                                                   String[] command,
-                                                   Map<String, String> env,
-                                                   File workDirectory)
-    throws ExecutionException {
-    PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
-    assert data != null;
-
-    GeneralCommandLine commandLine = new GeneralCommandLine();
-
-    commandLine.setWorkDirectory(workDirectory);
-
-    commandLine.withParameters(command);
-
-    commandLine.getEnvironment().putAll(env);
-
-    commandLine.getParametersList().set(0, PythonRemoteInterpreterManager.toSystemDependent(new File(data.getHelpersPath(),
-                                                                                                     PYDEV_PYDEVCONSOLE_PY)
-                                                                                              .getPath(),
-                                                                                            PySourcePosition.isWindowsPath(
-                                                                                              data.getInterpreterPath())
-    ));
-    commandLine.getParametersList().set(1, "0");
-    commandLine.getParametersList().set(2, "0");
-
-    try {
-      PyRemotePathMapper pathMapper = getPathMapper(getProject(), mySdk, myConsoleSettings);
-
-      assert pathMapper != null;
-
-      commandLine.putUserData(PyRemoteProcessStarter.OPEN_FOR_INCOMING_CONNECTION, true);
-
-      myRemoteProcessHandlerBase = PyRemoteProcessStarterManagerUtil
-        .getManager(data).startRemoteProcess(getProject(), commandLine, manager, data,
-                                             pathMapper);
-
-      myCommandLine = myRemoteProcessHandlerBase.getCommandLine();
-
-      RemoteProcess remoteProcess = myRemoteProcessHandlerBase.getProcess();
-
-      Couple<Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
-
-      if (remoteProcess instanceof Tunnelable) {
-        Tunnelable tunnelableProcess = (Tunnelable)remoteProcess;
-        tunnelableProcess.addLocalTunnel(myPorts[0], remotePorts.first);
-        tunnelableProcess.addRemoteTunnel(remotePorts.second, "localhost", myPorts[1]);
-
-        if (LOG.isDebugEnabled()) {
-          LOG.debug(String.format("Using tunneled communication for Python console: port %d (=> %d) on IDE side, " +
-                                  "port %d (=> %d) on pydevconsole.py side", myPorts[1], remotePorts.second, myPorts[0],
-                                  remotePorts.first));
-        }
-
-        myPydevConsoleCommunication = new PydevRemoteConsoleCommunication(getProject(), myPorts[0], remoteProcess, myPorts[1]);
-      }
-      else {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug(String.format("Using direct communication for Python console: port %d on IDE side, port %d on pydevconsole.py side",
-                                  remotePorts.second, remotePorts.first));
-        }
-
-        myPydevConsoleCommunication =
-          new PydevRemoteConsoleCommunication(getProject(), remotePorts.first, remoteProcess, remotePorts.second);
-      }
-
-      return remoteProcess;
-    }
-    catch (Exception e) {
-      throw new ExecutionException(e.getMessage(), e);
-    }
-  }
-
-  private static Couple<Integer> getRemotePortsFromProcess(RemoteProcess process) throws ExecutionException {
-    Scanner s = new Scanner(process.getInputStream());
-
-    return Couple.of(readInt(s, process), readInt(s, process));
-  }
-
-  private static int readInt(Scanner s, Process process) throws ExecutionException {
-    long started = System.currentTimeMillis();
-
-    StringBuilder sb = new StringBuilder();
-    boolean flag = false;
-
-    while (System.currentTimeMillis() - started < PORTS_WAITING_TIMEOUT) {
-      if (s.hasNextLine()) {
-        String line = s.nextLine();
-        sb.append(line).append("\n");
-        try {
-          int i = Integer.parseInt(line);
-          if (flag) {
-            LOG.warn("Unexpected strings in output:\n" + sb.toString());
-          }
-          return i;
-        }
-        catch (NumberFormatException ignored) {
-          flag = true;
-          continue;
-        }
-      }
-
-      TimeoutUtil.sleep(200);
-
-      if (process.exitValue() != 0) {
-        String error;
-        try {
-          error = "Console process terminated with error:\n" + StreamUtil.readText(process.getErrorStream()) + sb.toString();
-        }
-        catch (Exception ignored) {
-          error = "Console process terminated with exit code " + process.exitValue() + ", output:" + sb.toString();
-        }
-        throw new ExecutionException(error);
-      }
-      else {
-        break;
-      }
-    }
-
-    throw new ExecutionException("Couldn't read integer value from stream");
-  }
-
-  @Override
-  protected PyConsoleProcessHandler createProcessHandler(final Process process) {
-    if (PySdkUtil.isRemote(mySdk)) {
-      PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
-      if (manager != null) {
-        PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
-        assert data != null;
-        myProcessHandler =
-          manager.createConsoleProcessHandler((RemoteProcess)process, getConsoleView(), myPydevConsoleCommunication,
-                                              myCommandLine, CharsetToolkit.UTF8_CHARSET,
-                                              manager.setupMappings(getProject(), data, null),
-                                              myRemoteProcessHandlerBase.getRemoteSocketToLocalHostProvider());
-      }
-      else {
-        LOG.error("Can't create remote console process handler");
-      }
-    }
-    else {
-      myProcessHandler = new PyConsoleProcessHandler(process, getConsoleView(), myPydevConsoleCommunication, myCommandLine,
-                                                     CharsetToolkit.UTF8_CHARSET);
-    }
-    return myProcessHandler;
-  }
-
-  public void initAndRun(final String... statements2execute) throws ExecutionException {
-    super.initAndRun();
-
-    connect(statements2execute);
-  }
-
-  public void connect(final String[] statements2execute) {
-    if (handshake()) {
-      ApplicationManager.getApplication().invokeLater(() -> {
-        // Propagate console communication to language console
-        final PythonConsoleView consoleView = getConsoleView();
-
-        consoleView.setConsoleCommunication(myPydevConsoleCommunication);
-        consoleView.setSdk(mySdk);
-        consoleView.setExecutionHandler(myConsoleExecuteActionHandler);
-        myProcessHandler.addProcessListener(new ProcessAdapter() {
-          @Override
-          public void onTextAvailable(ProcessEvent event, Key outputType) {
-            consoleView.print(event.getText(), outputType);
-          }
-        });
-
-        enableConsoleExecuteAction();
-
-        for (String statement : statements2execute) {
-          consoleView.executeStatement(statement + "\n", ProcessOutputTypes.SYSTEM);
-        }
-
-        fireConsoleInitializedEvent(consoleView);
-      });
-    }
-    else {
-      getConsoleView().print("Couldn't connect to console process.", ProcessOutputTypes.STDERR);
-      myProcessHandler.destroyProcess();
-      finishConsole();
-    }
-  }
-
-  @Override
-  protected String constructConsoleTitle(@NotNull String consoleTitle) {
-    if (myConsoleTitle == null) {
-      myConsoleTitle = super.constructConsoleTitle(consoleTitle);
-    }
-    return myConsoleTitle;
-  }
-
-  protected AnAction createRerunAction() {
-    return new RestartAction(this);
-  }
-
-  private AnAction createInterruptAction() {
-    AnAction anAction = new AnAction() {
-      @Override
-      public void actionPerformed(final AnActionEvent e) {
-        if (myPydevConsoleCommunication.isExecuting()) {
-          getConsoleView().print("^C", ProcessOutputTypes.SYSTEM);
-        }
-        myPydevConsoleCommunication.interrupt();
-      }
-
-      @Override
-      public void update(final AnActionEvent e) {
-        EditorEx consoleEditor = getConsoleView().getConsoleEditor();
-        boolean enabled = IJSwingUtilities.hasFocus(consoleEditor.getComponent()) && !consoleEditor.getSelectionModel().hasSelection();
-        e.getPresentation().setEnabled(enabled);
-      }
-    };
-    anAction
-      .registerCustomShortcutSet(KeyEvent.VK_C, InputEvent.CTRL_MASK, getConsoleView().getConsoleEditor().getComponent());
-    anAction.getTemplatePresentation().setVisible(false);
-    return anAction;
-  }
-
-  private AnAction createTabCompletionAction() {
-    final AnAction runCompletions = new AnAction() {
-      @Override
-      public void actionPerformed(AnActionEvent e) {
-
-        Editor editor = getConsoleView().getConsoleEditor();
-        if (LookupManager.getActiveLookup(editor) != null) {
-          AnAction replace = ActionManager.getInstance().getAction("EditorChooseLookupItemReplace");
-          ActionUtil.performActionDumbAware(replace, e);
-          return;
-        }
-        AnAction completionAction = ActionManager.getInstance().getAction("CodeCompletion");
-        if (completionAction == null) {
-          return;
-        }
-        ActionUtil.performActionDumbAware(completionAction, e);
-      }
-
-      @Override
-      public void update(AnActionEvent e) {
-        Editor editor = getConsoleView().getConsoleEditor();
-        if (LookupManager.getActiveLookup(editor) != null) {
-          e.getPresentation().setEnabled(false);
-        }
-        int offset = editor.getCaretModel().getOffset();
-        Document document = editor.getDocument();
-        int lineStart = document.getLineStartOffset(document.getLineNumber(offset));
-        String textToCursor = document.getText(new TextRange(lineStart, offset));
-        e.getPresentation().setEnabled(!CharMatcher.WHITESPACE.matchesAllOf(textToCursor));
-      }
-    };
-
-    runCompletions
-      .registerCustomShortcutSet(KeyEvent.VK_TAB, 0, getConsoleView().getConsoleEditor().getComponent());
-    runCompletions.getTemplatePresentation().setVisible(false);
-    return runCompletions;
-  }
-
-
-
-
-
-  private AnAction createBackspaceHandlingAction() {
-    final AnAction upAction = new AnAction() {
-      @Override
-      public void actionPerformed(final AnActionEvent e) {
-        new WriteCommandAction(getConsoleView().getProject(), getConsoleView().getFile()) {
-          @Override
-          protected void run(@NotNull final Result result) throws Throwable {
-            String text = getConsoleView().getEditorDocument().getText();
-            String newText = text.substring(0, text.length() - myConsoleExecuteActionHandler.getPythonIndent());
-            getConsoleView().getEditorDocument().setText(newText);
-            getConsoleView().getConsoleEditor().getCaretModel().moveToOffset(newText.length());
-          }
-        }.execute();
-      }
-
-      @Override
-      public void update(final AnActionEvent e) {
-        e.getPresentation()
-          .setEnabled(myConsoleExecuteActionHandler.getCurrentIndentSize() >= myConsoleExecuteActionHandler.getPythonIndent() &&
-                      isIndentSubstring(getConsoleView().getEditorDocument().getText()));
-      }
-    };
-    upAction.registerCustomShortcutSet(KeyEvent.VK_BACK_SPACE, 0, null);
-    upAction.getTemplatePresentation().setVisible(false);
-    return upAction;
-  }
-
-  private boolean isIndentSubstring(String text) {
-    int indentSize = myConsoleExecuteActionHandler.getPythonIndent();
-    return text.length() >= indentSize && CharMatcher.WHITESPACE.matchesAllOf(text.substring(text.length() - indentSize));
-  }
-
-  private void enableConsoleExecuteAction() {
-    myConsoleExecuteActionHandler.setEnabled(true);
-  }
-
-  private boolean handshake() {
-    boolean res;
-    long started = System.currentTimeMillis();
-    do {
-      try {
-        res = myPydevConsoleCommunication.handshake();
-      }
-      catch (XmlRpcException ignored) {
-        res = false;
-      }
-      if (res) {
-        break;
-      }
-      else {
-        long now = System.currentTimeMillis();
-        if (now - started > APPROPRIATE_TO_WAIT) {
-          break;
-        }
-        else {
-          TimeoutUtil.sleep(100);
-        }
-      }
-    }
-    while (true);
-    return res;
-  }
-
-  @Override
-  protected AnAction createStopAction() {
-    final AnAction generalStopAction = super.createStopAction();
-    return createConsoleStoppingAction(generalStopAction);
-  }
-
-  @Override
-  protected AnAction createCloseAction(Executor defaultExecutor, final RunContentDescriptor descriptor) {
-    final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, descriptor);
-
-    final AnAction stopAction = new DumbAwareAction() {
-      @Override
-      public void update(AnActionEvent e) {
-        generalCloseAction.update(e);
-      }
-
-      @Override
-      public void actionPerformed(AnActionEvent e) {
-        e = stopConsole(e);
-
-        clearContent(descriptor);
-
-        generalCloseAction.actionPerformed(e);
-      }
-    };
-    stopAction.copyFrom(generalCloseAction);
-    return stopAction;
-  }
-
-  protected void clearContent(RunContentDescriptor descriptor) {
-  }
-
-  private AnAction createConsoleStoppingAction(final AnAction generalStopAction) {
-    final AnAction stopAction = new DumbAwareAction() {
-      @Override
-      public void update(AnActionEvent e) {
-        generalStopAction.update(e);
-      }
-
-      @Override
-      public void actionPerformed(AnActionEvent e) {
-        e = stopConsole(e);
-
-        generalStopAction.actionPerformed(e);
-      }
-    };
-    stopAction.copyFrom(generalStopAction);
-    return stopAction;
-  }
-
-  private AnActionEvent stopConsole(AnActionEvent e) {
-    if (myPydevConsoleCommunication != null) {
-      e = new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(),
-                            e.getPresentation(), e.getActionManager(), e.getModifiers());
-      try {
-        closeCommunication();
-        // waiting for REPL communication before destroying process handler
-        Thread.sleep(300);
-      }
-      catch (Exception ignored) {
-        // Ignore
-      }
-    }
-    return e;
-  }
-
-  protected AnAction createSplitLineAction() {
-
-    class ConsoleSplitLineAction extends EditorAction {
-
-      private static final String CONSOLE_SPLIT_LINE_ACTION_ID = "Console.SplitLine";
-
-      public ConsoleSplitLineAction() {
-        super(new EditorWriteActionHandler() {
-
-          private final SplitLineAction mySplitLineAction = new SplitLineAction();
-
-          @Override
-          public boolean isEnabled(Editor editor, DataContext dataContext) {
-            return mySplitLineAction.getHandler().isEnabled(editor, dataContext);
-          }
-
-          @Override
-          public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
-            ((EditorWriteActionHandler)mySplitLineAction.getHandler()).executeWriteAction(editor, caret, dataContext);
-            editor.getCaretModel().getCurrentCaret().moveCaretRelatively(0, 1, false, true);
-          }
-        });
-      }
-
-      public void setup() {
-        EmptyAction.setupAction(this, CONSOLE_SPLIT_LINE_ACTION_ID, null);
-      }
-    }
-
-    ConsoleSplitLineAction action = new ConsoleSplitLineAction();
-    action.setup();
-    return action;
-  }
-
-  private void closeCommunication() {
-    if (!myProcessHandler.isProcessTerminated()) {
-      myPydevConsoleCommunication.close();
-    }
-  }
-
-  @NotNull
-  @Override
-  protected ProcessBackedConsoleExecuteActionHandler createExecuteActionHandler() {
-    myConsoleExecuteActionHandler =
-      new PydevConsoleExecuteActionHandler(getConsoleView(), getProcessHandler(), myPydevConsoleCommunication);
-    myConsoleExecuteActionHandler.setEnabled(false);
-    new ConsoleHistoryController(myConsoleType.getTypeId(), "", getConsoleView()).install();
-    return myConsoleExecuteActionHandler;
-  }
-
-  public PydevConsoleCommunication getPydevConsoleCommunication() {
-    return myPydevConsoleCommunication;
-  }
-
-  public static boolean isInPydevConsole(final PsiElement element) {
+  static boolean isInPydevConsole(PsiElement element) {
     return element instanceof PydevConsoleElement || getConsoleCommunication(element) != null;
   }
 
-  public static boolean isPythonConsole(@Nullable ASTNode element) {
+  static boolean isPythonConsole(@Nullable ASTNode element) {
     return getPythonConsoleData(element) != null;
   }
 
   @Nullable
-  public static PythonConsoleData getPythonConsoleData(@Nullable ASTNode element) {
+  static PythonConsoleData getPythonConsoleData(@Nullable ASTNode element) {
     if (element == null || element.getPsi() == null || element.getPsi().getContainingFile() == null) {
       return null;
     }
 
-    VirtualFile file = getConsoleFile(element.getPsi().getContainingFile());
+    VirtualFile file = PydevConsoleRunnerImpl.getConsoleFile(element.getPsi().getContainingFile());
 
     if (file == null) {
       return null;
@@ -977,341 +207,29 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
     return file.getUserData(PyConsoleUtil.PYTHON_CONSOLE_DATA);
   }
 
-  private static VirtualFile getConsoleFile(PsiFile psiFile) {
-    VirtualFile file = psiFile.getViewProvider().getVirtualFile();
-    if (file instanceof LightVirtualFile) {
-      file = ((LightVirtualFile)file).getOriginalFile();
-    }
-    return file;
-  }
-
   @Nullable
-  public static ConsoleCommunication getConsoleCommunication(final PsiElement element) {
+  static ConsoleCommunication getConsoleCommunication(PsiElement element) {
     final PsiFile containingFile = element.getContainingFile();
     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_KEY) : null;
   }
 
   @Nullable
-  public static Sdk getConsoleSdk(final PsiElement element) {
+  static Sdk getConsoleSdk(PsiElement element) {
     final PsiFile containingFile = element.getContainingFile();
     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_SDK) : null;
   }
 
-  @Override
-  protected boolean shouldAddNumberToTitle() {
-    return true;
-  }
-
-  public void addConsoleListener(ConsoleListener consoleListener) {
-    myConsoleListeners.add(consoleListener);
-  }
-
-  public void removeConsoleListener(ConsoleListener consoleListener) {
-    myConsoleListeners.remove(consoleListener);
-  }
-
-  private void fireConsoleInitializedEvent(LanguageConsoleView consoleView) {
-    for (ConsoleListener listener : myConsoleListeners) {
-      listener.handleConsoleInitialized(consoleView);
-    }
-  }
-
-
-  public interface ConsoleListener {
-    void handleConsoleInitialized(LanguageConsoleView consoleView);
-  }
-
-
-  private static class RestartAction extends AnAction {
-    private PydevConsoleRunner myConsoleRunner;
-
-
-    private RestartAction(PydevConsoleRunner runner) {
-      copyFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN));
-      getTemplatePresentation().setIcon(AllIcons.Actions.Restart);
-      myConsoleRunner = runner;
-    }
-
-    @Override
-    public void actionPerformed(AnActionEvent e) {
-      myConsoleRunner.rerun();
-    }
-  }
-
-  private void rerun() {
-    new Task.Backgroundable(getProject(), "Restarting Console", true) {
-      @Override
-      public void run(@NotNull ProgressIndicator indicator) {
-        if (myProcessHandler != null) {
-          UIUtil.invokeLaterIfNeeded(() -> closeCommunication());
-
-          myProcessHandler.waitFor();
-        }
-
-        UIUtil.invokeLaterIfNeeded(() -> PydevConsoleRunner.this.run());
-      }
-    }.queue();
-  }
-
-  private class ShowVarsAction extends ToggleAction implements DumbAware {
-    private boolean mySelected = false;
+  void runSync();
 
-    public ShowVarsAction() {
-      super("Show Variables", "Shows active console variables", AllIcons.Debugger.Watches);
-    }
-
-    @Override
-    public boolean isSelected(AnActionEvent e) {
-      return mySelected;
-    }
-
-    @Override
-    public void setSelected(AnActionEvent e, boolean state) {
-      mySelected = state;
-
-      if (mySelected) {
-        getConsoleView().showVariables(myPydevConsoleCommunication);
-      }
-      else {
-        getConsoleView().restoreWindow();
-      }
-    }
-  }
-
-
-  private class ConnectDebuggerAction extends ToggleAction implements DumbAware {
-    private boolean mySelected = false;
-    private XDebugSession mySession = null;
-
-    public ConnectDebuggerAction() {
-      super("Attach Debugger", "Enables tracing of code executed in console", AllIcons.Actions.StartDebugger);
-    }
-
-    @Override
-    public boolean isSelected(AnActionEvent e) {
-      return mySelected;
-    }
-
-    @Override
-    public void update(AnActionEvent e) {
-      if (mySession != null) {
-        e.getPresentation().setEnabled(false);
-      }
-      else {
-        e.getPresentation().setEnabled(true);
-      }
-    }
-
-    @Override
-    public void setSelected(AnActionEvent e, boolean state) {
-      mySelected = state;
-
-      if (mySelected) {
-        try {
-          mySession = connectToDebugger();
-        }
-        catch (Exception e1) {
-          LOG.error(e1);
-          Messages.showErrorDialog("Can't connect to debugger", "Error Connecting Debugger");
-        }
-      }
-      else {
-        //TODO: disable debugging
-      }
-    }
-  }
-
-
-  private static class NewConsoleAction extends AnAction implements DumbAware {
-    public NewConsoleAction() {
-      super("New Console", "Creates new python console", AllIcons.General.Add);
-    }
-
-    @Override
-    public void update(AnActionEvent e) {
-      e.getPresentation().setEnabled(true);
-    }
-
-    @Override
-    public void actionPerformed(AnActionEvent e) {
-      PydevConsoleRunner runner =
-        PythonConsoleRunnerFactory.getInstance().createConsoleRunner(e.getData(CommonDataKeys.PROJECT), e.getData(LangDataKeys.MODULE));
-      runner.createNewConsole();
-    }
-  }
-
-  private XDebugSession connectToDebugger() throws ExecutionException {
-    final ServerSocket serverSocket = PythonCommandLineState.createServerSocket();
-
-    final XDebugSession session = XDebuggerManager.getInstance(getProject()).
-      startSessionAndShowTab("Python Console Debugger", PythonIcons.Python.Python, null, true, new XDebugProcessStarter() {
-        @NotNull
-        public XDebugProcess start(@NotNull final XDebugSession session) {
-          PythonDebugLanguageConsoleView debugConsoleView = new PythonDebugLanguageConsoleView(getProject(), mySdk);
-
-          PyConsoleDebugProcessHandler consoleDebugProcessHandler =
-            new PyConsoleDebugProcessHandler(myProcessHandler);
-
-          PyConsoleDebugProcess consoleDebugProcess =
-            new PyConsoleDebugProcess(session, serverSocket, debugConsoleView,
-                                      consoleDebugProcessHandler);
-
-          PythonDebugConsoleCommunication communication =
-            PyDebugRunner.initDebugConsoleView(getProject(), consoleDebugProcess, debugConsoleView, consoleDebugProcessHandler, session);
-
-          communication.addCommunicationListener(new ConsoleCommunicationListener() {
-            @Override
-            public void commandExecuted(boolean more) {
-              session.rebuildViews();
-            }
-
-            @Override
-            public void inputRequested() {
-            }
-          });
-
-          myPydevConsoleCommunication.setDebugCommunication(communication);
-          debugConsoleView.attachToProcess(consoleDebugProcessHandler);
-
-          consoleDebugProcess.waitForNextConnection();
+  void run();
 
-          try {
-            consoleDebugProcess.connect(myPydevConsoleCommunication);
-          }
-          catch (Exception e) {
-            LOG.error(e); //TODO
-          }
+  PydevConsoleCommunication getPydevConsoleCommunication();
 
-          myProcessHandler.notifyTextAvailable("\nDebugger connected.\n", ProcessOutputTypes.STDERR);
+  void addConsoleListener(PydevConsoleRunnerImpl.ConsoleListener consoleListener);
 
-          return consoleDebugProcess;
-        }
-      });
-
-    return session;
-  }
-
-  public static PythonConsoleRunnerFactory factory() {
-    return new PydevConsoleRunnerFactory();
-  }
-
-  private static class PythonConsoleRunParams implements PythonRunParams {
-    private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
-    private String myWorkingDir;
-    private Sdk mySdk;
-    private Map<String, String> myEnvironmentVariables;
-
-    public PythonConsoleRunParams(@NotNull PyConsoleOptions.PyConsoleSettings consoleSettings,
-                                  @NotNull String workingDir,
-                                  @NotNull Sdk sdk,
-                                  @NotNull Map<String, String> envs) {
-      myConsoleSettings = consoleSettings;
-      myWorkingDir = workingDir;
-      mySdk = sdk;
-      myEnvironmentVariables = envs;
-      myEnvironmentVariables.putAll(consoleSettings.getEnvs());
-    }
-
-    @Override
-    public String getInterpreterOptions() {
-      return myConsoleSettings.getInterpreterOptions();
-    }
-
-    @Override
-    public void setInterpreterOptions(String interpreterOptions) {
-      throw new UnsupportedOperationException();
-    }
+  PydevConsoleExecuteActionHandler getConsoleExecuteActionHandler();
 
-    @Override
-    public String getWorkingDirectory() {
-      return myWorkingDir;
-    }
-
-    @Override
-    public void setWorkingDirectory(String workingDirectory) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Nullable
-    @Override
-    public String getSdkHome() {
-      return mySdk.getHomePath();
-    }
-
-    @Override
-    public void setSdkHome(String sdkHome) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setModule(Module module) {
-      throw new UnsupportedOperationException();
-    }
+  PyConsoleProcessHandler getProcessHandler();
 
-    @Override
-    public String getModuleName() {
-      return myConsoleSettings.getModuleName();
-    }
-
-    @Override
-    public boolean isUseModuleSdk() {
-      return myConsoleSettings.isUseModuleSdk();
-    }
-
-    @Override
-    public void setUseModuleSdk(boolean useModuleSdk) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isPassParentEnvs() {
-      return myConsoleSettings.isPassParentEnvs();
-    }
-
-    @Override
-    public void setPassParentEnvs(boolean passParentEnvs) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Map<String, String> getEnvs() {
-      return myEnvironmentVariables;
-    }
-
-    @Override
-    public void setEnvs(Map<String, String> envs) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Nullable
-    @Override
-    public PathMappingSettings getMappingSettings() {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setMappingSettings(@Nullable PathMappingSettings mappingSettings) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean shouldAddContentRoots() {
-      return myConsoleSettings.shouldAddContentRoots();
-    }
-
-    @Override
-    public boolean shouldAddSourceRoots() {
-      return myConsoleSettings.shouldAddSourceRoots();
-    }
-
-    @Override
-    public void setAddContentRoots(boolean flag) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setAddSourceRoots(boolean flag) {
-      throw new UnsupportedOperationException();
-    }
-  }
+  PythonConsoleView getConsoleView();
 }
index df713a8364ecb57a70e9134d01003d46e6056479..ffecb6ddb39c434ba6630222429710b8711eae5b 100644 (file)
@@ -16,6 +16,8 @@
 package com.jetbrains.python.console;
 
 import com.google.common.collect.Maps;
+import com.intellij.openapi.application.TransactionGuard;
+import com.intellij.openapi.application.TransactionId;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.project.Project;
@@ -40,8 +42,8 @@ import java.util.Map;
  */
 public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
   @Override
-  public PydevConsoleRunner createConsoleRunner(@NotNull Project project,
-                                                @Nullable Module contextModule) {
+  public PydevConsoleRunnerImpl createConsoleRunner(@NotNull Project project,
+                                                    @Nullable Module contextModule) {
     Pair<Sdk, Module> sdkAndModule = PydevConsoleRunner.findPythonSdkAndModule(project, contextModule);
 
     Module module = sdkAndModule.second;
@@ -108,8 +110,14 @@ public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
 
     Map<String, String> envs = Maps.newHashMap(settingsProvider.getEnvs());
     putIPythonEnvFlag(project, envs);
-    
-    return createConsoleRunner(project, sdk, workingDir, envs, PyConsoleType.PYTHON, settingsProvider, setupFragment);
+
+    //The transaction is needed due the the FileDocumentManager.getInstance().saveAllDocuments() call in PydevConsoleRunnerImpl
+    Runnable rerunAction = () -> TransactionGuard.submitTransaction(project, () -> {
+      PydevConsoleRunnerImpl runner = createConsoleRunner(project, module);
+      runner.run();
+    });
+
+    return createConsoleRunner(project, sdk, workingDir, envs, PyConsoleType.PYTHON, settingsProvider, rerunAction, setupFragment);
   }
 
   public static void putIPythonEnvFlag(@NotNull Project project, Map<String, String> envs) {
@@ -117,13 +125,13 @@ public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
     envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled);
   }
 
-  protected PydevConsoleRunner createConsoleRunner(Project project,
-                                                   Sdk sdk,
-                                                   String workingDir,
-                                                   Map<String, String> envs,
-                                                   PyConsoleType consoleType,
-                                                   PyConsoleOptions.PyConsoleSettings settingsProvider,
-                                                   String... setupFragment) {
-    return new PydevConsoleRunner(project, sdk, consoleType, workingDir, envs, settingsProvider, setupFragment);
+  protected PydevConsoleRunnerImpl createConsoleRunner(Project project,
+                                                       Sdk sdk,
+                                                       String workingDir,
+                                                       Map<String, String> envs,
+                                                       PyConsoleType consoleType,
+                                                       PyConsoleOptions.PyConsoleSettings settingsProvider,
+                                                       Runnable rerunAction, String... setupFragment) {
+    return new PydevConsoleRunnerImpl(project, sdk, consoleType, workingDir, envs, settingsProvider, rerunAction, setupFragment);
   }
 }
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java
new file mode 100644 (file)
index 0000000..971f2d1
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console;
+
+import com.google.common.base.CharMatcher;
+import com.intellij.codeInsight.lookup.LookupManager;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.ExecutionHelper;
+import com.intellij.execution.ExecutionManager;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.EncodingEnvironmentUtil;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.configurations.ParamsGroup;
+import com.intellij.execution.configurations.PtyCommandLine;
+import com.intellij.execution.console.ConsoleExecuteAction;
+import com.intellij.execution.console.ConsoleHistoryController;
+import com.intellij.execution.console.LanguageConsoleView;
+import com.intellij.execution.executors.DefaultRunExecutor;
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessOutputTypes;
+import com.intellij.execution.process.ProcessTerminatedListener;
+import com.intellij.execution.runners.ConsoleTitleGen;
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.execution.ui.actions.CloseAction;
+import com.intellij.icons.AllIcons;
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
+import com.intellij.internal.statistic.UsageTrigger;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.actionSystem.ex.ActionUtil;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.actionSystem.EditorAction;
+import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
+import com.intellij.openapi.editor.actions.SplitLineAction;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.*;
+import com.intellij.openapi.util.io.StreamUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.CharsetToolkit;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.intellij.remote.RemoteProcess;
+import com.intellij.remote.Tunnelable;
+import com.intellij.testFramework.LightVirtualFile;
+import com.intellij.ui.JBColor;
+import com.intellij.ui.SideBorder;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IJSwingUtilities;
+import com.intellij.util.PathMappingSettings;
+import com.intellij.util.TimeoutUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.net.NetUtils;
+import com.intellij.util.ui.MessageCategory;
+import com.intellij.util.ui.UIUtil;
+import com.intellij.xdebugger.XDebugProcess;
+import com.intellij.xdebugger.XDebugProcessStarter;
+import com.intellij.xdebugger.XDebugSession;
+import com.intellij.xdebugger.XDebuggerManager;
+import com.jetbrains.python.PythonHelper;
+import com.jetbrains.python.console.actions.ShowVarsAction;
+import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
+import com.jetbrains.python.debugger.PyDebugRunner;
+import com.jetbrains.python.debugger.PySourcePosition;
+import com.jetbrains.python.remote.PyRemotePathMapper;
+import com.jetbrains.python.remote.PyRemoteProcessHandlerBase;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import com.jetbrains.python.run.*;
+import com.jetbrains.python.sdk.PySdkUtil;
+import icons.PythonIcons;
+import org.apache.xmlrpc.XmlRpcException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.*;
+import java.util.List;
+
+import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.registerActionShortcuts;
+
+/**
+ * @author oleg
+ */
+public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
+  public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
+  public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" +
+                                                     "sys.path.extend([" + WORKING_DIR_ENV + "])\n";
+  private static final Logger LOG = Logger.getInstance(PydevConsoleRunnerImpl.class.getName());
+  @SuppressWarnings("SpellCheckingInspection")
+  public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py";
+  public static final int PORTS_WAITING_TIMEOUT = 20000;
+  private static final String CONSOLE_FEATURE = "python.console";
+  private final Project myProject;
+  private final String myTitle;
+  private final String myWorkingDir;
+  private final Executor myExecutor;
+  private final Runnable myRunRunAction;
+  @NotNull
+  private Sdk mySdk;
+  private GeneralCommandLine myGeneralCommandLine;
+  protected int[] myPorts;
+  private PydevConsoleCommunication myPydevConsoleCommunication;
+  private PyConsoleProcessHandler myProcessHandler;
+  protected PydevConsoleExecuteActionHandler myConsoleExecuteActionHandler;
+  private List<ConsoleListener> myConsoleListeners = ContainerUtil.createLockFreeCopyOnWriteList();
+  private final PyConsoleType myConsoleType;
+  private Map<String, String> myEnvironmentVariables;
+  private String myCommandLine;
+  @NotNull private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
+  private String[] myStatementsToExecute = ArrayUtil.EMPTY_STRING_ARRAY;
+
+
+  private static final long APPROPRIATE_TO_WAIT = 60000;
+
+  private PyRemoteProcessHandlerBase myRemoteProcessHandlerBase;
+
+  private String myConsoleTitle = null;
+  private PythonConsoleView myConsoleView;
+
+  public PydevConsoleRunnerImpl(@NotNull final Project project,
+                                @NotNull Sdk sdk,
+                                @NotNull final PyConsoleType consoleType,
+                                @Nullable final String workingDir,
+                                Map<String, String> environmentVariables,
+                                @NotNull PyConsoleOptions.PyConsoleSettings settingsProvider,
+                                @NotNull Runnable rerunAction, String... statementsToExecute) {
+    myProject = project;
+    mySdk = sdk;
+    myTitle = consoleType.getTitle();
+    myWorkingDir = workingDir;
+    myConsoleType = consoleType;
+    myEnvironmentVariables = environmentVariables;
+    myConsoleSettings = settingsProvider;
+    myStatementsToExecute = statementsToExecute;
+    myRunRunAction = rerunAction;
+    PyConsoleToolWindowExecutor toolWindowExecutor = PyConsoleToolWindowExecutor.findInstance();
+    myExecutor = toolWindowExecutor != null ? toolWindowExecutor : DefaultRunExecutor.getRunExecutorInstance();
+  }
+
+
+  private List<AnAction> fillToolBarActions(final DefaultActionGroup toolbarActions,
+                                            final RunContentDescriptor contentDescriptor) {
+    //toolbarActions.add(backspaceHandlingAction);
+
+    toolbarActions.add(createRerunAction());
+
+    List<AnAction> actions = ContainerUtil.newArrayList();
+
+    //stop
+    actions.add(createStopAction());
+
+    //close
+    actions.add(createCloseAction(contentDescriptor));
+
+    // run action
+    actions.add(
+      new ConsoleExecuteAction(myConsoleView, myConsoleExecuteActionHandler, myConsoleExecuteActionHandler.getEmptyExecuteAction(),
+                               myConsoleExecuteActionHandler));
+
+    // Help
+    actions.add(CommonActionsManager.getInstance().createHelpAction("interactive_console"));
+
+    toolbarActions.addAll(actions);
+
+
+    actions.add(0, createRerunAction());
+
+    actions.add(createInterruptAction());
+    actions.add(createTabCompletionAction());
+
+    actions.add(createSplitLineAction());
+
+    toolbarActions.add(new ShowVarsAction(myConsoleView, myPydevConsoleCommunication));
+    toolbarActions.add(ConsoleHistoryController.getController(myConsoleView).getBrowseHistory());
+
+    toolbarActions.add(new ConnectDebuggerAction());
+
+    toolbarActions.add(new NewConsoleAction());
+
+    return actions;
+  }
+
+  @Override
+  public void runSync() {
+    myPorts = findAvailablePorts(myProject, myConsoleType);
+
+    assert myPorts != null;
+
+    myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, myWorkingDir, myPorts);
+    myCommandLine = myGeneralCommandLine.getCommandLineString();
+
+    try {
+      initAndRun();
+    }
+    catch (ExecutionException e) {
+      LOG.warn("Error running console", e);
+      ExecutionHelper.showErrors(myProject, Collections.<Exception>singletonList(e), "Python Console", null);
+    }
+
+    ProgressManager.getInstance().run(new Task.Backgroundable(myProject, "Connecting to Console", false) {
+      @Override
+      public void run(@NotNull final ProgressIndicator indicator) {
+        indicator.setText("Connecting to console...");
+        connect(myStatementsToExecute);
+      }
+    });
+  }
+
+
+  @Override
+  public void run() {
+    ApplicationManager.getApplication().invokeAndWait(() -> FileDocumentManager.getInstance().saveAllDocuments());
+
+    myPorts = findAvailablePorts(myProject, myConsoleType);
+
+    assert myPorts != null;
+
+    myGeneralCommandLine = createCommandLine(mySdk, myEnvironmentVariables, myWorkingDir, myPorts);
+    myCommandLine = myGeneralCommandLine.getCommandLineString();
+
+    UIUtil
+      .invokeLaterIfNeeded(() -> ProgressManager.getInstance().run(new Task.Backgroundable(myProject, "Connecting to Console", false) {
+        @Override
+        public void run(@NotNull final ProgressIndicator indicator) {
+          indicator.setText("Connecting to console...");
+          try {
+            initAndRun();
+            connect(myStatementsToExecute);
+          }
+          catch (final Exception e) {
+            LOG.warn("Error running console", e);
+            UIUtil.invokeAndWaitIfNeeded(new Runnable() {
+              @Override
+              public void run() {
+                showErrorsInConsole(e);
+              }
+            });
+          }
+        }
+      }));
+  }
+
+  private void showErrorsInConsole(Exception e) {
+
+    DefaultActionGroup actionGroup = new DefaultActionGroup(createRerunAction());
+
+    final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN,
+                                                                                        actionGroup, false);
+
+    // Runner creating
+    final JPanel panel = new JPanel(new BorderLayout());
+    panel.add(actionToolbar.getComponent(), BorderLayout.WEST);
+
+    NewErrorTreeViewPanel errorViewPanel = new NewErrorTreeViewPanel(myProject, null, false, false, null);
+
+    String[] messages = StringUtil.isNotEmpty(e.getMessage()) ? StringUtil.splitByLines(e.getMessage()) : ArrayUtil.EMPTY_STRING_ARRAY;
+    if (messages.length == 0) {
+      messages = new String[]{"Unknown error"};
+    }
+
+    errorViewPanel.addMessage(MessageCategory.ERROR, messages, null, -1, -1, null);
+    panel.add(errorViewPanel, BorderLayout.CENTER);
+
+
+    final RunContentDescriptor contentDescriptor =
+      new RunContentDescriptor(null, myProcessHandler, panel, "Error running console");
+
+    actionGroup.add(createCloseAction(contentDescriptor));
+
+    ExecutionManager.getInstance(myProject).getContentManager().showRunContent(myExecutor, contentDescriptor);
+
+  }
+
+  private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) {
+    final int[] ports;
+    try {
+      // File "pydev/console/pydevconsole.py", line 223, in <module>
+      // port, client_port = sys.argv[1:3]
+      ports = NetUtils.findAvailableSocketPorts(2);
+    }
+    catch (IOException e) {
+      ExecutionHelper.showErrors(project, Collections.<Exception>singletonList(e), consoleType.getTitle(), null);
+      return null;
+    }
+    return ports;
+  }
+
+  protected GeneralCommandLine createCommandLine(@NotNull final Sdk sdk,
+                                                 @NotNull final Map<String, String> environmentVariables,
+                                                 String workingDir, int[] ports) {
+    return doCreateConsoleCmdLine(sdk, environmentVariables, workingDir, ports, PythonHelper.CONSOLE);
+  }
+
+  @NotNull
+  protected GeneralCommandLine doCreateConsoleCmdLine(Sdk sdk,
+                                                      Map<String, String> environmentVariables,
+                                                      String workingDir,
+                                                      int[] ports,
+                                                      PythonHelper helper) {
+    GeneralCommandLine cmd =
+      PythonCommandLineState.createPythonCommandLine(myProject, new PythonConsoleRunParams(myConsoleSettings, workingDir, sdk,
+                                                                                           environmentVariables), false,
+                                                     PtyCommandLine.isEnabled() && !SystemInfo.isWindows);
+    cmd.withWorkDirectory(myWorkingDir);
+
+    ParamsGroup group = cmd.getParametersList().getParamsGroup(PythonCommandLineState.GROUP_SCRIPT);
+    helper.addToGroup(group, cmd);
+
+    for (int port : ports) {
+      group.addParameter(String.valueOf(port));
+    }
+
+    return cmd;
+  }
+
+  private PythonConsoleView createConsoleView() {
+    PythonConsoleView consoleView = new PythonConsoleView(myProject, myTitle, mySdk);
+    myPydevConsoleCommunication.setConsoleFile(consoleView.getVirtualFile());
+    consoleView.addMessageFilter(new PythonTracebackFilter(myProject));
+    return consoleView;
+  }
+
+  private Process createProcess() throws ExecutionException {
+    if (PySdkUtil.isRemote(mySdk)) {
+      PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+      if (manager != null) {
+        UsageTrigger.trigger(CONSOLE_FEATURE + ".remote");
+        return createRemoteConsoleProcess(manager, myGeneralCommandLine.getParametersList().getArray(),
+                                          myGeneralCommandLine.getEnvironment(), myGeneralCommandLine.getWorkDirectory());
+      }
+      throw new PythonRemoteInterpreterManager.PyRemoteInterpreterExecutionException();
+    }
+    else {
+      myCommandLine = myGeneralCommandLine.getCommandLineString();
+      Map<String, String> envs = myGeneralCommandLine.getEnvironment();
+      EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myGeneralCommandLine.getCharset());
+
+      UsageTrigger.trigger(CONSOLE_FEATURE + ".local");
+      final Process server = myGeneralCommandLine.createProcess();
+
+      try {
+        myPydevConsoleCommunication = new PydevConsoleCommunication(myProject, myPorts[0], server, myPorts[1]);
+      }
+      catch (Exception e) {
+        throw new ExecutionException(e.getMessage());
+      }
+      return server;
+    }
+  }
+
+  private RemoteProcess createRemoteConsoleProcess(PythonRemoteInterpreterManager manager,
+                                                   String[] command,
+                                                   Map<String, String> env,
+                                                   File workDirectory)
+    throws ExecutionException {
+    PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+    assert data != null;
+
+    GeneralCommandLine commandLine = new GeneralCommandLine();
+
+    commandLine.setWorkDirectory(workDirectory);
+
+    commandLine.withParameters(command);
+
+    commandLine.getEnvironment().putAll(env);
+
+    commandLine.getParametersList().set(0, PythonRemoteInterpreterManager.toSystemDependent(new File(data.getHelpersPath(),
+                                                                                                     PYDEV_PYDEVCONSOLE_PY)
+                                                                                              .getPath(),
+                                                                                            PySourcePosition.isWindowsPath(
+                                                                                              data.getInterpreterPath())
+    ));
+    commandLine.getParametersList().set(1, "0");
+    commandLine.getParametersList().set(2, "0");
+
+    try {
+      PyRemotePathMapper pathMapper = PydevConsoleRunner.getPathMapper(myProject, mySdk, myConsoleSettings);
+
+      assert pathMapper != null;
+
+      commandLine.putUserData(PyRemoteProcessStarter.OPEN_FOR_INCOMING_CONNECTION, true);
+
+      myRemoteProcessHandlerBase = PyRemoteProcessStarterManagerUtil
+        .getManager(data).startRemoteProcess(myProject, commandLine, manager, data,
+                                             pathMapper);
+
+      myCommandLine = myRemoteProcessHandlerBase.getCommandLine();
+
+      RemoteProcess remoteProcess = myRemoteProcessHandlerBase.getProcess();
+
+      Couple<Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
+
+      if (remoteProcess instanceof Tunnelable) {
+        Tunnelable tunnelableProcess = (Tunnelable)remoteProcess;
+        tunnelableProcess.addLocalTunnel(myPorts[0], remotePorts.first);
+        tunnelableProcess.addRemoteTunnel(remotePorts.second, "localhost", myPorts[1]);
+
+        if (LOG.isDebugEnabled()) {
+          LOG.debug(String.format("Using tunneled communication for Python console: port %d (=> %d) on IDE side, " +
+                                  "port %d (=> %d) on pydevconsole.py side", myPorts[1], remotePorts.second, myPorts[0],
+                                  remotePorts.first));
+        }
+
+        myPydevConsoleCommunication = new PydevRemoteConsoleCommunication(myProject, myPorts[0], remoteProcess, myPorts[1]);
+      }
+      else {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug(String.format("Using direct communication for Python console: port %d on IDE side, port %d on pydevconsole.py side",
+                                  remotePorts.second, remotePorts.first));
+        }
+
+        myPydevConsoleCommunication =
+          new PydevRemoteConsoleCommunication(myProject, remotePorts.first, remoteProcess, remotePorts.second);
+      }
+
+      return remoteProcess;
+    }
+    catch (Exception e) {
+      throw new ExecutionException(e.getMessage(), e);
+    }
+  }
+
+  private static Couple<Integer> getRemotePortsFromProcess(RemoteProcess process) throws ExecutionException {
+    Scanner s = new Scanner(process.getInputStream());
+
+    return Couple.of(readInt(s, process), readInt(s, process));
+  }
+
+  private static int readInt(Scanner s, Process process) throws ExecutionException {
+    long started = System.currentTimeMillis();
+
+    StringBuilder sb = new StringBuilder();
+    boolean flag = false;
+
+    while (System.currentTimeMillis() - started < PORTS_WAITING_TIMEOUT) {
+      if (s.hasNextLine()) {
+        String line = s.nextLine();
+        sb.append(line).append("\n");
+        try {
+          int i = Integer.parseInt(line);
+          if (flag) {
+            LOG.warn("Unexpected strings in output:\n" + sb.toString());
+          }
+          return i;
+        }
+        catch (NumberFormatException ignored) {
+          flag = true;
+          continue;
+        }
+      }
+
+      TimeoutUtil.sleep(200);
+
+      if (process.exitValue() != 0) {
+        String error;
+        try {
+          error = "Console process terminated with error:\n" + StreamUtil.readText(process.getErrorStream()) + sb.toString();
+        }
+        catch (Exception ignored) {
+          error = "Console process terminated with exit code " + process.exitValue() + ", output:" + sb.toString();
+        }
+        throw new ExecutionException(error);
+      }
+      else {
+        break;
+      }
+    }
+
+    throw new ExecutionException("Couldn't read integer value from stream");
+  }
+
+  private PyConsoleProcessHandler createProcessHandler(final Process process) {
+    if (PySdkUtil.isRemote(mySdk)) {
+      PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+      if (manager != null) {
+        PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+        assert data != null;
+        myProcessHandler =
+          manager.createConsoleProcessHandler((RemoteProcess)process, myConsoleView, myPydevConsoleCommunication,
+                                              myCommandLine, CharsetToolkit.UTF8_CHARSET,
+                                              manager.setupMappings(myProject, data, null),
+                                              myRemoteProcessHandlerBase.getRemoteSocketToLocalHostProvider());
+      }
+      else {
+        LOG.error("Can't create remote console process handler");
+      }
+    }
+    else {
+      myProcessHandler = new PyConsoleProcessHandler(process, myConsoleView, myPydevConsoleCommunication, myCommandLine,
+                                                     CharsetToolkit.UTF8_CHARSET);
+    }
+    return myProcessHandler;
+  }
+
+
+  private void initAndRun() throws ExecutionException {
+    // Create Server process
+    final Process process = createProcess();
+    UIUtil.invokeLaterIfNeeded(() -> {
+      // Init console view
+      myConsoleView = createConsoleView();
+      if (myConsoleView != null) {
+        ((JComponent)myConsoleView).setBorder(new SideBorder(JBColor.border(), SideBorder.LEFT));
+      }
+      myProcessHandler = createProcessHandler(process);
+
+      myConsoleExecuteActionHandler = createExecuteActionHandler();
+
+      ProcessTerminatedListener.attach(myProcessHandler);
+
+      PythonConsoleView consoleView = myConsoleView;
+      myProcessHandler.addProcessListener(new ProcessAdapter() {
+        @Override
+        public void processTerminated(ProcessEvent event) {
+          consoleView.setEditable(false);
+        }
+      });
+
+      // Attach to process
+      myConsoleView.attachToProcess(myProcessHandler);
+      createContentDescriptorAndActions();
+
+
+      // Run
+      myProcessHandler.startNotify();
+    });
+  }
+
+  protected void createContentDescriptorAndActions() {
+    // Runner creating
+    final DefaultActionGroup toolbarActions = new DefaultActionGroup();
+    final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false);
+
+    // Runner creating
+    final JPanel panel = new JPanel(new BorderLayout());
+    panel.add(actionToolbar.getComponent(), BorderLayout.WEST);
+    panel.add(myConsoleView.getComponent(), BorderLayout.CENTER);
+
+    actionToolbar.setTargetComponent(panel);
+
+    final RunContentDescriptor contentDescriptor =
+      new RunContentDescriptor(myConsoleView, myProcessHandler, panel, new ConsoleTitleGen(myProject, myTitle).makeTitle(), null);
+
+    contentDescriptor.setFocusComputable(() -> myConsoleView.getConsoleEditor().getContentComponent());
+    contentDescriptor.setAutoFocusContent(true);
+
+
+    // tool bar actions
+    final List<AnAction> actions = fillToolBarActions(toolbarActions, contentDescriptor);
+    registerActionShortcuts(actions, myConsoleView.getConsoleEditor().getComponent());
+    registerActionShortcuts(actions, panel);
+
+    ExecutionManager.getInstance(myProject).getContentManager().showRunContent(myExecutor, contentDescriptor);
+  }
+
+  private void connect(final String[] statements2execute) {
+    if (handshake()) {
+      ApplicationManager.getApplication().invokeLater(() -> {
+        // Propagate console communication to language console
+        final PythonConsoleView consoleView = myConsoleView;
+
+        consoleView.setConsoleCommunication(myPydevConsoleCommunication);
+        consoleView.setSdk(mySdk);
+        consoleView.setExecutionHandler(myConsoleExecuteActionHandler);
+        myProcessHandler.addProcessListener(new ProcessAdapter() {
+          @Override
+          public void onTextAvailable(ProcessEvent event, Key outputType) {
+            consoleView.print(event.getText(), outputType);
+          }
+        });
+
+        enableConsoleExecuteAction();
+
+        for (String statement : statements2execute) {
+          consoleView.executeStatement(statement + "\n", ProcessOutputTypes.SYSTEM);
+        }
+
+        fireConsoleInitializedEvent(consoleView);
+      });
+    }
+    else {
+      myConsoleView.print("Couldn't connect to console process.", ProcessOutputTypes.STDERR);
+      myProcessHandler.destroyProcess();
+      myConsoleView.setEditable(false);
+    }
+  }
+
+
+  protected AnAction createRerunAction() {
+    return new RestartAction(this);
+  }
+
+  private AnAction createInterruptAction() {
+    AnAction anAction = new AnAction() {
+      @Override
+      public void actionPerformed(final AnActionEvent e) {
+        if (myPydevConsoleCommunication.isExecuting()) {
+          myConsoleView.print("^C", ProcessOutputTypes.SYSTEM);
+        }
+        myPydevConsoleCommunication.interrupt();
+      }
+
+      @Override
+      public void update(final AnActionEvent e) {
+        EditorEx consoleEditor = myConsoleView.getConsoleEditor();
+        boolean enabled = IJSwingUtilities.hasFocus(consoleEditor.getComponent()) && !consoleEditor.getSelectionModel().hasSelection();
+        e.getPresentation().setEnabled(enabled);
+      }
+    };
+    anAction
+      .registerCustomShortcutSet(KeyEvent.VK_C, InputEvent.CTRL_MASK, myConsoleView.getConsoleEditor().getComponent());
+    anAction.getTemplatePresentation().setVisible(false);
+    return anAction;
+  }
+
+  private AnAction createTabCompletionAction() {
+    final AnAction runCompletions = new AnAction() {
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+
+        Editor editor = myConsoleView.getConsoleEditor();
+        if (LookupManager.getActiveLookup(editor) != null) {
+          AnAction replace = ActionManager.getInstance().getAction("EditorChooseLookupItemReplace");
+          ActionUtil.performActionDumbAware(replace, e);
+          return;
+        }
+        AnAction completionAction = ActionManager.getInstance().getAction("CodeCompletion");
+        if (completionAction == null) {
+          return;
+        }
+        ActionUtil.performActionDumbAware(completionAction, e);
+      }
+
+      @Override
+      public void update(AnActionEvent e) {
+        Editor editor = myConsoleView.getConsoleEditor();
+        if (LookupManager.getActiveLookup(editor) != null) {
+          e.getPresentation().setEnabled(false);
+        }
+        int offset = editor.getCaretModel().getOffset();
+        Document document = editor.getDocument();
+        int lineStart = document.getLineStartOffset(document.getLineNumber(offset));
+        String textToCursor = document.getText(new TextRange(lineStart, offset));
+        e.getPresentation().setEnabled(!CharMatcher.WHITESPACE.matchesAllOf(textToCursor));
+      }
+    };
+
+    runCompletions
+      .registerCustomShortcutSet(KeyEvent.VK_TAB, 0, myConsoleView.getConsoleEditor().getComponent());
+    runCompletions.getTemplatePresentation().setVisible(false);
+    return runCompletions;
+  }
+
+
+
+  private boolean isIndentSubstring(String text) {
+    int indentSize = myConsoleExecuteActionHandler.getPythonIndent();
+    return text.length() >= indentSize && CharMatcher.WHITESPACE.matchesAllOf(text.substring(text.length() - indentSize));
+  }
+
+  private void enableConsoleExecuteAction() {
+    myConsoleExecuteActionHandler.setEnabled(true);
+  }
+
+  private boolean handshake() {
+    boolean res;
+    long started = System.currentTimeMillis();
+    do {
+      try {
+        res = myPydevConsoleCommunication.handshake();
+      }
+      catch (XmlRpcException ignored) {
+        res = false;
+      }
+      if (res) {
+        break;
+      }
+      else {
+        long now = System.currentTimeMillis();
+        if (now - started > APPROPRIATE_TO_WAIT) {
+          break;
+        }
+        else {
+          TimeoutUtil.sleep(100);
+        }
+      }
+    }
+    while (true);
+    return res;
+  }
+
+
+  private AnAction createStopAction() {
+    AnAction generalStopAction = ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM);
+    final AnAction stopAction = new DumbAwareAction() {
+      @Override
+      public void update(AnActionEvent e) {
+        generalStopAction.update(e);
+      }
+
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        e = stopConsole(e);
+
+        generalStopAction.actionPerformed(e);
+      }
+    };
+    stopAction.copyFrom(generalStopAction);
+    return stopAction;
+  }
+
+  private AnAction createCloseAction(final RunContentDescriptor descriptor) {
+    final AnAction generalCloseAction = new CloseAction(myExecutor, descriptor, myProject);
+
+    final AnAction stopAction = new DumbAwareAction() {
+      @Override
+      public void update(AnActionEvent e) {
+        generalCloseAction.update(e);
+      }
+
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        e = stopConsole(e);
+
+        clearContent(descriptor);
+
+        generalCloseAction.actionPerformed(e);
+      }
+    };
+    stopAction.copyFrom(generalCloseAction);
+    return stopAction;
+  }
+
+  protected void clearContent(RunContentDescriptor descriptor) {
+  }
+
+  private AnActionEvent stopConsole(AnActionEvent e) {
+    if (myPydevConsoleCommunication != null) {
+      e = new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(),
+                            e.getPresentation(), e.getActionManager(), e.getModifiers());
+      try {
+        closeCommunication();
+        // waiting for REPL communication before destroying process handler
+        Thread.sleep(300);
+      }
+      catch (Exception ignored) {
+        // Ignore
+      }
+    }
+    return e;
+  }
+
+  protected AnAction createSplitLineAction() {
+
+    class ConsoleSplitLineAction extends EditorAction {
+
+      private static final String CONSOLE_SPLIT_LINE_ACTION_ID = "Console.SplitLine";
+
+      public ConsoleSplitLineAction() {
+        super(new EditorWriteActionHandler() {
+
+          private final SplitLineAction mySplitLineAction = new SplitLineAction();
+
+          @Override
+          public boolean isEnabled(Editor editor, DataContext dataContext) {
+            return mySplitLineAction.getHandler().isEnabled(editor, dataContext);
+          }
+
+          @Override
+          public void executeWriteAction(Editor editor, @Nullable Caret caret, DataContext dataContext) {
+            ((EditorWriteActionHandler)mySplitLineAction.getHandler()).executeWriteAction(editor, caret, dataContext);
+            editor.getCaretModel().getCurrentCaret().moveCaretRelatively(0, 1, false, true);
+          }
+        });
+      }
+
+      public void setup() {
+        EmptyAction.setupAction(this, CONSOLE_SPLIT_LINE_ACTION_ID, null);
+      }
+    }
+
+    ConsoleSplitLineAction action = new ConsoleSplitLineAction();
+    action.setup();
+    return action;
+  }
+
+  private void closeCommunication() {
+    if (!myProcessHandler.isProcessTerminated()) {
+      myPydevConsoleCommunication.close();
+    }
+  }
+
+  @NotNull
+  protected PydevConsoleExecuteActionHandler createExecuteActionHandler() {
+    myConsoleExecuteActionHandler =
+      new PydevConsoleExecuteActionHandler(myConsoleView, myProcessHandler, myPydevConsoleCommunication);
+    myConsoleExecuteActionHandler.setEnabled(false);
+    new ConsoleHistoryController(myConsoleType.getTypeId(), "", myConsoleView).install();
+    return myConsoleExecuteActionHandler;
+  }
+
+  @Override
+  public PydevConsoleCommunication getPydevConsoleCommunication() {
+    return myPydevConsoleCommunication;
+  }
+
+  static VirtualFile getConsoleFile(PsiFile psiFile) {
+    VirtualFile file = psiFile.getViewProvider().getVirtualFile();
+    if (file instanceof LightVirtualFile) {
+      file = ((LightVirtualFile)file).getOriginalFile();
+    }
+    return file;
+  }
+
+  @Override
+  public void addConsoleListener(ConsoleListener consoleListener) {
+    myConsoleListeners.add(consoleListener);
+  }
+
+  private void fireConsoleInitializedEvent(LanguageConsoleView consoleView) {
+    for (ConsoleListener listener : myConsoleListeners) {
+      listener.handleConsoleInitialized(consoleView);
+    }
+    myConsoleListeners.clear();
+  }
+
+  @Override
+  public PydevConsoleExecuteActionHandler getConsoleExecuteActionHandler() {
+    return myConsoleExecuteActionHandler;
+  }
+
+
+  private static class RestartAction extends AnAction {
+    private PydevConsoleRunnerImpl myConsoleRunner;
+
+
+    private RestartAction(PydevConsoleRunnerImpl runner) {
+      copyFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN));
+      getTemplatePresentation().setIcon(AllIcons.Actions.Restart);
+      myConsoleRunner = runner;
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      myConsoleRunner.rerun();
+    }
+  }
+
+  private void rerun() {
+    new Task.Backgroundable(myProject, "Restarting Console", true) {
+      @Override
+      public void run(@NotNull ProgressIndicator indicator) {
+        if (myProcessHandler != null) {
+          UIUtil.invokeAndWaitIfNeeded((Runnable)() -> closeCommunication());
+
+          boolean processStopped = myProcessHandler.waitFor(5000L);
+          if (!processStopped && myProcessHandler.canKillProcess()) {
+            myProcessHandler.killProcess();
+          }
+          myProcessHandler.waitFor();
+        }
+
+        UIUtil.invokeLaterIfNeeded(() -> {
+
+          myRunRunAction.run();
+        });
+      }
+    }.queue();
+  }
+
+
+  private class ConnectDebuggerAction extends ToggleAction implements DumbAware {
+    private boolean mySelected = false;
+    private XDebugSession mySession = null;
+
+    public ConnectDebuggerAction() {
+      super("Attach Debugger", "Enables tracing of code executed in console", AllIcons.Actions.StartDebugger);
+    }
+
+    @Override
+    public boolean isSelected(AnActionEvent e) {
+      return mySelected;
+    }
+
+    @Override
+    public void update(AnActionEvent e) {
+      if (mySession != null) {
+        e.getPresentation().setEnabled(false);
+      }
+      else {
+        e.getPresentation().setEnabled(true);
+      }
+    }
+
+    @Override
+    public void setSelected(AnActionEvent e, boolean state) {
+      mySelected = state;
+
+      if (mySelected) {
+        try {
+          mySession = connectToDebugger();
+        }
+        catch (Exception e1) {
+          LOG.error(e1);
+          Messages.showErrorDialog("Can't connect to debugger", "Error Connecting Debugger");
+        }
+      }
+      else {
+        //TODO: disable debugging
+      }
+    }
+  }
+
+
+  private static class NewConsoleAction extends AnAction implements DumbAware {
+    public NewConsoleAction() {
+      super("New Console", "Creates new python console", AllIcons.General.Add);
+    }
+
+    @Override
+    public void update(AnActionEvent e) {
+      e.getPresentation().setEnabled(true);
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      PydevConsoleRunner runner =
+        PythonConsoleRunnerFactory.getInstance().createConsoleRunner(e.getData(CommonDataKeys.PROJECT), e.getData(LangDataKeys.MODULE));
+      runner.run();
+    }
+  }
+
+  private XDebugSession connectToDebugger() throws ExecutionException {
+    final ServerSocket serverSocket = PythonCommandLineState.createServerSocket();
+
+    final XDebugSession session = XDebuggerManager.getInstance(myProject).
+      startSessionAndShowTab("Python Console Debugger", PythonIcons.Python.Python, null, true, new XDebugProcessStarter() {
+        @NotNull
+        public XDebugProcess start(@NotNull final XDebugSession session) {
+          PythonDebugLanguageConsoleView debugConsoleView = new PythonDebugLanguageConsoleView(myProject, mySdk);
+
+          PyConsoleDebugProcessHandler consoleDebugProcessHandler =
+            new PyConsoleDebugProcessHandler(myProcessHandler);
+
+          PyConsoleDebugProcess consoleDebugProcess =
+            new PyConsoleDebugProcess(session, serverSocket, debugConsoleView,
+                                      consoleDebugProcessHandler);
+
+          PythonDebugConsoleCommunication communication =
+            PyDebugRunner.initDebugConsoleView(myProject, consoleDebugProcess, debugConsoleView, consoleDebugProcessHandler, session);
+
+          communication.addCommunicationListener(new ConsoleCommunicationListener() {
+            @Override
+            public void commandExecuted(boolean more) {
+              session.rebuildViews();
+            }
+
+            @Override
+            public void inputRequested() {
+            }
+          });
+
+          myPydevConsoleCommunication.setDebugCommunication(communication);
+          debugConsoleView.attachToProcess(consoleDebugProcessHandler);
+
+          consoleDebugProcess.waitForNextConnection();
+
+          try {
+            consoleDebugProcess.connect(myPydevConsoleCommunication);
+          }
+          catch (Exception e) {
+            LOG.error(e); //TODO
+          }
+
+          myProcessHandler.notifyTextAvailable("\nDebugger connected.\n", ProcessOutputTypes.STDERR);
+
+          return consoleDebugProcess;
+        }
+      });
+
+    return session;
+  }
+
+  @Override
+  public PyConsoleProcessHandler getProcessHandler() {
+    return myProcessHandler;
+  }
+
+  @Override
+  public PythonConsoleView getConsoleView() {
+    return myConsoleView;
+  }
+
+  public static PythonConsoleRunnerFactory factory() {
+    return new PydevConsoleRunnerFactory();
+  }
+
+  private static class PythonConsoleRunParams implements PythonRunParams {
+    private final PyConsoleOptions.PyConsoleSettings myConsoleSettings;
+    private String myWorkingDir;
+    private Sdk mySdk;
+    private Map<String, String> myEnvironmentVariables;
+
+    public PythonConsoleRunParams(@NotNull PyConsoleOptions.PyConsoleSettings consoleSettings,
+                                  @NotNull String workingDir,
+                                  @NotNull Sdk sdk,
+                                  @NotNull Map<String, String> envs) {
+      myConsoleSettings = consoleSettings;
+      myWorkingDir = workingDir;
+      mySdk = sdk;
+      myEnvironmentVariables = envs;
+      myEnvironmentVariables.putAll(consoleSettings.getEnvs());
+    }
+
+    @Override
+    public String getInterpreterOptions() {
+      return myConsoleSettings.getInterpreterOptions();
+    }
+
+    @Override
+    public void setInterpreterOptions(String interpreterOptions) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getWorkingDirectory() {
+      return myWorkingDir;
+    }
+
+    @Override
+    public void setWorkingDirectory(String workingDirectory) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Nullable
+    @Override
+    public String getSdkHome() {
+      return mySdk.getHomePath();
+    }
+
+    @Override
+    public void setSdkHome(String sdkHome) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setModule(Module module) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getModuleName() {
+      return myConsoleSettings.getModuleName();
+    }
+
+    @Override
+    public boolean isUseModuleSdk() {
+      return myConsoleSettings.isUseModuleSdk();
+    }
+
+    @Override
+    public void setUseModuleSdk(boolean useModuleSdk) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isPassParentEnvs() {
+      return myConsoleSettings.isPassParentEnvs();
+    }
+
+    @Override
+    public void setPassParentEnvs(boolean passParentEnvs) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Map<String, String> getEnvs() {
+      return myEnvironmentVariables;
+    }
+
+    @Override
+    public void setEnvs(Map<String, String> envs) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Nullable
+    @Override
+    public PathMappingSettings getMappingSettings() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setMappingSettings(@Nullable PathMappingSettings mappingSettings) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean shouldAddContentRoots() {
+      return myConsoleSettings.shouldAddContentRoots();
+    }
+
+    @Override
+    public boolean shouldAddSourceRoots() {
+      return myConsoleSettings.shouldAddSourceRoots();
+    }
+
+    @Override
+    public void setAddContentRoots(boolean flag) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setAddSourceRoots(boolean flag) {
+      throw new UnsupportedOperationException();
+    }
+  }
+}
index 09638656df0211cb3801fb72c57e1da6a7edd7e0..57b51c924ff884f93de28e7b6ba63eb5a46f2270 100644 (file)
@@ -30,5 +30,5 @@ public abstract class PythonConsoleRunnerFactory {
     return ServiceManager.getService(PythonConsoleRunnerFactory.class);
   }
   public abstract PydevConsoleRunner createConsoleRunner(@NotNull final Project project,
-                                         @Nullable Module contextModule);
+                                                         @Nullable Module contextModule);
 }
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
deleted file mode 100644 (file)
index 37de07c..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.console;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
-import com.intellij.execution.ui.RunContentDescriptor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.SimpleToolWindowPanel;
-import com.intellij.openapi.util.ActionCallback;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowManager;
-import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
-import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
-import com.intellij.openapi.wm.impl.content.ToolWindowContentUi;
-import com.intellij.ui.content.Content;
-import com.intellij.ui.content.ContentFactory;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
-import java.util.List;
-
-/**
- * @author traff
- */
-public class PythonConsoleToolWindow {
-  public static final Key<RunContentDescriptor> CONTENT_DESCRIPTOR = Key.create("CONTENT_DESCRIPTOR");
-
-  public static final Function<Content, RunContentDescriptor>
-    CONTENT_TO_DESCRIPTOR_FUNCTION = new Function<Content, RunContentDescriptor>() {
-    @Override
-    public RunContentDescriptor apply(@Nullable Content input) {
-      return input != null ? input.getUserData(CONTENT_DESCRIPTOR) : null;
-    }
-  };
-
-  private final Project myProject;
-
-  private boolean myInitialized = false;
-
-  private ActionCallback myActivation = new ActionCallback();
-
-  public PythonConsoleToolWindow(Project project) {
-    myProject = project;
-  }
-
-  public static PythonConsoleToolWindow getInstance(@NotNull Project project) {
-    return project.getComponent(PythonConsoleToolWindow.class);
-  }
-
-  public List<RunContentDescriptor> getConsoleContentDescriptors() {
-    return FluentIterable.from(Lists.newArrayList(getToolWindow().getContentManager().getContents()))
-      .transform(CONTENT_TO_DESCRIPTOR_FUNCTION).filter(
-        Predicates.notNull()).toList();
-  }
-
-
-  public void init(final @NotNull ToolWindow toolWindow, final @NotNull RunContentDescriptor contentDescriptor) {
-    setContent(toolWindow, contentDescriptor);
-
-    if (!myInitialized) {
-      doInit(toolWindow);
-    }
-  }
-
-  private void doInit(@NotNull final ToolWindow toolWindow) {
-    myInitialized = true;
-
-    toolWindow.setToHideOnEmptyContent(true);
-
-    ((ToolWindowManagerEx)ToolWindowManager.getInstance(myProject)).addToolWindowManagerListener(new ToolWindowManagerListener() {
-      @Override
-      public void toolWindowRegistered(@NotNull String id) {
-      }
-
-      @Override
-      public void stateChanged() {
-        ToolWindow window = getToolWindow();
-        if (window != null) {
-          boolean visible = window.isVisible();
-          if (visible && toolWindow.getContentManager().getContentCount() == 0) {
-            PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(myProject, null);
-            runner.run();
-          }
-        }
-      }
-    });
-  }
-
-  private static void setContent(ToolWindow toolWindow, RunContentDescriptor contentDescriptor) {
-    toolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
-
-    Content content = toolWindow.getContentManager().findContent(contentDescriptor.getDisplayName());
-    if (content == null) {
-      content = createContent(contentDescriptor);
-      toolWindow.getContentManager().addContent(content);
-    }
-    else {
-      SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true);
-      resetContent(contentDescriptor, panel, content);
-    }
-
-    toolWindow.getContentManager().setSelectedContent(content);
-  }
-
-  public ToolWindow getToolWindow() {
-    return ToolWindowManager.getInstance(myProject).getToolWindow(PythonConsoleToolWindowFactory.ID);
-  }
-
-  private static Content createContent(final @NotNull RunContentDescriptor contentDescriptor) {
-    SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true);
-
-    Content content = ContentFactory.SERVICE.getInstance().createContent(panel, contentDescriptor.getDisplayName(), false);
-    content.setCloseable(true);
-
-    resetContent(contentDescriptor, panel, content);
-
-    return content;
-  }
-
-  private static void resetContent(RunContentDescriptor contentDescriptor, SimpleToolWindowPanel panel, Content content) {
-    RunContentDescriptor oldDescriptor = content.getDisposer() instanceof RunContentDescriptor ? (RunContentDescriptor)content.getDisposer() : null;
-    if (oldDescriptor != null) Disposer.dispose(oldDescriptor);
-
-    panel.setContent(contentDescriptor.getComponent());
-
-    content.setComponent(panel);
-    content.setDisposer(contentDescriptor);
-    content.setPreferredFocusableComponent(contentDescriptor.getComponent());
-
-    content.putUserData(CONTENT_DESCRIPTOR, contentDescriptor);
-  }
-
-  private static FocusListener createFocusListener(final ToolWindow toolWindow) {
-    return new FocusListener() {
-      @Override
-      public void focusGained(FocusEvent e) {
-        JComponent component = getComponentToFocus(toolWindow);
-        if (component != null) {
-          component.requestFocusInWindow();
-        }
-      }
-
-      @Override
-      public void focusLost(FocusEvent e) {
-
-      }
-    };
-  }
-
-  private static JComponent getComponentToFocus(ToolWindow window) {
-    return window.getContentManager().getComponent();
-  }
-
-  public void initialized() {
-    myActivation.setDone();
-  }
-
-  public void activate(@NotNull Runnable runnable) {
-    myActivation.doWhenDone(runnable);
-    getToolWindow().activate(null);
-  }
-
-  @Nullable
-  public RunContentDescriptor getSelectedContentDescriptor() {
-    return CONTENT_TO_DESCRIPTOR_FUNCTION.apply(getToolWindow().getContentManager().getSelectedContent());
-  }
-}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
deleted file mode 100644 (file)
index 2a49d5e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.console;
-
-import com.intellij.execution.console.LanguageConsoleView;
-import com.intellij.openapi.project.DumbAware;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowFactory;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author traff
- */
-public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAware {
-  public static final String ID = "Python Console";
-
-  @Override
-  public void createToolWindowContent(final @NotNull Project project, final @NotNull ToolWindow toolWindow) {
-    PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(project, null);
-    runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
-      @Override
-      public void handleConsoleInitialized(LanguageConsoleView consoleView) {
-        PythonConsoleToolWindow.getInstance(project).initialized();
-      }
-    });
-    runner.run();
-  }
-}
index 3675ab0ad429dfd6e724a321b4904f980fa982c2..4219b40a376069d9b8d606c166d0fa36f7eb1e3e 100644 (file)
@@ -18,13 +18,26 @@ package com.jetbrains.python.console;
 import com.intellij.codeInsight.hint.HintManager;
 import com.intellij.execution.console.LanguageConsoleImpl;
 import com.intellij.execution.filters.OpenFileHyperlinkInfo;
+import com.intellij.execution.impl.ConsoleViewUtil;
 import com.intellij.execution.process.ProcessOutputTypes;
 import com.intellij.execution.ui.ConsoleViewContentType;
 import com.intellij.execution.ui.ObservableConsoleView;
+import com.intellij.ide.highlighter.HighlighterFactory;
+import com.intellij.injected.editor.EditorWindow;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.TransactionGuard;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
 import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.ex.DocumentEx;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
+import com.intellij.openapi.editor.highlighter.EditorHighlighter;
+import com.intellij.openapi.editor.markup.HighlighterTargetArea;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.fileTypes.SyntaxHighlighter;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
@@ -32,16 +45,20 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
 import com.intellij.ui.JBSplitter;
 import com.intellij.util.TimeoutUtil;
 import com.intellij.util.ui.UIUtil;
 import com.intellij.xdebugger.impl.frame.XStandaloneVariablesView;
 import com.jetbrains.python.PythonLanguage;
 import com.jetbrains.python.console.completion.PythonConsoleAutopopupBlockingHandler;
+import com.jetbrains.python.console.parsing.PythonConsoleData;
 import com.jetbrains.python.console.pydev.ConsoleCommunication;
 import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
 import com.jetbrains.python.debugger.PyDebuggerEditorsProvider;
@@ -62,6 +79,7 @@ import java.awt.*;
 public class PythonConsoleView extends LanguageConsoleImpl implements ObservableConsoleView, PyCodeExecutor {
 
   private static final Logger LOG = Logger.getInstance(PythonConsoleView.class);
+  private final ConsolePromptDecorator myPromptView;
 
   private PydevConsoleExecuteActionHandler myExecuteActionHandler;
   private PyConsoleSourceHighlighter mySourceHighlighter;
@@ -79,13 +97,15 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
     getVirtualFile().putUserData(LanguageLevel.KEY, PythonSdkType.getLanguageLevelForSdk(sdk));
     // Mark editor as console one, to prevent autopopup completion
     getConsoleEditor().putUserData(PythonConsoleAutopopupBlockingHandler.REPL_KEY, new Object());
-
-    setPrompt(PyConsoleUtil.ORDINARY_PROMPT);
+    getHistoryViewer().putUserData(ConsoleViewUtil.EDITOR_IS_CONSOLE_HISTORY_VIEW, true);
+    super.setPrompt(null);
     setUpdateFoldingsEnabled(false);
     //noinspection ConstantConditions
     myPyHighlighter = new PyHighlighter(
       sdk != null && sdk.getVersionString() != null ? LanguageLevel.fromPythonVersion(sdk.getVersionString()) : LanguageLevel.getDefault());
     myScheme = getConsoleEditor().getColorsScheme();
+    PythonConsoleData data = PyConsoleUtil.getOrCreateIPythonData(getVirtualFile());
+    myPromptView = new ConsolePromptDecorator(this.getConsoleEditor(), data);
   }
 
   public void setConsoleCommunication(final ConsoleCommunication communication) {
@@ -104,14 +124,12 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
     }
   }
 
+
   public void inputRequested() {
     if (myExecuteActionHandler != null) {
       final ConsoleCommunication consoleCommunication = myExecuteActionHandler.getConsoleCommunication();
       if (consoleCommunication instanceof PythonDebugConsoleCommunication) {
-        ((PythonDebugConsoleCommunication)consoleCommunication).waitingForInput = true;
-        myExecuteActionHandler.inputRequested();
-        myExecuteActionHandler.setEnabled(true);
-        myExecuteActionHandler.clearInputBuffer();
+        consoleCommunication.notifyInputRequested();
       }
     }
   }
@@ -123,7 +141,7 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
 
   @Override
   public void executeCode(final @NotNull String code, @Nullable final Editor editor) {
-    showConsole(() -> ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing Code in Console...", false) {
+    ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing Code in Console...", false) {
       @Override
       public void run(@NotNull final ProgressIndicator indicator) {
         long time = System.currentTimeMillis();
@@ -141,44 +159,33 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
           TimeoutUtil.sleep(300);
         }
         if (!indicator.isCanceled()) {
-          doExecute(code);
+          executeInConsole(code);
         }
       }
-    }));
-  }
-
-  private void showConsole(@NotNull Runnable runnable) {
-    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(getProject());
-    if (toolWindow != null && toolWindow.getToolWindow() != null && !toolWindow.getToolWindow().isVisible() && !ApplicationManager.getApplication().isUnitTestMode()) {
-      toolWindow.getToolWindow().activate(runnable);
-    }
-    else {
-      runnable.run();
-    }
+    });
   }
 
-  private void doExecute(String code) {
-    String codeFragment = PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize());
-    codeFragment += "\n";
-    executeInConsole(codeFragment);
-  }
 
   public void executeInConsole(final String code) {
-    UIUtil.invokeLaterIfNeeded(() -> {
-      String text = getConsoleEditor().getDocument().getText();
+    final String codeToExecute = code.endsWith("\n") ? code : code + "\n";
 
-      setInputText(code);
+    TransactionGuard.submitTransaction(this, () -> {
+      String text = getConsoleEditor().getDocument().getText();
+      ApplicationManager.getApplication().runWriteAction(() -> setInputText(codeToExecute));
+      int oldOffset = getConsoleEditor().getCaretModel().getOffset();
+      getConsoleEditor().getCaretModel().moveToOffset(codeToExecute.length());
       myExecuteActionHandler.runExecuteAction(this);
 
       if (!StringUtil.isEmpty(text)) {
-        setInputText(text);
+        ApplicationManager.getApplication().runWriteAction(() -> setInputText(text));
+        getConsoleEditor().getCaretModel().moveToOffset(oldOffset);
       }
     });
   }
 
   public void executeStatement(@NotNull String statement, @NotNull final Key attributes) {
     print(statement, outputTypeForAttributes(attributes));
-    myExecuteActionHandler.processLine(statement, true);
+    myExecuteActionHandler.processLine(statement);
   }
 
   public void printText(String text, final ConsoleViewContentType outputType) {
@@ -293,10 +300,78 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
     splitWindow();
   }
 
+  protected final void doAddPromptToHistory(boolean isMainPrompt) {
+    flushDeferredText();
+    EditorEx viewer = getHistoryViewer();
+    DocumentEx document = viewer.getDocument();
+    RangeHighlighter highlighter = getHistoryViewer().getMarkupModel()
+      .addRangeHighlighter(document.getTextLength(), document.getTextLength(), 0, null, HighlighterTargetArea.EXACT_RANGE);
+    final String prompt;
+    if (isMainPrompt) {
+      prompt = myPromptView.getMainPrompt();
+      print(prompt + " ", myPromptView.getPromptAttributes());
+    }
+    else {
+      prompt = myPromptView.getIndentPrompt();
+      //todo should really be myPromptView.getPromptAttributes() output type
+      //but in that case flushing doesn't get handled correctly. Take a look at it later
+      print(prompt + " ", ConsoleViewContentType.USER_INPUT);
+    }
+
+    highlighter.putUserData(PyConsoleCopyHandler.PROMPT_LENGTH_MARKER, prompt.length() + 1);
+  }
+
+  @NotNull
+  protected String addTextRangeToHistory(@NotNull TextRange textRange, @NotNull EditorEx inputEditor, boolean preserveMarkup) {
+    String text;
+    EditorHighlighter highlighter;
+    if (inputEditor instanceof EditorWindow) {
+      PsiFile file = ((EditorWindow)inputEditor).getInjectedFile();
+      highlighter =
+        HighlighterFactory.createHighlighter(file.getVirtualFile(), EditorColorsManager.getInstance().getGlobalScheme(), getProject());
+      String fullText = InjectedLanguageUtil.getUnescapedText(file, null, null);
+      highlighter.setText(fullText);
+      text = textRange.substring(fullText);
+    }
+    else {
+      text = inputEditor.getDocument().getText(textRange);
+      highlighter = inputEditor.getHighlighter();
+    }
+    SyntaxHighlighter syntax =
+      highlighter instanceof LexerEditorHighlighter ? ((LexerEditorHighlighter)highlighter).getSyntaxHighlighter() : null;
+    doAddPromptToHistory(true);
+
+    if (syntax != null) {
+      ConsoleViewUtil.printWithHighlighting(this, text, syntax, () -> doAddPromptToHistory(false));
+    }
+    else {
+      print(text, ConsoleViewContentType.USER_INPUT);
+    }
+    print("\n", ConsoleViewContentType.NORMAL_OUTPUT);
+    return text;
+  }
+
+
+  @Override
+  protected JComponent createCenterComponent() {
+    //workaround for extra lines appearing in the console
+    JComponent centerComponent = super.createCenterComponent();
+    getHistoryViewer().getSettings().setAdditionalLinesCount(0);
+    getHistoryViewer().getSettings().setUseSoftWraps(false);
+    getConsoleEditor().getGutter().registerTextAnnotation(this.myPromptView);
+    getConsoleEditor().getGutterComponentEx().setBackground(getConsoleEditor().getBackgroundColor());
+    getConsoleEditor().getGutterComponentEx().revalidate();
+    getConsoleEditor().getColorsScheme().setColor(EditorColors.GUTTER_BACKGROUND, getConsoleEditor().getBackgroundColor());
+
+    // settings.set
+    return centerComponent;
+  }
+
+
   private void splitWindow() {
     Component console = getComponent(0);
     removeAll();
-    JBSplitter p = new JBSplitter(false, 2f/3);
+    JBSplitter p = new JBSplitter(false, 2f / 3);
     p.setFirstComponent((JComponent)console);
     p.setSecondComponent(mySplitView.getPanel());
     p.setShowDividerControls(true);
@@ -318,4 +393,33 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
     validate();
     repaint();
   }
+
+  @Nullable
+  @Override
+  public String getPrompt() {
+    if (myPromptView == null) // we're in the constructor!
+    {
+      return super.getPrompt();
+    }
+    return myPromptView.getMainPrompt();
+  }
+
+
+  @Override
+  public void setPrompt(@Nullable String prompt) {
+    if (this.myPromptView == null) // we're in the constructor!
+    {
+      super.setPrompt(prompt);
+      return;
+    }
+    if (prompt != null) {
+      this.myPromptView.setMainPrompt(prompt);
+    }
+  }
+
+
+  @Override
+  public void setPromptAttributes(@NotNull ConsoleViewContentType textAttributes) {
+    myPromptView.setPromptAttributes(textAttributes);
+  }
 }
index f0a16fcb3b69146a718eddddbd0fa39e034bc595..54968e2dc36db6abb56daa0f773390230a64b719 100644 (file)
@@ -39,9 +39,7 @@ import java.util.List;
 public class PythonDebugConsoleCommunication extends AbstractConsoleCommunication {
   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.console.pydev.PythonDebugConsoleCommunication");
   private final PyDebugProcess myDebugProcess;
-
-  private final StringBuilder myExpression = new StringBuilder();
-
+  private boolean myNeedsMore = false;
 
   public PythonDebugConsoleCommunication(Project project, PyDebugProcess debugProcess) {
     super(project);
@@ -64,6 +62,11 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
     return waitingForInput;
   }
 
+  @Override
+  public boolean needsMore() {
+    return myNeedsMore;
+  }
+
   @Override
   public boolean isExecuting() {
     return false;
@@ -91,36 +94,46 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
           final Charset defaultCharset = EncodingProjectManager.getInstance(myDebugProcess.getProject()).getDefaultCharset();
           processInput.write((code.getText()).getBytes(defaultCharset));
           processInput.flush();
+
         }
         catch (IOException e) {
           LOG.error(e.getMessage());
         }
       }
+      myNeedsMore = false;
       waitingForInput = false;
+      notifyCommandExecuted(waitingForInput);
+
     }
     else {
 
-      myExpression.append(code.getText());
-      exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback<Pair<String, Boolean>>() {
+      exec(new ConsoleCodeFragment(code.getText(), false), new PyDebugCallback<Pair<String, Boolean>>() {
         @Override
         public void ok(Pair<String, Boolean> executed) {
           boolean more = executed.second;
-
-          if (!more) {
-            myExpression.setLength(0);
-          }
+          myNeedsMore = more;
+          notifyCommandExecuted(more);
           callback.fun(new InterpreterResponse(more, isWaitingForInput()));
+
         }
 
         @Override
         public void error(PyDebuggerException exception) {
-          myExpression.setLength(0);
+          myNeedsMore = false;
+          notifyCommandExecuted(false);
           callback.fun(new InterpreterResponse(false, isWaitingForInput()));
         }
       });
     }
   }
 
+  @Override
+  public void notifyInputRequested() {
+    waitingForInput = true;
+    super.notifyInputRequested();
+  }
+
+
   @Override
   public void interrupt() {
     throw new UnsupportedOperationException();
diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java
deleted file mode 100644 (file)
index bc209aa..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.console;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Lists;
-import com.intellij.execution.Executor;
-import com.intellij.execution.ui.RunContentDescriptor;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.wm.ToolWindow;
-import com.intellij.openapi.wm.ToolWindowManager;
-import com.intellij.ui.content.Content;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author traff
- */
-public class PythonToolWindowConsoleRunner extends PydevConsoleRunner {
-  private ToolWindow myToolWindow;
-
-  public PythonToolWindowConsoleRunner(@NotNull Project project,
-                                       @NotNull Sdk sdk,
-                                       @NotNull PyConsoleType consoleType,
-                                       @Nullable String workingDir, Map<String, String> environmentVariables,
-                                       @NotNull PyConsoleOptions.PyConsoleSettings settingsProvider,
-                                       String... statementsToExecute) {
-    super(project, sdk, consoleType, workingDir, environmentVariables, settingsProvider, statementsToExecute);
-  }
-
-  @Override
-  public void open() {
-    getToolWindow().activate(() -> {
-    }, true);
-  }
-
-  public ToolWindow getToolWindow() {
-    if (myToolWindow == null) {
-      myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID);
-    }
-    return myToolWindow;
-  }
-
-  @Override
-  protected void showConsole(Executor defaultExecutor, @NotNull RunContentDescriptor contentDescriptor) {
-    PythonConsoleToolWindow consoleToolWindow = PythonConsoleToolWindow.getInstance(getProject());
-    consoleToolWindow.init(getToolWindow(), contentDescriptor);
-  }
-
-  @Override
-  protected void clearContent(RunContentDescriptor descriptor) {
-    Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
-    assert content != null;
-    getToolWindow().getContentManager().removeContent(content, true);
-  }
-
-  @Override
-  protected List<String> getActiveConsoleNames(final String consoleTitle) {
-    return FluentIterable.from(
-      Lists.newArrayList(PythonConsoleToolWindow.getInstance(getProject()).getToolWindow().getContentManager().getContents())).transform(
-      new Function<Content, String>() {
-        @Override
-        public String apply(Content input) {
-          return input.getDisplayName();
-        }
-      }).filter(new Predicate<String>() {
-      @Override
-      public boolean apply(String input) {
-        return input.contains(consoleTitle);
-      }
-    }).toList();
-  }
-}
diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java
deleted file mode 100644 (file)
index 9ce9d58..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.console;
-
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.projectRoots.Sdk;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Map;
-
-/**
- * @author traff
- */
-public class PythonToolWindowConsoleRunnerFactory extends PydevConsoleRunnerFactory {
-  @Override
-  protected PydevConsoleRunner createConsoleRunner(Project project,
-                                                   Sdk sdk,
-                                                   String workingDir,
-                                                   Map<String, String> envs, PyConsoleType consoleType,
-                                                   @NotNull PyConsoleOptions.PyConsoleSettings settingsProvider, String... setupFragment) {
-    return new PythonToolWindowConsoleRunner(project, sdk, consoleType, workingDir, envs, settingsProvider, setupFragment);
-  }
-}
index 0e5d682198ddbad149280e15eb1c21eb1a441ba2..4c189321417b643057496f83be7868c64496c829 100644 (file)
@@ -51,6 +51,6 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware {
 
   public void actionPerformed(final AnActionEvent e) {
     PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(e.getData(CommonDataKeys.PROJECT), e.getData(LangDataKeys.MODULE));
-    runner.open();
+    runner.runSync();
   }
 }
diff --git a/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt b/python/src/com/jetbrains/python/console/actions/ShowVarsAction.kt
new file mode 100644 (file)
index 0000000..5485784
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.console.actions
+
+import com.intellij.icons.AllIcons
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.actionSystem.ToggleAction
+import com.intellij.openapi.project.DumbAware
+import com.jetbrains.python.console.PydevConsoleCommunication
+import com.jetbrains.python.console.PythonConsoleView
+
+/**
+ * Created by Yuli Fiterman on 9/18/2016.
+ */
+class ShowVarsAction(private val consoleView: PythonConsoleView, private val consoleComm: PydevConsoleCommunication) : ToggleAction("Show Variables", "Shows active console variables", AllIcons.Debugger.Watches), DumbAware {
+  private var mySelected = false
+
+  override fun isSelected(e: AnActionEvent): Boolean {
+    return mySelected
+  }
+
+  override fun setSelected(e: AnActionEvent, state: Boolean) {
+    mySelected = state
+
+    if (mySelected) {
+      consoleView.showVariables(consoleComm)
+    }
+    else {
+      consoleView.restoreWindow()
+    }
+  }
+}
diff --git a/python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.java b/python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.java
deleted file mode 100644 (file)
index 4b777cc..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2000-2014 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jetbrains.python.debugger;
-
-import com.intellij.execution.console.LanguageConsoleView;
-import com.intellij.execution.process.ProcessHandler;
-import com.intellij.xdebugger.XDebugSessionListener;
-import com.jetbrains.python.console.PydevConsoleExecuteActionHandler;
-import com.jetbrains.python.console.pydev.ConsoleCommunication;
-
-/**
- * @author traff
- */
-public class PydevDebugConsoleExecuteActionHandler extends PydevConsoleExecuteActionHandler implements XDebugSessionListener {
-
-  public PydevDebugConsoleExecuteActionHandler(LanguageConsoleView consoleView,
-                                               ProcessHandler myProcessHandler,
-                                               ConsoleCommunication consoleCommunication) {
-    super(consoleView, myProcessHandler, consoleCommunication);
-  }
-
-  @Override
-  protected String getConsoleIsNotEnabledMessage() {
-    return "Pause the process to use command-line.";
-  }
-
-  @Override
-  public void sessionPaused() {
-    setEnabled(true);
-  }
-
-  @Override
-  public void sessionResumed() {
-    setEnabled(false);
-  }
-
-  @Override
-  public void sessionStopped() {
-    setEnabled(false);
-  }
-
-  @Override
-  public void stackFrameChanged() {
-  }
-
-  @Override
-  public void beforeSessionResume() {
-  }
-}
diff --git a/python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.kt b/python/src/com/jetbrains/python/debugger/PydevDebugConsoleExecuteActionHandler.kt
new file mode 100644 (file)
index 0000000..fe9b308
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.debugger
+
+import com.intellij.execution.console.LanguageConsoleView
+import com.intellij.execution.process.ProcessHandler
+import com.intellij.xdebugger.XDebugSessionListener
+import com.jetbrains.python.console.PydevConsoleExecuteActionHandler
+import com.jetbrains.python.console.pydev.ConsoleCommunication
+
+/**
+ * @author traff
+ */
+class PydevDebugConsoleExecuteActionHandler(consoleView: LanguageConsoleView,
+                                            myProcessHandler: ProcessHandler,
+                                            consoleCommunication: ConsoleCommunication) : PydevConsoleExecuteActionHandler(consoleView, myProcessHandler, consoleCommunication), XDebugSessionListener {
+
+  override val consoleIsNotEnabledMessage: String
+    get() = "Pause the process to use command-line."
+
+  override fun sessionPaused() {
+    isEnabled = true
+  }
+
+  override fun sessionResumed() {
+    isEnabled = false
+  }
+
+  override fun sessionStopped() {
+    isEnabled = false
+  }
+
+  override fun stackFrameChanged() {
+  }
+
+  override fun beforeSessionResume() {
+  }
+}
index 05d8d13a3940bd56d709e4c7e2da04fcf2fa026a..d7ea4e12e208cb6d4be4a18c5d552a2792074fa2 100644 (file)
@@ -23,6 +23,7 @@ import com.intellij.execution.Executor;
 import com.intellij.execution.configurations.GeneralCommandLine;
 import com.intellij.execution.configurations.ParametersList;
 import com.intellij.execution.configurations.ParamsGroup;
+import com.intellij.execution.console.ConsoleExecuteAction;
 import com.intellij.execution.executors.DefaultDebugExecutor;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.openapi.actionSystem.AnAction;
@@ -34,6 +35,8 @@ import com.jetbrains.python.PythonHelper;
 import com.jetbrains.python.console.PyConsoleOptions;
 import com.jetbrains.python.console.PyConsoleType;
 import com.jetbrains.python.console.PydevConsoleRunner;
+import com.jetbrains.python.console.PydevConsoleRunnerImpl;
+import com.jetbrains.python.console.actions.ShowVarsAction;
 import com.jetbrains.python.sdk.PythonEnvUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -41,6 +44,8 @@ import org.jetbrains.annotations.Nullable;
 import java.util.List;
 import java.util.Map;
 
+import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.registerActionShortcuts;
+
 /**
  * @author yole
  */
@@ -75,6 +80,7 @@ public class PythonScriptCommandLineState extends PythonCommandLineState {
         return null;
       }
       List<AnAction> actions = Lists.newArrayList(createActions(runner.getConsoleView(), runner.getProcessHandler()));
+      actions.add(new ShowVarsAction(runner.getConsoleView(), runner.getPydevConsoleCommunication()));
 
       return new DefaultExecutionResult(runner.getConsoleView(), runner.getProcessHandler(), actions.toArray(new AnAction[actions.size()]));
     }
@@ -107,7 +113,7 @@ public class PythonScriptCommandLineState extends PythonCommandLineState {
   /**
    * @author traff
    */
-  public class PythonScriptWithConsoleRunner extends PydevConsoleRunner {
+  public class PythonScriptWithConsoleRunner extends PydevConsoleRunnerImpl {
 
     private CommandLinePatcher[] myPatchers;
 
@@ -119,14 +125,17 @@ public class PythonScriptCommandLineState extends PythonCommandLineState {
                                          CommandLinePatcher[] patchers,
                                          PyConsoleOptions.PyConsoleSettings consoleSettings,
                                          String... statementsToExecute) {
-      super(project, sdk, consoleType, workingDir, environmentVariables, consoleSettings, statementsToExecute);
+      super(project, sdk, consoleType, workingDir, environmentVariables, consoleSettings, () -> {
+      }, statementsToExecute);
       myPatchers = patchers;
     }
 
     @Override
     protected void createContentDescriptorAndActions() {
-      AnAction a = createConsoleExecAction(myConsoleExecuteActionHandler);
+      AnAction a = new ConsoleExecuteAction(super.getConsoleView(), myConsoleExecuteActionHandler,
+                                            myConsoleExecuteActionHandler.getEmptyExecuteAction(), myConsoleExecuteActionHandler);
       registerActionShortcuts(Lists.newArrayList(a), getConsoleView().getConsoleEditor().getComponent());
+
     }
 
     @Override
index fb6e49f17f03317953c7b7852333143677ca3e14..2cba3d2e4069cafe6f0e2f760f6fdea3d44b0ec1 100644 (file)
@@ -17,7 +17,7 @@ package com.jetbrains.env.python.console;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.intellij.execution.Executor;
+import com.intellij.execution.ExecutionManager;
 import com.intellij.execution.console.LanguageConsoleView;
 import com.intellij.execution.process.ProcessAdapter;
 import com.intellij.execution.process.ProcessEvent;
@@ -122,14 +122,7 @@ public class PyConsoleTask extends PyExecutionFixtureTestTask {
 
     disposeConsoleProcess();
 
-    if (!myContentDescriptorRef.isNull()) {
-      UIUtil.invokeAndWaitIfNeeded(new Runnable() {
-        @Override
-        public void run() {
-          Disposer.dispose(myContentDescriptorRef.get());
-        }
-      });
-    }
+    ExecutionManager.getInstance(getProject()).getContentManager().getAllDescriptors().forEach((Disposer::dispose));
 
     if (myConsoleView != null) {
       new WriteAction() {
@@ -151,20 +144,15 @@ public class PyConsoleTask extends PyExecutionFixtureTestTask {
     setProcessCanTerminate(false);
 
     PydevConsoleRunner consoleRunner =
-      new PydevConsoleRunner(project, sdk, PyConsoleType.PYTHON, myFixture.getTempDirPath(), Maps.<String, String>newHashMap(), PyConsoleOptions.getInstance(project).getPythonConsoleSettings(),
-                             new String[]{}) {
-        @Override
-        protected void showConsole(Executor defaultExecutor, @NotNull RunContentDescriptor contentDescriptor) {
-          myContentDescriptorRef.set(contentDescriptor);
-          super.showConsole(defaultExecutor, contentDescriptor);
-        }
-      };
-
+      new PydevConsoleRunnerImpl(project, sdk, PyConsoleType.PYTHON, myFixture.getTempDirPath(), Maps.newHashMap(),
+                                 PyConsoleOptions.getInstance(project).getPythonConsoleSettings(),
+                                 () -> {
+                                 }, new String[]{});
     before();
 
     myConsoleInitSemaphore = new Semaphore(0);
 
-    consoleRunner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
+    consoleRunner.addConsoleListener(new PydevConsoleRunnerImpl.ConsoleListener() {
       @Override
       public void handleConsoleInitialized(LanguageConsoleView consoleView) {
         myConsoleInitSemaphore.release();
@@ -178,9 +166,9 @@ public class PyConsoleTask extends PyExecutionFixtureTestTask {
     myCommandSemaphore = new Semaphore(1);
 
     myConsoleView = consoleRunner.getConsoleView();
-    myProcessHandler = (PyConsoleProcessHandler)consoleRunner.getProcessHandler();
+    myProcessHandler = consoleRunner.getProcessHandler();
 
-    myExecuteHandler = (PydevConsoleExecuteActionHandler)consoleRunner.getConsoleExecuteActionHandler();
+    myExecuteHandler = consoleRunner.getConsoleExecuteActionHandler();
 
     myCommunication = consoleRunner.getPydevConsoleCommunication();
 
diff --git a/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt b/python/testSrc/com/jetbrains/python/PyConsoleEnterHandlerTest.kt
new file mode 100644 (file)
index 0000000..24d25db
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python
+
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.ex.EditorEx
+import com.intellij.openapi.util.Disposer
+import com.intellij.testFramework.EditorTestUtil
+import com.jetbrains.python.console.PyConsoleEnterHandler
+import com.jetbrains.python.console.PythonConsoleView
+import com.jetbrains.python.fixtures.PyTestCase
+
+/**
+ * Created by Yuli Fiterman on 9/20/2016.
+ */
+class PyConsoleEnterHandlerTest : PyTestCase() {
+
+
+  lateinit private var myEditor: Editor
+  lateinit private var myEnterHandler: PyConsoleEnterHandler
+
+
+  override fun setUp() {
+    super.setUp()
+    resetEditor()
+    myEnterHandler = PyConsoleEnterHandler()
+  }
+
+  private fun resetEditor() {
+    myEditor = disposeOnTearDown(PythonConsoleView(myFixture.project, "Console", projectDescriptor?.sdk)).consoleEditor
+  }
+
+  fun push(text: String): Boolean {
+    text.forEach { EditorTestUtil.performTypingAction(myEditor, it) }
+    return myEnterHandler.handleEnterPressed(myEditor as EditorEx)
+  }
+
+  fun testTripleQuotes() {
+    assertFalse(push("'''abs"))
+
+
+  }
+
+  fun testSimpleSingleLine() {
+    assertTrue(push("a = 1"))
+    resetEditor()
+    assertFalse(push("for a in range(5):"))
+    resetEditor()
+    assertFalse(push("a = [1,\n2,"))
+
+
+  }
+
+  fun testInputComplete1() {
+    assertFalse(push("if 1:"))
+    assertFalse(push("\tx=1"))
+    assertTrue(push(""))
+  }
+
+  fun testInputComplete2() {
+    assertFalse(push("x = (2+\\"))
+    assertTrue(push("3)"))
+  }
+
+  fun testInputComplete3() {
+    push("if 1:")
+    assertFalse(push("    x = (2+"))
+    assertFalse(push("    y = 3"))
+    assertTrue(push(""))
+  }
+
+  fun testInputComplete4() {
+    push("try:")
+    push("    a = 5")
+    push("except:")
+    assertFalse(push("    raise"))
+  }
+
+  fun testLineContinuation() {
+    assertFalse(push("import os, \\"))
+    assertTrue(push("sys"))
+  }
+
+  fun testLineContinuation2() {
+    assertTrue(push("1 \\\n\n"))
+  }
+
+  fun testCellMagicHelp() {
+    assertTrue(push("%%cellm?"))
+  }
+
+  fun testCellMagic() {
+    assertFalse(push("%%cellm firstline"))
+    assertFalse(push("  line2"))
+    assertTrue(push(""))
+
+  }
+
+  override fun tearDown() {
+    Disposer.dispose(testRootDisposable)
+    super.tearDown()
+  }
+
+
+}