--- /dev/null
+package org.simantics.scl.ui.editor;\r
+\r
+\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+\r
+import org.eclipse.jface.resource.ImageRegistry;\r
+import org.eclipse.jface.text.Document;\r
+import org.eclipse.jface.text.ITextListener;\r
+import org.eclipse.jface.text.Position;\r
+import org.eclipse.jface.text.TextEvent;\r
+import org.eclipse.jface.text.source.Annotation;\r
+import org.eclipse.jface.text.source.AnnotationModel;\r
+import org.eclipse.jface.text.source.AnnotationPainter;\r
+import org.eclipse.jface.text.source.IAnnotationModel;\r
+import org.eclipse.jface.text.source.ISharedTextColors;\r
+import org.eclipse.jface.text.source.OverviewRuler;\r
+import org.eclipse.jface.text.source.SourceViewer;\r
+import org.eclipse.jface.text.source.VerticalRuler;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.graphics.Point;\r
+import org.eclipse.swt.graphics.RGB;\r
+import org.eclipse.swt.layout.FillLayout;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.simantics.scl.compiler.ErrorMessage;\r
+import org.simantics.scl.compiler.InvalidInputException;\r
+import org.simantics.scl.compiler.SCLCompiler;\r
+import org.simantics.scl.compiler.SCLCompilerConfiguration;\r
+\r
+public class SCLTextEditor extends Composite {\r
+\r
+ private static final int DELAY_BEFORE_COMPILATION = 500 /*ms*/;\r
+ \r
+ SCLCompilerConfiguration configuration;\r
+ \r
+ SourceViewer viewer;\r
+ ImageRegistry imageRegistry;\r
+ SCLAnnotationAccess annotationAccess;\r
+ ISharedTextColors sharedTextColors;\r
+ IAnnotationModel annotationModel; \r
+ \r
+ public SCLTextEditor(Composite parent, int style, SCLCompilerConfiguration configuration) {\r
+ super(parent, style);\r
+ setLayout(new FillLayout());\r
+ \r
+ this.configuration = configuration;\r
+ \r
+ imageRegistry = new ImageRegistry(parent.getDisplay());\r
+ annotationAccess = new SCLAnnotationAccess(imageRegistry);\r
+ sharedTextColors = new SharedTextColors(getDisplay());\r
+ annotationModel = new AnnotationModel();\r
+ \r
+ VerticalRuler leftRuler = new VerticalRuler(12, annotationAccess);\r
+ leftRuler.setModel(annotationModel);\r
+ \r
+ OverviewRuler rightRuler = \r
+ new OverviewRuler(annotationAccess, 12, sharedTextColors);\r
+ rightRuler.setModel(annotationModel);\r
+ rightRuler.addAnnotationType("error");\r
+ rightRuler.setAnnotationTypeLayer("error", 0);\r
+ rightRuler.setAnnotationTypeColor("error", sharedTextColors.getColor(new RGB(255,0,128)));\r
+ \r
+ viewer = new SourceViewer(this, \r
+ leftRuler, rightRuler,\r
+ true,\r
+ SWT.H_SCROLL | SWT.V_SCROLL);\r
+ Document document = new Document();\r
+ viewer.setDocument(document, annotationModel);\r
+ viewer.setEditable(true);\r
+ viewer.configure(new SCLSourceViewerConfiguration(\r
+ getDisplay(), sharedTextColors));\r
+ \r
+ // Annotations to text area\r
+ AnnotationPainter annotationPainter = \r
+ new AnnotationPainter(viewer, annotationAccess);\r
+ annotationPainter.addAnnotationType("error");\r
+ annotationPainter.setAnnotationTypeColor("error", sharedTextColors.getColor(new RGB(255,0,128)));\r
+ viewer.addPainter(annotationPainter);\r
+ annotationModel.addAnnotationModelListener(annotationPainter);\r
+ \r
+ // Undo support (maybe not needed in workbench?)\r
+ viewer.getTextWidget().addKeyListener(new KeyAdapter() {\r
+ @Override\r
+ public void keyReleased(KeyEvent e) {\r
+ }\r
+ @Override\r
+ public void keyPressed(KeyEvent e) {\r
+ if(e.keyCode=='z'&& e.stateMask == SWT.CTRL) {\r
+ viewer.getUndoManager().undo();\r
+ }\r
+ else if(e.keyCode=='y'&& e.stateMask == SWT.CTRL) {\r
+ viewer.getUndoManager().redo();\r
+ }\r
+ }\r
+ }); \r
+ \r
+ // Automatic compilation when text changes\r
+ viewer.addTextListener(new ITextListener() { \r
+ @Override\r
+ public void textChanged(TextEvent event) {\r
+ scheduleCompilation(); \r
+ }\r
+ });\r
+ }\r
+ \r
+ @Override\r
+ public void dispose() {\r
+ super.dispose();\r
+ sharedTextColors.dispose();\r
+ } \r
+ \r
+ @SuppressWarnings("unchecked")\r
+ private void removeAnnotations() {\r
+ Iterator<Annotation> it = annotationModel.getAnnotationIterator();\r
+ while(it.hasNext()) {\r
+ Annotation annotation = it.next();\r
+ annotationModel.removeAnnotation(annotation);\r
+ }\r
+ }\r
+ \r
+ private void setAnnotations(Collection<ErrorMessage> messages) {\r
+ removeAnnotations();\r
+ for(ErrorMessage message : messages) {\r
+ annotationModel.addAnnotation(\r
+ new Annotation("error", true, message.getMessage()), \r
+ new Position(message.getStart(), message.getStop()-message.getStart()+1)); \r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Tries to compile current \r
+ */\r
+ private void compileSync(String code) {\r
+ try { \r
+ SCLCompiler.compileExpression(configuration, code);\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ removeAnnotations(); \r
+ }\r
+ }); \r
+ } catch (final InvalidInputException e) {\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ setAnnotations(e.getErrors()); \r
+ }\r
+ }); \r
+ } catch(Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+\r
+ Object compilationLock = new Object();\r
+ String codeToBeCompiled;\r
+ \r
+ private synchronized void scheduleCompilation() {\r
+ synchronized(compilationLock) {\r
+ if(codeToBeCompiled == null) {\r
+ new Thread("SCLTextEditor compilation") {\r
+ public void run() { \r
+ while(true) { \r
+ String code;\r
+ // Waits until code has remained unmodified for\r
+ // time specified by DELAY_BEFORE_COMPILATION. \r
+ synchronized(compilationLock) {\r
+ do {\r
+ code = codeToBeCompiled; \r
+ try {\r
+ compilationLock.wait(DELAY_BEFORE_COMPILATION);\r
+ } catch (InterruptedException e) {\r
+ }\r
+ } while(!code.equals(codeToBeCompiled));\r
+ }\r
+ \r
+ // Does the actual compilation and updates\r
+ // annotations.\r
+ compileSync(code);\r
+ \r
+ // If code was not modified during compilation,\r
+ // exits the compilation thread and sets\r
+ // codeToBeCompiled null to signal inactivity.\r
+ synchronized(compilationLock) {\r
+ if(code.equals(codeToBeCompiled)) {\r
+ codeToBeCompiled = null;\r
+ return;\r
+ }\r
+ }\r
+ } \r
+ }\r
+ }.start(); \r
+ }\r
+ codeToBeCompiled = viewer.getDocument().get();\r
+ compilationLock.notify();\r
+ }\r
+ }\r
+ \r
+ public String getContent() {\r
+ final String[] result = new String[1];\r
+ getDisplay().syncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ result[0] = viewer.getDocument().get();\r
+ }\r
+ });\r
+ return result[0];\r
+ }\r
+ \r
+ public void setContent(final String content) {\r
+ getDisplay().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (viewer.getTextWidget().isDisposed()) return;\r
+ viewer.getDocument().set(content);\r
+ }\r
+ });\r
+ }\r
+\r
+ private Point storedSelectedRange;\r
+\r
+ public void storeSelectedRange() {\r
+ storedSelectedRange = viewer.getSelectedRange();\r
+ }\r
+\r
+ public void restoreSelectedRange() {\r
+ if (storedSelectedRange != null) {\r
+ viewer.setSelectedRange(storedSelectedRange.x, storedSelectedRange.y);\r
+ storedSelectedRange = null;\r
+ }\r
+ }\r
+\r
+}\r