--- /dev/null
+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