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 {
36 public static final String JOB_NAME = "org.simantics.scl.console.job";
37 public static final long TERMINATE_GRACE_PERIOD = 1000L;
39 private THashSet<Job> currentJobs = new THashSet<Job>();
40 private final IdentitySchedulingRule schedulingRule = new IdentitySchedulingRule();
41 private ArrayList<SCLConsoleListener> listeners = new ArrayList<SCLConsoleListener>(2);
42 private boolean consoleIsEmpty = true;
44 SCLReportingHandler handler = new AbstractSCLReportingHandler() {
46 public void print(String text) {
47 appendOutput(text + "\n", null, null);
50 public void printError(String error) {
51 appendOutput(error + "\n", redColor, null);
54 public void printCommand(String command) {
55 appendOutput("> " + command.replace("\n", "\n ") + "\n", greenColor, null);
59 CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);
60 ContentProposalAdapter contentProposalAdapter;
62 public SCLConsole(Composite parent, int style) {
65 StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
66 SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
69 contentProposalAdapter = new ContentProposalAdapter(
71 styledTextContentAdapter,
73 KeyStroke.getInstance("Ctrl+Space"),
75 contentProposalAdapter.setAutoActivationDelay(200);
76 } catch (ParseException e) {
77 // No content assist then.
80 addContributedListeners();
84 protected boolean canExecuteCommand() {
85 return !contentProposalAdapter.isProposalPopupOpen();
89 public ErrorAnnotation[] validate(String command) {
91 return ErrorAnnotation.EMPTY_ARRAY;
93 CompilationError[] errors = session.validate(command);
94 if(errors.length == 0)
95 return ErrorAnnotation.EMPTY_ARRAY;
97 ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
98 for(int i=0;i<errors.length;++i) {
99 CompilationError error = errors[i];
100 int begin = Locations.beginOf(error.location);
101 if(begin == Integer.MAX_VALUE)
103 int end = Locations.endOf(error.location);
104 if(end == Integer.MIN_VALUE)
105 end = command.length();
113 annotations[i] = new ErrorAnnotation(begin, end, error.description);
119 private String jobNameFromCommand(String command) {
120 return command.split("\n")[0];
124 public void execute(final String command) {
125 Job job = new Job(jobNameFromCommand(command)) {
127 protected IStatus run(IProgressMonitor monitor) {
129 session.execute(command, handler);
131 synchronized(currentJobs) {
132 currentJobs.remove(this);
133 if(currentJobs.isEmpty())
134 for(SCLConsoleListener listener : listeners)
135 listener.finishedExecution();
138 return Status.OK_STATUS;
142 protected void canceling() {
143 Thread thread = getThread();
148 Thread.sleep(TERMINATE_GRACE_PERIOD);
149 } catch (InterruptedException e) {
154 thread = getThread();
159 job.setRule(schedulingRule);
160 synchronized(currentJobs) {
161 boolean firstJob = currentJobs.isEmpty();
162 currentJobs.add(job);
164 synchronized(listeners) {
165 for(SCLConsoleListener listener : listeners)
166 listener.startedExecution();
173 public CommandSession getSession() {
177 public SCLReportingHandler getHandler() {
181 public void interruptCurrentCommands() {
182 synchronized(currentJobs) {
183 for(Job job : currentJobs)
189 public void addListener(SCLConsoleListener listener) {
190 synchronized (listeners) {
191 listeners.add(listener);
195 public void removeListener(SCLConsoleListener listener) {
196 synchronized (listeners) {
197 listeners.remove(listener);
202 public void appendOutput(String text, Color foreground, Color background) {
203 super.appendOutput(text, foreground, background);
205 consoleIsEmpty = false;
206 synchronized (listeners) {
207 for(SCLConsoleListener listener : listeners)
208 listener.consoleIsNotEmptyAnymore();
214 public void clear() {
216 consoleIsEmpty = true;
219 private void addContributedListeners() {
220 final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
221 new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
222 SCLConsoleListener.class, null) {
224 public SCLConsoleListener addingService(
225 ServiceReference<SCLConsoleListener> reference) {
226 SCLConsoleListener listener = context.getService(reference);
227 addListener(listener);
232 public void modifiedService(
233 ServiceReference<SCLConsoleListener> reference,
234 SCLConsoleListener service) {
238 public void removedService(
239 ServiceReference<SCLConsoleListener> reference,
240 SCLConsoleListener service) {
241 removeListener(service);