]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java
LabelDecorator.decorateLabel can return null
[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         super(parent, style);
64         
65         StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter();
66         SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session);
67         
68         try {
69             contentProposalAdapter = new ContentProposalAdapter(
70                     input, 
71                     styledTextContentAdapter, 
72                     contentProvider, 
73                     KeyStroke.getInstance("Ctrl+Space"), 
74                     null);
75             contentProposalAdapter.setAutoActivationDelay(200);
76         } catch (ParseException e) {
77             // No content assist then.
78         }
79         
80         addContributedListeners();
81     }
82
83     @Override
84     protected boolean canExecuteCommand() {
85         return !contentProposalAdapter.isProposalPopupOpen();
86     }
87     
88     @Override
89     public ErrorAnnotation[] validate(String command) {
90         if(command.isEmpty())
91             return ErrorAnnotation.EMPTY_ARRAY;
92         
93         CompilationError[] errors = session.validate(command);
94         if(errors.length == 0)
95             return ErrorAnnotation.EMPTY_ARRAY;
96         
97         ErrorAnnotation[] annotations = new ErrorAnnotation[errors.length];
98         for(int i=0;i<errors.length;++i) {
99             CompilationError error = errors[i];
100             int begin = Locations.beginOf(error.location);
101             if(begin == Integer.MAX_VALUE)
102                 begin = 0;
103             int end = Locations.endOf(error.location);
104             if(end == Integer.MIN_VALUE)
105                 end = command.length();
106             if(begin == end) {
107                 if(begin > 0)
108                     --begin;
109                 else
110                     ++end;
111             }
112             
113             annotations[i] = new ErrorAnnotation(begin, end, error.description);
114         }
115         
116         return annotations;
117     }
118     
119     private String jobNameFromCommand(String command) {
120         return command.split("\n")[0];
121     }
122
123     @Override
124     public void execute(final String command) {
125         Job job = new Job(jobNameFromCommand(command)) {
126             @Override
127             protected IStatus run(IProgressMonitor monitor) {
128                 try {
129                     session.execute(command, handler);
130                 } finally {
131                     synchronized(currentJobs) {
132                         currentJobs.remove(this);
133                         if(currentJobs.isEmpty())
134                             for(SCLConsoleListener listener : listeners)
135                                 listener.finishedExecution();
136                     }
137                 }
138                 return Status.OK_STATUS;
139             }
140             
141             @Override
142             protected void canceling() {
143                 Thread thread = getThread();
144                 if(thread != null)
145                     thread.interrupt();
146                 
147                 try {
148                     Thread.sleep(TERMINATE_GRACE_PERIOD);
149                 } catch (InterruptedException e) {
150                     e.printStackTrace();
151                     return;
152                 }
153                 
154                 thread = getThread();
155                 if(thread != null)
156                     thread.stop();
157             }
158         };
159         job.setRule(schedulingRule);
160         synchronized(currentJobs) {
161             boolean firstJob = currentJobs.isEmpty();
162             currentJobs.add(job);
163             if(firstJob) {
164                 synchronized(listeners) {
165                     for(SCLConsoleListener listener : listeners)
166                         listener.startedExecution();
167                 }
168             }
169         }
170         job.schedule();
171     }
172
173     public CommandSession getSession() {
174         return session;
175     }
176     public void interruptCurrentCommands() {
177         synchronized(currentJobs) {
178             for(Job job : currentJobs)
179                 job.cancel();
180             currentJobs.clear();
181         }
182     }
183     
184     public void addListener(SCLConsoleListener listener) {
185         synchronized (listeners) {
186             listeners.add(listener);
187         }
188     }
189     
190     public void removeListener(SCLConsoleListener listener) {
191         synchronized (listeners) {
192             listeners.remove(listener);
193         }
194     }
195     
196     @Override
197     public void appendOutput(String text, Color foreground, Color background) {
198         super.appendOutput(text, foreground, background);
199         if(consoleIsEmpty) {
200             consoleIsEmpty = false;
201             synchronized (listeners) {
202                 for(SCLConsoleListener listener : listeners)
203                     listener.consoleIsNotEmptyAnymore();
204             }
205         }
206     }
207     
208     @Override
209     public void clear() {
210         super.clear();
211         consoleIsEmpty = true;
212     }
213
214     private void addContributedListeners() {
215         final BundleContext context = Activator.getInstance().getBundle().getBundleContext();
216         new ServiceTracker<SCLConsoleListener, SCLConsoleListener>(context,
217                 SCLConsoleListener.class, null) {
218                     @Override
219                     public SCLConsoleListener addingService(
220                             ServiceReference<SCLConsoleListener> reference) {
221                         SCLConsoleListener listener = context.getService(reference);
222                         addListener(listener);
223                         return listener;
224                     }
225
226                     @Override
227                     public void modifiedService(
228                             ServiceReference<SCLConsoleListener> reference,
229                             SCLConsoleListener service) {
230                     }
231
232                     @Override
233                     public void removedService(
234                             ServiceReference<SCLConsoleListener> reference,
235                             SCLConsoleListener service) {
236                         removeListener(service);
237                     }
238                 }.open();
239     }
240 }