package com.redhat.ceylon.eclipse.code.refactor; import static org.eclipse.core.commands.operations.OperationHistoryFactory.getOperationHistory; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.commands.operations.IUndoableOperation; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposal.DeleteBlockingExitPolicy; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IEditingSupport; import org.eclipse.jface.text.IUndoManager; import org.eclipse.jface.text.IUndoManagerExtension; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags; import org.eclipse.jface.text.link.LinkedPosition; import org.eclipse.jface.text.link.LinkedPositionGroup; import org.eclipse.swt.SWT; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Point; import com.redhat.ceylon.eclipse.code.editor.AbstractLinkedModeListener; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.editor.FocusEditingSupport; import com.redhat.ceylon.eclipse.util.LinkedMode; public abstract class AbstractLinkedMode { protected final CeylonEditor editor; private Point originalSelection; protected final LinkedModeModel linkedModeModel; private boolean showPreview = false; private IUndoableOperation startingUndoOperation; private RefactorInformationPopup infoPopup; private final boolean wasDirty; protected AbstractLinkedMode(CeylonEditor ceylonEditor) { editor = ceylonEditor; linkedModeModel = new LinkedModeModel(); wasDirty = ceylonEditor.isDirty(); } synchronized Point getOriginalSelection() { if (originalSelection == null) { originalSelection = editor.getCeylonSourceViewer() .getSelectedRange(); } return originalSelection; } protected RefactorInformationPopup getInfoPopup() { return infoPopup; } protected void openPopup() { // Must cache here, since editor context is not // available in menu from popup shell: infoPopup = new RefactorInformationPopup(editor, this); infoPopup.open(); } protected boolean isShowPreview() { return showPreview; } protected void enterLinkedMode(final IDocument document, int exitSequenceNumber, int exitPosition) throws BadLocationException { final IEditingSupport editingSupport = new FocusEditingSupport(editor) { public boolean ownsFocusShell() { return infoPopup != null && infoPopup.ownsFocusShell() || super.ownsFocusShell(); } }; LinkedMode.installLinkedMode(editor, linkedModeModel, this, exitSequenceNumber, exitPosition, editingSupport, createExitPolicy(document), new AbstractLinkedModeListener(editor, this) { @Override public void left(LinkedModeModel model, int flags) { editor.clearLinkedMode(); if (infoPopup != null) { infoPopup.close(); infoPopup=null; } editor.getSite() .getPage() .activate(editor); if ((flags&UPDATE_CARET)!=0) { done(); } else { if ((flags&EXTERNAL_MODIFICATION)==0) { editor.getCeylonSourceViewer() .invalidateTextPresentation(); } cancel(); } LinkedMode.unregisterEditingSupport( editor, editingSupport); } }); //NOTE: I hate this behavior in the Java editor! //viewer.setSelectedRange(originalSelection.x+adjust, originalSelection.y+adjust); // by default, full word is selected; restore original selection } DeleteBlockingExitPolicy createExitPolicy( final IDocument document) { return new DeleteBlockingExitPolicy(document) { @Override public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { showPreview = (event.stateMask & SWT.CTRL) != 0 && (event.character == SWT.CR || event.character == SWT.LF); return super.doExit(model, event, offset, length); } }; } protected int getExitPosition(int selectionOffset, int adjust) { return selectionOffset+adjust; } protected void cancel() {} protected void done() { if (!wasDirty || forceSave()) { editor.saveWithoutActions(); } } protected boolean forceSave() { return false; } boolean isCaretInLinkedPosition() { return getCurrentLinkedPosition() != null; } protected LinkedPosition getCurrentLinkedPosition() { Point selection = editor.getCeylonSourceViewer() .getSelectedRange(); Position pos = new Position(selection.x, selection.y); LinkedPositionGroup group = linkedModeModel.getGroupForPosition(pos); if (group!=null) { LinkedPosition[] positions = group .getPositions(); for (LinkedPosition position: positions) { if (position.includes(selection.x) && position.includes(selection.x+selection.y)) { return position; } } } return null; } protected abstract String getHintTemplate(); protected void addAdditionalMenuItems(IMenuManager manager) { IAction previewAction = createPreviewAction(); if (previewAction!=null) { previewAction.setAccelerator(SWT.CTRL | SWT.CR); previewAction.setEnabled(true); manager.add(previewAction); } IAction openDialogAction = createOpenDialogAction(); if (openDialogAction!=null) { manager.add(openDialogAction); } } protected Action createOpenDialogAction() { return null; } protected Action createPreviewAction() { return null; } protected void saveEditorState() { //save where we are before opening linked mode IUndoManager undoManager = editor.getCeylonSourceViewer() .getUndoManager(); if (undoManager instanceof IUndoManagerExtension) { IUndoManagerExtension undoManagerExtension = (IUndoManagerExtension) undoManager; IUndoContext undoContext = undoManagerExtension.getUndoContext(); IOperationHistory oh = getOperationHistory(); startingUndoOperation = oh.getUndoOperation(undoContext); } } protected void revertChanges() { //go back to where we were before opening linked mode try { editor.getSite() .getWorkbenchWindow() .run(false, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { IUndoManager undoManager = editor.getCeylonSourceViewer() .getUndoManager(); if (undoManager instanceof IUndoManagerExtension) { IUndoManagerExtension ext = (IUndoManagerExtension) undoManager; IUndoContext undoContext = ext.getUndoContext(); IOperationHistory oh = getOperationHistory(); while (undoManager.undoable()) { if (startingUndoOperation!=null && startingUndoOperation.equals( oh.getUndoOperation( undoContext))) { return; } undoManager.undo(); } } } }); } catch (Exception e) { e.printStackTrace(); } //make sure the AST is up dated before running the refactoring editor.getParseController() .parseAndTypecheck( editor.getCeylonSourceViewer().getDocument(), 10, new NullProgressMonitor(), null); } protected void updatePopupLocation() {} /*private void restoreFullSelection() { if (fOriginalSelection.y != 0) { int originalOffset= fOriginalSelection.x; LinkedPosition[] positions= fLinkedPositionGroup.getPositions(); for (int i= 0; i < positions.length; i++) { LinkedPosition position= positions[i]; if (! position.isDeleted() && position.includes(originalOffset)) { fEditor.getCeylonSourceViewer().setSelectedRange(position.offset, position.length); return; } } } }*/ }