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;
protected ResourceManager resourceManager;
public SCLModuleEditor2() {
super();
resourceManager = new LocalResourceManager(JFaceResources.getResources());
SCLSourceViewerConfigurationNew sourceViewerConfiguration = new SCLSourceViewerConfigurationNew(resourceManager);
setDocumentProvider(new SCLModuleEditor2DocumentProvider(sourceViewerConfiguration));
setSourceViewerConfiguration(sourceViewerConfiguration);
}
@Override
public boolean isTabsToSpacesConversionEnabled() {
return true;
}
@Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
super.init(site, input);
}
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
getEditorSite().getService(IContextService.class).activateContext("org.simantics.scl.ui.editor");
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
public void dispose() {
disposed = true;
super.dispose();
resourceManager.dispose();
}
public boolean isDisposed() {
return disposed;
}
public SCLTextEditorEnvironment getSCLTextEditorEnvironment() {
return ((SCLSourceViewerConfigurationNew)getSourceViewerConfiguration())
.getSclTextEditorEnvironment();
}
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);
}
}
}
}