1 package org.simantics.scl.ui.console;
3 import java.util.ArrayList;
5 import org.eclipse.core.runtime.IProgressMonitor;
6 import org.eclipse.core.runtime.IStatus;
7 import org.eclipse.core.runtime.Status;
8 import org.eclipse.core.runtime.jobs.Job;
9 import org.eclipse.jface.bindings.keys.KeyStroke;
10 import org.eclipse.jface.bindings.keys.ParseException;
11 import org.eclipse.jface.fieldassist.ContentProposalAdapter;
12 import org.eclipse.swt.graphics.Color;
13 import org.eclipse.swt.widgets.Composite;
14 import org.osgi.framework.BundleContext;
15 import org.osgi.framework.ServiceReference;
16 import org.osgi.util.tracker.ServiceTracker;
17 import org.simantics.scl.compiler.commands.CommandSession;
18 import org.simantics.scl.compiler.commands.SCLConsoleListener;
19 import org.simantics.scl.compiler.errors.CompilationError;
20 import org.simantics.scl.compiler.errors.Locations;
21 import org.simantics.scl.osgi.SCLOsgi;
22 import org.simantics.scl.runtime.reporting.AbstractSCLReportingHandler;
23 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
24 import org.simantics.scl.ui.Activator;
25 import org.simantics.scl.ui.assist.SCLContentProposalProvider;
26 import org.simantics.scl.ui.assist.StyledTextContentAdapter;
28 import gnu.trove.set.hash.THashSet;
31 * An SCL console with input and output area that can be embedded
32 * into any editor or view.
33 * @author Hannu Niemistö
35 public class SCLConsole extends AbstractCommandConsole {
38 * Use this option mask to exclude {@link SCLConsoleListener}s contributed as
39 * OSGi services from listening to this console.
41 public static final int EXCLUDE_CONTRIBUTED_LISTENERS = 1 << 10;
43 public static final String JOB_NAME = "org.simantics.scl.console.job";
44 public static final long TERMINATE_GRACE_PERIOD = 1000L;
46 private THashSet<Job> currentJobs = new THashSet<Job>();
47 private final IdentitySchedulingRule schedulingRule = new IdentitySchedulingRule();
48 private ArrayList<SCLConsoleListener> listeners = new ArrayList<SCLConsoleListener>(2);
49 private boolean consoleIsEmpty = true;
51 SCLReportingHandler handler = new AbstractSCLReportingHandler() {
53 public void print(String text) {
54 appendOutput(text + "\n", null, null);
57 public void printError(String error) {
58 appendOutput(error + "\n", redColor, null);
61 public void printCommand(String command) {
62 appendOutput("> " + command.replace("\n", "\n ") + "\n", greenColor, null);
66 CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);
67 ContentProposalAdapter contentProposalAdapter;
69 public SCLConsole(Composite parent, int style) {
70 this(parent, style, 0);
73 public SCLConsole(Composite parent, int style, int options) {
74 super(parent, style, options);
75 createContentProposalAdapter();
76 if (!hasOption(EXCLUDE_CONTRIBUTED_LISTENERS))
77 addContributedListeners();
80 protected void createContentProposalAdapter() {
84 StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
85 SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
87 contentProposalAdapter = new ContentProposalAdapter(
89 styledTextContentAdapter,
91 KeyStroke.getInstance("Ctrl+Space"),
93 contentProposalAdapter.setAutoActivationDelay(200);
94 } catch (ParseException e) {
95 // No content assist then.
100 protected boolean canExecuteCommand() {
101 return contentProposalAdapter == null || !contentProposalAdapter.isProposalPopupOpen();
105 public ErrorAnnotation[] validate(String command) {
106 if(command.isEmpty())
107 return ErrorAnnotation.EMPTY_ARRAY;
109 CompilationError[] errors = session.validate(command);
110 if(errors.length == 0)
111 return ErrorAnnotation.EMPTY_ARRAY;
113 ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
114 for(int i=0;i<errors.length;++i) {
115 CompilationError error = errors[i];
116 int begin = Locations.beginOf(error.location);
117 if(begin == Integer.MAX_VALUE)
119 int end = Locations.endOf(error.location);
120 if(end == Integer.MIN_VALUE)
121 end = command.length();
129 annotations[i] = new ErrorAnnotation(begin, end, error.description);
135 private String jobNameFromCommand(String command) {
136 return command.split("\n")[0];
140 public void execute(final String command) {
141 Job job = new Job(jobNameFromCommand(command)) {
143 protected IStatus run(IProgressMonitor monitor) {
145 session.execute(command, handler);
147 synchronized(currentJobs) {
148 currentJobs.remove(this);
149 if(currentJobs.isEmpty())
150 for(SCLConsoleListener listener : listeners)
151 listener.finishedExecution();
154 return Status.OK_STATUS;
158 protected void canceling() {
159 Thread thread = getThread();
164 Thread.sleep(TERMINATE_GRACE_PERIOD);
165 } catch (InterruptedException e) {
170 thread = getThread();
175 job.setRule(schedulingRule);
176 synchronized(currentJobs) {
177 boolean firstJob = currentJobs.isEmpty();
178 currentJobs.add(job);
180 synchronized(listeners) {
181 for(SCLConsoleListener listener : listeners)
182 listener.startedExecution();
189 public CommandSession getSession() {
193 public SCLReportingHandler getHandler() {
197 public void interruptCurrentCommands() {
198 synchronized(currentJobs) {
199 for(Job job : currentJobs)
205 public void addListener(SCLConsoleListener listener) {
206 synchronized (listeners) {
207 listeners.add(listener);
211 public void removeListener(SCLConsoleListener listener) {
212 synchronized (listeners) {
213 listeners.remove(listener);
218 public void appendOutput(String text, Color foreground, Color background) {
219 super.appendOutput(text, foreground, background);
221 consoleIsEmpty = false;
222 synchronized (listeners) {
223 for(SCLConsoleListener listener : listeners)
224 listener.consoleIsNotEmptyAnymore();
230 public void clear() {
232 consoleIsEmpty = true;
235 protected void addContributedListeners() {
236 final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
237 new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
238 SCLConsoleListener.class, null) {
240 public SCLConsoleListener addingService(
241 ServiceReference<SCLConsoleListener> reference) {
242 SCLConsoleListener listener = context.getService(reference);
243 addListener(listener);
248 public void modifiedService(
249 ServiceReference<SCLConsoleListener> reference,
250 SCLConsoleListener service) {
254 public void removedService(
255 ServiceReference<SCLConsoleListener> reference,
256 SCLConsoleListener service) {
257 removeListener(service);