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