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;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 import gnu.trove.set.hash.THashSet;
33 * An SCL console with input and output area that can be embedded
34 * into any editor or view.
35 * @author Hannu Niemistö
37 public class SCLConsole extends AbstractCommandConsole {
40 * Use this option mask to exclude {@link SCLConsoleListener}s contributed as
41 * OSGi services from listening to this console.
43 public static final int EXCLUDE_CONTRIBUTED_LISTENERS = 1 << 10;
45 public static final String JOB_NAME = "org.simantics.scl.console.job";
46 public static final long TERMINATE_GRACE_PERIOD = 1000L;
48 private THashSet<Job> currentJobs = new THashSet<Job>();
49 private final IdentitySchedulingRule schedulingRule = new IdentitySchedulingRule();
50 private ArrayList<SCLConsoleListener> listeners = new ArrayList<SCLConsoleListener>(2);
51 private boolean consoleIsEmpty = true;
53 SCLReportingHandler handler = new AbstractSCLReportingHandler() {
55 public void print(String text) {
56 appendOutput(text + "\n", null, null);
59 public void printError(String error) {
60 appendOutput(error + "\n", redColor, null);
63 public void printCommand(String command) {
64 appendOutput("> " + command.replace("\n", "\n ") + "\n", greenColor, null);
68 CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);
69 ContentProposalAdapter contentProposalAdapter;
71 public SCLConsole(Composite parent, int style) {
72 this(parent, style, 0);
75 public SCLConsole(Composite parent, int style, int options) {
76 super(parent, style, options);
77 createContentProposalAdapter();
78 if (!hasOption(EXCLUDE_CONTRIBUTED_LISTENERS))
79 addContributedListeners();
82 protected void createContentProposalAdapter() {
86 StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
87 SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
89 contentProposalAdapter = new ContentProposalAdapter(
91 styledTextContentAdapter,
93 KeyStroke.getInstance("Ctrl+Space"),
95 contentProposalAdapter.setAutoActivationDelay(200);
96 } catch (ParseException e) {
97 // No content assist then.
102 protected boolean canExecuteCommand() {
103 return contentProposalAdapter == null || !contentProposalAdapter.isProposalPopupOpen();
107 public ErrorAnnotation[] validate(String command) {
108 if(command.isEmpty())
109 return ErrorAnnotation.EMPTY_ARRAY;
111 CompilationError[] errors = session.validate(command);
112 if(errors.length == 0)
113 return ErrorAnnotation.EMPTY_ARRAY;
115 ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
116 for(int i=0;i<errors.length;++i) {
117 CompilationError error = errors[i];
118 int begin = Locations.beginOf(error.location);
119 if(begin == Integer.MAX_VALUE)
121 int end = Locations.endOf(error.location);
122 if(end == Integer.MIN_VALUE)
123 end = command.length();
131 annotations[i] = new ErrorAnnotation(begin, end, error.description);
137 private String jobNameFromCommand(String command) {
138 return command.split("\n")[0];
142 public void execute(final String command) {
143 Job job = new Job(jobNameFromCommand(command)) {
145 protected IStatus run(IProgressMonitor monitor) {
147 session.execute(command, handler);
149 synchronized(currentJobs) {
150 currentJobs.remove(this);
151 if(currentJobs.isEmpty())
152 for(SCLConsoleListener listener : listeners)
153 listener.finishedExecution();
156 return Status.OK_STATUS;
160 protected void canceling() {
161 Thread thread = getThread();
166 Thread.sleep(TERMINATE_GRACE_PERIOD);
167 } catch (InterruptedException e) {
172 thread = getThread();
177 job.setRule(schedulingRule);
178 synchronized(currentJobs) {
179 boolean firstJob = currentJobs.isEmpty();
180 currentJobs.add(job);
182 synchronized(listeners) {
183 for(SCLConsoleListener listener : listeners)
184 listener.startedExecution();
191 public CommandSession getSession() {
195 public SCLReportingHandler getHandler() {
199 public void interruptCurrentCommands() {
200 synchronized(currentJobs) {
201 for(Job job : currentJobs)
207 public void addListener(SCLConsoleListener listener) {
208 synchronized (listeners) {
209 listeners.add(listener);
213 public void removeListener(SCLConsoleListener listener) {
214 synchronized (listeners) {
215 listeners.remove(listener);
220 public void appendOutput(String text, Color foreground, Color background) {
221 super.appendOutput(text, foreground, background);
223 consoleIsEmpty = false;
224 synchronized (listeners) {
225 for(SCLConsoleListener listener : listeners)
226 listener.consoleIsNotEmptyAnymore();
232 public void clear() {
234 consoleIsEmpty = true;
237 protected void addContributedListeners() {
238 final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
239 new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
240 SCLConsoleListener.class, null) {
242 public SCLConsoleListener addingService(
243 ServiceReference<SCLConsoleListener> reference) {
244 SCLConsoleListener listener = context.getService(reference);
245 addListener(listener);
250 public void modifiedService(
251 ServiceReference<SCLConsoleListener> reference,
252 SCLConsoleListener service) {
256 public void removedService(
257 ServiceReference<SCLConsoleListener> reference,
258 SCLConsoleListener service) {
259 removeListener(service);
265 public Logger getLogger() {
266 return LoggerFactory.getLogger(getClass());