Added Python Console tool window.

This commit is contained in:
Dmitry Trofimov
2014-08-04 04:55:24 +02:00
parent b12d4428fe
commit d05c29bef0
7 changed files with 309 additions and 47 deletions
@@ -15,6 +15,9 @@
*/
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;
@@ -129,6 +132,7 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
registerActionShortcuts(actions, getLanguageConsole().getConsoleEditor().getComponent());
registerActionShortcuts(actions, panel);
panel.updateUI();
showConsole(defaultExecutor, contentDescriptor);
// Run
@@ -137,30 +141,21 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
private String constructConsoleTitle(final @NotNull String consoleTitle) {
if (shouldAddNumberToTitle()) {
List<RunContentDescriptor> consoles = ExecutionHelper.collectConsolesByDisplayName(myProject, new NotNullFunction<String, Boolean>() {
@NotNull
@Override
public Boolean fun(String dom) {
return dom.contains(consoleTitle);
}
});
List<String> activeConsoleNames = getActiveConsoleNames(consoleTitle);
int max = 0;
for (RunContentDescriptor dsc : consoles) {
ProcessHandler handler = dsc.getProcessHandler();
if (handler != null && !handler.isProcessTerminated()) {
if (max == 0) {
max = 1;
}
try {
int num = Integer.parseInt(dsc.getDisplayName().substring(consoleTitle.length() + 1, dsc.getDisplayName().length() - 1));
if (num > max) {
max = num;
}
}
catch (Exception ignored) {
//skip
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) + ")";
@@ -178,9 +173,9 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
return false;
}
protected void showConsole(Executor defaultExecutor, RunContentDescriptor myDescriptor) {
protected void showConsole(Executor defaultExecutor, RunContentDescriptor contentDescriptor) {
// Show in run toolwindow
ExecutionManager.getInstance(myProject).getContentManager().showRunContent(defaultExecutor, myDescriptor);
ExecutionManager.getInstance(myProject).getContentManager().showRunContent(defaultExecutor, contentDescriptor);
}
protected void finishConsole() {
@@ -245,11 +240,13 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
public static AnAction createConsoleExecAction(@NotNull LanguageConsoleView console,
@NotNull ProcessHandler processHandler,
@NotNull ProcessBackedConsoleExecuteActionHandler consoleExecuteActionHandler) {
return new ConsoleExecuteAction(console, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), consoleExecuteActionHandler);
return new ConsoleExecuteAction(console, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(),
consoleExecuteActionHandler);
}
protected AnAction createConsoleExecAction(@NotNull ProcessBackedConsoleExecuteActionHandler consoleExecuteActionHandler) {
return new ConsoleExecuteAction(myConsoleView, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(), consoleExecuteActionHandler);
return new ConsoleExecuteAction(myConsoleView, consoleExecuteActionHandler, consoleExecuteActionHandler.getEmptyExecuteAction(),
consoleExecuteActionHandler);
}
@SuppressWarnings("UnusedDeclaration")
@@ -301,4 +298,27 @@ public abstract class AbstractConsoleRunnerWithHistory<T extends LanguageConsole
public ProcessBackedConsoleExecuteActionHandler getConsoleExecuteActionHandler() {
return myConsoleExecuteActionHandler;
}
protected List<String> getActiveConsoleNames(final String consoleTitle) {
List<RunContentDescriptor> consoles = ExecutionHelper.collectConsolesByDisplayName(myProject, new NotNullFunction<String, Boolean>() {
@NotNull
@Override
public Boolean fun(String dom) {
return 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();
}
}
+12
View File
@@ -528,6 +528,12 @@
<!-- Packaging -->
<moduleService serviceInterface="com.jetbrains.python.packaging.PyPackageRequirementsSettings"
serviceImplementation="com.jetbrains.python.packaging.PyPackageRequirementsSettings"/>
<!-- Console -->
<toolWindow id="Python Console" anchor="bottom" icon=""
factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
</extensions>
<extensionPoints>
@@ -615,6 +621,12 @@
</component>
</project-components>
<project-components>
<component>
<implementation-class>com.jetbrains.python.console.PythonConsoleToolWindow</implementation-class>
</component>
</project-components>
<actions>
<group id="PyTypeHierarchyPopupMenu">
<reference ref="TypeHierarchyBase.BaseOnThisType"/>
@@ -17,7 +17,6 @@ package com.jetbrains.python.actions;
import com.intellij.execution.ExecutionHelper;
import com.intellij.execution.console.LanguageConsoleView;
import com.intellij.execution.console.LanguageConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.actionSystem.*;
@@ -215,7 +214,7 @@ public class ExecuteInConsoleAction extends AnAction {
private static void startConsole(final Project project,
final Consumer<PyCodeExecutor> consumer,
Module context) {
PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context);
PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context, null);
runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
@Override
public void handleConsoleInitialized(LanguageConsoleView consoleView) {
@@ -16,6 +16,10 @@
package com.jetbrains.python.console;
import com.google.common.base.CharMatcher;
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.google.common.collect.Maps;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionHelper;
@@ -59,11 +63,14 @@ import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.remote.RemoteSshProcess;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.content.Content;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IJSwingUtilities;
import com.intellij.util.PathMappingSettings;
@@ -132,6 +139,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
private static final long APPROPRIATE_TO_WAIT = 60000;
private PyRemoteSdkCredentials myRemoteCredentials;
private ToolWindow myToolWindow;
protected PydevConsoleRunner(@NotNull final Project project,
@NotNull Sdk sdk, @NotNull final PyConsoleType consoleType,
@@ -192,8 +200,10 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
@NotNull final PyConsoleType consoleType,
@Nullable final String workingDirectory,
@NotNull final Map<String, String> environmentVariables,
@Nullable final ToolWindow toolWindow,
final String... statements2execute) {
final PydevConsoleRunner consoleRunner = create(project, sdk, consoleType, workingDirectory, environmentVariables);
consoleRunner.setToolWindow(toolWindow);
consoleRunner.setStatementsToExecute(statements2execute);
consoleRunner.run();
return consoleRunner;
@@ -481,6 +491,12 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
}
}
@Override
protected void showConsole(Executor defaultExecutor, RunContentDescriptor contentDescriptor) {
PythonConsoleToolWindow terminalView = PythonConsoleToolWindow.getInstance(getProject());
terminalView.init(getToolWindow(), contentDescriptor);
}
protected AnAction createRerunAction() {
return new RestartAction(this);
}
@@ -583,9 +599,32 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
}
@Override
protected AnAction createCloseAction(Executor defaultExecutor, RunContentDescriptor myDescriptor) {
final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, myDescriptor);
return createConsoleStoppingAction(generalCloseAction);
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;
}
private void clearContent(RunContentDescriptor descriptor) {
Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
assert content != null;
getToolWindow().getContentManager().removeContent(content, true);
}
private AnAction createConsoleStoppingAction(final AnAction generalStopAction) {
@@ -597,26 +636,31 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
@Override
public void actionPerformed(AnActionEvent e) {
if (myPydevConsoleCommunication != null) {
final AnActionEvent furtherActionEvent =
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
}
generalStopAction.actionPerformed(furtherActionEvent);
}
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 {
@@ -738,6 +782,17 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
}
}
public ToolWindow getToolWindow() {
if (myToolWindow == null) {
myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID);
}
return myToolWindow;
}
public void setToolWindow(ToolWindow toolWindow) {
myToolWindow = toolWindow;
}
public interface ConsoleListener {
void handleConsoleInitialized(LanguageConsoleView consoleView);
}
@@ -889,4 +944,21 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
return session;
}
@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();
}
}
@@ -0,0 +1,124 @@
package com.jetbrains.python.console;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
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.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
/**
* @author traff
*/
public class PythonConsoleToolWindow {
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 void init(final @NotNull ToolWindow toolWindow, final @NotNull RunContentDescriptor contentDescriptor) {
addContent(toolWindow, contentDescriptor);
if (!myInitialized) {
doInit(toolWindow);
}
}
private void doInit(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) {
RunPythonConsoleAction.runPythonConsole(myProject, null, toolWindow);
}
}
}
});
}
private static void addContent(ToolWindow toolWindow, RunContentDescriptor contentDescriptor) {
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);
final 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) {
panel.setContent(contentDescriptor.getComponent());
//panel.addFocusListener(createFocusListener(toolWindow));
content.setPreferredFocusableComponent(contentDescriptor.getComponent());
}
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();
}
}
@@ -0,0 +1,33 @@
/*
* 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.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
/**
* @author traff
*/
public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAware {
public static final String ID = "Python Console";
@Override
public void createToolWindowContent(Project project, ToolWindow toolWindow) {
RunPythonConsoleAction.runPythonConsole(project, null, toolWindow);
}
}
@@ -32,6 +32,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.openapi.wm.ToolWindow;
import com.intellij.util.PathMappingSettings;
import com.jetbrains.python.buildout.BuildoutFacet;
import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
@@ -42,6 +43,7 @@ import com.jetbrains.python.sdk.PythonEnvUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
@@ -77,11 +79,11 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware {
public void actionPerformed(final AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
runPythonConsole(project, e.getData(LangDataKeys.MODULE));
runPythonConsole(project, e.getData(LangDataKeys.MODULE), null);
}
@NotNull
public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule) {
public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule, @Nullable ToolWindow toolWindow) {
assert project != null : "Project is null";
Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, contextModule);
@@ -152,7 +154,7 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware {
envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled);
return PydevConsoleRunner
.createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, setupFragment);
.createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, toolWindow, setupFragment);
}
public static PathMappingSettings getMappings(Project project, Sdk sdk) {