-package org.simantics.scl.ui.console;\r
-\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.util.ArrayList;\r
-\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.core.runtime.jobs.Job;\r
-import org.eclipse.jface.bindings.keys.KeyStroke;\r
-import org.eclipse.jface.bindings.keys.ParseException;\r
-import org.eclipse.jface.fieldassist.ContentProposalAdapter;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.osgi.framework.BundleContext;\r
-import org.osgi.framework.ServiceReference;\r
-import org.osgi.util.tracker.ServiceTracker;\r
-import org.simantics.scl.compiler.commands.CommandSession;\r
-import org.simantics.scl.compiler.commands.SCLConsoleListener;\r
-import org.simantics.scl.compiler.errors.CompilationError;\r
-import org.simantics.scl.compiler.errors.Locations;\r
-import org.simantics.scl.osgi.SCLOsgi;\r
-import org.simantics.scl.runtime.reporting.AbstractSCLReportingHandler;\r
-import org.simantics.scl.runtime.reporting.SCLReportingHandler;\r
-import org.simantics.scl.ui.Activator;\r
-import org.simantics.scl.ui.assist.SCLContentProposalProvider;\r
-import org.simantics.scl.ui.assist.StyledTextContentAdapter;\r
-\r
-/**\r
- * An SCL console with input and output area that can be embedded\r
- * into any editor or view.\r
- * @author Hannu Niemistö\r
- */\r
-public class SCLConsole extends AbstractCommandConsole {\r
- public static final String JOB_NAME = "org.simantics.scl.console.job";\r
- \r
- private THashSet<Job> currentJobs = new THashSet<Job>();\r
- private Thread currentThread;\r
- private final IdentitySchedulingRule schedulingRule = new IdentitySchedulingRule();\r
- private ArrayList<SCLConsoleListener> listeners = new ArrayList<SCLConsoleListener>(2);\r
- private boolean consoleIsEmpty = true;\r
-\r
- SCLReportingHandler handler = new AbstractSCLReportingHandler() {\r
- @Override\r
- public void print(String text) {\r
- appendOutput(text + "\n", null, null);\r
- }\r
- @Override\r
- public void printError(String error) {\r
- appendOutput(error + "\n", redColor, null);\r
- }\r
- @Override\r
- public void printCommand(String command) {\r
- appendOutput("> " + command.replace("\n", "\n ") + "\n", greenColor, null);\r
- }\r
- };\r
-\r
- CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);\r
- ContentProposalAdapter contentProposalAdapter;\r
- \r
- public SCLConsole(Composite parent, int style) {\r
- super(parent, style);\r
- \r
- StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();\r
- SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);\r
- \r
- try {\r
- contentProposalAdapter = new ContentProposalAdapter(\r
- input, \r
- styledTextContentAdapter, \r
- contentProvider, \r
- KeyStroke.getInstance("Ctrl+Space"), \r
- null);\r
- contentProposalAdapter.setAutoActivationDelay(200);\r
- } catch (ParseException e) {\r
- // No content assist then.\r
- }\r
- \r
- addContributedListeners();\r
- }\r
-\r
- @Override\r
- protected boolean canExecuteCommand() {\r
- return !contentProposalAdapter.isProposalPopupOpen();\r
- }\r
- \r
- @Override\r
- public ErrorAnnotation[] validate(String command) {\r
- if(command.isEmpty())\r
- return ErrorAnnotation.EMPTY_ARRAY;\r
- \r
- CompilationError[] errors = session.validate(command);\r
- if(errors.length == 0)\r
- return ErrorAnnotation.EMPTY_ARRAY;\r
- \r
- ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];\r
- for(int i=0;i<errors.length;++i) {\r
- CompilationError error = errors[i];\r
- int begin = Locations.beginOf(error.location);\r
- if(begin == Integer.MAX_VALUE)\r
- begin = 0;\r
- int end = Locations.endOf(error.location);\r
- if(end == Integer.MIN_VALUE)\r
- end = command.length();\r
- if(begin == end) {\r
- if(begin > 0)\r
- --begin;\r
- else\r
- ++end;\r
- }\r
- \r
- annotations[i] = new ErrorAnnotation(begin, end, error.description);\r
- }\r
- \r
- return annotations;\r
- }\r
- \r
- private String jobNameFromCommand(String command) {\r
- return command.split("\n")[0];\r
- }\r
-\r
- @Override\r
- public void execute(final String command) {\r
- Job job = new Job(jobNameFromCommand(command)) {\r
- @Override\r
- protected IStatus run(IProgressMonitor monitor) {\r
- try {\r
- synchronized(currentJobs) {\r
- currentJobs.remove(this);\r
- currentThread = Thread.currentThread();\r
- }\r
- session.execute(command, handler);\r
- } finally {\r
- synchronized(currentJobs) {\r
- currentThread = null;\r
- if(currentJobs.isEmpty())\r
- for(SCLConsoleListener listener : listeners)\r
- listener.finishedExecution();\r
- }\r
- }\r
- return Status.OK_STATUS;\r
- }\r
- };\r
- job.setRule(schedulingRule);\r
- synchronized(currentJobs) {\r
- boolean firstJob = currentJobs.isEmpty();\r
- currentJobs.add(job);\r
- if(firstJob) {\r
- synchronized(listeners) {\r
- for(SCLConsoleListener listener : listeners)\r
- listener.startedExecution();\r
- }\r
- }\r
- }\r
- job.schedule();\r
- }\r
-\r
- public CommandSession getSession() {\r
- return session;\r
- }\r
- public void interruptCurrentCommands() {\r
- synchronized(currentJobs) {\r
- for(Job job : currentJobs)\r
- job.cancel();\r
- currentJobs.clear();\r
- if(currentThread != null)\r
- currentThread.interrupt();\r
- }\r
- }\r
- \r
- public void addListener(SCLConsoleListener listener) {\r
- synchronized (listeners) {\r
- listeners.add(listener);\r
- }\r
- }\r
- \r
- public void removeListener(SCLConsoleListener listener) {\r
- synchronized (listeners) {\r
- listeners.remove(listener);\r
- }\r
- }\r
- \r
- @Override\r
- public void appendOutput(String text, Color foreground, Color background) {\r
- super.appendOutput(text, foreground, background);\r
- if(consoleIsEmpty) {\r
- consoleIsEmpty = false;\r
- synchronized (listeners) {\r
- for(SCLConsoleListener listener : listeners)\r
- listener.consoleIsNotEmptyAnymore();\r
- }\r
- }\r
- }\r
- \r
- @Override\r
- public void clear() {\r
- super.clear();\r
- consoleIsEmpty = true;\r
- }\r
-\r
- private void addContributedListeners() {\r
- final BundleContext context = Activator.getInstance().getBundle().getBundleContext();\r
- new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,\r
- SCLConsoleListener.class, null) {\r
- @Override\r
- public SCLConsoleListener addingService(\r
- ServiceReference<SCLConsoleListener> reference) {\r
- SCLConsoleListener listener = context.getService(reference);\r
- addListener(listener);\r
- return listener;\r
- }\r
-\r
- @Override\r
- public void modifiedService(\r
- ServiceReference<SCLConsoleListener> reference,\r
- SCLConsoleListener service) {\r
- }\r
-\r
- @Override\r
- public void removedService(\r
- ServiceReference<SCLConsoleListener> reference,\r
- SCLConsoleListener service) {\r
- removeListener(service);\r
- }\r
- }.open();\r
- }\r
-}\r
+package org.simantics.scl.ui.console;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.ParseException;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.simantics.scl.compiler.commands.CommandSession;
+import org.simantics.scl.compiler.commands.SCLConsoleListener;
+import org.simantics.scl.compiler.errors.CompilationError;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.scl.runtime.reporting.AbstractSCLReportingHandler;
+import org.simantics.scl.runtime.reporting.SCLReportingHandler;
+import org.simantics.scl.ui.Activator;
+import org.simantics.scl.ui.assist.SCLContentProposalProvider;
+import org.simantics.scl.ui.assist.StyledTextContentAdapter;
+
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * An SCL console with input and output area that can be embedded
+ * into any editor or view.
+ * @author Hannu Niemistö
+ */
+public class SCLConsole extends AbstractCommandConsole {
+ public static final String JOB_NAME = "org.simantics.scl.console.job";
+ public static final long TERMINATE_GRACE_PERIOD = 1000L;
+
+ private THashSet<Job> currentJobs = new THashSet<Job>();
+ private final IdentitySchedulingRule schedulingRule = new IdentitySchedulingRule();
+ private ArrayList<SCLConsoleListener> listeners = new ArrayList<SCLConsoleListener>(2);
+ private boolean consoleIsEmpty = true;
+
+ SCLReportingHandler handler = new AbstractSCLReportingHandler() {
+ @Override
+ public void print(String text) {
+ appendOutput(text + "\n", null, null);
+ }
+ @Override
+ public void printError(String error) {
+ appendOutput(error + "\n", redColor, null);
+ }
+ @Override
+ public void printCommand(String command) {
+ appendOutput("> " + command.replace("\n", "\n ") + "\n", greenColor, null);
+ }
+ };
+
+ CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);
+ ContentProposalAdapter contentProposalAdapter;
+
+ public SCLConsole(Composite parent, int style) {
+ super(parent, style);
+
+ StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
+ SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
+
+ try {
+ contentProposalAdapter = new ContentProposalAdapter(
+ input,
+ styledTextContentAdapter,
+ contentProvider,
+ KeyStroke.getInstance("Ctrl+Space"),
+ null);
+ contentProposalAdapter.setAutoActivationDelay(200);
+ } catch (ParseException e) {
+ // No content assist then.
+ }
+
+ addContributedListeners();
+ }
+
+ @Override
+ protected boolean canExecuteCommand() {
+ return !contentProposalAdapter.isProposalPopupOpen();
+ }
+
+ @Override
+ public ErrorAnnotation[] validate(String command) {
+ if(command.isEmpty())
+ return ErrorAnnotation.EMPTY_ARRAY;
+
+ CompilationError[] errors = session.validate(command);
+ if(errors.length == 0)
+ return ErrorAnnotation.EMPTY_ARRAY;
+
+ ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
+ for(int i=0;i<errors.length;++i) {
+ CompilationError error = errors[i];
+ int begin = Locations.beginOf(error.location);
+ if(begin == Integer.MAX_VALUE)
+ begin = 0;
+ int end = Locations.endOf(error.location);
+ if(end == Integer.MIN_VALUE)
+ end = command.length();
+ if(begin == end) {
+ if(begin > 0)
+ --begin;
+ else
+ ++end;
+ }
+
+ annotations[i] = new ErrorAnnotation(begin, end, error.description);
+ }
+
+ return annotations;
+ }
+
+ private String jobNameFromCommand(String command) {
+ return command.split("\n")[0];
+ }
+
+ @Override
+ public void execute(final String command) {
+ Job job = new Job(jobNameFromCommand(command)) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ session.execute(command, handler);
+ } finally {
+ synchronized(currentJobs) {
+ currentJobs.remove(this);
+ if(currentJobs.isEmpty())
+ for(SCLConsoleListener listener : listeners)
+ listener.finishedExecution();
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ protected void canceling() {
+ Thread thread = getThread();
+ if(thread != null)
+ thread.interrupt();
+
+ try {
+ Thread.sleep(TERMINATE_GRACE_PERIOD);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ thread = getThread();
+ if(thread != null)
+ thread.stop();
+ }
+ };
+ job.setRule(schedulingRule);
+ synchronized(currentJobs) {
+ boolean firstJob = currentJobs.isEmpty();
+ currentJobs.add(job);
+ if(firstJob) {
+ synchronized(listeners) {
+ for(SCLConsoleListener listener : listeners)
+ listener.startedExecution();
+ }
+ }
+ }
+ job.schedule();
+ }
+
+ public CommandSession getSession() {
+ return session;
+ }
+ public void interruptCurrentCommands() {
+ synchronized(currentJobs) {
+ for(Job job : currentJobs)
+ job.cancel();
+ currentJobs.clear();
+ }
+ }
+
+ public void addListener(SCLConsoleListener listener) {
+ synchronized (listeners) {
+ listeners.add(listener);
+ }
+ }
+
+ public void removeListener(SCLConsoleListener listener) {
+ synchronized (listeners) {
+ listeners.remove(listener);
+ }
+ }
+
+ @Override
+ public void appendOutput(String text, Color foreground, Color background) {
+ super.appendOutput(text, foreground, background);
+ if(consoleIsEmpty) {
+ consoleIsEmpty = false;
+ synchronized (listeners) {
+ for(SCLConsoleListener listener : listeners)
+ listener.consoleIsNotEmptyAnymore();
+ }
+ }
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ consoleIsEmpty = true;
+ }
+
+ private void addContributedListeners() {
+ final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
+ new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
+ SCLConsoleListener.class, null) {
+ @Override
+ public SCLConsoleListener addingService(
+ ServiceReference<SCLConsoleListener> reference) {
+ SCLConsoleListener listener = context.getService(reference);
+ addListener(listener);
+ return listener;
+ }
+
+ @Override
+ public void modifiedService(
+ ServiceReference<SCLConsoleListener> reference,
+ SCLConsoleListener service) {
+ }
+
+ @Override
+ public void removedService(
+ ServiceReference<SCLConsoleListener> reference,
+ SCLConsoleListener service) {
+ removeListener(service);
+ }
+ }.open();
+ }
+}