Duplex console; MultilineHandlingExecuteActionHandler

This commit is contained in:
Valentin Fondaratov
2014-06-26 12:24:19 +04:00
committed by Valentin Fondaratov
parent e66d6cd6fb
commit d3dea3fc01
5 changed files with 429 additions and 217 deletions
@@ -0,0 +1,250 @@
package com.intellij.execution.console;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ObservableConsoleView;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
public class DuplexConsoleView<S extends ConsoleView, T extends ConsoleView> extends JPanel implements ConsoleView, ObservableConsoleView {
private final static String PRIMARY_CONSOLE_PANEL = "PRIMARY_CONSOLE_PANEL";
private final static String SECONDARY_CONSOLE_PANEL = "SECONDARY_CONSOLE_PANEL";
private final S myPrimaryConsoleView;
private final T mySecondaryConsoleView;
private boolean myPrimary;
private ProcessHandler myProcessHandler;
public DuplexConsoleView(S primaryConsoleView, T secondaryConsoleView) {
super(new CardLayout());
myPrimaryConsoleView = primaryConsoleView;
mySecondaryConsoleView = secondaryConsoleView;
add(myPrimaryConsoleView.getComponent(), PRIMARY_CONSOLE_PANEL);
add(mySecondaryConsoleView.getComponent(), SECONDARY_CONSOLE_PANEL);
myPrimary = true;
enableConsole(false);
Disposer.register(this, myPrimaryConsoleView);
Disposer.register(this, mySecondaryConsoleView);
}
public static <S extends ConsoleView, T extends ConsoleView> DuplexConsoleView<S, T> create(S primary, T secondary) {
return new DuplexConsoleView<S, T>(primary, secondary);
}
public void enableConsole(boolean primary) {
if (primary == myPrimary) {
// nothing to do
return;
}
CardLayout cl = (CardLayout)(getLayout());
cl.show(this, primary ? PRIMARY_CONSOLE_PANEL : SECONDARY_CONSOLE_PANEL);
getSubConsoleView(primary).getComponent().requestFocus();
myPrimary = primary;
}
public boolean isPrimaryConsoleEnabled() {
return myPrimary;
}
public S getPrimaryConsoleView() {
return myPrimaryConsoleView;
}
public T getSecondaryConsoleView() {
return mySecondaryConsoleView;
}
public ConsoleView getSubConsoleView(boolean primary) {
return primary ? getPrimaryConsoleView() : getSecondaryConsoleView();
}
@Override
public void print(@NotNull String s, @NotNull ConsoleViewContentType contentType) {
myPrimaryConsoleView.print(s, contentType);
mySecondaryConsoleView.print(s, contentType);
}
@Override
public void clear() {
myPrimaryConsoleView.clear();
mySecondaryConsoleView.clear();
}
@Override
public void scrollTo(int offset) {
myPrimaryConsoleView.scrollTo(offset);
mySecondaryConsoleView.scrollTo(offset);
}
@Override
public void attachToProcess(ProcessHandler processHandler) {
myProcessHandler = processHandler;
myPrimaryConsoleView.attachToProcess(processHandler);
mySecondaryConsoleView.attachToProcess(processHandler);
}
@Override
public void setOutputPaused(boolean value) {
myPrimaryConsoleView.setOutputPaused(value);
mySecondaryConsoleView.setOutputPaused(value);
}
@Override
public boolean isOutputPaused() {
return false;
}
@Override
public boolean hasDeferredOutput() {
return myPrimaryConsoleView.hasDeferredOutput() && mySecondaryConsoleView.hasDeferredOutput();
}
@Override
public void performWhenNoDeferredOutput(Runnable runnable) {
}
@Override
public void setHelpId(String helpId) {
myPrimaryConsoleView.setHelpId(helpId);
mySecondaryConsoleView.setHelpId(helpId);
}
@Override
public void addMessageFilter(Filter filter) {
myPrimaryConsoleView.addMessageFilter(filter);
mySecondaryConsoleView.addMessageFilter(filter);
}
@Override
public void printHyperlink(String hyperlinkText, HyperlinkInfo info) {
myPrimaryConsoleView.printHyperlink(hyperlinkText, info);
mySecondaryConsoleView.printHyperlink(hyperlinkText, info);
}
@Override
public int getContentSize() {
return myPrimaryConsoleView.getContentSize();
}
@Override
public boolean canPause() {
return false;
}
@NotNull
@Override
public AnAction[] createConsoleActions() {
List<AnAction> actions = Lists.newArrayList();
actions.addAll(Arrays.asList(myPrimaryConsoleView.createConsoleActions()));
//for (AnAction action : mySecondaryConsoleView.createConsoleActions()) {
// if (!actions.contains(action)) {
// actions.add(action);
// }
//}
actions.add(new SwitchDuplexConsoleViewAction(this));
return ArrayUtil.toObjectArray(actions, AnAction.class);
}
@Override
public void allowHeavyFilters() {
myPrimaryConsoleView.allowHeavyFilters();
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public JComponent getPreferredFocusableComponent() {
return this;
}
@Override
public void dispose() {
// registered children in constructor
}
@Override
public void addChangeListener(@NotNull ChangeListener listener, @NotNull Disposable parent) {
if (myPrimaryConsoleView instanceof ObservableConsoleView) {
((ObservableConsoleView)myPrimaryConsoleView).addChangeListener(listener, parent);
}
if (mySecondaryConsoleView instanceof ObservableConsoleView) {
((ObservableConsoleView)mySecondaryConsoleView).addChangeListener(listener, parent);
}
}
private static class SwitchDuplexConsoleViewAction extends ToggleAction implements DumbAware {
private final DuplexConsoleView myConsole;
public SwitchDuplexConsoleViewAction(final DuplexConsoleView console) {
super(ExecutionBundle.message("run.configuration.show.command.line.action.name"), null,
AllIcons.Debugger.ToolConsole);
myConsole = console;
}
@Override
public boolean isSelected(final AnActionEvent event) {
return !myConsole.isPrimaryConsoleEnabled();
}
@Override
public void setSelected(final AnActionEvent event, final boolean flag) {
myConsole.enableConsole(!flag);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
update(event);
}
});
}
@Override
public void update(final AnActionEvent event) {
super.update(event);
final Presentation presentation = event.getPresentation();
final boolean isRunning = myConsole.myProcessHandler != null && !myConsole.myProcessHandler.isProcessTerminated();
if (isRunning) {
presentation.setEnabled(true);
}
else {
myConsole.enableConsole(true);
presentation.putClientProperty(SELECTED_PROPERTY, false);
presentation.setEnabled(false);
}
}
}
}
@@ -0,0 +1,158 @@
/*
* 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.intellij.execution.console;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public abstract class MultilineHandlingExecuteActionHandler extends BaseConsoleExecuteActionHandler {
@NotNull
private BaseConsoleExecuteActionHandler myDelegateHandler;
@Nullable
private String myMultilineToken = null;
@NotNull
protected StringBuilder myInputBuffer;
public MultilineHandlingExecuteActionHandler(@NotNull BaseConsoleExecuteActionHandler delegateHandler, boolean preserveMarkup) {
super(preserveMarkup);
myDelegateHandler = delegateHandler;
initInputBuffer();
}
@NotNull
protected abstract Collection<String> getMultilineTokens();
protected abstract boolean startsAnotherCodeFragment(@NotNull String line);
protected abstract void onMultilineEnter();
protected abstract void onMultilineExit();
protected abstract void onLineContinuation();
protected abstract boolean processLineIfShouldNotExecute(@NotNull String line);
@Nullable
protected String getOpeningMultilineToken(@NotNull String line) {
for (String token : getMultilineTokens()) {
if (isMultilineStarts(line, token)) {
return token;
}
}
return null;
}
protected boolean closesThisMultilineToken(@NotNull String line, @NotNull String token) {
return isMultilineStarts(line, token);
}
@Override
protected void execute(@NotNull String text, @NotNull LanguageConsoleView console) {
if (text.isEmpty()) {
processOneLine(text, console);
}
else {
if (StringUtil.countNewLines(text.trim()) > 0) {
executeMultiLine(text, console);
}
else {
processOneLine(text, console);
}
}
}
protected final void executeMultiLine(@NotNull String text, @NotNull LanguageConsoleView console) {
myInputBuffer.append(text);
myDelegateHandler.execute(myInputBuffer.toString(), console);
}
protected final void processOneLine(@NotNull String line, @NotNull LanguageConsoleView console) {
line = StringUtil.trimTrailing(line);
if (StringUtil.isEmptyOrSpaces(line)) {
doProcessLine("\n", console);
}
else if (startsAnotherCodeFragment(line)) {
doProcessLine("\n", console);
doProcessLine(line, console);
}
else {
doProcessLine(line, console);
}
}
protected final void doProcessLine(@NotNull final String line, @NotNull LanguageConsoleView console) {
if (!StringUtil.isEmptyOrSpaces(line)) {
myInputBuffer.append(line);
if (!line.endsWith("\n")) {
myInputBuffer.append("\n");
}
}
else if (StringUtil.isEmptyOrSpaces(myInputBuffer.toString())) {
myInputBuffer.append("");
}
// multiline strings handling
if (myMultilineToken == null) {
myMultilineToken = getOpeningMultilineToken(line);
if (myMultilineToken != null) {
// change language
onMultilineEnter();
return;
}
}
else {
if (closesThisMultilineToken(line, myMultilineToken)) {
myMultilineToken = null;
// restore language
onMultilineExit();
}
else {
if(line.equals("\n")) {
myInputBuffer.append("\n");
}
return;
}
}
// Process line continuation
if (line.endsWith("\\")) {
onLineContinuation();
return;
}
if (processLineIfShouldNotExecute(line)) {
return;
}
myDelegateHandler.execute(myInputBuffer.toString(), console);
initInputBuffer();
}
private void initInputBuffer() {
myInputBuffer = new StringBuilder();
}
private static boolean isMultilineStarts(String line, String token) {
return StringUtil.getOccurrenceCount(line, token) % 2 == 1;
}
}
@@ -16,7 +16,7 @@ home.directory.not.specified.for.jdk.error.message=Home directory is not specifi
run.configuration.pause.output.action.name=Pause Output
main.class.is.not.specified.error.message=Main class is not specified
close.tab.action.name=Close
run.configuration.show.command.line.action.name=Show Python Prompt
run.configuration.show.command.line.action.name=Show Console Prompt
#---
create.run.configuration.action.name=Create Run Configuration
@@ -59,7 +59,7 @@ public class PyOpenDebugConsoleAction extends AnAction implements DumbAware {
selectRunningProcess(e.getDataContext(), project, new Consumer<PythonDebugLanguageConsoleView>() {
@Override
public void consume(PythonDebugLanguageConsoleView view) {
view.showDebugConsole(true);
view.enableConsole(false);
IdeFocusManager.getInstance(project).requestFocus(view.getPydevConsoleView().getComponent(), true);
}
});
@@ -15,64 +15,27 @@
*/
package com.jetbrains.python.console;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionBundle;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.console.DuplexConsoleView;
import com.intellij.execution.console.LanguageConsoleImpl;
import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.ui.ObservableConsoleView;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Disposer;
import icons.PythonIcons;
import com.intellij.openapi.wm.IdeFocusManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
* @author traff
*/
public class PythonDebugLanguageConsoleView extends JPanel implements ConsoleView, ObservableConsoleView, PyCodeExecutor {
private final static String TEXT_CONSOLE_PANEL = "TEXT_CONSOLE_PANEL";
private final static String PYDEV_CONSOLE_PANEL = "PYDEV_CONSOLE_PANEL";
private final PythonConsoleView myPydevConsoleView;
private final ConsoleView myTextConsole;
public boolean myIsDebugConsole = false;
private ProcessHandler myProcessHandler;
public class PythonDebugLanguageConsoleView extends DuplexConsoleView<ConsoleView, PythonConsoleView> implements PyCodeExecutor {
public PythonDebugLanguageConsoleView(final Project project, Sdk sdk, ConsoleView consoleView) {
super(new CardLayout());
myPydevConsoleView = new PythonConsoleView(project, "Python Console", sdk);
myTextConsole = consoleView;
super(consoleView, new PythonConsoleView(project, "Python Console", sdk));
add(myTextConsole.getComponent(), TEXT_CONSOLE_PANEL);
add(myPydevConsoleView.getComponent(), PYDEV_CONSOLE_PANEL);
showDebugConsole(PyConsoleOptions.getInstance(project).isShowDebugConsoleByDefault());
Disposer.register(this, myPydevConsoleView);
Disposer.register(this, myTextConsole);
enableConsole(!PyConsoleOptions.getInstance(project).isShowDebugConsoleByDefault());
}
public PythonDebugLanguageConsoleView(final Project project, Sdk sdk) {
@@ -81,192 +44,33 @@ public class PythonDebugLanguageConsoleView extends JPanel implements ConsoleVie
@Override
public void executeCode(@NotNull String code, @Nullable Editor e) {
showDebugConsole(true);
enableConsole(false);
getPydevConsoleView().executeCode(code, e);
}
private void doShowConsole(String type) {
CardLayout cl = (CardLayout)(getLayout());
cl.show(this, type);
}
public boolean isDebugConsole() {
return myIsDebugConsole;
}
public void showDebugConsole(boolean flag) {
if (flag) {
doShowConsole(PYDEV_CONSOLE_PANEL);
myPydevConsoleView.requestFocus();
}
else {
doShowConsole(TEXT_CONSOLE_PANEL);
}
myIsDebugConsole = flag;
}
public PythonConsoleView getPydevConsoleView() {
return myPydevConsoleView;
return getSecondaryConsoleView();
}
public ConsoleViewImpl getTextConsole() {
if (myTextConsole instanceof ConsoleViewImpl) {
return (ConsoleViewImpl)myTextConsole;
ConsoleView consoleView = getPrimaryConsoleView();
if (consoleView instanceof ConsoleViewImpl) {
return (ConsoleViewImpl)consoleView;
}
return null;
}
@Override
public void allowHeavyFilters() {
myTextConsole.allowHeavyFilters();
}
public void enableConsole(boolean primary) {
super.enableConsole(primary);
@Override
public JComponent getComponent() {
return this;
}
if (!primary && !isPrimaryConsoleEnabled()) {
PythonConsoleView pydevConsoleView = getPydevConsoleView();
LanguageConsoleImpl languageConsole = pydevConsoleView.getConsole();
@Override
public JComponent getPreferredFocusableComponent() {
return this;
}
@Override
public void dispose() {
}
@Override
public void print(@NotNull String s, @NotNull ConsoleViewContentType contentType) {
myPydevConsoleView.print(s, contentType);
myTextConsole.print(s, contentType);
}
@Override
public void clear() {
myPydevConsoleView.clear();
myTextConsole.clear();
}
@Override
public void scrollTo(int offset) {
myPydevConsoleView.getLanguageConsole().getHistoryViewer().getCaretModel().moveToOffset(offset);
myPydevConsoleView.getLanguageConsole().getHistoryViewer().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
myTextConsole.scrollTo(offset);
}
@Override
public void attachToProcess(ProcessHandler processHandler) {
myProcessHandler = processHandler;
myPydevConsoleView.attachToProcess(processHandler);
myTextConsole.attachToProcess(processHandler);
}
@Override
public void setOutputPaused(boolean value) {
myPydevConsoleView.setOutputPaused(value);
myTextConsole.setOutputPaused(value);
}
@Override
public boolean isOutputPaused() {
return false;
}
@Override
public boolean hasDeferredOutput() {
return myPydevConsoleView.hasDeferredOutput() && myTextConsole.hasDeferredOutput();
}
@Override
public void performWhenNoDeferredOutput(Runnable runnable) {
}
@Override
public void setHelpId(String helpId) {
myPydevConsoleView.setHelpId(helpId);
myTextConsole.setHelpId(helpId);
}
@Override
public void addMessageFilter(Filter filter) {
myPydevConsoleView.addMessageFilter(filter);
myTextConsole.addMessageFilter(filter);
}
@Override
public void printHyperlink(String hyperlinkText, HyperlinkInfo info) {
myPydevConsoleView.printHyperlink(hyperlinkText, info);
myTextConsole.printHyperlink(hyperlinkText, info);
}
@Override
public int getContentSize() {
return myTextConsole.getContentSize();
}
@Override
public boolean canPause() {
return false;
}
@NotNull
@Override
public AnAction[] createConsoleActions() {
List<AnAction> actions = Lists.newArrayList(myTextConsole.createConsoleActions());
actions.add(new ShowDebugConsoleAction(this));
return actions.toArray(new AnAction[actions.size()]);
}
@Override
public void addChangeListener(@NotNull ChangeListener listener, @NotNull Disposable parent) {
myPydevConsoleView.addChangeListener(listener, parent);
if (myTextConsole instanceof ObservableConsoleView) {
((ObservableConsoleView)myTextConsole).addChangeListener(listener, parent);
}
}
private static class ShowDebugConsoleAction extends ToggleAction implements DumbAware {
private final PythonDebugLanguageConsoleView myConsole;
public ShowDebugConsoleAction(final PythonDebugLanguageConsoleView console) {
super(ExecutionBundle.message("run.configuration.show.command.line.action.name"), null,
PythonIcons.Python.Debug.CommandLine);
myConsole = console;
}
@Override
public boolean isSelected(final AnActionEvent event) {
return myConsole.isDebugConsole();
}
@Override
public void setSelected(final AnActionEvent event, final boolean flag) {
myConsole.showDebugConsole(flag);
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
update(event);
}
});
}
@Override
public void update(final AnActionEvent event) {
super.update(event);
final Presentation presentation = event.getPresentation();
final boolean isRunning = myConsole.myProcessHandler != null && !myConsole.myProcessHandler.isProcessTerminated();
if (isRunning) {
presentation.setEnabled(true);
}
else {
myConsole.showDebugConsole(false);
presentation.putClientProperty(SELECTED_PROPERTY, false);
presentation.setEnabled(false);
}
IdeFocusManager.findInstance().requestFocus(languageConsole.getConsoleEditor().getContentComponent(), true);
pydevConsoleView.updateUI();
languageConsole.getHistoryViewer().getComponent().updateUI();
}
}
}