/******************************************************************************* * Copyright (c) 2004 Eric Merritt and others. 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 * * Contributors: Eric Merritt Vlad Dumitrescu Alain O'Dea *******************************************************************************/ package org.erlide.ui.editors.erl; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ICharacterPairMatcher; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.projection.ProjectionSupport; import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IPartService; import org.eclipse.ui.IPathEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionContext; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.editors.text.TextFileDocumentProvider; import org.eclipse.ui.texteditor.ChainedPreferenceStore; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.IEditorStatusLine; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.ui.views.properties.IPropertySource; import org.erlide.engine.ErlangEngine; import org.erlide.engine.model.ErlModelException; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.erlang.IErlAttribute; import org.erlide.engine.model.erlang.IErlFunctionClause; import org.erlide.engine.model.erlang.IErlMember; import org.erlide.engine.model.erlang.ISourceRange; import org.erlide.engine.model.erlang.ISourceReference; import org.erlide.engine.model.root.IErlModule; import org.erlide.engine.model.root.IErlProject; import org.erlide.engine.services.parsing.ScannerService; import org.erlide.engine.services.search.XrefService; import org.erlide.ui.actions.CompositeActionGroup; import org.erlide.ui.actions.ErlangSearchActionGroup; import org.erlide.ui.editors.erl.actions.CallHierarchyAction; import org.erlide.ui.editors.erl.actions.CleanUpAction; import org.erlide.ui.editors.erl.actions.ClearAllCachesAction; import org.erlide.ui.editors.erl.actions.ClearCacheAction; import org.erlide.ui.editors.erl.actions.CompileAction; import org.erlide.ui.editors.erl.actions.GotoMatchingBracketAction; import org.erlide.ui.editors.erl.actions.ShowOutlineAction; import org.erlide.ui.editors.erl.folding.IErlangFoldingStructureProvider; import org.erlide.ui.editors.erl.folding.IErlangFoldingStructureProviderExtension; import org.erlide.ui.editors.erl.outline.ErlangContentProvider; import org.erlide.ui.editors.erl.outline.ErlangLabelProvider; import org.erlide.ui.editors.erl.outline.ErlangOutlinePage; import org.erlide.ui.editors.erl.outline.IOutlineContentCreator; import org.erlide.ui.editors.erl.outline.IOutlineSelectionHandler; import org.erlide.ui.editors.erl.outline.ISortableContentOutlinePage; import org.erlide.ui.editors.util.EditorUtility; import org.erlide.ui.internal.ErlideUIPlugin; import org.erlide.ui.prefs.PreferenceConstants; import org.erlide.ui.util.ColorManager; import org.erlide.ui.util.ErlModelUtils; import org.erlide.ui.util.ProblemsLabelDecorator; import org.erlide.ui.views.ErlangPropertySource; import org.erlide.util.ErlLogger; import org.erlide.util.IDisposable; import org.erlide.util.SystemConfiguration; /** * The actual editor itself * * * @author Eric Merrit [cyberlync at gmail dot com] */ public class ErlangEditor extends AbstractErlangEditor implements IOutlineContentCreator, IOutlineSelectionHandler { public static final String ERLANG_EDITOR_ID = "org.erlide.ui.editors.erl.ErlangEditor"; public static final String EDITOR_INDENTATION_WIDTH = "indentationWidth"; private IErlModule fModule = null; private ColorManager colorManager; private ErlangOutlinePage myOutlinePage; private IPropertySource myPropertySource; private ProjectionSupport fProjectionSupport; private IErlangFoldingStructureProvider fProjectionModelUpdater; private final ErlangEditorErrorTickUpdater fErlangEditorErrorTickUpdater; private ToggleFoldingRunner fFoldingRunner; private EditorSelectionChangedListener fEditorSelectionChangedListener; private final IPreferenceChangeListener fPreferenceChangeListener = new PreferenceChangeListener(); private final IPropertyChangeListener propertyChangeListener = new PropertyChangeListener(); private Object fSelection; private ActionGroup fActionGroups; private ActionGroup fContextMenuGroup; private ShowOutlineAction fShowOutline; private CompileAction compileAction; private CleanUpAction cleanUpAction; private ClearCacheAction clearCacheAction; private ClearAllCachesAction clearAllCachesAction; private CallHierarchyAction callhierarchy; private final AnnotationSupport annotationSupport; XrefService xrefService; final MarkOccurencesSupport markOccurencesHandler = new MarkOccurencesSupport(this, null, IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP); public ErlangEditor(final XrefService xrefService) { super(); fErlangEditorErrorTickUpdater = new ErlangEditorErrorTickUpdater(this); annotationSupport = new AnnotationSupport(this, getAnnotationPreferenceLookup()); this.xrefService = xrefService; setRulerContextMenuId("#ErlangEditorRulerContext"); } @Override public void dispose() { if (colorManager != null) { colorManager.dispose(); colorManager = null; } final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof IDisposable) { ((IDisposable) sourceViewer).dispose(); } final IEclipsePreferences node = ErlideUIPlugin.getPrefsNode(); node.removePreferenceChangeListener(fPreferenceChangeListener); ErlideUIPlugin.getDefault().getPreferenceStore() .removePropertyChangeListener(propertyChangeListener); if (fActionGroups != null) { fActionGroups.dispose(); fActionGroups = null; } fErlangEditorErrorTickUpdater.dispose(); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.uninstall(); } disposeModule(); // cancel possible running computation markOccurencesHandler.dispose(); if (getSourceViewerConfiguration() instanceof IDisposable) { ((IDisposable) getSourceViewerConfiguration()).dispose(); } super.dispose(); } @Override protected void initializeEditor() { colorManager = new ColorManager(); setDocumentProvider(new TextFileDocumentProvider()); final IPreferenceStore store = getErlangEditorPreferenceStore(); setPreferenceStore(store); final ErlangSourceViewerConfiguration cfg = new EditorConfiguration( getPreferenceStore(), this, colorManager); setSourceViewerConfiguration(cfg); } public static IPreferenceStore getErlangEditorPreferenceStore() { final IPreferenceStore generalTextStore = EditorsUI.getPreferenceStore(); return new ChainedPreferenceStore(new IPreferenceStore[] { ErlideUIPlugin.getDefault().getPreferenceStore(), generalTextStore }); } public void disposeModule() { if (fModule != null) { fModule.dispose(); fModule = null; } } @Override protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] { "org.erlide.ui.erlangEditorScope" }); //$NON-NLS-1$ } class PreferenceChangeListener implements IPreferenceChangeListener { @Override public void preferenceChange(final PreferenceChangeEvent event) { final String key = event.getKey(); if ("markingOccurences".equals(key)) { final boolean newBooleanValue = event.getNewValue().equals("true"); markOccurencesHandler.setEnabled(newBooleanValue); } } } class PropertyChangeListener implements IPropertyChangeListener { @Override public void propertyChange(final PropertyChangeEvent event) { if (getSourceViewerConfiguration() instanceof ErlangSourceViewerConfiguration) { final ErlangSourceViewerConfiguration configuration = (ErlangSourceViewerConfiguration) getSourceViewerConfiguration(); if (configuration.affectsTextPresentation(event)) { configuration.handlePropertyChangeEvent(event); getSourceViewer().invalidateTextPresentation(); } } } } protected final boolean isActiveEditor() { final IWorkbenchWindow window = getSite().getWorkbenchWindow(); final IWorkbenchPage page = window.getActivePage(); if (page == null) { return false; } final IEditorPart activeEditor = page.getActiveEditor(); return activeEditor != null && activeEditor.equals(this); } @Override protected void createActions() { super.createActions(); ActionGroup esg; fActionGroups = new CompositeActionGroup( new ActionGroup[] { esg = new ErlangSearchActionGroup(this) }); fContextMenuGroup = new CompositeActionGroup(new ActionGroup[] { esg }); createCommonActions(); compileAction = new CompileAction(getSite()); compileAction.setActionDefinitionId(IErlangEditorActionDefinitionIds.COMPILE); setAction("compileFile", compileAction); if (getModule() != null) { cleanUpAction = new CleanUpAction(getModule().getResource()); cleanUpAction .setActionDefinitionId(IErlangEditorActionDefinitionIds.CLEAN_UP); setAction("cleanUp", cleanUpAction); } callhierarchy = new CallHierarchyAction(this, getModule(), xrefService); callhierarchy .setActionDefinitionId(IErlangEditorActionDefinitionIds.CALLHIERARCHY); setAction("callHierarchy", callhierarchy); markAsStateDependentAction("CallHierarchy", true); markAsSelectionDependentAction("CallHierarchy", true); if (SystemConfiguration.getInstance().isClearCacheAvailable()) { setupClearCacheActions(); // PlatformUI.getWorkbench().getHelpSystem().setHelp(indentAction, // IErlangHelpContextIds.INDENT_ACTION); } fShowOutline = new ShowOutlineAction( ErlangEditorMessages.getBundleForConstructedKeys(), "ShowOutline.", this); fShowOutline.setActionDefinitionId(IErlangEditorActionDefinitionIds.SHOW_OUTLINE); setAction(IErlangEditorActionDefinitionIds.SHOW_OUTLINE, fShowOutline); markAsContentDependentAction(IErlangEditorActionDefinitionIds.SHOW_OUTLINE, true); final Action action = new GotoMatchingBracketAction(this); action.setActionDefinitionId( IErlangEditorActionDefinitionIds.GOTO_MATCHING_BRACKET); setAction(GotoMatchingBracketAction.GOTO_MATCHING_BRACKET, action); } private void setupClearCacheActions() { if (clearAllCachesAction == null) { clearAllCachesAction = new ClearAllCachesAction( ErlangEditorMessages.getBundleForConstructedKeys(), "ClearAllCaches.", this); clearAllCachesAction.setActionDefinitionId( IErlangEditorActionDefinitionIds.CLEAR_ALL_CACHES); setAction("ClearAllCaches", clearAllCachesAction); markAsStateDependentAction("ClearAllCaches", false); markAsSelectionDependentAction("ClearAllCaches", false); } if (clearCacheAction == null) { clearCacheAction = new ClearCacheAction( ErlangEditorMessages.getBundleForConstructedKeys(), "ClearCache.", this); clearCacheAction .setActionDefinitionId(IErlangEditorActionDefinitionIds.CLEAR_CACHE); setAction("ClearCache", clearCacheAction); markAsStateDependentAction("ClearCache", true); markAsSelectionDependentAction("ClearCache", true); } } @Override protected void editorContextMenuAboutToShow(final IMenuManager menu) { super.editorContextMenuAboutToShow(menu); if (SystemConfiguration.getInstance().isClearCacheAvailable()) { setupClearCacheActions(); menu.prependToGroup(ITextEditorActionConstants.GROUP_SETTINGS, clearCacheAction); menu.prependToGroup(ITextEditorActionConstants.GROUP_SETTINGS, clearAllCachesAction); } menu.prependToGroup(ITextEditorActionConstants.GROUP_OPEN, compileAction); menu.prependToGroup(ITextEditorActionConstants.GROUP_OPEN, fShowOutline); addCommonActions(menu); menu.appendToGroup(ITextEditorActionConstants.GROUP_FIND, callhierarchy); final ActionContext context = new ActionContext( getSelectionProvider().getSelection()); fContextMenuGroup.setContext(context); fContextMenuGroup.fillContextMenu(menu); fContextMenuGroup.setContext(null); } @Override public Object getAdapter(@SuppressWarnings("rawtypes") final Class required) { if (IContentOutlinePage.class.equals(required)) { if (myOutlinePage == null) { myOutlinePage = createOutlinePage(); } return myOutlinePage; } if (IPropertySource.class.equals(required)) { if (myPropertySource == null) { myPropertySource = new ErlangPropertySource(this); } return myPropertySource; } if (IErlangFoldingStructureProvider.class.equals(required)) { return fProjectionModelUpdater; } if (fProjectionSupport != null) { final Object adapter = fProjectionSupport.getAdapter(getSourceViewer(), required); if (adapter != null) { return adapter; } } return super.getAdapter(required); } public IDocument getDocument() { return getSourceViewer().getDocument(); } /** * Jumps to the matching bracket. */ public void gotoMatchingBracket() { final ISourceViewer sourceViewer = getSourceViewer(); final IDocument document = sourceViewer.getDocument(); if (document == null) { return; } final IRegion selection = getSignedSelection(sourceViewer); final int selectionLength = Math.abs(selection.getLength()); if (selectionLength > 1) { setStatusLineErrorMessage( ErlangEditorMessages.GotoMatchingBracket_error_invalidSelection); sourceViewer.getTextWidget().getDisplay().beep(); return; } // #26314 final int sourceCaretOffset = selection.getOffset() + selection.getLength(); // TODO fix me! // if (isSurroundedByBrackets(document, sourceCaretOffset)) // sourceCaretOffset -= selection.getLength(); final IRegion region = getBracketMatcher().match(document, sourceCaretOffset); if (region == null) { setStatusLineErrorMessage( ErlangEditorMessages.GotoMatchingBracket_error_noMatchingBracket); sourceViewer.getTextWidget().getDisplay().beep(); return; } final int offset = region.getOffset(); final int length = region.getLength(); if (length < 1) { return; } final int anchor = getBracketMatcher().getAnchor(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 int targetOffset = ICharacterPairMatcher.RIGHT == anchor ? offset + 1 : offset + length; boolean visible = false; if (sourceViewer instanceof ITextViewerExtension5) { final ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; visible = extension.modelOffset2WidgetOffset(targetOffset) > -1; } else { final IRegion visibleRegion = sourceViewer.getVisibleRegion(); // http://dev.eclipse.org/bugs/show_bug.cgi?id=34195 visible = targetOffset >= visibleRegion.getOffset() && targetOffset <= visibleRegion.getOffset() + visibleRegion.getLength(); } if (!visible) { setStatusLineErrorMessage( ErlangEditorMessages.GotoMatchingBracket_error_bracketOutsideSelectedElement); sourceViewer.getTextWidget().getDisplay().beep(); return; } if (selection.getLength() < 0) { targetOffset -= selection.getLength(); } sourceViewer.setSelectedRange(targetOffset, selection.getLength()); sourceViewer.revealRange(targetOffset, selection.getLength()); } /** * Returns the signed current selection. The length will be negative if the resulting * selection is right-to-left (RtoL). * <p> * The selection offset is model based. * </p> * * @param sourceViewer * the source viewer * @return a region denoting the current signed selection, for a resulting RtoL * selections length is < 0 */ protected IRegion getSignedSelection(final ISourceViewer sourceViewer) { final StyledText text = sourceViewer.getTextWidget(); final Point selection = text.getSelectionRange(); if (text.getCaretOffset() == selection.x) { selection.x = selection.x + selection.y; selection.y = -selection.y; } selection.x = widgetOffset2ModelOffset(sourceViewer, selection.x); return new Region(selection.x, selection.y); } /** * Sets the given message as error message to this editor's status line. * * @param msg * message to be set */ @Override protected void setStatusLineErrorMessage(final String msg) { final IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter( IEditorStatusLine.class); if (statusLine != null) { statusLine.setMessage(true, msg, null); } } /** * Sets the given message as message to this editor's status line. * * @param msg * message to be set * @since 3.0 */ @Override protected void setStatusLineMessage(final String msg) { final IEditorStatusLine statusLine = (IEditorStatusLine) getAdapter( IEditorStatusLine.class); if (statusLine != null) { statusLine.setMessage(false, msg, null); } } @Override protected void doSetInput(final IEditorInput input) throws CoreException { final IDocumentProvider provider = getDocumentProvider(); if (input != getEditorInput()) { disposeModule(); resetReconciler(); } super.doSetInput(input); final IDocument document = provider.getDocument(input); if (!(input instanceof IPathEditorInput) && document != null) { final ErlangDocumentSetupParticipant setupParticipant = new ErlangDocumentSetupParticipant(); setupParticipant.setup(document); } if (myOutlinePage != null) { // TODO should we use model events here? myOutlinePage.setInput(input); } final IErlModule module = getModule(); if (module != null) { fErlangEditorErrorTickUpdater.updateEditorImage(module); } if (document != null) { // scannerListener = new ScannerListener(); // document.addDocumentListener(scannerListener); // scannerListener.documentOpened(); if (document.getLength() > 0) { // fake a change if the document already has content // scannerListener.documentChanged(new DocumentEvent(document, // 0, // document.getLength(), document.get())); } } } protected ISourceReference computeHighlightRangeSourceReference() { final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return null; } final StyledText styledText = sourceViewer.getTextWidget(); if (styledText == null) { return null; } int caret = 0; if (sourceViewer instanceof ITextViewerExtension5) { final ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer; caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset()); } else { final int offset = sourceViewer.getVisibleRegion().getOffset(); caret = offset + styledText.getCaretOffset(); } final IErlElement element = getElementAt(caret, false); if (!(element instanceof ISourceReference)) { return null; } return (ISourceReference) element; } @Override public IErlModule getModule() { if (fModule == null) { try { fModule = ErlModelUtils.getModule(getEditorInput()); fModule.createScanner(); final ScannerService erlScanner = fModule.getScanner(); erlScanner.dispose(); } catch (final CoreException e) { } } return fModule; } @Override protected void addFoldingSupport(final ISourceViewer viewer) { if (EditorUtility.isFoldingEnabled()) { final ProjectionViewer projectionViewer = (ProjectionViewer) viewer; fProjectionSupport = new ProjectionSupport(projectionViewer, getAnnotationAccess(), getSharedColors()); fProjectionSupport.addSummarizableAnnotationType( "org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$ fProjectionSupport.addSummarizableAnnotationType( "org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$ fProjectionSupport.install(); fProjectionModelUpdater = ErlideUIPlugin.getDefault() .getFoldingStructureProviderRegistry().getCurrentFoldingProvider(); if (fProjectionModelUpdater != null) { fProjectionModelUpdater.install(this, projectionViewer); } } } /** * Returns the most narrow element including the given offset. If * <code>reconcile</code> is <code>true</code> the editor's input element is * reconciled in advance. If it is <code>false</code> this method only returns a * result if the editor's input element does not need to be reconciled. * * @param offset * the offset included by the retrieved element * @param reconcile * <code>true</code> if working copy should be reconciled * @return the most narrow element which includes the given offset * @throws ErlModelException */ @Override public IErlElement getElementAt(final int offset, final boolean reconcile) { final IErlModule module = getModule(); if (module == null) { return null; } try { if (reconcile) { synchronized (module) { module.open(null); return module.getElementAt(offset); } } else if (module.isConsistent()) { return module.getElementAt(offset); } } catch (final Exception e) { } return null; } protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener { /** * Installs this selection changed listener with the given selection provider. If * the selection provider is a post selection provider, post selection changed * events are the preferred choice, otherwise normal selection changed events are * requested. * * @param selectionProvider */ public void install(final ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { final IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.addPostSelectionChangedListener(this); } else { selectionProvider.addSelectionChangedListener(this); } } /** * Removes this selection changed listener from the given selection provider. * * @param selectionProvider * the selection provider */ public void uninstall(final ISelectionProvider selectionProvider) { if (selectionProvider == null) { return; } if (selectionProvider instanceof IPostSelectionProvider) { final IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider; provider.removePostSelectionChangedListener(this); } else { selectionProvider.removeSelectionChangedListener(this); } } } /** * Updates the Erlang outline page selection and this editor's range indicator. */ class EditorSelectionChangedListener extends AbstractSelectionChangedListener { /* * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged * (org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override public void selectionChanged(final SelectionChangedEvent event) { ErlangEditor.this.selectionChanged(); } } /** * Called from org.erlide.ui.editors.erl.outline.ErlangOutlinePage.createControl * (...).new OpenAndLinkWithEditorHelper() {...}.linkToEditor(ISelection) * * @param selection */ public void doSelectionChanged(final ISelection selection) { ISourceReference reference = null; if (selection instanceof IStructuredSelection) { final IStructuredSelection ss = (IStructuredSelection) selection; for (final Object o : ss.toArray()) { if (o instanceof ISourceReference) { reference = (ISourceReference) o; break; } } } if (!isActivePart() && ErlideUIPlugin.getActivePage() != null) { ErlideUIPlugin.getActivePage().bringToTop(this); } setSelection(reference, true); } protected void selectionChanged() { if (getSelectionProvider() == null) { return; } final ISourceReference element = computeHighlightRangeSourceReference(); if (isLinkedToOutlinePage()) { synchronizeOutlinePage(element); } setSelection(element, false); // updateStatusLine(); } private boolean isLinkedToOutlinePage() { if (myOutlinePage == null) { return false; } return myOutlinePage.isLinkedWithEditor(); } /** * Creates the outline page used with this editor. * * @return the created Erlang outline page */ protected ErlangOutlinePage createOutlinePage() { final ErlangOutlinePage page = new ErlangOutlinePage(this); page.setInput(getEditorInput()); return page; } /** * Informs the editor that its outline has been closed. */ public void outlinePageClosed() { if (myOutlinePage != null) { myOutlinePage = null; resetHighlightRange(); } } /** * @see org.eclipse.ui.part.WorkbenchPart#setTitleImage(Image titleImage); * @param image */ public void updatedTitleImage(final Image image) { setTitleImage(image); } /** * Synchronizes the outliner selection with the given element position in the editor. * * @param element * the java element to select */ protected void synchronizeOutlinePage(final ISourceReference element) { synchronizeOutlinePage(element, true); } /** * 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(final ISourceReference element, final boolean checkIfOutlinePageActive) { if (myOutlinePage != null) { myOutlinePage.select(element); } } /** * Synchronizes the outliner selection with the actual cursor position in the editor. */ public void synchronizeOutlinePageSelection() { synchronizeOutlinePage(computeHighlightRangeSourceReference()); } public void setSelection(final ISourceReference reference, final boolean moveCursor) { if (getSelectionProvider() == null) { return; } final ISelection selection = getSelectionProvider().getSelection(); if (selection instanceof TextSelection) { final TextSelection textSelection = (TextSelection) selection; if (moveCursor && (textSelection.getOffset() != 0 || textSelection.getLength() != 0)) { markInNavigationHistory(); } } if (reference != null) { StyledText textWidget = null; final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer == null) { return; } textWidget = sourceViewer.getTextWidget(); if (textWidget == null) { return; } try { ISourceRange range = null; 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 IErlMember) { range = ((IErlMember) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } } else if (reference instanceof IErlAttribute) { range = ((IErlAttribute) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } } else if (reference instanceof IErlFunctionClause) { range = ((IErlFunctionClause) reference).getNameRange(); if (range != null) { offset = range.getOffset(); length = range.getLength(); } } if (offset > -1 && length > 0) { try { textWidget.setRedraw(false); sourceViewer.revealRange(offset, length); sourceViewer.setSelectedRange(offset, length); } finally { textWidget.setRedraw(true); } markInNavigationHistory(); } } catch (final IllegalArgumentException x) { } } else if (moveCursor) { resetHighlightRange(); markInNavigationHistory(); } } public void setSelection(final IErlElement element) { if (element == null || element instanceof IErlModule) { return; } if (element instanceof ISourceReference) { final ISourceReference reference = (ISourceReference) element; // set highlight range setSelection(reference, true); if (myOutlinePage != null) { myOutlinePage.select(reference); } } } private IWorkbenchPart getActivePart() { final IWorkbenchWindow window = getSite().getWorkbenchWindow(); final IPartService service = window.getPartService(); final IWorkbenchPart part = service.getActivePart(); return part; } @Override public void createPartControl(final Composite parent) { try { super.createPartControl(parent); } catch (final IllegalArgumentException e) { // #661: nicer message if file is encoded in utf-8 throw filterUTF8Exception(e); } setupBracketInserter(); final ProjectionViewer v = (ProjectionViewer) getSourceViewer(); v.doOperation(ProjectionViewer.TOGGLE); fEditorSelectionChangedListener = new EditorSelectionChangedListener(); fEditorSelectionChangedListener.install(getSelectionProvider()); final IEclipsePreferences node = ErlideUIPlugin.getPrefsNode(); node.addPreferenceChangeListener(fPreferenceChangeListener); ErlideUIPlugin.getDefault().getPreferenceStore() .addPropertyChangeListener(propertyChangeListener); PlatformUI.getWorkbench() .addWindowListener(markOccurencesHandler.fActivationListener); if (markOccurencesHandler.isMarkingOccurrences()) { markOccurencesHandler.installOccurrencesFinder(true); } } private IllegalArgumentException filterUTF8Exception( final IllegalArgumentException e) { final StackTraceElement[] stack = e.getStackTrace(); boolean filterIt = false; for (final StackTraceElement element : stack) { if (filterIt) { break; } if (!element.getClassName().equals("org.eclipse.swt.SWT")) { filterIt = element.getClassName() .equals("org.eclipse.swt.custom.StyledText") && element.getMethodName().equals("setStyleRanges"); } } if (filterIt) { return new IllegalArgumentException( "The file's actual encoding doesn't match the declared one", e); } return e; } protected boolean isActivePart() { final IWorkbenchPart part = getActivePart(); return part != null && part.equals(this); } public final ISourceViewer getViewer() { return getSourceViewer(); } @Override public ViewerComparator createDefaultOutlineComparator() { return null; } @Override public ViewerComparator createOutlineComparator() { return null; } @Override public ITreeContentProvider createOutlineContentProvider() { return new ErlangContentProvider(); } @Override public ILabelProvider createOutlineLabelProvider() { final ErlangLabelProvider erlangLabelProvider = new ErlangLabelProvider(); erlangLabelProvider.addLabelDecorator(new ProblemsLabelDecorator()); return erlangLabelProvider; } @Override public Object getOutlineInput() { return getModule(); } @Override public ISortableContentOutlinePage getContentOutline() { return myOutlinePage; } @Override public void updateSelection(final SelectionChangedEvent event) { final ISelection sel = event.getSelection(); if (sel instanceof IStructuredSelection) { final IStructuredSelection structuredSelection = (IStructuredSelection) sel; updateSelection(structuredSelection.getFirstElement()); } } @Override public void updateSelection(final Object sel) { } public void selectionChanged(final SelectionChangedEvent event) { if (event.getSource() == getSelectionProvider()) { return; } final ISelection sel = event.getSelection(); if (sel instanceof ITextSelection) { return; } if (sel instanceof IStructuredSelection) { fSelection = ((IStructuredSelection) sel).getFirstElement(); } else { fSelection = null; } } public Object getSelection() { return fSelection; } @Override public void doSave(final IProgressMonitor progressMonitor) { // TODO: maybe this should be in a resource change listener? super.doSave(progressMonitor); resetAndCacheScannerAndParser(); } public void resetAndCacheScannerAndParser() { final IErlModule module = getModule(); if (module == null) { return; } resetReconciler(); try { module.createScanner(); module.getScanner().dispose(); module.resetAndCacheScannerAndParser(getDocument().get()); } catch (final ErlModelException e) { ErlLogger.error(e); } } public void resetReconciler() { ((EditorConfiguration) getSourceViewerConfiguration()).resetReconciler(); } @Override public void reconcileNow() { ((EditorConfiguration) getSourceViewerConfiguration()).reconcileNow(); } public ActionGroup getActionGroup() { return fActionGroups; } /** * Runner that will toggle folding either instantly (if the editor is visible) or the * next time it becomes visible. If a runner is started when there is already one * registered, the registered one is canceled as toggling folding twice is a no-op. * <p> * The access to the fFoldingRunner field is not thread-safe, it is assumed that * <code>runWhenNextVisible</code> is only called from the UI thread. * </p> * * @since 3.1 */ final class ToggleFoldingRunner implements IPartListener2 { /** * The workbench page we registered the part listener with, or <code>null</code>. */ private IWorkbenchPage fPage; /** * Does the actual toggling of projection. */ @SuppressWarnings("synthetic-access") private void toggleFolding() { final ISourceViewer sourceViewer = getSourceViewer(); if (sourceViewer instanceof ProjectionViewer) { final ProjectionViewer pv = (ProjectionViewer) sourceViewer; if (pv.isProjectionMode() != EditorUtility.isFoldingEnabled()) { if (pv.canDoOperation(ProjectionViewer.TOGGLE)) { pv.doOperation(ProjectionViewer.TOGGLE); } } } } /** * Makes sure that the editor's folding state is correct the next time it becomes * visible. If it already is visible, it toggles the folding state. If not, it * either registers a part listener to toggle folding when the editor becomes * visible, or cancels an already registered runner. */ public void runWhenNextVisible() { // if there is one already: toggling twice is the identity if (fFoldingRunner != null) { fFoldingRunner.cancel(); return; } final IWorkbenchPartSite site = getSite(); if (site != null) { final IWorkbenchPage page = site.getPage(); if (!page.isPartVisible(ErlangEditor.this)) { // if we're not visible - defer until visible fPage = page; fFoldingRunner = this; page.addPartListener(this); return; } } // we're visible - run now toggleFolding(); fFoldingRunner = null; } /** * Remove the listener and clear the field. */ private void cancel() { if (fPage != null) { fPage.removePartListener(this); fPage = null; } if (fFoldingRunner == this) { fFoldingRunner = null; } } /* * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui. * IWorkbenchPartReference) */ @Override public void partVisible(final IWorkbenchPartReference partRef) { if (ErlangEditor.this.equals(partRef.getPart(false))) { cancel(); toggleFolding(); fFoldingRunner = null; } } /* * @seeorg.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui. * IWorkbenchPartReference) */ @Override public void partClosed(final IWorkbenchPartReference partRef) { if (ErlangEditor.this.equals(partRef.getPart(false))) { cancel(); } } @Override public void partActivated(final IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(final IWorkbenchPartReference partRef) { } @Override public void partDeactivated(final IWorkbenchPartReference partRef) { } @Override public void partOpened(final IWorkbenchPartReference partRef) { } @Override public void partHidden(final IWorkbenchPartReference partRef) { } @Override public void partInputChanged(final IWorkbenchPartReference partRef) { } } @Override protected void handlePreferenceStoreChanged(final PropertyChangeEvent event) { final String property = event.getProperty(); final ISourceViewer sourceViewer = getSourceViewer(); try { if (PreferenceConstants.EDITOR_FOLDING_ENABLED.equals(property)) { if (sourceViewer instanceof ProjectionViewer) { new ToggleFoldingRunner().runWhenNextVisible(); } return; } } finally { super.handlePreferenceStoreChanged(event); } } public void expandCollapseFunctionsOrComments(final boolean collapse, final boolean comments) { if (fProjectionModelUpdater instanceof IErlangFoldingStructureProviderExtension) { final IErlangFoldingStructureProviderExtension ext = (IErlangFoldingStructureProviderExtension) fProjectionModelUpdater; if (collapse) { if (comments) { ext.collapseComments(); } else { ext.collapseFunctions(); } } else { ext.expandAll(); } } } /** * Returns the lock object for the given annotation model. * * @param annotationModel * the annotation model * @return the annotation model's lock object * @since 3.0 */ Object getLockObject(final IAnnotationModel annotationModel) { if (annotationModel instanceof ISynchronizable) { final Object lock = ((ISynchronizable) annotationModel).getLockObject(); if (lock != null) { return lock; } } return annotationModel; } @Override protected ScannerService getNewScanner() { return getModule().getScanner(); } /** * Mutex for the reconciler. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 * for a description of the problem. * <p> * remove once the underlying problem * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved. * </p> */ private final Object fReconcilerLock = new Object(); /** * Returns the mutex for the reconciler. See * https://bugs.eclipse.org/bugs/show_bug.cgi?id=63898 for a description of the * problem. * <p> * remove once the underlying problem * (https://bugs.eclipse.org/bugs/show_bug.cgi?id=66176) is solved. * </p> * * @return the lock reconcilers may use to synchronize on */ public Object getReconcilerLock() { return fReconcilerLock; } @Override public IErlProject getProject() { return ErlangEngine.getInstance().getModelUtilService().getProject(getModule()); } @Override public String getScannerName() { return getModule().getScannerName(); } /** * Returns whether the given annotation is configured as a target for the "Go to * Next/Previous Annotation" actions * * @param annotation * the annotation * @return <code>true</code> if this is a target, <code>false</code> otherwise * @since 3.0 */ @Override protected boolean isNavigationTarget(final Annotation annotation) { return annotationSupport.isNavigationTarget(annotation); } /** * Returns whether the given annotation is configured as a target for the "Go to * Next/Previous Annotation" actions * * @param annotation * the annotation * @return <code>true</code> if this is a target, <code>false</code> otherwise * @since 3.0 */ @Override public Annotation gotoAnnotation(final boolean forward) { return annotationSupport.gotoAnnotation(forward); } }