]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/editor2/SCLModuleEditor2.java
Implementing Java camelCase breaking in SCL module editor
[simantics/platform.git] / bundles / org.simantics.scl.ui / src / org / simantics / scl / ui / editor2 / SCLModuleEditor2.java
index 100f49e252f68fe6cc8e42b795b12acc0c9bf3b3..05015bb0b36dfeba8cee5afa2b44d3e11bd8d527 100644 (file)
@@ -1,17 +1,35 @@
 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.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.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 boolean disposed = false;
@@ -43,6 +61,34 @@ 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());
     }
@@ -66,4 +112,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 <code>position</code>.
+         *
+         * @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 <code>position</code>.
+         *
+         * @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);
+            }
+        }
+    }
+
 }