package org.rubypeople.rdt.internal.ui.rubyeditor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISelectionValidator; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.ITextInputListener; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.IWidgetTokenKeeper; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.contentassist.ContentAssistant; import org.eclipse.jface.text.contentassist.IContentAssistant; import org.eclipse.jface.text.link.LinkedModeModel; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.ListenerList; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPartService; import org.eclipse.ui.IWindowListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; import org.eclipse.ui.views.contentoutline.ContentOutline; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.jruby.ast.RootNode; import org.osgi.service.prefs.BackingStoreException; import org.rubypeople.rdt.core.IImportContainer; import org.rubypeople.rdt.core.IImportDeclaration; import org.rubypeople.rdt.core.IMember; import org.rubypeople.rdt.core.IRubyElement; import org.rubypeople.rdt.core.IRubyProject; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.ISourceFolder; import org.rubypeople.rdt.core.ISourceFolderRoot; import org.rubypeople.rdt.core.ISourceRange; import org.rubypeople.rdt.core.ISourceReference; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.internal.core.ExternalRubyScript; import org.rubypeople.rdt.internal.ui.RubyPlugin; import org.rubypeople.rdt.internal.ui.rubyeditor.RubyEditor.ITextConverter; import org.rubypeople.rdt.internal.ui.search.IOccurrencesFinder; import org.rubypeople.rdt.internal.ui.search.OccurrencesFinder; import org.rubypeople.rdt.internal.ui.text.ContentAssistPreference; import org.rubypeople.rdt.internal.ui.text.IRubyPartitions; import org.rubypeople.rdt.internal.ui.text.PreferencesAdapter; import org.rubypeople.rdt.internal.ui.text.RubyPairMatcher; import org.rubypeople.rdt.internal.ui.text.RubyWordFinder; import org.rubypeople.rdt.internal.ui.viewsupport.ISelectionListenerWithAST; import org.rubypeople.rdt.internal.ui.viewsupport.SelectionListenerWithASTManager; import org.rubypeople.rdt.ui.PreferenceConstants; import org.rubypeople.rdt.ui.RubyUI; import org.rubypeople.rdt.ui.rubyeditor.ICustomRubyOutlinePage; import org.rubypeople.rdt.ui.text.RubySourceViewerConfiguration; import org.rubypeople.rdt.ui.text.RubyTextTools; public abstract class RubyAbstractEditor extends TextEditor { private static final boolean CODE_ASSIST_DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.rubypeople.rdt.ui/debug/ResultCollector")); //$NON-NLS-1$//$NON-NLS-2$ protected RubyTextTools textTools; private ISourceReference reference; /** Outliner context menu Id */ protected String fOutlinerContextMenuId; /** The selection changed listener */ protected AbstractSelectionChangedListener fOutlineSelectionChangedListener = new OutlineSelectionChangedListener(); private ICustomRubyOutlinePage fOutlinePage; /** Preference key for matching brackets */ protected final static String MATCHING_BRACKETS= PreferenceConstants.EDITOR_MATCHING_BRACKETS; /** Preference key for matching brackets color */ protected final static String MATCHING_BRACKETS_COLOR= PreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; protected final static char[] BRACKETS= { '{', '}', '(', ')', '[', ']' }; private static final String CUSTOM_OUTLINE_EXTPOINT_ID = "customRubyOutlinePages"; /** The editor's bracket matcher */ protected RubyPairMatcher fBracketMatcher= new RubyPairMatcher(BRACKETS); /** * Holds the current occurrence annotations. * @since 3.0 */ private Annotation[] fOccurrenceAnnotations= null; /** * Tells whether all occurrences of the element at the * current caret location are automatically marked in * this editor. * @since 3.0 */ private boolean fMarkOccurrenceAnnotations; /** * Tells whether the occurrence annotations are sticky * i.e. whether they stay even if there's no valid Java * element at the current caret position. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fStickyOccurrenceAnnotations; /** * Tells whether to mark type occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkTypeOccurrences; /** * Tells whether to mark method occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkMethodOccurrences; /** * Tells whether to mark constant occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkConstantOccurrences; /** * Tells whether to mark field occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkFieldOccurrences; /** * Tells whether to mark local variable occurrences in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkLocalVariableOccurrences; /** * Tells whether to mark method exits in this editor. * Only valid if {@link #fMarkOccurrenceAnnotations} is <code>true</code>. * @since 3.0 */ private boolean fMarkMethodExitPoints; /** * The selection used when forcing occurrence marking * through code. * @since 3.0 */ private ISelection fForcedMarkOccurrencesSelection; /** * The document modification stamp at the time when the last * occurrence marking took place. * @since 3.1 */ //TODO: Do I need to use this? private long fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; /** * The internal shell activation listener for updating occurrences. */ private ActivationListener fActivationListener= new ActivationListener(); private ISelectionListenerWithAST fPostSelectionListenerWithAST; private OccurrencesFinderJob fOccurrencesFinderJob; /** The occurrences finder job canceler */ private OccurrencesFinderJobCanceler fOccurrencesFinderJobCanceler; private IRegion fMarkOccurrenceTargetRegion; /** * Creates and returns the preference store for this Ruby editor with the given input. * * @param input The editor input for which to create the preference store * @return the preference store for this editor * * @since 0.9.0 */ private IPreferenceStore createCombinedPreferenceStore(IEditorInput input) { List stores= new ArrayList(3); IRubyProject project= EditorUtility.getRubyProject(input); if (project != null) { stores.add(new EclipsePreferencesAdapter(new ProjectScope(project.getProject()), RubyCore.PLUGIN_ID)); } stores.add(RubyPlugin.getDefault().getPreferenceStore()); stores.add(new PreferencesAdapter(RubyCore.getPlugin().getPluginPreferences())); stores.add(EditorsUI.getPreferenceStore()); return new ChainedPreferenceStore((IPreferenceStore[]) stores.toArray(new IPreferenceStore[stores.size()])); } /** * Informs the editor that its outliner has been closed. */ public void outlinePageClosed() { if (fOutlinePage != null) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage = null; resetHighlightRange(); } } protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { support.setCharacterPairMatcher(fBracketMatcher); support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR); super.configureSourceViewerDecorationSupport(support); } protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { fAnnotationAccess = createAnnotationAccess(); fOverviewRuler = createOverviewRuler(getSharedColors()); IPreferenceStore store= getPreferenceStore(); ISourceViewer viewer = createRubySourceViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles, store); // ensure decoration support has been created and configured. getSourceViewerDecorationSupport(viewer); return viewer; } protected ISourceViewer createRubySourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean isOverviewRulerVisible, int styles, IPreferenceStore store) { return new AdaptedSourceViewer(parent, verticalRuler, overviewRuler, isOverviewRulerVisible, styles, store); } /** * Sets the outliner's context menu ID. * * @param menuId * the menu ID */ protected void setOutlinerContextMenuId(String menuId) { fOutlinerContextMenuId = menuId; } protected void initializeEditor() { super.initializeEditor(); IPreferenceStore store= createCombinedPreferenceStore(null); setPreferenceStore(store); RubyTextTools textTools= RubyPlugin.getDefault().getRubyTextTools(); setSourceViewerConfiguration(new RubySourceViewerConfiguration(textTools.getColorManager(), store, this, IRubyPartitions.RUBY_PARTITIONING)); fMarkOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_MARK_OCCURRENCES); fStickyOccurrenceAnnotations= store.getBoolean(PreferenceConstants.EDITOR_STICKY_OCCURRENCES); fMarkTypeOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES); fMarkMethodOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES); fMarkConstantOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES); fMarkFieldOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_FIELD_OCCURRENCES); fMarkLocalVariableOccurrences= store.getBoolean(PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES); fMarkMethodExitPoints= store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS); } /* * @see org.eclipse.ui.texteditor.AbstractTextEditor#setPreferenceStore(org.eclipse.jface.preference.IPreferenceStore) * @since 0.9.0 */ protected void setPreferenceStore(IPreferenceStore store) { super.setPreferenceStore(store); if (getSourceViewerConfiguration() instanceof RubySourceViewerConfiguration) { RubyTextTools textTools= RubyPlugin.getDefault().getRubyTextTools(); setSourceViewerConfiguration(new RubySourceViewerConfiguration(textTools.getColorManager(), store, this, IRubyPartitions.RUBY_PARTITIONING)); } if (getSourceViewer() instanceof RubySourceViewer) ((RubySourceViewer)getSourceViewer()).setPreferenceStore(store); } /* * (non-Javadoc) * * @see org.eclipse.ui.texteditor.ExtendedTextEditor#dispose() */ public void dispose() { super.dispose(); // cancel possible running computation fMarkOccurrenceAnnotations= false; uninstallOccurrencesFinder(); if (fActivationListener != null) { PlatformUI.getWorkbench().removeWindowListener(fActivationListener); fActivationListener= null; } } public Object getAdapter(Class required) { if (IContentOutlinePage.class.equals(required)) { if (fOutlinePage == null) fOutlinePage = createRubyOutlinePage(); return fOutlinePage; } return super.getAdapter(required); } public void createPartControl(Composite parent) { super.createPartControl(parent); if (fMarkOccurrenceAnnotations) installOccurrencesFinder(false); } protected ICustomRubyOutlinePage createRubyOutlinePage() { IRubyElement element = getInputRubyElement(); ICustomRubyOutlinePage outlinePage = null; List<ICustomRubyOutlinePage> customPages = getCustomOutlines(); for (ICustomRubyOutlinePage customRubyOutlinePage : customPages) { if (customRubyOutlinePage.isEnabled(element)) { customRubyOutlinePage.init(fOutlinerContextMenuId, this); outlinePage = (RubyOutlinePage) customRubyOutlinePage; break; } } if (outlinePage == null) { outlinePage = new RubyOutlinePage(); outlinePage.init(fOutlinerContextMenuId, this); } fOutlineSelectionChangedListener.install(outlinePage); setOutlinePageInput(outlinePage, getEditorInput()); return outlinePage; } private List<ICustomRubyOutlinePage> getCustomOutlines() { List<ICustomRubyOutlinePage> list = new ArrayList<ICustomRubyOutlinePage>(); IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(RubyPlugin.PLUGIN_ID, CUSTOM_OUTLINE_EXTPOINT_ID); if (extension == null) return list; IExtension[] extensions = extension.getExtensions(); // for all extensions of this point... for(int i = 0; i < extensions.length; i++) { IConfigurationElement[] configElements = extensions[i].getConfigurationElements(); // for all config elements named "page" for(int j = 0; j < configElements.length; j++) { final IConfigurationElement configElement = configElements[j]; String elementName =configElement.getName(); if (!("page".equals(elementName))) { //$NON-NLS-1$ continue; } try { ICustomRubyOutlinePage customPage = (ICustomRubyOutlinePage) configElement.createExecutableExtension("class"); list.add(customPage); } catch (CoreException e) { RubyPlugin.log(e); } } } return list; } /* * (non-Javadoc) * * @see org.eclipse.ui.editors.text.TextEditor#doSetInput(org.eclipse.ui.IEditorInput) */ protected void doSetInput(IEditorInput input) throws CoreException { // HACK Ugly code here in the beginning to handle Eclipse 3.2 and 3.3 opening external files. We wrap them into our own external ruby editor.. IPath path = null; if (externalFileOpenedInEclipse32(input)) { // Eclipse 3.2 uses this editor input for external files... try { Method method = input.getClass().getDeclaredMethod("getPath", new Class[0]); if (method != null) { path = (IPath) method.invoke(input, new Object[0]); } } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else if (externalFileOpenedInEclipse33(input)) { // Eclipse 3.3 uses this editor input for external files... try { Method method = input.getClass().getDeclaredMethod("getURI", new Class[0]); if (method != null) { URI uri = (URI) method.invoke(input, new Object[0]); if (uri != null && uri.getScheme().equals("file")) { path = new Path(uri.getPath()); } } } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (path != null && (externalFileOpenedInEclipse32(input) || externalFileOpenedInEclipse33(input))) { IProject[] projects = RubyCore.getRubyProjects(); if (projects != null && projects.length > 0) { // search for the file in one of the ruby projects loadpaths IRubyProject proj = RubyCore.create(projects[0]); ISourceFolderRoot root = proj.getSourceFolderRoot(path.removeLastSegments(1).toPortableString()); ISourceFolder folder = root.getSourceFolder(""); IRubyScript script = folder.getRubyScript(path.lastSegment()); // FIXME Check to see if it exists? input = new RubyScriptEditorInput((ExternalRubyScript) script); } } if (input instanceof IRubyScriptEditorInput) { setDocumentProvider(RubyPlugin.getDefault().getExternalDocumentProvider()); } else { setDocumentProvider(RubyPlugin.getDefault().getRubyDocumentProvider()); } super.doSetInput(input); setOutlinePageInput(fOutlinePage, input); } private boolean externalFileOpenedInEclipse33(IEditorInput input) { return input.getClass().getName().equals("org.eclipse.ui.ide.FileStoreEditorInput"); } private boolean externalFileOpenedInEclipse32(IEditorInput input) { return input.getClass().getName().equals("org.eclipse.ui.internal.editors.text.JavaFileEditorInput"); } protected void setOutlinePageInput(ICustomRubyOutlinePage page, IEditorInput input) { if (page == null) return; IRubyElement re= getInputRubyElement(); if (re != null && re.exists()) page.setInput(re); else page.setInput(null); } protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { String property= event.getProperty(); if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH.equals(property)) { /* * Ignore tab setting since we rely on the formatter preferences. * We do this outside the try-finally block to avoid that EDITOR_TAB_WIDTH * is handled by the sub-class (AbstractDecoratedTextEditor). */ return; } try { boolean newBooleanValue= false; Object newValue= event.getNewValue(); if (newValue != null) newBooleanValue= Boolean.valueOf(newValue.toString()).booleanValue(); if (PreferenceConstants.EDITOR_MARK_OCCURRENCES.equals(property)) { if (newBooleanValue != fMarkOccurrenceAnnotations) { fMarkOccurrenceAnnotations= newBooleanValue; if (!fMarkOccurrenceAnnotations) uninstallOccurrencesFinder(); else installOccurrencesFinder(true); } return; } if (PreferenceConstants.EDITOR_MARK_TYPE_OCCURRENCES.equals(property)) { fMarkTypeOccurrences= newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_METHOD_OCCURRENCES.equals(property)) { fMarkMethodOccurrences= newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_CONSTANT_OCCURRENCES.equals(property)) { fMarkConstantOccurrences= newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_FIELD_OCCURRENCES.equals(property)) { fMarkFieldOccurrences= newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES.equals(property)) { fMarkLocalVariableOccurrences= newBooleanValue; return; } if (PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS.equals(property)) { fMarkMethodExitPoints= newBooleanValue; return; } if (PreferenceConstants.EDITOR_STICKY_OCCURRENCES.equals(property)) { fStickyOccurrenceAnnotations= newBooleanValue; return; } AdaptedSourceViewer sourceViewer= (AdaptedSourceViewer) getSourceViewer(); if (sourceViewer == null) return; ((RubySourceViewerConfiguration)getSourceViewerConfiguration()).handlePropertyChangeEvent(event); IContentAssistant c= sourceViewer.getContentAssistant(); if (c instanceof ContentAssistant) ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event); } finally { super.handlePreferenceStoreChanged(event); } if (AbstractDecoratedTextEditorPreferenceConstants.SHOW_RANGE_INDICATOR.equals(property)) { // superclass already installed the range indicator Object newValue= event.getNewValue(); ISourceViewer viewer= getSourceViewer(); if (newValue != null && viewer != null) { if (Boolean.valueOf(newValue.toString()).booleanValue()) { // adjust the highlightrange in order to get the magnet right after changing the selection Point selection= viewer.getSelectedRange(); adjustHighlightRange(selection.x, selection.y); } } } } /** * Returns the Ruby element wrapped by this editors input. * * @return the Ruby element wrapped by this editors input. * @since 3.0 */ protected IRubyElement getInputRubyElement() { IEditorInput editorInput= getEditorInput(); if (editorInput == null) return null; return RubyUI.getEditorInputRubyElement(getEditorInput()); } protected void handleOutlinePageSelection(SelectionChangedEvent event) { StructuredSelection selection = (StructuredSelection) event.getSelection(); Iterator iter = ((IStructuredSelection) selection).iterator(); while (iter.hasNext()) { Object o = iter.next(); if (o instanceof ISourceReference) { reference = (ISourceReference) o; break; } } if (!isActivePart() && RubyPlugin.getActivePage() != null) RubyPlugin.getActivePage().bringToTop(this); // setSelection(reference, !isActivePart()); setSelection(reference, true); } protected boolean isActivePart() { IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } private IWorkbenchPart getActivePart() { IWorkbenchWindow window = getSite().getWorkbenchWindow(); IPartService service = window.getPartService(); IWorkbenchPart part = service.getActivePart(); return part; } public void setSelection(IRubyElement element) { if (element == null || element instanceof IRubyScript) { /* * If the element is an IRubyScript this unit is either the input of * this editor or not being displayed. In both cases, nothing should * happened. (http://dev.eclipse.org/bugs/show_bug.cgi?id=5128) */ return; } IRubyElement corresponding = getCorrespondingElement(element); if (corresponding instanceof ISourceReference) { ISourceReference reference = (ISourceReference) corresponding; // set highlight range setSelection(reference, true); // set outliner selection if (fOutlinePage != null) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage.select(reference); fOutlineSelectionChangedListener.install(fOutlinePage); } } } protected IRubyElement getCorrespondingElement(IRubyElement element) { // TODO: With new working copy story: original == working copy. // Note that the previous code could result in a reconcile as side // effect. Should check if that // is still required. return element; } /** * Synchronizes the outliner selection with the given element position in * the editor. * * @param element * the java element to select * @param checkIfOutlinePageActive * <code>true</code> if check for active outline page needs to * be done */ protected void synchronizeOutlinePage(ISourceReference element, boolean checkIfOutlinePageActive) { if (fOutlinePage != null && element != null && !(checkIfOutlinePageActive && isRubyOutlinePageActive())) { fOutlineSelectionChangedListener.uninstall(fOutlinePage); fOutlinePage.select(element); fOutlineSelectionChangedListener.install(fOutlinePage); } } private boolean isRubyOutlinePageActive() { IWorkbenchPart part = getActivePart(); return part instanceof ContentOutline && ((ContentOutline) part).getCurrentPage() == fOutlinePage; } protected void setSelection(ISourceReference reference, boolean moveCursor) { if (getSelectionProvider() == null) return; ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof TextSelection) { TextSelection textSelection = (TextSelection) selection; // PR 39995: [navigation] Forward history cleared after going back // in navigation history: // mark only in navigation history if the cursor is being moved // (which it isn't if // this is called from a PostSelectionEvent that should only update // the magnet) if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) markInNavigationHistory(); } if (reference != null) { StyledText textWidget = null; ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer != null) textWidget = sourceViewer.getTextWidget(); if (textWidget == null) return; try { ISourceRange range = null; // if (reference instanceof ILocalVariable) { // IRubyElement je = ((ILocalVariable) reference).getParent(); // if (je instanceof ISourceReference) range = // ((ISourceReference) je).getSourceRange(); // } else range = reference.getSourceRange(); if (range == null) return; int offset = range.getOffset(); int length = range.getLength(); if (offset < 0 || length < 0) return; setHighlightRange(offset, length, moveCursor); if (!moveCursor) return; offset = -1; length = -1; if (reference instanceof IMember) { range = ((IMember) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } // } else if (reference instanceof ILocalVariable) { // range= ((ILocalVariable)reference).getNameRange(); // if (range != null) { // offset= range.getOffset(); // length= range.getLength(); // } } else if (reference instanceof IImportDeclaration) { String name = ((IImportDeclaration) reference).getElementName(); if (name != null && name.length() > 0) { String content = reference.getSource(); if (content != null) { offset = range.getOffset() + content.indexOf(name); length = name.length(); } } } if (offset > -1 && length > 0) { try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } markInNavigationHistory(); } } catch (RubyModelException x) { } catch (IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } protected boolean affectsTextPresentation(PropertyChangeEvent event) { return ((RubySourceViewerConfiguration)getSourceViewerConfiguration()).affectsTextPresentation(event) || super.affectsTextPresentation(event); } protected void doSelectionChanged(SelectionChangedEvent event) { ISourceReference reference = null; ISelection selection = event.getSelection(); Iterator iter = ((IStructuredSelection) selection).iterator(); while (iter.hasNext()) { Object o = iter.next(); if (o instanceof ISourceReference) { reference = (ISourceReference) o; break; } } if (!isActivePart() && RubyPlugin.getActivePage() != null) RubyPlugin.getActivePage().bringToTop(this); setSelection(reference, !isActivePart()); } class OutlineSelectionChangedListener extends AbstractSelectionChangedListener { public void selectionChanged(SelectionChangedEvent event) { boolean isLinkingEnabled = PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE); if (isLinkingEnabled) { doSelectionChanged(event); } } } /** * Computes and returns the source reference that includes the caret and * serves as provider for the outline page selection and the editor range * indication. * * @return the computed source reference * @since 3.0 */ protected ISourceReference computeHighlightRangeSourceReference() { ISourceViewer sourceViewer= getSourceViewer(); if (sourceViewer == null) return null; StyledText styledText= sourceViewer.getTextWidget(); if (styledText == null) return null; int caret= 0; if (sourceViewer instanceof ITextViewerExtension5) { ITextViewerExtension5 extension= (ITextViewerExtension5)sourceViewer; caret= extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); } else { int offset= sourceViewer.getVisibleRegion().getOffset(); caret= offset + styledText.getCaretOffset(); } IRubyElement element= getElementAt(caret, false); if ( !(element instanceof ISourceReference)) return null; if (element.getElementType() == IRubyElement.IMPORT_DECLARATION) { IImportDeclaration declaration= (IImportDeclaration) element; IImportContainer container= (IImportContainer) declaration.getParent(); ISourceRange srcRange= null; try { srcRange= container.getSourceRange(); } catch (RubyModelException e) { } if (srcRange != null && srcRange.getOffset() == caret) return container; } return (ISourceReference) element; } protected abstract IRubyElement getElementAt(int caret, boolean b); protected abstract IRubyElement getElementAt(int offset); public final ISourceViewer getViewer() { return getSourceViewer(); } /** * Adapts an options {@link IEclipsePreferences} to {@link org.eclipse.jface.preference.IPreferenceStore}. * <p> * This preference store is read-only i.e. write access * throws an {@link java.lang.UnsupportedOperationException}. * </p> * * @since 3.1 */ private static class EclipsePreferencesAdapter implements IPreferenceStore { /** * Preference change listener. Listens for events preferences * fires a {@link org.eclipse.jface.util.PropertyChangeEvent} * on this adapter with arguments from the received event. */ private class PreferenceChangeListener implements IEclipsePreferences.IPreferenceChangeListener { /** * {@inheritDoc} */ public void preferenceChange(final IEclipsePreferences.PreferenceChangeEvent event) { if (Display.getCurrent() == null) { Display.getDefault().asyncExec(new Runnable() { public void run() { firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue()); } }); } else { firePropertyChangeEvent(event.getKey(), event.getOldValue(), event.getNewValue()); } } } // TODO When we move to Eclipse 3.2 change ListenerList to eclipse.core.runtime.ListenerList /** Listeners on on this adapter */ private ListenerList fListeners= new ListenerList(); /** Listener on the node */ private IEclipsePreferences.IPreferenceChangeListener fListener= new PreferenceChangeListener(); /** wrapped node */ private final IScopeContext fContext; private final String fQualifier; /** * Initialize with the node to wrap * * @param context The context to access */ public EclipsePreferencesAdapter(IScopeContext context, String qualifier) { fContext= context; fQualifier= qualifier; } private IEclipsePreferences getNode() { return fContext.getNode(fQualifier); } /** * {@inheritDoc} */ public void addPropertyChangeListener(IPropertyChangeListener listener) { if (fListeners.size() == 0) getNode().addPreferenceChangeListener(fListener); fListeners.add(listener); } /** * {@inheritDoc} */ public void removePropertyChangeListener(IPropertyChangeListener listener) { fListeners.remove(listener); if (fListeners.size() == 0) { getNode().removePreferenceChangeListener(fListener); } } /** * {@inheritDoc} */ public boolean contains(String name) { return getNode().get(name, null) != null; } /** * {@inheritDoc} */ public void firePropertyChangeEvent(String name, Object oldValue, Object newValue) { PropertyChangeEvent event= new PropertyChangeEvent(this, name, oldValue, newValue); Object[] listeners= fListeners.getListeners(); for (int i= 0; i < listeners.length; i++) ((IPropertyChangeListener) listeners[i]).propertyChange(event); } /** * {@inheritDoc} */ public boolean getBoolean(String name) { return getNode().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public boolean getDefaultBoolean(String name) { return BOOLEAN_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public double getDefaultDouble(String name) { return DOUBLE_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public float getDefaultFloat(String name) { return FLOAT_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public int getDefaultInt(String name) { return INT_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public long getDefaultLong(String name) { return LONG_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public String getDefaultString(String name) { return STRING_DEFAULT_DEFAULT; } /** * {@inheritDoc} */ public double getDouble(String name) { return getNode().getDouble(name, DOUBLE_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public float getFloat(String name) { return getNode().getFloat(name, FLOAT_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public int getInt(String name) { return getNode().getInt(name, INT_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public long getLong(String name) { return getNode().getLong(name, LONG_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public String getString(String name) { return getNode().get(name, STRING_DEFAULT_DEFAULT); } /** * {@inheritDoc} */ public boolean isDefault(String name) { return false; } /** * {@inheritDoc} */ public boolean needsSaving() { try { return getNode().keys().length > 0; } catch (BackingStoreException e) { // ignore } return true; } /** * {@inheritDoc} */ public void putValue(String name, String value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, double value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, float value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, int value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, long value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, String defaultObject) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setDefault(String name, boolean value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setToDefault(String name) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, double value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, float value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, int value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, long value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, String value) { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ public void setValue(String name, boolean value) { throw new UnsupportedOperationException(); } } class AdaptedSourceViewer extends RubySourceViewer { private List fTextConverters; private boolean fIgnoreTextConverters= false; public AdaptedSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler, boolean showAnnotationsOverview, int styles, IPreferenceStore store) { super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, store); } public IContentAssistant getContentAssistant() { return fContentAssistant; } public void addTextConverter(ITextConverter textConverter) { if (fTextConverters == null) { fTextConverters= new ArrayList(1); fTextConverters.add(textConverter); } else if (!fTextConverters.contains(textConverter)) fTextConverters.add(textConverter); } public void removeTextConverter(ITextConverter textConverter) { if (fTextConverters != null) { fTextConverters.remove(textConverter); if (fTextConverters.size() == 0) fTextConverters= null; } } /* * @see TextViewer#customizeDocumentCommand(DocumentCommand) */ protected void customizeDocumentCommand(DocumentCommand command) { super.customizeDocumentCommand(command); if (!fIgnoreTextConverters && fTextConverters != null) { for (Iterator e = fTextConverters.iterator(); e.hasNext();) ((ITextConverter) e.next()).customizeDocumentCommand(getDocument(), command); } } // http://dev.eclipse.org/bugs/show_bug.cgi?id=19270 public void updateIndentationPrefixes() { SourceViewerConfiguration configuration= getSourceViewerConfiguration(); String[] types= configuration.getConfiguredContentTypes(this); for (int i= 0; i < types.length; i++) { String[] prefixes= configuration.getIndentPrefixes(this, types[i]); if (prefixes != null && prefixes.length > 0) setIndentPrefixes(prefixes, types[i]); } } /* * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper) */ public boolean requestWidgetToken(IWidgetTokenKeeper requester) { if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) return false; return super.requestWidgetToken(requester); } /* * @see IWidgetTokenOwnerExtension#requestWidgetToken(IWidgetTokenKeeper, int) * @since 3.0 */ public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) { if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) return false; return super.requestWidgetToken(requester, priority); } /* * @see ITextOperationTarget#doOperation(int) */ public void doOperation(int operation) { if (getTextWidget() == null) return; switch (operation) { case CONTENTASSIST_PROPOSALS: long time= CODE_ASSIST_DEBUG ? System.currentTimeMillis() : 0; String msg= fContentAssistant.showPossibleCompletions(); if (CODE_ASSIST_DEBUG) { long delta= System.currentTimeMillis() - time; System.err.println("Code Assist (total): " + delta); //$NON-NLS-1$ } setStatusLineErrorMessage(msg); return; case QUICK_ASSIST: /* * XXX: We can get rid of this once the SourceViewer has a way to update the status line * https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787 */ msg= fQuickAssistAssistant.showPossibleQuickAssists(); setStatusLineErrorMessage(msg); return; case UNDO: fIgnoreTextConverters= true; super.doOperation(operation); fIgnoreTextConverters= false; return; case REDO: fIgnoreTextConverters= true; super.doOperation(operation); fIgnoreTextConverters= false; return; } super.doOperation(operation); } } /** * Internal activation listener. * @since 3.0 */ private class ActivationListener implements IWindowListener { /* * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow) * @since 3.1 */ public void windowActivated(IWorkbenchWindow window) { if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) { fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection(); updateOccurrenceAnnotations((ITextSelection)fForcedMarkOccurrencesSelection, RubyPlugin.getDefault().getASTProvider().getAST(getInputRubyElement(), ASTProvider.WAIT_NO, getProgressMonitor())); } } /* * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow) * @since 3.1 */ public void windowDeactivated(IWorkbenchWindow window) { if (window == getEditorSite().getWorkbenchWindow() && fMarkOccurrenceAnnotations && isActivePart()) removeOccurrenceAnnotations(); } /* * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow) * @since 3.1 */ public void windowClosed(IWorkbenchWindow window) { } /* * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow) * @since 3.1 */ public void windowOpened(IWorkbenchWindow window) { } } /** * Cancels the occurrences finder job upon document changes. * * @since 3.0 */ class OccurrencesFinderJobCanceler implements IDocumentListener, ITextInputListener { public void install() { ISourceViewer sourceViewer= getSourceViewer(); if (sourceViewer == null) return; StyledText text= sourceViewer.getTextWidget(); if (text == null || text.isDisposed()) return; sourceViewer.addTextInputListener(this); IDocument document= sourceViewer.getDocument(); if (document != null) document.addDocumentListener(this); } public void uninstall() { ISourceViewer sourceViewer= getSourceViewer(); if (sourceViewer != null) sourceViewer.removeTextInputListener(this); IDocumentProvider documentProvider= getDocumentProvider(); if (documentProvider != null) { IDocument document= documentProvider.getDocument(getEditorInput()); if (document != null) document.removeDocumentListener(this); } } /* * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.doCancel(); } /* * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { if (oldInput == null) return; oldInput.removeDocumentListener(this); } /* * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { if (newInput == null) return; newInput.addDocumentListener(this); } } /** * Finds and marks occurrence annotations. * * @since 3.0 */ class OccurrencesFinderJob extends Job { private IDocument fDocument; private ISelection fSelection; private ISelectionValidator fPostSelectionValidator; private boolean fCanceled= false; private IProgressMonitor fProgressMonitor; private Position[] fPositions; public OccurrencesFinderJob(IDocument document, Position[] positions, ISelection selection) { //TODO: Refactor job name to resource string somewhere super("OccurrencesFinderJob"); fDocument= document; fSelection= selection; fPositions= positions; if (getSelectionProvider() instanceof ISelectionValidator) fPostSelectionValidator= (ISelectionValidator)getSelectionProvider(); } // cannot use cancel() because it is declared final void doCancel() { fCanceled= true; cancel(); } private boolean isCanceled() { return fCanceled || fProgressMonitor.isCanceled() || fPostSelectionValidator != null && !(fPostSelectionValidator.isValid(fSelection) || fForcedMarkOccurrencesSelection == fSelection) || LinkedModeModel.hasInstalledModel(fDocument); } /* * @see Job#run(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus run(IProgressMonitor progressMonitor) { fProgressMonitor= progressMonitor; if (isCanceled()) return Status.CANCEL_STATUS; ITextViewer textViewer= getViewer(); if (textViewer == null) return Status.CANCEL_STATUS; IDocument document= textViewer.getDocument(); if (document == null) return Status.CANCEL_STATUS; IDocumentProvider documentProvider= getDocumentProvider(); if (documentProvider == null) return Status.CANCEL_STATUS; IAnnotationModel annotationModel= documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null) return Status.CANCEL_STATUS; // Add occurrence annotations int length= fPositions.length; Map annotationMap= new HashMap(length); for (int i= 0; i < length; i++) { if (isCanceled()) return Status.CANCEL_STATUS; String message; Position position= fPositions[i]; // Create & add annotation try { message= document.get(position.offset, position.length); } catch (BadLocationException ex) { // Skip this match continue; } annotationMap.put( new Annotation("org.rubypeople.rdt.ui.occurrences", false, message), //$NON-NLS-1$ position); } if (isCanceled()) return Status.CANCEL_STATUS; synchronized (getLockObject(annotationModel)) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension)annotationModel).replaceAnnotations(fOccurrenceAnnotations, annotationMap); } else { removeOccurrenceAnnotations(); Iterator iter= annotationMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry mapEntry= (Map.Entry)iter.next(); annotationModel.addAnnotation((Annotation)mapEntry.getKey(), (Position)mapEntry.getValue()); } } fOccurrenceAnnotations= (Annotation[])annotationMap.keySet().toArray(new Annotation[annotationMap.keySet().size()]); } return Status.OK_STATUS; } } /** * Updates the occurrences annotations based * on the current selection. * * @param selection the text selection * @param ast */ protected void updateOccurrenceAnnotations(ITextSelection selection, RootNode ast) { if (fOccurrencesFinderJob != null) fOccurrencesFinderJob.cancel(); if (!fMarkOccurrenceAnnotations) return; if (selection == null) return; IDocument document= getDocumentProvider().getDocument(getEditorInput()); if (document == null) return; if (document instanceof IDocumentExtension4) { int offset= selection.getOffset(); long currentModificationStamp= ((IDocumentExtension4)document).getModificationStamp(); if (fMarkOccurrenceTargetRegion != null && currentModificationStamp == fMarkOccurrenceModificationStamp) { if (fMarkOccurrenceTargetRegion.getOffset() <= offset && offset <= fMarkOccurrenceTargetRegion.getOffset() + fMarkOccurrenceTargetRegion.getLength()) return; } fMarkOccurrenceTargetRegion= RubyWordFinder.findWord(document, offset); fMarkOccurrenceModificationStamp= currentModificationStamp; } String source = document.get(); // Search for occurrences OccurrencesFinder finder = new OccurrencesFinder(); finder.setFMarkConstantOccurrences(fMarkConstantOccurrences); finder.setFMarkFieldOccurrences(fMarkFieldOccurrences); finder.setFMarkLocalVariableOccurrences(fMarkLocalVariableOccurrences); finder.setFMarkMethodExitPoints(fMarkMethodExitPoints); finder.setFMarkMethodOccurrences(fMarkMethodOccurrences); finder.setFMarkOccurrenceAnnotations(fMarkOccurrenceAnnotations); finder.setFMarkTypeOccurrences(fMarkTypeOccurrences); finder.setFStickyOccurrenceAnnotations(fStickyOccurrenceAnnotations); finder.initialize(ast, selection.getOffset(), selection.getLength()); List<Position> matches = finder.perform(); if (matches == null || matches.isEmpty()) { if (!fStickyOccurrenceAnnotations) { removeOccurrenceAnnotations(); } return; } // Convert to array Position[] positions = matches.toArray(new Position[matches.size()]); // Mark occurrences fOccurrencesFinderJob= new OccurrencesFinderJob(document, positions, selection); // fOccurrencesFinderJob.setPriority(Job.DECORATE); // fOccurrencesFinderJob.setSystem(true); // fOccurrencesFinderJob.schedule(); fOccurrencesFinderJob.run(new NullProgressMonitor()); } protected void setMarkOccurrencePreferences(IOccurrencesFinder occurrencesFinder) { } protected void installOccurrencesFinder(boolean forceUpdate) { fMarkOccurrenceAnnotations= true; fPostSelectionListenerWithAST= new ISelectionListenerWithAST() { public void selectionChanged(IEditorPart part, ITextSelection selection, RootNode astRoot) { updateOccurrenceAnnotations(selection, astRoot); } }; SelectionListenerWithASTManager.getDefault().addListener(this, fPostSelectionListenerWithAST); if (forceUpdate && getSelectionProvider() != null) { fForcedMarkOccurrencesSelection= getSelectionProvider().getSelection(); updateOccurrenceAnnotations((ITextSelection)fForcedMarkOccurrencesSelection, RubyPlugin.getDefault().getASTProvider().getAST(getInputRubyElement(), ASTProvider.WAIT_NO, getProgressMonitor())); } if (fOccurrencesFinderJobCanceler == null) { fOccurrencesFinderJobCanceler= new OccurrencesFinderJobCanceler(); fOccurrencesFinderJobCanceler.install(); } } protected void uninstallOccurrencesFinder() { fMarkOccurrenceAnnotations= false; if (fOccurrencesFinderJob != null) { fOccurrencesFinderJob.cancel(); fOccurrencesFinderJob= null; } if (fOccurrencesFinderJobCanceler != null) { fOccurrencesFinderJobCanceler.uninstall(); fOccurrencesFinderJobCanceler= null; } if (fPostSelectionListenerWithAST != null) { SelectionListenerWithASTManager.getDefault().removeListener(this, fPostSelectionListenerWithAST); fPostSelectionListenerWithAST= null; } removeOccurrenceAnnotations(); } protected boolean isMarkingOccurrences() { return fMarkOccurrenceAnnotations; } void removeOccurrenceAnnotations() { fMarkOccurrenceModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; fMarkOccurrenceTargetRegion= null; IDocumentProvider documentProvider= getDocumentProvider(); if (documentProvider == null) return; IAnnotationModel annotationModel= documentProvider.getAnnotationModel(getEditorInput()); if (annotationModel == null || fOccurrenceAnnotations == null) return; synchronized (getLockObject(annotationModel)) { if (annotationModel instanceof IAnnotationModelExtension) { ((IAnnotationModelExtension)annotationModel).replaceAnnotations(fOccurrenceAnnotations, null); } else { for (int i= 0, length= fOccurrenceAnnotations.length; i < length; i++) annotationModel.removeAnnotation(fOccurrenceAnnotations[i]); } fOccurrenceAnnotations= null; } } /** * Returns the lock object for the given annotation model. * * @param annotationModel the annotation model * @return the annotation model's lock object * @since 3.0 */ private Object getLockObject(IAnnotationModel annotationModel) { if (annotationModel instanceof ISynchronizable) { Object lock= ((ISynchronizable)annotationModel).getLockObject(); if (lock != null) return lock; } return annotationModel; } }