]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java
Added SCL Script Output console view.
[simantics/platform.git] / bundles / org.simantics.scl.ui / src / org / simantics / scl / ui / console / SCLConsole.java
1 package org.simantics.scl.ui.console;
2
3 import java.util.ArrayList;
4
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
28 import gnu.trove.set.hash.THashSet;
29
30 /**
31  * An SCL console with input and output area that can be embedded
32  * into any editor or view.
33  * @author Hannu Niemistö
34  */
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;
38         
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;
43
44         SCLReportingHandler handler = new AbstractSCLReportingHandler() {
45         @Override
46         public void print(String text) {
47             appendOutput(text + "\n", null, null);
48         }
49         @Override
50         public void printError(String error) {
51             appendOutput(error + "\n", redColor, null);
52         }
53         @Override
54         public void printCommand(String command) {
55             appendOutput("> " + command.replace("\n", "\n  ") + "\n", greenColor, null);
56         }
57     };
58
59     CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler);
60     ContentProposalAdapter contentProposalAdapter;
61
62     public SCLConsole(Composite parent, int style) {
63         this(parent, style, 0);
64     }
65
66     public SCLConsole(Composite parent, int style, int options) {
67         super(parent, style, options);
68         createContentProposalAdapter();
69         addContributedListeners();
70     }
71
72     protected void createContentProposalAdapter() {
73         if (input == null)
74             return;
75
76         StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
77         SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
78         try {
79             contentProposalAdapter = new ContentProposalAdapter(
80                     input, 
81                     styledTextContentAdapter, 
82                     contentProvider, 
83                     KeyStroke.getInstance("Ctrl+Space"), 
84                     null);
85             contentProposalAdapter.setAutoActivationDelay(200);
86         } catch (ParseException e) {
87             // No content assist then.
88         }
89     }
90
91     @Override
92     protected boolean canExecuteCommand() {
93         return contentProposalAdapter == null || !contentProposalAdapter.isProposalPopupOpen();
94     }
95
96     @Override
97     public ErrorAnnotation[] validate(String command) {
98         if(command.isEmpty())
99             return ErrorAnnotation.EMPTY_ARRAY;
100         
101         CompilationError[] errors = session.validate(command);
102         if(errors.length == 0)
103             return ErrorAnnotation.EMPTY_ARRAY;
104         
105         ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
106         for(int i=0;i<errors.length;++i) {
107             CompilationError error = errors[i];
108             int begin = Locations.beginOf(error.location);
109             if(begin == Integer.MAX_VALUE)
110                 begin = 0;
111             int end = Locations.endOf(error.location);
112             if(end == Integer.MIN_VALUE)
113                 end = command.length();
114             if(begin == end) {
115                 if(begin > 0)
116                     --begin;
117                 else
118                     ++end;
119             }
120             
121             annotations[i] = new ErrorAnnotation(begin, end, error.description);
122         }
123         
124         return annotations;
125     }
126     
127     private String jobNameFromCommand(String command) {
128         return command.split("\n")[0];
129     }
130
131     @Override
132     public void execute(final String command) {
133         Job job = new Job(jobNameFromCommand(command)) {
134             @Override
135             protected IStatus run(IProgressMonitor monitor) {
136                 try {
137                     session.execute(command, handler);
138                 } finally {
139                     synchronized(currentJobs) {
140                         currentJobs.remove(this);
141                         if(currentJobs.isEmpty())
142                             for(SCLConsoleListener listener : listeners)
143                                 listener.finishedExecution();
144                     }
145                 }
146                 return Status.OK_STATUS;
147             }
148             
149             @Override
150             protected void canceling() {
151                 Thread thread = getThread();
152                 if(thread != null)
153                     thread.interrupt();
154                 
155                 try {
156                     Thread.sleep(TERMINATE_GRACE_PERIOD);
157                 } catch (InterruptedException e) {
158                     e.printStackTrace();
159                     return;
160                 }
161                 
162                 thread = getThread();
163                 if(thread != null)
164                     thread.stop();
165             }
166         };
167         job.setRule(schedulingRule);
168         synchronized(currentJobs) {
169             boolean firstJob = currentJobs.isEmpty();
170             currentJobs.add(job);
171             if(firstJob) {
172                 synchronized(listeners) {
173                     for(SCLConsoleListener listener : listeners)
174                         listener.startedExecution();
175                 }
176             }
177         }
178         job.schedule();
179     }
180
181     public CommandSession getSession() {
182         return session;
183     }
184
185     public SCLReportingHandler getHandler() {
186         return handler;
187     }
188
189     public void interruptCurrentCommands() {
190         synchronized(currentJobs) {
191             for(Job job : currentJobs)
192                 job.cancel();
193             currentJobs.clear();
194         }
195     }
196     
197     public void addListener(SCLConsoleListener listener) {
198         synchronized (listeners) {
199             listeners.add(listener);
200         }
201     }
202     
203     public void removeListener(SCLConsoleListener listener) {
204         synchronized (listeners) {
205             listeners.remove(listener);
206         }
207     }
208     
209     @Override
210     public void appendOutput(String text, Color foreground, Color background) {
211         super.appendOutput(text, foreground, background);
212         if(consoleIsEmpty) {
213             consoleIsEmpty = false;
214             synchronized (listeners) {
215                 for(SCLConsoleListener listener : listeners)
216                     listener.consoleIsNotEmptyAnymore();
217             }
218         }
219     }
220     
221     @Override
222     public void clear() {
223         super.clear();
224         consoleIsEmpty = true;
225     }
226
227     protected void addContributedListeners() {
228         final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
229         new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
230                 SCLConsoleListener.class, null) {
231                     @Override
232                     public SCLConsoleListener addingService(
233                             ServiceReference<SCLConsoleListener> reference) {
234                         SCLConsoleListener listener = context.getService(reference);
235                         addListener(listener);
236                         return listener;
237                     }
238
239                     @Override
240                     public void modifiedService(
241                             ServiceReference<SCLConsoleListener> reference,
242                             SCLConsoleListener service) {
243                     }
244
245                     @Override
246                     public void removedService(
247                             ServiceReference<SCLConsoleListener> reference,
248                             SCLConsoleListener service) {
249                         removeListener(service);
250                     }
251                 }.open();
252     }
253 }