1 package org.simantics.scl.ui.editor2;
3 import java.text.CharacterIterator;
5 import org.eclipse.jface.action.IAction;
6 import org.eclipse.jface.resource.JFaceResources;
7 import org.eclipse.jface.resource.LocalResourceManager;
8 import org.eclipse.jface.resource.ResourceManager;
9 import org.eclipse.jface.text.BadLocationException;
10 import org.eclipse.jface.text.IDocument;
11 import org.eclipse.jface.text.link.LinkedModeModel;
12 import org.eclipse.jface.text.link.LinkedPosition;
13 import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
14 import org.eclipse.jface.text.source.ISourceViewer;
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.custom.ST;
17 import org.eclipse.swt.custom.StyledText;
18 import org.eclipse.swt.graphics.Point;
19 import org.eclipse.swt.widgets.Composite;
20 import org.eclipse.ui.IEditorInput;
21 import org.eclipse.ui.IEditorSite;
22 import org.eclipse.ui.PartInitException;
23 import org.eclipse.ui.contexts.IContextService;
24 import org.eclipse.ui.editors.text.TextEditor;
25 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
26 import org.eclipse.ui.texteditor.IUpdate;
27 import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
28 import org.eclipse.ui.texteditor.TextNavigationAction;
29 import org.simantics.scl.ui.editor.SCLSourceViewerConfigurationNew;
30 import org.simantics.scl.ui.editor.completion.SCLTextEditorEnvironment;
31 import org.simantics.scl.ui.editor2.iterator.DocumentCharacterIterator;
32 import org.simantics.scl.ui.editor2.iterator.JavaWordIterator;
34 import com.ibm.icu.text.BreakIterator;
36 public class SCLModuleEditor2 extends TextEditor {
38 private static final char[] CHARS = new char[] { '(', ')', '{', '}', '[', ']', '<', '>' };
40 private static final String MATCHING_BRACKETS = "matchingBrackets";
41 private static final String MATCHING_BRACKETS_COLOR = "matchingBracketsColor";
42 private static final String HIGHLIGHT_BRACKET_AT_CARET_LOCATION = "highlightBracketAtCaretLocation";
43 private static final String ENCLOSING_BRACKETS = "enclosingBrackets";
45 private boolean disposed = false;
46 protected ResourceManager resourceManager;
47 private DefaultCharacterPairMatcher matcher;
49 public SCLModuleEditor2() {
51 resourceManager = new LocalResourceManager(JFaceResources.getResources());
52 SCLSourceViewerConfigurationNew sourceViewerConfiguration = new SCLSourceViewerConfigurationNew(resourceManager);
53 setDocumentProvider(new SCLModuleEditor2DocumentProvider(sourceViewerConfiguration));
54 setSourceViewerConfiguration(sourceViewerConfiguration);
58 public boolean isTabsToSpacesConversionEnabled() {
63 public void init(IEditorSite site, IEditorInput input)
64 throws PartInitException {
65 super.init(site, input);
66 getPreferenceStore().setValue(MATCHING_BRACKETS, true);
67 getPreferenceStore().setValue(MATCHING_BRACKETS_COLOR, "192,192,192");
68 getPreferenceStore().setValue(HIGHLIGHT_BRACKET_AT_CARET_LOCATION, true);
69 getPreferenceStore().setValue(ENCLOSING_BRACKETS, true);
73 public void createPartControl(Composite parent) {
74 super.createPartControl(parent);
75 getEditorSite().getService(IContextService.class).activateContext("org.simantics.scl.ui.editor");
80 protected void createNavigationActions() {
81 super.createNavigationActions();
83 // Taken from org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.createNavigationActions()
84 final StyledText textWidget= getSourceViewer().getTextWidget();
86 IAction action = new NavigatePreviousSubWordAction();
87 action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS);
88 setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action);
89 textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL);
91 action = new NavigateNextSubWordAction();
92 action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT);
93 setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action);
94 textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL);
96 action = new SelectPreviousSubWordAction();
97 action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS);
98 setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action);
99 textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL);
101 action = new SelectNextSubWordAction();
102 action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT);
103 setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action);
104 textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL);
107 protected void updatePartName() {
108 setPartName(getEditorInput().getName());
112 protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
113 matcher = new DefaultCharacterPairMatcher(CHARS);
114 support.setCharacterPairMatcher(matcher);
115 support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS);
116 super.configureSourceViewerDecorationSupport(support);
120 public void dispose() {
123 resourceManager.dispose();
127 public boolean isDisposed() {
131 public SCLTextEditorEnvironment getSCLTextEditorEnvironment() {
132 return ((SCLSourceViewerConfigurationNew)getSourceViewerConfiguration())
133 .getSclTextEditorEnvironment();
136 public IDocument getDocument() {
137 return getSourceViewer().getDocument();
141 * Text navigation action to navigate to the next sub-word.
145 protected abstract class NextSubWordAction extends TextNavigationAction {
147 protected JavaWordIterator fIterator= new JavaWordIterator();
150 * Creates a new next sub-word action.
152 * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
154 protected NextSubWordAction(int code) {
155 super(getSourceViewer().getTextWidget(), code);
159 * @see org.eclipse.jface.action.IAction#run()
163 final ISourceViewer viewer= getSourceViewer();
164 final IDocument document= viewer.getDocument();
166 fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
167 int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
171 int next= findNextPosition(position);
172 if (isBlockSelectionModeEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) {
173 super.run(); // may navigate into virtual white space
174 } else if (next != BreakIterator.DONE) {
175 setCaretPosition(next);
176 getTextWidget().showSelection();
177 fireSelectionChanged();
179 } catch (BadLocationException x) {
185 * Finds the next position after the given position.
187 * @param position the current position
188 * @return the next position
190 protected int findNextPosition(int position) {
191 ISourceViewer viewer= getSourceViewer();
194 while (next != BreakIterator.DONE && widget == -1) { // XXX: optimize
195 next= fIterator.following(next);
196 if (next != BreakIterator.DONE)
197 widget= modelOffset2WidgetOffset(viewer, next);
200 IDocument document= viewer.getDocument();
201 LinkedModeModel model= LinkedModeModel.getModel(document, position);
202 if (model != null && next != BreakIterator.DONE) {
203 LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
204 if (linkedPosition != null) {
205 int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
206 if (position != linkedPositionEnd && linkedPositionEnd < next)
207 next= linkedPositionEnd;
209 LinkedPosition nextLinkedPosition= model.findPosition(new LinkedPosition(document, next, 0));
210 if (nextLinkedPosition != null) {
211 int nextLinkedPositionOffset= nextLinkedPosition.getOffset();
212 if (position != nextLinkedPositionOffset && nextLinkedPositionOffset < next)
213 next= nextLinkedPositionOffset;
222 * Sets the caret position to the sub-word boundary given with <code>position</code>.
224 * @param position Position where the action should move the caret
226 protected abstract void setCaretPosition(int position);
230 * Text navigation action to navigate to the next sub-word.
234 protected class NavigateNextSubWordAction extends NextSubWordAction {
237 * Creates a new navigate next sub-word action.
239 public NavigateNextSubWordAction() {
244 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
247 protected void setCaretPosition(final int position) {
248 getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
253 * Text operation action to delete the next sub-word.
257 protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate {
260 * Creates a new delete next sub-word action.
262 public DeleteNextSubWordAction() {
263 super(ST.DELETE_WORD_NEXT);
267 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
270 protected void setCaretPosition(final int position) {
271 if (!validateEditorInputState())
274 final ISourceViewer viewer= getSourceViewer();
275 StyledText text= viewer.getTextWidget();
276 Point widgetSelection= text.getSelection();
277 if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
278 final int caret= text.getCaretOffset();
279 final int offset= modelOffset2WidgetOffset(viewer, position);
281 if (caret == widgetSelection.x)
282 text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
284 text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
285 text.invokeAction(ST.DELETE_NEXT);
287 Point selection= viewer.getSelectedRange();
288 final int caret, length;
289 if (selection.y != 0) {
293 caret= widgetOffset2ModelOffset(viewer, text.getCaretOffset());
294 length= position - caret;
298 viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$
299 } catch (BadLocationException exception) {
306 * @see org.eclipse.ui.texteditor.IUpdate#update()
308 public void update() {
309 setEnabled(isEditorInputModifiable());
314 * Text operation action to select the next sub-word.
318 protected class SelectNextSubWordAction extends NextSubWordAction {
321 * Creates a new select next sub-word action.
323 public SelectNextSubWordAction() {
324 super(ST.SELECT_WORD_NEXT);
328 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
331 protected void setCaretPosition(final int position) {
332 final ISourceViewer viewer= getSourceViewer();
334 final StyledText text= viewer.getTextWidget();
335 if (text != null && !text.isDisposed()) {
337 final Point selection= text.getSelection();
338 final int caret= text.getCaretOffset();
339 final int offset= modelOffset2WidgetOffset(viewer, position);
341 if (caret == selection.x)
342 text.setSelectionRange(selection.y, offset - selection.y);
344 text.setSelectionRange(selection.x, offset - selection.x);
350 * Text navigation action to navigate to the previous sub-word.
354 protected abstract class PreviousSubWordAction extends TextNavigationAction {
356 protected JavaWordIterator fIterator= new JavaWordIterator();
359 * Creates a new previous sub-word action.
361 * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
363 protected PreviousSubWordAction(final int code) {
364 super(getSourceViewer().getTextWidget(), code);
368 * @see org.eclipse.jface.action.IAction#run()
372 final ISourceViewer viewer= getSourceViewer();
373 final IDocument document= viewer.getDocument();
375 fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
376 int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
380 int previous= findPreviousPosition(position);
381 if (isBlockSelectionModeEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) {
382 super.run(); // may navigate into virtual white space
383 } else if (previous != BreakIterator.DONE) {
384 setCaretPosition(previous);
385 getTextWidget().showSelection();
386 fireSelectionChanged();
388 } catch (BadLocationException x) {
389 // ignore - getLineOfOffset failed
395 * Finds the previous position before the given position.
397 * @param position the current position
398 * @return the previous position
400 protected int findPreviousPosition(int position) {
401 ISourceViewer viewer= getSourceViewer();
403 int previous= position;
404 while (previous != BreakIterator.DONE && widget == -1) { // XXX: optimize
405 previous= fIterator.preceding(previous);
406 if (previous != BreakIterator.DONE)
407 widget= modelOffset2WidgetOffset(viewer, previous);
410 IDocument document= viewer.getDocument();
411 LinkedModeModel model= LinkedModeModel.getModel(document, position);
412 if (model != null && previous != BreakIterator.DONE) {
413 LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
414 if (linkedPosition != null) {
415 int linkedPositionOffset= linkedPosition.getOffset();
416 if (position != linkedPositionOffset && previous < linkedPositionOffset)
417 previous= linkedPositionOffset;
419 LinkedPosition previousLinkedPosition= model.findPosition(new LinkedPosition(document, previous, 0));
420 if (previousLinkedPosition != null) {
421 int previousLinkedPositionEnd= previousLinkedPosition.getOffset() + previousLinkedPosition.getLength();
422 if (position != previousLinkedPositionEnd && previous < previousLinkedPositionEnd)
423 previous= previousLinkedPositionEnd;
432 * Sets the caret position to the sub-word boundary given with <code>position</code>.
434 * @param position Position where the action should move the caret
436 protected abstract void setCaretPosition(int position);
440 * Text navigation action to navigate to the previous sub-word.
444 protected class NavigatePreviousSubWordAction extends PreviousSubWordAction {
447 * Creates a new navigate previous sub-word action.
449 public NavigatePreviousSubWordAction() {
450 super(ST.WORD_PREVIOUS);
454 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
457 protected void setCaretPosition(final int position) {
458 getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
463 * Text operation action to delete the previous sub-word.
467 protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate {
470 * Creates a new delete previous sub-word action.
472 public DeletePreviousSubWordAction() {
473 super(ST.DELETE_WORD_PREVIOUS);
477 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
480 protected void setCaretPosition(int position) {
481 if (!validateEditorInputState())
485 final ISourceViewer viewer= getSourceViewer();
486 StyledText text= viewer.getTextWidget();
487 Point widgetSelection= text.getSelection();
488 if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
489 final int caret= text.getCaretOffset();
490 final int offset= modelOffset2WidgetOffset(viewer, position);
492 if (caret == widgetSelection.x)
493 text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
495 text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
496 text.invokeAction(ST.DELETE_PREVIOUS);
498 Point selection= viewer.getSelectedRange();
499 if (selection.y != 0) {
500 position= selection.x;
503 length= widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position;
507 viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$
508 } catch (BadLocationException exception) {
515 * @see org.eclipse.ui.texteditor.IUpdate#update()
517 public void update() {
518 setEnabled(isEditorInputModifiable());
523 * Text operation action to select the previous sub-word.
527 protected class SelectPreviousSubWordAction extends PreviousSubWordAction {
530 * Creates a new select previous sub-word action.
532 public SelectPreviousSubWordAction() {
533 super(ST.SELECT_WORD_PREVIOUS);
537 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
540 protected void setCaretPosition(final int position) {
541 final ISourceViewer viewer= getSourceViewer();
543 final StyledText text= viewer.getTextWidget();
544 if (text != null && !text.isDisposed()) {
546 final Point selection= text.getSelection();
547 final int caret= text.getCaretOffset();
548 final int offset= modelOffset2WidgetOffset(viewer, position);
550 if (caret == selection.x)
551 text.setSelectionRange(selection.y, offset - selection.y);
553 text.setSelectionRange(selection.x, offset - selection.x);