/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.python.pydev.shared_interactive_console.console.ui;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
import org.eclipse.debug.ui.console.IConsoleLineTracker;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleDocumentPartitioner;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.part.IPageBookViewPage;
import org.python.pydev.shared_core.callbacks.ICallback;
import org.python.pydev.shared_core.structure.Tuple;
import org.python.pydev.shared_core.utils.Reflection;
import org.python.pydev.shared_interactive_console.console.IScriptConsoleCommunication;
import org.python.pydev.shared_interactive_console.console.IScriptConsoleInterpreter;
import org.python.pydev.shared_interactive_console.console.InterpreterResponse;
import org.python.pydev.shared_interactive_console.console.ScriptConsoleHistory;
import org.python.pydev.shared_interactive_console.console.ScriptConsolePrompt;
import org.python.pydev.shared_interactive_console.console.ui.internal.ICommandHandler;
import org.python.pydev.shared_interactive_console.console.ui.internal.IHandleScriptAutoEditStrategy;
import org.python.pydev.shared_interactive_console.console.ui.internal.ScriptConsolePage;
import org.python.pydev.shared_interactive_console.console.ui.internal.ScriptConsoleSession;
import org.python.pydev.shared_interactive_console.console.ui.internal.ScriptConsoleViewer;
import org.python.pydev.shared_interactive_console.console.ui.internal.actions.AbstractHandleBackspaceAction;
import org.python.pydev.shared_ui.content_assist.AbstractCompletionProcessorWithCycling;
public abstract class ScriptConsole extends TextConsole implements ICommandHandler {
protected ScriptConsolePage page;
protected ScriptConsolePartitioner partitioner;
protected IScriptConsoleInterpreter interpreter;
protected ScriptConsoleSession session;
protected ListenerList consoleListeners;
protected ScriptConsolePrompt prompt;
protected ScriptConsoleHistory history;
private WeakReference<ScriptConsoleViewer> viewer;
public static final String DEFAULT_CONSOLE_TYPE = "org.python.pydev.debug.newconsole.PydevConsole";
public static final String SCRIPT_DEBUG_CONSOLE_IN_PROCESS_CONSOLE = "SCRIPT_DEBUG_CONSOLE_IN_PROCESS_CONSOLE";
// Backward-compatibility
public static ScriptConsole getActiveScriptConsole(String ignored) {
return getActiveScriptConsole();
}
public static Iterable<IConsoleView> iterConsoles() {
List<IConsoleView> consoles = new ArrayList<>();
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
List<IViewPart> consoleParts = getConsoleParts(page, false);
if (consoleParts.size() == 0) {
consoleParts = getConsoleParts(page, true);
}
for (IViewPart iViewPart : consoleParts) {
if (iViewPart instanceof IConsoleView) {
consoles.add((IConsoleView) iViewPart);
}
}
}
}
return consoles;
}
/**
* @return the currently active script console.
*/
@SuppressWarnings("restriction")
public static ScriptConsole getActiveScriptConsole() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
List<IViewPart> consoleParts = getConsoleParts(page, false);
if (consoleParts.size() == 0) {
consoleParts = getConsoleParts(page, true);
}
if (consoleParts.size() > 0) {
IConsoleView view = null;
long lastChangeMillis = Long.MIN_VALUE;
if (consoleParts.size() == 1) {
view = (IConsoleView) consoleParts.get(0);
} else {
//more than 1 view available
for (int i = 0; i < consoleParts.size(); i++) {
IConsoleView temp = (IConsoleView) consoleParts.get(i);
IConsole console = temp.getConsole();
if (console instanceof ScriptConsole) {
ScriptConsole tempConsole = (ScriptConsole) console;
ScriptConsoleViewer viewer = tempConsole.getViewer();
long tempLastChangeMillis = viewer.getLastChangeMillis();
if (tempLastChangeMillis > lastChangeMillis) {
lastChangeMillis = tempLastChangeMillis;
view = temp;
}
}
}
}
if (view != null) {
IConsole console = view.getConsole();
if (console instanceof ScriptConsole) {
return (ScriptConsole) console;
} else {
if (console instanceof ProcessConsole) {
ProcessConsole processConsole = (ProcessConsole) console;
Object scriptConsole = processConsole
.getAttribute(ScriptConsole.SCRIPT_DEBUG_CONSOLE_IN_PROCESS_CONSOLE);
if (scriptConsole instanceof ScriptConsole) {
ScriptConsole scriptConsole2 = (ScriptConsole) scriptConsole;
IScriptConsoleCommunication consoleCommunication = scriptConsole2.getInterpreter()
.getConsoleCommunication();
if (consoleCommunication.isConnected()) {
return scriptConsole2;
}
}
}
}
}
}
}
}
return null;
}
/**
* @param page the page where the console view is
* @param restore whether we should try to restore it
* @return a list with the parts containing the console
*/
private static List<IViewPart> getConsoleParts(IWorkbenchPage page, boolean restore) {
List<IViewPart> consoleParts = new ArrayList<IViewPart>();
IViewReference[] viewReferences = page.getViewReferences();
for (IViewReference ref : viewReferences) {
if (ref.getId().equals(IConsoleConstants.ID_CONSOLE_VIEW)) {
IViewPart part = ref.getView(restore);
if (part != null) {
consoleParts.add(part);
if (restore) {
return consoleParts;
}
}
}
}
return consoleParts;
}
@Override
protected IConsoleDocumentPartitioner getPartitioner() {
return partitioner;
}
public ScriptConsole(String consoleName, String consoleType, IScriptConsoleInterpreter interpreterArg) {
super(consoleName, consoleType, null, true);
this.interpreter = interpreterArg;
this.consoleListeners = new ListenerList(ListenerList.IDENTITY);
this.prompt = createConsolePrompt();
this.history = new ScriptConsoleHistory();
this.session = new ScriptConsoleSession();
addListener(this.session);
partitioner = new ScriptConsolePartitioner();
getDocument().setDocumentPartitioner(partitioner);
partitioner.connect(getDocument());
}
/**
* @return the assistant that should handle content assist requests (code completion)
*/
protected abstract IContentAssistProcessor createConsoleCompletionProcessor(ContentAssistant pyContentAssistant);
/**
* @return the assistant that should handle quick assist requests (quick fixes)
*/
protected abstract IQuickAssistProcessor createConsoleQuickAssistProcessor(QuickAssistAssistant quickAssist);
/**
* @return the text hover to be used in the console.
*/
protected abstract ITextHover createHover();
/**
* @return the console prompt that should be used.
*/
protected abstract ScriptConsolePrompt createConsolePrompt();
public IScriptConsoleSession getSession() {
return session;
}
public void addListener(IScriptConsoleListener listener) {
consoleListeners.add(listener);
}
public void removeListener(IScriptConsoleListener listener) {
consoleListeners.remove(listener);
}
protected void setInterpreter(IScriptConsoleInterpreter interpreter) {
this.interpreter = interpreter;
}
public IScriptConsoleInterpreter getInterpreter() {
return interpreter;
}
public ScriptConsolePrompt getPrompt() {
return prompt;
}
public ScriptConsoleHistory getHistory() {
return history;
}
/**
* Creates the actual page to be shown to the user.
*/
@Override
public IPageBookViewPage createPage(IConsoleView view) {
page = new ScriptConsolePage(this, view, createSourceViewerConfiguration());
return page;
}
public abstract SourceViewerConfiguration createSourceViewerConfiguration();
/**
* Clears the console
*/
@Override
public void clearConsole() {
page.clearConsolePage();
}
@Override
public void setOnContentsReceivedCallback(ICallback<Object, Tuple<String, String>> onContentsReceived) {
interpreter.setOnContentsReceivedCallback(onContentsReceived);
}
@Override
public boolean isOnStateWhereCommandHandlingShouldStop(String commandLine) {
final Object[] listeners = consoleListeners.getListeners();
for (Object listener : listeners) {
if (((IScriptConsoleListener) listener).isOnStateWhereCommandHandlingShouldStop(commandLine)) {
return true;
}
}
return false;
}
@Override
public void beforeHandleCommand(String userInput, ICallback<Object, InterpreterResponse> onResponseReceived) {
final Object[] listeners = consoleListeners.getListeners();
//notify about the user request in the UI thread.
for (Object listener : listeners) {
((IScriptConsoleListener) listener).userRequest(userInput, prompt);
}
}
/**
* Handles some command that the user entered
*
* @param userInput that's the command to be evaluated by the user.
*/
@Override
public void handleCommand(String userInput, final ICallback<Object, InterpreterResponse> onResponseReceived) {
final Object[] listeners = consoleListeners.getListeners();
//executes the user input in the interpreter
if (interpreter != null) {
interpreter.exec(userInput, new ICallback<Object, InterpreterResponse>() {
@Override
public Object call(final InterpreterResponse response) {
//sets the new mode
prompt.setMode(!response.more);
prompt.setNeedInput(response.need_input);
//notify about the console answer (not in the UI thread).
for (Object listener : listeners) {
((IScriptConsoleListener) listener).interpreterResponse(response, prompt);
}
onResponseReceived.call(response);
return null;
}
});
}
}
/**
* Fetch the current completions for the content presented in the user's ipython console
*/
@Override
public ICompletionProposal[] getTabCompletions(String commandLine, int cursorPosition) {
try {
ICompletionProposal[] completions = interpreter.getCompletions(viewer.get(), commandLine, cursorPosition,
cursorPosition, AbstractCompletionProcessorWithCycling.SHOW_FOR_TAB_COMPLETIONS);
return completions;
} catch (Exception e) {
}
return new ICompletionProposal[0];
}
/**
* Finishes the interpreter (and stops the communication)
*/
public void terminate() {
try {
if (history != null) {
history.close();
}
interpreter.close();
} catch (Exception e) {
}
history = null;
interpreter = null;
}
/**
* Interrupts the interpreter
*/
public void interrupt() {
try {
interpreter.interrupt();
getViewer().discardCommandLine();
} catch (Exception e) {
}
}
public void setViewer(ScriptConsoleViewer scriptConsoleViewer) {
this.viewer = new WeakReference<ScriptConsoleViewer>(scriptConsoleViewer);
}
public ScriptConsoleViewer getViewer() {
if (this.viewer != null) {
return this.viewer.get();
}
return null;
}
/**
* Must be overridden to create a style provider for the console (configures colors)
* @return a style provider.
*/
public abstract IConsoleStyleProvider createStyleProvider();
/**
* @return a list of trackers that'll identify links in the console passed.
*/
public abstract List<IConsoleLineTracker> createLineTrackers(final TextConsole console);
/**
* @return the commands that should be initially set in the prompt.
*/
public abstract String getInitialCommands();
/**
* Used for backward compatibility because the setBackground/getBackground is not available for eclipse 3.2
*/
private Color fPydevConsoleBackground;
/**
* Used for backward compatibility because the setBackground/getBackground is not available for eclipse 3.2
*/
public Color getPydevConsoleBackground() {
try {
Color ret = (Color) Reflection.invoke(this, "getBackground");
return ret;
} catch (Throwable e) {
//not available in eclipse 3.2
return fPydevConsoleBackground;
}
}
/**
* Used for backward compatibility because the setBackground/getBackground is not available for eclipse 3.2
*/
public void setPydevConsoleBackground(Color color) {
try {
Reflection.invoke(this, "setBackground", color);
} catch (Throwable e) {
//not available in eclipse 3.2
fPydevConsoleBackground = color;
}
}
public Object getInterpreterInfo() {
return this.interpreter.getInterpreterInfo();
}
/**
* @return
*/
public abstract boolean getFocusOnStart();
public abstract boolean getTabCompletionEnabled();
/**
* Enable/Disable linking of the debug console with the suspended frame.
*/
public void linkWithDebugSelection(boolean isLinkedWithDebug) {
this.interpreter.linkWithDebugSelection(isLinkedWithDebug);
}
public abstract AbstractHandleBackspaceAction getBackspaceAction();
public abstract void createActions(IToolBarManager toolbarManager);
public abstract IHandleScriptAutoEditStrategy getAutoEditStrategy();
}