blob: 79bc66b0f85afad1767b516062c9c8396a3ea84f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2016 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.eclipse.dltk.console.ui;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.debug.core.IStreamListener;
import org.eclipse.debug.core.model.IFlushableStreamMonitor;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.console.IScriptConsoleInterpreter;
import org.eclipse.dltk.console.IScriptExecResult;
import org.eclipse.dltk.console.IScriptInterpreter;
import org.eclipse.dltk.console.ScriptConsoleHistory;
import org.eclipse.dltk.console.ScriptConsolePrompt;
import org.eclipse.dltk.console.ScriptExecResult;
import org.eclipse.dltk.console.ui.internal.ICommandHandler;
import org.eclipse.dltk.console.ui.internal.ScriptConsolePage;
import org.eclipse.dltk.console.ui.internal.ScriptConsoleSession;
import org.eclipse.dltk.console.ui.internal.ScriptConsoleViewer;
import org.eclipse.dltk.console.ui.internal.ScriptConsoleViewer.ConsoleDocumentListener;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.launching.process.IScriptProcess;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.console.IConsoleDocumentPartitioner;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.part.IPageBookViewPage;
public class ScriptConsole extends TextConsole
implements ICommandHandler, IScriptConsole {
private ILaunch launch = null;
private ILaunchesListener2 listener = null;
private class ScriptConsoleLaunchListener implements ILaunchesListener2 {
@Override
public void launchesTerminated(ILaunch[] launches) {
if (terminated) {
return;
}
for (int i = 0; i < launches.length; i++) {
if (launches[i].equals(launch)) {
final ScriptConsoleViewer consoleViewer = (ScriptConsoleViewer) page
.getViewer();
page.getControl().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (consoleViewer != null) {
consoleViewer.disableProcessing();
// TODO use different color
updateText(consoleViewer,
Messages.ScriptConsole_processTerminated);
consoleViewer.setEditable(false);
}
}
});
}
}
}
@Override
public void launchesAdded(ILaunch[] launches) {
}
@Override
public void launchesChanged(ILaunch[] launches) {
}
@Override
public void launchesRemoved(ILaunch[] launches) {
}
}
private final class InitialStreamReader implements Runnable {
private final IScriptInterpreter interpreter;
private InitialStreamReader(IScriptInterpreter interpreter) {
this.interpreter = interpreter;
}
@Override
public void run() {
// We need to be sure what page is already created
while (page == null || (page != null && page.getViewer() == null)) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
final ScriptConsoleViewer viewer = (ScriptConsoleViewer) page
.getViewer();
InputStream stream = interpreter.getInitialOutputStream();
if (stream == null) {
return;
}
final BufferedReader reader = new BufferedReader(
new InputStreamReader(stream));
Thread readerThread = new Thread() {
@Override
public void run() {
while (!terminated) {
String readLine;
try {
readLine = reader.readLine();
if (readLine != null) {
updateText(viewer, readLine);
} else {
break;
}
} catch (IOException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
break;
}
}
enableEdit(viewer);
}
};
readerThread.start();
}
}
protected void enableEdit(final ScriptConsoleViewer viewer) {
Control control = viewer.getControl();
if (control == null) {
return;
}
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
viewer.setEditable(true);
}
});
}
private void updateText(final ScriptConsoleViewer viewer,
final String text) {
Control control = viewer.getControl();
if (control == null) {
return;
}
control.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
IDocument document = getDocument();
final String delim = TextUtilities
.getDefaultLineDelimiter(document);
getDocumentListener().write(text + delim, false);
}
});
}
private ScriptConsolePage page;
private ScriptConsolePartitioner partitioner;
private IContentAssistProcessor processor;
private ITextHover hover;
private IScriptInterpreter interpreter;
private ScriptConsoleSession session;
private ListenerList consoleListeners;
private ScriptConsolePrompt prompt;
private ScriptConsoleHistory history;
private boolean terminated = false;
@Override
protected IConsoleDocumentPartitioner getPartitioner() {
return partitioner;
}
public ScriptConsolePage getPage() {
return page;
}
public ScriptConsole(String consoleName, String consoleType,
ImageDescriptor image) {
super(consoleName, consoleType, image, true);
this.consoleListeners = new ListenerList(ListenerList.IDENTITY);
this.prompt = new ScriptConsolePrompt("=>", "->"); //$NON-NLS-1$ //$NON-NLS-2$
this.history = new ScriptConsoleHistory();
this.session = new ScriptConsoleSession();
addListener(this.session);
partitioner = new ScriptConsolePartitioner();
getDocument().setDocumentPartitioner(partitioner);
partitioner.connect(getDocument());
}
public ScriptConsole(String consoleName, String consoleType) {
this(consoleName, consoleType, null);
}
public IScriptConsoleSession getSession() {
return session;
}
public void addListener(IScriptConsoleListener listener) {
consoleListeners.add(listener);
}
public void removeListener(IScriptConsoleListener listener) {
consoleListeners.remove(listener);
}
protected void setContentAssistProcessor(
IContentAssistProcessor processor) {
this.processor = processor;
}
protected void setInterpreter(final IScriptInterpreter interpreter) {
this.interpreter = interpreter;
interpreter.addInitialListenerOperation(
new InitialStreamReader(interpreter));
}
public void setPrompt(ScriptConsolePrompt prompt) {
this.prompt = prompt;
}
public ScriptConsolePrompt getPrompt() {
return prompt;
}
public ScriptConsoleHistory getHistory() {
return history;
}
protected void setTextHover(ITextHover hover) {
this.hover = hover;
}
private ConsoleDocumentListener documentListener;
public ConsoleDocumentListener getDocumentListener() {
if (documentListener == null) {
documentListener = new ConsoleDocumentListener(this,
this.getPrompt(), this.getHistory());
documentListener.setDocument(getDocument());
}
return documentListener;
}
@Override
public IPageBookViewPage createPage(IConsoleView view) {
SourceViewerConfiguration cfg = new ScriptConsoleSourceViewerConfiguration(
processor, hover);
page = createPage(view, cfg);
return page;
}
protected ScriptConsolePage createPage(IConsoleView view,
SourceViewerConfiguration cfg) {
return new ScriptConsolePage(this, view, cfg);
}
@Override
public void clearConsole() {
page.clearConsolePage();
}
/**
* @since 2.0
*/
@Override
public void insertText(String line) {
page.insertText(line);
}
public int getState() {
return interpreter.getState();
}
@Override
public IScriptExecResult handleCommand(String userInput)
throws IOException {
if (this.interpreter == null || !this.interpreter.isValid()) {
return new ScriptExecResult(Util.EMPTY_STRING);
}
Object[] listeners = consoleListeners.getListeners();
for (int i = 0; i < listeners.length; i++) {
((IScriptConsoleListener) listeners[i]).userRequest(userInput);
}
IScriptExecResult output = interpreter.exec(userInput);
if (interpreter
.getState() == IScriptConsoleInterpreter.WAIT_NEW_COMMAND) {
prompt.setMode(true);
} else {
prompt.setMode(false);
}
for (int i = 0; i < listeners.length; i++) {
((IScriptConsoleListener) listeners[i]).interpreterResponse(output);
}
return output;
}
/**
* Executes the specified code and displays the results
*
* @param command
*/
public void executeCommand(String command) {
getDocumentListener().executeCommand(command);
}
@Override
public void terminate() {
terminated = true;
try {
interpreter.close();
} catch (IOException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
@Override
public void dispose() {
partitioner.clearRanges();
terminate();
if (listener != null) {
DebugPlugin.getDefault().getLaunchManager()
.removeLaunchListener(listener);
listener = null;
}
closeStreams();
disposeStreams();
super.dispose();
}
public void setLaunch(ILaunch launch) {
this.launch = launch;
if (this.launch != null && this.listener == null) {
this.listener = new ScriptConsoleLaunchListener();
DebugPlugin.getDefault().getLaunchManager()
.addLaunchListener(listener);
}
}
/**
* @return the launch
*/
public ILaunch getLaunch() {
return launch;
}
private Set<IScriptProcess> connectedProcesses;
/**
* @param process
*/
public synchronized void connect(IScriptProcess process) {
if (connectedProcesses == null) {
connectedProcesses = new HashSet<IScriptProcess>();
}
if (connectedProcesses.add(process)) {
final IStreamsProxy proxy = process.getScriptStreamsProxy();
if (proxy == null) {
return;
}
connect(proxy);
}
}
@Override
public void connect(final IStreamsProxy proxy) {
IStreamMonitor streamMonitor = proxy.getErrorStreamMonitor();
if (streamMonitor != null) {
connect(streamMonitor, IDebugUIConstants.ID_STANDARD_ERROR_STREAM);
}
streamMonitor = proxy.getOutputStreamMonitor();
if (streamMonitor != null) {
connect(streamMonitor, IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM);
}
}
private List<StreamListener> fStreamListeners = new ArrayList<StreamListener>();
/**
* @param streamMonitor
* @param idStandardErrorStream
*/
private void connect(IStreamMonitor streamMonitor,
String streamIdentifier) {
synchronized (streamMonitor) {
StreamListener listener = new StreamListener(streamIdentifier,
streamMonitor);
fStreamListeners.add(listener);
}
}
/**
* cleanup method to close all of the open stream to this console
*/
private synchronized void closeStreams() {
for (StreamListener listener : fStreamListeners) {
listener.closeStream();
}
}
/**
* disposes of the listeners for each of the stream associated with this
* console
*/
private synchronized void disposeStreams() {
for (StreamListener listener : fStreamListeners) {
listener.dispose();
}
}
/**
* This class listens to a specified IO stream
*/
private class StreamListener implements IStreamListener {
private IStreamMonitor fStreamMonitor;
private String fStreamId;
private boolean fFlushed = false;
private boolean fListenerRemoved = false;
public StreamListener(String streamIdentifier, IStreamMonitor monitor) {
this.fStreamId = streamIdentifier;
this.fStreamMonitor = monitor;
fStreamMonitor.addListener(this);
// fix to bug 121454. Ensure that output to fast processes is
// processed.
streamAppended(null, monitor);
}
@Override
public void streamAppended(String text, IStreamMonitor monitor) {
if (fFlushed) {
getDocumentListener().write(text,
IDebugUIConstants.ID_STANDARD_ERROR_STREAM
.equals(fStreamId));
} else {
String contents = null;
synchronized (fStreamMonitor) {
fFlushed = true;
contents = fStreamMonitor.getContents();
if (fStreamMonitor instanceof IFlushableStreamMonitor) {
IFlushableStreamMonitor m = (IFlushableStreamMonitor) fStreamMonitor;
m.flushContents();
m.setBuffered(false);
}
}
if (contents != null && contents.length() > 0) {
getDocumentListener().write(contents,
IDebugUIConstants.ID_STANDARD_ERROR_STREAM
.equals(fStreamId));
}
}
}
public void closeStream() {
if (fStreamMonitor == null) {
return;
}
synchronized (fStreamMonitor) {
fStreamMonitor.removeListener(this);
if (!fFlushed) {
String contents = fStreamMonitor.getContents();
streamAppended(contents, fStreamMonitor);
}
fListenerRemoved = true;
}
}
public void dispose() {
if (!fListenerRemoved) {
closeStream();
}
fStreamMonitor = null;
fStreamId = null;
}
}
}