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