/**
* Copyright (c) 2009, 2010 Mark Feber, MulgaSoft
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package com.mulgasoft.emacsplus;
import static com.mulgasoft.emacsplus.execute.RepeatCommandSupport.isRepeatCommand;
import static com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds.EMP_COPY;
import static com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds.METAX_EXECUTE;
import static com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds.YANK;
import static com.mulgasoft.emacsplus.IEmacsPlusCommandDefinitionIds.YANK_POP;
import java.util.HashMap;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IExecutionListener;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IMarkRegionTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import com.mulgasoft.emacsplus.commands.EmacsMovementHandler;
import com.mulgasoft.emacsplus.commands.EmacsPlusCmdHandler;
/**
* Utilities and Listeners associated with correcting the broken Eclipse
* behavior of Point and Mark
*
* @author Mark Feber - initial API and implementation
*/
@SuppressWarnings("serial")
public class MarkUtils {
private static final String MARK_SET = "Mark_Set"; //$NON-NLS-1$
private static IExecutionListener copyCmdExecListener;
private static IExecutionListener execExecListener;
private static IDocumentListener docListener = null;
private static String lastCommand = null;
private static String currentCommand = null;
private MarkUtils() {} // no children/instances allowed
// A hash of commands (and their inverse if appropriate) that should not clear the shift flag
private static HashMap<String, String> shiftMarkHash = new HashMap<String, String>() {
{
put(IEmacsPlusCommandDefinitionIds.FORWARD_CHAR, IEmacsPlusCommandDefinitionIds.BACKWARD_CHAR);
put(IEmacsPlusCommandDefinitionIds.BACKWARD_CHAR, IEmacsPlusCommandDefinitionIds.FORWARD_CHAR);
put(IEmacsPlusCommandDefinitionIds.FORWARD_WORD, IEmacsPlusCommandDefinitionIds.BACKWARD_WORD);
put(IEmacsPlusCommandDefinitionIds.BACKWARD_WORD, IEmacsPlusCommandDefinitionIds.FORWARD_WORD);
put(IEmacsPlusCommandDefinitionIds.MARK_FORWARD_SEXP, IEmacsPlusCommandDefinitionIds.MARK_BACKWARD_SEXP);
put(IEmacsPlusCommandDefinitionIds.SELECT_FORWARD_SEXP, IEmacsPlusCommandDefinitionIds.SELECT_BACKWARD_SEXP);
put(IEmacsPlusCommandDefinitionIds.MARK_BACKWARD_SEXP, IEmacsPlusCommandDefinitionIds.MARK_FORWARD_SEXP);
put(IEmacsPlusCommandDefinitionIds.SELECT_BACKWARD_SEXP, IEmacsPlusCommandDefinitionIds.SELECT_FORWARD_SEXP);
put(IEmacsPlusCommandDefinitionIds.SELECT_FORWARD_PARAGRAPH, IEmacsPlusCommandDefinitionIds.BACKWARD_MARK_PARAGRAPH);
put(IEmacsPlusCommandDefinitionIds.SELECT_BACKWARD_PARAGRAPH, IEmacsPlusCommandDefinitionIds.FORWARD_MARK_PARAGRAPH);
put(IEmacsPlusCommandDefinitionIds.FORWARD_MARK_PARAGRAPH, IEmacsPlusCommandDefinitionIds.BACKWARD_MARK_PARAGRAPH);
put(IEmacsPlusCommandDefinitionIds.BACKWARD_MARK_PARAGRAPH, IEmacsPlusCommandDefinitionIds.FORWARD_MARK_PARAGRAPH);
put(IEmacsPlusCommandDefinitionIds.FORWARD_BLOCK, IEmacsPlusCommandDefinitionIds.BACKWARD_BLOCK);
put(IEmacsPlusCommandDefinitionIds.BACKWARD_BLOCK, IEmacsPlusCommandDefinitionIds.FORWARD_BLOCK);
put(IEmacsPlusCommandDefinitionIds.NEXT_LINE, IEmacsPlusCommandDefinitionIds.PREVIOUS_LINE);
put(IEmacsPlusCommandDefinitionIds.PREVIOUS_LINE, IEmacsPlusCommandDefinitionIds.NEXT_LINE);
put(IEmacsPlusCommandDefinitionIds.BEGIN_LINE, IEmacsPlusCommandDefinitionIds.BEGIN_LINE);
put(IEmacsPlusCommandDefinitionIds.END_LINE, IEmacsPlusCommandDefinitionIds.END_LINE);
put(IEmacsPlusCommandDefinitionIds.SCROLL_UP, IEmacsPlusCommandDefinitionIds.SCROLL_DOWN);
put(IEmacsPlusCommandDefinitionIds.SCROLL_DOWN, IEmacsPlusCommandDefinitionIds.SCROLL_UP);
put(IEmacsPlusCommandDefinitionIds.SELECT_COLUMN_PREVIOUS,IEmacsPlusCommandDefinitionIds.SELECT_COLUMN_NEXT);
put(IEmacsPlusCommandDefinitionIds.SELECT_LINE_DOWN, IEmacsPlusCommandDefinitionIds.SELECT_LINE_UP);
put(IEmacsPlusCommandDefinitionIds.SELECT_WORD_PREVIOUS, IEmacsPlusCommandDefinitionIds.SELECT_WORD_NEXT);
put(IEmacsPlusCommandDefinitionIds.SELECT_LINE_START, IEmacsPlusCommandDefinitionIds.SELECT_LINE_END);
put(IEmacsPlusCommandDefinitionIds.SELECT_TEXT_START, IEmacsPlusCommandDefinitionIds.SELECT_TEXT_END);
put(IEmacsPlusCommandDefinitionIds.SELECT_LINE_END, IEmacsPlusCommandDefinitionIds.SELECT_LINE_START);
put(IEmacsPlusCommandDefinitionIds.SELECT_TEXT_END, IEmacsPlusCommandDefinitionIds.SELECT_TEXT_START);
put(IEmacsPlusCommandDefinitionIds.SELECT_COLUMN_NEXT,IEmacsPlusCommandDefinitionIds.SELECT_COLUMN_PREVIOUS);
put(IEmacsPlusCommandDefinitionIds.SELECT_LINE_UP, IEmacsPlusCommandDefinitionIds.SELECT_LINE_DOWN);
put(IEmacsPlusCommandDefinitionIds.SELECT_WORD_NEXT, IEmacsPlusCommandDefinitionIds.SELECT_WORD_PREVIOUS);
put(IEmacsPlusCommandDefinitionIds.SELECT_PAGE_UP, IEmacsPlusCommandDefinitionIds.SELECT_PAGE_DOWN);
put(IEmacsPlusCommandDefinitionIds.SELECT_PAGE_DOWN, IEmacsPlusCommandDefinitionIds.SELECT_PAGE_UP);
put(IEmacsPlusCommandDefinitionIds.BACK_TO_INDENT, IEmacsPlusCommandDefinitionIds.BACK_TO_INDENT);
put(IEmacsPlusCommandDefinitionIds.UNIVERSAL_ARGUMENT, IEmacsPlusCommandDefinitionIds.UNIVERSAL_ARGUMENT);
}
};
// A hash of commands (and their inverse if appropriate) that should not clear the mark flag
private static HashMap<String, String> markHash = new HashMap<String, String>(shiftMarkHash) {
{
put(IEmacsPlusCommandDefinitionIds.BEGIN_BUFFER, IEmacsPlusCommandDefinitionIds.END_BUFFER);
put(IEmacsPlusCommandDefinitionIds.END_BUFFER, IEmacsPlusCommandDefinitionIds.BEGIN_BUFFER);
// non-movement commands
put(IEmacsPlusCommandDefinitionIds.SET_MARK, IEmacsPlusCommandDefinitionIds.SET_MARK);
// NB: the command Meta-X calls, may clear the flag
put(IEmacsPlusCommandDefinitionIds.METAX_EXECUTE, IEmacsPlusCommandDefinitionIds.METAX_EXECUTE);
}
};
public static boolean isShiftCommand(String cmdId) {
return shiftMarkHash.containsKey(cmdId);
}
/**
* Return the inverse operation for the specified Emacs+ command Id.
* The inverse operation, if present, is used when a negative universal argument is detected.
*
* @param commandId
* @return the inverse of the command id or null
*/
public static String getInverseId(String commandId) {
String result = markHash.get(commandId);
if (result != null && result.equals(commandId)) {
result = null;
}
return result;
}
public static String getCurrentCommand() {
return currentCommand;
}
public static void setCurrentCommand(String commandId) {
currentCommand = commandId;
}
/**
* Get the current Mark position
*
* @param editor
* @return the Mark position in model coords (-1 if not set)
*/
public static int getMark(ITextEditor editor) {
int result = -1;
ITextViewerExtension ive = getITextViewer(editor);
if (ive != null) {
result = ive.getMark();
}
return result;
}
/**
* Set Mark at current cursor position and push the previous Mark on the
* Mark Ring
*
* @param editor
* @return the mark position in document coords
*/
public static int setMark(ITextEditor editor) {
IMarkRegionTarget markTarget = (IMarkRegionTarget) editor.getAdapter(IMarkRegionTarget.class);
int localMark = getMark(editor);
markTarget.setMarkAtCursor(true);
int newMark = getMark(editor);
MarkRing.addMark(editor, editor.getDocumentProvider().getDocument(editor.getEditorInput()), localMark, newMark);
EmacsPlusUtils.showMessage(editor, MARK_SET, false);
return newMark;
}
public static int setMark(ITextEditor editor, int offset) {
return setMark(editor, MarkUtils.getITextViewer(editor), offset, true);
}
/**
* Set Mark at offset, and potentially save the marks in the Mark Rings
*
* @param editor
* @param ve
* @param offset - the offset in document (absolute) coords
* @param save - true if we're (potentially) saving in the Mark Rings
* @return the mark position in document coords
*/
public static int setMark(ITextEditor editor, ITextViewerExtension ve, int offset, boolean save) {
int result = -1;
if (ve != null) {
int localMark = ve.getMark();
ve.setMark(offset);
result = ve.getMark();
if (save) {
MarkRing.addMark(editor, ve.getRewriteTarget().getDocument(), localMark, result);
EmacsPlusUtils.showMessage(editor, MARK_SET, false);
}
}
return result;
}
/**
* Pop the current buffer's Mark from the Mark Ring
*
* @param editor
* @return the offset of the Mark
*/
public static Position popMark(ITextEditor editor) {
return popMark(editor, editor.getDocumentProvider().getDocument(editor.getEditorInput()));
}
/**
* Pop the current buffers Mark from the Mark Ring
*
* @param editor
* @return the offset of the Mark
*/
public static Position popMark(ITextEditor editor, IDocument document) {
return MarkRing.popMark(document, getMark(editor));
}
/**
* Pop the current global Mark
*
* @return the location of the Mark
*/
public static IBufferLocation popGlobalMark(boolean norotate) {
return MarkRing.popGlobalMark(norotate);
}
/**
* Support simple clear mark/selection on TextConsole
*
* @param viewer
*/
public static void clearConsoleMark(TextConsoleViewer viewer) {
if (viewer != null) {
StyledText st = viewer.getTextWidget();
st.setSelection(st.getCaretOffset());
viewer.setMark(-1);
}
}
/**
* Get the selection point from the text widget
*
* @param editor
*
* @return the selection point iff it is a (Styled)Text widget
* x is the offset of the first selected character,
* y is the offset after the last selected character.
*/
public static int getCharCount(ITextEditor editor) {
int result = -1;
Control text = (Control) editor.getAdapter(Control.class);
if (text instanceof StyledText) {
result = ((StyledText) text).getCharCount();
} else if (text instanceof Text) {
result = ((Text) text).getCharCount();
}
return result;
}
/**
* Get the current caret offset in widget coords
*
* @param editor
* @return the caret position
*/
public static int getCaretOffset(ITextEditor editor) {
int result = 0;
Control text = getTextWidget(editor);
if (text instanceof StyledText) {
result = ((StyledText) text).getCaretOffset();
} else if (text instanceof Text) {
result = ((Text) text).getCaretPosition();
}
return result;
}
/**
* Get the current cursor offset in model coords
*
* @param editor
* @return the cursor position
*/
public static int getCursorOffset(ITextEditor editor) {
int result = 0;
int len = 0;
Control text = getTextWidget(editor);
if (text instanceof StyledText) {
result = ((StyledText) text).getCaretOffset();
len = ((StyledText) text).getCharCount();
} else if (text instanceof Text) {
result = ((Text) text).getCaretPosition();
}
ITextViewerExtension5 ve5;
try {
int off = result;
ve5 = (ITextViewerExtension5) MarkUtils.getITextViewer(editor);
result = ve5.widgetOffset2ModelOffset(result);
// end of buffer computations differ in different (non standard) viewers
// so, if out of bounds returned, back up one
if (result == -1 && off == len) {
result = ve5.widgetOffset2ModelOffset(off - 1);
}
} catch (Exception e) {
}
return result;
}
/**
* Set the cursor offset using the editors selection provider
*
* @param editor
* @param offset - in model coords
*/
public static void setCursorOffset(ITextEditor editor, int offset) {
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
setSelection(editor, new TextSelection(document, offset, 0));
}
// Note: ITextViewerExtension5 is not available on all TextViewers
public static int widget2ModelOffset(ISourceViewer viewer, int widgetOffset) {
int result = widgetOffset;
try {
if (viewer instanceof ITextViewerExtension5) {
result = ((ITextViewerExtension5) viewer).widgetOffset2ModelOffset(result);
}
} catch (Exception e) {
}
return result;
}
public static int model2WidgetOffset(ISourceViewer viewer, int modelOffset) {
int result = modelOffset;
try {
if (viewer instanceof ITextViewerExtension5) {
result = ((ITextViewerExtension5) viewer).modelOffset2WidgetOffset(result);
}
} catch (Exception e) {
}
return result;
}
public static void revealRange(ITextEditor editor, int start, int length) {
findSourceViewer(editor).revealRange(start, length);
}
/**
* @param editor
* @param start
* - in model coords
* @param length
* - in model coords
*/
public static ITextSelection setSelection(ITextEditor editor, int start, int length) {
TextViewer tv = findTextViewer(editor);
ITextSelection selection = new TextSelection(null, start, length);
if (tv != null) {
tv.setSelection(selection, false);
} else {
setSelection(editor, selection);
}
return selection;
}
/**
* Wrap selection provider setSelection within a block disabling highlight
* range to protect against the JavaEditor from changing the narrow focus in
* - org.eclipse.jdt.internal.ui.javaeditor.TogglePresentationAction -
*
* @param editor
* @param selection
* in model coords
*/
public static ITextSelection setSelection(ITextEditor editor, ITextSelection selection) {
boolean isNarrow = editor.showsHighlightRangeOnly();
// Use the text widget, as the IRewriteTarget has unpleasant scrolling side effects
Control text = getTextWidget(editor);
try {
text.setRedraw(false);
if (isNarrow) {
editor.showHighlightRangeOnly(false);
}
editor.getSelectionProvider().setSelection(selection);
} finally {
if (isNarrow) {
editor.showHighlightRangeOnly(isNarrow);
}
text.setRedraw(true);
}
return selection;
}
/**
* Get the widget associated with the editor
*
* @param editor
* @return a Control iff it is a (Styled)Text widget
*/
public static Control getTextWidget(ITextEditor editor) {
Control result = null;
Control text = (Control) editor.getAdapter(Control.class);
if (text instanceof StyledText) {
result = (StyledText) text;
} else if (text instanceof Text) {
result = (Text) text;
}
return result;
}
/**
* Get the selection point from the text widget
*
* @param editor
*
* @return the selection point iff it is a (Styled)Text widget
* x is the offset of the first selected character,
* y is the offset after the last selected character.
*/
public static Point getWidgetSelection(ITextEditor editor) {
Point result = null;
Control text = (Control) editor.getAdapter(Control.class);
if (text instanceof StyledText) {
result = ((StyledText) text).getSelection();
} else if (text instanceof Text) {
result = ((Text) text).getSelection();
}
return result;
}
/**
* set the selection in the text widget
*
* @param editor
* @param start
* - the start of selection in widget coords
* @param end
* - the end of selection in widget coords
*/
public static void setWidgetSelection(ITextEditor editor, int start, int end) {
Control text = (Control) editor.getAdapter(Control.class);
if (text instanceof StyledText) {
((StyledText) text).setSelection(start, end);
} else if (text instanceof Text) {
((Text) text).setSelection(start, end);
}
}
public static String getWidgetLineDelimiter(ITextEditor editor) {
String result = "\n"; //$NON-NLS-1$
Control w = (Control) editor.getAdapter(Control.class);
if (w instanceof StyledText) {
result = ((StyledText) w).getLineDelimiter();
} else if (w instanceof Text) {
result = ((Text) w).getLineDelimiter();
}
return result;
}
public static StyledText getStyledWidget(ITextEditor editor) {
StyledText result = null;
Control w = (Control) editor.getAdapter(Control.class);
if (w instanceof StyledText) {
result = (StyledText) w;
}
return result;
}
/*************************** Tag Mark handling ***************************/
/**
* Set the current tag Mark
*
* @return the location of the Mark
*/
public static int setTagMark(ITextEditor editor, int offset) {
int result = (offset == -1) ? setMark(editor) : setMark(editor,offset);
if (result!= -1) {
// add to the global tag mark ring
MarkRing.addTagMark(editor,editor.getDocumentProvider().getDocument(editor.getEditorInput()),result);
}
return result;
}
/**
* Pop the current tag Mark
*
* @return the location of the Mark
*/
public static IBufferLocation popTagMark() {
return MarkRing.popTagMark();
}
static IExecutionListener getTagListener() {
return tagMarker;
}
private static IExecutionListener tagMarker = new IExecutionListener() {
public void notHandled(String commandId, NotHandledException exception) { }
public void postExecuteFailure(String commandId, ExecutionException exception) { }
public void postExecuteSuccess(String commandId, Object returnValue) { }
public void preExecute(String commandId, ExecutionEvent event) {
try {
ITextEditor editor = EmacsPlusUtils.getActiveTextEditor(HandlerUtil.getActiveEditorChecked(event));
if (editor != null) {
MarkUtils.setTagMark(editor,-1);
}
} catch (ExecutionException e) {
// ignore any (unlikely) error
}
}
};
/*************************** Command state handling ***************************/
private static boolean ignoreDispatchId = false;
// We need to ignore commands we invoke from other commands
public static void setIgnoreDispatchId(boolean state) {
ignoreDispatchId = state;
}
public static interface ICommandIdListener {
void setCommandId(String commandId);
}
private static ListenerList commandIdListeners = new ListenerList();
public static void addCommandIdListener(ICommandIdListener listener) {
commandIdListeners.add(listener);
}
public static void removeCommandIdListener(ICommandIdListener listener) {
commandIdListeners.remove(listener);
}
/*************************** Enhanced Mark handling ***************************/
// Special command listeners to enforce emacs selection behavior in relation to mark
public static void addActivationListeners(ITextEditor editor) {
if (editor != null) {
addExecutionListeners(editor);
addDocumentListeners(editor);
}
}
public static void removeActivationListeners(ITextEditor editor) {
if (editor != null) {
removeExecutionListeners(editor);
removeDocumentListeners(editor);
}
}
public static String getLastCommandId() {
return lastCommand;
}
private static void setLastCommand(String commandId) {
// repeat commands (^X Z) should not change command state
if (!isRepeatCommand(commandId)) {
for (Object listener : commandIdListeners.getListeners()) {
if (!ignoreDispatchId) {
((ICommandIdListener)listener).setCommandId(commandId);
}
}
lastCommand = commandId;
}
}
// Copy command should clear mark & region
private static void addExecutionListeners(final ITextEditor editor) {
// handle multi-part editors which don't call deactivate for individual parts
MarkUtils.removeExecutionListeners(editor);
lastCommand = null; // initialize command id state
ICommandService ics = (ICommandService) editor.getSite().getService(ICommandService.class);
if (ics != null) {
// Add a listener for every command on this editor to clear the mark region
// when the text changes
execExecListener = new IExecutionListener() {
ITextEditor cEditor = editor;
public void notHandled(String commandId, NotHandledException exception) {
currentCommand = null;
}
public void postExecuteFailure(String commandId, ExecutionException exception) {
currentCommand = null;
}
public void postExecuteSuccess(String commandId, Object returnValue) {
if (EmacsPlusCmdHandler.isChanged) {
clearMarkRegion(cEditor);
EmacsPlusCmdHandler.isChanged = false;
}
if (!markHash.containsKey(commandId)) {
// clear the mark flag
EmacsPlusCmdHandler.setFlagMark(false);
}
// remember command id of last command executed
setLastCommand(commandId);
currentCommand = null;
}
public void preExecute(String commandId, ExecutionEvent event) {
if (notYank(commandId)) {
// Fix (possible regression?) to disallow yank-pop after non-yank
KillRing.getInstance().setYanked(false);
}
EmacsPlusCmdHandler.isChanged = false;
currentCommand = commandId;
}
private boolean notYank(String commandId) {
return (!(YANK.equals(commandId) || YANK_POP.equals(commandId)|| METAX_EXECUTE.equals(commandId)));
}
};
ics.addExecutionListener(execExecListener);
Command com = ics.getCommand(EMP_COPY);
if (com != null) {
// Add a listener to COPY command to always clear the mark region
copyCmdExecListener = new IExecutionListener() {
ITextEditor cEditor = editor;
public void notHandled(String commandId, NotHandledException exception) {
}
public void postExecuteFailure(String commandId, ExecutionException exception) {
}
public void postExecuteSuccess(String commandId, Object returnValue) {
clearMarkRegion(cEditor);
}
public void preExecute(String commandId, ExecutionEvent event) {
}
};
com.addExecutionListener(copyCmdExecListener);
}
}
}
private static void removeExecutionListeners(ITextEditor editor) {
ICommandService ics = (ICommandService) editor.getSite().getService(ICommandService.class);
if (ics != null) {
if (execExecListener != null) {
ics.removeExecutionListener(execExecListener);
}
if (copyCmdExecListener != null) {
Command com = ics.getCommand(EMP_COPY);
if (com != null) {
com.removeExecutionListener(copyCmdExecListener);
}
}
}
copyCmdExecListener = null;
execExecListener = null;
}
// If the document changes, flag it for the command listener
private static void addDocumentListeners(ITextEditor editor) {
// handle multi-part editors which don't call deactivate for individual parts
MarkUtils.removeDocumentListeners(editor);
IDocumentProvider idp = editor.getDocumentProvider();
IDocument document;
// add null check for document, due to an unreproducible NPE reported by a clojure user
if (idp != null && (document = idp.getDocument(editor.getEditorInput())) != null) {
docListener = new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent event) {
}
public void documentChanged(DocumentEvent event) {
EmacsPlusCmdHandler.isChanged = true;
}
};
document.addDocumentListener(docListener);
}
}
private static void removeDocumentListeners(ITextEditor editor) {
if (docListener != null) {
IDocumentProvider idp = editor.getDocumentProvider();
IDocument document;
// add null check for document, due to an unreproducible NPE reported by a clojure user
if (idp != null && (document = idp.getDocument(editor.getEditorInput())) != null) {
document.removeDocumentListener(docListener);
}
}
docListener = null;
}
private static void clearMarkRegion(ITextEditor editor) {
int markOffset = getMark(editor);
if (markOffset != -1) {
// clear shift flag as well
EmacsMovementHandler.clearShifted();
ISelectionProvider isp = editor.getSelectionProvider();
ITextSelection sel = (ITextSelection) isp.getSelection();
if (sel.getOffset() <= markOffset && markOffset <= sel.getOffset() + sel.getLength()) {
int offset = getCursorOffset(editor);
// clear the selection
setCursorOffset(editor, offset);
}
}
}
public static int model2WidgetOffset(ITextEditor editor, int pos) {
return MarkUtils.model2WidgetOffset(findSourceViewer(editor), pos);
}
// Totally Evil
// The protected method & private field that gives us the editor viewer for registration purposes
private static String RE_METHOD_ID = "getSourceViewer"; //$NON-NLS-1$
private static String RE_MEMBER_ID = "fSourceViewer"; //$NON-NLS-1$
public static ITextViewerExtension getITextViewer(ITextEditor editor) {
ITextViewerExtension result = null;
ISourceViewer viewer = findSourceViewer(editor);
if ((viewer instanceof ITextViewerExtension)) {
result = ((ITextViewerExtension) viewer);
}
return result;
}
private static ISourceViewer findSourceViewer(ITextEditor editor) {
// evil
ISourceViewer result = null;
if (editor != null && editor instanceof AbstractTextEditor) {
result = (ISourceViewer) EmacsPlusUtils.getAM((AbstractTextEditor) editor, RE_METHOD_ID);
if (result == null) {
// even more evil
result = (ISourceViewer) EmacsPlusUtils.getAF((AbstractTextEditor) editor, RE_MEMBER_ID);
}
}
return result;
}
private static TextViewer findTextViewer(ITextEditor editor) {
// evil
TextViewer result = null;
ISourceViewer sv = null;
if (editor != null && editor instanceof AbstractTextEditor) {
sv = (ISourceViewer) EmacsPlusUtils.getAM((AbstractTextEditor) editor, RE_METHOD_ID);
if (sv == null) {
// even more evil
sv = (ISourceViewer) EmacsPlusUtils.getAF((AbstractTextEditor) editor, RE_MEMBER_ID);
}
}
if (sv instanceof TextViewer) {
result = (TextViewer) sv;
}
return result;
}
}