1 package org.simantics.scl.ui.editor;
4 import java.util.Collection;
5 import java.util.Iterator;
7 import org.eclipse.jface.resource.ImageRegistry;
8 import org.eclipse.jface.text.Document;
9 import org.eclipse.jface.text.ITextListener;
10 import org.eclipse.jface.text.Position;
11 import org.eclipse.jface.text.TextEvent;
12 import org.eclipse.jface.text.source.Annotation;
13 import org.eclipse.jface.text.source.AnnotationModel;
14 import org.eclipse.jface.text.source.AnnotationPainter;
15 import org.eclipse.jface.text.source.IAnnotationModel;
16 import org.eclipse.jface.text.source.ISharedTextColors;
17 import org.eclipse.jface.text.source.OverviewRuler;
18 import org.eclipse.jface.text.source.SourceViewer;
19 import org.eclipse.jface.text.source.VerticalRuler;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.events.KeyAdapter;
22 import org.eclipse.swt.events.KeyEvent;
23 import org.eclipse.swt.graphics.Point;
24 import org.eclipse.swt.graphics.RGB;
25 import org.eclipse.swt.layout.FillLayout;
26 import org.eclipse.swt.widgets.Composite;
27 import org.simantics.scl.compiler.ErrorMessage;
28 import org.simantics.scl.compiler.InvalidInputException;
29 import org.simantics.scl.compiler.SCLCompiler;
30 import org.simantics.scl.compiler.SCLCompilerConfiguration;
32 public class SCLTextEditor extends Composite {
34 private static final int DELAY_BEFORE_COMPILATION = 500 /*ms*/;
36 SCLCompilerConfiguration configuration;
39 ImageRegistry imageRegistry;
40 SCLAnnotationAccess annotationAccess;
41 ISharedTextColors sharedTextColors;
42 IAnnotationModel annotationModel;
44 public SCLTextEditor(Composite parent, int style, SCLCompilerConfiguration configuration) {
46 setLayout(new FillLayout());
48 this.configuration = configuration;
50 imageRegistry = new ImageRegistry(parent.getDisplay());
51 annotationAccess = new SCLAnnotationAccess(imageRegistry);
52 sharedTextColors = new SharedTextColors(getDisplay());
53 annotationModel = new AnnotationModel();
55 VerticalRuler leftRuler = new VerticalRuler(12, annotationAccess);
56 leftRuler.setModel(annotationModel);
58 OverviewRuler rightRuler =
59 new OverviewRuler(annotationAccess, 12, sharedTextColors);
60 rightRuler.setModel(annotationModel);
61 rightRuler.addAnnotationType("error");
62 rightRuler.setAnnotationTypeLayer("error", 0);
63 rightRuler.setAnnotationTypeColor("error", sharedTextColors.getColor(new RGB(255,0,128)));
65 viewer = new SourceViewer(this,
66 leftRuler, rightRuler,
68 SWT.H_SCROLL | SWT.V_SCROLL);
69 Document document = new Document();
70 viewer.setDocument(document, annotationModel);
71 viewer.setEditable(true);
72 viewer.configure(new SCLSourceViewerConfiguration(
73 getDisplay(), sharedTextColors));
75 // Annotations to text area
76 AnnotationPainter annotationPainter =
77 new AnnotationPainter(viewer, annotationAccess);
78 annotationPainter.addAnnotationType("error");
79 annotationPainter.setAnnotationTypeColor("error", sharedTextColors.getColor(new RGB(255,0,128)));
80 viewer.addPainter(annotationPainter);
81 annotationModel.addAnnotationModelListener(annotationPainter);
83 // Undo support (maybe not needed in workbench?)
84 viewer.getTextWidget().addKeyListener(new KeyAdapter() {
86 public void keyReleased(KeyEvent e) {
89 public void keyPressed(KeyEvent e) {
90 if(e.keyCode=='z'&& e.stateMask == SWT.CTRL) {
91 viewer.getUndoManager().undo();
93 else if(e.keyCode=='y'&& e.stateMask == SWT.CTRL) {
94 viewer.getUndoManager().redo();
99 // Automatic compilation when text changes
100 viewer.addTextListener(new ITextListener() {
102 public void textChanged(TextEvent event) {
103 scheduleCompilation();
109 public void dispose() {
111 sharedTextColors.dispose();
114 @SuppressWarnings("unchecked")
115 private void removeAnnotations() {
116 Iterator<Annotation> it = annotationModel.getAnnotationIterator();
117 while(it.hasNext()) {
118 Annotation annotation = it.next();
119 annotationModel.removeAnnotation(annotation);
123 private void setAnnotations(Collection<ErrorMessage> messages) {
125 for(ErrorMessage message : messages) {
126 annotationModel.addAnnotation(
127 new Annotation("error", true, message.getMessage()),
128 new Position(message.getStart(), message.getStop()-message.getStart()+1));
133 * Tries to compile current
135 private void compileSync(String code) {
137 SCLCompiler.compileExpression(configuration, code);
138 getDisplay().asyncExec(new Runnable() {
144 } catch (final InvalidInputException e) {
145 getDisplay().asyncExec(new Runnable() {
148 setAnnotations(e.getErrors());
151 } catch(Exception e) {
156 Object compilationLock = new Object();
157 String codeToBeCompiled;
159 private synchronized void scheduleCompilation() {
160 synchronized(compilationLock) {
161 if(codeToBeCompiled == null) {
162 new Thread("SCLTextEditor compilation") {
166 // Waits until code has remained unmodified for
167 // time specified by DELAY_BEFORE_COMPILATION.
168 synchronized(compilationLock) {
170 code = codeToBeCompiled;
172 compilationLock.wait(DELAY_BEFORE_COMPILATION);
173 } catch (InterruptedException e) {
175 } while(!code.equals(codeToBeCompiled));
178 // Does the actual compilation and updates
182 // If code was not modified during compilation,
183 // exits the compilation thread and sets
184 // codeToBeCompiled null to signal inactivity.
185 synchronized(compilationLock) {
186 if(code.equals(codeToBeCompiled)) {
187 codeToBeCompiled = null;
195 codeToBeCompiled = viewer.getDocument().get();
196 compilationLock.notify();
200 public String getContent() {
201 final String[] result = new String[1];
202 getDisplay().syncExec(new Runnable() {
205 result[0] = viewer.getDocument().get();
211 public void setContent(final String content) {
212 getDisplay().asyncExec(new Runnable() {
215 if (viewer.getTextWidget().isDisposed()) return;
216 viewer.getDocument().set(content);
221 private Point storedSelectedRange;
223 public void storeSelectedRange() {
224 storedSelectedRange = viewer.getSelectedRange();
227 public void restoreSelectedRange() {
228 if (storedSelectedRange != null) {
229 viewer.setSelectedRange(storedSelectedRange.x, storedSelectedRange.y);
230 storedSelectedRange = null;