Revert to Tool Window API
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 11 Oct 2016 01:46:03 +0000 (03:46 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 11 Oct 2016 02:29:44 +0000 (04:29 +0200)
17 files changed:
platform/lang-api/src/com/intellij/execution/Executor.java
platform/lang-impl/src/com/intellij/execution/runners/ConsoleTitleGen.kt
platform/lang-impl/src/com/intellij/execution/ui/RunContentManagerImpl.java
platform/platform-resources/src/META-INF/LangExtensionPoints.xml
python/ide/src/META-INF/pycharm-core.xml
python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java
python/src/com/jetbrains/python/console/PyConsoleStarter.kt [deleted file]
python/src/com/jetbrains/python/console/PyConsoleToolWindowExecutor.java [deleted file]
python/src/com/jetbrains/python/console/PydevConsoleRunner.java
python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java
python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java
python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java [new file with mode: 0644]
python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.kt [new file with mode: 0644]
python/src/com/jetbrains/python/console/PythonConsoleView.java
python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java
python/testSrc/com/jetbrains/env/python/console/PyConsoleTask.java

index d5cec10d5f8420fbeaabd3bc3192e288345e40c5..b5258d2991a0028cc994482bcd2e38d79eda0bbf 100644 (file)
@@ -33,9 +33,6 @@ 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.
index 6bd14252e9281059785d12bae1a7d10647c18215..e203e29530cb21c0d624c00397167c0a47cd5df9 100644 (file)
@@ -22,7 +22,7 @@ import com.intellij.openapi.project.Project
 import java.util.stream.Collectors
 
 /**
- * Created by Yuli Fiterman on 9/13/2016.
+ * @author traff
  */
 open class ConsoleTitleGen @JvmOverloads constructor(private val myProject: Project, private val consoleTitle: String, private val shouldAddNumberToTitle: Boolean = true) {
 
index e53676eb12bcbe730eeeb291b485f628ceac36bc..189b6fa2a98274001b1f52310a7ddf7c077c9da7 100644 (file)
@@ -30,7 +30,6 @@ 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;
@@ -88,11 +87,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
     }
 
     for (Executor executor : ExecutorRegistry.getInstance().getRegisteredExecutors()) {
-      registerToolwindow(executor, toolWindowManager, true);
-    }
-
-    for (Executor executor : Extensions.getExtensions(Executor.INTERNAL_EXECUTOR_EXTENSION_NAME)) {
-      registerToolwindow(executor, toolWindowManager, false);
+      registerToolwindow(executor, toolWindowManager);
     }
 
     toolWindowManager.addToolWindowManagerListener(new ToolWindowManagerAdapter() {
@@ -121,9 +116,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
   public void dispose() {
   }
 
-  private void registerToolwindow(@NotNull final Executor executor,
-                                  @NotNull ToolWindowManagerEx toolWindowManager,
-                                  boolean autoRemoveOnEmpty) {
+  private void registerToolwindow(@NotNull final Executor executor, @NotNull ToolWindowManagerEx toolWindowManager) {
     final String toolWindowId = executor.getToolWindowId();
     if (toolWindowManager.getToolWindow(toolWindowId) != null) {
       return;
@@ -153,9 +146,7 @@ public class RunContentManagerImpl implements RunContentManager, Disposable {
 
     toolWindow.setIcon(executor.getToolWindowIcon());
     myToolwindowIdToBaseIconMap.put(toolWindowId, executor.getToolWindowIcon());
-    if (autoRemoveOnEmpty) {
-      new ContentManagerWatcher(toolWindow, contentManager);
-    }
+    new ContentManagerWatcher(toolWindow, contentManager);
     contentManager.addContentManagerListener(new ContentManagerAdapter() {
       @Override
       public void selectionChanged(final ContentManagerEvent event) {
@@ -264,8 +255,7 @@ 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);
@@ -373,9 +363,7 @@ 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());
   }
@@ -421,7 +409,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;
     }
@@ -620,8 +608,7 @@ 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 {
@@ -654,7 +641,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 cec19d6cc0fdb550e241e27ef3d8e6f36bc1d441..94009d6d5886ee2dda14dca8b672edb90ee8d3c2 100644 (file)
     <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"/>
 
index 3cb80f00d5d29ea9e27a8639f0645cedb3d78ea8..b9e322946fb70fa37319c53949690cc7f4609daa 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"/>
 
     <renameHandler implementation="com.intellij.platform.renameProject.ProjectFolderRenameHandler"/>
 
     <!-- Console -->
-    <internal_executor id="PyConsoleToolWindowExecutor" implementation="com.jetbrains.python.console.PyConsoleToolWindowExecutor"/>
-    <postStartupActivity implementation="com.jetbrains.python.console.PyConsoleStarter"/>
+
+    <toolWindow id="Python Console" anchor="bottom" icon="PythonIcons.Python.PythonConsoleToolWindow"
+                factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
+
 
 
     <directoryIndexExcludePolicy implementation="com.jetbrains.python.PyDirectoryIndexExcludePolicy"/>
index bc00af821211c229f33e3281f70131de83a55f6e..c9b74190e31fa131d8e1925d14fdcaae942a5716 100644 (file)
@@ -33,6 +33,7 @@ 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;
@@ -179,9 +180,22 @@ 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.newArrayList();
+    }
+
+    Collection<RunContentDescriptor> descriptors =
+      ExecutionHelper.findRunningConsole(project, dom -> dom.getExecutionConsole() instanceof PyCodeExecutor && isAlive(dom));
 
-    return ExecutionHelper.findRunningConsole(project, dom -> dom.getExecutionConsole() instanceof PyCodeExecutor && isAlive(dom));
+    if (descriptors.isEmpty() && toolWindow != null) {
+      return toolWindow.getConsoleContentDescriptors();
+    }
+    else {
+      return descriptors;
+    }
   }
 
   private static boolean isAlive(RunContentDescriptor dom) {
@@ -203,21 +217,33 @@ 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();
 
-    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);
+        RunContentDescriptor descriptor = descs.get(0);
+        if (descriptor != null && descriptor.getExecutionConsole() instanceof PyCodeExecutor) {
+          consumer.consume((PyCodeExecutor)descriptor.getExecutionConsole());
         }
-      }
-    });
-    runner.run();
+      });
+    }
+    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();
+    }
   }
 
-
   private static boolean canFindConsole(AnActionEvent e) {
     Project project = e.getProject();
     if (project != null) {
diff --git a/python/src/com/jetbrains/python/console/PyConsoleStarter.kt b/python/src/com/jetbrains/python/console/PyConsoleStarter.kt
deleted file mode 100644 (file)
index b5ea727..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index fb5a683..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 d638fcbc0e938037dcdcc875f83ac44505f3a5f0..b5b3771e05db60fbd21045b344e942fd025bbc88 100644 (file)
@@ -60,7 +60,7 @@ public interface PydevConsoleRunner {
   Key<ConsoleCommunication> CONSOLE_KEY = new Key<>("PYDEV_CONSOLE_KEY");
   Key<Sdk> CONSOLE_SDK = new Key<>("PYDEV_CONSOLE_SDK_KEY");
 
-  public interface ConsoleListener {
+  interface ConsoleListener {
     void handleConsoleInitialized(LanguageConsoleView consoleView);
   }
 
@@ -219,6 +219,8 @@ public interface PydevConsoleRunner {
     return containingFile != null ? containingFile.getCopyableUserData(CONSOLE_SDK) : null;
   }
 
+  void open();
+
   void runSync();
 
   void run();
index 9046a494f0a0a3fba01aafda6147ed2b1efb40c2..cdb18d421d9a410a4aa9dcfe9e9694b59ef3d78f 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.roots.ModuleRootManager;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Consumer;
 import com.intellij.util.PathMapper;
 import com.jetbrains.python.buildout.BuildoutFacet;
 import com.jetbrains.python.run.PythonCommandLineState;
@@ -112,11 +113,11 @@ public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
     Map<String, String> envs = Maps.newHashMap(settingsProvider.getEnvs());
     putIPythonEnvFlag(project, envs);
 
-    //The transaction is needed due the the FileDocumentManager.getInstance().saveAllDocuments() call in PydevConsoleRunnerImpl
-    Runnable rerunAction = () -> TransactionGuard.submitTransaction(project, () -> {
+    Consumer<String> rerunAction = title -> {
       PydevConsoleRunnerImpl runner = createConsoleRunner(project, module);
+      runner.setConsoleTitle(title);
       runner.run();
-    });
+    };
 
     return createConsoleRunner(project, sdk, workingDir, envs, PyConsoleType.PYTHON, settingsProvider, rerunAction, setupFragment);
   }
@@ -133,7 +134,7 @@ public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory {
                                                        Map<String, String> envs,
                                                        PyConsoleType consoleType,
                                                        PyConsoleOptions.PyConsoleSettings settingsProvider,
-                                                       Runnable rerunAction, String... setupFragment) {
+                                                       Consumer<String> rerunAction, String... setupFragment) {
     return new PydevConsoleRunnerImpl(project, sdk, consoleType, workingDir, envs, settingsProvider, rerunAction, setupFragment);
   }
 }
index 971f2d1bc909aefe25c80668f7743119392fa8b2..578323d58e0a9e2310c3c770cb342f0b829ea564 100644 (file)
@@ -16,6 +16,7 @@
 package com.jetbrains.python.console;
 
 import com.google.common.base.CharMatcher;
+import com.google.common.collect.Lists;
 import com.intellij.codeInsight.lookup.LookupManager;
 import com.intellij.execution.ExecutionException;
 import com.intellij.execution.ExecutionHelper;
@@ -43,6 +44,7 @@ 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.application.TransactionGuard;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Caret;
 import com.intellij.openapi.editor.Document;
@@ -72,10 +74,8 @@ 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.ui.content.Content;
+import com.intellij.util.*;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.net.NetUtils;
 import com.intellij.util.ui.MessageCategory;
@@ -109,11 +109,12 @@ import java.io.IOException;
 import java.net.ServerSocket;
 import java.util.*;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static com.intellij.execution.runners.AbstractConsoleRunnerWithHistory.registerActionShortcuts;
 
 /**
- * @author oleg
+ * @author traff, oleg
  */
 public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
   public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS";
@@ -127,8 +128,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
   private final Project myProject;
   private final String myTitle;
   private final String myWorkingDir;
-  private final Executor myExecutor;
-  private final Runnable myRunRunAction;
+  private final Consumer<String> myRerunAction;
   @NotNull
   private Sdk mySdk;
   private GeneralCommandLine myGeneralCommandLine;
@@ -157,7 +157,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
                                 @Nullable final String workingDir,
                                 Map<String, String> environmentVariables,
                                 @NotNull PyConsoleOptions.PyConsoleSettings settingsProvider,
-                                @NotNull Runnable rerunAction, String... statementsToExecute) {
+                                @NotNull Consumer<String> rerunAction, String... statementsToExecute) {
     myProject = project;
     mySdk = sdk;
     myTitle = consoleType.getTitle();
@@ -166,11 +166,12 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
     myEnvironmentVariables = environmentVariables;
     myConsoleSettings = settingsProvider;
     myStatementsToExecute = statementsToExecute;
-    myRunRunAction = rerunAction;
-    PyConsoleToolWindowExecutor toolWindowExecutor = PyConsoleToolWindowExecutor.findInstance();
-    myExecutor = toolWindowExecutor != null ? toolWindowExecutor : DefaultRunExecutor.getRunExecutorInstance();
+    myRerunAction = rerunAction;
   }
 
+  public void setConsoleTitle(String consoleTitle) {
+    myConsoleTitle = consoleTitle;
+  }
 
   private List<AnAction> fillToolBarActions(final DefaultActionGroup toolbarActions,
                                             final RunContentDescriptor contentDescriptor) {
@@ -214,6 +215,16 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
     return actions;
   }
 
+  @Override
+  public void open() {
+    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject);
+    if (toolWindow != null) {
+      toolWindow.getToolWindow().activate(() -> {}, true);
+    } else {
+      runSync();
+    }
+  }
+
   @Override
   public void runSync() {
     myPorts = findAvailablePorts(myProject, myConsoleType);
@@ -243,7 +254,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
 
   @Override
   public void run() {
-    ApplicationManager.getApplication().invokeAndWait(() -> FileDocumentManager.getInstance().saveAllDocuments());
+    TransactionGuard.submitTransaction(myProject, () -> FileDocumentManager.getInstance().saveAllDocuments());
 
     myPorts = findAvailablePorts(myProject, myConsoleType);
 
@@ -301,8 +312,24 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
 
     actionGroup.add(createCloseAction(contentDescriptor));
 
-    ExecutionManager.getInstance(myProject).getContentManager().showRunContent(myExecutor, contentDescriptor);
+    showContentDescriptor(contentDescriptor);
+  }
+
+
+  private void showContentDescriptor(RunContentDescriptor contentDescriptor) {
+    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject);
+    if (toolWindow != null) {
+      toolWindow
+        .init(PythonConsoleToolWindow.getToolWindow(myProject), contentDescriptor);
+    }
+    else {
+      ExecutionManager
+        .getInstance(myProject).getContentManager().showRunContent(getExecutor(), contentDescriptor);
+    }
+  }
 
+  private static Executor getExecutor() {
+    return DefaultRunExecutor.getRunExecutorInstance();
   }
 
   private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) {
@@ -572,8 +599,26 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
 
     actionToolbar.setTargetComponent(panel);
 
+    if (myConsoleTitle == null) {
+      myConsoleTitle = new ConsoleTitleGen(myProject, myTitle) {
+        @NotNull
+        @Override
+        protected List<String> getActiveConsoles(@NotNull String consoleTitle) {
+          PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject);
+          if (toolWindow != null && toolWindow.getToolWindow() != null) {
+            return Lists.newArrayList(toolWindow.getToolWindow().getContentManager().getContents()).stream().map(c -> c.getDisplayName())
+              .collect(
+                Collectors.toList());
+          }
+          else {
+            return super.getActiveConsoles(consoleTitle);
+          }
+        }
+      }.makeTitle();
+    }
+
     final RunContentDescriptor contentDescriptor =
-      new RunContentDescriptor(myConsoleView, myProcessHandler, panel, new ConsoleTitleGen(myProject, myTitle).makeTitle(), null);
+      new RunContentDescriptor(myConsoleView, myProcessHandler, panel, myConsoleTitle, null);
 
     contentDescriptor.setFocusComputable(() -> myConsoleView.getConsoleEditor().getContentComponent());
     contentDescriptor.setAutoFocusContent(true);
@@ -584,7 +629,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
     registerActionShortcuts(actions, myConsoleView.getConsoleEditor().getComponent());
     registerActionShortcuts(actions, panel);
 
-    ExecutionManager.getInstance(myProject).getContentManager().showRunContent(myExecutor, contentDescriptor);
+    showContentDescriptor(contentDescriptor);
   }
 
   private void connect(final String[] statements2execute) {
@@ -610,6 +655,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
         }
 
         fireConsoleInitializedEvent(consoleView);
+        consoleView.initialized();
       });
     }
     else {
@@ -686,7 +732,6 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
   }
 
 
-
   private boolean isIndentSubstring(String text) {
     int indentSize = myConsoleExecuteActionHandler.getPythonIndent();
     return text.length() >= indentSize && CharMatcher.WHITESPACE.matchesAllOf(text.substring(text.length() - indentSize));
@@ -744,7 +789,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
   }
 
   private AnAction createCloseAction(final RunContentDescriptor descriptor) {
-    final AnAction generalCloseAction = new CloseAction(myExecutor, descriptor, myProject);
+    final AnAction generalCloseAction = new CloseAction(getExecutor(), descriptor, myProject);
 
     final AnAction stopAction = new DumbAwareAction() {
       @Override
@@ -766,6 +811,12 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
   }
 
   protected void clearContent(RunContentDescriptor descriptor) {
+    PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject);
+    if (toolWindow != null && toolWindow.getToolWindow() != null) {
+      Content content = toolWindow.getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
+      assert content != null;
+      toolWindow.getToolWindow().getContentManager().removeContent(content, true);
+    }
   }
 
   private AnActionEvent stopConsole(AnActionEvent e) {
@@ -895,8 +946,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner {
         }
 
         UIUtil.invokeLaterIfNeeded(() -> {
-
-          myRunRunAction.run();
+          myRerunAction.consume(myConsoleTitle);
         });
       }
     }.queue();
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
new file mode 100644 (file)
index 0000000..362206a
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+  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(myProject).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(myProject);
+        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 getToolWindow(myProject);
+  }
+
+  public static ToolWindow getToolWindow(Project project) {
+    return ToolWindowManager.getInstance(project).getToolWindow(PythonConsoleToolWindowFactory.Companion.getID());
+  }
+
+  public void setContent(RunContentDescriptor contentDescriptor) {
+    setContent(getToolWindow(myProject), contentDescriptor);
+  }
+
+  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 activate(@NotNull Runnable runnable) {
+    getToolWindow(myProject).activate(runnable);
+  }
+
+  @Nullable
+  public RunContentDescriptor getSelectedContentDescriptor() {
+    return CONTENT_TO_DESCRIPTOR_FUNCTION.apply(getToolWindow(myProject).getContentManager().getSelectedContent());
+  }
+}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.kt b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.kt
new file mode 100644 (file)
index 0000000..3f22047
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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
+
+/**
+ * @author traff
+ */
+class PythonConsoleToolWindowFactory : ToolWindowFactory, DumbAware {
+
+  override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
+    val runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(project, null)
+    runner.runSync()
+  }
+
+  companion object {
+    val ID = "Python Console"
+  }
+}
index 4219b40a376069d9b8d606c166d0fa36f7eb1e3e..e5e41604b94db6ac804ad608d4d5c25850f5067a 100644 (file)
@@ -43,6 +43,7 @@ import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.util.ActionCallback;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.TextRange;
@@ -90,6 +91,7 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
   private boolean myFirstRun = true;
 
   private XStandaloneVariablesView mySplitView;
+  private ActionCallback myInitialized = new ActionCallback();
 
   public PythonConsoleView(final Project project, final String title, final Sdk sdk) {
     super(project, title, PythonLanguage.getInstance());
@@ -141,28 +143,30 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
 
   @Override
   public void executeCode(final @NotNull String code, @Nullable final Editor editor) {
-    ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing Code in Console...", false) {
-      @Override
-      public void run(@NotNull final ProgressIndicator indicator) {
-        long time = System.currentTimeMillis();
-        while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) {
-          if (indicator.isCanceled()) {
-            break;
-          }
-          if (System.currentTimeMillis() - time > 1000) {
-            if (editor != null) {
-              UIUtil.invokeLaterIfNeeded(
-                () -> HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage()));
-            }
-            return;
-          }
-          TimeoutUtil.sleep(300);
-        }
-        if (!indicator.isCanceled()) {
-          executeInConsole(code);
-        }
-      }
-    });
+    myInitialized.doWhenDone(() ->
+                               ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing Code in Console...", false) {
+                                 @Override
+                                 public void run(@NotNull final ProgressIndicator indicator) {
+                                   long time = System.currentTimeMillis();
+                                   while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) {
+                                     if (indicator.isCanceled()) {
+                                       break;
+                                     }
+                                     if (System.currentTimeMillis() - time > 1000) {
+                                       if (editor != null) {
+                                         UIUtil.invokeLaterIfNeeded(
+                                           () -> HintManager.getInstance()
+                                             .showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage()));
+                                       }
+                                       return;
+                                     }
+                                     TimeoutUtil.sleep(300);
+                                   }
+                                   if (!indicator.isCanceled()) {
+                                     executeInConsole(code);
+                                   }
+                                 }
+                               }));
   }
 
 
@@ -422,4 +426,8 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable
   public void setPromptAttributes(@NotNull ConsoleViewContentType textAttributes) {
     myPromptView.setPromptAttributes(textAttributes);
   }
+
+  public void initialized() {
+    myInitialized.setDone();
+  }
 }
index 4c189321417b643057496f83be7868c64496c829..0e5d682198ddbad149280e15eb1c21eb1a441ba2 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.runSync();
+    runner.open();
   }
 }
index d7ea4e12e208cb6d4be4a18c5d552a2792074fa2..b0797faad76c617c587d44e5f9cd9084d87a776f 100644 (file)
@@ -125,8 +125,7 @@ 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, (s) -> {}, statementsToExecute);
       myPatchers = patchers;
     }
 
@@ -135,7 +134,6 @@ public class PythonScriptCommandLineState extends PythonCommandLineState {
       AnAction a = new ConsoleExecuteAction(super.getConsoleView(), myConsoleExecuteActionHandler,
                                             myConsoleExecuteActionHandler.getEmptyExecuteAction(), myConsoleExecuteActionHandler);
       registerActionShortcuts(Lists.newArrayList(a), getConsoleView().getConsoleEditor().getComponent());
-
     }
 
     @Override
index 2cba3d2e4069cafe6f0e2f760f6fdea3d44b0ec1..b1c2e4c34c9425e09a56546f01d42533ca2a20a1 100644 (file)
@@ -146,8 +146,7 @@ public class PyConsoleTask extends PyExecutionFixtureTestTask {
     PydevConsoleRunner consoleRunner =
       new PydevConsoleRunnerImpl(project, sdk, PyConsoleType.PYTHON, myFixture.getTempDirPath(), Maps.newHashMap(),
                                  PyConsoleOptions.getInstance(project).getPythonConsoleSettings(),
-                                 () -> {
-                                 }, new String[]{});
+                                 (s) -> {});
     before();
 
     myConsoleInitSemaphore = new Semaphore(0);