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.eclipse.ui.views.contentoutline.IContentOutlinePage;
30 import org.simantics.scl.ui.editor.SCLSourceViewerConfigurationNew;
31 import org.simantics.scl.ui.editor.completion.SCLTextEditorEnvironment;
32 import org.simantics.scl.ui.editor2.iterator.DocumentCharacterIterator;
33 import org.simantics.scl.ui.editor2.iterator.JavaWordIterator;
34 import org.simantics.scl.ui.outline.SCLModuleOutlinePage;
36 import com.ibm.icu.text.BreakIterator;
38 public class SCLModuleEditor2 extends TextEditor {
40 private static final char[] CHARS = new char[] { '(', ')', '{', '}', '[', ']', '<', '>' };
42 private static final String MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$
43 private static final String MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; //$NON-NLS-1$
44 private static final String HIGHLIGHT_BRACKET_AT_CARET_LOCATION = "highlightBracketAtCaretLocation"; //$NON-NLS-1$
45 private static final String ENCLOSING_BRACKETS = "enclosingBrackets"; //$NON-NLS-1$
47 private boolean disposed = false;
48 protected ResourceManager resourceManager;
49 private DefaultCharacterPairMatcher matcher;
51 private SCLModuleOutlinePage outline;
53 public SCLModuleEditor2() {
55 resourceManager = new LocalResourceManager(JFaceResources.getResources());
56 SCLSourceViewerConfigurationNew sourceViewerConfiguration = new SCLSourceViewerConfigurationNew(resourceManager);
57 setDocumentProvider(new SCLModuleEditor2DocumentProvider(sourceViewerConfiguration));
58 setSourceViewerConfiguration(sourceViewerConfiguration);
59 outline = new SCLModuleOutlinePage(this);
63 public boolean isTabsToSpacesConversionEnabled() {
68 public void init(IEditorSite site, IEditorInput input)
69 throws PartInitException {
70 super.init(site, input);
71 getPreferenceStore().setValue(MATCHING_BRACKETS, true);
72 getPreferenceStore().setValue(MATCHING_BRACKETS_COLOR, "192,192,192"); //$NON-NLS-1$
73 getPreferenceStore().setValue(HIGHLIGHT_BRACKET_AT_CARET_LOCATION, true);
74 getPreferenceStore().setValue(ENCLOSING_BRACKETS, true);
78 public void createPartControl(Composite parent) {
79 super.createPartControl(parent);
80 getEditorSite().getService(IContextService.class).activateContext("org.simantics.scl.ui.editor"); //$NON-NLS-1$
85 protected void createNavigationActions() {
86 super.createNavigationActions();
88 // Taken from org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.createNavigationActions()
89 final StyledText textWidget= getSourceViewer().getTextWidget();
91 IAction action = new NavigatePreviousSubWordAction();
92 action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS);
93 setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action);
94 textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL);
96 action = new NavigateNextSubWordAction();
97 action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT);
98 setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action);
99 textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL);
101 action = new SelectPreviousSubWordAction();
102 action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS);
103 setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action);
104 textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL);
106 action = new SelectNextSubWordAction();
107 action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT);
108 setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action);
109 textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL);
112 protected void updatePartName() {
113 setPartName(getEditorInput().getName());
117 protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
118 matcher = new DefaultCharacterPairMatcher(CHARS);
119 support.setCharacterPairMatcher(matcher);
120 support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS);
121 super.configureSourceViewerDecorationSupport(support);
125 public void dispose() {
128 resourceManager.dispose();
129 if (matcher != null) matcher.dispose();
132 public boolean isDisposed() {
136 public SCLTextEditorEnvironment getSCLTextEditorEnvironment() {
137 return ((SCLSourceViewerConfigurationNew)getSourceViewerConfiguration())
138 .getSclTextEditorEnvironment();
141 public IDocument getDocument() {
142 return getSourceViewer().getDocument();
145 @SuppressWarnings("unchecked")
147 public <T> T getAdapter(Class<T> adapter) {
148 if (IContentOutlinePage.class.equals(adapter)) {
151 return super.getAdapter(adapter);
155 * Text navigation action to navigate to the next sub-word.
159 protected abstract class NextSubWordAction extends TextNavigationAction {
161 protected JavaWordIterator fIterator= new JavaWordIterator();
164 * Creates a new next sub-word action.
166 * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
168 protected NextSubWordAction(int code) {
169 super(getSourceViewer().getTextWidget(), code);
173 * @see org.eclipse.jface.action.IAction#run()
177 final ISourceViewer viewer= getSourceViewer();
178 final IDocument document= viewer.getDocument();
180 fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
181 int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
185 int next= findNextPosition(position);
186 if (isBlockSelectionModeEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) {
187 super.run(); // may navigate into virtual white space
188 } else if (next != BreakIterator.DONE) {
189 setCaretPosition(next);
190 getTextWidget().showSelection();
191 fireSelectionChanged();
193 } catch (BadLocationException x) {
199 * Finds the next position after the given position.
201 * @param position the current position
202 * @return the next position
204 protected int findNextPosition(int position) {
205 ISourceViewer viewer= getSourceViewer();
208 while (next != BreakIterator.DONE && widget == -1) { // XXX: optimize
209 next= fIterator.following(next);
210 if (next != BreakIterator.DONE)
211 widget= modelOffset2WidgetOffset(viewer, next);
214 IDocument document= viewer.getDocument();
215 LinkedModeModel model= LinkedModeModel.getModel(document, position);
216 if (model != null && next != BreakIterator.DONE) {
217 LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
218 if (linkedPosition != null) {
219 int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
220 if (position != linkedPositionEnd && linkedPositionEnd < next)
221 next= linkedPositionEnd;
223 LinkedPosition nextLinkedPosition= model.findPosition(new LinkedPosition(document, next, 0));
224 if (nextLinkedPosition != null) {
225 int nextLinkedPositionOffset= nextLinkedPosition.getOffset();
226 if (position != nextLinkedPositionOffset && nextLinkedPositionOffset < next)
227 next= nextLinkedPositionOffset;
236 * Sets the caret position to the sub-word boundary given with <code>position</code>.
238 * @param position Position where the action should move the caret
240 protected abstract void setCaretPosition(int position);
244 * Text navigation action to navigate to the next sub-word.
248 protected class NavigateNextSubWordAction extends NextSubWordAction {
251 * Creates a new navigate next sub-word action.
253 public NavigateNextSubWordAction() {
258 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
261 protected void setCaretPosition(final int position) {
262 getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
267 * Text operation action to delete the next sub-word.
271 protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate {
274 * Creates a new delete next sub-word action.
276 public DeleteNextSubWordAction() {
277 super(ST.DELETE_WORD_NEXT);
281 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
284 protected void setCaretPosition(final int position) {
285 if (!validateEditorInputState())
288 final ISourceViewer viewer= getSourceViewer();
289 StyledText text= viewer.getTextWidget();
290 Point widgetSelection= text.getSelection();
291 if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
292 final int caret= text.getCaretOffset();
293 final int offset= modelOffset2WidgetOffset(viewer, position);
295 if (caret == widgetSelection.x)
296 text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
298 text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
299 text.invokeAction(ST.DELETE_NEXT);
301 Point selection= viewer.getSelectedRange();
302 final int caret, length;
303 if (selection.y != 0) {
307 caret= widgetOffset2ModelOffset(viewer, text.getCaretOffset());
308 length= position - caret;
312 viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$
313 } catch (BadLocationException exception) {
320 * @see org.eclipse.ui.texteditor.IUpdate#update()
322 public void update() {
323 setEnabled(isEditorInputModifiable());
328 * Text operation action to select the next sub-word.
332 protected class SelectNextSubWordAction extends NextSubWordAction {
335 * Creates a new select next sub-word action.
337 public SelectNextSubWordAction() {
338 super(ST.SELECT_WORD_NEXT);
342 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int)
345 protected void setCaretPosition(final int position) {
346 final ISourceViewer viewer= getSourceViewer();
348 final StyledText text= viewer.getTextWidget();
349 if (text != null && !text.isDisposed()) {
351 final Point selection= text.getSelection();
352 final int caret= text.getCaretOffset();
353 final int offset= modelOffset2WidgetOffset(viewer, position);
355 if (caret == selection.x)
356 text.setSelectionRange(selection.y, offset - selection.y);
358 text.setSelectionRange(selection.x, offset - selection.x);
364 * Text navigation action to navigate to the previous sub-word.
368 protected abstract class PreviousSubWordAction extends TextNavigationAction {
370 protected JavaWordIterator fIterator= new JavaWordIterator();
373 * Creates a new previous sub-word action.
375 * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST.
377 protected PreviousSubWordAction(final int code) {
378 super(getSourceViewer().getTextWidget(), code);
382 * @see org.eclipse.jface.action.IAction#run()
386 final ISourceViewer viewer= getSourceViewer();
387 final IDocument document= viewer.getDocument();
389 fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document));
390 int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset());
394 int previous= findPreviousPosition(position);
395 if (isBlockSelectionModeEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) {
396 super.run(); // may navigate into virtual white space
397 } else if (previous != BreakIterator.DONE) {
398 setCaretPosition(previous);
399 getTextWidget().showSelection();
400 fireSelectionChanged();
402 } catch (BadLocationException x) {
403 // ignore - getLineOfOffset failed
409 * Finds the previous position before the given position.
411 * @param position the current position
412 * @return the previous position
414 protected int findPreviousPosition(int position) {
415 ISourceViewer viewer= getSourceViewer();
417 int previous= position;
418 while (previous != BreakIterator.DONE && widget == -1) { // XXX: optimize
419 previous= fIterator.preceding(previous);
420 if (previous != BreakIterator.DONE)
421 widget= modelOffset2WidgetOffset(viewer, previous);
424 IDocument document= viewer.getDocument();
425 LinkedModeModel model= LinkedModeModel.getModel(document, position);
426 if (model != null && previous != BreakIterator.DONE) {
427 LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0));
428 if (linkedPosition != null) {
429 int linkedPositionOffset= linkedPosition.getOffset();
430 if (position != linkedPositionOffset && previous < linkedPositionOffset)
431 previous= linkedPositionOffset;
433 LinkedPosition previousLinkedPosition= model.findPosition(new LinkedPosition(document, previous, 0));
434 if (previousLinkedPosition != null) {
435 int previousLinkedPositionEnd= previousLinkedPosition.getOffset() + previousLinkedPosition.getLength();
436 if (position != previousLinkedPositionEnd && previous < previousLinkedPositionEnd)
437 previous= previousLinkedPositionEnd;
446 * Sets the caret position to the sub-word boundary given with <code>position</code>.
448 * @param position Position where the action should move the caret
450 protected abstract void setCaretPosition(int position);
454 * Text navigation action to navigate to the previous sub-word.
458 protected class NavigatePreviousSubWordAction extends PreviousSubWordAction {
461 * Creates a new navigate previous sub-word action.
463 public NavigatePreviousSubWordAction() {
464 super(ST.WORD_PREVIOUS);
468 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
471 protected void setCaretPosition(final int position) {
472 getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position));
477 * Text operation action to delete the previous sub-word.
481 protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate {
484 * Creates a new delete previous sub-word action.
486 public DeletePreviousSubWordAction() {
487 super(ST.DELETE_WORD_PREVIOUS);
491 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
494 protected void setCaretPosition(int position) {
495 if (!validateEditorInputState())
499 final ISourceViewer viewer= getSourceViewer();
500 StyledText text= viewer.getTextWidget();
501 Point widgetSelection= text.getSelection();
502 if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) {
503 final int caret= text.getCaretOffset();
504 final int offset= modelOffset2WidgetOffset(viewer, position);
506 if (caret == widgetSelection.x)
507 text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y);
509 text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x);
510 text.invokeAction(ST.DELETE_PREVIOUS);
512 Point selection= viewer.getSelectedRange();
513 if (selection.y != 0) {
514 position= selection.x;
517 length= widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position;
521 viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$
522 } catch (BadLocationException exception) {
529 * @see org.eclipse.ui.texteditor.IUpdate#update()
531 public void update() {
532 setEnabled(isEditorInputModifiable());
537 * Text operation action to select the previous sub-word.
541 protected class SelectPreviousSubWordAction extends PreviousSubWordAction {
544 * Creates a new select previous sub-word action.
546 public SelectPreviousSubWordAction() {
547 super(ST.SELECT_WORD_PREVIOUS);
551 * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int)
554 protected void setCaretPosition(final int position) {
555 final ISourceViewer viewer= getSourceViewer();
557 final StyledText text= viewer.getTextWidget();
558 if (text != null && !text.isDisposed()) {
560 final Point selection= text.getSelection();
561 final int caret= text.getCaretOffset();
562 final int offset= modelOffset2WidgetOffset(viewer, position);
564 if (caret == selection.x)
565 text.setSelectionRange(selection.y, offset - selection.y);
567 text.setSelectionRange(selection.x, offset - selection.x);