X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scl.ui%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fui%2Feditor2%2FSCLModuleEditor2.java;h=0e068d69bd3568387a8ba73fbbe1599d741e15a3;hp=100f49e252f68fe6cc8e42b795b12acc0c9bf3b3;hb=40a091212c4702e861d504e8a1d8e9c2550dfaf6;hpb=06ee0c4c71cd9e372969da1570e7fcac2c4397a5 diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/SCLModuleEditor2.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/SCLModuleEditor2.java index 100f49e25..0e068d69b 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/SCLModuleEditor2.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/SCLModuleEditor2.java @@ -1,21 +1,50 @@ package org.simantics.scl.ui.editor2; +import java.text.CharacterIterator; + +import org.eclipse.jface.action.IAction; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.link.LinkedModeModel; +import org.eclipse.jface.text.link.LinkedPosition; +import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ST; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.editors.text.TextEditor; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; +import org.eclipse.ui.texteditor.IUpdate; +import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; +import org.eclipse.ui.texteditor.TextNavigationAction; import org.simantics.scl.ui.editor.SCLSourceViewerConfigurationNew; import org.simantics.scl.ui.editor.completion.SCLTextEditorEnvironment; +import org.simantics.scl.ui.editor2.iterator.DocumentCharacterIterator; +import org.simantics.scl.ui.editor2.iterator.JavaWordIterator; + +import com.ibm.icu.text.BreakIterator; public class SCLModuleEditor2 extends TextEditor { + + private static final char[] CHARS = new char[] { '(', ')', '{', '}', '[', ']', '<', '>' }; + + private static final String MATCHING_BRACKETS = "matchingBrackets"; + private static final String MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; + private static final String HIGHLIGHT_BRACKET_AT_CARET_LOCATION = "highlightBracketAtCaretLocation"; + private static final String ENCLOSING_BRACKETS = "enclosingBrackets"; + private boolean disposed = false; protected ResourceManager resourceManager; + private DefaultCharacterPairMatcher matcher; public SCLModuleEditor2() { super(); @@ -34,6 +63,10 @@ public class SCLModuleEditor2 extends TextEditor { public void init(IEditorSite site, IEditorInput input) throws PartInitException { super.init(site, input); + getPreferenceStore().setValue(MATCHING_BRACKETS, true); + getPreferenceStore().setValue(MATCHING_BRACKETS_COLOR, "192,192,192"); + getPreferenceStore().setValue(HIGHLIGHT_BRACKET_AT_CARET_LOCATION, true); + getPreferenceStore().setValue(ENCLOSING_BRACKETS, true); } @Override @@ -43,15 +76,52 @@ public class SCLModuleEditor2 extends TextEditor { updatePartName(); } + @Override + protected void createNavigationActions() { + super.createNavigationActions(); + + // Taken from org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.createNavigationActions() + final StyledText textWidget= getSourceViewer().getTextWidget(); + + IAction action = new NavigatePreviousSubWordAction(); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_PREVIOUS); + setAction(ITextEditorActionDefinitionIds.WORD_PREVIOUS, action); + textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_LEFT, SWT.NULL); + + action = new NavigateNextSubWordAction(); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_NEXT); + setAction(ITextEditorActionDefinitionIds.WORD_NEXT, action); + textWidget.setKeyBinding(SWT.CTRL | SWT.ARROW_RIGHT, SWT.NULL); + + action = new SelectPreviousSubWordAction(); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS); + setAction(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, action); + textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_LEFT, SWT.NULL); + + action = new SelectNextSubWordAction(); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT); + setAction(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT, action); + textWidget.setKeyBinding(SWT.CTRL | SWT.SHIFT | SWT.ARROW_RIGHT, SWT.NULL); + } + protected void updatePartName() { setPartName(getEditorInput().getName()); } + + @Override + protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { + matcher = new DefaultCharacterPairMatcher(CHARS); + support.setCharacterPairMatcher(matcher); + support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS); + super.configureSourceViewerDecorationSupport(support); + } @Override public void dispose() { disposed = true; super.dispose(); resourceManager.dispose(); + matcher.dispose(); } public boolean isDisposed() { @@ -66,4 +136,423 @@ public class SCLModuleEditor2 extends TextEditor { public IDocument getDocument() { return getSourceViewer().getDocument(); } + + /** + * Text navigation action to navigate to the next sub-word. + * + * @since 3.0 + */ + protected abstract class NextSubWordAction extends TextNavigationAction { + + protected JavaWordIterator fIterator= new JavaWordIterator(); + + /** + * Creates a new next sub-word action. + * + * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. + */ + protected NextSubWordAction(int code) { + super(getSourceViewer().getTextWidget(), code); + } + + /* + * @see org.eclipse.jface.action.IAction#run() + */ + @Override + public void run() { + final ISourceViewer viewer= getSourceViewer(); + final IDocument document= viewer.getDocument(); + try { + fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document)); + int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); + if (position == -1) + return; + + int next= findNextPosition(position); + if (isBlockSelectionModeEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) { + super.run(); // may navigate into virtual white space + } else if (next != BreakIterator.DONE) { + setCaretPosition(next); + getTextWidget().showSelection(); + fireSelectionChanged(); + } + } catch (BadLocationException x) { + // ignore + } + } + + /** + * Finds the next position after the given position. + * + * @param position the current position + * @return the next position + */ + protected int findNextPosition(int position) { + ISourceViewer viewer= getSourceViewer(); + int widget= -1; + int next= position; + while (next != BreakIterator.DONE && widget == -1) { // XXX: optimize + next= fIterator.following(next); + if (next != BreakIterator.DONE) + widget= modelOffset2WidgetOffset(viewer, next); + } + + IDocument document= viewer.getDocument(); + LinkedModeModel model= LinkedModeModel.getModel(document, position); + if (model != null && next != BreakIterator.DONE) { + LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0)); + if (linkedPosition != null) { + int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength(); + if (position != linkedPositionEnd && linkedPositionEnd < next) + next= linkedPositionEnd; + } else { + LinkedPosition nextLinkedPosition= model.findPosition(new LinkedPosition(document, next, 0)); + if (nextLinkedPosition != null) { + int nextLinkedPositionOffset= nextLinkedPosition.getOffset(); + if (position != nextLinkedPositionOffset && nextLinkedPositionOffset < next) + next= nextLinkedPositionOffset; + } + } + } + + return next; + } + + /** + * Sets the caret position to the sub-word boundary given with position. + * + * @param position Position where the action should move the caret + */ + protected abstract void setCaretPosition(int position); + } + + /** + * Text navigation action to navigate to the next sub-word. + * + * @since 3.0 + */ + protected class NavigateNextSubWordAction extends NextSubWordAction { + + /** + * Creates a new navigate next sub-word action. + */ + public NavigateNextSubWordAction() { + super(ST.WORD_NEXT); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(final int position) { + getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); + } + } + + /** + * Text operation action to delete the next sub-word. + * + * @since 3.0 + */ + protected class DeleteNextSubWordAction extends NextSubWordAction implements IUpdate { + + /** + * Creates a new delete next sub-word action. + */ + public DeleteNextSubWordAction() { + super(ST.DELETE_WORD_NEXT); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(final int position) { + if (!validateEditorInputState()) + return; + + final ISourceViewer viewer= getSourceViewer(); + StyledText text= viewer.getTextWidget(); + Point widgetSelection= text.getSelection(); + if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) { + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == widgetSelection.x) + text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); + else + text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); + text.invokeAction(ST.DELETE_NEXT); + } else { + Point selection= viewer.getSelectedRange(); + final int caret, length; + if (selection.y != 0) { + caret= selection.x; + length= selection.y; + } else { + caret= widgetOffset2ModelOffset(viewer, text.getCaretOffset()); + length= position - caret; + } + + try { + viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ + } catch (BadLocationException exception) { + // Should not happen + } + } + } + + /* + * @see org.eclipse.ui.texteditor.IUpdate#update() + */ + public void update() { + setEnabled(isEditorInputModifiable()); + } + } + + /** + * Text operation action to select the next sub-word. + * + * @since 3.0 + */ + protected class SelectNextSubWordAction extends NextSubWordAction { + + /** + * Creates a new select next sub-word action. + */ + public SelectNextSubWordAction() { + super(ST.SELECT_WORD_NEXT); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.NextSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(final int position) { + final ISourceViewer viewer= getSourceViewer(); + + final StyledText text= viewer.getTextWidget(); + if (text != null && !text.isDisposed()) { + + final Point selection= text.getSelection(); + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == selection.x) + text.setSelectionRange(selection.y, offset - selection.y); + else + text.setSelectionRange(selection.x, offset - selection.x); + } + } + } + + /** + * Text navigation action to navigate to the previous sub-word. + * + * @since 3.0 + */ + protected abstract class PreviousSubWordAction extends TextNavigationAction { + + protected JavaWordIterator fIterator= new JavaWordIterator(); + + /** + * Creates a new previous sub-word action. + * + * @param code Action code for the default operation. Must be an action code from @see org.eclipse.swt.custom.ST. + */ + protected PreviousSubWordAction(final int code) { + super(getSourceViewer().getTextWidget(), code); + } + + /* + * @see org.eclipse.jface.action.IAction#run() + */ + @Override + public void run() { + final ISourceViewer viewer= getSourceViewer(); + final IDocument document= viewer.getDocument(); + try { + fIterator.setText((CharacterIterator)new DocumentCharacterIterator(document)); + int position= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); + if (position == -1) + return; + + int previous= findPreviousPosition(position); + if (isBlockSelectionModeEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) { + super.run(); // may navigate into virtual white space + } else if (previous != BreakIterator.DONE) { + setCaretPosition(previous); + getTextWidget().showSelection(); + fireSelectionChanged(); + } + } catch (BadLocationException x) { + // ignore - getLineOfOffset failed + } + + } + + /** + * Finds the previous position before the given position. + * + * @param position the current position + * @return the previous position + */ + protected int findPreviousPosition(int position) { + ISourceViewer viewer= getSourceViewer(); + int widget= -1; + int previous= position; + while (previous != BreakIterator.DONE && widget == -1) { // XXX: optimize + previous= fIterator.preceding(previous); + if (previous != BreakIterator.DONE) + widget= modelOffset2WidgetOffset(viewer, previous); + } + + IDocument document= viewer.getDocument(); + LinkedModeModel model= LinkedModeModel.getModel(document, position); + if (model != null && previous != BreakIterator.DONE) { + LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, position, 0)); + if (linkedPosition != null) { + int linkedPositionOffset= linkedPosition.getOffset(); + if (position != linkedPositionOffset && previous < linkedPositionOffset) + previous= linkedPositionOffset; + } else { + LinkedPosition previousLinkedPosition= model.findPosition(new LinkedPosition(document, previous, 0)); + if (previousLinkedPosition != null) { + int previousLinkedPositionEnd= previousLinkedPosition.getOffset() + previousLinkedPosition.getLength(); + if (position != previousLinkedPositionEnd && previous < previousLinkedPositionEnd) + previous= previousLinkedPositionEnd; + } + } + } + + return previous; + } + + /** + * Sets the caret position to the sub-word boundary given with position. + * + * @param position Position where the action should move the caret + */ + protected abstract void setCaretPosition(int position); + } + + /** + * Text navigation action to navigate to the previous sub-word. + * + * @since 3.0 + */ + protected class NavigatePreviousSubWordAction extends PreviousSubWordAction { + + /** + * Creates a new navigate previous sub-word action. + */ + public NavigatePreviousSubWordAction() { + super(ST.WORD_PREVIOUS); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(final int position) { + getTextWidget().setCaretOffset(modelOffset2WidgetOffset(getSourceViewer(), position)); + } + } + + /** + * Text operation action to delete the previous sub-word. + * + * @since 3.0 + */ + protected class DeletePreviousSubWordAction extends PreviousSubWordAction implements IUpdate { + + /** + * Creates a new delete previous sub-word action. + */ + public DeletePreviousSubWordAction() { + super(ST.DELETE_WORD_PREVIOUS); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(int position) { + if (!validateEditorInputState()) + return; + + final int length; + final ISourceViewer viewer= getSourceViewer(); + StyledText text= viewer.getTextWidget(); + Point widgetSelection= text.getSelection(); + if (isBlockSelectionModeEnabled() && widgetSelection.y != widgetSelection.x) { + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == widgetSelection.x) + text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); + else + text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); + text.invokeAction(ST.DELETE_PREVIOUS); + } else { + Point selection= viewer.getSelectedRange(); + if (selection.y != 0) { + position= selection.x; + length= selection.y; + } else { + length= widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position; + } + + try { + viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ + } catch (BadLocationException exception) { + // Should not happen + } + } + } + + /* + * @see org.eclipse.ui.texteditor.IUpdate#update() + */ + public void update() { + setEnabled(isEditorInputModifiable()); + } + } + + /** + * Text operation action to select the previous sub-word. + * + * @since 3.0 + */ + protected class SelectPreviousSubWordAction extends PreviousSubWordAction { + + /** + * Creates a new select previous sub-word action. + */ + public SelectPreviousSubWordAction() { + super(ST.SELECT_WORD_PREVIOUS); + } + + /* + * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor.PreviousSubWordAction#setCaretPosition(int) + */ + @Override + protected void setCaretPosition(final int position) { + final ISourceViewer viewer= getSourceViewer(); + + final StyledText text= viewer.getTextWidget(); + if (text != null && !text.isDisposed()) { + + final Point selection= text.getSelection(); + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == selection.x) + text.setSelectionRange(selection.y, offset - selection.y); + else + text.setSelectionRange(selection.x, offset - selection.x); + } + } + } + }